Add a new compiler option moduleSuffixes to expand the node module resolver's search algorithm (#48189)

* Add moduleSuffixes compiler option and related tests. Update baselines for compiler options tests.

* Add a flag to the command-line parser which allows "list" params to preserve "falsy" values such as empty strings. Falsy values are normally stripped out.

* Add tests. Rework resolver logic to only run module-suffix code when needed.

* PR feedback

* Add test

* Remove unnecessary conditional.
This commit is contained in:
Adam Foxman
2022-03-30 15:23:26 -07:00
committed by GitHub
parent c639d3afb9
commit 41aca7c337
105 changed files with 1728 additions and 2 deletions

View File

@@ -0,0 +1,14 @@
// moduleSuffixes is empty. Normal module resolution should occur.
// @filename: /tsconfig.json
{
"compilerOptions": {
"moduleResolution": "node",
"traceResolution": true,
"moduleSuffixes": []
}
}
// @filename: /index.ts
import { base } from "./foo";
// @filename: /foo.ts
export function base() {}

View File

@@ -0,0 +1,12 @@
// moduleSuffixes is not specified. Normal module resolution should occur.
// @filename: /tsconfig.json
{
"compilerOptions": {
"moduleResolution": "node",
"traceResolution": true,
}
}
// @filename: /index.ts
import { base } from "./foo";
// @filename: /foo.ts
export function base() {}

View File

@@ -0,0 +1,17 @@
// moduleSuffixes has one entry and there's a matching file.
// @filename: /tsconfig.json
{
"compilerOptions": {
"moduleResolution": "node",
"traceResolution": true,
"moduleSuffixes": [".ios"]
}
}
// @filename: /index.ts
import { ios } from "./foo";
// @filename: /foo.ios.ts
export function ios() {}
// @filename: /foo.ts
export function base() {}

View File

@@ -0,0 +1,15 @@
// moduleSuffixes has one blank entry. Normal module resolution should occur.
// @filename: /tsconfig.json
{
"compilerOptions": {
"moduleResolution": "node",
"traceResolution": true,
"moduleSuffixes": [""]
}
}
// @filename: /index.ts
import { base } from "./foo";
// @filename: /foo.ts
export function base() {}

View File

@@ -0,0 +1,15 @@
// moduleSuffixes has one entry but there isn't a matching file. Module resolution should fail.
// @filename: /tsconfig.json
{
"compilerOptions": {
"moduleResolution": "node",
"traceResolution": true,
"moduleSuffixes": [".ios"]
}
}
// @filename: /index.ts
import { ios } from "./foo";
// @filename: /foo.ts
export function base() {}

View File

@@ -0,0 +1,17 @@
// moduleSuffixes has one entry and there's a matching dir with an index file.
// @filename: /tsconfig.json
{
"compilerOptions": {
"moduleResolution": "node",
"traceResolution": true,
"moduleSuffixes": [".ios"]
}
}
// @filename: /index.ts
import { ios } from "./foo";
// @filename: /foo/index.ios.ts
export function ios() {}
// @filename: /foo/index.ts
export function base() {}

View File

@@ -0,0 +1,31 @@
// moduleSuffixes has one entry and there's a matching package.
// @fullEmitPaths: true
// @filename: /tsconfig.json
{
"compilerOptions": {
"allowJs": true,
"checkJs": false,
"outDir": "bin",
"moduleResolution": "node",
"traceResolution": true,
"moduleSuffixes": [".ios"]
}
}
// @filename: /node_modules/some-library/index.ios.js
"use strict";
exports.__esModule = true;
function ios() {}
exports.ios = ios;
// @filename: /node_modules/some-library/index.ios.d.ts
export declare function ios(): void;
// @filename: /node_modules/some-library/index.js
"use strict";
exports.__esModule = true;
function base() {}
exports.base = base;
// @filename: /node_modules/some-library/index.d.ts
export declare function base(): void;
// @filename: /index.ts
import { ios } from "some-library";

View File

@@ -0,0 +1,31 @@
// moduleSuffixes has one entry and there's a matching package with a specific path.
// @fullEmitPaths: true
// @filename: /tsconfig.json
{
"compilerOptions": {
"allowJs": true,
"checkJs": false,
"outDir": "bin",
"moduleResolution": "node",
"traceResolution": true,
"moduleSuffixes": [".ios"]
}
}
// @filename: /node_modules/some-library/foo.ios.js
"use strict";
exports.__esModule = true;
function iosfoo() {}
exports.iosfoo = iosfoo;
// @filename: /node_modules/some-library/foo.ios.d.ts
export declare function iosfoo(): void;
// @filename: /node_modules/some-library/foo.js
"use strict";
exports.__esModule = true;
function basefoo() {}
exports.basefoo = basefoo;
// @filename: /node_modules/some-library/foo.d.ts
export declare function basefoo(): void;
// @filename: /index.ts
import { iosfoo } from "some-library/foo";

View File

@@ -0,0 +1,38 @@
// moduleSuffixes has one entry and there's a matching package. use the 'paths' option to map the package.
// @fullEmitPaths: true
// @filename: /tsconfig.json
{
"compilerOptions": {
"allowJs": true,
"checkJs": false,
"outDir": "bin",
"moduleResolution": "node",
"traceResolution": true,
"moduleSuffixes": [".ios"],
"baseUrl": "/",
"paths": {
"some-library": ["node_modules/some-library/lib"],
"some-library/*": ["node_modules/some-library/lib/*"]
}
}
}
// @filename: /node_modules/some-library/lib/index.ios.js
"use strict";
exports.__esModule = true;
function ios() {}
exports.ios = ios;
// @filename: /node_modules/some-library/lib/index.ios.d.ts
export declare function ios(): void;
// @filename: /node_modules/some-library/lib/index.js
"use strict";
exports.__esModule = true;
function base() {}
exports.base = base;
// @filename: /node_modules/some-library/lib/index.d.ts
export declare function base(): void;
// @filename: /test.ts
import { ios } from "some-library";
import { ios as ios2 } from "some-library/index";
import { ios as ios3 } from "some-library/index.js";

View File

@@ -0,0 +1,18 @@
// moduleSuffixes has one entry and there's a matching package with TS files.
// @fullEmitPaths: true
// @filename: /tsconfig.json
{
"compilerOptions": {
"outDir": "bin",
"moduleResolution": "node",
"traceResolution": true,
"moduleSuffixes": [".ios"]
}
}
// @filename: /node_modules/some-library/index.ios.ts
export function ios() {}
// @filename: /node_modules/some-library/index.ts
export function base() {}
// @filename: /test.ts
import { ios } from "some-library";

View File

@@ -0,0 +1,26 @@
// moduleSuffixes has one entry and there's a matching file. module name explicitly includes JS file extension.
// @fullEmitPaths: true
// @filename: /tsconfig.json
{
"compilerOptions": {
"allowJs": true,
"checkJs": false,
"outDir": "bin",
"moduleResolution": "node",
"traceResolution": true,
"moduleSuffixes": [".ios"]
}
}
// @filename: /index.ts
import { ios } from "./foo.js";
// @filename: /foo.ios.js
"use strict";
exports.__esModule = true;
function ios() {}
exports.ios = ios;
// @filename: /foo.js
"use strict";
exports.__esModule = true;
function base() {}
exports.base = base;

View File

@@ -0,0 +1,25 @@
// moduleSuffixes has one entry and there's a matching file. module name explicitly includes JSON file extension.
// @fullEmitPaths: true
// @filename: /tsconfig.json
{
"compilerOptions": {
"esModuleInterop": true,
"resolveJsonModule": true,
"outDir": "bin",
"moduleResolution": "node",
"traceResolution": true,
"moduleSuffixes": [".ios"]
}
}
// @filename: /index.ts
import foo from "./foo.json";
console.log(foo.ios);
// @filename: /foo.ios.json
{
"ios": "platform ios"
}
// @filename: /foo.json
{
"base": "platform base"
}

View File

@@ -0,0 +1,19 @@
// moduleSuffixes has three entries, and the last one is blank. Module resolution should match on the first suffix.
// @filename: /tsconfig.json
{
"compilerOptions": {
"moduleResolution": "node",
"traceResolution": true,
"moduleSuffixes": ["-ios", "__native", ""]
}
}
// @filename: /index.ts
import { ios } from "./foo";
// @filename: /foo-ios.ts
export function ios() {}
// @filename: /foo__native.ts
export function native() {}
// @filename: /foo.ts
export function base() {}

View File

@@ -0,0 +1,17 @@
// moduleSuffixes has three entries, and the last one is blank. Module resolution should match on the second suffix.
// @filename: /tsconfig.json
{
"compilerOptions": {
"moduleResolution": "node",
"traceResolution": true,
"moduleSuffixes": ["-ios", "__native", ""]
}
}
// @filename: /index.ts
import { native } from "./foo";
// @filename: /foo__native.ts
export function native() {}
// @filename: /foo.ts
export function base() {}

View File

@@ -0,0 +1,15 @@
// moduleSuffixes has three entries, and the last one is blank. Module resolution should match on the blank suffix.
// @filename: /tsconfig.json
{
"compilerOptions": {
"moduleResolution": "node",
"traceResolution": true,
"moduleSuffixes": ["-ios", "__native", ""]
}
}
// @filename: /index.ts
import { base } from "./foo";
// @filename: /foo.ts
export function base() {}

View File

@@ -0,0 +1,13 @@
// moduleSuffixes has three entries, and the last one is blank. Module resolution should fail.
// @filename: /tsconfig.json
{
"compilerOptions": {
"moduleResolution": "node",
"traceResolution": true,
"moduleSuffixes": ["-ios", "__native", ""]
}
}
// @filename: /index.ts
import { base } from "./foo";