diff --git a/cli/help.md b/cli/help.md index 5918065a02f..947a2bd1848 100644 --- a/cli/help.md +++ b/cli/help.md @@ -23,6 +23,7 @@ Basic options: --amd.autoId Generate the AMD ID based off the chunk name --amd.basePath Path to prepend to auto generated AMD ID --amd.define Function to use in place of `define` +--amd.forceJsExtensionForImports Use `.js` extension in AMD imports --assetFileNames Name pattern for emitted assets --banner Code to insert at top of bundle (outside wrapper) --chunkFileNames Name pattern for emitted secondary chunks diff --git a/docs/01-command-line-reference.md b/docs/01-command-line-reference.md index 5aa845b6faa..525a360b983 100755 --- a/docs/01-command-line-reference.md +++ b/docs/01-command-line-reference.md @@ -358,6 +358,7 @@ Many options have command line equivalents. In those cases, any arguments passed --amd.autoId Generate the AMD ID based off the chunk name --amd.basePath Path to prepend to auto generated AMD ID --amd.define Function to use in place of `define` +--amd.forceJsExtensionForImports Use `.js` extension in AMD imports --assetFileNames Name pattern for emitted assets --banner Code to insert at top of bundle (outside wrapper) --chunkFileNames Name pattern for emitted secondary chunks diff --git a/docs/999-big-list-of-options.md b/docs/999-big-list-of-options.md index a251653576b..da4aace22a4 100755 --- a/docs/999-big-list-of-options.md +++ b/docs/999-big-list-of-options.md @@ -1331,6 +1331,23 @@ export default { // -> def(['dependency'],... ``` +**output.amd.forceJsExtensionForImports**
Type: `boolean`
CLI: `--amd.forceJsExtensionForImports`
Default: `false` + +Add `.js` extension for imports of generated chunks and local AMD modules: + +```js +// rollup.config.js +export default { + ..., + format: 'amd', + amd: { + forceJsExtensionForImports: true + } +}; + +// -> define(['./chunk-or-local-file.js', 'dependency', 'third/dependency'],... +``` + #### output.esModule Type: `boolean`
CLI: `--esModule`/`--no-esModule`
Default: `true` diff --git a/src/Chunk.ts b/src/Chunk.ts index bb7fdee3484..bea8d36972c 100644 --- a/src/Chunk.ts +++ b/src/Chunk.ts @@ -938,7 +938,8 @@ export default class Chunk { options: NormalizedOutputOptions, snippets: GenerateCodeSnippets ): void { - const stripKnownJsExtensions = options.format === 'amd'; + const stripKnownJsExtensions = + options.format === 'amd' && !options.amd.forceJsExtensionForImports; for (const [module, code] of this.renderedModuleSources) { for (const { node, resolution } of module.dynamicImports) { const chunk = this.chunkByModule.get(resolution as Module); diff --git a/src/finalisers/amd.ts b/src/finalisers/amd.ts index 7d409f71b07..244c8d6f8a0 100644 --- a/src/finalisers/amd.ts +++ b/src/finalisers/amd.ts @@ -3,7 +3,7 @@ import type { NormalizedOutputOptions } from '../rollup/types'; import getCompleteAmdId from './shared/getCompleteAmdId'; import { getExportBlock, getNamespaceMarkers } from './shared/getExportBlock'; import getInteropBlock from './shared/getInteropBlock'; -import removeExtensionFromRelativeAmdId from './shared/removeExtensionFromRelativeAmdId'; +import updateExtensionForRelativeAmdId from './shared/updateExtensionForRelativeAmdId'; import warnOnBuiltins from './shared/warnOnBuiltins'; import type { FinaliserOptions } from './index'; @@ -35,7 +35,9 @@ export default function amd( }: NormalizedOutputOptions ): Bundle { warnOnBuiltins(warn, dependencies); - const deps = dependencies.map(m => `'${removeExtensionFromRelativeAmdId(m.id)}'`); + const deps = dependencies.map( + m => `'${updateExtensionForRelativeAmdId(m.id, amd.forceJsExtensionForImports)}'` + ); const args = dependencies.map(m => m.name); const { n, getNonArrowFunctionIntro, _ } = snippets; diff --git a/src/finalisers/shared/addJsExtension.ts b/src/finalisers/shared/addJsExtension.ts new file mode 100644 index 00000000000..4c483d935ee --- /dev/null +++ b/src/finalisers/shared/addJsExtension.ts @@ -0,0 +1,3 @@ +export default function addJsExtension(name: string): string { + return name.endsWith('.js') ? name : name + '.js'; +} diff --git a/src/finalisers/shared/removeExtensionFromRelativeAmdId.ts b/src/finalisers/shared/removeExtensionFromRelativeAmdId.ts deleted file mode 100644 index 4b04af1e7c9..00000000000 --- a/src/finalisers/shared/removeExtensionFromRelativeAmdId.ts +++ /dev/null @@ -1,8 +0,0 @@ -import removeJsExtension from './removeJsExtension'; - -// AMD resolution will only respect the AMD baseUrl if the .js extension is omitted. -// The assumption is that this makes sense for all relative ids: -// https://requirejs.org/docs/api.html#jsfiles -export default function removeExtensionFromRelativeAmdId(id: string): string { - return id[0] === '.' ? removeJsExtension(id) : id; -} diff --git a/src/finalisers/shared/updateExtensionForRelativeAmdId.ts b/src/finalisers/shared/updateExtensionForRelativeAmdId.ts new file mode 100644 index 00000000000..2baf20f444b --- /dev/null +++ b/src/finalisers/shared/updateExtensionForRelativeAmdId.ts @@ -0,0 +1,16 @@ +import addJsExtension from './addJsExtension'; +import removeJsExtension from './removeJsExtension'; + +// AMD resolution will only respect the AMD baseUrl if the .js extension is omitted. +// The assumption is that this makes sense for all relative ids: +// https://requirejs.org/docs/api.html#jsfiles +export default function updateExtensionForRelativeAmdId( + id: string, + forceJsExtensionForImports: boolean +): string { + if (id[0] !== '.') { + return id; + } + + return forceJsExtensionForImports ? addJsExtension(id) : removeJsExtension(id); +} diff --git a/src/finalisers/umd.ts b/src/finalisers/umd.ts index 46292d50df9..30d08225fee 100644 --- a/src/finalisers/umd.ts +++ b/src/finalisers/umd.ts @@ -5,10 +5,10 @@ import type { GenerateCodeSnippets } from '../utils/generateCodeSnippets'; import getCompleteAmdId from './shared/getCompleteAmdId'; import { getExportBlock, getNamespaceMarkers } from './shared/getExportBlock'; import getInteropBlock from './shared/getInteropBlock'; -import removeExtensionFromRelativeAmdId from './shared/removeExtensionFromRelativeAmdId'; import { keypath } from './shared/sanitize'; import { assignToDeepVariable } from './shared/setupNamespace'; import trimEmptyImports from './shared/trimEmptyImports'; +import updateExtensionForRelativeAmdId from './shared/updateExtensionForRelativeAmdId'; import warnOnBuiltins from './shared/warnOnBuiltins'; import type { FinaliserOptions } from './index'; @@ -73,7 +73,9 @@ export default function umd( warnOnBuiltins(warn, dependencies); - const amdDeps = dependencies.map(m => `'${removeExtensionFromRelativeAmdId(m.id)}'`); + const amdDeps = dependencies.map( + m => `'${updateExtensionForRelativeAmdId(m.id, amd.forceJsExtensionForImports)}'` + ); const cjsDeps = dependencies.map(m => `require('${m.id}')`); const trimmedImports = trimEmptyImports(dependencies); diff --git a/src/rollup/types.d.ts b/src/rollup/types.d.ts index 29e6cb4db37..d235d9a03b1 100644 --- a/src/rollup/types.d.ts +++ b/src/rollup/types.d.ts @@ -631,6 +631,7 @@ export type AmdOptions = ( } ) & { define?: string; + forceJsExtensionForImports?: boolean; }; export type NormalizedAmdOptions = ( @@ -644,6 +645,7 @@ export type NormalizedAmdOptions = ( } ) & { define: string; + forceJsExtensionForImports: boolean; }; export interface OutputOptions { diff --git a/src/utils/options/normalizeOutputOptions.ts b/src/utils/options/normalizeOutputOptions.ts index 26974c88147..4fdf9143dec 100644 --- a/src/utils/options/normalizeOutputOptions.ts +++ b/src/utils/options/normalizeOutputOptions.ts @@ -225,10 +225,17 @@ const getPreserveModulesRoot = ( }; const getAmd = (config: OutputOptions): NormalizedOutputOptions['amd'] => { - const mergedOption: { autoId: boolean; basePath: string; define: string; id?: string } = { + const mergedOption: { + autoId: boolean; + basePath: string; + define: string; + forceJsExtensionForImports: boolean; + id?: string; + } = { autoId: false, basePath: '', define: 'define', + forceJsExtensionForImports: false, ...config.amd }; @@ -256,12 +263,14 @@ const getAmd = (config: OutputOptions): NormalizedOutputOptions['amd'] => { normalized = { autoId: true, basePath: mergedOption.basePath, - define: mergedOption.define + define: mergedOption.define, + forceJsExtensionForImports: mergedOption.forceJsExtensionForImports }; } else { normalized = { autoId: false, define: mergedOption.define, + forceJsExtensionForImports: mergedOption.forceJsExtensionForImports, id: mergedOption.id }; } diff --git a/test/form/samples/amd-keep-extension/_config.js b/test/form/samples/amd-keep-extension/_config.js new file mode 100644 index 00000000000..f7ada5d8e9b --- /dev/null +++ b/test/form/samples/amd-keep-extension/_config.js @@ -0,0 +1,7 @@ +module.exports = { + description: 'keep extension for AMD modules', + options: { + external: ['./relative', 'abso/lute', './relative.js', 'abso/lute.js'], + output: { amd: { forceJsExtensionForImports: true }, interop: 'default' } + } +}; diff --git a/test/form/samples/amd-keep-extension/_expected/amd.js b/test/form/samples/amd-keep-extension/_expected/amd.js new file mode 100644 index 00000000000..85d309017dd --- /dev/null +++ b/test/form/samples/amd-keep-extension/_expected/amd.js @@ -0,0 +1,5 @@ +define(['./relative.js', 'abso/lute', './relative.js', 'abso/lute.js'], (function (relative, absolute, relativeExtension, absoluteExtension) { 'use strict'; + + console.log(relative, absolute, relativeExtension, absoluteExtension); + +})); diff --git a/test/form/samples/amd-keep-extension/_expected/cjs.js b/test/form/samples/amd-keep-extension/_expected/cjs.js new file mode 100644 index 00000000000..d2ccd7929f1 --- /dev/null +++ b/test/form/samples/amd-keep-extension/_expected/cjs.js @@ -0,0 +1,8 @@ +'use strict'; + +var relative = require('./relative'); +var absolute = require('abso/lute'); +var relativeExtension = require('./relative.js'); +var absoluteExtension = require('abso/lute.js'); + +console.log(relative, absolute, relativeExtension, absoluteExtension); diff --git a/test/form/samples/amd-keep-extension/_expected/es.js b/test/form/samples/amd-keep-extension/_expected/es.js new file mode 100644 index 00000000000..946ad911dcd --- /dev/null +++ b/test/form/samples/amd-keep-extension/_expected/es.js @@ -0,0 +1,6 @@ +import relative from './relative'; +import absolute from 'abso/lute'; +import relativeExtension from './relative.js'; +import absoluteExtension from 'abso/lute.js'; + +console.log(relative, absolute, relativeExtension, absoluteExtension); diff --git a/test/form/samples/amd-keep-extension/_expected/iife.js b/test/form/samples/amd-keep-extension/_expected/iife.js new file mode 100644 index 00000000000..8386b840067 --- /dev/null +++ b/test/form/samples/amd-keep-extension/_expected/iife.js @@ -0,0 +1,6 @@ +(function (relative, absolute, relativeExtension, absoluteExtension) { + 'use strict'; + + console.log(relative, absolute, relativeExtension, absoluteExtension); + +})(relative, absolute, relativeExtension, absoluteExtension); diff --git a/test/form/samples/amd-keep-extension/_expected/system.js b/test/form/samples/amd-keep-extension/_expected/system.js new file mode 100644 index 00000000000..c9483aa3c95 --- /dev/null +++ b/test/form/samples/amd-keep-extension/_expected/system.js @@ -0,0 +1,20 @@ +System.register(['./relative', 'abso/lute', './relative.js', 'abso/lute.js'], (function () { + 'use strict'; + var relative, absolute, relativeExtension, absoluteExtension; + return { + setters: [function (module) { + relative = module["default"]; + }, function (module) { + absolute = module["default"]; + }, function (module) { + relativeExtension = module["default"]; + }, function (module) { + absoluteExtension = module["default"]; + }], + execute: (function () { + + console.log(relative, absolute, relativeExtension, absoluteExtension); + + }) + }; +})); diff --git a/test/form/samples/amd-keep-extension/_expected/umd.js b/test/form/samples/amd-keep-extension/_expected/umd.js new file mode 100644 index 00000000000..6418f82b4ba --- /dev/null +++ b/test/form/samples/amd-keep-extension/_expected/umd.js @@ -0,0 +1,9 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(require('./relative'), require('abso/lute'), require('./relative.js'), require('abso/lute.js')) : + typeof define === 'function' && define.amd ? define(['./relative.js', 'abso/lute', './relative.js', 'abso/lute.js'], factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.relative, global.absolute, global.relativeExtension, global.absoluteExtension)); +})(this, (function (relative, absolute, relativeExtension, absoluteExtension) { 'use strict'; + + console.log(relative, absolute, relativeExtension, absoluteExtension); + +})); diff --git a/test/form/samples/amd-keep-extension/main.js b/test/form/samples/amd-keep-extension/main.js new file mode 100644 index 00000000000..946ad911dcd --- /dev/null +++ b/test/form/samples/amd-keep-extension/main.js @@ -0,0 +1,6 @@ +import relative from './relative'; +import absolute from 'abso/lute'; +import relativeExtension from './relative.js'; +import absoluteExtension from 'abso/lute.js'; + +console.log(relative, absolute, relativeExtension, absoluteExtension); diff --git a/test/function/samples/output-options-hook/_config.js b/test/function/samples/output-options-hook/_config.js index bdb7cc2cec3..e52ab8da8eb 100644 --- a/test/function/samples/output-options-hook/_config.js +++ b/test/function/samples/output-options-hook/_config.js @@ -18,7 +18,8 @@ module.exports = { assert.deepStrictEqual(JSON.parse(JSON.stringify(options)), { amd: { define: 'define', - autoId: false + autoId: false, + forceJsExtensionForImports: false }, assetFileNames: 'assets/[name]-[hash][extname]', chunkFileNames: '[name]-[hash].js',