diff --git a/src/Chunk.ts b/src/Chunk.ts index 9d2fbad3b41..429db2079f9 100644 --- a/src/Chunk.ts +++ b/src/Chunk.ts @@ -28,6 +28,7 @@ import { collapseSourcemaps } from './utils/collapseSourcemaps'; import { createHash } from './utils/crypto'; import { deconflictChunk } from './utils/deconflictChunk'; import { errFailedValidation, error } from './utils/error'; +import { escapeId } from './utils/escapeId'; import { sortByExecutionOrder } from './utils/executionOrder'; import { assignExportsToMangledNames, assignExportsToNames } from './utils/exportNames'; import getExportMode from './utils/getExportMode'; @@ -628,12 +629,16 @@ export default class Chunk { // populate ids in the rendered declarations only here // as chunk ids known only after prerender for (const dependency of this.dependencies) { - if (dependency instanceof ExternalModule && !dependency.renormalizeRenderPath) continue; const renderedDependency = this.renderedDependencies!.get(dependency)!; - const depId = dependency instanceof ExternalModule ? renderedDependency.id : dependency.id!; - if (dependency instanceof Chunk) + if (dependency instanceof ExternalModule) { + const originalId = dependency.renderPath; + renderedDependency.id = escapeId( + dependency.renormalizeRenderPath ? this.getRelativePath(originalId, false) : originalId + ); + } else { renderedDependency.namedExportsMode = dependency.exportMode !== 'default'; - renderedDependency.id = this.getRelativePath(depId, false); + renderedDependency.id = escapeId(this.getRelativePath(dependency.id!, false)); + } } this.finaliseDynamicImports(options); @@ -935,25 +940,18 @@ export default class Chunk { namedExportsMode = dep.exportMode !== 'default'; } - let id: string = undefined as any; - let globalName: string = undefined as any; - if (dep instanceof ExternalModule) { - id = dep.renderPath; - if (options.format === 'umd' || options.format === 'iife') { - globalName = getGlobalName( + dependencies.set(dep, { + exportsDefault, + exportsNames, + globalName: (dep instanceof ExternalModule && + (options.format === 'umd' || options.format === 'iife') && + getGlobalName( dep, options.globals, exportsNames || exportsDefault, this.inputOptions.onwarn - )!; - } - } - - dependencies.set(dep, { - exportsDefault, - exportsNames, - globalName, - id, // chunk id updated on render + )) as string, + id: undefined as any, // chunk id updated on render imports: imports.length > 0 ? imports : (null as any), isChunk: dep instanceof Chunk, name: dep.variableName, diff --git a/src/utils/escapeId.ts b/src/utils/escapeId.ts new file mode 100644 index 00000000000..da413d41c95 --- /dev/null +++ b/src/utils/escapeId.ts @@ -0,0 +1,12 @@ +const quoteNewlineRegEx = /['\r\n\u2028\u2029]/g; +const replacements = { + '\n': '\\n', + '\r': '\\r', + "'": "\\'", + '\u2028': '\\u2028', + '\u2029': '\\u2029' +}; + +export function escapeId(id: string) { + return id.replace(quoteNewlineRegEx, match => replacements[match as keyof typeof replacements]); +} diff --git a/test/form/samples/quote-id/_config.js b/test/form/samples/quote-id/_config.js new file mode 100644 index 00000000000..3dfa80c9996 --- /dev/null +++ b/test/form/samples/quote-id/_config.js @@ -0,0 +1,29 @@ +const path = require('path'); + +const external1 = "quoted'\r\n\u2028\u2029external1"; +const external2 = path.join(__dirname, "quoted'\r\n\u2028\u2029external2"); + +module.exports = { + description: 'supports quote characters in external ids', + options: { + output: { + name: 'Q', + globals: { + [external1]: 'quotedExternal1', + [external2]: 'quotedExternal2' + } + }, + plugins: [ + { + resolveId(id, parent) { + if (id === 'external1') { + return { id: external1, external: true }; + } + if (id === 'external2') { + return { id: external2, external: true }; + } + } + } + ] + } +}; diff --git a/test/form/samples/quote-id/_expected/amd.js b/test/form/samples/quote-id/_expected/amd.js new file mode 100644 index 00000000000..b908f89dd23 --- /dev/null +++ b/test/form/samples/quote-id/_expected/amd.js @@ -0,0 +1,5 @@ +define(['quoted\'\r\n\u2028\u2029external1', './quoted\'\r\n\u2028\u2029external2'], function (quoted_____external1, quoted_____external2) { 'use strict'; + + console.log(quoted_____external1.foo, quoted_____external2.bar); + +}); diff --git a/test/form/samples/quote-id/_expected/cjs.js b/test/form/samples/quote-id/_expected/cjs.js new file mode 100644 index 00000000000..28ce92f286d --- /dev/null +++ b/test/form/samples/quote-id/_expected/cjs.js @@ -0,0 +1,6 @@ +'use strict'; + +var quoted_____external1 = require('quoted\'\r\n\u2028\u2029external1'); +var quoted_____external2 = require('./quoted\'\r\n\u2028\u2029external2'); + +console.log(quoted_____external1.foo, quoted_____external2.bar); diff --git a/test/form/samples/quote-id/_expected/es.js b/test/form/samples/quote-id/_expected/es.js new file mode 100644 index 00000000000..7d54a160fef --- /dev/null +++ b/test/form/samples/quote-id/_expected/es.js @@ -0,0 +1,4 @@ +import { foo } from 'quoted\'\r\n\u2028\u2029external1'; +import { bar } from './quoted\'\r\n\u2028\u2029external2'; + +console.log(foo, bar); diff --git a/test/form/samples/quote-id/_expected/iife.js b/test/form/samples/quote-id/_expected/iife.js new file mode 100644 index 00000000000..2f853d9bceb --- /dev/null +++ b/test/form/samples/quote-id/_expected/iife.js @@ -0,0 +1,6 @@ +(function (quoted_____external1, quoted_____external2) { + 'use strict'; + + console.log(quoted_____external1.foo, quoted_____external2.bar); + +}(quotedExternal1, quotedExternal2)); diff --git a/test/form/samples/quote-id/_expected/system.js b/test/form/samples/quote-id/_expected/system.js new file mode 100644 index 00000000000..e6face3d7c8 --- /dev/null +++ b/test/form/samples/quote-id/_expected/system.js @@ -0,0 +1,16 @@ +System.register('Q', ['quoted\'\r\n\u2028\u2029external1', './quoted\'\r\n\u2028\u2029external2'], function () { + 'use strict'; + var foo, bar; + return { + setters: [function (module) { + foo = module.foo; + }, function (module) { + bar = module.bar; + }], + execute: function () { + + console.log(foo, bar); + + } + }; +}); diff --git a/test/form/samples/quote-id/_expected/umd.js b/test/form/samples/quote-id/_expected/umd.js new file mode 100644 index 00000000000..605d5b089b1 --- /dev/null +++ b/test/form/samples/quote-id/_expected/umd.js @@ -0,0 +1,9 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('quoted\'\r\n\u2028\u2029external1'), require('./quoted\'\r\n\u2028\u2029external2')) : + typeof define === 'function' && define.amd ? define(['quoted\'\r\n\u2028\u2029external1', './quoted\'\r\n\u2028\u2029external2'], factory) : + (global = global || self, factory(global.quotedExternal1, global.quotedExternal2)); +}(this, (function (quoted_____external1, quoted_____external2) { 'use strict'; + + console.log(quoted_____external1.foo, quoted_____external2.bar); + +}))); diff --git a/test/form/samples/quote-id/main.js b/test/form/samples/quote-id/main.js new file mode 100644 index 00000000000..f392585109d --- /dev/null +++ b/test/form/samples/quote-id/main.js @@ -0,0 +1,3 @@ +import { foo } from 'external1'; +import { bar } from 'external2'; +console.log(foo, bar);