Support js nested namespace decls on exports

and module.exports.
This commit is contained in:
Nathan Shively-Sanders 2018-02-27 10:20:16 -08:00
parent 4099d48ea5
commit c3143d2e47
10 changed files with 459 additions and 32 deletions

View file

@ -2303,7 +2303,17 @@ namespace ts {
// When we create a property via 'exports.foo = bar', the 'exports.foo' property access
// expression is the declaration
setCommonJsModuleIndicator(node);
declareSymbol(file.symbol.exports, file.symbol, <PropertyAccessExpression>node.left, SymbolFlags.Property | SymbolFlags.ExportValue, SymbolFlags.None);
const lhs = node.left as PropertyAccessEntityNameExpression;
const symbol = forEachIdentifierInEntityName(lhs.expression, (id, original, e) => {
if (isExportsOrModuleExportsOrAlias(file, e) || (isIdentifier(e) && e.escapedText === "module" && original === undefined)) {
return file.symbol;
}
Debug.assert(!!original);
const s = getJSInitializerSymbol(original);
addDeclarationToSymbol(s, id, SymbolFlags.Module | SymbolFlags.JSContainer);
return s;
});
declareSymbol(symbol.exports, symbol, lhs, SymbolFlags.Property | SymbolFlags.ExportValue, SymbolFlags.None);
}
function bindModuleExportsAssignment(node: BinaryExpression) {
@ -2460,14 +2470,14 @@ namespace ts {
}
}
function forEachIdentifierInEntityName(e: EntityNameExpression, action: (e: Identifier, symbol: Symbol) => Symbol): Symbol {
function forEachIdentifierInEntityName(e: EntityNameExpression, action: (e: Identifier, symbol: Symbol, k: EntityNameExpression) => Symbol): Symbol {
if (isIdentifier(e)) {
return action(e, lookupSymbolForPropertyAccess(e));
return action(e, lookupSymbolForPropertyAccess(e), e);
}
else {
const s = getJSInitializerSymbol(forEachIdentifierInEntityName(e.expression, action));
Debug.assert(!!s && !!s.exports);
return action(e.name, s.exports.get(e.name.escapedText));
return action(e.name, s.exports.get(e.name.escapedText), e);
}
}

View file

@ -1585,8 +1585,9 @@ namespace ts {
}
function checkAndReportErrorForUsingTypeAsNamespace(errorLocation: Node, name: __String, meaning: SymbolFlags): boolean {
if (meaning === SymbolFlags.Namespace) {
const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.Type & ~SymbolFlags.Namespace, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined, /*isUse*/ false));
const namespaceMeaning = SymbolFlags.Namespace | (isInJavaScriptFile(errorLocation) ? SymbolFlags.Value : 0);
if (meaning === namespaceMeaning) {
const symbol = resolveSymbol(resolveName(errorLocation, name, SymbolFlags.Type & ~namespaceMeaning, /*nameNotFoundMessage*/undefined, /*nameArg*/ undefined, /*isUse*/ false));
const parent = errorLocation.parent;
if (symbol) {
if (isQualifiedName(parent)) {
@ -1992,9 +1993,10 @@ namespace ts {
return undefined;
}
const namespaceMeaning = SymbolFlags.Namespace | (isInJavaScriptFile(name) ? meaning & SymbolFlags.Value : 0);
let symbol: Symbol;
if (name.kind === SyntaxKind.Identifier) {
const message = meaning === SymbolFlags.Namespace ? Diagnostics.Cannot_find_namespace_0 : Diagnostics.Cannot_find_name_0;
const message = meaning === namespaceMeaning ? Diagnostics.Cannot_find_namespace_0 : Diagnostics.Cannot_find_name_0;
symbol = resolveName(location || name, name.escapedText, meaning, ignoreErrors ? undefined : message, name, /*isUse*/ true);
if (!symbol) {
@ -2004,7 +2006,7 @@ namespace ts {
else if (name.kind === SyntaxKind.QualifiedName || name.kind === SyntaxKind.PropertyAccessExpression) {
const left = name.kind === SyntaxKind.QualifiedName ? name.left : name.expression;
const right = name.kind === SyntaxKind.QualifiedName ? name.right : name.name;
let namespace = resolveEntityName(left, SymbolFlags.Namespace, ignoreErrors, /*dontResolveAlias*/ false, location);
let namespace = resolveEntityName(left, namespaceMeaning, ignoreErrors, /*dontResolveAlias*/ false, location);
if (!namespace || nodeIsMissing(right)) {
return undefined;
}
@ -2012,7 +2014,7 @@ namespace ts {
return namespace;
}
if (isInJavaScriptFile(name)) {
const initializer = getDeclaredJavascriptInitializer(namespace.valueDeclaration);
const initializer = getDeclaredJavascriptInitializer(namespace.valueDeclaration) || getAssignedJavascriptInitializer(namespace.valueDeclaration);
if (initializer) {
namespace = getSymbolOfNode(initializer);
}

View file

@ -1488,7 +1488,8 @@ namespace ts {
}
export function getAssignedJavascriptInitializer(node: Node) {
return (isBinaryExpression(node) && node.operatorToken.kind === SyntaxKind.BarBarToken || isPropertyAccessExpression(node)) &&
return node &&
(isBinaryExpression(node) && node.operatorToken.kind === SyntaxKind.BarBarToken || isPropertyAccessExpression(node)) &&
node.parent && isBinaryExpression(node.parent) &&
node.parent.operatorToken.kind === SyntaxKind.EqualsToken &&
(getJavascriptInitializer(node.parent.right) || getDefaultedJavascriptInitializer(node.parent.left as EntityNameExpression, node.parent.right));
@ -1570,33 +1571,30 @@ namespace ts {
if (lhs.expression.kind === SyntaxKind.ThisKeyword) {
return SpecialPropertyAssignmentKind.ThisProperty;
}
if (isIdentifier(lhs.expression)) {
if (lhs.expression.escapedText === "exports") {
// exports.name = expr
return SpecialPropertyAssignmentKind.ExportsProperty;
}
else if (lhs.expression.escapedText === "module" && lhs.name.escapedText === "exports") {
// module.exports = expr
return SpecialPropertyAssignmentKind.ModuleExports;
}
else if (isIdentifier(lhs.expression) && lhs.expression.escapedText === "module" && lhs.name.escapedText === "exports") {
// module.exports = expr
return SpecialPropertyAssignmentKind.ModuleExports;
}
if (isEntityNameExpression(lhs.expression)) {
else if (isEntityNameExpression(lhs.expression)) {
if (lhs.name.escapedText === "prototype" && isObjectLiteralExpression(expr.right)) {
// F.prototype = { ... }
return SpecialPropertyAssignmentKind.Prototype;
}
else if (isPropertyAccessExpression(lhs.expression)) {
// chained dot, e.g. x.y.z = expr; this var is the 'x.y' part
if (isIdentifier(lhs.expression.expression) &&
lhs.expression.expression.escapedText === "module" &&
lhs.expression.name.escapedText === "exports") {
// module.exports.name = expr
return SpecialPropertyAssignmentKind.ExportsProperty;
}
if (lhs.expression.name.escapedText === "prototype") {
// F.G....prototype.x = expr
return SpecialPropertyAssignmentKind.PrototypeProperty;
}
else if (isPropertyAccessExpression(lhs.expression) && lhs.expression.name.escapedText === "prototype") {
// F.G....prototype.x = expr
return SpecialPropertyAssignmentKind.PrototypeProperty;
}
let nextToLast = lhs;
while (isPropertyAccessExpression(nextToLast.expression)) {
nextToLast = nextToLast.expression;
}
Debug.assert(isIdentifier(nextToLast.expression));
const id = nextToLast.expression as Identifier;
if (id.escapedText === "exports" ||
id.escapedText === "module" && nextToLast.name.escapedText === "exports") {
// exports.name = expr OR module.exports.name = expr
return SpecialPropertyAssignmentKind.ExportsProperty;
}
// F.G...x = expr
return SpecialPropertyAssignmentKind.Property;

View file

@ -0,0 +1,74 @@
=== tests/cases/conformance/salsa/mod.js ===
exports.n = {};
>exports.n : Symbol(n, Decl(mod.js, 0, 0))
>exports : Symbol(n, Decl(mod.js, 0, 0))
>n : Symbol(n, Decl(mod.js, 0, 0))
exports.n.K = function () {
>exports.n.K : Symbol(K, Decl(mod.js, 0, 15))
>exports.n : Symbol(K, Decl(mod.js, 0, 15))
>exports : Symbol("tests/cases/conformance/salsa/mod", Decl(mod.js, 0, 0))
>n : Symbol(n, Decl(mod.js, 0, 0))
>K : Symbol(K, Decl(mod.js, 0, 15))
this.x = 10;
>this : Symbol(__object, Decl(mod.js, 0, 11), Decl(mod.js, 1, 8))
>x : Symbol((Anonymous function).x, Decl(mod.js, 1, 27))
}
exports.Classic = class {
>exports.Classic : Symbol(Classic, Decl(mod.js, 3, 1))
>exports : Symbol(Classic, Decl(mod.js, 3, 1))
>Classic : Symbol(Classic, Decl(mod.js, 3, 1))
constructor() {
this.p = 1
>this.p : Symbol((Anonymous class).p, Decl(mod.js, 5, 19))
>this : Symbol((Anonymous class), Decl(mod.js, 4, 17))
>p : Symbol((Anonymous class).p, Decl(mod.js, 5, 19))
}
}
=== tests/cases/conformance/salsa/use.js ===
import * as s from './mod'
>s : Symbol(s, Decl(use.js, 0, 6))
var k = new s.n.K()
>k : Symbol(k, Decl(use.js, 2, 3))
>s.n.K : Symbol(K, Decl(mod.js, 0, 15))
>s.n : Symbol(s.n, Decl(mod.js, 0, 0))
>s : Symbol(s, Decl(use.js, 0, 6))
>n : Symbol(s.n, Decl(mod.js, 0, 0))
>K : Symbol(K, Decl(mod.js, 0, 15))
k.x
>k.x : Symbol((Anonymous function).x, Decl(mod.js, 1, 27))
>k : Symbol(k, Decl(use.js, 2, 3))
>x : Symbol((Anonymous function).x, Decl(mod.js, 1, 27))
var classic = new s.Classic()
>classic : Symbol(classic, Decl(use.js, 4, 3))
>s.Classic : Symbol(s.Classic, Decl(mod.js, 3, 1))
>s : Symbol(s, Decl(use.js, 0, 6))
>Classic : Symbol(s.Classic, Decl(mod.js, 3, 1))
/** @param {s.n.K} c
@param {s.Classic} classic */
function f(c, classic) {
>f : Symbol(f, Decl(use.js, 4, 29))
>c : Symbol(c, Decl(use.js, 9, 11))
>classic : Symbol(classic, Decl(use.js, 9, 13))
c.x
>c.x : Symbol((Anonymous function).x, Decl(mod.js, 1, 27))
>c : Symbol(c, Decl(use.js, 9, 11))
>x : Symbol((Anonymous function).x, Decl(mod.js, 1, 27))
classic.p
>classic.p : Symbol((Anonymous class).p, Decl(mod.js, 5, 19))
>classic : Symbol(classic, Decl(use.js, 9, 13))
>p : Symbol((Anonymous class).p, Decl(mod.js, 5, 19))
}

View file

@ -0,0 +1,87 @@
=== tests/cases/conformance/salsa/mod.js ===
exports.n = {};
>exports.n = {} : typeof __object
>exports.n : typeof __object
>exports : typeof "tests/cases/conformance/salsa/mod"
>n : typeof __object
>{} : typeof __object
exports.n.K = function () {
>exports.n.K = function () { this.x = 10;} : () => void
>exports.n.K : () => void
>exports.n : typeof __object
>exports : typeof "tests/cases/conformance/salsa/mod"
>n : typeof __object
>K : () => void
>function () { this.x = 10;} : () => void
this.x = 10;
>this.x = 10 : 10
>this.x : any
>this : typeof __object
>x : any
>10 : 10
}
exports.Classic = class {
>exports.Classic = class { constructor() { this.p = 1 }} : typeof (Anonymous class)
>exports.Classic : typeof (Anonymous class)
>exports : typeof "tests/cases/conformance/salsa/mod"
>Classic : typeof (Anonymous class)
>class { constructor() { this.p = 1 }} : typeof (Anonymous class)
constructor() {
this.p = 1
>this.p = 1 : 1
>this.p : number
>this : this
>p : number
>1 : 1
}
}
=== tests/cases/conformance/salsa/use.js ===
import * as s from './mod'
>s : typeof s
var k = new s.n.K()
>k : { x: number; }
>new s.n.K() : { x: number; }
>s.n.K : () => void
>s.n : typeof __object
>s : typeof s
>n : typeof __object
>K : () => void
k.x
>k.x : number
>k : { x: number; }
>x : number
var classic = new s.Classic()
>classic : (Anonymous class)
>new s.Classic() : (Anonymous class)
>s.Classic : typeof (Anonymous class)
>s : typeof s
>Classic : typeof (Anonymous class)
/** @param {s.n.K} c
@param {s.Classic} classic */
function f(c, classic) {
>f : (c: { x: number; }, classic: (Anonymous class)) => void
>c : { x: number; }
>classic : (Anonymous class)
c.x
>c.x : number
>c : { x: number; }
>x : number
classic.p
>classic.p : number
>classic : (Anonymous class)
>p : number
}

View file

@ -0,0 +1,37 @@
tests/cases/conformance/salsa/mod.js(1,1): error TS2304: Cannot find name 'module'.
tests/cases/conformance/salsa/mod.js(2,1): error TS2304: Cannot find name 'module'.
tests/cases/conformance/salsa/mod.js(5,1): error TS2304: Cannot find name 'module'.
==== tests/cases/conformance/salsa/mod.js (3 errors) ====
module.exports.n = {};
~~~~~~
!!! error TS2304: Cannot find name 'module'.
module.exports.n.K = function C() {
~~~~~~
!!! error TS2304: Cannot find name 'module'.
this.x = 10;
}
module.exports.Classic = class {
~~~~~~
!!! error TS2304: Cannot find name 'module'.
constructor() {
this.p = 1
}
}
==== tests/cases/conformance/salsa/use.js (0 errors) ====
import * as s from './mod'
var k = new s.n.K()
k.x
var classic = new s.Classic()
/** @param {s.n.K} c
@param {s.Classic} classic */
function f(c, classic) {
c.x
classic.p
}

View file

@ -0,0 +1,67 @@
=== tests/cases/conformance/salsa/mod.js ===
module.exports.n = {};
>module.exports : Symbol(n, Decl(mod.js, 0, 0))
>n : Symbol(n, Decl(mod.js, 0, 0))
module.exports.n.K = function C() {
>module.exports.n : Symbol(K, Decl(mod.js, 0, 22))
>K : Symbol(K, Decl(mod.js, 0, 22))
>C : Symbol(C, Decl(mod.js, 1, 20))
this.x = 10;
>x : Symbol(C.x, Decl(mod.js, 1, 35))
}
module.exports.Classic = class {
>module.exports : Symbol(Classic, Decl(mod.js, 3, 1))
>Classic : Symbol(Classic, Decl(mod.js, 3, 1))
constructor() {
this.p = 1
>this.p : Symbol((Anonymous class).p, Decl(mod.js, 5, 19))
>this : Symbol((Anonymous class), Decl(mod.js, 4, 24))
>p : Symbol((Anonymous class).p, Decl(mod.js, 5, 19))
}
}
=== tests/cases/conformance/salsa/use.js ===
import * as s from './mod'
>s : Symbol(s, Decl(use.js, 0, 6))
var k = new s.n.K()
>k : Symbol(k, Decl(use.js, 2, 3))
>s.n.K : Symbol(K, Decl(mod.js, 0, 22))
>s.n : Symbol(s.n, Decl(mod.js, 0, 0))
>s : Symbol(s, Decl(use.js, 0, 6))
>n : Symbol(s.n, Decl(mod.js, 0, 0))
>K : Symbol(K, Decl(mod.js, 0, 22))
k.x
>k.x : Symbol(C.x, Decl(mod.js, 1, 35))
>k : Symbol(k, Decl(use.js, 2, 3))
>x : Symbol(C.x, Decl(mod.js, 1, 35))
var classic = new s.Classic()
>classic : Symbol(classic, Decl(use.js, 4, 3))
>s.Classic : Symbol(s.Classic, Decl(mod.js, 3, 1))
>s : Symbol(s, Decl(use.js, 0, 6))
>Classic : Symbol(s.Classic, Decl(mod.js, 3, 1))
/** @param {s.n.K} c
@param {s.Classic} classic */
function f(c, classic) {
>f : Symbol(f, Decl(use.js, 4, 29))
>c : Symbol(c, Decl(use.js, 9, 11))
>classic : Symbol(classic, Decl(use.js, 9, 13))
c.x
>c.x : Symbol(C.x, Decl(mod.js, 1, 35))
>c : Symbol(c, Decl(use.js, 9, 11))
>x : Symbol(C.x, Decl(mod.js, 1, 35))
classic.p
>classic.p : Symbol((Anonymous class).p, Decl(mod.js, 5, 19))
>classic : Symbol(classic, Decl(use.js, 9, 13))
>p : Symbol((Anonymous class).p, Decl(mod.js, 5, 19))
}

View file

@ -0,0 +1,92 @@
=== tests/cases/conformance/salsa/mod.js ===
module.exports.n = {};
>module.exports.n = {} : typeof __object
>module.exports.n : any
>module.exports : any
>module : any
>exports : any
>n : any
>{} : typeof __object
module.exports.n.K = function C() {
>module.exports.n.K = function C() { this.x = 10;} : () => void
>module.exports.n.K : any
>module.exports.n : any
>module.exports : any
>module : any
>exports : any
>n : any
>K : any
>function C() { this.x = 10;} : () => void
>C : () => void
this.x = 10;
>this.x = 10 : 10
>this.x : any
>this : any
>x : any
>10 : 10
}
module.exports.Classic = class {
>module.exports.Classic = class { constructor() { this.p = 1 }} : typeof (Anonymous class)
>module.exports.Classic : any
>module.exports : any
>module : any
>exports : any
>Classic : any
>class { constructor() { this.p = 1 }} : typeof (Anonymous class)
constructor() {
this.p = 1
>this.p = 1 : 1
>this.p : number
>this : this
>p : number
>1 : 1
}
}
=== tests/cases/conformance/salsa/use.js ===
import * as s from './mod'
>s : typeof s
var k = new s.n.K()
>k : { x: number; }
>new s.n.K() : { x: number; }
>s.n.K : () => void
>s.n : typeof __object
>s : typeof s
>n : typeof __object
>K : () => void
k.x
>k.x : number
>k : { x: number; }
>x : number
var classic = new s.Classic()
>classic : (Anonymous class)
>new s.Classic() : (Anonymous class)
>s.Classic : typeof (Anonymous class)
>s : typeof s
>Classic : typeof (Anonymous class)
/** @param {s.n.K} c
@param {s.Classic} classic */
function f(c, classic) {
>f : (c: { x: number; }, classic: (Anonymous class)) => void
>c : { x: number; }
>classic : (Anonymous class)
c.x
>c.x : number
>c : { x: number; }
>x : number
classic.p
>classic.p : number
>classic : (Anonymous class)
>p : number
}

View file

@ -0,0 +1,31 @@
// @allowJs: true
// @checkJs: true
// @noEmit: true
// @Filename: mod.js
exports.n = {};
exports.n.K = function () {
this.x = 10;
}
exports.Classic = class {
constructor() {
this.p = 1
}
}
// @Filename: use.js
import * as s from './mod'
var k = new s.n.K()
k.x
var classic = new s.Classic()
/** @param {s.n.K} c
@param {s.Classic} classic */
function f(c, classic) {
c.x
classic.p
}

View file

@ -0,0 +1,29 @@
// @allowJs: true
// @checkJs: true
// @noEmit: true
// @Filename: mod.js
module.exports.n = {};
module.exports.n.K = function C() {
this.x = 10;
}
module.exports.Classic = class {
constructor() {
this.p = 1
}
}
// @Filename: use.js
import * as s from './mod'
var k = new s.n.K()
k.x
var classic = new s.Classic()
/** @param {s.n.K} c
@param {s.Classic} classic */
function f(c, classic) {
c.x
classic.p
}