Skip to content
This repository was archived by the owner on May 22, 2025. It is now read-only.

Commit 2d154f9

Browse files
committed
Fix googmodule with TypeScript 3.9
1 parent 03e4676 commit 2d154f9

File tree

1 file changed

+89
-1
lines changed

1 file changed

+89
-1
lines changed

src/googmodule.ts

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,33 @@ function isEsModuleProperty(stmt: ts.ExpressionStatement): boolean {
7777
return prop.initializer.kind === ts.SyntaxKind.TrueKeyword;
7878
}
7979

80+
function isHoistedExportAssignment(stmt: ts.ExpressionStatement): boolean {
81+
function checkVoid0Assignment(expr: ts.Expression): boolean {
82+
// Ensure this looks something like `exports.abc = exports.xyz = void 0;`.
83+
if (!ts.isBinaryExpression(expr)) return false;
84+
if (expr.operatorToken.kind !== ts.SyntaxKind.EqualsToken) return false;
85+
86+
// Ensure the left side of the expression is an access on `exports`.
87+
if (!ts.isPropertyAccessExpression(expr.left)) return false;
88+
if (!ts.isIdentifier(expr.left.expression)) return false;
89+
if (expr.left.expression.escapedText !== 'exports') return false;
90+
91+
// If the right side is another `exports.abc = ...` check that to see if we eventually hit a
92+
// `void 0`.
93+
if (ts.isBinaryExpression(expr.right)) {
94+
return checkVoid0Assignment(expr.right);
95+
}
96+
97+
// Ensure the right side is exactly "void 0";
98+
if (!ts.isVoidExpression(expr.right)) return false;
99+
if (!ts.isNumericLiteral(expr.right.expression)) return false;
100+
if (expr.right.expression.text !== '0') return false;
101+
return true;
102+
}
103+
104+
return checkVoid0Assignment(stmt.expression);
105+
}
106+
80107
/**
81108
* Returns the string argument if call is of the form
82109
* require('foo')
@@ -467,6 +494,61 @@ export function commonJsToGoogmoduleTransformer(
467494
return [require, exportStmt];
468495
}
469496

497+
function rewriteObjectDefinePropertyOnExports(stmt: ts.ExpressionStatement): ts.Statement|
498+
null {
499+
if (!ts.isCallExpression(stmt.expression)) return null;
500+
501+
const callExpr = stmt.expression;
502+
if (!ts.isPropertyAccessExpression(callExpr.expression)) return null;
503+
504+
const propAccess = callExpr.expression;
505+
if (!ts.isIdentifier(propAccess.expression)) return null;
506+
if (propAccess.expression.text !== 'Object') return null;
507+
if (propAccess.name.text !== 'defineProperty') return null;
508+
509+
const [objDefArg1, objDefArg2, objDefArg3] = callExpr.arguments;
510+
if (callExpr.arguments.length !== 3) return null;
511+
if (!ts.isIdentifier(objDefArg1)) return null;
512+
if (objDefArg1.text !== 'exports') return null;
513+
if (!ts.isStringLiteral(objDefArg2)) return null;
514+
if (!ts.isObjectLiteralExpression(objDefArg3)) return null;
515+
516+
function findPropConfigFor(name: string) {
517+
return (p: ts.ObjectLiteralElementLike) => {
518+
return ts.isPropertyAssignment(p) && ts.isIdentifier(p.name) && p.name.text === name;
519+
};
520+
}
521+
522+
const enumerableConfig = objDefArg3.properties.find(findPropConfigFor('enumerable'));
523+
if (!enumerableConfig) return null;
524+
if (!ts.isPropertyAssignment(enumerableConfig)) return null;
525+
if (enumerableConfig.initializer.kind !== ts.SyntaxKind.TrueKeyword) return null;
526+
527+
const getConfig = objDefArg3.properties.find(findPropConfigFor('get'));
528+
if (!getConfig) return null;
529+
if (!ts.isPropertyAssignment(getConfig)) return null;
530+
if (!ts.isFunctionExpression(getConfig.initializer)) return null;
531+
532+
const getterFunc = getConfig.initializer;
533+
if (getterFunc.body.statements.length !== 1) return null;
534+
535+
const getterReturn = getterFunc.body.statements[0];
536+
if (!ts.isReturnStatement(getterReturn)) return null;
537+
538+
const realExportValue = getterReturn.expression;
539+
if (!realExportValue) return null;
540+
541+
const exportStmt = ts.setOriginalNode(
542+
ts.setTextRange(
543+
ts.createExpressionStatement(ts.createAssignment(
544+
ts.createPropertyAccess(ts.createIdentifier('exports'), objDefArg2.text),
545+
realExportValue)),
546+
stmt),
547+
stmt);
548+
549+
return exportStmt;
550+
}
551+
470552
/**
471553
* visitTopLevelStatement implements the main CommonJS to goog.module conversion. It visits a
472554
* SourceFile level statement and adds a (possibly) transformed representation of it into
@@ -498,7 +580,8 @@ export function commonJsToGoogmoduleTransformer(
498580
case ts.SyntaxKind.ExpressionStatement: {
499581
const exprStmt = node as ts.ExpressionStatement;
500582
// Check for "use strict" and certain Object.defineProperty and skip it if necessary.
501-
if (isUseStrict(exprStmt) || isEsModuleProperty(exprStmt)) {
583+
if (isUseStrict(exprStmt) || isEsModuleProperty(exprStmt) ||
584+
isHoistedExportAssignment(exprStmt)) {
502585
stmts.push(createNotEmittedStatementWithComments(sf, exprStmt));
503586
return;
504587
}
@@ -526,6 +609,11 @@ export function commonJsToGoogmoduleTransformer(
526609
stmts.push(...exportStarAsNs);
527610
return;
528611
}
612+
const exportFromObjDefProp = rewriteObjectDefinePropertyOnExports(exprStmt);
613+
if (exportFromObjDefProp) {
614+
stmts.push(exportFromObjDefProp);
615+
return;
616+
}
529617
// Check for:
530618
// "require('foo');" (a require for its side effects)
531619
const expr = exprStmt.expression;

0 commit comments

Comments
 (0)