Skip to content

Commit ac8edc6

Browse files
Completion list for JSON files imported using CommonJS require
1 parent 8ad68ad commit ac8edc6

26 files changed

+336
-17
lines changed

src/compiler/checker.ts

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1342,8 +1342,8 @@ namespace ts {
13421342
return (symbol.flags & meaning) || dontResolveAlias ? symbol : resolveAlias(symbol);
13431343
}
13441344

1345-
function resolveExternalModuleName(location: Node, moduleReferenceExpression: Expression): Symbol {
1346-
return resolveExternalModuleNameWorker(location, moduleReferenceExpression, Diagnostics.Cannot_find_module_0);
1345+
function resolveExternalModuleName(location: Node, moduleReferenceExpression: Expression, reportModuleNotFoundError = true): Symbol {
1346+
return resolveExternalModuleNameWorker(location, moduleReferenceExpression, reportModuleNotFoundError ? Diagnostics.Cannot_find_module_0 : undefined);
13471347
}
13481348

13491349
function resolveExternalModuleNameWorker(location: Node, moduleReferenceExpression: Expression, moduleNotFoundError: DiagnosticMessage): Symbol {
@@ -4881,11 +4881,17 @@ namespace ts {
48814881
}
48824882

48834883
function resolveExternalModuleTypeByLiteral(name: StringLiteral) {
4884-
const moduleSym = resolveExternalModuleName(name, name);
4884+
const nameIsInJavaScriptFile = isInJavaScriptFile(name);
4885+
const moduleSym = resolveExternalModuleName(name, name, /*reportModuleNotFoundError*/ nameIsInJavaScriptFile);
48854886
if (moduleSym) {
4886-
const resolvedModuleSymbol = resolveExternalModuleSymbol(moduleSym);
4887-
if (resolvedModuleSymbol) {
4888-
return getTypeOfSymbol(resolvedModuleSymbol);
4887+
const sourceFile = getDeclarationOfKind(moduleSym, SyntaxKind.SourceFile) as SourceFile;
4888+
const resolvedFileIsJSON = sourceFile && getScriptKindFromFileName(sourceFile.fileName) === ScriptKind.JSON;
4889+
// Treated as external module import if it is in JavaScript file or when JSON is "required"
4890+
if (nameIsInJavaScriptFile || resolvedFileIsJSON) {
4891+
const resolvedModuleSymbol = resolveExternalModuleSymbol(moduleSym);
4892+
if (resolvedModuleSymbol) {
4893+
return getTypeOfSymbol(resolvedModuleSymbol);
4894+
}
48894895
}
48904896
}
48914897

@@ -12824,8 +12830,8 @@ namespace ts {
1282412830
}
1282512831
}
1282612832

12827-
// In JavaScript files, calls to any identifier 'require' are treated as external module imports
12828-
if (isInJavaScriptFile(node) && isCommonJsRequire(node)) {
12833+
// Calls to commonjs 'require' are treated as external module imports in JavaScript files or when JSON is "required"
12834+
if (isCommonJsRequire(node)) {
1282912835
return resolveExternalModuleTypeByLiteral(<StringLiteral>node.arguments[0]);
1283012836
}
1283112837

@@ -18793,7 +18799,7 @@ namespace ts {
1879318799
(<ImportDeclaration>node.parent).moduleSpecifier === node)) {
1879418800
return resolveExternalModuleName(node, <LiteralExpression>node);
1879518801
}
18796-
if (isInJavaScriptFile(node) && isRequireCall(node.parent, /*checkArgumentIsStringLiteral*/ false)) {
18802+
if (isCommonJsRequire(node.parent)) {
1879718803
return resolveExternalModuleName(node, <LiteralExpression>node);
1879818804
}
1879918805
// Fall through

src/compiler/core.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1793,6 +1793,8 @@ namespace ts {
17931793
return ScriptKind.TS;
17941794
case ".tsx":
17951795
return ScriptKind.TSX;
1796+
case ".json":
1797+
return ScriptKind.JSON;
17961798
default:
17971799
return ScriptKind.Unknown;
17981800
}
@@ -1806,6 +1808,7 @@ namespace ts {
18061808
export const supportedTypescriptExtensionsForExtractExtension = [".d.ts", ".ts", ".tsx"];
18071809
export const supportedJavascriptExtensions = [".js", ".jsx"];
18081810
const allSupportedExtensions = supportedTypeScriptExtensions.concat(supportedJavascriptExtensions);
1811+
export const supportedTypeScriptAndJsonExtensions = supportedTypeScriptExtensions.concat([".json"]);
18091812

18101813
export function getSupportedExtensions(options?: CompilerOptions): string[] {
18111814
return options && options.allowJs ? allSupportedExtensions : supportedTypeScriptExtensions;

src/compiler/moduleNameResolver.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -514,7 +514,8 @@ namespace ts {
514514

515515
export function nodeModuleNameResolver(moduleName: string, containingFile: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModuleWithFailedLookupLocations {
516516
const containingDirectory = getDirectoryPath(containingFile);
517-
const supportedExtensions = getSupportedExtensions(compilerOptions);
517+
const isCommonJS = getEmitModuleKind(compilerOptions) === ModuleKind.CommonJS;
518+
const supportedExtensions = getSupportedExtensions(compilerOptions).concat(isCommonJS ? [".json"] : []);
518519
const traceEnabled = isTraceEnabled(compilerOptions, host);
519520

520521
const failedLookupLocations: string[] = [];

src/compiler/parser.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -636,7 +636,17 @@ namespace ts {
636636
nextToken();
637637
processReferenceComments(sourceFile);
638638

639-
sourceFile.statements = parseList(ParsingContext.SourceElements, parseStatement);
639+
if (scriptKind === ScriptKind.JSON) {
640+
const exportAssignment = <ExportAssignment>createNode(SyntaxKind.ExportAssignment);
641+
const json = parseObjectLiteralExpression();
642+
exportAssignment.expression = json;
643+
exportAssignment.isExportEquals = true;
644+
finishNode(exportAssignment);
645+
sourceFile.statements = <NodeArray<Statement>>[<Statement>exportAssignment];
646+
}
647+
else {
648+
sourceFile.statements = parseList(ParsingContext.SourceElements, parseStatement);
649+
}
640650
Debug.assert(token() === SyntaxKind.EndOfFileToken);
641651
sourceFile.endOfFileToken = <EndOfFileToken>parseTokenNode();
642652

src/compiler/program.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -583,7 +583,7 @@ namespace ts {
583583
getNewLine: () => host.getNewLine(),
584584
getSourceFile: program.getSourceFile,
585585
getSourceFileByPath: program.getSourceFileByPath,
586-
getSourceFiles: program.getSourceFiles,
586+
getSourceFiles: () => filter(program.getSourceFiles(), sourceFile => sourceFile.scriptKind !== ScriptKind.JSON),
587587
isSourceFileFromExternalLibrary: (file: SourceFile) => !!sourceFilesFoundSearchingNodeModules[file.path],
588588
writeFile: writeFileCallback || (
589589
(fileName, data, writeByteOrderMark, onError, sourceFiles) => host.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles)),
@@ -984,6 +984,7 @@ namespace ts {
984984

985985
const isJavaScriptFile = isSourceFileJavaScript(file);
986986
const isExternalModuleFile = isExternalModule(file);
987+
const isCommonJS = getEmitModuleKind(options) === ModuleKind.CommonJS;
987988

988989
let imports: LiteralExpression[];
989990
let moduleAugmentations: LiteralExpression[];
@@ -1001,7 +1002,7 @@ namespace ts {
10011002

10021003
for (const node of file.statements) {
10031004
collectModuleReferences(node, /*inAmbientModule*/ false);
1004-
if (isJavaScriptFile) {
1005+
if (isJavaScriptFile || isCommonJS) {
10051006
collectRequireCalls(node);
10061007
}
10071008
}

src/compiler/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3077,7 +3077,8 @@ namespace ts {
30773077
JS = 1,
30783078
JSX = 2,
30793079
TS = 3,
3080-
TSX = 4
3080+
TSX = 4,
3081+
JSON = 5
30813082
}
30823083

30833084
export const enum ScriptTarget {

src/harness/unittests/moduleResolution.ts

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ namespace ts {
100100
assert.equal(resolution.resolvedModule.resolvedFileName, moduleFile.name);
101101
assert.equal(!!resolution.resolvedModule.isExternalLibraryImport, false);
102102
// expect three failed lookup location - attempt to load module as file with all supported extensions
103-
assert.equal(resolution.failedLookupLocations.length, supportedTypeScriptExtensions.length);
103+
assert.equal(resolution.failedLookupLocations.length, supportedTypeScriptAndJsonExtensions.length);
104104
}
105105
}
106106

@@ -154,6 +154,7 @@ namespace ts {
154154
"/a/b/foo.ts",
155155
"/a/b/foo.tsx",
156156
"/a/b/foo.d.ts",
157+
"/a/b/foo.json",
157158
"/a/b/foo/index.ts",
158159
"/a/b/foo/index.tsx",
159160
]);
@@ -589,25 +590,30 @@ import b = require("./moduleB");
589590
"/root/folder1/file2.ts",
590591
"/root/folder1/file2.tsx",
591592
"/root/folder1/file2.d.ts",
593+
"/root/folder1/file2.json",
592594
"/root/folder1/file2/package.json",
593595
"/root/folder1/file2/index.ts",
594596
"/root/folder1/file2/index.tsx",
595-
"/root/folder1/file2/index.d.ts"
597+
"/root/folder1/file2/index.d.ts",
598+
"/root/folder1/file2/index.json"
596599
// then first attempt on 'generated/*' was successful
597600
]);
598601
check("folder2/file3", file3, [
599602
// first try '*'
600603
"/root/folder2/file3.ts",
601604
"/root/folder2/file3.tsx",
602605
"/root/folder2/file3.d.ts",
606+
"/root/folder2/file3.json",
603607
"/root/folder2/file3/package.json",
604608
"/root/folder2/file3/index.ts",
605609
"/root/folder2/file3/index.tsx",
606610
"/root/folder2/file3/index.d.ts",
611+
"/root/folder2/file3/index.json",
607612
// then use remapped location
608613
"/root/generated/folder2/file3.ts",
609614
"/root/generated/folder2/file3.tsx",
610615
"/root/generated/folder2/file3.d.ts",
616+
"/root/generated/folder2/file3.json",
611617
"/root/generated/folder2/file3/package.json",
612618
"/root/generated/folder2/file3/index.ts",
613619
"/root/generated/folder2/file3/index.tsx",
@@ -618,14 +624,17 @@ import b = require("./moduleB");
618624
"/root/folder2/file4.ts",
619625
"/root/folder2/file4.tsx",
620626
"/root/folder2/file4.d.ts",
627+
"/root/folder2/file4.json",
621628
"/root/folder2/file4/package.json",
622629
"/root/folder2/file4/index.ts",
623630
"/root/folder2/file4/index.tsx",
624631
"/root/folder2/file4/index.d.ts",
632+
"/root/folder2/file4/index.json",
625633
// try to load from file from remapped location
626634
"/root/generated/folder2/file4.ts",
627635
"/root/generated/folder2/file4.tsx",
628-
"/root/generated/folder2/file4.d.ts"
636+
"/root/generated/folder2/file4.d.ts",
637+
"/root/generated/folder2/file4.json"
629638
// success on loading as from folder
630639
]);
631640
check("somefolder/file5", file5, [
@@ -634,6 +643,7 @@ import b = require("./moduleB");
634643
"/root/someanotherfolder/file5.ts",
635644
"/root/someanotherfolder/file5.tsx",
636645
"/root/someanotherfolder/file5.d.ts",
646+
"/root/someanotherfolder/file5.json",
637647
// load from folder
638648
"/root/someanotherfolder/file5/package.json",
639649
"/root/someanotherfolder/file5/index.ts",
@@ -646,21 +656,25 @@ import b = require("./moduleB");
646656
"/root/file6.ts",
647657
"/root/file6.tsx",
648658
"/root/file6.d.ts",
659+
"/root/file6.json",
649660
// load from folder
650661
"/root/file6/package.json",
651662
"/root/file6/index.ts",
652663
"/root/file6/index.tsx",
653664
"/root/file6/index.d.ts",
665+
"/root/file6/index.json",
654666
// then try 'generated/*'
655667
// load from file
656668
"/root/generated/file6.ts",
657669
"/root/generated/file6.tsx",
658670
"/root/generated/file6.d.ts",
671+
"/root/generated/file6.json",
659672
// load from folder
660673
"/root/generated/file6/package.json",
661674
"/root/generated/file6/index.ts",
662675
"/root/generated/file6/index.tsx",
663676
"/root/generated/file6/index.d.ts",
677+
"/root/generated/file6/index.json",
664678
// fallback to standard node behavior
665679
// load from file
666680
"/root/folder1/node_modules/file6.ts",
@@ -774,11 +788,13 @@ import b = require("./moduleB");
774788
"/root/folder1/file2.ts",
775789
"/root/folder1/file2.tsx",
776790
"/root/folder1/file2.d.ts",
791+
"/root/folder1/file2.json",
777792
// load from folder
778793
"/root/folder1/file2/package.json",
779794
"/root/folder1/file2/index.ts",
780795
"/root/folder1/file2/index.tsx",
781796
"/root/folder1/file2/index.d.ts",
797+
"/root/folder1/file2/index.json",
782798
// success after using alternative rootDir entry
783799
]);
784800
check("../folder1/file1", file3, file1, [
@@ -787,11 +803,13 @@ import b = require("./moduleB");
787803
"/root/generated/folder1/file1.ts",
788804
"/root/generated/folder1/file1.tsx",
789805
"/root/generated/folder1/file1.d.ts",
806+
"/root/generated/folder1/file1.json",
790807
// load from module
791808
"/root/generated/folder1/file1/package.json",
792809
"/root/generated/folder1/file1/index.ts",
793810
"/root/generated/folder1/file1/index.tsx",
794811
"/root/generated/folder1/file1/index.d.ts",
812+
"/root/generated/folder1/file1/index.json",
795813
// success after using alternative rootDir entry
796814
]);
797815
check("../folder1/file1_1", file3, file1_1, [
@@ -800,16 +818,19 @@ import b = require("./moduleB");
800818
"/root/generated/folder1/file1_1.ts",
801819
"/root/generated/folder1/file1_1.tsx",
802820
"/root/generated/folder1/file1_1.d.ts",
821+
"/root/generated/folder1/file1_1.json",
803822
// load from folder
804823
"/root/generated/folder1/file1_1/package.json",
805824
"/root/generated/folder1/file1_1/index.ts",
806825
"/root/generated/folder1/file1_1/index.tsx",
807826
"/root/generated/folder1/file1_1/index.d.ts",
827+
"/root/generated/folder1/file1_1/index.json",
808828
// try alternative rootDir entry
809829
// load from file
810830
"/root/folder1/file1_1.ts",
811831
"/root/folder1/file1_1.tsx",
812832
"/root/folder1/file1_1.d.ts",
833+
"/root/folder1/file1_1.json",
813834
// load from directory
814835
"/root/folder1/file1_1/package.json",
815836
"/root/folder1/file1_1/index.ts",
@@ -906,6 +927,7 @@ import b = require("./moduleB");
906927
"/root/src/libs/guid.ts",
907928
"/root/src/libs/guid.tsx",
908929
"/root/src/libs/guid.d.ts",
930+
"/root/src/libs/guid.json",
909931
]);
910932
}
911933
});

src/services/completions.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,22 @@
33
/* @internal */
44
namespace ts.Completions {
55
export function getCompletionsAtPosition(host: LanguageServiceHost, typeChecker: TypeChecker, log: (message: string) => void, compilerOptions: CompilerOptions, sourceFile: SourceFile, position: number): CompletionInfo {
6+
// export function getCompletionsAtPosition(_host: LanguageServiceHost, _typeChecker: TypeChecker, _log: (message: string) => void, _compilerOptions: CompilerOptions, _sourceFile: SourceFile, _position: number): CompletionInfo {
7+
8+
// return {
9+
// isGlobalCompletion: false,
10+
// isMemberCompletion: false,
11+
// isNewIdentifierLocation: false,
12+
// entries: [
13+
// {
14+
// name: "test5",
15+
// kind: ScriptElementKind.classElement,
16+
// kindModifiers: ScriptElementKindModifier.none,
17+
// sortText: "test"
18+
// }
19+
// ]
20+
// };
21+
622
if (isInReferenceComment(sourceFile, position)) {
723
return getTripleSlashReferenceCompletion(sourceFile, position);
824
}

tests/baselines/reference/importWithTrailingSlash_noResolve.trace.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@
66
"File '/foo/index.ts' does not exist.",
77
"File '/foo/index.tsx' does not exist.",
88
"File '/foo/index.d.ts' does not exist.",
9+
"File '/foo/index.json' does not exist.",
910
"======== Module name './foo/' was not resolved. ========"
1011
]

tests/baselines/reference/moduleResolutionWithExtensions.trace.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"File '/src/a.js.ts' does not exist.",
1212
"File '/src/a.js.tsx' does not exist.",
1313
"File '/src/a.js.d.ts' does not exist.",
14+
"File '/src/a.js.json' does not exist.",
1415
"File name '/src/a.js' has a '.js' extension - stripping it",
1516
"File '/src/a.ts' exist - use it as a name resolution result.",
1617
"Resolving real path for '/src/a.ts', result '/src/a.ts'",
@@ -21,6 +22,7 @@
2122
"File '/src/jquery.js.ts' does not exist.",
2223
"File '/src/jquery.js.tsx' does not exist.",
2324
"File '/src/jquery.js.d.ts' does not exist.",
25+
"File '/src/jquery.js.json' does not exist.",
2426
"File name '/src/jquery.js' has a '.js' extension - stripping it",
2527
"File '/src/jquery.ts' does not exist.",
2628
"File '/src/jquery.tsx' does not exist.",

0 commit comments

Comments
 (0)