diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 711cbcb9e37..902353aded6 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -12825,16 +12825,41 @@ namespace ts { } // In JavaScript files, calls to any identifier 'require' are treated as external module imports - if (isInJavaScriptFile(node) && - isRequireCall(node, /*checkArgumentIsStringLiteral*/true) && - // Make sure require is not a local function - !resolveName(node.expression, (node.expression).text, SymbolFlags.Value, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined)) { + if (isInJavaScriptFile(node) && isCommonJsRequire(node)) { return resolveExternalModuleTypeByLiteral(node.arguments[0]); } return getReturnTypeOfSignature(signature); } + function isCommonJsRequire(node: Node) { + if (!isRequireCall(node, /*checkArgumentIsStringLiteral*/true)) { + return false; + } + // Make sure require is not a local function + const resolvedRequire = resolveName(node.expression, (node.expression).text, SymbolFlags.Value, /*nameNotFoundMessage*/ undefined, /*nameArg*/ undefined); + if (!resolvedRequire) { + // project does not contain symbol named 'require' - assume commonjs require + return true; + } + // project includes symbol named 'require' - make sure that it it ambient and local non-alias + if (resolvedRequire.flags & SymbolFlags.Alias) { + return false; + } + + const targetDeclarationKind = resolvedRequire.flags & SymbolFlags.Function + ? SyntaxKind.FunctionDeclaration + : resolvedRequire.flags & SymbolFlags.Variable + ? SyntaxKind.VariableDeclaration + : SyntaxKind.Unknown; + if (targetDeclarationKind !== SyntaxKind.Unknown) { + const decl = getDeclarationOfKind(resolvedRequire, targetDeclarationKind); + // function/variable declaration should be ambient + return isInAmbientContext(decl); + } + return false; + } + function checkTaggedTemplateExpression(node: TaggedTemplateExpression): Type { return getReturnTypeOfSignature(getResolvedSignature(node)); } diff --git a/tests/baselines/reference/ambientRequireFunction.js b/tests/baselines/reference/ambientRequireFunction.js new file mode 100644 index 00000000000..cd00b287c82 --- /dev/null +++ b/tests/baselines/reference/ambientRequireFunction.js @@ -0,0 +1,21 @@ +//// [tests/cases/compiler/ambientRequireFunction.ts] //// + +//// [node.d.ts] + + +declare function require(moduleName: string): any; + +declare module "fs" { + export function readFileSync(s: string): string; +} + +//// [app.js] +/// + +const fs = require("fs"); +const text = fs.readFileSync("/a/b/c"); + +//// [app.js] +/// +var fs = require("fs"); +var text = fs.readFileSync("/a/b/c"); diff --git a/tests/baselines/reference/ambientRequireFunction.symbols b/tests/baselines/reference/ambientRequireFunction.symbols new file mode 100644 index 00000000000..6afb47e37fb --- /dev/null +++ b/tests/baselines/reference/ambientRequireFunction.symbols @@ -0,0 +1,27 @@ +=== tests/cases/compiler/app.js === +/// + +const fs = require("fs"); +>fs : Symbol(fs, Decl(app.js, 2, 5)) +>require : Symbol(require, Decl(node.d.ts, 0, 0)) +>"fs" : Symbol("fs", Decl(node.d.ts, 2, 50)) + +const text = fs.readFileSync("/a/b/c"); +>text : Symbol(text, Decl(app.js, 3, 5)) +>fs.readFileSync : Symbol(readFileSync, Decl(node.d.ts, 4, 21)) +>fs : Symbol(fs, Decl(app.js, 2, 5)) +>readFileSync : Symbol(readFileSync, Decl(node.d.ts, 4, 21)) + +=== tests/cases/compiler/node.d.ts === + + +declare function require(moduleName: string): any; +>require : Symbol(require, Decl(node.d.ts, 0, 0)) +>moduleName : Symbol(moduleName, Decl(node.d.ts, 2, 25)) + +declare module "fs" { + export function readFileSync(s: string): string; +>readFileSync : Symbol(readFileSync, Decl(node.d.ts, 4, 21)) +>s : Symbol(s, Decl(node.d.ts, 5, 33)) +} + diff --git a/tests/baselines/reference/ambientRequireFunction.types b/tests/baselines/reference/ambientRequireFunction.types new file mode 100644 index 00000000000..7b01a59268f --- /dev/null +++ b/tests/baselines/reference/ambientRequireFunction.types @@ -0,0 +1,30 @@ +=== tests/cases/compiler/app.js === +/// + +const fs = require("fs"); +>fs : typeof "fs" +>require("fs") : typeof "fs" +>require : (moduleName: string) => any +>"fs" : "fs" + +const text = fs.readFileSync("/a/b/c"); +>text : string +>fs.readFileSync("/a/b/c") : string +>fs.readFileSync : (s: string) => string +>fs : typeof "fs" +>readFileSync : (s: string) => string +>"/a/b/c" : "/a/b/c" + +=== tests/cases/compiler/node.d.ts === + + +declare function require(moduleName: string): any; +>require : (moduleName: string) => any +>moduleName : string + +declare module "fs" { + export function readFileSync(s: string): string; +>readFileSync : (s: string) => string +>s : string +} + diff --git a/tests/baselines/reference/localRequireFunction.js b/tests/baselines/reference/localRequireFunction.js new file mode 100644 index 00000000000..1ae20821a07 --- /dev/null +++ b/tests/baselines/reference/localRequireFunction.js @@ -0,0 +1,15 @@ +//// [app.js] + +function require(a) { + return a; +} + +const fs = require("fs"); +const text = fs.readFileSync("/a/b/c"); + +//// [app.js] +function require(a) { + return a; +} +var fs = require("fs"); +var text = fs.readFileSync("/a/b/c"); diff --git a/tests/baselines/reference/localRequireFunction.symbols b/tests/baselines/reference/localRequireFunction.symbols new file mode 100644 index 00000000000..b977328f0ef --- /dev/null +++ b/tests/baselines/reference/localRequireFunction.symbols @@ -0,0 +1,18 @@ +=== tests/cases/compiler/app.js === + +function require(a) { +>require : Symbol(require, Decl(app.js, 0, 0)) +>a : Symbol(a, Decl(app.js, 1, 17)) + + return a; +>a : Symbol(a, Decl(app.js, 1, 17)) +} + +const fs = require("fs"); +>fs : Symbol(fs, Decl(app.js, 5, 5)) +>require : Symbol(require, Decl(app.js, 0, 0)) + +const text = fs.readFileSync("/a/b/c"); +>text : Symbol(text, Decl(app.js, 6, 5)) +>fs : Symbol(fs, Decl(app.js, 5, 5)) + diff --git a/tests/baselines/reference/localRequireFunction.types b/tests/baselines/reference/localRequireFunction.types new file mode 100644 index 00000000000..4b5c7578cd9 --- /dev/null +++ b/tests/baselines/reference/localRequireFunction.types @@ -0,0 +1,24 @@ +=== tests/cases/compiler/app.js === + +function require(a) { +>require : (a: any) => any +>a : any + + return a; +>a : any +} + +const fs = require("fs"); +>fs : any +>require("fs") : any +>require : (a: any) => any +>"fs" : "fs" + +const text = fs.readFileSync("/a/b/c"); +>text : any +>fs.readFileSync("/a/b/c") : any +>fs.readFileSync : any +>fs : any +>readFileSync : any +>"/a/b/c" : "/a/b/c" + diff --git a/tests/cases/compiler/ambientRequireFunction.ts b/tests/cases/compiler/ambientRequireFunction.ts new file mode 100644 index 00000000000..ce9019e00bc --- /dev/null +++ b/tests/cases/compiler/ambientRequireFunction.ts @@ -0,0 +1,17 @@ +// @module: commonjs +// @allowJs: true +// @outDir: ./out/ + +// @filename: node.d.ts + +declare function require(moduleName: string): any; + +declare module "fs" { + export function readFileSync(s: string): string; +} + +// @filename: app.js +/// + +const fs = require("fs"); +const text = fs.readFileSync("/a/b/c"); \ No newline at end of file diff --git a/tests/cases/compiler/localRequireFunction.ts b/tests/cases/compiler/localRequireFunction.ts new file mode 100644 index 00000000000..c8f3c2452ff --- /dev/null +++ b/tests/cases/compiler/localRequireFunction.ts @@ -0,0 +1,11 @@ +// @module: commonjs +// @allowJs: true +// @outDir: ./out/ + +// @filename: app.js +function require(a) { + return a; +} + +const fs = require("fs"); +const text = fs.readFileSync("/a/b/c"); \ No newline at end of file