Skip to content

Commit 6d2aaa1

Browse files
authored
Add basic CommonJS support (#748)
1 parent 34c6c15 commit 6d2aaa1

File tree

1,096 files changed

+16891
-14410
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

1,096 files changed

+16891
-14410
lines changed

internal/ast/ast.go

+73-6
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,7 @@ func (n *Node) Expression() *Node {
357357
return n.AsThrowStatement().Expression
358358
case KindExternalModuleReference:
359359
return n.AsExternalModuleReference().Expression
360-
case KindExportAssignment:
360+
case KindExportAssignment, KindJSExportAssignment:
361361
return n.AsExportAssignment().Expression
362362
case KindDecorator:
363363
return n.AsDecorator().Expression
@@ -532,7 +532,7 @@ func (n *Node) Type() *Node {
532532
return n.AsJSDocNonNullableType().Type
533533
case KindJSDocOptionalType:
534534
return n.AsJSDocOptionalType().Type
535-
case KindEnumMember, KindBindingElement, KindExportAssignment, KindBinaryExpression:
535+
case KindEnumMember, KindBindingElement, KindExportAssignment, KindJSExportAssignment, KindBinaryExpression, KindCommonJSExport:
536536
return nil
537537
default:
538538
funcLike := n.FunctionLikeData()
@@ -907,6 +907,10 @@ func (n *Node) AsExportAssignment() *ExportAssignment {
907907
return n.data.(*ExportAssignment)
908908
}
909909

910+
func (n *Node) AsCommonJSExport() *CommonJSExport {
911+
return n.data.(*CommonJSExport)
912+
}
913+
910914
func (n *Node) AsObjectLiteralExpression() *ObjectLiteralExpression {
911915
return n.data.(*ObjectLiteralExpression)
912916
}
@@ -4314,6 +4318,7 @@ func IsNamedImports(node *Node) bool {
43144318

43154319
// This is either an `export =` or an `export default` declaration.
43164320
// Unless `isExportEquals` is set, this node was parsed as an `export default`.
4321+
// If Kind is KindJSExportAssignment, it is a synthetic declaration for `module.exports =`.
43174322
type ExportAssignment struct {
43184323
StatementBase
43194324
DeclarationBase
@@ -4323,17 +4328,25 @@ type ExportAssignment struct {
43234328
Expression *Expression // Expression
43244329
}
43254330

4326-
func (f *NodeFactory) NewExportAssignment(modifiers *ModifierList, isExportEquals bool, expression *Expression) *Node {
4331+
func (f *NodeFactory) newExportOrJSExportAssignment(kind Kind, modifiers *ModifierList, isExportEquals bool, expression *Expression) *Node {
43274332
data := &ExportAssignment{}
43284333
data.modifiers = modifiers
43294334
data.IsExportEquals = isExportEquals
43304335
data.Expression = expression
4331-
return f.newNode(KindExportAssignment, data)
4336+
return f.newNode(kind, data)
4337+
}
4338+
4339+
func (f *NodeFactory) NewExportAssignment(modifiers *ModifierList, isExportEquals bool, expression *Expression) *Node {
4340+
return f.newExportOrJSExportAssignment(KindExportAssignment, modifiers, isExportEquals, expression)
4341+
}
4342+
4343+
func (f *NodeFactory) NewJSExportAssignment(expression *Expression) *Node {
4344+
return f.newExportOrJSExportAssignment(KindJSExportAssignment, nil /*modifiers*/, true, expression)
43324345
}
43334346

43344347
func (f *NodeFactory) UpdateExportAssignment(node *ExportAssignment, modifiers *ModifierList, expression *Expression) *Node {
43354348
if modifiers != node.modifiers || expression != node.Expression {
4336-
return updateNode(f.NewExportAssignment(modifiers, node.IsExportEquals, expression), node.AsNode(), f.hooks)
4349+
return updateNode(f.newExportOrJSExportAssignment(node.Kind, modifiers, node.IsExportEquals, expression), node.AsNode(), f.hooks)
43374350
}
43384351
return node.AsNode()
43394352
}
@@ -4347,7 +4360,7 @@ func (node *ExportAssignment) VisitEachChild(v *NodeVisitor) *Node {
43474360
}
43484361

43494362
func (node *ExportAssignment) Clone(f NodeFactoryCoercible) *Node {
4350-
return cloneNode(f.AsNodeFactory().NewExportAssignment(node.Modifiers(), node.IsExportEquals, node.Expression), node.AsNode(), f.AsNodeFactory().hooks)
4363+
return cloneNode(f.AsNodeFactory().newExportOrJSExportAssignment(node.Kind, node.Modifiers(), node.IsExportEquals, node.Expression), node.AsNode(), f.AsNodeFactory().hooks)
43514364
}
43524365

43534366
func (node *ExportAssignment) computeSubtreeFacts() SubtreeFacts {
@@ -4358,6 +4371,60 @@ func IsExportAssignment(node *Node) bool {
43584371
return node.Kind == KindExportAssignment
43594372
}
43604373

4374+
func IsJSExportAssignment(node *Node) bool {
4375+
return node.Kind == KindJSExportAssignment
4376+
}
4377+
4378+
func IsAnyExportAssignment(node *Node) bool {
4379+
return node.Kind == KindExportAssignment || node.Kind == KindJSExportAssignment
4380+
}
4381+
4382+
// CommonJSExport
4383+
4384+
type CommonJSExport struct {
4385+
StatementBase
4386+
DeclarationBase
4387+
ExportableBase
4388+
ModifiersBase
4389+
name *IdentifierNode
4390+
Initializer *Expression
4391+
}
4392+
4393+
func (f *NodeFactory) NewCommonJSExport(modifiers *ModifierList, name *IdentifierNode, initializer *Expression) *Node {
4394+
data := &CommonJSExport{}
4395+
data.modifiers = modifiers
4396+
data.name = name
4397+
data.Initializer = initializer
4398+
return newNode(KindCommonJSExport, data, f.hooks)
4399+
}
4400+
4401+
func (f *NodeFactory) UpdateCommonJSExport(node *CommonJSExport, modifiers *ModifierList, name *IdentifierNode, initializer *Expression) *Node {
4402+
if modifiers != node.modifiers || initializer != node.Initializer || name != node.name {
4403+
return updateNode(f.NewCommonJSExport(node.modifiers, name, initializer), node.AsNode(), f.hooks)
4404+
}
4405+
return node.AsNode()
4406+
}
4407+
4408+
func (node *CommonJSExport) ForEachChild(v Visitor) bool {
4409+
return visitModifiers(v, node.modifiers) || visit(v, node.name) || visit(v, node.Initializer)
4410+
}
4411+
4412+
func (node *CommonJSExport) VisitEachChild(v *NodeVisitor) *Node {
4413+
return v.Factory.UpdateCommonJSExport(node, v.visitModifiers(node.modifiers), v.visitNode(node.name), v.visitNode(node.Initializer))
4414+
}
4415+
4416+
func (node *CommonJSExport) Clone(f NodeFactoryCoercible) *Node {
4417+
return cloneNode(f.AsNodeFactory().NewCommonJSExport(node.Modifiers(), node.name, node.Initializer), node.AsNode(), f.AsNodeFactory().hooks)
4418+
}
4419+
4420+
func IsCommonJSExport(node *Node) bool {
4421+
return node.Kind == KindCommonJSExport
4422+
}
4423+
4424+
func (node *CommonJSExport) Name() *DeclarationName {
4425+
return node.name
4426+
}
4427+
43614428
// NamespaceExportDeclaration
43624429

43634430
type NamespaceExportDeclaration struct {

internal/ast/kind.go

+3-1
Original file line numberDiff line numberDiff line change
@@ -377,8 +377,10 @@ const (
377377
KindJSDocImportTag
378378
// Synthesized list
379379
KindSyntaxList
380-
// Synthesized JS nodes
380+
// Reparsed JS nodes
381381
KindJSTypeAliasDeclaration
382+
KindJSExportAssignment
383+
KindCommonJSExport
382384
// Transformation nodes
383385
KindNotEmittedStatement
384386
KindPartiallyEmittedExpression

internal/ast/kind_stringer_generated.go

+9-7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/ast/symbol.go

+1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ const (
4949
InternalSymbolNameExportEquals = "export=" // Export assignment symbol
5050
InternalSymbolNameDefault = "default" // Default export symbol (technically not wholly internal, but included here for usability)
5151
InternalSymbolNameThis = "this"
52+
InternalSymbolNameModuleExports = "module.exports"
5253
)
5354

5455
func SymbolName(symbol *Symbol) string {

internal/ast/utilities.go

+46-41
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,7 @@ func isDeclarationStatementKind(kind Kind) bool {
649649
KindImportEqualsDeclaration,
650650
KindExportDeclaration,
651651
KindExportAssignment,
652+
KindJSExportAssignment,
652653
KindNamespaceExportDeclaration:
653654
return true
654655
}
@@ -1304,7 +1305,7 @@ func GetElementOrPropertyAccessArgumentExpressionOrName(node *Node) *Node {
13041305
}
13051306

13061307
func GetElementOrPropertyAccessName(node *Node) string {
1307-
name := getElementOrPropertyAccessArgumentExpressionOrName(node)
1308+
name := GetElementOrPropertyAccessArgumentExpressionOrName(node)
13081309
if name == nil {
13091310
return ""
13101311
}
@@ -1351,10 +1352,10 @@ func GetNonAssignedNameOfDeclaration(declaration *Node) *Node {
13511352
switch declaration.Kind {
13521353
case KindBinaryExpression:
13531354
if IsFunctionPropertyAssignment(declaration) {
1354-
return getElementOrPropertyAccessArgumentExpressionOrName(declaration.AsBinaryExpression().Left)
1355+
return GetElementOrPropertyAccessArgumentExpressionOrName(declaration.AsBinaryExpression().Left)
13551356
}
13561357
return nil
1357-
case KindExportAssignment:
1358+
case KindExportAssignment, KindJSExportAssignment:
13581359
expr := declaration.AsExportAssignment().Expression
13591360
if IsIdentifier(expr) {
13601361
return expr
@@ -1414,22 +1415,6 @@ func IsFunctionPropertyAssignment(node *Node) bool {
14141415
return false
14151416
}
14161417

1417-
// Does not handle signed numeric names like `a[+0]` - handling those would require handling prefix unary expressions
1418-
// throughout late binding handling as well, which is awkward (but ultimately probably doable if there is demand)
1419-
func getElementOrPropertyAccessArgumentExpressionOrName(node *Node) *Node {
1420-
switch node.Kind {
1421-
case KindPropertyAccessExpression:
1422-
return node.Name()
1423-
case KindElementAccessExpression:
1424-
arg := SkipParentheses(node.AsElementAccessExpression().ArgumentExpression)
1425-
if IsStringOrNumericLiteralLike(arg) {
1426-
return arg
1427-
}
1428-
return node
1429-
}
1430-
panic("Unhandled case in getElementOrPropertyAccessArgumentExpressionOrName")
1431-
}
1432-
14331418
/**
14341419
* A declaration has a dynamic name if all of the following are true:
14351420
* 1. The declaration has a computed property name.
@@ -1495,10 +1480,6 @@ func IsEffectiveExternalModule(node *SourceFile, compilerOptions *core.CompilerO
14951480
return IsExternalModule(node) || (isCommonJSContainingModuleKind(compilerOptions.GetEmitModuleKind()) && node.CommonJSModuleIndicator != nil)
14961481
}
14971482

1498-
func IsEffectiveExternalModuleWorker(node *SourceFile, moduleKind core.ModuleKind) bool {
1499-
return IsExternalModule(node) || (isCommonJSContainingModuleKind(moduleKind) && node.CommonJSModuleIndicator != nil)
1500-
}
1501-
15021483
func isCommonJSContainingModuleKind(kind core.ModuleKind) bool {
15031484
return kind == core.ModuleKindCommonJS || kind == core.ModuleKindNode16 || kind == core.ModuleKindNodeNext
15041485
}
@@ -1668,20 +1649,7 @@ func IsEnumConst(node *Node) bool {
16681649
}
16691650

16701651
func ExportAssignmentIsAlias(node *Node) bool {
1671-
return isAliasableExpression(getExportAssignmentExpression(node))
1672-
}
1673-
1674-
func getExportAssignmentExpression(node *Node) *Node {
1675-
switch node.Kind {
1676-
case KindExportAssignment:
1677-
return node.AsExportAssignment().Expression
1678-
case KindBinaryExpression:
1679-
return node.AsBinaryExpression().Right
1680-
}
1681-
panic("Unhandled case in getExportAssignmentExpression")
1682-
}
1683-
1684-
func isAliasableExpression(e *Node) bool {
1652+
e := node.AsExportAssignment().Expression
16851653
return IsEntityNameExpression(e) || IsClassExpression(e)
16861654
}
16871655

@@ -2086,6 +2054,7 @@ func GetMeaningFromDeclaration(node *Node) SemanticMeaning {
20862054
KindImportEqualsDeclaration,
20872055
KindImportDeclaration,
20882056
KindExportAssignment,
2057+
KindJSExportAssignment,
20892058
KindExportDeclaration:
20902059
return SemanticMeaningAll
20912060

@@ -2474,22 +2443,27 @@ func IsNonLocalAlias(symbol *Symbol, excludes SymbolFlags) bool {
24742443
// An alias symbol is created by one of the following declarations:
24752444
//
24762445
// import <symbol> = ...
2446+
// const <symbol> = ... (JS only)
2447+
// const { <symbol>, ... } = ... (JS only)
24772448
// import <symbol> from ...
24782449
// import * as <symbol> from ...
24792450
// import { x as <symbol> } from ...
24802451
// export { x as <symbol> } from ...
24812452
// export * as ns <symbol> from ...
24822453
// export = <EntityNameExpression>
24832454
// export default <EntityNameExpression>
2455+
// module.exports = <EntityNameExpression> (JS only)
24842456
func IsAliasSymbolDeclaration(node *Node) bool {
24852457
switch node.Kind {
24862458
case KindImportEqualsDeclaration, KindNamespaceExportDeclaration, KindNamespaceImport, KindNamespaceExport,
24872459
KindImportSpecifier, KindExportSpecifier:
24882460
return true
24892461
case KindImportClause:
24902462
return node.AsImportClause().Name() != nil
2491-
case KindExportAssignment:
2463+
case KindExportAssignment, KindJSExportAssignment:
24922464
return ExportAssignmentIsAlias(node)
2465+
case KindVariableDeclaration, KindBindingElement:
2466+
return IsVariableDeclarationInitializedToRequire(node)
24932467
}
24942468
return false
24952469
}
@@ -2568,7 +2542,7 @@ func ForEachDynamicImportOrRequireCall(
25682542
lastIndex, size := findImportOrRequire(file.Text(), 0)
25692543
for lastIndex >= 0 {
25702544
node := GetNodeAtPosition(file, lastIndex, isJavaScriptFile && includeTypeSpaceImports)
2571-
if isJavaScriptFile && IsRequireCall(node, requireStringLiteralLikeArgument) {
2545+
if isJavaScriptFile && IsRequireCall(node) {
25722546
if cb(node, node.Arguments()[0]) {
25732547
return true
25742548
}
@@ -2595,7 +2569,8 @@ func ForEachDynamicImportOrRequireCall(
25952569
return false
25962570
}
25972571

2598-
func IsRequireCall(node *Node, requireStringLiteralLikeArgument bool) bool {
2572+
// IsVariableDeclarationInitializedToRequire should be used wherever parent pointers are set
2573+
func IsRequireCall(node *Node) bool {
25992574
if !IsCallExpression(node) {
26002575
return false
26012576
}
@@ -2606,7 +2581,7 @@ func IsRequireCall(node *Node, requireStringLiteralLikeArgument bool) bool {
26062581
if len(call.Arguments.Nodes) != 1 {
26072582
return false
26082583
}
2609-
return !requireStringLiteralLikeArgument || IsStringLiteralLike(call.Arguments.Nodes[0])
2584+
return IsStringLiteralLike(call.Arguments.Nodes[0])
26102585
}
26112586

26122587
func IsUnterminatedLiteral(node *Node) bool {
@@ -2664,6 +2639,36 @@ func GetPragmaArgument(pragma *Pragma, name string) string {
26642639
return ""
26652640
}
26662641

2642+
// Of the form: `const x = require("x")` or `const { x } = require("x")` or with `var` or `let`
2643+
// The variable must not be exported and must not have a type annotation, even a jsdoc one.
2644+
// The initializer must be a call to `require` with a string literal or a string literal-like argument.
2645+
func IsVariableDeclarationInitializedToRequire(node *Node) bool {
2646+
if !IsInJSFile(node) {
2647+
return false
2648+
}
2649+
if node.Kind == KindBindingElement {
2650+
node = node.Parent.Parent
2651+
}
2652+
if node.Kind != KindVariableDeclaration {
2653+
return false
2654+
}
2655+
2656+
return node.Parent.Parent.ModifierFlags()&ModifierFlagsExport == 0 &&
2657+
node.AsVariableDeclaration().Initializer != nil &&
2658+
node.Type() == nil &&
2659+
IsRequireCall(node.AsVariableDeclaration().Initializer)
2660+
}
2661+
2662+
func IsModuleExportsAccessExpression(node *Node) bool {
2663+
return (IsPropertyAccessExpression(node) || isLiteralLikeElementAccess(node)) &&
2664+
IsModuleIdentifier(node.Expression()) &&
2665+
GetElementOrPropertyAccessName(node) == "exports"
2666+
}
2667+
2668+
func isLiteralLikeElementAccess(node *Node) bool {
2669+
return node.Kind == KindElementAccessExpression && IsStringOrNumericLiteralLike(node.AsElementAccessExpression().ArgumentExpression)
2670+
}
2671+
26672672
func IsCheckJSEnabledForFile(sourceFile *SourceFile, compilerOptions *core.CompilerOptions) bool {
26682673
if sourceFile.CheckJsDirective != nil {
26692674
return sourceFile.CheckJsDirective.Enabled

0 commit comments

Comments
 (0)