Skip to content

Commit

Permalink
debug next branch
Browse files Browse the repository at this point in the history
  • Loading branch information
wernerglinka committed Feb 16, 2022
1 parent 62a16b2 commit e8b0513
Show file tree
Hide file tree
Showing 16 changed files with 155 additions and 194 deletions.
297 changes: 153 additions & 144 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
'use strict';

const debug = require('debug')('metalsmith-metadata');
const debug = require('debug')('metalsmith-metameta');
const {
promises: { readFile, readdir }
} = require('fs');
const path = require('path');
const extension = path.extname;
const { relative, join, extname: extension, sep, basename, dirname } = require('path');
const yaml = require('js-yaml');
const toml = require('toml');

Expand All @@ -26,6 +25,40 @@ function normalizeOptions(options) {
return Object.assign({}, defaults, options || {});
}

/**
* groupMetadataSources
* Function to split array values into four groups
* - local files in Metalsmith source folder
* - local directories in Metalsmith source folder
* - external files outside Metalsmith source folder but in Metalsmith directory
* - external directories outside Metalsmith source folder but in Metalsmith directory
*
* @param {array} arr
* @param {function} filter
*
*/
function groupMetadataSources(arr, src) {
return arr.reduce((accu, value) => {
// local file
if(!!extension(value.path) && value.path.startsWith(src)) {
accu[0].push(value);
}
// local directory
if(!extension(value.path) && value.path.startsWith(src)) {
accu[1].push(value);
}
// external file
if(!!extension(value.path) && !value.path.startsWith(src)) {
accu[2].push(value);
}
// external directory
if(!extension(value.path) && !value.path.startsWith(src)) {
accu[3].push(value);
}
return accu;
}, [[],[],[],[]]);
}

/**
* YAML to JSON
* @param {*} string - YAML file
Expand Down Expand Up @@ -53,33 +86,59 @@ function tomlToJSON(string) {
}

/**
* getExternalFile
* Reads file content in either .json, .yaml, .yml or .toml format
* @param {*} filePath
* @returns Content of the file in .json
* toJson
* Converts YAML, YML, TOML and filebuffer to JSON
* @param {string} file
* @param {string} ext
* @returns JSON object literal
*/
async function getExternalFile(filePath) {
const fileExtension = extension(filePath);
const fileBuffer = await readFile(filePath);
function toJson(file, ext) {
let fileContent;

switch (fileExtension) {
switch (ext) {
case '.yaml':
case '.yml':
fileContent = yamlToJSON(fileBuffer);
try {
fileContent = yamlToJSON(file);
} catch(e) {
debug(e);
}
break;
case '.toml':
fileContent = tomlToJSON(fileBuffer);
try { // remove object prototype
fileContent = JSON.parse(JSON.stringify(tomlToJSON(file)));
} catch(e) {
debug(e);
}
break;
case '.json':
fileContent = JSON.parse(fileBuffer.toString()); // remove line breaks etc from the filebuffer
try {
fileContent = JSON.parse(file.toString()); // remove line breaks etc from the filebuffer
} catch(e) {
debug(e);
}
break;
default:
fileContent = JSON.parse(fileBuffer.toString());
fileContent = "";
debug("Unsupported file type");
}

return fileContent;
}

/**
* getExternalFile
* Reads file content in either .json, .yaml, .yml or .toml format
* @param {*} filePath
* @returns Content of the file in .json
*/
async function getExternalFile(filePath) {
const fileExtension = extension(filePath);
const fileBuffer = await readFile(filePath);

return toJson(fileBuffer, fileExtension);
}

/**
* getDirectoryFiles
* @param {*} directoryPath
Expand All @@ -98,7 +157,7 @@ async function getDirectoryFiles(directoryPath) {
*/
async function getDirectoryFilesContent(directoryPath, fileList) {
const fileContent = await fileList.map(async (file) => {
return await getExternalFile(path.join(directoryPath, file));
return await getExternalFile(join(directoryPath, file));
});
return await Promise.all(fileContent);
}
Expand Down Expand Up @@ -126,6 +185,7 @@ async function getFileObject(filePath, optionKey, allMetadata) {
async function getDirectoryObject(directoryPath, optionKey, allMetadata) {
return getDirectoryFiles(directoryPath)
.then((fileBuffers) => {

const groupMetadata = [];
fileBuffers.forEach((fileBuffer) => {
groupMetadata.push(fileBuffer);
Expand All @@ -138,7 +198,7 @@ async function getDirectoryObject(directoryPath, optionKey, allMetadata) {
}
})
.catch((e) => {
done(e.message);
debug(e);
});
}

Expand Down Expand Up @@ -170,147 +230,96 @@ async function getDirectoryObject(directoryPath, optionKey, allMetadata) {

function initMetadata(options) {
options = normalizeOptions(options);
debug("Receiving options: %O", options)

return function metameta(files, metalsmith, done) {
// return if no options
if(Object.keys(options).length === 0) {
debug("Found no metadata options");
done();
}

const allMetadata = metalsmith.metadata();

// array to hold all active promises during external file reads. Will be
// used with Promise.allSettled to invoke done()
const allPromises = [];

// loop over all options/metadata files/directories
Object.keys(options).forEach(function (optionKey) {
// check if file is located inside the metalsmith source directory
const metaFilePath = options[optionKey];

const msDirectory = metalsmith.directory();
const srcDirectory = metalsmith.source();
const msSourceFolder = srcDirectory.replace(msDirectory, '.');

const isLocal = metaFilePath.startsWith(msSourceFolder);

// flag to be reset when valid filepath is detected
let validFilepath = false;

/*
* if file or directory is local we can get the metadata from the metalsmith file object
*/
if (isLocal) {
// get object key from the options
const key = metaFilePath.replace(`${msSourceFolder}${path.sep}`, '');

// check if the optionKey element has a file exension
const fileExtension = extension(metaFilePath);
if (fileExtension) {
if (!!fileExtension.match('(ya?ml|toml|json)')) {
let metadata;
// get the data from file object
try {
metadata = files[key].contents.toString();
} catch (error) {
done(error);
}
if (!!fileExtension.match('(ya?ml)')) {
metadata = JSON.stringify(yamlToJSON(metadata));
}
if (fileExtension === '.toml') {
metadata = JSON.stringify(tomlToJSON(metadata));
}

// to temp meta object
allMetadata[optionKey] = JSON.parse(metadata);
// ... and remove this file from the metalsmith build process
delete files[key];

// indicate filepath is valid
validFilepath = true;
} else {
done(`${fileExtension} is not a valid file type`);
}
} else {
// assume this is a directory, all files in this directory will be concatenated into one
// metadata object
const groupMetadata = [];
Object.keys(files).forEach(function (file) {
if (file.includes(key)) {
const fileExtension = extension(file);
if (!!fileExtension.match('(ya?ml|toml|json)')) {
let metadata;
// get the data from file object
try {
metadata = files[file].contents.toString();
} catch (error) {
done(error);
}
if (fileExtension === '.yaml' || fileExtension === '.yml') {
metadata = JSON.stringify(yamlToJSON(metadata));
}
if (fileExtension === '.toml') {
metadata = JSON.stringify(tomlToJSON(metadata));
}

groupMetadata.push(JSON.parse(metadata));
} else {
done(`${fileExtension} is not a valid file type`);
}
}
});

if (groupMetadata.length) {
allMetadata[optionKey] = groupMetadata;
} else {
done(`No files found in this directory "${key}"`);
}

// indicate filepath is valid
validFilepath = true;
// create array with all option values relative to metalsmith directory
const allOptions = Object.keys(options).map(key => (
{
"key": key,
"path": relative(metalsmith.directory(), options[key])
}
));

// get metalsmith source directory
const metalsmithSource = relative(metalsmith.directory(), metalsmith.source());
// group option values into local/external files/directories
const [localFiles, localDirs, externalFiles, externalDirs] = groupMetadataSources(allOptions, metalsmithSource);

localFiles.forEach(function(file){
// option path is relative to metalsmith root
// convert dir path to be relative to metalsmith source as in files object key
const filePath = relative(metalsmith.source(), file.path);
const fileExtension = extension(filePath);

// get the data from file object
const metadata = toJson(files[filePath].contents, fileExtension);

// to temp meta object
allMetadata[file.key] = metadata;

debug("Adding these local files to metadata: %O", metadata);

// ... and remove this file from the metalsmith build process
delete files[file.key];
});

localDirs.forEach(function(dir) {
// option path is relative to metalsmith root
// convert dir path to be relative to metalsmith source
const filePath = relative(metalsmith.source(), dir.path);

const groupMetadata = [];
Object.keys(files).forEach(function (file) {
const fileExtension = extension(file);
if (file.includes(filePath)) {
// get the data from file object
const metadata = toJson(files[file].contents, fileExtension);

debug("Adding these local directories to metadata: %O", metadata);

groupMetadata.push(metadata);

}
});

if (groupMetadata.length) {
allMetadata[dir.key] = groupMetadata;
} else {
/*
* isExternal
* if file or directory is external we get the metadata from respective files
*/

// get object key
const key = metaFilePath.slice(2);

// check if the optionKey has a file exension
const fileExtension = extension(metaFilePath);
if (fileExtension) {
if (!!fileExtension.match('(ya?ml|toml|json)')) {
// read external file content and store in metadata object
const filePath = path.join(metalsmith._directory, key);
const extFilePromise = getFileObject(filePath, optionKey, allMetadata);

// add this promise to allPromises array. Will be later used with Promise.allSettled to invoke done()
allPromises.push(extFilePromise);

// indicate filepath is valid
validFilepath = true;
}
} else {
// assume this is a directory
// get content of all files in this directory, concatenated into one metadata object
const directoryPath = path.join(metalsmith._directory, key);
const extDirectoryPromise = getDirectoryObject(directoryPath, optionKey, allMetadata);

// add this promise to allPromises array. Will be later used with Promise.allSettled to invoke done()
allPromises.push(extDirectoryPromise);

// indicate filepath is valid
validFilepath = true;
}
done(`No files found in this directory "${dir}"`);
}
});

if (!validFilepath) {
done(
`${metaFilePath} is not a valid metafile path. Path must be relative to Metalsmith root`
);
}
externalFiles.forEach(function(file) {
const extFilePromise = getFileObject(file.path, file.key, allMetadata);

// add this promise to allPromises array. Will be later used with Promise.allSettled to invoke done()
allPromises.push(extFilePromise);
});

externalDirs.forEach(function(dir) {
// get content of all files in this directory, concatenated into one metadata object
//const directoryPath = join(metalsmith.directory(), dir.path);
const extDirectoryPromise = getDirectoryObject(dir.path, dir.key, allMetadata);

// add this promise to allPromises array. Will be later used with Promise.allSettled to invoke done()
allPromises.push(extDirectoryPromise);
});

// Promise.allSettled is used to invoke done()
Promise.allSettled(allPromises).then(() => done());
// Promise.all is used to invoke done()
Promise.all(allPromises).then(() => done());
};
}

Expand Down
3 changes: 0 additions & 3 deletions tests/fixtures/build/data/folder-test/etiam.json

This file was deleted.

3 changes: 0 additions & 3 deletions tests/fixtures/build/data/folder-test/fringilla.json

This file was deleted.

3 changes: 0 additions & 3 deletions tests/fixtures/build/data/folder-test/sollicitudin.json

This file was deleted.

Loading

0 comments on commit e8b0513

Please sign in to comment.