113 lines
3 KiB
JavaScript
113 lines
3 KiB
JavaScript
|
/* eslint-disable no-underscore-dangle */
|
||
|
const yaml = require('js-yaml');
|
||
|
|
||
|
const PLUGIN_NAME = 'GraphqlKnownOperationsPlugin';
|
||
|
const GRAPHQL_PATH_REGEX = /(query|mutation)\.graphql$/;
|
||
|
const OPERATION_NAME_SOURCE_REGEX = /^\s*module\.exports.*oneQuery.*"(\w+)"/gm;
|
||
|
|
||
|
/**
|
||
|
* Returns whether a given webpack module is a "graphql" module
|
||
|
*/
|
||
|
const isGraphqlModule = (module) => {
|
||
|
return GRAPHQL_PATH_REGEX.test(module.resource);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Returns graphql operation names we can parse from the given module
|
||
|
*
|
||
|
* Since webpack gives us the source **after** the graphql-tag/loader runs,
|
||
|
* we can look for specific lines we're guaranteed to have from the
|
||
|
* graphql-tag/loader.
|
||
|
*/
|
||
|
const getOperationNames = (module) => {
|
||
|
const originalSource = module.originalSource();
|
||
|
|
||
|
if (!originalSource) {
|
||
|
return [];
|
||
|
}
|
||
|
|
||
|
const matches = originalSource.source().toString().matchAll(OPERATION_NAME_SOURCE_REGEX);
|
||
|
|
||
|
return Array.from(matches).map((match) => match[1]);
|
||
|
};
|
||
|
|
||
|
const createFileContents = (knownOperations) => {
|
||
|
const sourceData = Array.from(knownOperations.values()).sort((a, b) => a.localeCompare(b));
|
||
|
|
||
|
return yaml.dump(sourceData);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Creates a webpack4 compatible "RawSource"
|
||
|
*
|
||
|
* Inspired from https://sourcegraph.com/github.com/FormidableLabs/webpack-stats-plugin@e050ff8c362d5ddd45c66ade724d4a397ace3e5c/-/blob/lib/stats-writer-plugin.js?L144
|
||
|
*/
|
||
|
const createWebpackRawSource = (source) => {
|
||
|
const buff = Buffer.from(source, 'utf-8');
|
||
|
|
||
|
return {
|
||
|
source() {
|
||
|
return buff;
|
||
|
},
|
||
|
size() {
|
||
|
return buff.length;
|
||
|
},
|
||
|
};
|
||
|
};
|
||
|
|
||
|
const onSucceedModule = ({ module, knownOperations }) => {
|
||
|
if (!isGraphqlModule(module)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
getOperationNames(module).forEach((x) => knownOperations.add(x));
|
||
|
};
|
||
|
|
||
|
const onCompilerEmit = ({ compilation, knownOperations, filename }) => {
|
||
|
const contents = createFileContents(knownOperations);
|
||
|
const source = createWebpackRawSource(contents);
|
||
|
|
||
|
const asset = compilation.getAsset(filename);
|
||
|
if (asset) {
|
||
|
compilation.updateAsset(filename, source);
|
||
|
} else {
|
||
|
compilation.emitAsset(filename, source);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Webpack plugin that outputs a file containing known graphql operations.
|
||
|
*
|
||
|
* A lot of the mechanices was expired from [this example][1].
|
||
|
*
|
||
|
* [1]: https://sourcegraph.com/github.com/FormidableLabs/webpack-stats-plugin@e050ff8c362d5ddd45c66ade724d4a397ace3e5c/-/blob/lib/stats-writer-plugin.js?L136
|
||
|
*/
|
||
|
class GraphqlKnownOperationsPlugin {
|
||
|
constructor({ filename }) {
|
||
|
this._filename = filename;
|
||
|
}
|
||
|
|
||
|
apply(compiler) {
|
||
|
const knownOperations = new Set();
|
||
|
|
||
|
compiler.hooks.emit.tap(PLUGIN_NAME, (compilation) => {
|
||
|
onCompilerEmit({
|
||
|
compilation,
|
||
|
knownOperations,
|
||
|
filename: this._filename,
|
||
|
});
|
||
|
});
|
||
|
|
||
|
compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
|
||
|
compilation.hooks.succeedModule.tap(PLUGIN_NAME, (module) => {
|
||
|
onSucceedModule({
|
||
|
module,
|
||
|
knownOperations,
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
module.exports = GraphqlKnownOperationsPlugin;
|