Skip to content

Commit

Permalink
Support nested conditions, improve recursive globbing
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewbranch committed Jun 28, 2022
1 parent ed77c7c commit 7480024
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 14 deletions.
32 changes: 18 additions & 14 deletions src/services/stringCompletions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -711,7 +711,7 @@ namespace ts.Completions.StringCompletions {
extensionOptions,
host,
keys,
key => getPatternFromFirstMatchingCondition(exports[key], conditions),
key => singleElementArray(getPatternFromFirstMatchingCondition(exports[key], conditions)),
comparePatternKeys);
return;
}
Expand All @@ -726,15 +726,15 @@ namespace ts.Completions.StringCompletions {
return arrayFrom(result.values());
}

function getPatternFromFirstMatchingCondition(target: unknown, conditions: readonly string[]): [string] | undefined {
function getPatternFromFirstMatchingCondition(target: unknown, conditions: readonly string[]): string | undefined {
if (typeof target === "string") {
return [target];
return target;
}
if (target && typeof target === "object" && !isArray(target)) {
for (const condition in target) {
if (condition === "default" || conditions.indexOf(condition) > -1 || isApplicableVersionedTypesKey(conditions, condition)) {
const pattern = (target as MapLike<unknown>)[condition];
return typeof pattern === "string" ? [pattern] : undefined;
return getPatternFromFirstMatchingCondition(pattern, conditions);
}
}
}
Expand Down Expand Up @@ -804,27 +804,31 @@ namespace ts.Completions.StringCompletions {
const baseDirectory = normalizePath(combinePaths(packageDirectory, expandedPrefixDirectory));
const completePrefix = fragmentHasPath ? baseDirectory : ensureTrailingDirectorySeparator(baseDirectory) + normalizedPrefixBase;

// If we have a suffix of at least a whole filename (i.e., not just an extension), then we need
// to read the directory all the way down to see which directories contain a file matching that suffix.
const suffixHasFilename = normalizedSuffix && containsSlash(normalizedSuffix);
const includeGlob = suffixHasFilename ? "**/*" : "./*";
// If we have a suffix, then we read the directory all the way down to avoid returning completions for
// directories that don't contain files that would match the suffix. A previous comment here was concerned
// about the case where `normalizedSuffix` includes a `?` character, which should be interpreted literally,
// but will match any single character as part of the `include` pattern in `tryReadDirectory`. This is not
// a problem, because (in the extremely unusual circumstance where the suffix has a `?` in it) a `?`
// interpreted as "any character" can only return *too many* results as compared to the literal
// interpretation, so we can filter those superfluous results out via `trimPrefixAndSuffix` as we've always
// done.
const includeGlob = normalizedSuffix ? "**/*" + normalizedSuffix : "./*";

const matches = mapDefined(tryReadDirectory(host, baseDirectory, extensionOptions.extensions, /*exclude*/ undefined, [includeGlob]), match => {
const trimmedWithPattern = trimPrefixAndSuffix(match);
if (trimmedWithPattern) {
if (containsSlash(trimmedWithPattern)) {
return directoryResult(getPathComponents(trimmedWithPattern)[0]);
return directoryResult(getPathComponents(removeLeadingDirectorySeparator(trimmedWithPattern))[1]);
}
const { name, extension } = getFilenameWithExtensionOption(trimmedWithPattern, host.getCompilationSettings(), extensionOptions.includeExtensionsOption);
return nameAndKind(name, ScriptElementKind.scriptElement, extension);
}
});

// If the suffix had a whole filename in it, we already recursively searched for all possible files
// that could match it and returned the directories that could possibly lead to matches. Otherwise,
// assume any directory could have something valid to import (not a great assumption - we could
// consider doing a whole glob more often and caching the results?)
const directories = suffixHasFilename
// If we had a suffix, we already recursively searched for all possible files that could match
// it and returned the directories leading to those files. Otherwise, assume any directory could
// have something valid to import.
const directories = normalizedSuffix
? emptyArray
: mapDefined(tryGetDirectories(host, baseDirectory), dir => dir === "node_modules" ? undefined : directoryResult(dir));
return [...matches, ...directories];
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/// <reference path="fourslash.ts" />

// @module: nodenext

// @Filename: /node_modules/foo/package.json
//// {
//// "name": "foo",
//// "main": "dist/index.js",
//// "module": "dist/index.mjs",
//// "types": "dist/index.d.ts",
//// "exports": {
//// ".": {
//// "import": {
//// "types": "./dist/types/index.d.mts",
//// "default": "./dist/esm/index.mjs"
//// },
//// "default": {
//// "types": "./dist/types/index.d.ts",
//// "default": "./dist/cjs/index.js"
//// }
//// },
//// "./*": {
//// "import": {
//// "types": "./dist/types/*.d.mts",
//// "default": "./dist/esm/*.mjs"
//// },
//// "default": {
//// "types": "./dist/types/*.d.ts",
//// "default": "./dist/cjs/*.js"
//// }
//// },
//// "./only-in-cjs": {
//// "require": {
//// "types": "./dist/types/only-in-cjs/index.d.ts",
//// "default": "./dist/cjs/only-in-cjs/index.js"
//// }
//// }
//// }
//// }

// @Filename: /node_modules/foo/dist/types/index.d.mts
//// export const index = 0;

// @Filename: /node_modules/foo/dist/types/index.d.ts
//// export const index = 0;

// @Filename: /node_modules/foo/dist/types/blah.d.mts
//// export const blah = 0;

// @Filename: /node_modules/foo/dist/types/blah.d.ts
//// export const blah = 0;

// @Filename: /node_modules/foo/dist/types/only-in-cjs/index.d.ts
//// export const onlyInCjs = 0;

// @Filename: /index.mts
//// import { } from "foo//**/";

verify.completions({
marker: "",
isNewIdentifierLocation: true,
exact: [
{ name: "blah", kind: "script", kindModifiers: "" },
{ name: "index", kind: "script", kindModifiers: "" },
]
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/// <reference path="fourslash.ts" />

// @module: nodenext

// @Filename: /node_modules/foo/package.json
//// {
//// "name": "foo",
//// "main": "dist/index.js",
//// "module": "dist/index.mjs",
//// "types": "dist/index.d.ts",
//// "exports": {
//// "./*": "./dist/*?.d.ts"
//// }
//// }

// @Filename: /node_modules/foo/dist/index.d.ts
//// export const index = 0;

// @Filename: /node_modules/foo/dist/blah?.d.ts
//// export const blah = 0;

// @Filename: /index.mts
//// import { } from "foo//**/";

verify.completions({
marker: "",
isNewIdentifierLocation: true,
exact: [
{ name: "blah", kind: "script", kindModifiers: "" },
]
});

0 comments on commit 7480024

Please sign in to comment.