Skip to content
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

Alias for commonjs require in JS #39770

Merged
merged 39 commits into from
Aug 17, 2020
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
4f9ccd0
First attempt at aliases for require
sandersn Jul 9, 2020
d692387
Merge branch 'master' into alias-for-require
sandersn Jul 9, 2020
277d52a
test+initial support for const x=require
sandersn Jul 9, 2020
bcdc2e4
1st round of baseline improvements
sandersn Jul 9, 2020
20aa6ed
2nd round of baseline updates
sandersn Jul 9, 2020
f213707
support property access after require
sandersn Jul 10, 2020
122babd
check @type tag on require
sandersn Jul 10, 2020
f32ec4e
Merge branch 'master' into alias-for-require
sandersn Jul 10, 2020
e1e32af
forbid expando missing namespaces on aliases
sandersn Jul 10, 2020
4ddb3d0
accept error baselines that are good, actually
sandersn Jul 10, 2020
58a0b65
Scribbling on d.ts emit code
sandersn Jul 15, 2020
d885b67
use getSpecifierForModuleSymbol
sandersn Jul 15, 2020
5a0b092
hideous hack for module.exports of aliases
sandersn Jul 15, 2020
64c8c86
Merge branch 'master' into alias-for-require
sandersn Jul 23, 2020
55a13b7
Fix module.exports.x --> export list emit
sandersn Jul 23, 2020
7ce7c8c
fix isLocalImport predicate
sandersn Jul 24, 2020
ca5a57b
require only creates aliases in JS
sandersn Jul 24, 2020
e9a355d
re-handle json imports
sandersn Jul 24, 2020
febcf0e
update fourslash baseline
sandersn Jul 24, 2020
3ee9b6f
Cleanup in the checker
sandersn Jul 24, 2020
a671b45
Function for getting module name from require call
sandersn Jul 24, 2020
6dfbec3
Merge branch 'master' into alias-for-require
sandersn Jul 24, 2020
0c92277
First round of cleanup plus a new test
sandersn Jul 27, 2020
dee0561
more small cleanup
sandersn Jul 27, 2020
e32df83
more cleanup, including lint
sandersn Jul 27, 2020
f4b5124
use trackSymbol, not serializeTypeForDeclaration
sandersn Jul 27, 2020
a841988
Merge branch 'master' into alias-for-require
sandersn Jul 28, 2020
0694890
Merge branch 'master' into alias-for-require
sandersn Aug 11, 2020
781bec2
Code review comments, plus remove unneeded code
sandersn Aug 11, 2020
2bfeeeb
find all refs works
sandersn Aug 12, 2020
0ba0d90
Merge branch 'master' into alias-for-require
sandersn Aug 12, 2020
62e5b18
remove old ad-hoc code
sandersn Aug 13, 2020
2679b03
make it clear that old behaviour is not that correct
sandersn Aug 13, 2020
03fb2d4
update api baselines
sandersn Aug 13, 2020
7aeb306
remove outdated comment
sandersn Aug 13, 2020
d2c0b3c
Merge branch 'master' into alias-for-require
sandersn Aug 13, 2020
9d2ce87
Merge branch 'master' into alias-for-require
sandersn Aug 17, 2020
283a520
PR feedback
sandersn Aug 17, 2020
6c7897e
add a fail-case test (which passes!)
sandersn Aug 17, 2020
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
5 changes: 4 additions & 1 deletion src/compiler/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3185,7 +3185,10 @@ namespace ts {
}

if (!isBindingPattern(node.name)) {
if (isBlockOrCatchScoped(node)) {
if (isInJSFile(node) && isRequireVariableDeclaration(node, /*requireStringLiteralLikeArgument*/ true) && !getJSDocTypeTag(node)) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note that once this works well enough, we could turn it on in TS as well, which would be pretty great.

declareSymbolAndAddToSymbolTable(node as Declaration, SymbolFlags.Alias, SymbolFlags.AliasExcludes);
}
else if (isBlockOrCatchScoped(node)) {
bindBlockScopedDeclaration(node, SymbolFlags.BlockScopedVariable, SymbolFlags.BlockScopedVariableExcludes);
}
else if (isParameterDeclaration(node)) {
Expand Down
195 changes: 128 additions & 67 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

15 changes: 14 additions & 1 deletion src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1847,6 +1847,11 @@ namespace ts {
return (<ExternalModuleReference>(<ImportEqualsDeclaration>node).moduleReference).expression;
}

export function getExternalModuleRequireArgument(node: Node) {
return isRequireVariableDeclaration(node, /*requireStringLiteralLikeArgument*/ true)
&& (getFirstPropertyAccessExpression(node.initializer) as CallExpression).arguments[0] as StringLiteral;
}

export function isInternalModuleImportEqualsDeclaration(node: Node): node is ImportEqualsDeclaration {
return node.kind === SyntaxKind.ImportEqualsDeclaration && (<ImportEqualsDeclaration>node).moduleReference.kind !== SyntaxKind.ExternalModuleReference;
}
Expand Down Expand Up @@ -1914,7 +1919,8 @@ namespace ts {
export function isRequireVariableDeclaration(node: Node, requireStringLiteralLikeArgument: true): node is RequireVariableDeclaration;
export function isRequireVariableDeclaration(node: Node, requireStringLiteralLikeArgument: boolean): node is VariableDeclaration;
export function isRequireVariableDeclaration(node: Node, requireStringLiteralLikeArgument: boolean): node is VariableDeclaration {
return isVariableDeclaration(node) && !!node.initializer && isRequireCall(node.initializer, requireStringLiteralLikeArgument);
node = getRootDeclaration(node);
return isVariableDeclaration(node) && !!node.initializer && isRequireCall(getFirstPropertyAccessExpression(node.initializer), requireStringLiteralLikeArgument);
}

export function isRequireVariableStatement(node: Node, requireStringLiteralLikeArgument = true): node is RequireVariableStatement {
Expand Down Expand Up @@ -4726,6 +4732,13 @@ namespace ts {
}
}

export function getFirstPropertyAccessExpression(expr: Expression): Expression {
sandersn marked this conversation as resolved.
Show resolved Hide resolved
while (isPropertyAccessExpression(expr)) {
expr = expr.expression;
}
return expr;
}

export function isDottedName(node: Expression): boolean {
return node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.ThisKeyword || node.kind === SyntaxKind.SuperKeyword ||
node.kind === SyntaxKind.PropertyAccessExpression && isDottedName((<PropertyAccessExpression>node).expression) ||
Expand Down
6 changes: 3 additions & 3 deletions tests/baselines/reference/ambientRequireFunction.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
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, 0, 50))
>"fs" : Symbol(fs, Decl(node.d.ts, 0, 50))

const text = fs.readFileSync("/a/b/c");
>text : Symbol(text, Decl(app.js, 3, 5))
>fs.readFileSync : Symbol(readFileSync, Decl(node.d.ts, 2, 21))
>fs.readFileSync : Symbol(fs.readFileSync, Decl(node.d.ts, 2, 21))
>fs : Symbol(fs, Decl(app.js, 2, 5))
>readFileSync : Symbol(readFileSync, Decl(node.d.ts, 2, 21))
>readFileSync : Symbol(fs.readFileSync, Decl(node.d.ts, 2, 21))

=== tests/cases/compiler/node.d.ts ===
declare function require(moduleName: string): any;
Expand Down
6 changes: 3 additions & 3 deletions tests/baselines/reference/ambientRequireFunction.types
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@
/// <reference path="node.d.ts"/>

const fs = require("fs");
>fs : typeof import("fs")
>require("fs") : typeof import("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 import("fs")
>fs : typeof fs
>readFileSync : (s: string) => string
>"/a/b/c" : "/a/b/c"

Expand Down
10 changes: 5 additions & 5 deletions tests/baselines/reference/chainedPrototypeAssignment.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,19 @@
var mod = require('./mod');
>mod : Symbol(mod, Decl(use.js, 1, 3))
>require : Symbol(require, Decl(types.d.ts, 0, 0))
>'./mod' : Symbol("tests/cases/conformance/salsa/mod", Decl(mod.js, 0, 0))
>'./mod' : Symbol(mod, Decl(mod.js, 0, 0))

var a = new mod.A()
>a : Symbol(a, Decl(use.js, 2, 3))
>mod.A : Symbol(A, Decl(mod.js, 6, 1))
>mod.A : Symbol(mod.A, Decl(mod.js, 6, 1))
>mod : Symbol(mod, Decl(use.js, 1, 3))
>A : Symbol(A, Decl(mod.js, 6, 1))
>A : Symbol(mod.A, Decl(mod.js, 6, 1))

var b = new mod.B()
>b : Symbol(b, Decl(use.js, 3, 3))
>mod.B : Symbol(B, Decl(mod.js, 7, 13))
>mod.B : Symbol(mod.B, Decl(mod.js, 7, 13))
>mod : Symbol(mod, Decl(use.js, 1, 3))
>B : Symbol(B, Decl(mod.js, 7, 13))
>B : Symbol(mod.B, Decl(mod.js, 7, 13))

a.m('nope')
>a.m : Symbol(m, Decl(mod.js, 9, 29))
Expand Down
8 changes: 4 additions & 4 deletions tests/baselines/reference/chainedPrototypeAssignment.types
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
=== tests/cases/conformance/salsa/use.js ===
/// <reference path='./types.d.ts'/>
var mod = require('./mod');
>mod : typeof import("tests/cases/conformance/salsa/mod")
>require('./mod') : typeof import("tests/cases/conformance/salsa/mod")
>mod : typeof mod
>require('./mod') : typeof mod
>require : (name: string) => any
>'./mod' : "./mod"

var a = new mod.A()
>a : A
>new mod.A() : A
>mod.A : typeof A
>mod : typeof import("tests/cases/conformance/salsa/mod")
>mod : typeof mod
>A : typeof A

var b = new mod.B()
>b : B
>new mod.B() : B
>mod.B : typeof B
>mod : typeof import("tests/cases/conformance/salsa/mod")
>mod : typeof mod
>B : typeof B

a.m('nope')
Expand Down
60 changes: 30 additions & 30 deletions tests/baselines/reference/checkOtherObjectAssignProperty.symbols
Original file line number Diff line number Diff line change
@@ -1,61 +1,61 @@
=== tests/cases/conformance/jsdoc/importer.js ===
const mod = require("./mod1");
>mod : Symbol(mod, Decl(importer.js, 0, 5), Decl(importer.js, 6, 9), Decl(importer.js, 9, 14), Decl(importer.js, 10, 14), Decl(importer.js, 11, 13) ... and 2 more)
>mod : Symbol(mod, Decl(importer.js, 0, 5))
>require : Symbol(require)
>"./mod1" : Symbol("tests/cases/conformance/jsdoc/mod1", Decl(mod1.js, 0, 0))
>"./mod1" : Symbol(mod, Decl(mod1.js, 0, 0))

mod.thing;
>mod.thing : Symbol(thing, Decl(mod1.js, 0, 42))
>mod : Symbol(mod, Decl(importer.js, 0, 5), Decl(importer.js, 6, 9), Decl(importer.js, 9, 14), Decl(importer.js, 10, 14), Decl(importer.js, 11, 13) ... and 2 more)
>thing : Symbol(thing, Decl(mod1.js, 0, 42))
>mod.thing : Symbol(mod.thing, Decl(mod1.js, 0, 42))
>mod : Symbol(mod, Decl(importer.js, 0, 5))
>thing : Symbol(mod.thing, Decl(mod1.js, 0, 42))

mod.other;
>mod : Symbol(mod, Decl(importer.js, 0, 5), Decl(importer.js, 6, 9), Decl(importer.js, 9, 14), Decl(importer.js, 10, 14), Decl(importer.js, 11, 13) ... and 2 more)
>mod : Symbol(mod, Decl(importer.js, 0, 5))

mod.prop;
>mod : Symbol(mod, Decl(importer.js, 0, 5), Decl(importer.js, 6, 9), Decl(importer.js, 9, 14), Decl(importer.js, 10, 14), Decl(importer.js, 11, 13) ... and 2 more)
>mod : Symbol(mod, Decl(importer.js, 0, 5))

mod.bad1;
>mod.bad1 : Symbol(bad1, Decl(mod1.js, 10, 72))
>mod : Symbol(mod, Decl(importer.js, 0, 5), Decl(importer.js, 6, 9), Decl(importer.js, 9, 14), Decl(importer.js, 10, 14), Decl(importer.js, 11, 13) ... and 2 more)
>bad1 : Symbol(bad1, Decl(mod1.js, 10, 72))
>mod.bad1 : Symbol(mod.bad1, Decl(mod1.js, 10, 72))
>mod : Symbol(mod, Decl(importer.js, 0, 5))
>bad1 : Symbol(mod.bad1, Decl(mod1.js, 10, 72))

mod.bad2;
>mod.bad2 : Symbol(bad2, Decl(mod1.js, 13, 44))
>mod : Symbol(mod, Decl(importer.js, 0, 5), Decl(importer.js, 6, 9), Decl(importer.js, 9, 14), Decl(importer.js, 10, 14), Decl(importer.js, 11, 13) ... and 2 more)
>bad2 : Symbol(bad2, Decl(mod1.js, 13, 44))
>mod.bad2 : Symbol(mod.bad2, Decl(mod1.js, 13, 44))
>mod : Symbol(mod, Decl(importer.js, 0, 5))
>bad2 : Symbol(mod.bad2, Decl(mod1.js, 13, 44))

mod.bad3;
>mod.bad3 : Symbol(bad3, Decl(mod1.js, 14, 77))
>mod : Symbol(mod, Decl(importer.js, 0, 5), Decl(importer.js, 6, 9), Decl(importer.js, 9, 14), Decl(importer.js, 10, 14), Decl(importer.js, 11, 13) ... and 2 more)
>bad3 : Symbol(bad3, Decl(mod1.js, 14, 77))
>mod.bad3 : Symbol(mod.bad3, Decl(mod1.js, 14, 77))
>mod : Symbol(mod, Decl(importer.js, 0, 5))
>bad3 : Symbol(mod.bad3, Decl(mod1.js, 14, 77))


mod.thing = 0;
>mod.thing : Symbol(thing, Decl(mod1.js, 0, 42))
>mod : Symbol(mod, Decl(importer.js, 0, 5), Decl(importer.js, 6, 9), Decl(importer.js, 9, 14), Decl(importer.js, 10, 14), Decl(importer.js, 11, 13) ... and 2 more)
>thing : Symbol(thing, Decl(mod1.js, 0, 42))
>mod.thing : Symbol(mod.thing, Decl(mod1.js, 0, 42))
>mod : Symbol(mod, Decl(importer.js, 0, 5))
>thing : Symbol(mod.thing, Decl(mod1.js, 0, 42))

mod.other = 0;
>mod : Symbol(mod, Decl(importer.js, 0, 5), Decl(importer.js, 6, 9), Decl(importer.js, 9, 14), Decl(importer.js, 10, 14), Decl(importer.js, 11, 13) ... and 2 more)
>mod : Symbol(mod, Decl(importer.js, 0, 5))

mod.prop = 0;
>mod : Symbol(mod, Decl(importer.js, 0, 5), Decl(importer.js, 6, 9), Decl(importer.js, 9, 14), Decl(importer.js, 10, 14), Decl(importer.js, 11, 13) ... and 2 more)
>mod : Symbol(mod, Decl(importer.js, 0, 5))

mod.bad1 = 0;
>mod.bad1 : Symbol(bad1, Decl(mod1.js, 10, 72))
>mod : Symbol(mod, Decl(importer.js, 0, 5), Decl(importer.js, 6, 9), Decl(importer.js, 9, 14), Decl(importer.js, 10, 14), Decl(importer.js, 11, 13) ... and 2 more)
>bad1 : Symbol(bad1, Decl(mod1.js, 10, 72))
>mod.bad1 : Symbol(mod.bad1, Decl(mod1.js, 10, 72))
>mod : Symbol(mod, Decl(importer.js, 0, 5))
>bad1 : Symbol(mod.bad1, Decl(mod1.js, 10, 72))

mod.bad2 = 0;
>mod.bad2 : Symbol(bad2, Decl(mod1.js, 13, 44))
>mod : Symbol(mod, Decl(importer.js, 0, 5), Decl(importer.js, 6, 9), Decl(importer.js, 9, 14), Decl(importer.js, 10, 14), Decl(importer.js, 11, 13) ... and 2 more)
>bad2 : Symbol(bad2, Decl(mod1.js, 13, 44))
>mod.bad2 : Symbol(mod.bad2, Decl(mod1.js, 13, 44))
>mod : Symbol(mod, Decl(importer.js, 0, 5))
>bad2 : Symbol(mod.bad2, Decl(mod1.js, 13, 44))

mod.bad3 = 0;
>mod.bad3 : Symbol(bad3, Decl(mod1.js, 14, 77))
>mod : Symbol(mod, Decl(importer.js, 0, 5), Decl(importer.js, 6, 9), Decl(importer.js, 9, 14), Decl(importer.js, 10, 14), Decl(importer.js, 11, 13) ... and 2 more)
>bad3 : Symbol(bad3, Decl(mod1.js, 14, 77))
>mod.bad3 : Symbol(mod.bad3, Decl(mod1.js, 14, 77))
>mod : Symbol(mod, Decl(importer.js, 0, 5))
>bad3 : Symbol(mod.bad3, Decl(mod1.js, 14, 77))

=== tests/cases/conformance/jsdoc/mod1.js ===
const obj = { value: 42, writable: true };
Expand Down
28 changes: 14 additions & 14 deletions tests/baselines/reference/checkOtherObjectAssignProperty.types
Original file line number Diff line number Diff line change
@@ -1,80 +1,80 @@
=== tests/cases/conformance/jsdoc/importer.js ===
const mod = require("./mod1");
>mod : typeof import("tests/cases/conformance/jsdoc/mod1")
>require("./mod1") : typeof import("tests/cases/conformance/jsdoc/mod1")
>mod : typeof mod
>require("./mod1") : typeof mod
>require : any
>"./mod1" : "./mod1"

mod.thing;
>mod.thing : number
>mod : typeof import("tests/cases/conformance/jsdoc/mod1")
>mod : typeof mod
>thing : number

mod.other;
>mod.other : any
>mod : typeof import("tests/cases/conformance/jsdoc/mod1")
>mod : typeof mod
>other : any

mod.prop;
>mod.prop : any
>mod : typeof import("tests/cases/conformance/jsdoc/mod1")
>mod : typeof mod
>prop : any

mod.bad1;
>mod.bad1 : any
>mod : typeof import("tests/cases/conformance/jsdoc/mod1")
>mod : typeof mod
>bad1 : any

mod.bad2;
>mod.bad2 : string
>mod : typeof import("tests/cases/conformance/jsdoc/mod1")
>mod : typeof mod
>bad2 : string

mod.bad3;
>mod.bad3 : any
>mod : typeof import("tests/cases/conformance/jsdoc/mod1")
>mod : typeof mod
>bad3 : any


mod.thing = 0;
>mod.thing = 0 : 0
>mod.thing : number
>mod : typeof import("tests/cases/conformance/jsdoc/mod1")
>mod : typeof mod
>thing : number
>0 : 0

mod.other = 0;
>mod.other = 0 : 0
>mod.other : any
>mod : typeof import("tests/cases/conformance/jsdoc/mod1")
>mod : typeof mod
>other : any
>0 : 0

mod.prop = 0;
>mod.prop = 0 : 0
>mod.prop : any
>mod : typeof import("tests/cases/conformance/jsdoc/mod1")
>mod : typeof mod
>prop : any
>0 : 0

mod.bad1 = 0;
>mod.bad1 = 0 : 0
>mod.bad1 : any
>mod : typeof import("tests/cases/conformance/jsdoc/mod1")
>mod : typeof mod
>bad1 : any
>0 : 0

mod.bad2 = 0;
>mod.bad2 = 0 : 0
>mod.bad2 : any
>mod : typeof import("tests/cases/conformance/jsdoc/mod1")
>mod : typeof mod
>bad2 : any
>0 : 0

mod.bad3 = 0;
>mod.bad3 = 0 : 0
>mod.bad3 : any
>mod : typeof import("tests/cases/conformance/jsdoc/mod1")
>mod : typeof mod
>bad3 : any
>0 : 0

Expand Down
8 changes: 4 additions & 4 deletions tests/baselines/reference/constructorFunctions2.types
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ declare var module: any, exports: any;

=== tests/cases/conformance/salsa/index.js ===
const A = require("./other");
>A : typeof import("tests/cases/conformance/salsa/other")
>require("./other") : typeof import("tests/cases/conformance/salsa/other")
>A : typeof A
>require("./other") : typeof A
>require : (id: string) => any
>"./other" : "./other"

const a = new A().id;
>a : number
>new A().id : number
>new A() : import("tests/cases/conformance/salsa/other")
>A : typeof import("tests/cases/conformance/salsa/other")
>new A() : A
>A : typeof A
>id : number

const B = function() { this.id = 1; }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
=== tests/cases/compiler/index.js ===
const _item = require("./namespacer");
>_item : typeof B
>require("./namespacer") : typeof B
>_item : typeof _item
>require("./namespacer") : typeof _item
>require : any
>"./namespacer" : "./namespacer"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,12 @@ declare class Bar {
}
//// [cls.d.ts]
export = Foo;
declare const Foo_base: typeof import("./bar");
declare class Foo extends Foo_base {
declare class Foo extends Bar {
}
declare namespace Foo {
export { Strings };
}
import Bar = require("./bar");
declare namespace Strings {
const a: string;
const b: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
=== tests/cases/conformance/jsdoc/declarations/cls.js ===
const Bar = require("./bar");
>Bar : typeof import("tests/cases/conformance/jsdoc/declarations/bar")
>require("./bar") : typeof import("tests/cases/conformance/jsdoc/declarations/bar")
>Bar : typeof Bar
>require("./bar") : typeof Bar
>require : any
>"./bar" : "./bar"

Expand All @@ -20,7 +20,7 @@ const Strings = {
};
class Foo extends Bar {}
>Foo : Foo
>Bar : import("tests/cases/conformance/jsdoc/declarations/bar")
>Bar : Bar

module.exports = Foo;
>module.exports = Foo : typeof Foo
Expand Down
3 changes: 2 additions & 1 deletion tests/baselines/reference/jsDeclarationsCrossfileMerge.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@ module.exports.memberName = "thing";


//// [index.d.ts]
declare const _exports: typeof import("./exporter").default;
declare const _exports: typeof m.default;
export = _exports;
import m = require("./exporter");
Loading