Skip to content

Completion list for JSON files imported using CommonJS require #9221

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

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
24 changes: 15 additions & 9 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1342,8 +1342,8 @@ namespace ts {
return (symbol.flags & meaning) || dontResolveAlias ? symbol : resolveAlias(symbol);
}

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

function resolveExternalModuleNameWorker(location: Node, moduleReferenceExpression: Expression, moduleNotFoundError: DiagnosticMessage): Symbol {
Expand Down Expand Up @@ -4881,11 +4881,17 @@ namespace ts {
}

function resolveExternalModuleTypeByLiteral(name: StringLiteral) {
const moduleSym = resolveExternalModuleName(name, name);
const nameIsInJavaScriptFile = isInJavaScriptFile(name);
const moduleSym = resolveExternalModuleName(name, name, /*reportModuleNotFoundError*/ nameIsInJavaScriptFile);
if (moduleSym) {
const resolvedModuleSymbol = resolveExternalModuleSymbol(moduleSym);
if (resolvedModuleSymbol) {
return getTypeOfSymbol(resolvedModuleSymbol);
const sourceFile = getDeclarationOfKind(moduleSym, SyntaxKind.SourceFile) as SourceFile;
const resolvedFileIsJSON = sourceFile && getScriptKindFromFileName(sourceFile.fileName) === ScriptKind.JSON;
// Treated as external module import if it is in JavaScript file or when JSON is "required"
if (nameIsInJavaScriptFile || resolvedFileIsJSON) {
const resolvedModuleSymbol = resolveExternalModuleSymbol(moduleSym);
if (resolvedModuleSymbol) {
return getTypeOfSymbol(resolvedModuleSymbol);
}
}
}

Expand Down Expand Up @@ -12824,8 +12830,8 @@ namespace ts {
}
}

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

Expand Down Expand Up @@ -18793,7 +18799,7 @@ namespace ts {
(<ImportDeclaration>node.parent).moduleSpecifier === node)) {
return resolveExternalModuleName(node, <LiteralExpression>node);
}
if (isInJavaScriptFile(node) && isRequireCall(node.parent, /*checkArgumentIsStringLiteral*/ false)) {
if (isCommonJsRequire(node.parent)) {
return resolveExternalModuleName(node, <LiteralExpression>node);
}
// Fall through
Expand Down
3 changes: 3 additions & 0 deletions src/compiler/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1793,6 +1793,8 @@ namespace ts {
return ScriptKind.TS;
case ".tsx":
return ScriptKind.TSX;
case ".json":
return ScriptKind.JSON;
default:
return ScriptKind.Unknown;
}
Expand All @@ -1806,6 +1808,7 @@ namespace ts {
export const supportedTypescriptExtensionsForExtractExtension = [".d.ts", ".ts", ".tsx"];
export const supportedJavascriptExtensions = [".js", ".jsx"];
const allSupportedExtensions = supportedTypeScriptExtensions.concat(supportedJavascriptExtensions);
export const supportedTypeScriptAndJsonExtensions = supportedTypeScriptExtensions.concat([".json"]);

export function getSupportedExtensions(options?: CompilerOptions): string[] {
return options && options.allowJs ? allSupportedExtensions : supportedTypeScriptExtensions;
Expand Down
3 changes: 2 additions & 1 deletion src/compiler/moduleNameResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,8 @@ namespace ts {

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

const failedLookupLocations: string[] = [];
Expand Down
12 changes: 11 additions & 1 deletion src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -636,7 +636,17 @@ namespace ts {
nextToken();
processReferenceComments(sourceFile);

sourceFile.statements = parseList(ParsingContext.SourceElements, parseStatement);
if (scriptKind === ScriptKind.JSON) {
const exportAssignment = <ExportAssignment>createNode(SyntaxKind.ExportAssignment);
const json = parseObjectLiteralExpression();
exportAssignment.expression = json;
exportAssignment.isExportEquals = true;
finishNode(exportAssignment);
sourceFile.statements = <NodeArray<Statement>>[<Statement>exportAssignment];
}
else {
sourceFile.statements = parseList(ParsingContext.SourceElements, parseStatement);
}
Debug.assert(token() === SyntaxKind.EndOfFileToken);
sourceFile.endOfFileToken = <EndOfFileToken>parseTokenNode();

Expand Down
5 changes: 3 additions & 2 deletions src/compiler/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -583,7 +583,7 @@ namespace ts {
getNewLine: () => host.getNewLine(),
getSourceFile: program.getSourceFile,
getSourceFileByPath: program.getSourceFileByPath,
getSourceFiles: program.getSourceFiles,
getSourceFiles: () => filter(program.getSourceFiles(), sourceFile => sourceFile.scriptKind !== ScriptKind.JSON),
isSourceFileFromExternalLibrary: (file: SourceFile) => !!sourceFilesFoundSearchingNodeModules[file.path],
writeFile: writeFileCallback || (
(fileName, data, writeByteOrderMark, onError, sourceFiles) => host.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles)),
Expand Down Expand Up @@ -984,6 +984,7 @@ namespace ts {

const isJavaScriptFile = isSourceFileJavaScript(file);
const isExternalModuleFile = isExternalModule(file);
const isCommonJS = getEmitModuleKind(options) === ModuleKind.CommonJS;

let imports: LiteralExpression[];
let moduleAugmentations: LiteralExpression[];
Expand All @@ -1001,7 +1002,7 @@ namespace ts {

for (const node of file.statements) {
collectModuleReferences(node, /*inAmbientModule*/ false);
if (isJavaScriptFile) {
if (isJavaScriptFile || isCommonJS) {
collectRequireCalls(node);
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3077,7 +3077,8 @@ namespace ts {
JS = 1,
JSX = 2,
TS = 3,
TSX = 4
TSX = 4,
JSON = 5
}

export const enum ScriptTarget {
Expand Down
28 changes: 25 additions & 3 deletions src/harness/unittests/moduleResolution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ namespace ts {
assert.equal(resolution.resolvedModule.resolvedFileName, moduleFile.name);
assert.equal(!!resolution.resolvedModule.isExternalLibraryImport, false);
// expect three failed lookup location - attempt to load module as file with all supported extensions
assert.equal(resolution.failedLookupLocations.length, supportedTypeScriptExtensions.length);
assert.equal(resolution.failedLookupLocations.length, supportedTypeScriptAndJsonExtensions.length);
}
}

Expand Down Expand Up @@ -154,6 +154,7 @@ namespace ts {
"/a/b/foo.ts",
"/a/b/foo.tsx",
"/a/b/foo.d.ts",
"/a/b/foo.json",
"/a/b/foo/index.ts",
"/a/b/foo/index.tsx",
]);
Expand Down Expand Up @@ -589,25 +590,30 @@ import b = require("./moduleB");
"/root/folder1/file2.ts",
"/root/folder1/file2.tsx",
"/root/folder1/file2.d.ts",
"/root/folder1/file2.json",
"/root/folder1/file2/package.json",
"/root/folder1/file2/index.ts",
"/root/folder1/file2/index.tsx",
"/root/folder1/file2/index.d.ts"
"/root/folder1/file2/index.d.ts",
"/root/folder1/file2/index.json"
// then first attempt on 'generated/*' was successful
]);
check("folder2/file3", file3, [
// first try '*'
"/root/folder2/file3.ts",
"/root/folder2/file3.tsx",
"/root/folder2/file3.d.ts",
"/root/folder2/file3.json",
"/root/folder2/file3/package.json",
"/root/folder2/file3/index.ts",
"/root/folder2/file3/index.tsx",
"/root/folder2/file3/index.d.ts",
"/root/folder2/file3/index.json",
// then use remapped location
"/root/generated/folder2/file3.ts",
"/root/generated/folder2/file3.tsx",
"/root/generated/folder2/file3.d.ts",
"/root/generated/folder2/file3.json",
"/root/generated/folder2/file3/package.json",
"/root/generated/folder2/file3/index.ts",
"/root/generated/folder2/file3/index.tsx",
Expand All @@ -618,14 +624,17 @@ import b = require("./moduleB");
"/root/folder2/file4.ts",
"/root/folder2/file4.tsx",
"/root/folder2/file4.d.ts",
"/root/folder2/file4.json",
"/root/folder2/file4/package.json",
"/root/folder2/file4/index.ts",
"/root/folder2/file4/index.tsx",
"/root/folder2/file4/index.d.ts",
"/root/folder2/file4/index.json",
// try to load from file from remapped location
"/root/generated/folder2/file4.ts",
"/root/generated/folder2/file4.tsx",
"/root/generated/folder2/file4.d.ts"
"/root/generated/folder2/file4.d.ts",
"/root/generated/folder2/file4.json"
// success on loading as from folder
]);
check("somefolder/file5", file5, [
Expand All @@ -634,6 +643,7 @@ import b = require("./moduleB");
"/root/someanotherfolder/file5.ts",
"/root/someanotherfolder/file5.tsx",
"/root/someanotherfolder/file5.d.ts",
"/root/someanotherfolder/file5.json",
// load from folder
"/root/someanotherfolder/file5/package.json",
"/root/someanotherfolder/file5/index.ts",
Expand All @@ -646,21 +656,25 @@ import b = require("./moduleB");
"/root/file6.ts",
"/root/file6.tsx",
"/root/file6.d.ts",
"/root/file6.json",
// load from folder
"/root/file6/package.json",
"/root/file6/index.ts",
"/root/file6/index.tsx",
"/root/file6/index.d.ts",
"/root/file6/index.json",
// then try 'generated/*'
// load from file
"/root/generated/file6.ts",
"/root/generated/file6.tsx",
"/root/generated/file6.d.ts",
"/root/generated/file6.json",
// load from folder
"/root/generated/file6/package.json",
"/root/generated/file6/index.ts",
"/root/generated/file6/index.tsx",
"/root/generated/file6/index.d.ts",
"/root/generated/file6/index.json",
// fallback to standard node behavior
// load from file
"/root/folder1/node_modules/file6.ts",
Expand Down Expand Up @@ -774,11 +788,13 @@ import b = require("./moduleB");
"/root/folder1/file2.ts",
"/root/folder1/file2.tsx",
"/root/folder1/file2.d.ts",
"/root/folder1/file2.json",
// load from folder
"/root/folder1/file2/package.json",
"/root/folder1/file2/index.ts",
"/root/folder1/file2/index.tsx",
"/root/folder1/file2/index.d.ts",
"/root/folder1/file2/index.json",
// success after using alternative rootDir entry
]);
check("../folder1/file1", file3, file1, [
Expand All @@ -787,11 +803,13 @@ import b = require("./moduleB");
"/root/generated/folder1/file1.ts",
"/root/generated/folder1/file1.tsx",
"/root/generated/folder1/file1.d.ts",
"/root/generated/folder1/file1.json",
// load from module
"/root/generated/folder1/file1/package.json",
"/root/generated/folder1/file1/index.ts",
"/root/generated/folder1/file1/index.tsx",
"/root/generated/folder1/file1/index.d.ts",
"/root/generated/folder1/file1/index.json",
// success after using alternative rootDir entry
]);
check("../folder1/file1_1", file3, file1_1, [
Expand All @@ -800,16 +818,19 @@ import b = require("./moduleB");
"/root/generated/folder1/file1_1.ts",
"/root/generated/folder1/file1_1.tsx",
"/root/generated/folder1/file1_1.d.ts",
"/root/generated/folder1/file1_1.json",
// load from folder
"/root/generated/folder1/file1_1/package.json",
"/root/generated/folder1/file1_1/index.ts",
"/root/generated/folder1/file1_1/index.tsx",
"/root/generated/folder1/file1_1/index.d.ts",
"/root/generated/folder1/file1_1/index.json",
// try alternative rootDir entry
// load from file
"/root/folder1/file1_1.ts",
"/root/folder1/file1_1.tsx",
"/root/folder1/file1_1.d.ts",
"/root/folder1/file1_1.json",
// load from directory
"/root/folder1/file1_1/package.json",
"/root/folder1/file1_1/index.ts",
Expand Down Expand Up @@ -906,6 +927,7 @@ import b = require("./moduleB");
"/root/src/libs/guid.ts",
"/root/src/libs/guid.tsx",
"/root/src/libs/guid.d.ts",
"/root/src/libs/guid.json",
]);
}
});
Expand Down
16 changes: 16 additions & 0 deletions src/services/completions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,22 @@
/* @internal */
namespace ts.Completions {
export function getCompletionsAtPosition(host: LanguageServiceHost, typeChecker: TypeChecker, log: (message: string) => void, compilerOptions: CompilerOptions, sourceFile: SourceFile, position: number): CompletionInfo {
// export function getCompletionsAtPosition(_host: LanguageServiceHost, _typeChecker: TypeChecker, _log: (message: string) => void, _compilerOptions: CompilerOptions, _sourceFile: SourceFile, _position: number): CompletionInfo {

// return {
// isGlobalCompletion: false,
// isMemberCompletion: false,
// isNewIdentifierLocation: false,
// entries: [
// {
// name: "test5",
// kind: ScriptElementKind.classElement,
// kindModifiers: ScriptElementKindModifier.none,
// sortText: "test"
// }
// ]
// };

if (isInReferenceComment(sourceFile, position)) {
return getTripleSlashReferenceCompletion(sourceFile, position);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@
"File '/foo/index.ts' does not exist.",
"File '/foo/index.tsx' does not exist.",
"File '/foo/index.d.ts' does not exist.",
"File '/foo/index.json' does not exist.",
"======== Module name './foo/' was not resolved. ========"
]
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"File '/src/a.js.ts' does not exist.",
"File '/src/a.js.tsx' does not exist.",
"File '/src/a.js.d.ts' does not exist.",
"File '/src/a.js.json' does not exist.",
"File name '/src/a.js' has a '.js' extension - stripping it",
"File '/src/a.ts' exist - use it as a name resolution result.",
"Resolving real path for '/src/a.ts', result '/src/a.ts'",
Expand All @@ -21,6 +22,7 @@
"File '/src/jquery.js.ts' does not exist.",
"File '/src/jquery.js.tsx' does not exist.",
"File '/src/jquery.js.d.ts' does not exist.",
"File '/src/jquery.js.json' does not exist.",
"File name '/src/jquery.js' has a '.js' extension - stripping it",
"File '/src/jquery.ts' does not exist.",
"File '/src/jquery.tsx' does not exist.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"File '/src/library-a.ts' does not exist.",
"File '/src/library-a.tsx' does not exist.",
"File '/src/library-a.d.ts' does not exist.",
"File '/src/library-a.json' does not exist.",
"File '/src/library-a/package.json' does not exist.",
"File '/src/library-a/index.ts' exist - use it as a name resolution result.",
"Resolving real path for '/src/library-a/index.ts', result '/src/library-a/index.ts'",
Expand All @@ -15,6 +16,7 @@
"File '/src/library-b.ts' does not exist.",
"File '/src/library-b.tsx' does not exist.",
"File '/src/library-b.d.ts' does not exist.",
"File '/src/library-b.json' does not exist.",
"File '/src/library-b/package.json' does not exist.",
"File '/src/library-b/index.ts' exist - use it as a name resolution result.",
"Resolving real path for '/src/library-b/index.ts', result '/src/library-b/index.ts'",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@
"File 'c:/root/file4.ts' does not exist.",
"File 'c:/root/file4.tsx' does not exist.",
"File 'c:/root/file4.d.ts' does not exist.",
"File 'c:/root/file4.json' does not exist.",
"File 'c:/root/file4/package.json' does not exist.",
"File 'c:/root/file4/index.ts' does not exist.",
"File 'c:/root/file4/index.tsx' does not exist.",
"File 'c:/root/file4/index.d.ts' does not exist.",
"File 'c:/root/file4/index.json' does not exist.",
"Loading module 'file4' from 'node_modules' folder.",
"File 'c:/root/folder2/node_modules/file4.ts' does not exist.",
"File 'c:/root/folder2/node_modules/file4.tsx' does not exist.",
Expand Down
Loading