Skip to content

Commit 2b06e62

Browse files
committed
Add error for unsafe CJS-style resolution
1 parent 2439577 commit 2b06e62

File tree

10 files changed

+87
-1
lines changed

10 files changed

+87
-1
lines changed

src/compiler/checker.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,7 @@ import {
352352
getPropertyAssignmentAliasLikeExpression,
353353
getPropertyNameForPropertyNameNode,
354354
getPropertyNameFromType,
355+
getRelativePathFromFile,
355356
getResolutionDiagnostic,
356357
getResolutionModeOverride,
357358
getResolveJsonModule,
@@ -414,6 +415,7 @@ import {
414415
hasSyntacticModifiers,
415416
hasType,
416417
HeritageClause,
418+
hostGetCanonicalFileName,
417419
Identifier,
418420
identifierToKeywordKind,
419421
IdentifierTypePredicate,
@@ -692,6 +694,7 @@ import {
692694
isParenthesizedTypeNode,
693695
isPartOfParameterDeclaration,
694696
isPartOfTypeNode,
697+
isPartOfTypeOnlyImportOrExportDeclaration,
695698
isPartOfTypeQuery,
696699
isPlainJsFile,
697700
isPrefixUnaryExpression,
@@ -992,6 +995,7 @@ import {
992995
ShorthandPropertyAssignment,
993996
shouldAllowImportingTsExtension,
994997
shouldPreserveConstEnums,
998+
shouldRewriteModuleSpecifier,
995999
Signature,
9961000
SignatureDeclaration,
9971001
SignatureFlags,
@@ -4656,6 +4660,19 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
46564660
error(errorNode, Diagnostics.An_import_path_can_only_end_with_a_0_extension_when_allowImportingTsExtensions_is_enabled, tsExtension);
46574661
}
46584662
}
4663+
else if (
4664+
!(location.flags & NodeFlags.Ambient)
4665+
&& !resolvedModule.resolvedUsingTsExtension
4666+
&& shouldRewriteModuleSpecifier(moduleReference, compilerOptions)
4667+
&& !isLiteralImportTypeNode(location)
4668+
&& !isPartOfTypeOnlyImportOrExportDeclaration(location)
4669+
) {
4670+
error(
4671+
errorNode,
4672+
Diagnostics.This_relative_import_path_is_unsafe_to_rewrite_because_it_looks_like_a_file_name_but_actually_resolves_to_0,
4673+
getRelativePathFromFile(getNormalizedAbsolutePath(currentSourceFile.fileName, host.getCurrentDirectory()), resolvedModule.resolvedFileName, hostGetCanonicalFileName(host)),
4674+
);
4675+
}
46594676

46604677
if (sourceFile.symbol) {
46614678
if (errorNode && resolvedModule.isExternalLibraryImport && !resolutionExtensionIsTSOrJson(resolvedModule.extension)) {

src/compiler/diagnosticMessages.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3948,6 +3948,11 @@
39483948
"category": "Error",
39493949
"code": 2873
39503950
},
3951+
"This relative import path is unsafe to rewrite because it looks like a file name, but actually resolves to \"$0\".": {
3952+
"category": "Error",
3953+
"code": 2874
3954+
},
3955+
39513956
"Import declaration '{0}' is using private name '{1}'.": {
39523957
"category": "Error",
39533958
"code": 4000

src/compiler/transformers/ts.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ import {
182182
setTypeNode,
183183
ShorthandPropertyAssignment,
184184
shouldPreserveConstEnums,
185+
shouldRewriteModuleSpecifier,
185186
skipOuterExpressions,
186187
skipPartiallyEmittedExpressions,
187188
skipTrivia,
@@ -2283,7 +2284,7 @@ export function transformTypeScript(context: TransformationContext) {
22832284
function rewriteModuleSpecifier(node: Expression): Expression;
22842285
function rewriteModuleSpecifier(node: Expression | undefined): Expression | undefined;
22852286
function rewriteModuleSpecifier(node: Expression | undefined): Expression | undefined {
2286-
if (!node || !compilerOptions.rewriteRelativeImportExtensions || !isStringLiteral(node) || !pathIsRelative(node.text) || isDeclarationFileName(node.text)) {
2287+
if (!node || !isStringLiteral(node) || !shouldRewriteModuleSpecifier(node.text, compilerOptions)) {
22872288
return node;
22882289
}
22892290
const updatedText = changeExtension(node.text, getOutputExtension(node.text, compilerOptions));

src/compiler/utilities.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4233,6 +4233,11 @@ export function tryGetImportFromModuleSpecifier(node: StringLiteralLike): AnyVal
42334233
}
42344234
}
42354235

4236+
/** @internal */
4237+
export function shouldRewriteModuleSpecifier(specifier: string, compilerOptions: CompilerOptions): boolean {
4238+
return !!compilerOptions.rewriteRelativeImportExtensions && pathIsRelative(specifier) && !isDeclarationFileName(specifier);
4239+
}
4240+
42364241
/** @internal */
42374242
export function getExternalModuleName(node: AnyImportOrReExport | ImportTypeNode | ImportCall | ModuleDeclaration | JSDocImportTag): Expression | undefined {
42384243
switch (node.kind) {

src/compiler/utilitiesPublic.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1553,6 +1553,10 @@ export function isTypeOnlyImportOrExportDeclaration(node: Node): node is TypeOnl
15531553
return isTypeOnlyImportDeclaration(node) || isTypeOnlyExportDeclaration(node);
15541554
}
15551555

1556+
export function isPartOfTypeOnlyImportOrExportDeclaration(node: Node): boolean {
1557+
return findAncestor(node, isTypeOnlyImportOrExportDeclaration) !== undefined;
1558+
}
1559+
15561560
export function isStringTextContainingNode(node: Node): node is StringLiteral | TemplateLiteralToken {
15571561
return node.kind === SyntaxKind.StringLiteral || isTemplateLiteralKind(node.kind);
15581562
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
index.ts(1,16): error TS2874: This relative import path is unsafe to rewrite because it looks like a file name, but actually resolves to "$0".
2+
3+
4+
==== foo.ts/index.ts (0 errors) ====
5+
export {};
6+
7+
==== index.ts (1 errors) ====
8+
import {} from "./foo.ts";
9+
~~~~~~~~~~
10+
!!! error TS2874: This relative import path is unsafe to rewrite because it looks like a file name, but actually resolves to "$0".
11+
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//// [tests/cases/conformance/externalModules/rewriteRelativeImportExtensions/cjsErrors.ts] ////
2+
3+
//// [index.ts]
4+
export {};
5+
6+
//// [index.ts]
7+
import {} from "./foo.ts";
8+
9+
10+
//// [index.js]
11+
"use strict";
12+
Object.defineProperty(exports, "__esModule", { value: true });
13+
//// [index.js]
14+
"use strict";
15+
Object.defineProperty(exports, "__esModule", { value: true });
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//// [tests/cases/conformance/externalModules/rewriteRelativeImportExtensions/cjsErrors.ts] ////
2+
3+
=== foo.ts/index.ts ===
4+
5+
export {};
6+
7+
=== index.ts ===
8+
9+
import {} from "./foo.ts";
10+
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//// [tests/cases/conformance/externalModules/rewriteRelativeImportExtensions/cjsErrors.ts] ////
2+
3+
=== foo.ts/index.ts ===
4+
5+
export {};
6+
7+
=== index.ts ===
8+
9+
import {} from "./foo.ts";
10+
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// @module: nodenext
2+
// @rewriteRelativeImportExtensions: true
3+
4+
// @Filename: foo.ts/index.ts
5+
export {};
6+
7+
// @Filename: index.ts
8+
import {} from "./foo.ts";

0 commit comments

Comments
 (0)