@ima/cli
Version:
IMA.js CLI tool to build, develop and work with IMA.js applications.
115 lines (114 loc) • 4.9 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ManifestPlugin = void 0;
const schema_utils_1 = require("schema-utils");
const webpack_1 = require("webpack");
const options_json_1 = __importDefault(require("./options.json"));
// This seed is shared among instances of this plugin
const seed = {
assets: {},
assetsByCompiler: {
client: {},
'client.es': {},
server: {},
},
};
/**
* This plugin takes care of generating application runtime script
* consisting of @ima/core runner polyfill implementation embedded
* with the webpack runtime execution code.
*/
class ManifestPlugin {
#pluginName;
#options;
static #generated = {};
constructor(options) {
this.#pluginName = this.constructor.name;
this.#options = options;
// Track generated configurations status
ManifestPlugin.#generated[options.context.name] = 0;
// Validate options
(0, schema_utils_1.validate)(options_json_1.default, this.#options, {
name: this.#pluginName,
baseDataPath: 'options',
});
seed.publicPath = this.#options.imaConfig.publicPath;
}
apply(compiler) {
compiler.hooks.compilation.tap(this.#pluginName, compilation => {
compilation.hooks.processAssets.tap({
name: this.#pluginName,
stage: Infinity,
}, (assets) => this.generate(assets, compilation));
});
}
/**
* Generate runner code from compiled assets.
*/
generate(assets, compilation) {
const compilationName = compilation?.name;
if (!compilationName) {
return;
}
Object.keys(assets)
.filter(assetName => !assetName.includes('/chunk.') &&
!assetName.includes('runner.js') &&
this.#filter(assetName))
.forEach(assetName => {
const asset = compilation.getAsset(assetName);
if (!asset?.name) {
return;
}
const fileName = `${asset.name}`;
const name = `${assetName.replace(`.${asset?.info?.contenthash}`, '')}`;
const assetWithInfo = {
name,
fileName,
...asset?.info,
};
seed.assetsByCompiler[compilationName][name] = assetWithInfo;
seed.assets[name] = assetWithInfo;
});
// Mark this configuration as generated
ManifestPlugin.#generated[compilationName]++;
const generatedValues = Object.values(ManifestPlugin.#generated);
// Once all configurations are generated the same amount of times then we can emit the manifest file
// Beware! If you are changing this code, please note:
// Using counter instead of boolean values prevents an issue, where configuration "A" is compiled multiple times
// before configuration "B" is compiled even once.
// With boolean values, we would emit the manifest file once configuration "B" is compiled for the first time.
// Emitting the manifest file resets the `generated` values and after the second configuration "B" is generated
// we get into inconsistent state, where we already consider configuration "B" to be ready for emitting,
// but it is not. The next manifest re-build is then triggered too early and we are stuck in this inconsistent state.
// From the user perspective, when we run `ima dev` and we change some file, the manifest file is always one version
// behind for one of the configurations. It is very hard to debug this issue, because it is not deterministic
// and it is hard to reproduce.
if (generatedValues.every(v => v === generatedValues[0])) {
// Reset tracking info
Object.keys(ManifestPlugin.#generated).forEach(key => (ManifestPlugin.#generated[key] =
0));
// Emit compiled ima runner with embedded runtime codes
return compilation.emitAsset('./manifest.json', new webpack_1.sources.RawSource(JSON.stringify(seed, null, 2)));
}
}
/**
* Filter for compilation specific assets
*/
#filter(assetName) {
const { outputFolders, processCss } = this.#options.context;
// Include bundle-specific JS files
if (/\.(js)$/.test(assetName) && assetName.startsWith(outputFolders.js)) {
return true;
}
// Include CSS only from the root directory
if (processCss &&
new RegExp(`${outputFolders.css}/[\\w.\\-_]+\\.css$`).test(assetName)) {
return true;
}
return false;
}
}
exports.ManifestPlugin = ManifestPlugin;