From d5fa133143d3dd894420c6026a18ecde8046bbd2 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Mon, 26 Apr 2021 17:41:33 +0200 Subject: [PATCH 01/25] add sanitizeFileNames option --- docs/999-big-list-of-options.md | 8 ++++++++ src/Chunk.ts | 14 +++++++++++--- src/rollup/types.d.ts | 1 + src/utils/options/normalizeOutputOptions.ts | 1 + test/form/samples/no-sanitize/_config.js | 6 ++++++ test/form/samples/no-sanitize/_expected/amd.js | 9 +++++++++ test/form/samples/no-sanitize/_expected/cjs.js | 9 +++++++++ test/form/samples/no-sanitize/_expected/es.js | 3 +++ test/form/samples/no-sanitize/_expected/iife.js | 10 ++++++++++ test/form/samples/no-sanitize/_expected/system.js | 14 ++++++++++++++ test/form/samples/no-sanitize/_expected/umd.js | 13 +++++++++++++ test/form/samples/no-sanitize/main.js | 3 +++ 12 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 test/form/samples/no-sanitize/_config.js create mode 100644 test/form/samples/no-sanitize/_expected/amd.js create mode 100644 test/form/samples/no-sanitize/_expected/cjs.js create mode 100644 test/form/samples/no-sanitize/_expected/es.js create mode 100644 test/form/samples/no-sanitize/_expected/iife.js create mode 100644 test/form/samples/no-sanitize/_expected/system.js create mode 100644 test/form/samples/no-sanitize/_expected/umd.js create mode 100644 test/form/samples/no-sanitize/main.js diff --git a/docs/999-big-list-of-options.md b/docs/999-big-list-of-options.md index 4e8c7d97432..c56f112a454 100755 --- a/docs/999-big-list-of-options.md +++ b/docs/999-big-list-of-options.md @@ -1320,6 +1320,14 @@ Default: `false` Generate `const` declarations for exports rather than `var` declarations. +#### output.sanitizeFileName +Type: `boolean | (string) => string`
+Default: `true` + +Set to `false` to disable all chunk name sanitizations (removal of `\0`, `?` and `*` characters). + +Alternatively set to a function to allow custom chunk name sanitization. + #### output.strict Type: `boolean`
CLI: `--strict`/`--no-strict`
diff --git a/src/Chunk.ts b/src/Chunk.ts index 0d79ffb1811..0ede0a52eb4 100644 --- a/src/Chunk.ts +++ b/src/Chunk.ts @@ -430,6 +430,14 @@ export default class Chunk { ); } + sanitizeFileName (id: string) { + if (this.outputOptions.sanitizeFileName === false) + return id; + else if (typeof this.outputOptions.sanitizeFileName === 'function') + return this.outputOptions.sanitizeFileName(id); + return sanitizeFileName(id); + } + generateIdPreserveModules( preserveModulesRelativeDir: string, options: NormalizedOutputOptions, @@ -437,7 +445,7 @@ export default class Chunk { unsetOptions: Set ): string { const id = this.orderedModules[0].id; - const sanitizedId = sanitizeFileName(id); + const sanitizedId = this.sanitizeFileName(id); let path: string; if (isAbsolute(id)) { const extension = extname(id); @@ -501,7 +509,7 @@ export default class Chunk { } getChunkName(): string { - return this.name || (this.name = sanitizeFileName(this.getFallbackChunkName())); + return this.name || (this.name = this.sanitizeFileName(this.getFallbackChunkName())); } getExportNames(): string[] { @@ -816,7 +824,7 @@ export default class Chunk { if (fileName) { this.fileName = fileName; } else { - this.name = sanitizeFileName(name || getChunkNameFromModule(facadedModule)); + this.name = this.sanitizeFileName(name || getChunkNameFromModule(facadedModule)); } } diff --git a/src/rollup/types.d.ts b/src/rollup/types.d.ts index b9dda715aa8..efc06e77f57 100644 --- a/src/rollup/types.d.ts +++ b/src/rollup/types.d.ts @@ -693,6 +693,7 @@ export interface NormalizedOutputOptions { preferConst: boolean; preserveModules: boolean; preserveModulesRoot: string | undefined; + sanitizeFileName: boolean | ((string) => string); sourcemap: boolean | 'inline' | 'hidden'; sourcemapExcludeSources: boolean; sourcemapFile: string | undefined; diff --git a/src/utils/options/normalizeOutputOptions.ts b/src/utils/options/normalizeOutputOptions.ts index 5cf14024e29..5218a1cc805 100644 --- a/src/utils/options/normalizeOutputOptions.ts +++ b/src/utils/options/normalizeOutputOptions.ts @@ -66,6 +66,7 @@ export function normalizeOutputOptions( preferConst: (config.preferConst as boolean | undefined) || false, preserveModules, preserveModulesRoot: getPreserveModulesRoot(config), + sanitizeFileName: config.sanitizeFileName as NormalizedOutputOptions['sanitizeFileName'] ?? true, sourcemap: (config.sourcemap as boolean | 'inline' | 'hidden' | undefined) || false, sourcemapExcludeSources: (config.sourcemapExcludeSources as boolean | undefined) || false, sourcemapFile: config.sourcemapFile as string | undefined, diff --git a/test/form/samples/no-sanitize/_config.js b/test/form/samples/no-sanitize/_config.js new file mode 100644 index 00000000000..7bac20fc520 --- /dev/null +++ b/test/form/samples/no-sanitize/_config.js @@ -0,0 +1,6 @@ +module.exports = { + description: 'supports disabling sanitization', + options: { + sanitizeFileName: false + } +}; diff --git a/test/form/samples/no-sanitize/_expected/amd.js b/test/form/samples/no-sanitize/_expected/amd.js new file mode 100644 index 00000000000..3b864a9ba7a --- /dev/null +++ b/test/form/samples/no-sanitize/_expected/amd.js @@ -0,0 +1,9 @@ +define(['?do-not-sanitize'], function (external) { 'use strict'; + + function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } + + var external__default = /*#__PURE__*/_interopDefaultLegacy(external); + + console.log(external__default['default']); + +}); diff --git a/test/form/samples/no-sanitize/_expected/cjs.js b/test/form/samples/no-sanitize/_expected/cjs.js new file mode 100644 index 00000000000..ea81323c4d0 --- /dev/null +++ b/test/form/samples/no-sanitize/_expected/cjs.js @@ -0,0 +1,9 @@ +'use strict'; + +var external = require('\0do-not-sanitize'); + +function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } + +var external__default = /*#__PURE__*/_interopDefaultLegacy(external); + +console.log(external__default['default']); diff --git a/test/form/samples/no-sanitize/_expected/es.js b/test/form/samples/no-sanitize/_expected/es.js new file mode 100644 index 00000000000..c1a1ed0868f --- /dev/null +++ b/test/form/samples/no-sanitize/_expected/es.js @@ -0,0 +1,3 @@ +import external from '\0do-not-sanitize'; + +console.log(external); diff --git a/test/form/samples/no-sanitize/_expected/iife.js b/test/form/samples/no-sanitize/_expected/iife.js new file mode 100644 index 00000000000..db1e304cd12 --- /dev/null +++ b/test/form/samples/no-sanitize/_expected/iife.js @@ -0,0 +1,10 @@ +(function (external) { + 'use strict'; + + function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } + + var external__default = /*#__PURE__*/_interopDefaultLegacy(external); + + console.log(external__default['default']); + +}(external)); diff --git a/test/form/samples/no-sanitize/_expected/system.js b/test/form/samples/no-sanitize/_expected/system.js new file mode 100644 index 00000000000..c3693d2a84a --- /dev/null +++ b/test/form/samples/no-sanitize/_expected/system.js @@ -0,0 +1,14 @@ +System.register(['\0do-not-sanitize'], function () { + 'use strict'; + var external; + return { + setters: [function (module) { + external = module.default; + }], + execute: function () { + + console.log(external); + + } + }; +}); diff --git a/test/form/samples/no-sanitize/_expected/umd.js b/test/form/samples/no-sanitize/_expected/umd.js new file mode 100644 index 00000000000..3c42d2a349e --- /dev/null +++ b/test/form/samples/no-sanitize/_expected/umd.js @@ -0,0 +1,13 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(require(':do-not-sanitize')) : + typeof define === 'function' && define.amd ? define([':do-not-sanitize'], factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.external)); +}(this, (function (external) { 'use strict'; + + function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } + + var external__default = /*#__PURE__*/_interopDefaultLegacy(external); + + console.log(external__default['default']); + +}))); diff --git a/test/form/samples/no-sanitize/main.js b/test/form/samples/no-sanitize/main.js new file mode 100644 index 00000000000..647e6149f14 --- /dev/null +++ b/test/form/samples/no-sanitize/main.js @@ -0,0 +1,3 @@ +import external from ':do-not-sanitize'; + +console.log(external); \ No newline at end of file From bf119c86a1484e708b7972c08a709610d239b594 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Mon, 26 Apr 2021 17:53:15 +0200 Subject: [PATCH 02/25] typo --- test/form/samples/no-sanitize/_config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/form/samples/no-sanitize/_config.js b/test/form/samples/no-sanitize/_config.js index 7bac20fc520..10db81b7477 100644 --- a/test/form/samples/no-sanitize/_config.js +++ b/test/form/samples/no-sanitize/_config.js @@ -1,6 +1,6 @@ module.exports = { description: 'supports disabling sanitization', options: { - sanitizeFileName: false + sanitizeFileNames: false } }; From 9835bd154296bfe94b3f003bd1081e7a382a42cf Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Mon, 26 Apr 2021 18:45:04 +0200 Subject: [PATCH 03/25] fixups --- src/rollup/types.d.ts | 1 + src/utils/options/mergeOptions.ts | 1 + test/form/samples/no-sanitize/_expected/amd.js | 2 +- test/form/samples/no-sanitize/_expected/cjs.js | 2 +- test/form/samples/no-sanitize/_expected/es.js | 2 +- test/form/samples/no-sanitize/_expected/system.js | 2 +- test/misc/optionList.js | 2 +- 7 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/rollup/types.d.ts b/src/rollup/types.d.ts index efc06e77f57..663c9639dde 100644 --- a/src/rollup/types.d.ts +++ b/src/rollup/types.d.ts @@ -649,6 +649,7 @@ export interface OutputOptions { preferConst?: boolean; preserveModules?: boolean; preserveModulesRoot?: string; + sanitizeFileName?: boolean | ((string) => string); sourcemap?: boolean | 'inline' | 'hidden'; sourcemapExcludeSources?: boolean; sourcemapFile?: string; diff --git a/src/utils/options/mergeOptions.ts b/src/utils/options/mergeOptions.ts index 822e26c0f8f..0c0ebeec6be 100644 --- a/src/utils/options/mergeOptions.ts +++ b/src/utils/options/mergeOptions.ts @@ -223,6 +223,7 @@ function mergeOutputOptions( preferConst: getOption('preferConst'), preserveModules: getOption('preserveModules'), preserveModulesRoot: getOption('preserveModulesRoot'), + sanitizeFileName: getOption('sanitizeFileName'), sourcemap: getOption('sourcemap'), sourcemapExcludeSources: getOption('sourcemapExcludeSources'), sourcemapFile: getOption('sourcemapFile'), diff --git a/test/form/samples/no-sanitize/_expected/amd.js b/test/form/samples/no-sanitize/_expected/amd.js index 3b864a9ba7a..862646b86c8 100644 --- a/test/form/samples/no-sanitize/_expected/amd.js +++ b/test/form/samples/no-sanitize/_expected/amd.js @@ -1,4 +1,4 @@ -define(['?do-not-sanitize'], function (external) { 'use strict'; +define([':do-not-sanitize'], function (external) { 'use strict'; function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } diff --git a/test/form/samples/no-sanitize/_expected/cjs.js b/test/form/samples/no-sanitize/_expected/cjs.js index ea81323c4d0..5e15ad515ee 100644 --- a/test/form/samples/no-sanitize/_expected/cjs.js +++ b/test/form/samples/no-sanitize/_expected/cjs.js @@ -1,6 +1,6 @@ 'use strict'; -var external = require('\0do-not-sanitize'); +var external = require(':do-not-sanitize'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } diff --git a/test/form/samples/no-sanitize/_expected/es.js b/test/form/samples/no-sanitize/_expected/es.js index c1a1ed0868f..e8e32d45ac2 100644 --- a/test/form/samples/no-sanitize/_expected/es.js +++ b/test/form/samples/no-sanitize/_expected/es.js @@ -1,3 +1,3 @@ -import external from '\0do-not-sanitize'; +import external from ':do-not-sanitize'; console.log(external); diff --git a/test/form/samples/no-sanitize/_expected/system.js b/test/form/samples/no-sanitize/_expected/system.js index c3693d2a84a..343c933945d 100644 --- a/test/form/samples/no-sanitize/_expected/system.js +++ b/test/form/samples/no-sanitize/_expected/system.js @@ -1,4 +1,4 @@ -System.register(['\0do-not-sanitize'], function () { +System.register([':do-not-sanitize'], function () { 'use strict'; var external; return { diff --git a/test/misc/optionList.js b/test/misc/optionList.js index 72c376d9724..c4b485a6c2a 100644 --- a/test/misc/optionList.js +++ b/test/misc/optionList.js @@ -3,4 +3,4 @@ exports.input = exports.flags = 'acorn, acornInjectPlugins, amd, assetFileNames, banner, c, cache, chunkFileNames, compact, config, context, d, dir, dynamicImportFunction, e, entryFileNames, environment, esModule, experimentalCacheExpiry, exports, extend, external, externalLiveBindings, f, failAfterWarnings, file, footer, format, freeze, g, globals, h, hoistTransitiveImports, i, indent, inlineDynamicImports, input, interop, intro, m, makeAbsoluteExternalsRelative, manualChunks, minifyInternalExports, moduleContext, n, name, namespaceToStringTag, noConflict, o, onwarn, outro, p, paths, perf, plugin, plugins, preferConst, preserveEntrySignatures, preserveModules, preserveModulesRoot, preserveSymlinks, shimMissingExports, silent, sourcemap, sourcemapExcludeSources, sourcemapFile, stdin, strict, strictDeprecations, systemNullSetters, treeshake, v, validate, w, waitForBundleInput, watch'; exports.output = - 'amd, assetFileNames, banner, chunkFileNames, compact, dir, dynamicImportFunction, entryFileNames, esModule, exports, extend, externalLiveBindings, file, footer, format, freeze, globals, hoistTransitiveImports, indent, inlineDynamicImports, interop, intro, manualChunks, minifyInternalExports, name, namespaceToStringTag, noConflict, outro, paths, plugins, preferConst, preserveModules, preserveModulesRoot, sourcemap, sourcemapExcludeSources, sourcemapFile, sourcemapPathTransform, strict, systemNullSetters, validate'; + 'amd, assetFileNames, banner, chunkFileNames, compact, dir, dynamicImportFunction, entryFileNames, esModule, exports, extend, externalLiveBindings, file, footer, format, freeze, globals, hoistTransitiveImports, indent, inlineDynamicImports, interop, intro, manualChunks, minifyInternalExports, name, namespaceToStringTag, noConflict, outro, paths, plugins, preferConst, preserveModules, preserveModulesRoot, sanitizeFileName, sourcemap, sourcemapExcludeSources, sourcemapFile, sourcemapPathTransform, strict, systemNullSetters, validate'; From 41ea93de124fbc2f5d981ffea2e44c69d1e1767a Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Mon, 26 Apr 2021 18:54:06 +0200 Subject: [PATCH 04/25] fixup tests --- test/form/samples/no-sanitize/_config.js | 1 + test/function/samples/output-options-hook/_config.js | 1 + test/misc/optionList.js | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/test/form/samples/no-sanitize/_config.js b/test/form/samples/no-sanitize/_config.js index 10db81b7477..33aa33d6844 100644 --- a/test/form/samples/no-sanitize/_config.js +++ b/test/form/samples/no-sanitize/_config.js @@ -1,6 +1,7 @@ module.exports = { description: 'supports disabling sanitization', options: { + external: [':do-not-sanitize'], sanitizeFileNames: false } }; diff --git a/test/function/samples/output-options-hook/_config.js b/test/function/samples/output-options-hook/_config.js index 904cf55577e..8bb0f44e5b9 100644 --- a/test/function/samples/output-options-hook/_config.js +++ b/test/function/samples/output-options-hook/_config.js @@ -42,6 +42,7 @@ module.exports = { plugins: [], preferConst: false, preserveModules: false, + sanitizeFileName: true, sourcemap: false, sourcemapExcludeSources: false, strict: true, diff --git a/test/misc/optionList.js b/test/misc/optionList.js index c4b485a6c2a..bf94effa264 100644 --- a/test/misc/optionList.js +++ b/test/misc/optionList.js @@ -1,6 +1,6 @@ exports.input = 'acorn, acornInjectPlugins, cache, context, experimentalCacheExpiry, external, inlineDynamicImports, input, makeAbsoluteExternalsRelative, manualChunks, moduleContext, onwarn, perf, plugins, preserveEntrySignatures, preserveModules, preserveSymlinks, shimMissingExports, strictDeprecations, treeshake, watch'; exports.flags = - 'acorn, acornInjectPlugins, amd, assetFileNames, banner, c, cache, chunkFileNames, compact, config, context, d, dir, dynamicImportFunction, e, entryFileNames, environment, esModule, experimentalCacheExpiry, exports, extend, external, externalLiveBindings, f, failAfterWarnings, file, footer, format, freeze, g, globals, h, hoistTransitiveImports, i, indent, inlineDynamicImports, input, interop, intro, m, makeAbsoluteExternalsRelative, manualChunks, minifyInternalExports, moduleContext, n, name, namespaceToStringTag, noConflict, o, onwarn, outro, p, paths, perf, plugin, plugins, preferConst, preserveEntrySignatures, preserveModules, preserveModulesRoot, preserveSymlinks, shimMissingExports, silent, sourcemap, sourcemapExcludeSources, sourcemapFile, stdin, strict, strictDeprecations, systemNullSetters, treeshake, v, validate, w, waitForBundleInput, watch'; + 'acorn, acornInjectPlugins, amd, assetFileNames, banner, c, cache, chunkFileNames, compact, config, context, d, dir, dynamicImportFunction, e, entryFileNames, environment, esModule, experimentalCacheExpiry, exports, extend, external, externalLiveBindings, f, failAfterWarnings, file, footer, format, freeze, g, globals, h, hoistTransitiveImports, i, indent, inlineDynamicImports, input, interop, intro, m, makeAbsoluteExternalsRelative, manualChunks, minifyInternalExports, moduleContext, n, name, namespaceToStringTag, noConflict, o, onwarn, outro, p, paths, perf, plugin, plugins, preferConst, preserveEntrySignatures, preserveModules, preserveModulesRoot, preserveSymlinks, sanitizeFileNames, shimMissingExports, silent, sourcemap, sourcemapExcludeSources, sourcemapFile, stdin, strict, strictDeprecations, systemNullSetters, treeshake, v, validate, w, waitForBundleInput, watch'; exports.output = 'amd, assetFileNames, banner, chunkFileNames, compact, dir, dynamicImportFunction, entryFileNames, esModule, exports, extend, externalLiveBindings, file, footer, format, freeze, globals, hoistTransitiveImports, indent, inlineDynamicImports, interop, intro, manualChunks, minifyInternalExports, name, namespaceToStringTag, noConflict, outro, paths, plugins, preferConst, preserveModules, preserveModulesRoot, sanitizeFileName, sourcemap, sourcemapExcludeSources, sourcemapFile, sourcemapPathTransform, strict, systemNullSetters, validate'; From 5fe93f817545dab3213b6e9f2e081b0639f067c8 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Mon, 26 Apr 2021 18:56:47 +0200 Subject: [PATCH 05/25] lint --- src/Chunk.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Chunk.ts b/src/Chunk.ts index 0ede0a52eb4..52414acc7c5 100644 --- a/src/Chunk.ts +++ b/src/Chunk.ts @@ -430,14 +430,6 @@ export default class Chunk { ); } - sanitizeFileName (id: string) { - if (this.outputOptions.sanitizeFileName === false) - return id; - else if (typeof this.outputOptions.sanitizeFileName === 'function') - return this.outputOptions.sanitizeFileName(id); - return sanitizeFileName(id); - } - generateIdPreserveModules( preserveModulesRelativeDir: string, options: NormalizedOutputOptions, @@ -1252,6 +1244,14 @@ export default class Chunk { } } + sanitizeFileName (id: string) { + if (this.outputOptions.sanitizeFileName === false) + return id; + else if (typeof this.outputOptions.sanitizeFileName === 'function') + return this.outputOptions.sanitizeFileName(id); + return sanitizeFileName(id); + } + private setExternalRenderPaths(options: NormalizedOutputOptions, inputBase: string) { for (const dependency of [...this.dependencies, ...this.dynamicDependencies]) { if (dependency instanceof ExternalModule) { From 7256f414953e33e0c5a780e0c16eb82621acce54 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Mon, 26 Apr 2021 19:03:29 +0200 Subject: [PATCH 06/25] lint --- src/Chunk.ts | 16 ++++++++-------- test/form/samples/no-sanitize/_config.js | 2 +- test/misc/optionList.js | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Chunk.ts b/src/Chunk.ts index 52414acc7c5..0f06403f1b1 100644 --- a/src/Chunk.ts +++ b/src/Chunk.ts @@ -796,6 +796,14 @@ export default class Chunk { return { code, map }; } + sanitizeFileName (id: string) { + if (this.outputOptions.sanitizeFileName === false) + return id; + else if (typeof this.outputOptions.sanitizeFileName === 'function') + return this.outputOptions.sanitizeFileName(id); + return sanitizeFileName(id); + } + private addDependenciesToChunk( moduleDependencies: Set, chunkDependencies: Set @@ -1244,14 +1252,6 @@ export default class Chunk { } } - sanitizeFileName (id: string) { - if (this.outputOptions.sanitizeFileName === false) - return id; - else if (typeof this.outputOptions.sanitizeFileName === 'function') - return this.outputOptions.sanitizeFileName(id); - return sanitizeFileName(id); - } - private setExternalRenderPaths(options: NormalizedOutputOptions, inputBase: string) { for (const dependency of [...this.dependencies, ...this.dynamicDependencies]) { if (dependency instanceof ExternalModule) { diff --git a/test/form/samples/no-sanitize/_config.js b/test/form/samples/no-sanitize/_config.js index 33aa33d6844..087bab94c3d 100644 --- a/test/form/samples/no-sanitize/_config.js +++ b/test/form/samples/no-sanitize/_config.js @@ -2,6 +2,6 @@ module.exports = { description: 'supports disabling sanitization', options: { external: [':do-not-sanitize'], - sanitizeFileNames: false + sanitizeFileName: false } }; diff --git a/test/misc/optionList.js b/test/misc/optionList.js index bf94effa264..b0cadbe0fc9 100644 --- a/test/misc/optionList.js +++ b/test/misc/optionList.js @@ -1,6 +1,6 @@ exports.input = 'acorn, acornInjectPlugins, cache, context, experimentalCacheExpiry, external, inlineDynamicImports, input, makeAbsoluteExternalsRelative, manualChunks, moduleContext, onwarn, perf, plugins, preserveEntrySignatures, preserveModules, preserveSymlinks, shimMissingExports, strictDeprecations, treeshake, watch'; exports.flags = - 'acorn, acornInjectPlugins, amd, assetFileNames, banner, c, cache, chunkFileNames, compact, config, context, d, dir, dynamicImportFunction, e, entryFileNames, environment, esModule, experimentalCacheExpiry, exports, extend, external, externalLiveBindings, f, failAfterWarnings, file, footer, format, freeze, g, globals, h, hoistTransitiveImports, i, indent, inlineDynamicImports, input, interop, intro, m, makeAbsoluteExternalsRelative, manualChunks, minifyInternalExports, moduleContext, n, name, namespaceToStringTag, noConflict, o, onwarn, outro, p, paths, perf, plugin, plugins, preferConst, preserveEntrySignatures, preserveModules, preserveModulesRoot, preserveSymlinks, sanitizeFileNames, shimMissingExports, silent, sourcemap, sourcemapExcludeSources, sourcemapFile, stdin, strict, strictDeprecations, systemNullSetters, treeshake, v, validate, w, waitForBundleInput, watch'; + 'acorn, acornInjectPlugins, amd, assetFileNames, banner, c, cache, chunkFileNames, compact, config, context, d, dir, dynamicImportFunction, e, entryFileNames, environment, esModule, experimentalCacheExpiry, exports, extend, external, externalLiveBindings, f, failAfterWarnings, file, footer, format, freeze, g, globals, h, hoistTransitiveImports, i, indent, inlineDynamicImports, input, interop, intro, m, makeAbsoluteExternalsRelative, manualChunks, minifyInternalExports, moduleContext, n, name, namespaceToStringTag, noConflict, o, onwarn, outro, p, paths, perf, plugin, plugins, preferConst, preserveEntrySignatures, preserveModules, preserveModulesRoot, preserveSymlinks, sanitizeFileName, shimMissingExports, silent, sourcemap, sourcemapExcludeSources, sourcemapFile, stdin, strict, strictDeprecations, systemNullSetters, treeshake, v, validate, w, waitForBundleInput, watch'; exports.output = 'amd, assetFileNames, banner, chunkFileNames, compact, dir, dynamicImportFunction, entryFileNames, esModule, exports, extend, externalLiveBindings, file, footer, format, freeze, globals, hoistTransitiveImports, indent, inlineDynamicImports, interop, intro, manualChunks, minifyInternalExports, name, namespaceToStringTag, noConflict, outro, paths, plugins, preferConst, preserveModules, preserveModulesRoot, sanitizeFileName, sourcemap, sourcemapExcludeSources, sourcemapFile, sourcemapPathTransform, strict, systemNullSetters, validate'; From 847e24b394e4ecb8aa293e4666dd802338854f52 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Mon, 26 Apr 2021 19:07:51 +0200 Subject: [PATCH 07/25] fixup output --- test/form/samples/no-sanitize/_config.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/form/samples/no-sanitize/_config.js b/test/form/samples/no-sanitize/_config.js index 087bab94c3d..d3f5f0f8f12 100644 --- a/test/form/samples/no-sanitize/_config.js +++ b/test/form/samples/no-sanitize/_config.js @@ -2,6 +2,8 @@ module.exports = { description: 'supports disabling sanitization', options: { external: [':do-not-sanitize'], - sanitizeFileName: false + output: { + sanitizeFileName: false + } } }; From a9e3630ffb0a68ff1bfa55e8b572be7120f58950 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Mon, 26 Apr 2021 19:12:05 +0200 Subject: [PATCH 08/25] fixup types --- src/rollup/types.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rollup/types.d.ts b/src/rollup/types.d.ts index 663c9639dde..02ee1637c79 100644 --- a/src/rollup/types.d.ts +++ b/src/rollup/types.d.ts @@ -649,7 +649,7 @@ export interface OutputOptions { preferConst?: boolean; preserveModules?: boolean; preserveModulesRoot?: string; - sanitizeFileName?: boolean | ((string) => string); + sanitizeFileName?: boolean | ((fileName: string) => string); sourcemap?: boolean | 'inline' | 'hidden'; sourcemapExcludeSources?: boolean; sourcemapFile?: string; @@ -694,7 +694,7 @@ export interface NormalizedOutputOptions { preferConst: boolean; preserveModules: boolean; preserveModulesRoot: string | undefined; - sanitizeFileName: boolean | ((string) => string); + sanitizeFileName: boolean | ((fileName: string) => string); sourcemap: boolean | 'inline' | 'hidden'; sourcemapExcludeSources: boolean; sourcemapFile: string | undefined; From 9a3668d594687a2bae8b3e1b08312c390186229e Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Mon, 26 Apr 2021 19:30:21 +0200 Subject: [PATCH 09/25] test --- test/form/samples/no-sanitize/_config.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/form/samples/no-sanitize/_config.js b/test/form/samples/no-sanitize/_config.js index d3f5f0f8f12..c16deb8d650 100644 --- a/test/form/samples/no-sanitize/_config.js +++ b/test/form/samples/no-sanitize/_config.js @@ -2,8 +2,5 @@ module.exports = { description: 'supports disabling sanitization', options: { external: [':do-not-sanitize'], - output: { - sanitizeFileName: false - } } }; From 42673012538d103920539ba021f5a24b67e63e15 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Mon, 26 Apr 2021 21:04:02 +0200 Subject: [PATCH 10/25] fixup test --- src/Chunk.ts | 18 ++++++++-------- src/utils/relativeId.ts | 4 ++-- src/utils/renderNamePattern.ts | 6 ++++-- test/form/samples/no-sanitize/_config.js | 6 ------ .../form/samples/no-sanitize/_expected/amd.js | 9 -------- .../form/samples/no-sanitize/_expected/cjs.js | 9 -------- test/form/samples/no-sanitize/_expected/es.js | 3 --- .../samples/no-sanitize/_expected/iife.js | 10 --------- .../samples/no-sanitize/_expected/system.js | 14 ------------- .../form/samples/no-sanitize/_expected/umd.js | 13 ------------ test/form/samples/no-sanitize/main.js | 3 --- test/hooks/index.js | 21 +++++++++++++++++++ 12 files changed, 36 insertions(+), 80 deletions(-) delete mode 100644 test/form/samples/no-sanitize/_config.js delete mode 100644 test/form/samples/no-sanitize/_expected/amd.js delete mode 100644 test/form/samples/no-sanitize/_expected/cjs.js delete mode 100644 test/form/samples/no-sanitize/_expected/es.js delete mode 100644 test/form/samples/no-sanitize/_expected/iife.js delete mode 100644 test/form/samples/no-sanitize/_expected/system.js delete mode 100644 test/form/samples/no-sanitize/_expected/umd.js delete mode 100644 test/form/samples/no-sanitize/main.js diff --git a/src/Chunk.ts b/src/Chunk.ts index 0f06403f1b1..5c089a43648 100644 --- a/src/Chunk.ts +++ b/src/Chunk.ts @@ -210,6 +210,7 @@ export default class Chunk { } = Object.create(null); private renderedModuleSources = new Map(); private renderedSource: MagicStringBundle | null = null; + private sanitizeFileName: (id: string) => string; private sortedExportNames: string[] | null = null; private strictFacade = false; private usedModules: Module[] = undefined as any; @@ -254,6 +255,12 @@ export default class Chunk { } } this.suggestedVariableName = makeLegal(this.generateVariableName()); + if (this.outputOptions.sanitizeFileName === false) + this.sanitizeFileName = (id) => id; + else if (typeof this.outputOptions.sanitizeFileName === 'function') + this.sanitizeFileName = this.outputOptions.sanitizeFileName; + else + this.sanitizeFileName = sanitizeFileName; } canModuleBeFacade(module: Module, exposedVariables: Set): boolean { @@ -424,7 +431,8 @@ export default class Chunk { ? this.computeContentHashWithDependencies(addons, options, existingNames) : '[hash]', name: () => this.getChunkName() - } + }, + this.sanitizeFileName ), existingNames ); @@ -796,14 +804,6 @@ export default class Chunk { return { code, map }; } - sanitizeFileName (id: string) { - if (this.outputOptions.sanitizeFileName === false) - return id; - else if (typeof this.outputOptions.sanitizeFileName === 'function') - return this.outputOptions.sanitizeFileName(id); - return sanitizeFileName(id); - } - private addDependenciesToChunk( moduleDependencies: Set, chunkDependencies: Set diff --git a/src/utils/relativeId.ts b/src/utils/relativeId.ts index e3d3d36475b..aa6451c93a5 100644 --- a/src/utils/relativeId.ts +++ b/src/utils/relativeId.ts @@ -1,5 +1,5 @@ import { basename, extname, isAbsolute, relative, resolve } from './path'; -import { sanitizeFileName } from './sanitizeFileName'; +import { sanitizeFileName as defaultSanitizeFileName } from './sanitizeFileName'; export function getAliasName(id: string) { const base = basename(id); @@ -11,7 +11,7 @@ export default function relativeId(id: string) { return relative(resolve(), id); } -export function isPlainPathFragment(name: string) { +export function isPlainPathFragment(name: string, sanitizeFileName = defaultSanitizeFileName) { // not starting with "/", "./", "../" return ( name[0] !== '/' && diff --git a/src/utils/renderNamePattern.ts b/src/utils/renderNamePattern.ts index 13690decc71..d20e70d3d03 100644 --- a/src/utils/renderNamePattern.ts +++ b/src/utils/renderNamePattern.ts @@ -1,13 +1,15 @@ import { errFailedValidation, error } from './error'; import { extname } from './path'; import { isPlainPathFragment } from './relativeId'; +import { sanitizeFileName as defaultSanitizeFileName } from './sanitizeFileName'; export function renderNamePattern( pattern: string, patternName: string, - replacements: { [name: string]: () => string } + replacements: { [name: string]: () => string }, + sanitizeFileName = defaultSanitizeFileName ) { - if (!isPlainPathFragment(pattern)) + if (!isPlainPathFragment(pattern, sanitizeFileName)) return error( errFailedValidation( `Invalid pattern "${pattern}" for "${patternName}", patterns can be neither absolute nor relative paths and must not contain invalid characters.` diff --git a/test/form/samples/no-sanitize/_config.js b/test/form/samples/no-sanitize/_config.js deleted file mode 100644 index c16deb8d650..00000000000 --- a/test/form/samples/no-sanitize/_config.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - description: 'supports disabling sanitization', - options: { - external: [':do-not-sanitize'], - } -}; diff --git a/test/form/samples/no-sanitize/_expected/amd.js b/test/form/samples/no-sanitize/_expected/amd.js deleted file mode 100644 index 862646b86c8..00000000000 --- a/test/form/samples/no-sanitize/_expected/amd.js +++ /dev/null @@ -1,9 +0,0 @@ -define([':do-not-sanitize'], function (external) { 'use strict'; - - function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } - - var external__default = /*#__PURE__*/_interopDefaultLegacy(external); - - console.log(external__default['default']); - -}); diff --git a/test/form/samples/no-sanitize/_expected/cjs.js b/test/form/samples/no-sanitize/_expected/cjs.js deleted file mode 100644 index 5e15ad515ee..00000000000 --- a/test/form/samples/no-sanitize/_expected/cjs.js +++ /dev/null @@ -1,9 +0,0 @@ -'use strict'; - -var external = require(':do-not-sanitize'); - -function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } - -var external__default = /*#__PURE__*/_interopDefaultLegacy(external); - -console.log(external__default['default']); diff --git a/test/form/samples/no-sanitize/_expected/es.js b/test/form/samples/no-sanitize/_expected/es.js deleted file mode 100644 index e8e32d45ac2..00000000000 --- a/test/form/samples/no-sanitize/_expected/es.js +++ /dev/null @@ -1,3 +0,0 @@ -import external from ':do-not-sanitize'; - -console.log(external); diff --git a/test/form/samples/no-sanitize/_expected/iife.js b/test/form/samples/no-sanitize/_expected/iife.js deleted file mode 100644 index db1e304cd12..00000000000 --- a/test/form/samples/no-sanitize/_expected/iife.js +++ /dev/null @@ -1,10 +0,0 @@ -(function (external) { - 'use strict'; - - function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } - - var external__default = /*#__PURE__*/_interopDefaultLegacy(external); - - console.log(external__default['default']); - -}(external)); diff --git a/test/form/samples/no-sanitize/_expected/system.js b/test/form/samples/no-sanitize/_expected/system.js deleted file mode 100644 index 343c933945d..00000000000 --- a/test/form/samples/no-sanitize/_expected/system.js +++ /dev/null @@ -1,14 +0,0 @@ -System.register([':do-not-sanitize'], function () { - 'use strict'; - var external; - return { - setters: [function (module) { - external = module.default; - }], - execute: function () { - - console.log(external); - - } - }; -}); diff --git a/test/form/samples/no-sanitize/_expected/umd.js b/test/form/samples/no-sanitize/_expected/umd.js deleted file mode 100644 index 3c42d2a349e..00000000000 --- a/test/form/samples/no-sanitize/_expected/umd.js +++ /dev/null @@ -1,13 +0,0 @@ -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(require(':do-not-sanitize')) : - typeof define === 'function' && define.amd ? define([':do-not-sanitize'], factory) : - (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.external)); -}(this, (function (external) { 'use strict'; - - function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } - - var external__default = /*#__PURE__*/_interopDefaultLegacy(external); - - console.log(external__default['default']); - -}))); diff --git a/test/form/samples/no-sanitize/main.js b/test/form/samples/no-sanitize/main.js deleted file mode 100644 index 647e6149f14..00000000000 --- a/test/form/samples/no-sanitize/main.js +++ /dev/null @@ -1,3 +0,0 @@ -import external from ':do-not-sanitize'; - -console.log(external); \ No newline at end of file diff --git a/test/hooks/index.js b/test/hooks/index.js index 30e26bd75a8..dbca152d9c9 100644 --- a/test/hooks/index.js +++ b/test/hooks/index.js @@ -1100,6 +1100,27 @@ describe('hooks', () => { }); }); + it('supports disabling sanitization for in-memory / in-browser / non-fs builds', () => { + return rollup + .rollup({ + input: 'input.js', + plugins: [{ + resolveId: id => id, + load: () => `export default 5` + }] + }) + .then(bundle => { + return bundle.generate({ + format: 'es', + sanitizeFileName: false, + entryFileNames: 'test:[name]' + }); + }) + .then(({ output }) => { + assert.strictEqual(output[0].fileName, 'test:input'); + }); + }); + describe('deprecated', () => { it('caches chunk emission in transform hook', () => { let cache; From 8f36785055d0017685933933c9e4b87b031cc5e7 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Tue, 27 Apr 2021 14:39:59 +0200 Subject: [PATCH 11/25] pr feedback --- src/Chunk.ts | 16 ++++------------ src/rollup/types.d.ts | 2 +- src/utils/options/normalizeOutputOptions.ts | 3 ++- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/src/Chunk.ts b/src/Chunk.ts index 5c089a43648..b4d86e69d2f 100644 --- a/src/Chunk.ts +++ b/src/Chunk.ts @@ -56,7 +56,6 @@ import relativeId, { getAliasName } from './utils/relativeId'; import renderChunk from './utils/renderChunk'; import { RenderOptions } from './utils/renderHelpers'; import { makeUnique, renderNamePattern } from './utils/renderNamePattern'; -import { sanitizeFileName } from './utils/sanitizeFileName'; import { timeEnd, timeStart } from './utils/timers'; import { MISSING_EXPORT_SHIM_VARIABLE } from './utils/variableNames'; @@ -210,7 +209,6 @@ export default class Chunk { } = Object.create(null); private renderedModuleSources = new Map(); private renderedSource: MagicStringBundle | null = null; - private sanitizeFileName: (id: string) => string; private sortedExportNames: string[] | null = null; private strictFacade = false; private usedModules: Module[] = undefined as any; @@ -255,12 +253,6 @@ export default class Chunk { } } this.suggestedVariableName = makeLegal(this.generateVariableName()); - if (this.outputOptions.sanitizeFileName === false) - this.sanitizeFileName = (id) => id; - else if (typeof this.outputOptions.sanitizeFileName === 'function') - this.sanitizeFileName = this.outputOptions.sanitizeFileName; - else - this.sanitizeFileName = sanitizeFileName; } canModuleBeFacade(module: Module, exposedVariables: Set): boolean { @@ -432,7 +424,7 @@ export default class Chunk { : '[hash]', name: () => this.getChunkName() }, - this.sanitizeFileName + this.outputOptions.sanitizeFileName ), existingNames ); @@ -445,7 +437,7 @@ export default class Chunk { unsetOptions: Set ): string { const id = this.orderedModules[0].id; - const sanitizedId = this.sanitizeFileName(id); + const sanitizedId = this.outputOptions.sanitizeFileName(id); let path: string; if (isAbsolute(id)) { const extension = extname(id); @@ -509,7 +501,7 @@ export default class Chunk { } getChunkName(): string { - return this.name || (this.name = this.sanitizeFileName(this.getFallbackChunkName())); + return this.name || (this.name = this.outputOptions.sanitizeFileName(this.getFallbackChunkName())); } getExportNames(): string[] { @@ -824,7 +816,7 @@ export default class Chunk { if (fileName) { this.fileName = fileName; } else { - this.name = this.sanitizeFileName(name || getChunkNameFromModule(facadedModule)); + this.name = this.outputOptions.sanitizeFileName(name || getChunkNameFromModule(facadedModule)); } } diff --git a/src/rollup/types.d.ts b/src/rollup/types.d.ts index 02ee1637c79..8c949b54c2d 100644 --- a/src/rollup/types.d.ts +++ b/src/rollup/types.d.ts @@ -694,7 +694,7 @@ export interface NormalizedOutputOptions { preferConst: boolean; preserveModules: boolean; preserveModulesRoot: string | undefined; - sanitizeFileName: boolean | ((fileName: string) => string); + sanitizeFileName: (fileName: string) => string; sourcemap: boolean | 'inline' | 'hidden'; sourcemapExcludeSources: boolean; sourcemapFile: string | undefined; diff --git a/src/utils/options/normalizeOutputOptions.ts b/src/utils/options/normalizeOutputOptions.ts index 5218a1cc805..431f4f196b4 100644 --- a/src/utils/options/normalizeOutputOptions.ts +++ b/src/utils/options/normalizeOutputOptions.ts @@ -14,6 +14,7 @@ import { import { ensureArray } from '../ensureArray'; import { errInvalidExportOptionValue, error, warnDeprecation } from '../error'; import { resolve } from '../path'; +import { sanitizeFileName as defaultSanitizeFileName } from '../sanitizeFileName'; import { GenericConfigObject, warnUnknownOptions } from './options'; export function normalizeOutputOptions( @@ -66,7 +67,7 @@ export function normalizeOutputOptions( preferConst: (config.preferConst as boolean | undefined) || false, preserveModules, preserveModulesRoot: getPreserveModulesRoot(config), - sanitizeFileName: config.sanitizeFileName as NormalizedOutputOptions['sanitizeFileName'] ?? true, + sanitizeFileName: (typeof config.sanitizeFileName === 'function' ? config.sanitizeFileNames : config.sanitizeFileName === false ? (id) => id : defaultSanitizeFileName) as NormalizedOutputOptions['sanitizeFileName'], sourcemap: (config.sourcemap as boolean | 'inline' | 'hidden' | undefined) || false, sourcemapExcludeSources: (config.sourcemapExcludeSources as boolean | undefined) || false, sourcemapFile: config.sourcemapFile as string | undefined, From cfb34259485209d95b9e8689ca01e155f67a52a7 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Tue, 27 Apr 2021 14:51:46 +0200 Subject: [PATCH 12/25] remove sanitize defaults --- src/Bundle.ts | 2 +- src/Chunk.ts | 3 ++- src/utils/FileEmitter.ts | 25 +++++++++++---------- src/utils/PluginDriver.ts | 3 ++- src/utils/options/normalizeOutputOptions.ts | 2 +- src/utils/relativeId.ts | 3 +-- src/utils/renderNamePattern.ts | 5 ++--- 7 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/Bundle.ts b/src/Bundle.ts index 65f97532c52..01e731fa2a7 100644 --- a/src/Bundle.ts +++ b/src/Bundle.ts @@ -43,7 +43,7 @@ export default class Bundle { const outputBundle: OutputBundleWithPlaceholders = Object.create(null); this.pluginDriver.setOutputBundle( outputBundle, - this.outputOptions.assetFileNames, + this.outputOptions, this.facadeChunkByModule ); try { diff --git a/src/Chunk.ts b/src/Chunk.ts index b4d86e69d2f..e199cb27d6f 100644 --- a/src/Chunk.ts +++ b/src/Chunk.ts @@ -455,7 +455,8 @@ export default class Chunk { extname: () => extension, format: () => options.format as string, name: () => this.getChunkName() - } + }, + this.outputOptions.sanitizeFileName ); const currentPath = `${currentDir}/${fileName}`; const { preserveModulesRoot } = options; diff --git a/src/utils/FileEmitter.ts b/src/utils/FileEmitter.ts index 2c63cbd79f5..cce6d53d97f 100644 --- a/src/utils/FileEmitter.ts +++ b/src/utils/FileEmitter.ts @@ -5,8 +5,8 @@ import { EmittedChunk, FilePlaceholder, NormalizedInputOptions, + NormalizedOutputOptions, OutputBundleWithPlaceholders, - PreRenderedAsset, WarningHandler } from '../rollup/types'; import { BuildPhase } from './buildPhase'; @@ -29,8 +29,8 @@ import { isPlainPathFragment } from './relativeId'; import { makeUnique, renderNamePattern } from './renderNamePattern'; interface OutputSpecificFileData { - assetFileNames: string | ((assetInfo: PreRenderedAsset) => string); bundle: OutputBundleWithPlaceholders; + outputOptions: NormalizedOutputOptions; } function generateAssetFileName( @@ -41,9 +41,9 @@ function generateAssetFileName( const emittedName = name || 'asset'; return makeUnique( renderNamePattern( - typeof output.assetFileNames === 'function' - ? output.assetFileNames({ name, source, type: 'asset' }) - : output.assetFileNames, + typeof output.outputOptions.assetFileNames === 'function' + ? output.outputOptions.assetFileNames({ name, source, type: 'asset' }) + : output.outputOptions.assetFileNames, 'output.assetFileNames', { hash() { @@ -56,7 +56,8 @@ function generateAssetFileName( ext: () => extname(emittedName).substr(1), extname: () => extname(emittedName), name: () => emittedName.substr(0, emittedName.length - extname(emittedName).length) - } + }, + output.outputOptions.sanitizeFileName ), output.bundle ); @@ -113,10 +114,10 @@ function hasValidType( function hasValidName(emittedFile: { type: 'asset' | 'chunk'; [key: string]: unknown; -}): emittedFile is EmittedFile { +}, sanitizeFileName: (fileName: string) => string): emittedFile is EmittedFile { const validatedName = emittedFile.fileName || emittedFile.name; return ( - !validatedName || (typeof validatedName === 'string' && isPlainPathFragment(validatedName)) + !validatedName || (typeof validatedName === 'string' && isPlainPathFragment(validatedName, sanitizeFileName)) ); } @@ -186,7 +187,7 @@ export class FileEmitter { ) ); } - if (!hasValidName(emittedFile)) { + if (!hasValidName(emittedFile, this.output!.outputOptions.sanitizeFileName)) { return error( errFailedValidation( `The "fileName" or "name" properties of emitted files must be strings that are neither absolute nor relative paths and do not contain invalid characters, received "${ @@ -235,12 +236,12 @@ export class FileEmitter { public setOutputBundle = ( outputBundle: OutputBundleWithPlaceholders, - assetFileNames: string | ((assetInfo: PreRenderedAsset) => string), + outputOptions: NormalizedOutputOptions, facadeChunkByModule: Map ): void => { this.output = { - assetFileNames, - bundle: outputBundle + bundle: outputBundle, + outputOptions }; this.facadeChunkByModule = facadeChunkByModule; for (const emittedFile of this.filesByReferenceId.values()) { diff --git a/src/utils/PluginDriver.ts b/src/utils/PluginDriver.ts index 61dfce57095..aa9d87d74a5 100644 --- a/src/utils/PluginDriver.ts +++ b/src/utils/PluginDriver.ts @@ -7,6 +7,7 @@ import { EmitFile, FirstPluginHooks, NormalizedInputOptions, + NormalizedOutputOptions, OutputBundleWithPlaceholders, OutputPluginHooks, ParallelPluginHooks, @@ -74,7 +75,7 @@ export class PluginDriver { public getFileName: (fileReferenceId: string) => string; public setOutputBundle: ( outputBundle: OutputBundleWithPlaceholders, - assetFileNames: string | ((assetInfo: PreRenderedAsset) => string), + outputOptions: NormalizedOutputOptions, facadeChunkByModule: Map ) => void; diff --git a/src/utils/options/normalizeOutputOptions.ts b/src/utils/options/normalizeOutputOptions.ts index 431f4f196b4..1531b6ea8d4 100644 --- a/src/utils/options/normalizeOutputOptions.ts +++ b/src/utils/options/normalizeOutputOptions.ts @@ -67,7 +67,7 @@ export function normalizeOutputOptions( preferConst: (config.preferConst as boolean | undefined) || false, preserveModules, preserveModulesRoot: getPreserveModulesRoot(config), - sanitizeFileName: (typeof config.sanitizeFileName === 'function' ? config.sanitizeFileNames : config.sanitizeFileName === false ? (id) => id : defaultSanitizeFileName) as NormalizedOutputOptions['sanitizeFileName'], + sanitizeFileName: (typeof config.sanitizeFileName === 'function' ? config.sanitizeFileName : config.sanitizeFileName === false ? (id) => id : defaultSanitizeFileName) as NormalizedOutputOptions['sanitizeFileName'], sourcemap: (config.sourcemap as boolean | 'inline' | 'hidden' | undefined) || false, sourcemapExcludeSources: (config.sourcemapExcludeSources as boolean | undefined) || false, sourcemapFile: config.sourcemapFile as string | undefined, diff --git a/src/utils/relativeId.ts b/src/utils/relativeId.ts index aa6451c93a5..f878172db5c 100644 --- a/src/utils/relativeId.ts +++ b/src/utils/relativeId.ts @@ -1,5 +1,4 @@ import { basename, extname, isAbsolute, relative, resolve } from './path'; -import { sanitizeFileName as defaultSanitizeFileName } from './sanitizeFileName'; export function getAliasName(id: string) { const base = basename(id); @@ -11,7 +10,7 @@ export default function relativeId(id: string) { return relative(resolve(), id); } -export function isPlainPathFragment(name: string, sanitizeFileName = defaultSanitizeFileName) { +export function isPlainPathFragment(name: string, sanitizeFileName: (fileName: string) => string) { // not starting with "/", "./", "../" return ( name[0] !== '/' && diff --git a/src/utils/renderNamePattern.ts b/src/utils/renderNamePattern.ts index d20e70d3d03..ca474d0639b 100644 --- a/src/utils/renderNamePattern.ts +++ b/src/utils/renderNamePattern.ts @@ -1,13 +1,12 @@ import { errFailedValidation, error } from './error'; import { extname } from './path'; import { isPlainPathFragment } from './relativeId'; -import { sanitizeFileName as defaultSanitizeFileName } from './sanitizeFileName'; export function renderNamePattern( pattern: string, patternName: string, replacements: { [name: string]: () => string }, - sanitizeFileName = defaultSanitizeFileName + sanitizeFileName: (fileName: string) => string ) { if (!isPlainPathFragment(pattern, sanitizeFileName)) return error( @@ -22,7 +21,7 @@ export function renderNamePattern( ); } const replacement = replacements[type](); - if (!isPlainPathFragment(replacement)) + if (!isPlainPathFragment(replacement, sanitizeFileName)) return error( errFailedValidation( `Invalid substitution "${replacement}" for placeholder "[${type}]" in "${patternName}" pattern, can be neither absolute nor relative path.` From 6f18e84f190c83a2917fef74f7a76d6654d5a54c Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Tue, 27 Apr 2021 14:53:30 +0200 Subject: [PATCH 13/25] fixup test --- test/function/samples/output-options-hook/_config.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/function/samples/output-options-hook/_config.js b/test/function/samples/output-options-hook/_config.js index 8bb0f44e5b9..904cf55577e 100644 --- a/test/function/samples/output-options-hook/_config.js +++ b/test/function/samples/output-options-hook/_config.js @@ -42,7 +42,6 @@ module.exports = { plugins: [], preferConst: false, preserveModules: false, - sanitizeFileName: true, sourcemap: false, sourcemapExcludeSources: false, strict: true, From 630270216b276f28b63a7407e23f669d95debb92 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Tue, 27 Apr 2021 15:05:10 +0200 Subject: [PATCH 14/25] fixups --- src/utils/FileEmitter.ts | 53 ++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/src/utils/FileEmitter.ts b/src/utils/FileEmitter.ts index cce6d53d97f..68c7255a102 100644 --- a/src/utils/FileEmitter.ts +++ b/src/utils/FileEmitter.ts @@ -28,22 +28,18 @@ import { extname } from './path'; import { isPlainPathFragment } from './relativeId'; import { makeUnique, renderNamePattern } from './renderNamePattern'; -interface OutputSpecificFileData { - bundle: OutputBundleWithPlaceholders; - outputOptions: NormalizedOutputOptions; -} - function generateAssetFileName( name: string | undefined, source: string | Uint8Array, - output: OutputSpecificFileData + outputOptions: NormalizedOutputOptions, + bundle: OutputBundleWithPlaceholders ): string { const emittedName = name || 'asset'; return makeUnique( renderNamePattern( - typeof output.outputOptions.assetFileNames === 'function' - ? output.outputOptions.assetFileNames({ name, source, type: 'asset' }) - : output.outputOptions.assetFileNames, + typeof outputOptions.assetFileNames === 'function' + ? outputOptions.assetFileNames({ name, source, type: 'asset' }) + : outputOptions.assetFileNames, 'output.assetFileNames', { hash() { @@ -57,9 +53,9 @@ function generateAssetFileName( extname: () => extname(emittedName), name: () => emittedName.substr(0, emittedName.length - extname(emittedName).length) }, - output.outputOptions.sanitizeFileName + outputOptions.sanitizeFileName ), - output.bundle + bundle ); } @@ -156,15 +152,18 @@ function getChunkFileName( } export class FileEmitter { + private bundle: OutputBundleWithPlaceholders | null = null; private facadeChunkByModule: Map | null = null; private filesByReferenceId: Map; - private output: OutputSpecificFileData | null = null; + private readonly outputOptions: NormalizedOutputOptions; constructor( private readonly graph: Graph, private readonly options: NormalizedInputOptions, + outputOptions: NormalizedOutputOptions, baseFileEmitter?: FileEmitter ) { + this.outputOptions = outputOptions; this.filesByReferenceId = baseFileEmitter ? new Map(baseFileEmitter.filesByReferenceId) : new Map(); @@ -187,7 +186,7 @@ export class FileEmitter { ) ); } - if (!hasValidName(emittedFile, this.output!.outputOptions.sanitizeFileName)) { + if (!hasValidName(emittedFile, this.outputOptions.sanitizeFileName)) { return error( errFailedValidation( `The "fileName" or "name" properties of emitted files must be strings that are neither absolute nor relative paths and do not contain invalid characters, received "${ @@ -227,8 +226,8 @@ export class FileEmitter { return error(errAssetSourceAlreadySet(consumedFile.name || referenceId)); } const source = getValidSource(requestedSource, consumedFile, referenceId); - if (this.output) { - this.finalizeAsset(consumedFile, source, referenceId, this.output); + if (this.bundle) { + this.finalizeAsset(consumedFile, source, referenceId, this.bundle); } else { consumedFile.source = source; } @@ -236,22 +235,18 @@ export class FileEmitter { public setOutputBundle = ( outputBundle: OutputBundleWithPlaceholders, - outputOptions: NormalizedOutputOptions, facadeChunkByModule: Map ): void => { - this.output = { - bundle: outputBundle, - outputOptions - }; + this.bundle = outputBundle; this.facadeChunkByModule = facadeChunkByModule; for (const emittedFile of this.filesByReferenceId.values()) { if (emittedFile.fileName) { - reserveFileNameInBundle(emittedFile.fileName, this.output.bundle, this.options.onwarn); + reserveFileNameInBundle(emittedFile.fileName, this.bundle, this.options.onwarn); } } for (const [referenceId, consumedFile] of this.filesByReferenceId.entries()) { if (consumedFile.type === 'asset' && consumedFile.source !== undefined) { - this.finalizeAsset(consumedFile, consumedFile.source, referenceId, this.output); + this.finalizeAsset(consumedFile, consumedFile.source, referenceId, this.bundle); } } }; @@ -286,12 +281,12 @@ export class FileEmitter { consumedAsset, emittedAsset.fileName || emittedAsset.name || emittedAsset.type ); - if (this.output) { + if (this.bundle) { if (emittedAsset.fileName) { - reserveFileNameInBundle(emittedAsset.fileName, this.output.bundle, this.options.onwarn); + reserveFileNameInBundle(emittedAsset.fileName, this.bundle, this.options.onwarn); } if (source !== undefined) { - this.finalizeAsset(consumedAsset, source, referenceId, this.output); + this.finalizeAsset(consumedAsset, source, referenceId, this.bundle); } } return referenceId; @@ -329,18 +324,18 @@ export class FileEmitter { consumedFile: ConsumedFile, source: string | Uint8Array, referenceId: string, - output: OutputSpecificFileData + bundle: OutputBundleWithPlaceholders ): void { const fileName = consumedFile.fileName || - findExistingAssetFileNameWithSource(output.bundle, source) || - generateAssetFileName(consumedFile.name, source, output); + findExistingAssetFileNameWithSource(bundle, source) || + generateAssetFileName(consumedFile.name, source, this.outputOptions, bundle); // We must not modify the original assets to avoid interaction between outputs const assetWithFileName = { ...consumedFile, source, fileName }; this.filesByReferenceId.set(referenceId, assetWithFileName); const options = this.options; - output.bundle[fileName] = { + bundle[fileName] = { fileName, name: consumedFile.name, get isAsset(): true { From 5eb05d3b4d35e5f2592d767d4169b0569ea8040b Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Tue, 27 Apr 2021 15:28:01 +0200 Subject: [PATCH 15/25] fixup emitFile binding --- src/utils/PluginContext.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/PluginContext.ts b/src/utils/PluginContext.ts index 70ca86d58c3..87214faffdf 100644 --- a/src/utils/PluginContext.ts +++ b/src/utils/PluginContext.ts @@ -103,7 +103,7 @@ export function getPluginContext( true, options ), - emitFile: fileEmitter.emitFile, + emitFile: fileEmitter.emitFile.bind(fileEmitter), error(err): never { return throwPluginError(err, plugin.name); }, From 6cdbebd6af18a8fa66b13a6b1e2ce1c373fb9604 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Tue, 27 Apr 2021 15:39:25 +0200 Subject: [PATCH 16/25] fixup wiring --- src/utils/FileEmitter.ts | 10 +++++----- src/utils/PluginDriver.ts | 15 +++++---------- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/utils/FileEmitter.ts b/src/utils/FileEmitter.ts index 68c7255a102..eeb362452cb 100644 --- a/src/utils/FileEmitter.ts +++ b/src/utils/FileEmitter.ts @@ -155,15 +155,13 @@ export class FileEmitter { private bundle: OutputBundleWithPlaceholders | null = null; private facadeChunkByModule: Map | null = null; private filesByReferenceId: Map; - private readonly outputOptions: NormalizedOutputOptions; + private outputOptions: NormalizedOutputOptions | null = null; constructor( private readonly graph: Graph, private readonly options: NormalizedInputOptions, - outputOptions: NormalizedOutputOptions, baseFileEmitter?: FileEmitter ) { - this.outputOptions = outputOptions; this.filesByReferenceId = baseFileEmitter ? new Map(baseFileEmitter.filesByReferenceId) : new Map(); @@ -186,7 +184,7 @@ export class FileEmitter { ) ); } - if (!hasValidName(emittedFile, this.outputOptions.sanitizeFileName)) { + if (!hasValidName(emittedFile, this.outputOptions!.sanitizeFileName)) { return error( errFailedValidation( `The "fileName" or "name" properties of emitted files must be strings that are neither absolute nor relative paths and do not contain invalid characters, received "${ @@ -235,8 +233,10 @@ export class FileEmitter { public setOutputBundle = ( outputBundle: OutputBundleWithPlaceholders, + outputOptions: NormalizedOutputOptions, facadeChunkByModule: Map ): void => { + this.outputOptions = outputOptions; this.bundle = outputBundle; this.facadeChunkByModule = facadeChunkByModule; for (const emittedFile of this.filesByReferenceId.values()) { @@ -329,7 +329,7 @@ export class FileEmitter { const fileName = consumedFile.fileName || findExistingAssetFileNameWithSource(bundle, source) || - generateAssetFileName(consumedFile.name, source, this.outputOptions, bundle); + generateAssetFileName(consumedFile.name, source, this.outputOptions!, bundle); // We must not modify the original assets to avoid interaction between outputs const assetWithFileName = { ...consumedFile, source, fileName }; diff --git a/src/utils/PluginDriver.ts b/src/utils/PluginDriver.ts index aa9d87d74a5..0023aabaaf4 100644 --- a/src/utils/PluginDriver.ts +++ b/src/utils/PluginDriver.ts @@ -15,7 +15,6 @@ import { PluginContext, PluginHooks, PluginValueHooks, - PreRenderedAsset, SequentialPluginHooks, SerializablePluginCache, SyncPluginHooks @@ -93,15 +92,11 @@ export class PluginDriver { ) { warnDeprecatedHooks(userPlugins, options); this.pluginCache = pluginCache; - this.fileEmitter = new FileEmitter( - graph, - options, - basePluginDriver && basePluginDriver.fileEmitter - ); - this.emitFile = this.fileEmitter.emitFile; - this.getFileName = this.fileEmitter.getFileName; - this.finaliseAssets = this.fileEmitter.assertAssetsFinalized; - this.setOutputBundle = this.fileEmitter.setOutputBundle; + this.fileEmitter = new FileEmitter(graph, options, basePluginDriver && basePluginDriver.fileEmitter); + this.emitFile = this.fileEmitter.emitFile.bind(this.fileEmitter); + this.getFileName = this.fileEmitter.getFileName.bind(this.fileEmitter); + this.finaliseAssets = this.fileEmitter.assertAssetsFinalized.bind(this.fileEmitter); + this.setOutputBundle = this.fileEmitter.setOutputBundle.bind(this.fileEmitter); this.plugins = userPlugins.concat(basePluginDriver ? basePluginDriver.plugins : []); const existingPluginNames = new Set(); for (const plugin of this.plugins) { From c2101249fbb74dd72c4b87654011d4c5105bdf58 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Tue, 27 Apr 2021 15:46:03 +0200 Subject: [PATCH 17/25] hmm --- src/utils/FileEmitter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/FileEmitter.ts b/src/utils/FileEmitter.ts index eeb362452cb..6b7ede7a5e8 100644 --- a/src/utils/FileEmitter.ts +++ b/src/utils/FileEmitter.ts @@ -184,7 +184,7 @@ export class FileEmitter { ) ); } - if (!hasValidName(emittedFile, this.outputOptions!.sanitizeFileName)) { + if (this.outputOptions && !hasValidName(emittedFile, this.outputOptions.sanitizeFileName)) { return error( errFailedValidation( `The "fileName" or "name" properties of emitted files must be strings that are neither absolute nor relative paths and do not contain invalid characters, received "${ From 816124ad45c1d0a6bed9a72648c77af6a1f751d1 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Tue, 27 Apr 2021 15:54:00 +0200 Subject: [PATCH 18/25] handle non output options case --- src/utils/FileEmitter.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/utils/FileEmitter.ts b/src/utils/FileEmitter.ts index 6b7ede7a5e8..19ef476805f 100644 --- a/src/utils/FileEmitter.ts +++ b/src/utils/FileEmitter.ts @@ -27,6 +27,7 @@ import { import { extname } from './path'; import { isPlainPathFragment } from './relativeId'; import { makeUnique, renderNamePattern } from './renderNamePattern'; +import { sanitizeFileName } from './sanitizeFileName'; function generateAssetFileName( name: string | undefined, @@ -184,7 +185,7 @@ export class FileEmitter { ) ); } - if (this.outputOptions && !hasValidName(emittedFile, this.outputOptions.sanitizeFileName)) { + if (!hasValidName(emittedFile, this.outputOptions?.sanitizeFileName || sanitizeFileName)) { return error( errFailedValidation( `The "fileName" or "name" properties of emitted files must be strings that are neither absolute nor relative paths and do not contain invalid characters, received "${ From 5c1dd6e559801fdc9d2cd9d6d78924cc19953135 Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Wed, 28 Apr 2021 12:55:21 +0200 Subject: [PATCH 19/25] update fragment validation --- src/utils/FileEmitter.ts | 19 ++++++------------- src/utils/relativeId.ts | 11 +++-------- src/utils/renderNamePattern.ts | 15 +++++---------- 3 files changed, 14 insertions(+), 31 deletions(-) diff --git a/src/utils/FileEmitter.ts b/src/utils/FileEmitter.ts index 19ef476805f..b470e541bb4 100644 --- a/src/utils/FileEmitter.ts +++ b/src/utils/FileEmitter.ts @@ -25,9 +25,8 @@ import { warnDeprecation } from './error'; import { extname } from './path'; -import { isPlainPathFragment } from './relativeId'; +import { isPathFragment } from './relativeId'; import { makeUnique, renderNamePattern } from './renderNamePattern'; -import { sanitizeFileName } from './sanitizeFileName'; function generateAssetFileName( name: string | undefined, @@ -53,8 +52,7 @@ function generateAssetFileName( ext: () => extname(emittedName).substr(1), extname: () => extname(emittedName), name: () => emittedName.substr(0, emittedName.length - extname(emittedName).length) - }, - outputOptions.sanitizeFileName + } ), bundle ); @@ -108,14 +106,9 @@ function hasValidType( ); } -function hasValidName(emittedFile: { - type: 'asset' | 'chunk'; - [key: string]: unknown; -}, sanitizeFileName: (fileName: string) => string): emittedFile is EmittedFile { +function hasValidName(emittedFile: { type: 'asset' | 'chunk'; [key: string]: unknown; }): emittedFile is EmittedFile { const validatedName = emittedFile.fileName || emittedFile.name; - return ( - !validatedName || (typeof validatedName === 'string' && isPlainPathFragment(validatedName, sanitizeFileName)) - ); + return !validatedName || typeof validatedName === 'string' && !isPathFragment(validatedName); } function getValidSource( @@ -185,10 +178,10 @@ export class FileEmitter { ) ); } - if (!hasValidName(emittedFile, this.outputOptions?.sanitizeFileName || sanitizeFileName)) { + if (!hasValidName(emittedFile)) { return error( errFailedValidation( - `The "fileName" or "name" properties of emitted files must be strings that are neither absolute nor relative paths and do not contain invalid characters, received "${ + `The "fileName" or "name" properties of emitted files must be strings that are neither absolute nor relative paths, received "${ emittedFile.fileName || emittedFile.name }".` ) diff --git a/src/utils/relativeId.ts b/src/utils/relativeId.ts index f878172db5c..6618a683459 100644 --- a/src/utils/relativeId.ts +++ b/src/utils/relativeId.ts @@ -10,12 +10,7 @@ export default function relativeId(id: string) { return relative(resolve(), id); } -export function isPlainPathFragment(name: string, sanitizeFileName: (fileName: string) => string) { - // not starting with "/", "./", "../" - return ( - name[0] !== '/' && - !(name[0] === '.' && (name[1] === '/' || name[1] === '.')) && - sanitizeFileName(name) === name && - !isAbsolute(name) - ); +export function isPathFragment(name: string) { + // starting with "/", "./", "../", "C:/" + return name[0] === '/' || name[0] === '.' && (name[1] === '/' || name[1] === '.') || isAbsolute(name); } diff --git a/src/utils/renderNamePattern.ts b/src/utils/renderNamePattern.ts index ca474d0639b..debc84c4ca8 100644 --- a/src/utils/renderNamePattern.ts +++ b/src/utils/renderNamePattern.ts @@ -1,17 +1,12 @@ import { errFailedValidation, error } from './error'; import { extname } from './path'; -import { isPlainPathFragment } from './relativeId'; +import { isPathFragment } from './relativeId'; -export function renderNamePattern( - pattern: string, - patternName: string, - replacements: { [name: string]: () => string }, - sanitizeFileName: (fileName: string) => string -) { - if (!isPlainPathFragment(pattern, sanitizeFileName)) +export function renderNamePattern(pattern: string, patternName: string, replacements: { [name: string]: () => string }) { + if (isPathFragment(pattern)) return error( errFailedValidation( - `Invalid pattern "${pattern}" for "${patternName}", patterns can be neither absolute nor relative paths and must not contain invalid characters.` + `Invalid pattern "${pattern}" for "${patternName}", patterns can be neither absolute nor relative paths.` ) ); return pattern.replace(/\[(\w+)\]/g, (_match, type) => { @@ -21,7 +16,7 @@ export function renderNamePattern( ); } const replacement = replacements[type](); - if (!isPlainPathFragment(replacement, sanitizeFileName)) + if (isPathFragment(replacement)) return error( errFailedValidation( `Invalid substitution "${replacement}" for placeholder "[${type}]" in "${patternName}" pattern, can be neither absolute nor relative path.` From 622925c142c25c63a4dfd29ccf4b4acd6cf6258c Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Wed, 28 Apr 2021 13:06:42 +0200 Subject: [PATCH 20/25] update tests --- .../emit-asset/invalid-asset-name/_config.js | 2 +- .../emit-file/invalid-asset-name/_config.js | 2 +- .../emit-file/invalid-asset-name2/_config.js | 19 ------------------- .../emit-file/invalid-asset-name3/_config.js | 2 +- .../non-invalid-asset-name/_config.js | 11 +++++++++++ .../main.js | 0 .../samples/invalid-pattern/_config.js | 11 ----------- .../samples/pattern-encodings/_config.js | 6 ++++++ .../main.js | 0 9 files changed, 20 insertions(+), 33 deletions(-) delete mode 100644 test/function/samples/emit-file/invalid-asset-name2/_config.js create mode 100644 test/function/samples/emit-file/non-invalid-asset-name/_config.js rename test/function/samples/emit-file/{invalid-asset-name2 => non-invalid-asset-name}/main.js (100%) delete mode 100644 test/function/samples/invalid-pattern/_config.js create mode 100644 test/function/samples/pattern-encodings/_config.js rename test/function/samples/{invalid-pattern => pattern-encodings}/main.js (100%) diff --git a/test/function/samples/deprecated/emit-asset/invalid-asset-name/_config.js b/test/function/samples/deprecated/emit-asset/invalid-asset-name/_config.js index 8add304a305..f807478d8f4 100644 --- a/test/function/samples/deprecated/emit-asset/invalid-asset-name/_config.js +++ b/test/function/samples/deprecated/emit-asset/invalid-asset-name/_config.js @@ -13,7 +13,7 @@ module.exports = { code: 'PLUGIN_ERROR', hook: 'buildStart', message: - 'The "fileName" or "name" properties of emitted files must be strings that are neither absolute nor relative paths and do not contain invalid characters, received "/test.ext".', + 'The "fileName" or "name" properties of emitted files must be strings that are neither absolute nor relative paths, received "/test.ext".', plugin: 'test-plugin', pluginCode: 'VALIDATION_ERROR' } diff --git a/test/function/samples/emit-file/invalid-asset-name/_config.js b/test/function/samples/emit-file/invalid-asset-name/_config.js index 58ff955cc15..1d58fe9c100 100644 --- a/test/function/samples/emit-file/invalid-asset-name/_config.js +++ b/test/function/samples/emit-file/invalid-asset-name/_config.js @@ -12,7 +12,7 @@ module.exports = { code: 'PLUGIN_ERROR', hook: 'buildStart', message: - 'The "fileName" or "name" properties of emitted files must be strings that are neither absolute nor relative paths and do not contain invalid characters, received "/test.ext".', + 'The "fileName" or "name" properties of emitted files must be strings that are neither absolute nor relative paths, received "/test.ext".', plugin: 'test-plugin', pluginCode: 'VALIDATION_ERROR' } diff --git a/test/function/samples/emit-file/invalid-asset-name2/_config.js b/test/function/samples/emit-file/invalid-asset-name2/_config.js deleted file mode 100644 index c0ff923d737..00000000000 --- a/test/function/samples/emit-file/invalid-asset-name2/_config.js +++ /dev/null @@ -1,19 +0,0 @@ -module.exports = { - description: 'throws for invalid asset names with special characters', - options: { - plugins: { - name: 'test-plugin', - buildStart() { - this.emitFile({ type: 'asset', name: '\0test.ext', source: 'content' }); - } - } - }, - error: { - code: 'PLUGIN_ERROR', - hook: 'buildStart', - message: - 'The "fileName" or "name" properties of emitted files must be strings that are neither absolute nor relative paths and do not contain invalid characters, received "\u0000test.ext".', - plugin: 'test-plugin', - pluginCode: 'VALIDATION_ERROR' - } -}; diff --git a/test/function/samples/emit-file/invalid-asset-name3/_config.js b/test/function/samples/emit-file/invalid-asset-name3/_config.js index da96f00fc22..5d720fda2e8 100644 --- a/test/function/samples/emit-file/invalid-asset-name3/_config.js +++ b/test/function/samples/emit-file/invalid-asset-name3/_config.js @@ -12,7 +12,7 @@ module.exports = { code: 'PLUGIN_ERROR', hook: 'buildStart', message: - 'The "fileName" or "name" properties of emitted files must be strings that are neither absolute nor relative paths and do not contain invalid characters, received "F:\\test.ext".', + 'The "fileName" or "name" properties of emitted files must be strings that are neither absolute nor relative paths, received "F:\\test.ext".', plugin: 'test-plugin', pluginCode: 'VALIDATION_ERROR' } diff --git a/test/function/samples/emit-file/non-invalid-asset-name/_config.js b/test/function/samples/emit-file/non-invalid-asset-name/_config.js new file mode 100644 index 00000000000..e5474d5df05 --- /dev/null +++ b/test/function/samples/emit-file/non-invalid-asset-name/_config.js @@ -0,0 +1,11 @@ +module.exports = { + description: 'throws for invalid asset names with special characters', + options: { + plugins: { + name: 'test-plugin', + buildStart() { + this.emitFile({ type: 'asset', name: '\0test.ext', source: 'content' }); + } + } + } +}; diff --git a/test/function/samples/emit-file/invalid-asset-name2/main.js b/test/function/samples/emit-file/non-invalid-asset-name/main.js similarity index 100% rename from test/function/samples/emit-file/invalid-asset-name2/main.js rename to test/function/samples/emit-file/non-invalid-asset-name/main.js diff --git a/test/function/samples/invalid-pattern/_config.js b/test/function/samples/invalid-pattern/_config.js deleted file mode 100644 index 6275e9e38ac..00000000000 --- a/test/function/samples/invalid-pattern/_config.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - description: 'throws for invalid patterns', - options: { - output: { entryFileNames: '\0main.js' } - }, - generateError: { - code: 'VALIDATION_ERROR', - message: - 'Invalid pattern "\0main.js" for "output.entryFileNames", patterns can be neither absolute nor relative paths and must not contain invalid characters.' - } -}; diff --git a/test/function/samples/pattern-encodings/_config.js b/test/function/samples/pattern-encodings/_config.js new file mode 100644 index 00000000000..9aefc4fd6c0 --- /dev/null +++ b/test/function/samples/pattern-encodings/_config.js @@ -0,0 +1,6 @@ +module.exports = { + description: 'throws for invalid patterns', + options: { + output: { entryFileNames: '\0main.js' } + } +}; diff --git a/test/function/samples/invalid-pattern/main.js b/test/function/samples/pattern-encodings/main.js similarity index 100% rename from test/function/samples/invalid-pattern/main.js rename to test/function/samples/pattern-encodings/main.js From aa231ce47a73ef7db8f53173e5a120a9720d211a Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Wed, 28 Apr 2021 13:16:24 +0200 Subject: [PATCH 21/25] fixup tests --- test/function/samples/emit-file/non-invalid-asset-name/main.js | 3 ++- test/function/samples/pattern-encodings/main.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/test/function/samples/emit-file/non-invalid-asset-name/main.js b/test/function/samples/emit-file/non-invalid-asset-name/main.js index c4b940fc952..bfeff5f065d 100644 --- a/test/function/samples/emit-file/non-invalid-asset-name/main.js +++ b/test/function/samples/emit-file/non-invalid-asset-name/main.js @@ -1 +1,2 @@ -throw new Error('should not build'); +console.log('should build'); + diff --git a/test/function/samples/pattern-encodings/main.js b/test/function/samples/pattern-encodings/main.js index a9244a453fb..bb06158e22d 100644 --- a/test/function/samples/pattern-encodings/main.js +++ b/test/function/samples/pattern-encodings/main.js @@ -1 +1,2 @@ -throw new Error('Not executed'); +console.log('ok'); + From 101d412e4b2e21c83ff6b044b55f25a82a2610de Mon Sep 17 00:00:00 2001 From: Guy Bedford Date: Wed, 28 Apr 2021 13:21:42 +0200 Subject: [PATCH 22/25] ts fix --- src/Chunk.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Chunk.ts b/src/Chunk.ts index e199cb27d6f..57103cdfcbf 100644 --- a/src/Chunk.ts +++ b/src/Chunk.ts @@ -423,8 +423,7 @@ export default class Chunk { ? this.computeContentHashWithDependencies(addons, options, existingNames) : '[hash]', name: () => this.getChunkName() - }, - this.outputOptions.sanitizeFileName + } ), existingNames ); @@ -455,8 +454,7 @@ export default class Chunk { extname: () => extension, format: () => options.format as string, name: () => this.getChunkName() - }, - this.outputOptions.sanitizeFileName + } ); const currentPath = `${currentDir}/${fileName}`; const { preserveModulesRoot } = options; From daaafe2ba8389cc2d084e0e2620618687ac208d6 Mon Sep 17 00:00:00 2001 From: Lukas Taegert-Atkinson Date: Thu, 29 Apr 2021 06:20:45 +0200 Subject: [PATCH 23/25] Add docs in various places --- cli/help.md | 1 + docs/01-command-line-reference.md | 2 ++ docs/02-javascript-api.md | 1 + docs/999-big-list-of-options.md | 1 + 4 files changed, 5 insertions(+) diff --git a/cli/help.md b/cli/help.md index b6b853dc50b..a6f24792880 100644 --- a/cli/help.md +++ b/cli/help.md @@ -51,6 +51,7 @@ Basic options: --preserveModules Preserve module structure --preserveModulesRoot Put preserved modules under this path at root level --preserveSymlinks Do not follow symlinks when resolving files +--no-sanitizeFileName Do not replace invalid characters in file names --shimMissingExports Create shim variables for missing exports --silent Don't print warnings --sourcemapExcludeSources Do not include source code in source maps diff --git a/docs/01-command-line-reference.md b/docs/01-command-line-reference.md index f3438bf71c1..659e7f0ba42 100755 --- a/docs/01-command-line-reference.md +++ b/docs/01-command-line-reference.md @@ -95,6 +95,7 @@ export default { // can be an array (for multiple inputs) namespaceToStringTag, noConflict, preferConst, + sanitizeFileName, strict, systemNullSetters }, @@ -310,6 +311,7 @@ Many options have command line equivalents. In those cases, any arguments passed --preserveModules Preserve module structure --preserveModulesRoot Put preserved modules under this path at root level --preserveSymlinks Do not follow symlinks when resolving files +--no-sanitizeFileName Do not replace invalid characters in file names --shimMissingExports Create shim variables for missing exports --silent Don't print warnings --sourcemapExcludeSources Do not include source code in source maps diff --git a/docs/02-javascript-api.md b/docs/02-javascript-api.md index 93253d4260a..725e2f5d5d7 100755 --- a/docs/02-javascript-api.md +++ b/docs/02-javascript-api.md @@ -161,6 +161,7 @@ const outputOptions = { namespaceToStringTag, noConflict, preferConst, + sanitizeFileName, strict, systemNullSetters }; diff --git a/docs/999-big-list-of-options.md b/docs/999-big-list-of-options.md index c56f112a454..76e730ed3d6 100755 --- a/docs/999-big-list-of-options.md +++ b/docs/999-big-list-of-options.md @@ -1322,6 +1322,7 @@ Generate `const` declarations for exports rather than `var` declarations. #### output.sanitizeFileName Type: `boolean | (string) => string`
+CLI: `--sanitizeFileName`/`no-sanitizeFileName` Default: `true` Set to `false` to disable all chunk name sanitizations (removal of `\0`, `?` and `*` characters). From 7b6effc009457ef032d00ffae95c39f73bc034e1 Mon Sep 17 00:00:00 2001 From: Lukas Taegert-Atkinson Date: Thu, 29 Apr 2021 06:38:51 +0200 Subject: [PATCH 24/25] Test custom sanitizer and sanitize asset names --- src/utils/FileEmitter.ts | 2 +- .../samples/custom-sanitizer/_config.js | 18 ++++++++++++++++++ .../custom-sanitizer/_expected/amd/_sset.txt | 1 + .../custom-sanitizer/_expected/amd/m_in.js | 5 +++++ .../custom-sanitizer/_expected/cjs/_sset.txt | 1 + .../custom-sanitizer/_expected/cjs/m_in.js | 3 +++ .../custom-sanitizer/_expected/es/_sset.txt | 1 + .../custom-sanitizer/_expected/es/m_in.js | 1 + .../_expected/system/_sset.txt | 1 + .../custom-sanitizer/_expected/system/m_in.js | 10 ++++++++++ .../samples/custom-sanitizer/main.js | 2 ++ 11 files changed, 44 insertions(+), 1 deletion(-) create mode 100644 test/chunking-form/samples/custom-sanitizer/_config.js create mode 100644 test/chunking-form/samples/custom-sanitizer/_expected/amd/_sset.txt create mode 100644 test/chunking-form/samples/custom-sanitizer/_expected/amd/m_in.js create mode 100644 test/chunking-form/samples/custom-sanitizer/_expected/cjs/_sset.txt create mode 100644 test/chunking-form/samples/custom-sanitizer/_expected/cjs/m_in.js create mode 100644 test/chunking-form/samples/custom-sanitizer/_expected/es/_sset.txt create mode 100644 test/chunking-form/samples/custom-sanitizer/_expected/es/m_in.js create mode 100644 test/chunking-form/samples/custom-sanitizer/_expected/system/_sset.txt create mode 100644 test/chunking-form/samples/custom-sanitizer/_expected/system/m_in.js create mode 100644 test/chunking-form/samples/custom-sanitizer/main.js diff --git a/src/utils/FileEmitter.ts b/src/utils/FileEmitter.ts index b470e541bb4..65520b91fc3 100644 --- a/src/utils/FileEmitter.ts +++ b/src/utils/FileEmitter.ts @@ -34,7 +34,7 @@ function generateAssetFileName( outputOptions: NormalizedOutputOptions, bundle: OutputBundleWithPlaceholders ): string { - const emittedName = name || 'asset'; + const emittedName = outputOptions.sanitizeFileName(name || 'asset'); return makeUnique( renderNamePattern( typeof outputOptions.assetFileNames === 'function' diff --git a/test/chunking-form/samples/custom-sanitizer/_config.js b/test/chunking-form/samples/custom-sanitizer/_config.js new file mode 100644 index 00000000000..0abca0b2f08 --- /dev/null +++ b/test/chunking-form/samples/custom-sanitizer/_config.js @@ -0,0 +1,18 @@ +module.exports = { + description: 'supports custom file name sanitizer functions', + options: { + plugins: [ + { + buildStart() { + this.emitFile({ type: 'asset', name: 'asset.txt', source: 'asset' }); + } + } + ], + output: { + assetFileNames: '[name][extname]', + sanitizeFileName(id) { + return id.replace(/a/g, '_'); + } + } + } +}; diff --git a/test/chunking-form/samples/custom-sanitizer/_expected/amd/_sset.txt b/test/chunking-form/samples/custom-sanitizer/_expected/amd/_sset.txt new file mode 100644 index 00000000000..a95e94f6604 --- /dev/null +++ b/test/chunking-form/samples/custom-sanitizer/_expected/amd/_sset.txt @@ -0,0 +1 @@ +asset \ No newline at end of file diff --git a/test/chunking-form/samples/custom-sanitizer/_expected/amd/m_in.js b/test/chunking-form/samples/custom-sanitizer/_expected/amd/m_in.js new file mode 100644 index 00000000000..6ec9254acf0 --- /dev/null +++ b/test/chunking-form/samples/custom-sanitizer/_expected/amd/m_in.js @@ -0,0 +1,5 @@ +define(function () { 'use strict'; + + console.log('ok'); + +}); diff --git a/test/chunking-form/samples/custom-sanitizer/_expected/cjs/_sset.txt b/test/chunking-form/samples/custom-sanitizer/_expected/cjs/_sset.txt new file mode 100644 index 00000000000..a95e94f6604 --- /dev/null +++ b/test/chunking-form/samples/custom-sanitizer/_expected/cjs/_sset.txt @@ -0,0 +1 @@ +asset \ No newline at end of file diff --git a/test/chunking-form/samples/custom-sanitizer/_expected/cjs/m_in.js b/test/chunking-form/samples/custom-sanitizer/_expected/cjs/m_in.js new file mode 100644 index 00000000000..faea9b6d894 --- /dev/null +++ b/test/chunking-form/samples/custom-sanitizer/_expected/cjs/m_in.js @@ -0,0 +1,3 @@ +'use strict'; + +console.log('ok'); diff --git a/test/chunking-form/samples/custom-sanitizer/_expected/es/_sset.txt b/test/chunking-form/samples/custom-sanitizer/_expected/es/_sset.txt new file mode 100644 index 00000000000..a95e94f6604 --- /dev/null +++ b/test/chunking-form/samples/custom-sanitizer/_expected/es/_sset.txt @@ -0,0 +1 @@ +asset \ No newline at end of file diff --git a/test/chunking-form/samples/custom-sanitizer/_expected/es/m_in.js b/test/chunking-form/samples/custom-sanitizer/_expected/es/m_in.js new file mode 100644 index 00000000000..37108886b56 --- /dev/null +++ b/test/chunking-form/samples/custom-sanitizer/_expected/es/m_in.js @@ -0,0 +1 @@ +console.log('ok'); diff --git a/test/chunking-form/samples/custom-sanitizer/_expected/system/_sset.txt b/test/chunking-form/samples/custom-sanitizer/_expected/system/_sset.txt new file mode 100644 index 00000000000..a95e94f6604 --- /dev/null +++ b/test/chunking-form/samples/custom-sanitizer/_expected/system/_sset.txt @@ -0,0 +1 @@ +asset \ No newline at end of file diff --git a/test/chunking-form/samples/custom-sanitizer/_expected/system/m_in.js b/test/chunking-form/samples/custom-sanitizer/_expected/system/m_in.js new file mode 100644 index 00000000000..2181247e7d3 --- /dev/null +++ b/test/chunking-form/samples/custom-sanitizer/_expected/system/m_in.js @@ -0,0 +1,10 @@ +System.register([], function () { + 'use strict'; + return { + execute: function () { + + console.log('ok'); + + } + }; +}); diff --git a/test/chunking-form/samples/custom-sanitizer/main.js b/test/chunking-form/samples/custom-sanitizer/main.js new file mode 100644 index 00000000000..bb06158e22d --- /dev/null +++ b/test/chunking-form/samples/custom-sanitizer/main.js @@ -0,0 +1,2 @@ +console.log('ok'); + From 466d3d118005f55dceb5c9064010695ae384c045 Mon Sep 17 00:00:00 2001 From: Lukas Taegert-Atkinson Date: Thu, 29 Apr 2021 06:52:59 +0200 Subject: [PATCH 25/25] Add another test --- test/function/samples/invalid-pattern/_config.js | 11 +++++++++++ test/function/samples/invalid-pattern/main.js | 0 2 files changed, 11 insertions(+) create mode 100644 test/function/samples/invalid-pattern/_config.js create mode 100644 test/function/samples/invalid-pattern/main.js diff --git a/test/function/samples/invalid-pattern/_config.js b/test/function/samples/invalid-pattern/_config.js new file mode 100644 index 00000000000..3c2a6b7aa58 --- /dev/null +++ b/test/function/samples/invalid-pattern/_config.js @@ -0,0 +1,11 @@ +module.exports = { + description: 'throws for invalid patterns', + options: { + output: { entryFileNames: '../main.js' } + }, + generateError: { + code: 'VALIDATION_ERROR', + message: + 'Invalid pattern "../main.js" for "output.entryFileNames", patterns can be neither absolute nor relative paths.' + } +}; diff --git a/test/function/samples/invalid-pattern/main.js b/test/function/samples/invalid-pattern/main.js new file mode 100644 index 00000000000..e69de29bb2d