gitlab-org--gitlab-foss/config/plugins/graphql_known_operations_plugin.js

112 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;