UNPKG

webpack-userscript

Version:
220 lines 9.79 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.UserscriptPlugin = void 0; const tslib_1 = require("tslib"); const node_path_1 = tslib_1.__importDefault(require("node:path")); const tapable_1 = require("tapable"); const webpack_1 = require("webpack"); const const_1 = require("./const"); const features_1 = require("./features"); const utils_1 = require("./utils"); const { ConcatSource, RawSource } = webpack_1.sources; class UserscriptPlugin { constructor(options = {}) { this.name = 'UserscriptPlugin'; this.hooks = { init: new tapable_1.AsyncParallelHook(['compiler']), close: new tapable_1.AsyncParallelHook(['compiler']), preprocess: new tapable_1.AsyncParallelHook([ 'compilation', 'context', ]), process: new tapable_1.AsyncParallelHook([ 'compilation', 'context', ]), headers: new tapable_1.AsyncSeriesWaterfallHook([ 'headersProps', 'context', ]), proxyHeaders: new tapable_1.AsyncSeriesWaterfallHook(['headersProps', 'context']), proxyScriptFile: new tapable_1.AsyncSeriesWaterfallHook([ 'proxyScriptFile', 'context', ]), renderHeaders: new tapable_1.AsyncSeriesBailHook([ 'headersProps', ]), renderProxyHeaders: new tapable_1.AsyncSeriesBailHook([ 'headersProps', ]), }; this.contexts = new WeakMap(); this.options = {}; const { metajs = true, strict = true } = options; Object.assign(options, { metajs, strict }); this.features = [ new features_1.LoadHeaders(options), new features_1.FixTags(options), new features_1.ResolveBaseURLs(options), new features_1.ProcessSSRI(options), new features_1.SetDefaultTags(options), new features_1.ProcessProxyScript(options), new features_1.Interpolater(options), new features_1.ValidateHeaders(options), new features_1.RenderHeaders(options), ]; this.options = options; } apply(compiler) { const name = this.name; let buildNo = 0; const initPromise = new Promise((resolve) => queueMicrotask(() => resolve(this.init(compiler)))); compiler.hooks.beforeCompile.tapPromise(name, () => initPromise); compiler.hooks.compilation.tap(name, (compilation) => { this.contexts.set(compilation, { buildNo: ++buildNo, buildTime: (0, utils_1.date)(), fileInfo: [], }); compilation.hooks.processAssets.tapPromise({ name, stage: webpack_1.Compilation.PROCESS_ASSETS_STAGE_PRE_PROCESS, }, () => this.preprocess(compilation)); compilation.hooks.processAssets.tapPromise({ name, // we should generate userscript files // only if optimization of source files are complete stage: webpack_1.Compilation.PROCESS_ASSETS_STAGE_SUMMARIZE, }, () => this.process(compilation)); }); compiler.hooks.done.tapPromise(name, () => this.close(compiler)); for (const feature of this.features) { feature.apply(this); } } init(compiler) { return tslib_1.__awaiter(this, void 0, void 0, function* () { yield this.hooks.init.promise(compiler); }); } close(compiler) { return tslib_1.__awaiter(this, void 0, void 0, function* () { yield this.hooks.close.promise(compiler); }); } preprocess(compilation) { return tslib_1.__awaiter(this, void 0, void 0, function* () { const context = this.contexts.get(compilation); /* istanbul ignore next */ if (!context) { return; } context.fileInfo = this.collectFileInfo(compilation); yield this.hooks.preprocess.promise(compilation, context); }); } process(compilation) { return tslib_1.__awaiter(this, void 0, void 0, function* () { const context = this.contexts.get(compilation); /* istanbul ignore next */ if (!context) { return; } yield Promise.all(context.fileInfo.map((fileInfo) => this.emitUserscript(compilation, context, fileInfo))); for (const { originalFile, userjsFile } of context.fileInfo) { if (originalFile !== userjsFile) { compilation.deleteAsset(originalFile); } } yield this.hooks.process.promise(compilation, context); }); } collectFileInfo(compilation) { var _a, _b, _c; const fileInfo = []; for (const entrypoint of compilation.entrypoints.values()) { const chunk = entrypoint.getEntrypointChunk(); for (const originalFile of chunk.files) { let q = originalFile.indexOf('?'); if (q < 0) { q = originalFile.length; } const filepath = originalFile.slice(0, q); const query = originalFile.slice(q); const dirname = node_path_1.default.dirname(filepath); const filename = node_path_1.default.basename(filepath); const basename = filepath.endsWith('.user.js') ? node_path_1.default.basename(filepath, '.user.js') : filepath.endsWith('.js') ? node_path_1.default.basename(filepath, '.js') : filepath; const extname = node_path_1.default.extname(filepath); const userjsFile = node_path_1.default.join(dirname, basename + '.user.js') + query; const metajsFile = node_path_1.default.join(dirname, basename + '.meta.js'); const fileInfoEntry = { chunk, originalFile, userjsFile, metajsFile, filename, dirname, basename, query, extname, }; if ((_c = (_b = (_a = this.options).skip) === null || _b === void 0 ? void 0 : _b.call(_a, fileInfoEntry)) !== null && _c !== void 0 ? _c : extname !== '.js') { continue; } fileInfo.push(fileInfoEntry); } } return fileInfo; } emitUserscript(compilation, context, fileInfo) { return tslib_1.__awaiter(this, void 0, void 0, function* () { const { metajs, proxyScript, i18n } = this.options; const { originalFile, chunk, metajsFile, userjsFile } = fileInfo; const sourceAsset = compilation.getAsset(originalFile); const waterfall = Object.assign(Object.assign({}, context), { fileInfo, compilation }); if (!sourceAsset) { /* istanbul ignore next */ return; } const localizedHeaders = new Map(); const headers = yield this.hooks.headers.promise({}, Object.assign(Object.assign({}, waterfall), { locale: const_1.DEFAULT_LOCALE_KEY })); localizedHeaders.set(const_1.DEFAULT_LOCALE_KEY, headers); if (i18n) { yield Promise.all(Object.keys(i18n).map((locale) => tslib_1.__awaiter(this, void 0, void 0, function* () { localizedHeaders.set(locale, yield this.hooks.headers.promise({}, Object.assign(Object.assign({}, waterfall), { locale }))); }))); } const headersStr = yield this.hooks.renderHeaders.promise(localizedHeaders); const proxyHeaders = proxyScript ? yield this.hooks.proxyHeaders.promise(headers, Object.assign(Object.assign({}, waterfall), { locale: const_1.DEFAULT_LOCALE_KEY })) : undefined; const proxyScriptFile = proxyScript ? yield this.hooks.proxyScriptFile.promise('', Object.assign(Object.assign({}, waterfall), { locale: const_1.DEFAULT_LOCALE_KEY })) : undefined; const proxyHeadersStr = proxyHeaders ? yield this.hooks.renderProxyHeaders.promise(proxyHeaders) : undefined; if (userjsFile !== originalFile) { compilation.emitAsset(userjsFile, new ConcatSource(headersStr, '\n', sourceAsset.source), { minimized: true, }); chunk.files.add(userjsFile); } else { compilation.updateAsset(userjsFile, new ConcatSource(headersStr, '\n', sourceAsset.source), { minimized: true, }); } if (metajs !== false) { compilation.emitAsset(metajsFile, new RawSource(headersStr), { minimized: true, }); chunk.auxiliaryFiles.add(metajsFile); } if (proxyScriptFile !== undefined && proxyHeadersStr !== undefined) { compilation.emitAsset(proxyScriptFile, new RawSource(proxyHeadersStr), { minimized: true, }); chunk.auxiliaryFiles.add(proxyScriptFile); } }); } } exports.UserscriptPlugin = UserscriptPlugin; //# sourceMappingURL=plugin.js.map