Skip to content

Commit 328a5ef

Browse files
authored
Merge pull request #13024 from Microsoft/package_json_unsupported_extension
Skip files from package.json "main" or "types" if they have an unsupported extension.
2 parents f3ad09a + bf94a4a commit 328a5ef

12 files changed

+169
-16
lines changed

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2685,6 +2685,10 @@
26852685
"category": "Message",
26862686
"code": 6080
26872687
},
2688+
"File '{0}' has an unsupported extension, so skipping it.": {
2689+
"category": "Message",
2690+
"code": 6081
2691+
},
26882692
"Only 'amd' and 'system' modules are supported alongside --{0}.": {
26892693
"category": "Error",
26902694
"code": 6082

src/compiler/moduleNameResolver.ts

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,6 @@ namespace ts {
4747
return resolved.path;
4848
}
4949

50-
/** Create Resolved from a file with unknown extension. */
51-
function resolvedFromAnyFile(path: string): Resolved | undefined {
52-
return { path, extension: extensionFromPath(path) };
53-
}
54-
5550
/** Adds `isExernalLibraryImport` to a Resolved to get a ResolvedModule. */
5651
function resolvedModuleFromResolved({ path, extension }: Resolved, isExternalLibraryImport: boolean): ResolvedModuleFull {
5752
return { resolvedFileName: path, extension, isExternalLibraryImport };
@@ -71,7 +66,8 @@ namespace ts {
7166
traceEnabled: boolean;
7267
}
7368

74-
function tryReadTypesSection(extensions: Extensions, packageJsonPath: string, baseDirectory: string, state: ModuleResolutionState): string {
69+
/** Reads from "main" or "types"/"typings" depending on `extensions`. */
70+
function tryReadPackageJsonMainOrTypes(extensions: Extensions, packageJsonPath: string, baseDirectory: string, state: ModuleResolutionState): string {
7571
const jsonContent = readJson(packageJsonPath, state.host);
7672

7773
switch (extensions) {
@@ -714,18 +710,21 @@ namespace ts {
714710
if (state.traceEnabled) {
715711
trace(state.host, Diagnostics.Found_package_json_at_0, packageJsonPath);
716712
}
717-
const typesFile = tryReadTypesSection(extensions, packageJsonPath, candidate, state);
718-
if (typesFile) {
719-
const onlyRecordFailures = !directoryProbablyExists(getDirectoryPath(typesFile), state.host);
713+
const mainOrTypesFile = tryReadPackageJsonMainOrTypes(extensions, packageJsonPath, candidate, state);
714+
if (mainOrTypesFile) {
715+
const onlyRecordFailures = !directoryProbablyExists(getDirectoryPath(mainOrTypesFile), state.host);
720716
// A package.json "typings" may specify an exact filename, or may choose to omit an extension.
721-
const fromFile = tryFile(typesFile, failedLookupLocations, onlyRecordFailures, state);
722-
if (fromFile) {
723-
// Note: this would allow a package.json to specify a ".js" file as typings. Maybe that should be forbidden.
724-
return resolvedFromAnyFile(fromFile);
717+
const fromExactFile = tryFile(mainOrTypesFile, failedLookupLocations, onlyRecordFailures, state);
718+
if (fromExactFile) {
719+
const resolved = fromExactFile && resolvedIfExtensionMatches(extensions, fromExactFile);
720+
if (resolved) {
721+
return resolved;
722+
}
723+
trace(state.host, Diagnostics.File_0_has_an_unsupported_extension_so_skipping_it, fromExactFile);
725724
}
726-
const x = tryAddingExtensions(typesFile, Extensions.TypeScript, failedLookupLocations, onlyRecordFailures, state);
727-
if (x) {
728-
return x;
725+
const resolved = tryAddingExtensions(mainOrTypesFile, Extensions.TypeScript, failedLookupLocations, onlyRecordFailures, state);
726+
if (resolved) {
727+
return resolved;
729728
}
730729
}
731730
else {
@@ -745,6 +744,24 @@ namespace ts {
745744
return loadModuleFromFile(extensions, combinePaths(candidate, "index"), failedLookupLocations, !directoryExists, state);
746745
}
747746

747+
/** Resolve from an arbitrarily specified file. Return `undefined` if it has an unsupported extension. */
748+
function resolvedIfExtensionMatches(extensions: Extensions, path: string): Resolved | undefined {
749+
const extension = tryGetExtensionFromPath(path);
750+
return extension !== undefined && extensionIsOk(extensions, extension) ? { path, extension } : undefined;
751+
}
752+
753+
/** True if `extension` is one of the supported `extensions`. */
754+
function extensionIsOk(extensions: Extensions, extension: Extension): boolean {
755+
switch (extensions) {
756+
case Extensions.JavaScript:
757+
return extension === Extension.Js || extension === Extension.Jsx;
758+
case Extensions.TypeScript:
759+
return extension === Extension.Ts || extension === Extension.Tsx || extension === Extension.Dts;
760+
case Extensions.DtsOnly:
761+
return extension === Extension.Dts;
762+
}
763+
}
764+
748765
function pathToPackageJson(directory: string): string {
749766
return combinePaths(directory, "package.json");
750767
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//// [tests/cases/compiler/moduleResolutionWithExtensions_unexpected.ts] ////
2+
3+
//// [normalize.css]
4+
// This tests that a package.json "main" with an unexpected extension is ignored.
5+
6+
This file is not read.
7+
8+
//// [package.json]
9+
{ "main": "normalize.css" }
10+
11+
//// [a.ts]
12+
import "normalize.css";
13+
14+
15+
//// [a.js]
16+
"use strict";
17+
require("normalize.css");
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
=== /a.ts ===
2+
import "normalize.css";
3+
No type information for this code.
4+
No type information for this code.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
[
2+
"======== Resolving module 'normalize.css' from '/a.ts'. ========",
3+
"Module resolution kind is not specified, using 'NodeJs'.",
4+
"Loading module 'normalize.css' from 'node_modules' folder.",
5+
"File '/node_modules/normalize.css.ts' does not exist.",
6+
"File '/node_modules/normalize.css.tsx' does not exist.",
7+
"File '/node_modules/normalize.css.d.ts' does not exist.",
8+
"Found 'package.json' at '/node_modules/normalize.css/package.json'.",
9+
"'package.json' does not have a 'types' or 'main' field.",
10+
"File '/node_modules/normalize.css/index.ts' does not exist.",
11+
"File '/node_modules/normalize.css/index.tsx' does not exist.",
12+
"File '/node_modules/normalize.css/index.d.ts' does not exist.",
13+
"File '/node_modules/@types/normalize.css.d.ts' does not exist.",
14+
"File '/node_modules/@types/normalize.css/package.json' does not exist.",
15+
"File '/node_modules/@types/normalize.css/index.d.ts' does not exist.",
16+
"Loading module 'normalize.css' from 'node_modules' folder.",
17+
"File '/node_modules/normalize.css.js' does not exist.",
18+
"File '/node_modules/normalize.css.jsx' does not exist.",
19+
"Found 'package.json' at '/node_modules/normalize.css/package.json'.",
20+
"No types specified in 'package.json', so returning 'main' value of 'normalize.css'",
21+
"File '/node_modules/normalize.css/normalize.css' exist - use it as a name resolution result.",
22+
"File '/node_modules/normalize.css/normalize.css' has an unsupported extension, so skipping it.",
23+
"File '/node_modules/normalize.css/normalize.css.ts' does not exist.",
24+
"File '/node_modules/normalize.css/normalize.css.tsx' does not exist.",
25+
"File '/node_modules/normalize.css/normalize.css.d.ts' does not exist.",
26+
"File '/node_modules/normalize.css/index.js' does not exist.",
27+
"File '/node_modules/normalize.css/index.jsx' does not exist.",
28+
"======== Module name 'normalize.css' was not resolved. ========"
29+
]
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
=== /a.ts ===
2+
import "normalize.css";
3+
No type information for this code.
4+
No type information for this code.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//// [tests/cases/compiler/moduleResolutionWithExtensions_unexpected2.ts] ////
2+
3+
//// [foo.js]
4+
// This tests that a package.json "types" with an unexpected extension is ignored.
5+
6+
This file is not read.
7+
8+
//// [package.json]
9+
{ "types": "foo.js" }
10+
11+
//// [a.ts]
12+
import "foo";
13+
14+
15+
//// [a.js]
16+
"use strict";
17+
require("foo");
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
=== /a.ts ===
2+
import "foo";
3+
No type information for this code.
4+
No type information for this code.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
[
2+
"======== Resolving module 'foo' from '/a.ts'. ========",
3+
"Module resolution kind is not specified, using 'NodeJs'.",
4+
"Loading module 'foo' from 'node_modules' folder.",
5+
"File '/node_modules/foo.ts' does not exist.",
6+
"File '/node_modules/foo.tsx' does not exist.",
7+
"File '/node_modules/foo.d.ts' does not exist.",
8+
"Found 'package.json' at '/node_modules/foo/package.json'.",
9+
"'package.json' has 'types' field 'foo.js' that references '/node_modules/foo/foo.js'.",
10+
"File '/node_modules/foo/foo.js' exist - use it as a name resolution result.",
11+
"File '/node_modules/foo/foo.js' has an unsupported extension, so skipping it.",
12+
"File '/node_modules/foo/foo.js.ts' does not exist.",
13+
"File '/node_modules/foo/foo.js.tsx' does not exist.",
14+
"File '/node_modules/foo/foo.js.d.ts' does not exist.",
15+
"File '/node_modules/foo/index.ts' does not exist.",
16+
"File '/node_modules/foo/index.tsx' does not exist.",
17+
"File '/node_modules/foo/index.d.ts' does not exist.",
18+
"File '/node_modules/@types/foo.d.ts' does not exist.",
19+
"File '/node_modules/@types/foo/package.json' does not exist.",
20+
"File '/node_modules/@types/foo/index.d.ts' does not exist.",
21+
"Loading module 'foo' from 'node_modules' folder.",
22+
"File '/node_modules/foo.js' does not exist.",
23+
"File '/node_modules/foo.jsx' does not exist.",
24+
"Found 'package.json' at '/node_modules/foo/package.json'.",
25+
"'package.json' does not have a 'types' or 'main' field.",
26+
"File '/node_modules/foo/index.js' does not exist.",
27+
"File '/node_modules/foo/index.jsx' does not exist.",
28+
"======== Module name 'foo' was not resolved. ========"
29+
]
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
=== /a.ts ===
2+
import "foo";
3+
No type information for this code.
4+
No type information for this code.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// @noImplicitReferences: true
2+
// @traceResolution: true
3+
// This tests that a package.json "main" with an unexpected extension is ignored.
4+
5+
// @Filename: /node_modules/normalize.css/normalize.css
6+
This file is not read.
7+
8+
// @Filename: /node_modules/normalize.css/package.json
9+
{ "main": "normalize.css" }
10+
11+
// @Filename: /a.ts
12+
import "normalize.css";
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// @noImplicitReferences: true
2+
// @traceResolution: true
3+
// This tests that a package.json "types" with an unexpected extension is ignored.
4+
5+
// @Filename: /node_modules/foo/foo.js
6+
This file is not read.
7+
8+
// @Filename: /node_modules/foo/package.json
9+
{ "types": "foo.js" }
10+
11+
// @Filename: /a.ts
12+
import "foo";

0 commit comments

Comments
 (0)