diff --git a/src/rollup/types.d.ts b/src/rollup/types.d.ts index 710131e8144..fa03f8f18e9 100644 --- a/src/rollup/types.d.ts +++ b/src/rollup/types.d.ts @@ -590,7 +590,7 @@ export interface InputOptions { perf?: boolean; plugins?: InputPluginOption; preserveEntrySignatures?: PreserveEntrySignaturesOption; - preserveSymlinks?: boolean; + preserveSymlinks?: boolean | 'auto'; shimMissingExports?: boolean; strictDeprecations?: boolean; treeshake?: boolean | TreeshakingPreset | TreeshakingOptions; @@ -616,7 +616,7 @@ export interface NormalizedInputOptions { perf: boolean; plugins: Plugin[]; preserveEntrySignatures: PreserveEntrySignaturesOption; - preserveSymlinks: boolean; + preserveSymlinks: boolean | 'auto'; shimMissingExports: boolean; strictDeprecations: boolean; treeshake: false | NormalizedTreeshakingOptions; diff --git a/src/utils/resolveId.ts b/src/utils/resolveId.ts index 812f2e9012e..681d9c34575 100644 --- a/src/utils/resolveId.ts +++ b/src/utils/resolveId.ts @@ -8,7 +8,7 @@ import { resolveIdViaPlugins } from './resolveIdViaPlugins'; export async function resolveId( source: string, importer: string | undefined, - preserveSymlinks: boolean, + preserveSymlinks: boolean | 'auto', pluginDriver: PluginDriver, moduleLoaderResolveId: ModuleLoaderResolveId, skip: readonly { importer: string | undefined; plugin: Plugin; source: string }[] | null, @@ -48,14 +48,31 @@ export async function resolveId( // are skipped at this stage. if (importer !== undefined && !isAbsolute(source) && source[0] !== '.') return null; - // `resolve` processes paths from right to left, prepending them until an - // absolute path is created. Absolute importees therefore shortcircuit the - // resolve call and require no special handing on our part. - // See https://nodejs.org/api/path.html#path_path_resolve_paths - return addJsExtensionIfNecessary( - importer ? resolve(dirname(importer), source) : resolve(source), - preserveSymlinks - ); + switch (preserveSymlinks) { + case 'auto': { + // Try to resolve the importee. + if (!importer) return addJsExtensionIfNecessary(resolve(source), true); + const resolved = await addJsExtensionIfNecessary(resolve(dirname(importer), source), true); + if (resolved) return resolved; + + // Since resolving the importee failed, see if the importer is a symlink and can be resolved. + importer = await addJsExtensionIfNecessary(resolve(importer), false); + if (!importer) return; + + // Try again + return addJsExtensionIfNecessary(resolve(dirname(importer), source), true); + } + default: { + // `resolve` processes paths from right to left, prepending them until an + // absolute path is created. Absolute importees therefore shortcircuit the + // resolve call and require no special handing on our part. + // See https://nodejs.org/api/path.html#path_path_resolve_paths + return addJsExtensionIfNecessary( + importer ? resolve(dirname(importer), source) : resolve(source), + preserveSymlinks + ); + } + } } async function addJsExtensionIfNecessary( diff --git a/test/function/samples/preserve-symlink-auto-follow/_config.js b/test/function/samples/preserve-symlink-auto-follow/_config.js new file mode 100644 index 00000000000..97413750381 --- /dev/null +++ b/test/function/samples/preserve-symlink-auto-follow/_config.js @@ -0,0 +1,7 @@ +module.exports = defineTest({ + skipIfWindows: true, + description: 'auto symlinks', + options: { + preserveSymlinks: 'auto' + } +}); diff --git a/test/function/samples/preserve-symlink-auto-follow/foo.js b/test/function/samples/preserve-symlink-auto-follow/foo.js new file mode 120000 index 00000000000..e72d2ba25d7 --- /dev/null +++ b/test/function/samples/preserve-symlink-auto-follow/foo.js @@ -0,0 +1 @@ +symlinked/foo.js \ No newline at end of file diff --git a/test/function/samples/preserve-symlink-auto-follow/main.js b/test/function/samples/preserve-symlink-auto-follow/main.js new file mode 100644 index 00000000000..06d0e4a5da6 --- /dev/null +++ b/test/function/samples/preserve-symlink-auto-follow/main.js @@ -0,0 +1,2 @@ +import foo from './foo.js'; +assert.equal(foo, 'BAZ'); diff --git a/test/function/samples/preserve-symlink-auto-follow/symlinked/bar.js b/test/function/samples/preserve-symlink-auto-follow/symlinked/bar.js new file mode 100644 index 00000000000..d08083eef99 --- /dev/null +++ b/test/function/samples/preserve-symlink-auto-follow/symlinked/bar.js @@ -0,0 +1,3 @@ +export default function () { + return 'BAZ'; +} diff --git a/test/function/samples/preserve-symlink-auto-follow/symlinked/foo.js b/test/function/samples/preserve-symlink-auto-follow/symlinked/foo.js new file mode 100644 index 00000000000..2f145c44589 --- /dev/null +++ b/test/function/samples/preserve-symlink-auto-follow/symlinked/foo.js @@ -0,0 +1,2 @@ +import bar from './bar.js'; +export default bar(); diff --git a/test/function/samples/preserve-symlink-auto-no-follow/_config.js b/test/function/samples/preserve-symlink-auto-no-follow/_config.js new file mode 100644 index 00000000000..97413750381 --- /dev/null +++ b/test/function/samples/preserve-symlink-auto-no-follow/_config.js @@ -0,0 +1,7 @@ +module.exports = defineTest({ + skipIfWindows: true, + description: 'auto symlinks', + options: { + preserveSymlinks: 'auto' + } +}); diff --git a/test/function/samples/preserve-symlink-auto-no-follow/bar.js b/test/function/samples/preserve-symlink-auto-no-follow/bar.js new file mode 100644 index 00000000000..3a70296e3cd --- /dev/null +++ b/test/function/samples/preserve-symlink-auto-no-follow/bar.js @@ -0,0 +1,3 @@ +export default function () { + return 'BAR'; +} diff --git a/test/function/samples/preserve-symlink-auto-no-follow/foo.js b/test/function/samples/preserve-symlink-auto-no-follow/foo.js new file mode 120000 index 00000000000..e72d2ba25d7 --- /dev/null +++ b/test/function/samples/preserve-symlink-auto-no-follow/foo.js @@ -0,0 +1 @@ +symlinked/foo.js \ No newline at end of file diff --git a/test/function/samples/preserve-symlink-auto-no-follow/main.js b/test/function/samples/preserve-symlink-auto-no-follow/main.js new file mode 100644 index 00000000000..3b64bae5806 --- /dev/null +++ b/test/function/samples/preserve-symlink-auto-no-follow/main.js @@ -0,0 +1,2 @@ +import foo from './foo.js'; +assert.equal(foo, 'BAR'); diff --git a/test/function/samples/preserve-symlink-auto-no-follow/symlinked/bar.js b/test/function/samples/preserve-symlink-auto-no-follow/symlinked/bar.js new file mode 100644 index 00000000000..d08083eef99 --- /dev/null +++ b/test/function/samples/preserve-symlink-auto-no-follow/symlinked/bar.js @@ -0,0 +1,3 @@ +export default function () { + return 'BAZ'; +} diff --git a/test/function/samples/preserve-symlink-auto-no-follow/symlinked/foo.js b/test/function/samples/preserve-symlink-auto-no-follow/symlinked/foo.js new file mode 100644 index 00000000000..2f145c44589 --- /dev/null +++ b/test/function/samples/preserve-symlink-auto-no-follow/symlinked/foo.js @@ -0,0 +1,2 @@ +import bar from './bar.js'; +export default bar();