@ima/cli
Version:
IMA.js CLI tool to build, develop and work with IMA.js applications.
93 lines (92 loc) • 3.96 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.GenerateRunnerPlugin = void 0;
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const ejs_1 = __importDefault(require("ejs"));
const schema_utils_1 = require("schema-utils");
const webpack_1 = require("webpack");
const options_json_1 = __importDefault(require("./options.json"));
// Stores runtime code across multiple webpack compilers.
const runtimeStorage = {};
/**
* This plugin takes care of generating application runtime script
* consisting of @ima/core runner polyfill implementation embedded
* with the webpack runtime execution code.
*/
class GenerateRunnerPlugin {
#pluginName;
#options;
#runnerTemplate;
constructor(options) {
this.#pluginName = this.constructor.name;
this.#options = options;
// Validate options
(0, schema_utils_1.validate)(options_json_1.default, this.#options, {
name: this.#pluginName,
baseDataPath: 'options',
});
// Pre-compile runner ejs template
this.#runnerTemplate = ejs_1.default.compile(options.runnerTemplate ??
fs_1.default.readFileSync(path_1.default.resolve(path_1.default.dirname(require.resolve('@ima/core')), '../../polyfill/runner.ejs'), 'utf8'));
}
apply(compiler) {
compiler.hooks.compilation.tap(this.#pluginName, compilation => {
compilation.hooks.processAssets.tapPromise({
name: this.#pluginName,
stage: compiler.webpack.Compilation.PROCESS_ASSETS_STAGE_DERIVED,
}, (assets) => this.generate(assets, compilation));
});
}
/**
* Generate runner code from compiled assets.
*/
async generate(assets, compilation) {
const runtimeAsset = Object.keys(assets).find(assetName => /runtime\.(.+)\.js$/.test(assetName));
if (!runtimeAsset) {
return;
}
const { name, forceLegacy, command, legacy } = this.#options.context;
const { disableLegacyBuild } = this.#options.imaConfig;
// Save runtime code into storage
runtimeStorage[name === 'client.es' ? 'esRuntimeCode' : 'runtimeCode'] =
assets[runtimeAsset].source().toString();
const { esRuntimeCode, runtimeCode } = runtimeStorage;
// Delete runtime asset since we inline it in the IMA runner.
compilation.deleteAsset(runtimeAsset);
if ((disableLegacyBuild && esRuntimeCode) ||
(command == 'build' && esRuntimeCode && runtimeCode) ||
(command == 'dev' && legacy && esRuntimeCode && runtimeCode) ||
(command == 'dev' && !legacy && esRuntimeCode)) {
const generatedRunner = this.#runnerTemplate({
forceLegacy: !!forceLegacy,
esRuntime: esRuntimeCode
? this.#addSlashes(esRuntimeCode)
: '// es.runtime not generated',
runtime: runtimeCode
? this.#addSlashes(runtimeCode)
: '// runtime not generated',
});
// Emit compiled ima runner with embedded runtime codes
return compilation.emitAsset('./server/runner.js', new webpack_1.sources.RawSource(generatedRunner));
}
}
/**
* Add slashes to the runtime code so it can be used
* as a string argument in the Function constructor.
*/
#addSlashes(code) {
return code
.replace(/\\/g, '\\\\')
.replace(/\t/g, '\\t')
.replace(/\n/g, '\\n')
.replace(/\f/g, '\\f')
.replace(/\r/g, '\\r')
.replace(/'/g, "\\'")
.replace(/"/g, '\\"');
}
}
exports.GenerateRunnerPlugin = GenerateRunnerPlugin;