diff --git a/internal/module/resolver.go b/internal/module/resolver.go index 2b6612d625..f5ae94746f 100644 --- a/internal/module/resolver.go +++ b/internal/module/resolver.go @@ -532,7 +532,7 @@ func (r *resolutionState) loadModuleFromSelfNameReference() *resolved { } func (r *resolutionState) loadModuleFromImports() *resolved { - if r.name == "#" || strings.HasPrefix(r.name, "#/") { + if r.name == "#" || (strings.HasPrefix(r.name, "#/") && (r.features&NodeResolutionFeaturesImportsPatternRoot) == 0) { if r.tracer != nil { r.tracer.write(diagnostics.Invalid_import_specifier_0_has_no_possible_resolutions, r.name) } diff --git a/internal/module/types.go b/internal/module/types.go index 7999e022a3..59b79950f3 100644 --- a/internal/module/types.go +++ b/internal/module/types.go @@ -33,12 +33,15 @@ const ( NodeResolutionFeaturesSelfName NodeResolutionFeaturesExports NodeResolutionFeaturesExportsPatternTrailers + // allowing `#/` root imports in package.json imports field + // not supported until mass adoption - https://github.com/nodejs/node/pull/60864 + NodeResolutionFeaturesImportsPatternRoot NodeResolutionFeaturesNone NodeResolutionFeatures = 0 - NodeResolutionFeaturesAll = NodeResolutionFeaturesImports | NodeResolutionFeaturesSelfName | NodeResolutionFeaturesExports | NodeResolutionFeaturesExportsPatternTrailers + NodeResolutionFeaturesAll = NodeResolutionFeaturesImports | NodeResolutionFeaturesSelfName | NodeResolutionFeaturesExports | NodeResolutionFeaturesExportsPatternTrailers | NodeResolutionFeaturesImportsPatternRoot NodeResolutionFeaturesNode16Default = NodeResolutionFeaturesImports | NodeResolutionFeaturesSelfName | NodeResolutionFeaturesExports | NodeResolutionFeaturesExportsPatternTrailers NodeResolutionFeaturesNodeNextDefault = NodeResolutionFeaturesAll - NodeResolutionFeaturesBundlerDefault = NodeResolutionFeaturesImports | NodeResolutionFeaturesSelfName | NodeResolutionFeaturesExports | NodeResolutionFeaturesExportsPatternTrailers + NodeResolutionFeaturesBundlerDefault = NodeResolutionFeaturesImports | NodeResolutionFeaturesSelfName | NodeResolutionFeaturesExports | NodeResolutionFeaturesExportsPatternTrailers | NodeResolutionFeaturesImportsPatternRoot ) type PackageId struct { diff --git a/testdata/baselines/reference/compiler/nodeModulesPackageImportsRootWildcard.js b/testdata/baselines/reference/compiler/nodeModulesPackageImportsRootWildcard.js new file mode 100644 index 0000000000..799bfc0beb --- /dev/null +++ b/testdata/baselines/reference/compiler/nodeModulesPackageImportsRootWildcard.js @@ -0,0 +1,89 @@ +//// [tests/cases/compiler/nodeModulesPackageImportsRootWildcard.ts] //// + +//// [package.json] +{ + "name": "package", + "private": true, + "type": "module", + "imports": { + "#/*": "./src/*" + } +} +//// [foo.ts] +export const foo = "foo"; +//// [bar.ts] +export const bar = "bar"; +//// [baz.ts] +export const baz = "baz"; +//// [index.ts] +// esm format file +import { foo } from "#/foo.js"; +import { bar } from "#/features/bar.js"; +import { baz } from "#/nested/deep/baz.js"; +foo; +bar; +baz; +//// [index.mts] +// esm format file +import { foo } from "#/foo.js"; +import { bar } from "#/features/bar.js"; +import { baz } from "#/nested/deep/baz.js"; +foo; +bar; +baz; +//// [index.cts] +// cjs format file +import { foo } from "#/foo.js"; +import { bar } from "#/features/bar.js"; +import { baz } from "#/nested/deep/baz.js"; +foo; +bar; +baz; + + +//// [foo.js] +export const foo = "foo"; +//// [bar.js] +export const bar = "bar"; +//// [baz.js] +export const baz = "baz"; +//// [index.js] +// esm format file +import { foo } from "#/foo.js"; +import { bar } from "#/features/bar.js"; +import { baz } from "#/nested/deep/baz.js"; +foo; +bar; +baz; +//// [index.mjs] +// esm format file +import { foo } from "#/foo.js"; +import { bar } from "#/features/bar.js"; +import { baz } from "#/nested/deep/baz.js"; +foo; +bar; +baz; +//// [index.cjs] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +// cjs format file +const foo_js_1 = require("#/foo.js"); +const bar_js_1 = require("#/features/bar.js"); +const baz_js_1 = require("#/nested/deep/baz.js"); +foo_js_1.foo; +bar_js_1.bar; +baz_js_1.baz; + + +//// [foo.d.ts] +export declare const foo = "foo"; +//// [bar.d.ts] +export declare const bar = "bar"; +//// [baz.d.ts] +export declare const baz = "baz"; +//// [index.d.ts] +export {}; +//// [index.d.mts] +export {}; +//// [index.d.cts] +export {}; diff --git a/testdata/baselines/reference/compiler/nodeModulesPackageImportsRootWildcard.symbols b/testdata/baselines/reference/compiler/nodeModulesPackageImportsRootWildcard.symbols new file mode 100644 index 0000000000..3035fc230e --- /dev/null +++ b/testdata/baselines/reference/compiler/nodeModulesPackageImportsRootWildcard.symbols @@ -0,0 +1,74 @@ +//// [tests/cases/compiler/nodeModulesPackageImportsRootWildcard.ts] //// + +=== src/foo.ts === +export const foo = "foo"; +>foo : Symbol(foo, Decl(foo.ts, 0, 12)) + +=== src/features/bar.ts === +export const bar = "bar"; +>bar : Symbol(bar, Decl(bar.ts, 0, 12)) + +=== src/nested/deep/baz.ts === +export const baz = "baz"; +>baz : Symbol(baz, Decl(baz.ts, 0, 12)) + +=== index.ts === +// esm format file +import { foo } from "#/foo.js"; +>foo : Symbol(foo, Decl(index.ts, 1, 8)) + +import { bar } from "#/features/bar.js"; +>bar : Symbol(bar, Decl(index.ts, 2, 8)) + +import { baz } from "#/nested/deep/baz.js"; +>baz : Symbol(baz, Decl(index.ts, 3, 8)) + +foo; +>foo : Symbol(foo, Decl(index.ts, 1, 8)) + +bar; +>bar : Symbol(bar, Decl(index.ts, 2, 8)) + +baz; +>baz : Symbol(baz, Decl(index.ts, 3, 8)) + +=== index.mts === +// esm format file +import { foo } from "#/foo.js"; +>foo : Symbol(foo, Decl(index.mts, 1, 8)) + +import { bar } from "#/features/bar.js"; +>bar : Symbol(bar, Decl(index.mts, 2, 8)) + +import { baz } from "#/nested/deep/baz.js"; +>baz : Symbol(baz, Decl(index.mts, 3, 8)) + +foo; +>foo : Symbol(foo, Decl(index.mts, 1, 8)) + +bar; +>bar : Symbol(bar, Decl(index.mts, 2, 8)) + +baz; +>baz : Symbol(baz, Decl(index.mts, 3, 8)) + +=== index.cts === +// cjs format file +import { foo } from "#/foo.js"; +>foo : Symbol(foo, Decl(index.cts, 1, 8)) + +import { bar } from "#/features/bar.js"; +>bar : Symbol(bar, Decl(index.cts, 2, 8)) + +import { baz } from "#/nested/deep/baz.js"; +>baz : Symbol(baz, Decl(index.cts, 3, 8)) + +foo; +>foo : Symbol(foo, Decl(index.cts, 1, 8)) + +bar; +>bar : Symbol(bar, Decl(index.cts, 2, 8)) + +baz; +>baz : Symbol(baz, Decl(index.cts, 3, 8)) + diff --git a/testdata/baselines/reference/compiler/nodeModulesPackageImportsRootWildcard.trace.json b/testdata/baselines/reference/compiler/nodeModulesPackageImportsRootWildcard.trace.json new file mode 100644 index 0000000000..446f02461e --- /dev/null +++ b/testdata/baselines/reference/compiler/nodeModulesPackageImportsRootWildcard.trace.json @@ -0,0 +1,72 @@ +======== Resolving module '#/foo.js' from '/.src/index.ts'. ======== +Module resolution kind is not specified, using 'NodeNext'. +Resolving in ESM mode with conditions 'import', 'types', 'node'. +Found 'package.json' at '/.src/package.json'. +Using 'imports' subpath '#/*' with target './src/foo.js'. +File name '/.src/src/foo.js' has a '.js' extension - stripping it. +File '/.src/src/foo.ts' exists - use it as a name resolution result. +======== Module name '#/foo.js' was successfully resolved to '/.src/src/foo.ts'. ======== +======== Resolving module '#/features/bar.js' from '/.src/index.ts'. ======== +Module resolution kind is not specified, using 'NodeNext'. +Resolving in ESM mode with conditions 'import', 'types', 'node'. +File '/.src/package.json' exists according to earlier cached lookups. +Using 'imports' subpath '#/*' with target './src/features/bar.js'. +File name '/.src/src/features/bar.js' has a '.js' extension - stripping it. +File '/.src/src/features/bar.ts' exists - use it as a name resolution result. +======== Module name '#/features/bar.js' was successfully resolved to '/.src/src/features/bar.ts'. ======== +======== Resolving module '#/nested/deep/baz.js' from '/.src/index.ts'. ======== +Module resolution kind is not specified, using 'NodeNext'. +Resolving in ESM mode with conditions 'import', 'types', 'node'. +File '/.src/package.json' exists according to earlier cached lookups. +Using 'imports' subpath '#/*' with target './src/nested/deep/baz.js'. +File name '/.src/src/nested/deep/baz.js' has a '.js' extension - stripping it. +File '/.src/src/nested/deep/baz.ts' exists - use it as a name resolution result. +======== Module name '#/nested/deep/baz.js' was successfully resolved to '/.src/src/nested/deep/baz.ts'. ======== +======== Resolving module '#/foo.js' from '/.src/index.mts'. ======== +Module resolution kind is not specified, using 'NodeNext'. +Resolving in ESM mode with conditions 'import', 'types', 'node'. +File '/.src/package.json' exists according to earlier cached lookups. +Using 'imports' subpath '#/*' with target './src/foo.js'. +File name '/.src/src/foo.js' has a '.js' extension - stripping it. +File '/.src/src/foo.ts' exists - use it as a name resolution result. +======== Module name '#/foo.js' was successfully resolved to '/.src/src/foo.ts'. ======== +======== Resolving module '#/features/bar.js' from '/.src/index.mts'. ======== +Module resolution kind is not specified, using 'NodeNext'. +Resolving in ESM mode with conditions 'import', 'types', 'node'. +File '/.src/package.json' exists according to earlier cached lookups. +Using 'imports' subpath '#/*' with target './src/features/bar.js'. +File name '/.src/src/features/bar.js' has a '.js' extension - stripping it. +File '/.src/src/features/bar.ts' exists - use it as a name resolution result. +======== Module name '#/features/bar.js' was successfully resolved to '/.src/src/features/bar.ts'. ======== +======== Resolving module '#/nested/deep/baz.js' from '/.src/index.mts'. ======== +Module resolution kind is not specified, using 'NodeNext'. +Resolving in ESM mode with conditions 'import', 'types', 'node'. +File '/.src/package.json' exists according to earlier cached lookups. +Using 'imports' subpath '#/*' with target './src/nested/deep/baz.js'. +File name '/.src/src/nested/deep/baz.js' has a '.js' extension - stripping it. +File '/.src/src/nested/deep/baz.ts' exists - use it as a name resolution result. +======== Module name '#/nested/deep/baz.js' was successfully resolved to '/.src/src/nested/deep/baz.ts'. ======== +======== Resolving module '#/foo.js' from '/.src/index.cts'. ======== +Module resolution kind is not specified, using 'NodeNext'. +Resolving in CJS mode with conditions 'require', 'types', 'node'. +File '/.src/package.json' exists according to earlier cached lookups. +Using 'imports' subpath '#/*' with target './src/foo.js'. +File name '/.src/src/foo.js' has a '.js' extension - stripping it. +File '/.src/src/foo.ts' exists - use it as a name resolution result. +======== Module name '#/foo.js' was successfully resolved to '/.src/src/foo.ts'. ======== +======== Resolving module '#/features/bar.js' from '/.src/index.cts'. ======== +Module resolution kind is not specified, using 'NodeNext'. +Resolving in CJS mode with conditions 'require', 'types', 'node'. +File '/.src/package.json' exists according to earlier cached lookups. +Using 'imports' subpath '#/*' with target './src/features/bar.js'. +File name '/.src/src/features/bar.js' has a '.js' extension - stripping it. +File '/.src/src/features/bar.ts' exists - use it as a name resolution result. +======== Module name '#/features/bar.js' was successfully resolved to '/.src/src/features/bar.ts'. ======== +======== Resolving module '#/nested/deep/baz.js' from '/.src/index.cts'. ======== +Module resolution kind is not specified, using 'NodeNext'. +Resolving in CJS mode with conditions 'require', 'types', 'node'. +File '/.src/package.json' exists according to earlier cached lookups. +Using 'imports' subpath '#/*' with target './src/nested/deep/baz.js'. +File name '/.src/src/nested/deep/baz.js' has a '.js' extension - stripping it. +File '/.src/src/nested/deep/baz.ts' exists - use it as a name resolution result. +======== Module name '#/nested/deep/baz.js' was successfully resolved to '/.src/src/nested/deep/baz.ts'. ======== diff --git a/testdata/baselines/reference/compiler/nodeModulesPackageImportsRootWildcard.types b/testdata/baselines/reference/compiler/nodeModulesPackageImportsRootWildcard.types new file mode 100644 index 0000000000..c5c9206f71 --- /dev/null +++ b/testdata/baselines/reference/compiler/nodeModulesPackageImportsRootWildcard.types @@ -0,0 +1,77 @@ +//// [tests/cases/compiler/nodeModulesPackageImportsRootWildcard.ts] //// + +=== src/foo.ts === +export const foo = "foo"; +>foo : "foo" +>"foo" : "foo" + +=== src/features/bar.ts === +export const bar = "bar"; +>bar : "bar" +>"bar" : "bar" + +=== src/nested/deep/baz.ts === +export const baz = "baz"; +>baz : "baz" +>"baz" : "baz" + +=== index.ts === +// esm format file +import { foo } from "#/foo.js"; +>foo : "foo" + +import { bar } from "#/features/bar.js"; +>bar : "bar" + +import { baz } from "#/nested/deep/baz.js"; +>baz : "baz" + +foo; +>foo : "foo" + +bar; +>bar : "bar" + +baz; +>baz : "baz" + +=== index.mts === +// esm format file +import { foo } from "#/foo.js"; +>foo : "foo" + +import { bar } from "#/features/bar.js"; +>bar : "bar" + +import { baz } from "#/nested/deep/baz.js"; +>baz : "baz" + +foo; +>foo : "foo" + +bar; +>bar : "bar" + +baz; +>baz : "baz" + +=== index.cts === +// cjs format file +import { foo } from "#/foo.js"; +>foo : "foo" + +import { bar } from "#/features/bar.js"; +>bar : "bar" + +import { baz } from "#/nested/deep/baz.js"; +>baz : "baz" + +foo; +>foo : "foo" + +bar; +>bar : "bar" + +baz; +>baz : "baz" + diff --git a/testdata/baselines/reference/compiler/nodeModulesPackageImportsRootWildcardNode16.errors.txt b/testdata/baselines/reference/compiler/nodeModulesPackageImportsRootWildcardNode16.errors.txt new file mode 100644 index 0000000000..ea7a1a8796 --- /dev/null +++ b/testdata/baselines/reference/compiler/nodeModulesPackageImportsRootWildcardNode16.errors.txt @@ -0,0 +1,21 @@ +index.ts(2,21): error TS2307: Cannot find module '#/foo.js' or its corresponding type declarations. + + +==== package.json (0 errors) ==== + { + "name": "package", + "private": true, + "type": "module", + "imports": { + "#/*": "./src/*" + } + } +==== src/foo.ts (0 errors) ==== + export const foo = "foo"; +==== index.ts (1 errors) ==== + // esm format file + import { foo } from "#/foo.js"; + ~~~~~~~~~~ +!!! error TS2307: Cannot find module '#/foo.js' or its corresponding type declarations. + foo; + \ No newline at end of file diff --git a/testdata/baselines/reference/compiler/nodeModulesPackageImportsRootWildcardNode16.js b/testdata/baselines/reference/compiler/nodeModulesPackageImportsRootWildcardNode16.js new file mode 100644 index 0000000000..7447e50858 --- /dev/null +++ b/testdata/baselines/reference/compiler/nodeModulesPackageImportsRootWildcardNode16.js @@ -0,0 +1,31 @@ +//// [tests/cases/compiler/nodeModulesPackageImportsRootWildcardNode16.ts] //// + +//// [package.json] +{ + "name": "package", + "private": true, + "type": "module", + "imports": { + "#/*": "./src/*" + } +} +//// [foo.ts] +export const foo = "foo"; +//// [index.ts] +// esm format file +import { foo } from "#/foo.js"; +foo; + + +//// [foo.js] +export const foo = "foo"; +//// [index.js] +// esm format file +import { foo } from "#/foo.js"; +foo; + + +//// [foo.d.ts] +export declare const foo = "foo"; +//// [index.d.ts] +export {}; diff --git a/testdata/baselines/reference/compiler/nodeModulesPackageImportsRootWildcardNode16.symbols b/testdata/baselines/reference/compiler/nodeModulesPackageImportsRootWildcardNode16.symbols new file mode 100644 index 0000000000..8e06737364 --- /dev/null +++ b/testdata/baselines/reference/compiler/nodeModulesPackageImportsRootWildcardNode16.symbols @@ -0,0 +1,14 @@ +//// [tests/cases/compiler/nodeModulesPackageImportsRootWildcardNode16.ts] //// + +=== src/foo.ts === +export const foo = "foo"; +>foo : Symbol(foo, Decl(foo.ts, 0, 12)) + +=== index.ts === +// esm format file +import { foo } from "#/foo.js"; +>foo : Symbol(foo, Decl(index.ts, 1, 8)) + +foo; +>foo : Symbol(foo, Decl(index.ts, 1, 8)) + diff --git a/testdata/baselines/reference/compiler/nodeModulesPackageImportsRootWildcardNode16.trace.json b/testdata/baselines/reference/compiler/nodeModulesPackageImportsRootWildcardNode16.trace.json new file mode 100644 index 0000000000..92546135b7 --- /dev/null +++ b/testdata/baselines/reference/compiler/nodeModulesPackageImportsRootWildcardNode16.trace.json @@ -0,0 +1,21 @@ +======== Resolving module '#/foo.js' from '/.src/index.ts'. ======== +Module resolution kind is not specified, using 'Node16'. +Resolving in ESM mode with conditions 'import', 'types', 'node'. +Invalid import specifier '#/foo.js' has no possible resolutions. +Found 'package.json' at '/.src/package.json'. +Loading module '#/foo.js' from 'node_modules' folder, target file types: TypeScript, JavaScript, Declaration. +Searching all ancestor node_modules directories for preferred extensions: TypeScript, Declaration. +Directory '/.src/node_modules' does not exist, skipping all lookups in it. +File name '/.src/node_modules/#/foo.js' has a '.js' extension - stripping it. +Directory '/.src/node_modules/@types' does not exist, skipping all lookups in it. +File name '/.src/node_modules/@types/#/foo.js' has a '.js' extension - stripping it. +Directory '/node_modules' does not exist, skipping all lookups in it. +File name '/node_modules/#/foo.js' has a '.js' extension - stripping it. +Directory '/node_modules/@types' does not exist, skipping all lookups in it. +File name '/node_modules/@types/#/foo.js' has a '.js' extension - stripping it. +Searching all ancestor node_modules directories for fallback extensions: JavaScript. +Directory '/.src/node_modules' does not exist, skipping all lookups in it. +File name '/.src/node_modules/#/foo.js' has a '.js' extension - stripping it. +Directory '/node_modules' does not exist, skipping all lookups in it. +File name '/node_modules/#/foo.js' has a '.js' extension - stripping it. +======== Module name '#/foo.js' was not resolved. ======== diff --git a/testdata/baselines/reference/compiler/nodeModulesPackageImportsRootWildcardNode16.types b/testdata/baselines/reference/compiler/nodeModulesPackageImportsRootWildcardNode16.types new file mode 100644 index 0000000000..54fd4afd51 --- /dev/null +++ b/testdata/baselines/reference/compiler/nodeModulesPackageImportsRootWildcardNode16.types @@ -0,0 +1,15 @@ +//// [tests/cases/compiler/nodeModulesPackageImportsRootWildcardNode16.ts] //// + +=== src/foo.ts === +export const foo = "foo"; +>foo : "foo" +>"foo" : "foo" + +=== index.ts === +// esm format file +import { foo } from "#/foo.js"; +>foo : any + +foo; +>foo : any + diff --git a/testdata/tests/cases/compiler/nodeModulesPackageImportsRootWildcard.ts b/testdata/tests/cases/compiler/nodeModulesPackageImportsRootWildcard.ts new file mode 100644 index 0000000000..5508eaab2f --- /dev/null +++ b/testdata/tests/cases/compiler/nodeModulesPackageImportsRootWildcard.ts @@ -0,0 +1,42 @@ +// @module: nodenext +// @declaration: true +// @traceResolution: true +// @filename: package.json +{ + "name": "package", + "private": true, + "type": "module", + "imports": { + "#/*": "./src/*" + } +} +// @filename: src/foo.ts +export const foo = "foo"; +// @filename: src/features/bar.ts +export const bar = "bar"; +// @filename: src/nested/deep/baz.ts +export const baz = "baz"; +// @filename: index.ts +// esm format file +import { foo } from "#/foo.js"; +import { bar } from "#/features/bar.js"; +import { baz } from "#/nested/deep/baz.js"; +foo; +bar; +baz; +// @filename: index.mts +// esm format file +import { foo } from "#/foo.js"; +import { bar } from "#/features/bar.js"; +import { baz } from "#/nested/deep/baz.js"; +foo; +bar; +baz; +// @filename: index.cts +// cjs format file +import { foo } from "#/foo.js"; +import { bar } from "#/features/bar.js"; +import { baz } from "#/nested/deep/baz.js"; +foo; +bar; +baz; diff --git a/testdata/tests/cases/compiler/nodeModulesPackageImportsRootWildcardNode16.ts b/testdata/tests/cases/compiler/nodeModulesPackageImportsRootWildcardNode16.ts new file mode 100644 index 0000000000..edd2b10c5a --- /dev/null +++ b/testdata/tests/cases/compiler/nodeModulesPackageImportsRootWildcardNode16.ts @@ -0,0 +1,18 @@ +// @module: node16 +// @declaration: true +// @traceResolution: true +// @filename: package.json +{ + "name": "package", + "private": true, + "type": "module", + "imports": { + "#/*": "./src/*" + } +} +// @filename: src/foo.ts +export const foo = "foo"; +// @filename: index.ts +// esm format file +import { foo } from "#/foo.js"; +foo;