diff --git a/src/application/ReferenceSourceImportEntries.ts b/src/application/ReferenceSourceImportEntries.ts index d2602df044..c53df6a9d1 100644 --- a/src/application/ReferenceSourceImportEntries.ts +++ b/src/application/ReferenceSourceImportEntries.ts @@ -110,9 +110,11 @@ const resolveInternalSpecifier = ( const directoryReference = specifier.endsWith("/") || [".", ".."].includes(posix.basename(specifier)); const suffixes = ["", ".ts", ".js", ".mjs", ".cjs", ".tsx", ".jsx"]; + // A directory resolves only to an index with an extension; neither Node nor + // TypeScript selects an extensionless `index` file. const candidates = suffixes.flatMap((suffix) => [ ...(directoryReference ? [] : [`${normalized}${suffix}`]), - posix.join(normalized, `index${suffix}`), + ...(suffix === "" ? [] : [posix.join(normalized, `index${suffix}`)]), ]); // Retain present runtime files first; substitute only a missing counterpart. // Counterparts cover explicit filenames, not directory/index resolution. diff --git a/tests/boundary/cli/referenceSourceDirectoryImports.test.ts b/tests/boundary/cli/referenceSourceDirectoryImports.test.ts index 6270855cbc..8fbd8a421c 100644 --- a/tests/boundary/cli/referenceSourceDirectoryImports.test.ts +++ b/tests/boundary/cli/referenceSourceDirectoryImports.test.ts @@ -29,11 +29,13 @@ describe.skipIf(process.platform === "win32")( ["direct.cjs", "./lib.js", "lib.js"], ["fallback.cjs", "./fallback", "fallback/index.js"], ["legacy-order.cjs", "./dep", "dep/index.js"], + ["bare-index.cjs", "./bare", "bare.js"], ] as const; const unresolved = [ ["missing.cjs", "./missing/", "missing/"], ["escape.cjs", "../outside/", "../outside/"], ["main.ts", "./folder.js/.", "folder.js"], + ["only-bare.cjs", "./only/", "only/"], ] as const; const files = new Map([ ["index.js", 'module.exports = "root index";\n'], @@ -48,6 +50,9 @@ describe.skipIf(process.platform === "win32")( 'export const value = "emitted filename counterpart";\n', ], ["missing.js", 'module.exports = "not a directory";\n'], + ["bare.js", 'module.exports = "file before directory";\n'], + ["bare/index", 'module.exports = "extensionless index";\n'], + ["only/index", 'module.exports = "extensionless index";\n'], ...[...imports, ...unresolved].map( ([path, specifier]) => [path, `require(${JSON.stringify(specifier)});\n`] as const, @@ -75,6 +80,9 @@ describe.skipIf(process.platform === "win32")( expect(() => createRequire(join(root, "main.ts")).resolve("./folder.js/."), ).toThrow(); + expect(() => + createRequire(join(root, "only-bare.cjs")).resolve("./only/"), + ).toThrow(); expect( createRequire(join(root, "escape.cjs")).resolve("../outside/"), ).toBe(outside);