From 8ec593aec2504a2257069032e14c1f9d7454b12c Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Thu, 6 Apr 2017 08:01:15 -0700 Subject: [PATCH 1/2] Support @types module resolution from scoped packages --- src/compiler/diagnosticMessages.json | 4 +++ src/compiler/moduleNameResolver.ts | 15 +++++++++- .../library-reference-scoped-packages.js | 11 +++++++ .../library-reference-scoped-packages.symbols | 7 +++++ ...brary-reference-scoped-packages.trace.json | 12 ++++++++ .../library-reference-scoped-packages.types | 8 +++++ tests/baselines/reference/scopedPackages.js | 20 +++++++++++++ .../reference/scopedPackages.symbols | 22 ++++++++++++++ .../reference/scopedPackages.trace.json | 30 +++++++++++++++++++ .../baselines/reference/scopedPackages.types | 22 ++++++++++++++ .../reference/scopedPackagesClassic.js | 12 ++++++++ .../reference/scopedPackagesClassic.symbols | 8 +++++ .../scopedPackagesClassic.trace.json | 9 ++++++ .../reference/scopedPackagesClassic.types | 9 ++++++ .../moduleResolution/scopedPackages.ts | 17 +++++++++++ .../moduleResolution/scopedPackagesClassic.ts | 10 +++++++ .../library-reference-scoped-packages.ts | 9 ++++++ 17 files changed, 224 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/library-reference-scoped-packages.js create mode 100644 tests/baselines/reference/library-reference-scoped-packages.symbols create mode 100644 tests/baselines/reference/library-reference-scoped-packages.trace.json create mode 100644 tests/baselines/reference/library-reference-scoped-packages.types create mode 100644 tests/baselines/reference/scopedPackages.js create mode 100644 tests/baselines/reference/scopedPackages.symbols create mode 100644 tests/baselines/reference/scopedPackages.trace.json create mode 100644 tests/baselines/reference/scopedPackages.types create mode 100644 tests/baselines/reference/scopedPackagesClassic.js create mode 100644 tests/baselines/reference/scopedPackagesClassic.symbols create mode 100644 tests/baselines/reference/scopedPackagesClassic.trace.json create mode 100644 tests/baselines/reference/scopedPackagesClassic.types create mode 100644 tests/cases/conformance/moduleResolution/scopedPackages.ts create mode 100644 tests/cases/conformance/moduleResolution/scopedPackagesClassic.ts create mode 100644 tests/cases/conformance/references/library-reference-scoped-packages.ts diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 075d4247460..6a5b6fa7526 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3177,6 +3177,10 @@ "category": "Message", "code": 6181 }, + "Scoped package detected, looking in '{0}'": { + "category": "Message", + "code": "6182" + }, "Variable '{0}' implicitly has an '{1}' type.": { "category": "Error", diff --git a/src/compiler/moduleNameResolver.ts b/src/compiler/moduleNameResolver.ts index 5280c794725..44779c3b482 100644 --- a/src/compiler/moduleNameResolver.ts +++ b/src/compiler/moduleNameResolver.ts @@ -954,10 +954,23 @@ namespace ts { } nodeModulesAtTypesExists = false; } - return loadModuleFromNodeModulesFolder(Extensions.DtsOnly, moduleName, nodeModulesAtTypes, nodeModulesAtTypesExists, failedLookupLocations, state); + return loadModuleFromNodeModulesFolder(Extensions.DtsOnly, mangleScopedPackage(moduleName, state.host), nodeModulesAtTypes, nodeModulesAtTypesExists, failedLookupLocations, state); } } + /** For a scoped package, we must look in `@types/foo__bar` instead of `@types/@foo/bar`. */ + function mangleScopedPackage(moduleName: string, host: ModuleResolutionHost): string { + if (startsWith(moduleName, "@")) { + const replaceSlash = moduleName.replace(ts.directorySeparator, "__"); + if (replaceSlash !== moduleName) { + const mangled = replaceSlash.slice(1); // Take off the "@" + trace(host, Diagnostics.Scoped_package_detected_looking_in_0, mangled); + return mangled; + } + } + return moduleName; + } + function tryFindNonRelativeModuleNameInCache(cache: PerModuleNameCache | undefined, moduleName: string, containingDirectory: string, traceEnabled: boolean, host: ModuleResolutionHost): SearchResult { const result = cache && cache.get(containingDirectory); if (result) { diff --git a/tests/baselines/reference/library-reference-scoped-packages.js b/tests/baselines/reference/library-reference-scoped-packages.js new file mode 100644 index 00000000000..3019008c16b --- /dev/null +++ b/tests/baselines/reference/library-reference-scoped-packages.js @@ -0,0 +1,11 @@ +//// [tests/cases/conformance/references/library-reference-scoped-packages.ts] //// + +//// [index.d.ts] +export const y = 0; + +//// [a.ts] +/// + + +//// [a.js] +/// diff --git a/tests/baselines/reference/library-reference-scoped-packages.symbols b/tests/baselines/reference/library-reference-scoped-packages.symbols new file mode 100644 index 00000000000..60ccaab89d2 --- /dev/null +++ b/tests/baselines/reference/library-reference-scoped-packages.symbols @@ -0,0 +1,7 @@ +=== /a.ts === +/// +No type information for this code. +No type information for this code.=== /node_modules/@types/beep__boop/index.d.ts === +export const y = 0; +>y : Symbol(y, Decl(index.d.ts, 0, 12)) + diff --git a/tests/baselines/reference/library-reference-scoped-packages.trace.json b/tests/baselines/reference/library-reference-scoped-packages.trace.json new file mode 100644 index 00000000000..54dcf95fe50 --- /dev/null +++ b/tests/baselines/reference/library-reference-scoped-packages.trace.json @@ -0,0 +1,12 @@ +[ + "======== Resolving type reference directive '@beep/boop', containing file '/a.ts', root directory 'types'. ========", + "Resolving with primary search path 'types'.", + "Directory 'types/@beep' does not exist, skipping all lookups in it.", + "Looking up in 'node_modules' folder, initial location '/'.", + "Scoped package detected, looking in 'beep__boop'", + "File '/node_modules/@types/beep__boop.d.ts' does not exist.", + "File '/node_modules/@types/beep__boop/package.json' does not exist.", + "File '/node_modules/@types/beep__boop/index.d.ts' exist - use it as a name resolution result.", + "Resolving real path for '/node_modules/@types/beep__boop/index.d.ts', result '/node_modules/@types/beep__boop/index.d.ts'.", + "======== Type reference directive '@beep/boop' was successfully resolved to '/node_modules/@types/beep__boop/index.d.ts', primary: false. ========" +] \ No newline at end of file diff --git a/tests/baselines/reference/library-reference-scoped-packages.types b/tests/baselines/reference/library-reference-scoped-packages.types new file mode 100644 index 00000000000..bbb3062d98b --- /dev/null +++ b/tests/baselines/reference/library-reference-scoped-packages.types @@ -0,0 +1,8 @@ +=== /a.ts === +/// +No type information for this code. +No type information for this code.=== /node_modules/@types/beep__boop/index.d.ts === +export const y = 0; +>y : 0 +>0 : 0 + diff --git a/tests/baselines/reference/scopedPackages.js b/tests/baselines/reference/scopedPackages.js new file mode 100644 index 00000000000..75606d2e1b9 --- /dev/null +++ b/tests/baselines/reference/scopedPackages.js @@ -0,0 +1,20 @@ +//// [tests/cases/conformance/moduleResolution/scopedPackages.ts] //// + +//// [index.d.ts] +export const x: number; + +//// [index.d.ts] +export const y: number; + +//// [z.d.ts] +export const z: number; + +//// [a.ts] +import { x } from "@cow/boy"; +import { y } from "@be/bop"; +import { z } from "@be/bop/e/z"; + + +//// [a.js] +"use strict"; +exports.__esModule = true; diff --git a/tests/baselines/reference/scopedPackages.symbols b/tests/baselines/reference/scopedPackages.symbols new file mode 100644 index 00000000000..e992c4d9375 --- /dev/null +++ b/tests/baselines/reference/scopedPackages.symbols @@ -0,0 +1,22 @@ +=== /a.ts === +import { x } from "@cow/boy"; +>x : Symbol(x, Decl(a.ts, 0, 8)) + +import { y } from "@be/bop"; +>y : Symbol(y, Decl(a.ts, 1, 8)) + +import { z } from "@be/bop/e/z"; +>z : Symbol(z, Decl(a.ts, 2, 8)) + +=== /node_modules/@cow/boy/index.d.ts === +export const x: number; +>x : Symbol(x, Decl(index.d.ts, 0, 12)) + +=== /node_modules/@types/be__bop/index.d.ts === +export const y: number; +>y : Symbol(y, Decl(index.d.ts, 0, 12)) + +=== /node_modules/@types/be__bop/e/z.d.ts === +export const z: number; +>z : Symbol(z, Decl(z.d.ts, 0, 12)) + diff --git a/tests/baselines/reference/scopedPackages.trace.json b/tests/baselines/reference/scopedPackages.trace.json new file mode 100644 index 00000000000..20df3bec172 --- /dev/null +++ b/tests/baselines/reference/scopedPackages.trace.json @@ -0,0 +1,30 @@ +[ + "======== Resolving module '@cow/boy' from '/a.ts'. ========", + "Module resolution kind is not specified, using 'NodeJs'.", + "Loading module '@cow/boy' from 'node_modules' folder, target file type 'TypeScript'.", + "File '/node_modules/@cow/boy.ts' does not exist.", + "File '/node_modules/@cow/boy.tsx' does not exist.", + "File '/node_modules/@cow/boy.d.ts' does not exist.", + "File '/node_modules/@cow/boy/package.json' does not exist.", + "File '/node_modules/@cow/boy/index.ts' does not exist.", + "File '/node_modules/@cow/boy/index.tsx' does not exist.", + "File '/node_modules/@cow/boy/index.d.ts' exist - use it as a name resolution result.", + "Resolving real path for '/node_modules/@cow/boy/index.d.ts', result '/node_modules/@cow/boy/index.d.ts'.", + "======== Module name '@cow/boy' was successfully resolved to '/node_modules/@cow/boy/index.d.ts'. ========", + "======== Resolving module '@be/bop' from '/a.ts'. ========", + "Module resolution kind is not specified, using 'NodeJs'.", + "Loading module '@be/bop' from 'node_modules' folder, target file type 'TypeScript'.", + "Scoped package detected, looking in 'be__bop'", + "File '/node_modules/@types/be__bop.d.ts' does not exist.", + "File '/node_modules/@types/be__bop/package.json' does not exist.", + "File '/node_modules/@types/be__bop/index.d.ts' exist - use it as a name resolution result.", + "Resolving real path for '/node_modules/@types/be__bop/index.d.ts', result '/node_modules/@types/be__bop/index.d.ts'.", + "======== Module name '@be/bop' was successfully resolved to '/node_modules/@types/be__bop/index.d.ts'. ========", + "======== Resolving module '@be/bop/e/z' from '/a.ts'. ========", + "Module resolution kind is not specified, using 'NodeJs'.", + "Loading module '@be/bop/e/z' from 'node_modules' folder, target file type 'TypeScript'.", + "Scoped package detected, looking in 'be__bop/e/z'", + "File '/node_modules/@types/be__bop/e/z.d.ts' exist - use it as a name resolution result.", + "Resolving real path for '/node_modules/@types/be__bop/e/z.d.ts', result '/node_modules/@types/be__bop/e/z.d.ts'.", + "======== Module name '@be/bop/e/z' was successfully resolved to '/node_modules/@types/be__bop/e/z.d.ts'. ========" +] \ No newline at end of file diff --git a/tests/baselines/reference/scopedPackages.types b/tests/baselines/reference/scopedPackages.types new file mode 100644 index 00000000000..c876590f54a --- /dev/null +++ b/tests/baselines/reference/scopedPackages.types @@ -0,0 +1,22 @@ +=== /a.ts === +import { x } from "@cow/boy"; +>x : number + +import { y } from "@be/bop"; +>y : number + +import { z } from "@be/bop/e/z"; +>z : number + +=== /node_modules/@cow/boy/index.d.ts === +export const x: number; +>x : number + +=== /node_modules/@types/be__bop/index.d.ts === +export const y: number; +>y : number + +=== /node_modules/@types/be__bop/e/z.d.ts === +export const z: number; +>z : number + diff --git a/tests/baselines/reference/scopedPackagesClassic.js b/tests/baselines/reference/scopedPackagesClassic.js new file mode 100644 index 00000000000..f42acde2306 --- /dev/null +++ b/tests/baselines/reference/scopedPackagesClassic.js @@ -0,0 +1,12 @@ +//// [tests/cases/conformance/moduleResolution/scopedPackagesClassic.ts] //// + +//// [index.d.ts] +export const x = 0; + +//// [a.ts] +import { x } from "@see/saw"; + + +//// [a.js] +"use strict"; +exports.__esModule = true; diff --git a/tests/baselines/reference/scopedPackagesClassic.symbols b/tests/baselines/reference/scopedPackagesClassic.symbols new file mode 100644 index 00000000000..e0d84f44c91 --- /dev/null +++ b/tests/baselines/reference/scopedPackagesClassic.symbols @@ -0,0 +1,8 @@ +=== /a.ts === +import { x } from "@see/saw"; +>x : Symbol(x, Decl(a.ts, 0, 8)) + +=== /node_modules/@types/see__saw/index.d.ts === +export const x = 0; +>x : Symbol(x, Decl(index.d.ts, 0, 12)) + diff --git a/tests/baselines/reference/scopedPackagesClassic.trace.json b/tests/baselines/reference/scopedPackagesClassic.trace.json new file mode 100644 index 00000000000..c58c7d2ed10 --- /dev/null +++ b/tests/baselines/reference/scopedPackagesClassic.trace.json @@ -0,0 +1,9 @@ +[ + "======== Resolving module '@see/saw' from '/a.ts'. ========", + "Explicitly specified module resolution kind: 'Classic'.", + "Scoped package detected, looking in 'see__saw'", + "File '/node_modules/@types/see__saw.d.ts' does not exist.", + "File '/node_modules/@types/see__saw/package.json' does not exist.", + "File '/node_modules/@types/see__saw/index.d.ts' exist - use it as a name resolution result.", + "======== Module name '@see/saw' was successfully resolved to '/node_modules/@types/see__saw/index.d.ts'. ========" +] \ No newline at end of file diff --git a/tests/baselines/reference/scopedPackagesClassic.types b/tests/baselines/reference/scopedPackagesClassic.types new file mode 100644 index 00000000000..65047f2436d --- /dev/null +++ b/tests/baselines/reference/scopedPackagesClassic.types @@ -0,0 +1,9 @@ +=== /a.ts === +import { x } from "@see/saw"; +>x : 0 + +=== /node_modules/@types/see__saw/index.d.ts === +export const x = 0; +>x : 0 +>0 : 0 + diff --git a/tests/cases/conformance/moduleResolution/scopedPackages.ts b/tests/cases/conformance/moduleResolution/scopedPackages.ts new file mode 100644 index 00000000000..b604f10d7ec --- /dev/null +++ b/tests/cases/conformance/moduleResolution/scopedPackages.ts @@ -0,0 +1,17 @@ +// @noImplicitReferences: true +// @traceResolution: true +// @typeRoots: types + +// @filename: /node_modules/@cow/boy/index.d.ts +export const x: number; + +// @filename: /node_modules/@types/be__bop/index.d.ts +export const y: number; + +// @filename: /node_modules/@types/be__bop/e/z.d.ts +export const z: number; + +// @filename: /a.ts +import { x } from "@cow/boy"; +import { y } from "@be/bop"; +import { z } from "@be/bop/e/z"; diff --git a/tests/cases/conformance/moduleResolution/scopedPackagesClassic.ts b/tests/cases/conformance/moduleResolution/scopedPackagesClassic.ts new file mode 100644 index 00000000000..41b6fc09e19 --- /dev/null +++ b/tests/cases/conformance/moduleResolution/scopedPackagesClassic.ts @@ -0,0 +1,10 @@ +// @noImplicitReferences: true +// @traceResolution: true +// @typeRoots: types +// @moduleResolution: classic + +// @filename: /node_modules/@types/see__saw/index.d.ts +export const x = 0; + +// @filename: /a.ts +import { x } from "@see/saw"; diff --git a/tests/cases/conformance/references/library-reference-scoped-packages.ts b/tests/cases/conformance/references/library-reference-scoped-packages.ts new file mode 100644 index 00000000000..86a1ad3e93d --- /dev/null +++ b/tests/cases/conformance/references/library-reference-scoped-packages.ts @@ -0,0 +1,9 @@ +// @noImplicitReferences: true +// @traceResolution: true +// @typeRoots: types + +// @filename: /node_modules/@types/beep__boop/index.d.ts +export const y = 0; + +// @filename: /a.ts +/// From e9f95e2296e66b6fd2afc51663309426bbf3e0ae Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Mon, 10 Apr 2017 09:06:26 -0700 Subject: [PATCH 2/2] Only trace if traceEnabled --- src/compiler/moduleNameResolver.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/compiler/moduleNameResolver.ts b/src/compiler/moduleNameResolver.ts index 44779c3b482..e42088c3a2b 100644 --- a/src/compiler/moduleNameResolver.ts +++ b/src/compiler/moduleNameResolver.ts @@ -954,17 +954,19 @@ namespace ts { } nodeModulesAtTypesExists = false; } - return loadModuleFromNodeModulesFolder(Extensions.DtsOnly, mangleScopedPackage(moduleName, state.host), nodeModulesAtTypes, nodeModulesAtTypesExists, failedLookupLocations, state); + return loadModuleFromNodeModulesFolder(Extensions.DtsOnly, mangleScopedPackage(moduleName, state), nodeModulesAtTypes, nodeModulesAtTypesExists, failedLookupLocations, state); } } /** For a scoped package, we must look in `@types/foo__bar` instead of `@types/@foo/bar`. */ - function mangleScopedPackage(moduleName: string, host: ModuleResolutionHost): string { + function mangleScopedPackage(moduleName: string, state: ModuleResolutionState): string { if (startsWith(moduleName, "@")) { const replaceSlash = moduleName.replace(ts.directorySeparator, "__"); if (replaceSlash !== moduleName) { const mangled = replaceSlash.slice(1); // Take off the "@" - trace(host, Diagnostics.Scoped_package_detected_looking_in_0, mangled); + if (state.traceEnabled) { + trace(state.host, Diagnostics.Scoped_package_detected_looking_in_0, mangled); + } return mangled; } }