10000 `moduleResolution: node12` support by weswigham · Pull Request #45884 · microsoft/TypeScript · GitHub
[go: up one dir, main page]

Skip to content

moduleResolution: node12 support #45884

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 31 commits into from
Sep 24, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
c416aa9
Initial support for module: node12
weswigham Jun 8, 2021
34812f0
Add allowJs and declaration emit enabled tests
weswigham Jun 8, 2021
b447ed7
Fix typos
weswigham Jul 12, 2021
9b83a2e
cts, mts, cjs, mjs, etc extension support
weswigham Jul 6, 2021
44675c9
Merge branch 'main' into module-node
weswigham Aug 26, 2021
55d0728
Merge branch 'module-node' into mjscjs-support
weswigham Aug 26, 2021
e6a386c
Fix watch of files whose intepretation changes due to a package.json …
weswigham Aug 26, 2021
fd2190f
Merge branch 'main' into module-node
weswigham Sep 1, 2021
13e76d7
Merge branch 'module-node' into mjscjs-support
weswigham Sep 1, 2021
b12315f
Minor PR feedback
weswigham Sep 1, 2021
15e1904
Adjust error message
weswigham Sep 1, 2021
093002c
Initial import/export/self-name support
weswigham Sep 14, 2021
4ab3f7e
Merge branch 'main' into module-node
weswigham Sep 15, 2021
a092fd3
Merge branch 'module-node' into mjscjs-support
weswigham Sep 15, 2021
5bf44ac
Accept new error codes
weswigham Sep 15, 2021
859abb6
Merge branch 'mjscjs-support' into imports-exports-selfs
weswigham Sep 15, 2021
c21cb48
TypesVersions support in export/import map conditions
weswigham Sep 15, 2021
9294633
Fix import suggestion and autoimport default extensions under new res…
weswigham Sep 20, 2021
c776453
Add tests for import maps non-relative name lookup feature
weswigham Sep 20, 2021
ae83938
Fix isDeclarationFileName for .d.mts and .d.cts
weswigham Sep 20, 2021
8ab7517
Preserve new extensions when generating module specifiers
weswigham Sep 20, 2021
222ba00
Fix spurious implict any suggestion caused by file ordering bug and o…
weswigham Sep 21, 2021
408ef20
Fix a bunch of incremental bugs that dont repro under fourslash for s…
weswigham Sep 21, 2021
6aaa681
Merge branch 'main' into module-node
weswigham Sep 21, 2021
21b73e8
Accept updated baseline
weswigham Sep 21, 2021
c160805
Merge branch 'module-node' into mjscjs-support
weswigham Sep 21, 2021
44b81b2
Merge branch 'mjscjs-support' into imports-exports-selfs
weswigham Sep 21, 2021
4a33975
Always include extensions on completions for cjs/mjs style imports
weswigham Sep 21, 2021
76afb79
String completion relative import suggestions respect the mode of the…
weswigham Sep 21, 2021
0891db4
Style feedback
weswigham Sep 23, 2021
a2e799a
Change diagnostic case
weswigham Sep 24, 2021
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
Prev Previous commit
Next Next commit
cts, mts, cjs, mjs, etc extension support
  • Loading branch information
weswigham committed Jul 12, 2021
commit 9b83a2ef040f8cdc538011273248bc6bb31e715c
2 changes: 1 addition & 1 deletion src/compiler/builderState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ namespace ts {
);
const firstDts = firstOrUndefined(emitOutput.outputFiles);
if (firstDts) {
Debug.assert(fileExtensionIs(firstDts.name, Extension.Dts), "File extension for signature expected to be dts", () => `Found: ${getAnyExtensionFromPath(firstDts.name)} for ${firstDts.name}:: All output files: ${JSON.stringify(emitOutput.outputFiles.map(f => f.name))}`);
Debug.assert(fileExtensionIsOneOf(firstDts.name, [Extension.Dts, Extension.Dmts, Extension.Dcts]), "File extension for signature expected to be dts", () => `Found: ${getAnyExtensionFromPath(firstDts.name)} for ${firstDts.name}:: All output files: ${JSON.stringify(emitOutput.outputFiles.map(f => f.name))}`);
latestSignature = (computeHash || generateDjb2Hash)(firstDts.text);
if (exportedModulesMapCache && latestSignature !== prevSignature) {
updateExportedModules(sourceFile, emitOutput.exportedModulesFromDeclarationEmit, exportedModulesMapCache);
Expand Down
12 changes: 12 additions & 0 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30386,6 +30386,12 @@ namespace ts {
}

function checkAssertion(node: AssertionExpression) {
if (node.kind === SyntaxKind.TypeAssertionExpression) {
const file = getSourceFileOfNode(node);
if (file && fileExtensionIsOneOf(file.fileName, [Extension.Cts, Extension.Mts])) {
grammarErrorOnNode(node, Diagnostics.This_syntax_is_reserved_in_files_with_this_extension_Use_an_as_expression_instead);
}
}
return checkAssertionWorker(node, node.type, node.expression);
}

Expand Down Expand Up @@ -41549,6 +41555,12 @@ namespace ts {
return false;
}

if (node.typeParameters && !(length(node.typeParameters) > 1 || node.typeParameters.hasTrailingComma || node.typeParameters[0].constraint)) {
if (file && fileExtensionIsOneOf(file.fileName, [Extension.Mts, Extension.Cts])) {
grammarErrorOnNode(node.typeParameters[0], Diagnostics.This_syntax_is_reserved_in_files_with_this_extension_Add_a_trailing_comma_or_explicit_constraint);
}
}

const { equalsGreaterThanToken } = node;
const startLine = getLineAndCharacterOfPosition(file, equalsGreaterThanToken.pos).line;
const endLine = getLineAndCharacterOfPosition(file, equalsGreaterThanToken.end).line;
Expand Down
45 changes: 28 additions & 17 deletions src/compiler/commandLineParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3214,7 +3214,7 @@ namespace ts {

let jsonOnlyIncludeRegexes: readonly RegExp[] | undefined;
if (validatedIncludeSpecs && validatedIncludeSpecs.length > 0) {
for (const file of host.readDirectory(basePath, supportedExtensionsWithJsonIfResolveJsonModule, validatedExcludeSpecs, validatedIncludeSpecs, /*depth*/ undefined)) {
for (const file of host.readDirectory(basePath, flatten(supportedExtensionsWithJsonIfResolveJsonModule), validatedExcludeSpecs, validatedIncludeSpecs, /*depth*/ undefined)) {
if (fileExtensionIs(file, Extension.Json)) {
// Valid only if *.json specified
if (!jsonOnlyIncludeRegexes) {
Expand Down Expand Up @@ -3439,16 +3439,24 @@ namespace ts {
* extension priority.
*
* @param file The path to the file.
* @param extensionPriority The priority of the extension.
* @param context The expansion context.
*/
function hasFileWithHigherPriorityExtension(file: string, literalFiles: ESMap<string, string>, wildcardFiles: ESMap<string, string>, extensions: readonly string[], keyMapper: (value: string) => string) {
const extensionPriority = getExtensionPriority(file, extensions);
const adjustedExtensionPriority = adjustExtensionPriority(extensionPriority, extensions);
for (let i = ExtensionPriority.Highest; i < adjustedExtensionPriority; i++) {
const higherPriorityExtension = extensions[i];
const higherPriorityPath = keyMapper(changeExtension(file, higherPriorityExtension));
function hasFileWithHigherPriorityExtension(file: string, literalFiles: ESMap<string, string>, wildcardFiles: ESMap<string, string>, extensions: readonly string[][], keyMapper: (value: string) => string) {
const extensionGroup = forEach(extensions, group => fileExtensionIsOneOf(file, group) ? group : undefined);
if (!extensionGroup) {
return false;
}
for (const ext of extensionGroup) {
if (fileExtensionIs(file, ext)) {
break;
}
const higherPriorityPath = keyMapper(changeExtension(file, ext));
if (literalFiles.has(higherPriorityPath) || wildcardFiles.has(higherPriorityPath)) {
if (ext === Extension.Dts && (fileExtensionIs(file, Extension.Js) || fileExtensionIs(file, Extension.Jsx))) {
// LEGACY BEHAVIOR: An off-by-one bug somewhere in the extension priority system for wildcard module loading allowed declaration
// files to be loaded alongside their js(x) counterparts. We regard this as generally undesirable, but retain the behavior to
// prevent breakage.
continue;
}
return true;
}
}
Expand All @@ -3461,15 +3469,18 @@ namespace ts {
* already been included.
*
* @param file The path to the file.
* @param extensionPriority The priority of the extension.
* @param context The expansion context.
*/
function removeWildcardFilesWithLowerPriorityExtension(file: string, wildcardFiles: ESMap<string, string>, extensions: readonly string[], keyMapper: (value: string) => string) {
const extensionPriority = getExtensionPriority(file, extensions);
const nextExtensionPriority = getNextLowestExtensionPriority(extensionPriority, extensions);
for (let i = nextExtensionPriority; i < extensions.length; i++) {
const lowerPriorityExtension = extensions[i];
const lowerPriorityPath = keyMapper(changeExtension(file, lowerPriorityExtension));
function removeWildcardFilesWithLowerPriorityExtension(file: string, wildcardFiles: ESMap<string, string>, extensions: readonly string[][], keyMapper: (value: string) => string) {
const extensionGroup = forEach(extensions, group => fileExtensionIsOneOf(file, group) ? group : undefined);
if (!extensionGroup) {
return;
}
for (let i = extensionGroup.length - 1; i >= 0; i--) {
const ext = extensionGroup[i];
if (fileExtensionIs(file, ext)) {
break;
}
const lowerPriorityPath = keyMapper(changeExtension(file, ext));
wildcardFiles.delete(lowerPriorityPath);
}
}
Expand Down
8 changes: 8 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -5975,6 +5975,14 @@
"category": "Error",
"code": 7057
},
"This syntax is reserved in files with this extension. Use an `as` expression instead.": {
"category": "Error",
"code": 7058
},
"This syntax is reserved in files with this extension. Add a trailing comma or explicit constraint.": {
"category": "Error",
"code": 7059
},

"You cannot rename this element.": {
"category": "Error",
Expand Down
34 changes: 13 additions & 21 deletions src/compiler/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ namespace ts {
return getOutputPathsForBundle(options, forceDtsPaths);
}
else {
const ownOutputFilePath = getOwnEmitOutputFilePath(sourceFile.fileName, host, getOutputExtension(sourceFile, options));
const ownOutputFilePath = getOwnEmitOutputFilePath(sourceFile.fileName, host, getOutputExtension(sourceFile.fileName, options));
const isJsonFile = isJsonSourceFile(sourceFile);
// If json file emits to the same location skip writing it, if emitDeclarationOnly skip writing it
const isJsonEmittedToSameLocation = isJsonFile &&
Expand All @@ -106,26 +106,23 @@ namespace ts {
return (options.sourceMap && !options.inlineSourceMap) ? jsFilePath + ".map" : undefined;
}

// JavaScript files are always LanguageVariant.JSX, as JSX syntax is allowed in .js files also.
// So for JavaScript files, '.jsx' is only emitted if the input was '.jsx', and JsxEmit.Preserve.
// For TypeScript, the only time to emit with a '.jsx' extension, is on JSX input, and JsxEmit.Preserve
/* @internal */
export function getOutputExtension(sourceFile: SourceFile, options: CompilerOptions): Extension {
if (isJsonSourceFile(sourceFile)) {
export function getOutputExtension(fileName: string, options: CompilerOptions): Extension {
if (fileExtensionIs(fileName, Extension.Json)) {
return Extension.Json;
}

if (options.jsx === JsxEmit.Preserve) {
if (isSourceFileJS(sourceFile)) {
if (fileExtensionIs(sourceFile.fileName, Extension.Jsx)) {
return Extension.Jsx;
}
}
else if (sourceFile.languageVariant === LanguageVariant.JSX) {
// TypeScript source file preserving JSX syntax
if (fileExtensionIsOneOf(fileName, [Extension.Jsx, Extension.Tsx])) {
return Extension.Jsx;
}
}
if (fileExtensionIsOneOf(fileName, [Extension.Mts, Extension.Mjs])) {
return Extension.Mjs;
}
if (fileExtensionIsOneOf(fileName, [Extension.Cts, Extension.Cjs])) {
return Extension.Cjs;
}
return Extension.Js;
}

Expand All @@ -140,10 +137,9 @@ namespace ts {

/* @internal */
export function getOutputDeclarationFileName(inputFileName: string, configFile: ParsedCommandLine, ignoreCase: boolean, getCommonSourceDirectory?: () => string) {
Debug.assert(!fileExtensionIs(inputFileName, Extension.Dts) && !fileExtensionIs(inputFileName, Extension.Json));
return changeExtension(
getOutputPathWithoutChangingExt(inputFileName, configFile, ignoreCase, configFile.options.declarationDir || configFile.options.outDir, getCommonSourceDirectory),
Extension.Dts
getDeclarationEmitExtensionForPath(inputFileName)
);
}

Expand All @@ -152,11 +148,7 @@ namespace ts {
const isJsonFile = fileExtensionIs(inputFileName, Extension.Json);
const outputFileName = changeExtension(
getOutputPathWithoutChangingExt(inputFileName, configFile, ignoreCase, configFile.options.outDir, getCommonSourceDirectory),
isJsonFile ?
Extension.Json :
configFile.options.jsx === JsxEmit.Preserve && (fileExtensionIs(inputFileName, Extension.Tsx) || fileExtensionIs(inputFileName, Extension.Jsx)) ?
Extension.Jsx :
Extension.Js
getOutputExtension(inputFileName, configFile.options)
);
return !isJsonFile || comparePaths(inputFileName, outputFileName, Debug.checkDefined(configFile.options.configFilePath), ignoreCase) !== Comparison.EqualTo ?
outputFileName :
Expand Down Expand Up @@ -238,7 +230,7 @@ namespace ts {
export function getCommonSourceDirectoryOfConfig({ options, fileNames }: ParsedCommandLine, ignoreCase: boolean): string {
return getCommonSourceDirectory(
options,
() => filter(fileNames, file => !(options.noEmitForJsFiles && fileExtensionIsOneOf(file, supportedJSExtensions)) && !fileExtensionIs(file, Extension.Dts)),
() => filter(fileNames, file => !(options.noEmitForJsFiles && fileExtensionIsOneOf(file, supportedJSExtensionsFlat)) && !fileExtensionIs(file, Extension.Dts)),
getDirectoryPath(normalizeSlashes(Debug.checkDefined(options.configFilePath))),
createGetCanonicalFileName(!ignoreCase)
);
Expand Down
57 changes: 49 additions & 8 deletions src/compiler/moduleNameResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1244,11 +1244,12 @@ namespace ts {
function loadModuleFromFile(extensions: Extensions, candidate: string, onlyRecordFailures: boolean, state: ModuleResolutionState): PathAndExtension | undefined {
if (extensions === Extensions.Json || extensions === Extensions.TSConfig) {
const extensionLess = tryRemoveExtension(candidate, Extension.Json);
return (extensionLess === undefined && extensions === Extensions.Json) ? undefined : tryAddingExtensions(extensionLess || candidate, extensions, onlyRecordFailures, state);
const extension = extensionLess ? candidate.substring(extensionLess.length) : "";
return (extensionLess === undefined && extensions === Extensions.Json) ? undefined : tryAddingExtensions(extensionLess || candidate, extensions, extension, onlyRecordFailures, state);
}

// First, try adding an extension. An import of "foo" could be matched by a file "foo.ts", or "foo.js" by "foo.js.ts"
const resolvedByAddingExtension = tryAddingExtensions(candidate, extensions, onlyRecordFailures, state);
const resolvedByAddingExtension = tryAddingExtensions(candidate, extensions, "", onlyRecordFailures, state);
if (resolvedByAddingExtension) {
return resolvedByAddingExtension;
}
Expand All @@ -1257,16 +1258,16 @@ namespace ts {
// e.g. "./foo.js" can be matched by "./foo.ts" or "./foo.d.ts"
if (hasJSFileExtension(candidate)) {
const extensionless = removeFileExtension(candidate);
const extension = candidate.substring(extensionless.length);
if (state.traceEnabled) {
const extension = candidate.substring(extensionless.length);
trace(state.host, Diagnostics.File_name_0_has_a_1_extension_stripping_it, candidate, extension);
}
return tryAddingExtensions(extensionless, extensions, onlyRecordFailures, state);
return tryAddingExtensions(extensionless, extensions, extension, onlyRecordFailures, state);
}
}

/** Try to return an existing file that adds one of the `extensions` to `candidate`. */
function tryAddingExtensions(candidate: string, extensions: Extensions, onlyRecordFailures: boolean, state: ModuleResolutionState): PathAndExtension | undefined {
function tryAddingExtensions(candidate: string, extensions: Extensions, originalExtension: string, onlyRecordFailures: boolean, state: ModuleResolutionState): PathAndExtension | undefined {
if (!onlyRecordFailures) {
// check if containing folder exists - if it doesn't then just record failures for all supported extensions without disk probing
const directory = getDirectoryPath(candidate);
Expand All @@ -1277,11 +1278,51 @@ namespace ts {

switch (extensions) {
case Extensions.DtsOnly:
return tryExtension(Extension.Dts);
switch (originalExtension) {
case Extension.Mjs:
case Extension.Mts:
case Extension.Dmts:
return tryExtension(Extension.Dmts);
case Extension.Cjs:
case Extension.Cts:
case Extension.Dcts:
return tryExtension(Extension.Dcts);
case Extension.Json:
candidate += Extension.Json;
return tryExtension(Extension.Dts);
default: return tryExtension(Extension.Dts);
}
case Extensions.TypeScript:
return tryExtension(Extension.Ts) || tryExtension(Extension.Tsx) || tryExtension(Extension.Dts);
switch (originalExtension) {
case Extension.Mjs:
case Extension.Mts:
case Extension.Dmts:
return tryExtension(Extension.Mts) || tryExtension(Extension.Dmts);
case Extension.Cjs:
case Extension.Cts:
case Extension.Dcts:
return tryExtension(Extension.Cts) || tryExtension(Extension.Dcts);
case Extension.Json:
candidate += Extension.Json;
return tryExtension(Extension.Dts);
default:
return tryExtension(Extension.Ts) || tryExtension(Extension.Tsx) || tryExtension(Extension.Dts);
}
case Extensions.JavaScript:
return tryExtension(Extension.Js) || tryExtension(Extension.Jsx);
switch (originalExtension) {
case Extension.Mjs:
case Extension.Mts:
case Extension.Dmts:
return tryExtension(Extension.Mjs);
case Extension.Cjs:
case Extension.Cts:
case Extension.Dcts:
return tryExtension(Extension.Cjs);
case Extension.Json:
return tryExtension(Extension.Json);
default:
return tryExtension(Extension.Js) || tryExtension(Extension.Jsx);
}
case Extensions.TSConfig:
case Extensions.Json:
return tryExtension(Extension.Json);
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/moduleSpecifiers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -652,7 +652,7 @@ namespace ts.moduleSpecifiers {
function tryGetAnyFileFromPath(host: ModuleSpecifierResolutionHost, path: string) {
if (!host.fileExists) return;
// We check all js, `node` and `json` extensions in addition to TS, since node module resolution would also choose those over the directory
const extensions = getSupportedExtensions({ allowJs: true }, [{ extension: "node", isMixedContent: false }, { extension: "json", isMixedContent: false, scriptKind: ScriptKind.JSON }]);
const extensions = flatten(getSupportedExtensions({ allowJs: true }, [{ extension: "node", isMixedContent: false }, { extension: "json", isMixedContent: false, scriptKind: ScriptKind.JSON }]));
for (const e of extensions) {
const fullPath = path + e;
if (host.fileExists(fullPath)) {
Expand Down
Loading
0