Skip to content

Skip files from package.json "main" or "types" if they have an unsupported extension. #13024

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Dec 20, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -2685,6 +2685,10 @@
"category": "Message",
"code": 6080
},
"File '{0}' has an unsupported extension, so skipping it.": {
"category": "Message",
"code": 6081
},
"Only 'amd' and 'system' modules are supported alongside --{0}.": {
"category": "Error",
"code": 6082
Expand Down
49 changes: 33 additions & 16 deletions src/compiler/moduleNameResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,6 @@ namespace ts {
return resolved.path;
}

/** Create Resolved from a file with unknown extension. */
function resolvedFromAnyFile(path: string): Resolved | undefined {
return { path, extension: extensionFromPath(path) };
}

/** Adds `isExernalLibraryImport` to a Resolved to get a ResolvedModule. */
function resolvedModuleFromResolved({ path, extension }: Resolved, isExternalLibraryImport: boolean): ResolvedModuleFull {
return { resolvedFileName: path, extension, isExternalLibraryImport };
Expand All @@ -71,7 +66,8 @@ namespace ts {
traceEnabled: boolean;
}

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

switch (extensions) {
Expand Down Expand Up @@ -678,18 +674,21 @@ namespace ts {
if (state.traceEnabled) {
trace(state.host, Diagnostics.Found_package_json_at_0, packageJsonPath);
}
const typesFile = tryReadTypesSection(extensions, packageJsonPath, candidate, state);
if (typesFile) {
const onlyRecordFailures = !directoryProbablyExists(getDirectoryPath(typesFile), state.host);
const mainOrTypesFile = tryReadPackageJsonMainOrTypes(extensions, packageJsonPath, candidate, state);
if (mainOrTypesFile) {
const onlyRecordFailures = !directoryProbablyExists(getDirectoryPath(mainOrTypesFile), state.host);
// A package.json "typings" may specify an exact filename, or may choose to omit an extension.
const fromFile = tryFile(typesFile, failedLookupLocations, onlyRecordFailures, state);
if (fromFile) {
// Note: this would allow a package.json to specify a ".js" file as typings. Maybe that should be forbidden.
return resolvedFromAnyFile(fromFile);
const fromExactFile = tryFile(mainOrTypesFile, failedLookupLocations, onlyRecordFailures, state);
if (fromExactFile) {
const resolved = fromExactFile && resolvedIfExtensionMatches(extensions, fromExactFile);
if (resolved) {
return resolved;
}
trace(state.host, Diagnostics.File_0_has_an_unsupported_extension_so_skipping_it, fromExactFile);
}
const x = tryAddingExtensions(typesFile, Extensions.TypeScript, failedLookupLocations, onlyRecordFailures, state);
if (x) {
return x;
const resolved = tryAddingExtensions(mainOrTypesFile, Extensions.TypeScript, failedLookupLocations, onlyRecordFailures, state);
if (resolved) {
return resolved;
}
}
else {
Expand All @@ -709,6 +708,24 @@ namespace ts {
return loadModuleFromFile(extensions, combinePaths(candidate, "index"), failedLookupLocations, !directoryExists, state);
}

/** Resolve from an arbitrarily specified file. Return `undefined` if it has an unsupported extension. */
function resolvedIfExtensionMatches(extensions: Extensions, path: string): Resolved | undefined {
const extension = tryGetExtensionFromPath(path);
return extension !== undefined && extensionIsOk(extensions, extension) ? { path, extension } : undefined;
}

/** True if `extension` is one of the supported `extensions`. */
function extensionIsOk(extensions: Extensions, extension: Extension): boolean {
switch (extensions) {
case Extensions.JavaScript:
return extension === Extension.Js || extension === Extension.Jsx;
case Extensions.TypeScript:
return extension === Extension.Ts || extension === Extension.Tsx || extension === Extension.Dts;
case Extensions.DtsOnly:
return extension === Extension.Dts;
}
}

function pathToPackageJson(directory: string): string {
return combinePaths(directory, "package.json");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//// [tests/cases/compiler/moduleResolutionWithExtensions_unexpected.ts] ////

//// [normalize.css]
// This tests that a package.json "main" with an unexpected extension is ignored.

This file is not read.

//// [package.json]
{ "main": "normalize.css" }

//// [a.ts]
import "normalize.css";


//// [a.js]
"use strict";
require("normalize.css");
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
=== /a.ts ===
import "normalize.css";
No type information for this code.
No type information for this code.
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
[
"======== Resolving module 'normalize.css' from '/a.ts'. ========",
"Module resolution kind is not specified, using 'NodeJs'.",
"Loading module 'normalize.css' from 'node_modules' folder.",
"File '/node_modules/normalize.css.ts' does not exist.",
"File '/node_modules/normalize.css.tsx' does not exist.",
"File '/node_modules/normalize.css.d.ts' does not exist.",
"Found 'package.json' at '/node_modules/normalize.css/package.json'.",
"'package.json' does not have a 'types' or 'main' field.",
"File '/node_modules/normalize.css/index.ts' does not exist.",
"File '/node_modules/normalize.css/index.tsx' does not exist.",
"File '/node_modules/normalize.css/index.d.ts' does not exist.",
"File '/node_modules/@types/normalize.css.d.ts' does not exist.",
"File '/node_modules/@types/normalize.css/package.json' does not exist.",
"File '/node_modules/@types/normalize.css/index.d.ts' does not exist.",
"Loading module 'normalize.css' from 'node_modules' folder.",
"File '/node_modules/normalize.css.js' does not exist.",
"File '/node_modules/normalize.css.jsx' does not exist.",
"Found 'package.json' at '/node_modules/normalize.css/package.json'.",
"No types specified in 'package.json', so returning 'main' value of 'normalize.css'",
"File '/node_modules/normalize.css/normalize.css' exist - use it as a name resolution result.",
"File '/node_modules/normalize.css/normalize.css' has an unsupported extension, so skipping it.",
"File '/node_modules/normalize.css/normalize.css.ts' does not exist.",
"File '/node_modules/normalize.css/normalize.css.tsx' does not exist.",
"File '/node_modules/normalize.css/normalize.css.d.ts' does not exist.",
"File '/node_modules/normalize.css/index.js' does not exist.",
"File '/node_modules/normalize.css/index.jsx' does not exist.",
"======== Module name 'normalize.css' was not resolved. ========"
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
=== /a.ts ===
import "normalize.css";
No type information for this code.
No type information for this code.
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//// [tests/cases/compiler/moduleResolutionWithExtensions_unexpected2.ts] ////

//// [foo.js]
// This tests that a package.json "types" with an unexpected extension is ignored.

This file is not read.

//// [package.json]
{ "types": "foo.js" }

//// [a.ts]
import "foo";


//// [a.js]
"use strict";
require("foo");
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
=== /a.ts ===
import "foo";
No type information for this code.
No type information for this code.
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
[
"======== Resolving module 'foo' from '/a.ts'. ========",
"Module resolution kind is not specified, using 'NodeJs'.",
"Loading module 'foo' from 'node_modules' folder.",
"File '/node_modules/foo.ts' does not exist.",
"File '/node_modules/foo.tsx' does not exist.",
"File '/node_modules/foo.d.ts' does not exist.",
"Found 'package.json' at '/node_modules/foo/package.json'.",
"'package.json' has 'types' field 'foo.js' that references '/node_modules/foo/foo.js'.",
"File '/node_modules/foo/foo.js' exist - use it as a name resolution result.",
"File '/node_modules/foo/foo.js' has an unsupported extension, so skipping it.",
"File '/node_modules/foo/foo.js.ts' does not exist.",
"File '/node_modules/foo/foo.js.tsx' does not exist.",
"File '/node_modules/foo/foo.js.d.ts' does not exist.",
"File '/node_modules/foo/index.ts' does not exist.",
"File '/node_modules/foo/index.tsx' does not exist.",
"File '/node_modules/foo/index.d.ts' does not exist.",
"File '/node_modules/@types/foo.d.ts' does not exist.",
"File '/node_modules/@types/foo/package.json' does not exist.",
"File '/node_modules/@types/foo/index.d.ts' does not exist.",
"Loading module 'foo' from 'node_modules' folder.",
"File '/node_modules/foo.js' does not exist.",
"File '/node_modules/foo.jsx' does not exist.",
"Found 'package.json' at '/node_modules/foo/package.json'.",
"'package.json' does not have a 'types' or 'main' field.",
"File '/node_modules/foo/index.js' does not exist.",
"File '/node_modules/foo/index.jsx' does not exist.",
"======== Module name 'foo' was not resolved. ========"
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
=== /a.ts ===
import "foo";
No type information for this code.
No type information for this code.
12 changes: 12 additions & 0 deletions tests/cases/compiler/moduleResolutionWithExtensions_unexpected.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// @noImplicitReferences: true
// @traceResolution: true
// This tests that a package.json "main" with an unexpected extension is ignored.

// @Filename: /node_modules/normalize.css/normalize.css
This file is not read.

// @Filename: /node_modules/normalize.css/package.json
{ "main": "normalize.css" }

// @Filename: /a.ts
import "normalize.css";
12 changes: 12 additions & 0 deletions tests/cases/compiler/moduleResolutionWithExtensions_unexpected2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// @noImplicitReferences: true
// @traceResolution: true
// This tests that a package.json "types" with an unexpected extension is ignored.

// @Filename: /node_modules/foo/foo.js
This file is not read.

// @Filename: /node_modules/foo/package.json
{ "types": "foo.js" }

// @Filename: /a.ts
import "foo";