diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 369d61ce484b7..5aba1b4600667 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -12819,16 +12819,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 0000000000000..cd00b287c829a --- /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 0000000000000..6afb47e37fb0a --- /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 0000000000000..7b01a59268fc3 --- /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 0000000000000..1ae20821a0741 --- /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 0000000000000..b977328f0efb2 --- /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 0000000000000..4b5c7578cd976 --- /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 0000000000000..ce9019e00bcf7 --- /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 0000000000000..c8f3c2452ff35 --- /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 diff --git a/tests/webTestServer.ts b/tests/webTestServer.ts index 97027dfdd4913..3d23ef3e961a4 100644 --- a/tests/webTestServer.ts +++ b/tests/webTestServer.ts @@ -128,7 +128,7 @@ function dir(dirPath: string, spec?: string, options?: any) { // fs.rmdirSync won't delete directories with files in it function deleteFolderRecursive(dirPath: string) { if (fs.existsSync(dirPath)) { - fs.readdirSync(dirPath).forEach((file, index) => { + fs.readdirSync(dirPath).forEach((file) => { const curPath = path.join(path, file); if (fs.statSync(curPath).isDirectory()) { // recurse deleteFolderRecursive(curPath); @@ -141,7 +141,7 @@ function deleteFolderRecursive(dirPath: string) { } }; -function writeFile(path: string, data: any, opts: { recursive: boolean }) { +function writeFile(path: string, data: any) { ensureDirectoriesExist(getDirectoryPath(path)); fs.writeFileSync(path, data); } @@ -208,7 +208,7 @@ enum RequestType { Unknown } -function getRequestOperation(req: http.ServerRequest, filename: string) { +function getRequestOperation(req: http.ServerRequest) { if (req.method === "GET" && req.url.indexOf("?") === -1) { if (req.url.indexOf(".") !== -1) return RequestType.GetFile; else return RequestType.GetDir; @@ -258,7 +258,7 @@ function handleRequestOperation(req: http.ServerRequest, res: http.ServerRespons break; case RequestType.WriteFile: processPost(req, res, (data) => { - writeFile(reqPath, data, { recursive: true }); + writeFile(reqPath, data); }); send(ResponseCode.Success, res, undefined); break; @@ -306,7 +306,7 @@ http.createServer((req: http.ServerRequest, res: http.ServerResponse) => { log(`${req.method} ${req.url}`); const uri = url.parse(req.url).pathname; const reqPath = path.join(process.cwd(), uri); - const operation = getRequestOperation(req, reqPath); + const operation = getRequestOperation(req); handleRequestOperation(req, res, operation, reqPath); }).listen(port);