Skip to content

Instantly share code, notes, and snippets.

@lmorchard
Created November 29, 2017 21:47
Show Gist options
  • Save lmorchard/d4a8159d9cab44e7c56556c0158d1f82 to your computer and use it in GitHub Desktop.
Save lmorchard/d4a8159d9cab44e7c56556c0158d1f82 to your computer and use it in GitHub Desktop.
const fs = require('fs');
const path = require('path');
const util = require('util');
const globby = require('globby');
const readFile = util.promisify(fs.readFile);
// const YAML = require('yamljs');
// const ContentTransformerPlugin = require('./frontend/lib/content-transformer-plugin');
//
// new ContentTransformerPlugin({
// inputs: {
// experiments: 'content-src/experiments/**/*.yaml',
// news_updates: 'content-src/news_updates.yaml'
// },
// parser: ({ file }) => YAML.parse(file.content.toString('utf8')),
// transforms: [
// ({ inputs }) => [['TESTING 1', 'moooo ' + Date.now()]],
// ({ inputs }) => [['TESTING 2', 'foooo ' + Date.now()]],
// async ({ inputs }) =>
// inputs.experiments
// .filter(file => file.modified)
// .map(async ({ filename, parsed }) => {
// return [filename + '.txt', 'foooo ' + Date.now()];
// })
// ]
// })
class ContentTransformerPlugin {
constructor(options = {}) {
// Assemble options with defaults.
this.options = {
inputs: {},
parser: () => {},
transforms: [],
...options
};
// Resolve configured input globs into file tracking records
this.inputs = {};
Object.entries(this.options.inputs).forEach(([key, patterns]) => {
this.inputs[key] = globby.sync(patterns).map(filename => ({
filename,
content: null,
parsed: null,
lastModified: null,
modified: true
}));
});
}
apply(compiler) {
compiler.plugin('emit', (compilation, callback) => {
const result = (async () => {
const inputs = await this.updateInputs(compiler, compilation);
const result = await this.runTransforms(compiler, compilation);
return result;
})();
result.then(() => callback(), err => callback(err));
});
}
updateInputs(compiler, compilation) {
const updateFile = async (file, key) => {
// Update full file path from current compiler.
file.path = path.join(compiler.context, file.filename);
// Add the file to the dependency watch list
compilation.fileDependencies.push(file.path);
// Work out whether this file has been modified.
const currentModified = compilation.fileTimestamps[file.path];
file.modified = !file.lastModified || file.lastModified < currentModified;
file.lastModified = currentModified;
if (file.modified) {
// If modified, load & parse the file.
file.content = await readFile(file.filename);
file.parsed = await this.options.parser({
key,
file,
compiler,
compilation
});
}
};
// Return a promise with a flat set of all pending file updates.
return Promise.all(
Object.entries(this.inputs).reduce(
(pending, [key, files]) =>
pending.concat(files.map(file => updateFile(file, key))),
[]
)
);
}
runTransforms(compiler, compilation) {
const runTransform = async transform => {
// Transform can be async, so await.
const pending = await transform({
inputs: this.inputs,
compiler,
compilation
});
// Each array item from transform can be async, so await those too.
const result = await Promise.all(pending);
// Finally, add the generated assets to the compilation.
result.forEach(([name, content]) => {
compilation.assets[name] = {
source: () => content,
size: () => content.length
};
});
};
// Return a promise to run all the transforms.
return Promise.all(this.options.transforms.map(runTransform));
}
}
module.exports = ContentTransformerPlugin;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment