Skip to content

Commit 9dde56c

Browse files
authored
Add path completions for package.json exports with wildcards (#49644)
* Support path completions for exports wildcards * Break up results by directory * Share code between typesVersions and exports processing * Revert completion kind change * Add kinds to tests * Update existing test * Support nested conditions, improve recursive globbing
1 parent fefe220 commit 9dde56c

9 files changed

+493
-107
lines changed

src/compiler/moduleNameResolver.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2092,10 +2092,11 @@ namespace ts {
20922092
}
20932093

20942094
/**
2095+
* @internal
20952096
* From https://github.com/nodejs/node/blob/8f39f51cbbd3b2de14b9ee896e26421cc5b20121/lib/internal/modules/esm/resolve.js#L722 -
20962097
* "longest" has some nuance as to what "longest" means in the presence of pattern trailers
20972098
*/
2098-
function comparePatternKeys(a: string, b: string) {
2099+
export function comparePatternKeys(a: string, b: string) {
20992100
const aPatternIndex = a.indexOf("*");
21002101
const bPatternIndex = b.indexOf("*");
21012102
const baseLenA = aPatternIndex === -1 ? a.length : aPatternIndex + 1;
@@ -2361,7 +2362,7 @@ namespace ts {
23612362
}
23622363

23632364
/* @internal */
2364-
export function isApplicableVersionedTypesKey(conditions: string[], key: string) {
2365+
export function isApplicableVersionedTypesKey(conditions: readonly string[], key: string) {
23652366
if (conditions.indexOf("types") === -1) return false; // only apply versioned types conditions if the types condition is applied
23662367
if (!startsWith(key, "types@")) return false;
23672368
const range = VersionRange.tryParse(key.substring("types@".length));

src/services/stringCompletions.ts

Lines changed: 191 additions & 104 deletions
Large diffs are not rendered by default.
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
// @module: nodenext
4+
5+
// @Filename: /node_modules/foo/package.json
6+
//// {
7+
//// "name": "foo",
8+
//// "main": "dist/index.js",
9+
//// "module": "dist/index.mjs",
10+
//// "types": "dist/index.d.ts",
11+
//// "exports": {
12+
//// ".": {
13+
//// "types": "./dist/index.d.ts",
14+
//// "import": "./dist/index.mjs",
15+
//// "default": "./dist/index.js"
16+
//// },
17+
//// "./*": {
18+
//// "types": "./dist/*.d.ts",
19+
//// "import": "./dist/*.mjs",
20+
//// "default": "./dist/*.js"
21+
//// },
22+
//// "./arguments": {
23+
//// "types": "./dist/arguments/index.d.ts",
24+
//// "import": "./dist/arguments/index.mjs",
25+
//// "default": "./dist/arguments/index.js"
26+
//// }
27+
//// }
28+
//// }
29+
30+
// @Filename: /node_modules/foo/dist/index.d.ts
31+
//// export const index = 0;
32+
33+
// @Filename: /node_modules/foo/dist/blah.d.ts
34+
//// export const blah = 0;
35+
36+
// @Filename: /node_modules/foo/dist/arguments/index.d.ts
37+
//// export const arguments = 0;
38+
39+
// @Filename: /index.mts
40+
//// import { } from "foo//**/";
41+
42+
verify.completions({
43+
marker: "",
44+
isNewIdentifierLocation: true,
45+
exact: [
46+
{ name: "blah", kind: "script", kindModifiers: "" },
47+
{ name: "index", kind: "script", kindModifiers: "" },
48+
{ name: "arguments", kind: "script", kindModifiers: "" },
49+
]
50+
});
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
// @module: nodenext
4+
5+
// @Filename: /node_modules/salesforce-pageobjects/package.json
6+
//// {
7+
//// "name": "salesforce-pageobjects",
8+
//// "version": "1.0.0",
9+
//// "exports": {
10+
//// "./*": {
11+
//// "types": "./dist/*.d.ts",
12+
//// "import": "./dist/*.mjs",
13+
//// "default": "./dist/*.js"
14+
//// }
15+
//// }
16+
//// }
17+
18+
// @Filename: /node_modules/salesforce-pageobjects/dist/action/pageObjects/actionRenderer.d.ts
19+
//// export const actionRenderer = 0;
20+
21+
// @Filename: /index.mts
22+
//// import { } from "salesforce-pageobjects//**/";
23+
24+
verify.completions({
25+
marker: "",
26+
isNewIdentifierLocation: true,
27+
exact: [{ name: "action", kind: "directory" }]
28+
});
29+
30+
edit.insert("action/");
31+
32+
verify.completions({
33+
isNewIdentifierLocation: true,
34+
exact: [{ name: "pageObjects", kind: "directory" }],
35+
});
36+
37+
edit.insert("pageObjects/");
38+
39+
verify.completions({
40+
isNewIdentifierLocation: true,
41+
exact: [{ name: "actionRenderer", kind: "script" }],
42+
});
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
// @module: nodenext
4+
5+
// @Filename: /node_modules/foo/package.json
6+
//// {
7+
//// "types": "index.d.ts",
8+
//// "exports": {
9+
//// "./component-*": {
10+
//// "types@>=4.3.5": "types/components/*.d.ts"
11+
//// }
12+
//// }
13+
//// }
14+
15+
// @Filename: /node_modules/foo/nope.d.ts
16+
//// export const nope = 0;
17+
18+
// @Filename: /node_modules/foo/types/components/index.d.ts
19+
//// export const index = 0;
20+
21+
// @Filename: /node_modules/foo/types/components/blah.d.ts
22+
//// export const blah = 0;
23+
24+
// @Filename: /node_modules/foo/types/components/subfolder/one.d.ts
25+
//// export const one = 0;
26+
27+
// @Filename: /a.ts
28+
//// import { } from "foo//**/";
29+
30+
verify.completions({
31+
marker: "",
32+
isNewIdentifierLocation: true,
33+
exact: [
34+
{ name: "component-blah", kind: "script" },
35+
{ name: "component-index", kind: "script" },
36+
{ name: "component-subfolder", kind: "directory" },
37+
],
38+
});
39+
40+
edit.insert("component-subfolder/");
41+
42+
verify.completions({
43+
isNewIdentifierLocation: true,
44+
exact: [{ name: "one", kind: "script" }],
45+
});
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
// @module: nodenext
4+
5+
// @Filename: /node_modules/foo/package.json
6+
//// {
7+
//// "types": "index.d.ts",
8+
//// "exports": {
9+
//// "./*": "dist/*",
10+
//// "./foo/*": "dist/*",
11+
//// "./bar/*": "dist/*",
12+
//// "./exact-match": "dist/index.d.ts"
13+
//// }
14+
//// }
15+
16+
// @Filename: /node_modules/foo/nope.d.ts
17+
//// export const nope = 0;
18+
19+
// @Filename: /node_modules/foo/dist/index.d.ts
20+
//// export const index = 0;
21+
22+
// @Filename: /node_modules/foo/dist/blah.d.ts
23+
//// export const blah = 0;
24+
25+
// @Filename: /node_modules/foo/dist/foo/onlyInFooFolder.d.ts
26+
//// export const foo = 0;
27+
28+
// @Filename: /node_modules/foo/dist/subfolder/one.d.ts
29+
//// export const one = 0;
30+
31+
// @Filename: /a.mts
32+
//// import { } from "foo//**/";
33+
34+
verify.completions({
35+
marker: "",
36+
isNewIdentifierLocation: true,
37+
exact: [
38+
{ name: "blah.js", kind: "script", kindModifiers: ".js" },
39+
{ name: "index.js", kind: "script", kindModifiers: ".js" },
40+
{ name: "foo", kind: "directory" },
41+
{ name: "subfolder", kind: "directory" },
42+
{ name: "bar", kind: "directory" },
43+
{ name: "exact-match", kind: "script" },
44+
],
45+
});
46+
47+
edit.insert("foo/");
48+
49+
verify.completions({
50+
isNewIdentifierLocation: true,
51+
exact: [
52+
{ name: "blah.js", kind: "script", kindModifiers: ".js" },
53+
{ name: "index.js", kind: "script", kindModifiers: ".js" },
54+
{ name: "foo", kind: "directory" },
55+
{ name: "subfolder", kind: "directory" },
56+
],
57+
});
58+
59+
edit.insert("foo/");
60+
61+
verify.completions({
62+
isNewIdentifierLocation: true,
63+
exact: [{ name: "onlyInFooFolder.js", kind: "script", kindModifiers: ".js" }],
64+
});
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
// @module: nodenext
4+
5+
// @Filename: /node_modules/foo/package.json
6+
//// {
7+
//// "name": "foo",
8+
//// "main": "dist/index.js",
9+
//// "module": "dist/index.mjs",
10+
//// "types": "dist/index.d.ts",
11+
//// "exports": {
12+
//// ".": {
13+
//// "import": {
14+
//// "types": "./dist/types/index.d.mts",
15+
//// "default": "./dist/esm/index.mjs"
16+
//// },
17+
//// "default": {
18+
//// "types": "./dist/types/index.d.ts",
19+
//// "default": "./dist/cjs/index.js"
20+
//// }
21+
//// },
22+
//// "./*": {
23+
//// "import": {
24+
//// "types": "./dist/types/*.d.mts",
25+
//// "default": "./dist/esm/*.mjs"
26+
//// },
27+
//// "default": {
28+
//// "types": "./dist/types/*.d.ts",
29+
//// "default": "./dist/cjs/*.js"
30+
//// }
31+
//// },
32+
//// "./only-in-cjs": {
33+
//// "require": {
34+
//// "types": "./dist/types/only-in-cjs/index.d.ts",
35+
//// "default": "./dist/cjs/only-in-cjs/index.js"
36+
//// }
37+
//// }
38+
//// }
39+
//// }
40+
41+
// @Filename: /node_modules/foo/dist/types/index.d.mts
42+
//// export const index = 0;
43+
44+
// @Filename: /node_modules/foo/dist/types/index.d.ts
45+
//// export const index = 0;
46+
47+
// @Filename: /node_modules/foo/dist/types/blah.d.mts
48+
//// export const blah = 0;
49+
50+
// @Filename: /node_modules/foo/dist/types/blah.d.ts
51+
//// export const blah = 0;
52+
53+
// @Filename: /node_modules/foo/dist/types/only-in-cjs/index.d.ts
54+
//// export const onlyInCjs = 0;
55+
56+
// @Filename: /index.mts
57+
//// import { } from "foo//**/";
58+
59+
verify.completions({
60+
marker: "",
61+
isNewIdentifierLocation: true,
62+
exact: [
63+
{ name: "blah", kind: "script", kindModifiers: "" },
64+
{ name: "index", kind: "script", kindModifiers: "" },
65+
]
66+
});
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/// <reference path="fourslash.ts" />
2+
3+
// @module: nodenext
4+
5+
// @Filename: /node_modules/foo/package.json
6+
//// {
7+
//// "name": "foo",
8+
//// "main": "dist/index.js",
9+
//// "module": "dist/index.mjs",
10+
//// "types": "dist/index.d.ts",
11+
//// "exports": {
12+
//// "./*": "./dist/*?.d.ts"
13+
//// }
14+
//// }
15+
16+
// @Filename: /node_modules/foo/dist/index.d.ts
17+
//// export const index = 0;
18+
19+
// @Filename: /node_modules/foo/dist/blah?.d.ts
20+
//// export const blah = 0;
21+
22+
// @Filename: /index.mts
23+
//// import { } from "foo//**/";
24+
25+
verify.completions({
26+
marker: "",
27+
isNewIdentifierLocation: true,
28+
exact: [
29+
{ name: "blah", kind: "script", kindModifiers: "" },
30+
]
31+
});

tests/cases/fourslash/server/nodeNextPathCompletions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,6 @@
3838

3939
verify.baselineCompletions();
4040
edit.insert("dependency/");
41-
verify.completions({ exact: ["lol", "dir/"], isNewIdentifierLocation: true });
41+
verify.completions({ exact: ["lol", "dir"], isNewIdentifierLocation: true });
4242
edit.insert("l");
4343
verify.completions({ exact: ["lol"], isNewIdentifierLocation: true });

0 commit comments

Comments
 (0)