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 4e8c7d97432..76e730ed3d6 100755
--- a/docs/999-big-list-of-options.md
+++ b/docs/999-big-list-of-options.md
@@ -1320,6 +1320,15 @@ Default: `false`
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).
+
+Alternatively set to a function to allow custom chunk name sanitization.
+
#### output.strict
Type: `boolean`
CLI: `--strict`/`--no-strict`
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 0d79ffb1811..57103cdfcbf 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';
@@ -437,7 +436,7 @@ export default class Chunk {
unsetOptions: Set
): string {
const id = this.orderedModules[0].id;
- const sanitizedId = sanitizeFileName(id);
+ const sanitizedId = this.outputOptions.sanitizeFileName(id);
let path: string;
if (isAbsolute(id)) {
const extension = extname(id);
@@ -501,7 +500,7 @@ export default class Chunk {
}
getChunkName(): string {
- return this.name || (this.name = sanitizeFileName(this.getFallbackChunkName()));
+ return this.name || (this.name = this.outputOptions.sanitizeFileName(this.getFallbackChunkName()));
}
getExportNames(): string[] {
@@ -816,7 +815,7 @@ export default class Chunk {
if (fileName) {
this.fileName = fileName;
} else {
- this.name = 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 b9dda715aa8..8c949b54c2d 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 | ((fileName: string) => string);
sourcemap?: boolean | 'inline' | 'hidden';
sourcemapExcludeSources?: boolean;
sourcemapFile?: string;
@@ -693,6 +694,7 @@ export interface NormalizedOutputOptions {
preferConst: boolean;
preserveModules: boolean;
preserveModulesRoot: string | undefined;
+ sanitizeFileName: (fileName: string) => string;
sourcemap: boolean | 'inline' | 'hidden';
sourcemapExcludeSources: boolean;
sourcemapFile: string | undefined;
diff --git a/src/utils/FileEmitter.ts b/src/utils/FileEmitter.ts
index 2c63cbd79f5..65520b91fc3 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';
@@ -25,25 +25,21 @@ import {
warnDeprecation
} from './error';
import { extname } from './path';
-import { isPlainPathFragment } from './relativeId';
+import { isPathFragment } from './relativeId';
import { makeUnique, renderNamePattern } from './renderNamePattern';
-interface OutputSpecificFileData {
- assetFileNames: string | ((assetInfo: PreRenderedAsset) => string);
- bundle: OutputBundleWithPlaceholders;
-}
-
function generateAssetFileName(
name: string | undefined,
source: string | Uint8Array,
- output: OutputSpecificFileData
+ outputOptions: NormalizedOutputOptions,
+ bundle: OutputBundleWithPlaceholders
): string {
- const emittedName = name || 'asset';
+ const emittedName = outputOptions.sanitizeFileName(name || 'asset');
return makeUnique(
renderNamePattern(
- typeof output.assetFileNames === 'function'
- ? output.assetFileNames({ name, source, type: 'asset' })
- : output.assetFileNames,
+ typeof outputOptions.assetFileNames === 'function'
+ ? outputOptions.assetFileNames({ name, source, type: 'asset' })
+ : outputOptions.assetFileNames,
'output.assetFileNames',
{
hash() {
@@ -58,7 +54,7 @@ function generateAssetFileName(
name: () => emittedName.substr(0, emittedName.length - extname(emittedName).length)
}
),
- output.bundle
+ bundle
);
}
@@ -110,14 +106,9 @@ function hasValidType(
);
}
-function hasValidName(emittedFile: {
- type: 'asset' | 'chunk';
- [key: string]: unknown;
-}): 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))
- );
+ return !validatedName || typeof validatedName === 'string' && !isPathFragment(validatedName);
}
function getValidSource(
@@ -155,9 +146,10 @@ function getChunkFileName(
}
export class FileEmitter {
+ private bundle: OutputBundleWithPlaceholders | null = null;
private facadeChunkByModule: Map | null = null;
private filesByReferenceId: Map;
- private output: OutputSpecificFileData | null = null;
+ private outputOptions: NormalizedOutputOptions | null = null;
constructor(
private readonly graph: Graph,
@@ -189,7 +181,7 @@ export class FileEmitter {
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
}".`
)
@@ -226,8 +218,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;
}
@@ -235,22 +227,20 @@ export class FileEmitter {
public setOutputBundle = (
outputBundle: OutputBundleWithPlaceholders,
- assetFileNames: string | ((assetInfo: PreRenderedAsset) => string),
+ outputOptions: NormalizedOutputOptions,
facadeChunkByModule: Map
): void => {
- this.output = {
- assetFileNames,
- bundle: outputBundle
- };
+ this.outputOptions = 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);
}
}
};
@@ -285,12 +275,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;
@@ -328,18 +318,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 {
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);
},
diff --git a/src/utils/PluginDriver.ts b/src/utils/PluginDriver.ts
index 61dfce57095..0023aabaaf4 100644
--- a/src/utils/PluginDriver.ts
+++ b/src/utils/PluginDriver.ts
@@ -7,6 +7,7 @@ import {
EmitFile,
FirstPluginHooks,
NormalizedInputOptions,
+ NormalizedOutputOptions,
OutputBundleWithPlaceholders,
OutputPluginHooks,
ParallelPluginHooks,
@@ -14,7 +15,6 @@ import {
PluginContext,
PluginHooks,
PluginValueHooks,
- PreRenderedAsset,
SequentialPluginHooks,
SerializablePluginCache,
SyncPluginHooks
@@ -74,7 +74,7 @@ export class PluginDriver {
public getFileName: (fileReferenceId: string) => string;
public setOutputBundle: (
outputBundle: OutputBundleWithPlaceholders,
- assetFileNames: string | ((assetInfo: PreRenderedAsset) => string),
+ outputOptions: NormalizedOutputOptions,
facadeChunkByModule: Map
) => void;
@@ -92,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) {
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/src/utils/options/normalizeOutputOptions.ts b/src/utils/options/normalizeOutputOptions.ts
index 5cf14024e29..1531b6ea8d4 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,6 +67,7 @@ export function normalizeOutputOptions(
preferConst: (config.preferConst as boolean | undefined) || false,
preserveModules,
preserveModulesRoot: getPreserveModulesRoot(config),
+ 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 e3d3d36475b..6618a683459 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 } from './sanitizeFileName';
export function getAliasName(id: string) {
const base = basename(id);
@@ -11,12 +10,7 @@ export default function relativeId(id: string) {
return relative(resolve(), id);
}
-export function isPlainPathFragment(name: 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 13690decc71..debc84c4ca8 100644
--- a/src/utils/renderNamePattern.ts
+++ b/src/utils/renderNamePattern.ts
@@ -1,16 +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 }
-) {
- if (!isPlainPathFragment(pattern))
+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) => {
@@ -20,7 +16,7 @@ export function renderNamePattern(
);
}
const replacement = replacements[type]();
- if (!isPlainPathFragment(replacement))
+ if (isPathFragment(replacement))
return error(
errFailedValidation(
`Invalid substitution "${replacement}" for placeholder "[${type}]" in "${patternName}" pattern, can be neither absolute nor relative path.`
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');
+
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-name2/main.js b/test/function/samples/emit-file/invalid-asset-name2/main.js
deleted file mode 100644
index c4b940fc952..00000000000
--- a/test/function/samples/emit-file/invalid-asset-name2/main.js
+++ /dev/null
@@ -1 +0,0 @@
-throw new Error('should not build');
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/non-invalid-asset-name/main.js b/test/function/samples/emit-file/non-invalid-asset-name/main.js
new file mode 100644
index 00000000000..bfeff5f065d
--- /dev/null
+++ b/test/function/samples/emit-file/non-invalid-asset-name/main.js
@@ -0,0 +1,2 @@
+console.log('should build');
+
diff --git a/test/function/samples/invalid-pattern/_config.js b/test/function/samples/invalid-pattern/_config.js
index 6275e9e38ac..3c2a6b7aa58 100644
--- a/test/function/samples/invalid-pattern/_config.js
+++ b/test/function/samples/invalid-pattern/_config.js
@@ -1,11 +1,11 @@
module.exports = {
description: 'throws for invalid patterns',
options: {
- output: { entryFileNames: '\0main.js' }
+ output: { entryFileNames: '../main.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.'
+ '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
index a9244a453fb..e69de29bb2d 100644
--- a/test/function/samples/invalid-pattern/main.js
+++ b/test/function/samples/invalid-pattern/main.js
@@ -1 +0,0 @@
-throw new Error('Not executed');
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/pattern-encodings/main.js b/test/function/samples/pattern-encodings/main.js
new file mode 100644
index 00000000000..bb06158e22d
--- /dev/null
+++ b/test/function/samples/pattern-encodings/main.js
@@ -0,0 +1,2 @@
+console.log('ok');
+
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;
diff --git a/test/misc/optionList.js b/test/misc/optionList.js
index 72c376d9724..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, 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, 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';