From 3bab84bf28998b551e29940dc4bad14fca652fd2 Mon Sep 17 00:00:00 2001 From: James Diefenderfer Date: Fri, 15 Nov 2019 12:47:53 -0800 Subject: [PATCH] add support for dynamic filenames Add support for custom filename template for worker scripts. --- README.md | 6 +++--- index.d.ts | 12 ++++++------ index.js | 35 ++++++++++++++++++++++++----------- languages.js | 4 ---- 4 files changed, 33 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index fd5cd66c..21d4dd91 100644 --- a/README.md +++ b/README.md @@ -46,11 +46,11 @@ monaco.editor.create(document.getElementById('container'), { Options can be passed in to `MonacoWebpackPlugin`. They can be used to generate a smaller editor bundle by selecting only certain languages or only certain editor features: -* `output` (`string`) - custom output path for worker scripts, relative to the main webpack `output.path`. - * default value: `''`. +* `filename` (`string`) - custom filename template for worker scripts, respects the same options as [loader-utils' interpolateName](https://github.com/webpack/loader-utils#interpolatename). Useful for adding content-based hashes so that files can be served with long-lived caching headers. + * default value: `'[name].worker.js'`. * `languages` (`string[]`) - include only a subset of the languages supported. * default value: `['apex', 'azcli', 'bat', 'clojure', 'coffee', 'cpp', 'csharp', 'csp', 'css', 'dockerfile', 'fsharp', 'go', 'handlebars', 'html', 'ini', 'java', 'javascript', 'json', 'less', 'lua', 'markdown', 'msdax', 'mysql', 'objective', 'perl', 'pgsql', 'php', 'postiats', 'powerquery', 'powershell', 'pug', 'python', 'r', 'razor', 'redis', 'redshift', 'ruby', 'rust', 'sb', 'scheme', 'scss', 'shell', 'solidity', 'sql', 'st', 'swift', 'typescript', 'vb', 'xml', 'yaml']`. - + Some languages share the same web worker. If one of the following languages is included, you must also include the language responsible for instantiating their shared worker: | Language | Instantiator | diff --git a/index.d.ts b/index.d.ts index d5ae5fa6..7e29e030 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,12 +1,6 @@ import { Plugin } from 'webpack' interface IMonacoEditorWebpackPluginOpts { - /** - * custom output path for worker scripts, relative to the main webpack `output.path`. - * Defaults to ''. - */ - output?: string; - /** * Include only a subset of the languages supported. */ @@ -17,6 +11,12 @@ interface IMonacoEditorWebpackPluginOpts { * Use e.g. '!contextmenu' to exclude a certain feature. */ features?: string[]; + + /** + * Specify a filename template to use for generated files. + * Use e.g. '[name].worker.[contenthash].js' to include content-based hashes. + */ + filename?: string; } declare class MonacoEditorWebpackPlugin extends Plugin { diff --git a/index.js b/index.js index 60783f03..043348c6 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,8 @@ const path = require('path'); const webpack = require('webpack'); +const loaderUtils = require('loader-utils'); +const fs = require('fs'); + const AddWorkerEntryPointPlugin = require('./plugins/AddWorkerEntryPointPlugin'); const INCLUDE_LOADER_PATH = require.resolve('./loaders/include'); @@ -9,7 +12,6 @@ const EDITOR_MODULE = { worker: { id: 'vs/editor/editor', entry: 'vs/editor/editor.worker', - output: 'editor.worker.js', fallback: undefined }, alias: undefined, @@ -17,10 +19,22 @@ const EDITOR_MODULE = { const LANGUAGES = require('./languages'); const FEATURES = require('./features'); +/** + * Return a resolved path for a given Monaco file. + */ function resolveMonacoPath(filePath) { return require.resolve(path.join('monaco-editor/esm', filePath)); } +/** + * Return the interpolated final filename for a worker, respecting the file name template. + */ +function getWorkerFilename(filename, entry) { + return loaderUtils.interpolateName({resourcePath: entry}, filename, { + content: fs.readFileSync(resolveMonacoPath(entry)) + }); +} + const languagesById = fromPairs( flatMap(toPairs(LANGUAGES), ([id, language]) => [id].concat(language.alias || []).map((label) => [label, mixin({ label }, language)]) @@ -53,23 +67,22 @@ class MonacoWebpackPlugin { constructor(options = {}) { const languages = options.languages || Object.keys(languagesById); const features = getFeaturesIds(options.features || [], featuresById); - const output = options.output || ''; this.options = { languages: languages.map((id) => languagesById[id]).filter(Boolean), features: features.map(id => featuresById[id]).filter(Boolean), - output, + filename: options.filename || "[name].worker.js" }; } apply(compiler) { - const { languages, features, output } = this.options; + const { languages, features, filename } = this.options; const publicPath = getPublicPath(compiler); const modules = [EDITOR_MODULE].concat(languages).concat(features); const workers = modules.map( ({ label, alias, worker }) => worker && (mixin({ label, alias }, worker)) ).filter(Boolean); - const rules = createLoaderRules(languages, features, workers, output, publicPath); - const plugins = createPlugins(workers, output); + const rules = createLoaderRules(languages, features, workers, publicPath, filename); + const plugins = createPlugins(workers, filename); addCompilerRules(compiler, rules); addCompilerPlugins(compiler, plugins); } @@ -89,11 +102,11 @@ function getPublicPath(compiler) { return compiler.options.output && compiler.options.output.publicPath || ''; } -function createLoaderRules(languages, features, workers, outputPath, publicPath) { +function createLoaderRules(languages, features, workers, publicPath, filename) { if (!languages.length && !features.length) { return []; } const languagePaths = flatArr(languages.map(({ entry }) => entry).filter(Boolean)); const featurePaths = flatArr(features.map(({ entry }) => entry).filter(Boolean)); - const workerPaths = fromPairs(workers.map(({ label, output }) => [label, path.join(outputPath, output)])); + const workerPaths = fromPairs(workers.map(({ label, entry }) => [label, getWorkerFilename(filename, entry)])); if (workerPaths['typescript']) { // javascript shares the same worker workerPaths['javascript'] = workerPaths['typescript']; @@ -140,14 +153,14 @@ function createLoaderRules(languages, features, workers, outputPath, publicPath) ]; } -function createPlugins(workers, outputPath) { +function createPlugins(workers, filename) { return ( [] - .concat(uniqBy(workers, ({ id }) => id).map(({ id, entry, output }) => + .concat(uniqBy(workers, ({ id }) => id).map(({ id, entry }) => new AddWorkerEntryPointPlugin({ id, entry: resolveMonacoPath(entry), - filename: path.join(outputPath, output), + filename: getWorkerFilename(filename, entry), plugins: [ new webpack.optimize.LimitChunkCountPlugin({ maxChunks: 1 }), ], diff --git a/languages.js b/languages.js index 6a968cbf..1d88eb95 100644 --- a/languages.js +++ b/languages.js @@ -47,7 +47,6 @@ module.exports = { worker: { id: 'vs/language/css/cssWorker', entry: 'vs/language/css/css.worker', - output: 'css.worker.js', fallback: 'vs/language/css/cssWorker', }, alias: undefined, @@ -80,7 +79,6 @@ module.exports = { worker: { id: 'vs/language/html/htmlWorker', entry: 'vs/language/html/html.worker', - output: 'html.worker.js', fallback: 'vs/language/html/htmlWorker', }, alias: undefined, @@ -105,7 +103,6 @@ module.exports = { worker: { id: 'vs/language/json/jsonWorker', entry: 'vs/language/json/json.worker', - output: 'json.worker.js', fallback: 'vs/language/json/jsonWorker', }, alias: undefined, @@ -258,7 +255,6 @@ module.exports = { worker: { id: 'vs/language/typescript/tsWorker', entry: 'vs/language/typescript/ts.worker', - output: 'typescript.worker.js', fallback: 'vs/language/typescript/tsWorker', }, alias: undefined,