2011 lines
78 KiB
Go
2011 lines
78 KiB
Go
package moduletransforms
|
|
|
|
import (
|
|
"slices"
|
|
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/binder"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/collections"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/core"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/printer"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/transformers"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath"
|
|
)
|
|
|
|
type CommonJSModuleTransformer struct {
|
|
transformers.Transformer
|
|
topLevelVisitor *ast.NodeVisitor // visits statements at top level of a module
|
|
topLevelNestedVisitor *ast.NodeVisitor // visits nested statements at top level of a module
|
|
discardedValueVisitor *ast.NodeVisitor // visits expressions whose values would be discarded at runtime
|
|
assignmentPatternVisitor *ast.NodeVisitor // visits assignment patterns in a destructuring assignment
|
|
compilerOptions *core.CompilerOptions
|
|
resolver binder.ReferenceResolver
|
|
getEmitModuleFormatOfFile func(file ast.HasFileName) core.ModuleKind
|
|
moduleKind core.ModuleKind
|
|
languageVersion core.ScriptTarget
|
|
currentSourceFile *ast.SourceFile
|
|
currentModuleInfo *externalModuleInfo
|
|
parentNode *ast.Node // used for ancestor tracking via pushNode/popNode to detect expression identifiers
|
|
currentNode *ast.Node // used for ancestor tracking via pushNode/popNode to detect expression identifiers
|
|
}
|
|
|
|
func NewCommonJSModuleTransformer(opts *transformers.TransformOptions) *transformers.Transformer {
|
|
compilerOptions := opts.CompilerOptions
|
|
emitContext := opts.Context
|
|
resolver := opts.Resolver
|
|
if resolver == nil {
|
|
resolver = binder.NewReferenceResolver(compilerOptions, binder.ReferenceResolverHooks{})
|
|
}
|
|
tx := &CommonJSModuleTransformer{compilerOptions: compilerOptions, resolver: resolver, getEmitModuleFormatOfFile: opts.GetEmitModuleFormatOfFile}
|
|
tx.topLevelVisitor = emitContext.NewNodeVisitor(tx.visitTopLevel)
|
|
tx.topLevelNestedVisitor = emitContext.NewNodeVisitor(tx.visitTopLevelNested)
|
|
tx.discardedValueVisitor = emitContext.NewNodeVisitor(tx.visitDiscardedValue)
|
|
tx.assignmentPatternVisitor = emitContext.NewNodeVisitor(tx.visitAssignmentPattern)
|
|
tx.languageVersion = compilerOptions.GetEmitScriptTarget()
|
|
tx.moduleKind = compilerOptions.GetEmitModuleKind()
|
|
return tx.NewTransformer(tx.visit, emitContext)
|
|
}
|
|
|
|
// Pushes a new child node onto the ancestor tracking stack, returning the grandparent node to be restored later via `popNode`.
|
|
func (tx *CommonJSModuleTransformer) pushNode(node *ast.Node) (grandparentNode *ast.Node) {
|
|
grandparentNode = tx.parentNode
|
|
tx.parentNode = tx.currentNode
|
|
tx.currentNode = node
|
|
return grandparentNode
|
|
}
|
|
|
|
// Pops the last child node off the ancestor tracking stack, restoring the grandparent node.
|
|
func (tx *CommonJSModuleTransformer) popNode(grandparentNode *ast.Node) {
|
|
tx.currentNode = tx.parentNode
|
|
tx.parentNode = grandparentNode
|
|
}
|
|
|
|
// Visits a node at the top level of the source file.
|
|
func (tx *CommonJSModuleTransformer) visitTopLevel(node *ast.Node) *ast.Node {
|
|
grandparentNode := tx.pushNode(node)
|
|
defer tx.popNode(grandparentNode)
|
|
|
|
switch node.Kind {
|
|
case ast.KindImportDeclaration:
|
|
node = tx.visitTopLevelImportDeclaration(node.AsImportDeclaration())
|
|
case ast.KindImportEqualsDeclaration:
|
|
node = tx.visitTopLevelImportEqualsDeclaration(node.AsImportEqualsDeclaration())
|
|
case ast.KindExportDeclaration:
|
|
node = tx.visitTopLevelExportDeclaration(node.AsExportDeclaration())
|
|
case ast.KindExportAssignment:
|
|
node = tx.visitTopLevelExportAssignment(node.AsExportAssignment())
|
|
case ast.KindFunctionDeclaration:
|
|
node = tx.visitTopLevelFunctionDeclaration(node.AsFunctionDeclaration())
|
|
case ast.KindClassDeclaration:
|
|
node = tx.visitTopLevelClassDeclaration(node.AsClassDeclaration())
|
|
case ast.KindVariableStatement:
|
|
node = tx.visitTopLevelVariableStatement(node.AsVariableStatement())
|
|
default:
|
|
node = tx.visitTopLevelNestedNoStack(node)
|
|
}
|
|
return node
|
|
}
|
|
|
|
// Visits nested elements at the top-level of a module.
|
|
func (tx *CommonJSModuleTransformer) visitTopLevelNested(node *ast.Node) *ast.Node {
|
|
grandparentNode := tx.pushNode(node)
|
|
defer tx.popNode(grandparentNode)
|
|
|
|
return tx.visitTopLevelNestedNoStack(node)
|
|
}
|
|
|
|
// Visits nested elements at the top-level of a module without ancestor tracking.
|
|
func (tx *CommonJSModuleTransformer) visitTopLevelNestedNoStack(node *ast.Node) *ast.Node {
|
|
switch node.Kind {
|
|
case ast.KindVariableStatement:
|
|
node = tx.visitTopLevelNestedVariableStatement(node.AsVariableStatement())
|
|
case ast.KindForStatement:
|
|
node = tx.visitTopLevelNestedForStatement(node.AsForStatement())
|
|
case ast.KindForInStatement, ast.KindForOfStatement:
|
|
node = tx.visitTopLevelNestedForInOrOfStatement(node.AsForInOrOfStatement())
|
|
case ast.KindDoStatement:
|
|
node = tx.visitTopLevelNestedDoStatement(node.AsDoStatement())
|
|
case ast.KindWhileStatement:
|
|
node = tx.visitTopLevelNestedWhileStatement(node.AsWhileStatement())
|
|
case ast.KindLabeledStatement:
|
|
node = tx.visitTopLevelNestedLabeledStatement(node.AsLabeledStatement())
|
|
case ast.KindWithStatement:
|
|
node = tx.visitTopLevelNestedWithStatement(node.AsWithStatement())
|
|
case ast.KindIfStatement:
|
|
node = tx.visitTopLevelNestedIfStatement(node.AsIfStatement())
|
|
case ast.KindSwitchStatement:
|
|
node = tx.visitTopLevelNestedSwitchStatement(node.AsSwitchStatement())
|
|
case ast.KindCaseBlock:
|
|
node = tx.visitTopLevelNestedCaseBlock(node.AsCaseBlock())
|
|
case ast.KindCaseClause, ast.KindDefaultClause:
|
|
node = tx.visitTopLevelNestedCaseOrDefaultClause(node.AsCaseOrDefaultClause())
|
|
case ast.KindTryStatement:
|
|
node = tx.visitTopLevelNestedTryStatement(node.AsTryStatement())
|
|
case ast.KindCatchClause:
|
|
node = tx.visitTopLevelNestedCatchClause(node.AsCatchClause())
|
|
case ast.KindBlock:
|
|
node = tx.visitTopLevelNestedBlock(node.AsBlock())
|
|
default:
|
|
node = tx.visitNoStack(node, false /*resultIsDiscarded*/)
|
|
}
|
|
return node
|
|
}
|
|
|
|
// Visits source elements that are not top-level or top-level nested statements.
|
|
func (tx *CommonJSModuleTransformer) visit(node *ast.Node) *ast.Node {
|
|
grandparentNode := tx.pushNode(node)
|
|
defer tx.popNode(grandparentNode)
|
|
|
|
return tx.visitNoStack(node, false /*resultIsDiscarded*/)
|
|
}
|
|
|
|
// Visits source elements that are not top-level or top-level nested statements without ancestor tracking.
|
|
func (tx *CommonJSModuleTransformer) visitNoStack(node *ast.Node, resultIsDiscarded bool) *ast.Node {
|
|
// This visitor does not need to descend into the tree if there are no dynamic imports or identifiers in the subtree
|
|
if !ast.IsSourceFile(node) && node.SubtreeFacts()&(ast.SubtreeContainsDynamicImport|ast.SubtreeContainsIdentifier) == 0 {
|
|
return node
|
|
}
|
|
|
|
switch node.Kind {
|
|
case ast.KindSourceFile:
|
|
node = tx.visitSourceFile(node.AsSourceFile())
|
|
case ast.KindForStatement:
|
|
node = tx.visitForStatement(node.AsForStatement())
|
|
case ast.KindForInStatement, ast.KindForOfStatement:
|
|
node = tx.visitForInOrOfStatement(node.AsForInOrOfStatement())
|
|
case ast.KindExpressionStatement:
|
|
node = tx.visitExpressionStatement(node.AsExpressionStatement())
|
|
case ast.KindVoidExpression:
|
|
node = tx.visitVoidExpression(node.AsVoidExpression())
|
|
case ast.KindParenthesizedExpression:
|
|
node = tx.visitParenthesizedExpression(node.AsParenthesizedExpression(), resultIsDiscarded)
|
|
case ast.KindPartiallyEmittedExpression:
|
|
node = tx.visitPartiallyEmittedExpression(node.AsPartiallyEmittedExpression(), resultIsDiscarded)
|
|
case ast.KindCallExpression:
|
|
node = tx.visitCallExpression(node.AsCallExpression())
|
|
case ast.KindTaggedTemplateExpression:
|
|
node = tx.visitTaggedTemplateExpression(node.AsTaggedTemplateExpression())
|
|
case ast.KindBinaryExpression:
|
|
node = tx.visitBinaryExpression(node.AsBinaryExpression(), resultIsDiscarded)
|
|
case ast.KindPrefixUnaryExpression:
|
|
node = tx.visitPrefixUnaryExpression(node.AsPrefixUnaryExpression(), resultIsDiscarded)
|
|
case ast.KindPostfixUnaryExpression:
|
|
node = tx.visitPostfixUnaryExpression(node.AsPostfixUnaryExpression(), resultIsDiscarded)
|
|
case ast.KindShorthandPropertyAssignment:
|
|
node = tx.visitShorthandPropertyAssignment(node.AsShorthandPropertyAssignment())
|
|
case ast.KindIdentifier:
|
|
node = tx.visitIdentifier(node)
|
|
default:
|
|
node = tx.Visitor().VisitEachChild(node)
|
|
}
|
|
|
|
return node
|
|
}
|
|
|
|
// Visits source elements whose value is discarded if they are expressions.
|
|
func (tx *CommonJSModuleTransformer) visitDiscardedValue(node *ast.Node) *ast.Node {
|
|
grandparentNode := tx.pushNode(node)
|
|
defer tx.popNode(grandparentNode)
|
|
|
|
return tx.visitNoStack(node, true /*resultIsDiscarded*/)
|
|
}
|
|
|
|
func (tx *CommonJSModuleTransformer) visitAssignmentPattern(node *ast.Node) *ast.Node {
|
|
grandparentNode := tx.pushNode(node)
|
|
defer tx.popNode(grandparentNode)
|
|
|
|
return tx.visitAssignmentPatternNoStack(node)
|
|
}
|
|
|
|
func (tx *CommonJSModuleTransformer) visitAssignmentPatternNoStack(node *ast.Node) *ast.Node {
|
|
switch node.Kind {
|
|
// AssignmentPattern
|
|
case ast.KindObjectLiteralExpression, ast.KindArrayLiteralExpression:
|
|
node = tx.assignmentPatternVisitor.VisitEachChild(node)
|
|
|
|
// AssignmentProperty
|
|
case ast.KindPropertyAssignment:
|
|
node = tx.visitAssignmentProperty(node.AsPropertyAssignment())
|
|
case ast.KindShorthandPropertyAssignment:
|
|
node = tx.visitShorthandAssignmentProperty(node.AsShorthandPropertyAssignment())
|
|
|
|
// AssignmentRestProperty
|
|
case ast.KindSpreadAssignment:
|
|
node = tx.visitAssignmentRestProperty(node.AsSpreadAssignment())
|
|
|
|
// AssignmentRestElement
|
|
case ast.KindSpreadElement:
|
|
node = tx.visitAssignmentRestElement(node.AsSpreadElement())
|
|
|
|
// AssignmentElement
|
|
default:
|
|
if ast.IsExpression(node) {
|
|
node = tx.visitAssignmentElement(node)
|
|
break
|
|
}
|
|
|
|
node = tx.visitNoStack(node, false /*resultIsDiscarded*/)
|
|
}
|
|
return node
|
|
}
|
|
|
|
func (tx *CommonJSModuleTransformer) visitSourceFile(node *ast.SourceFile) *ast.Node {
|
|
if node.IsDeclarationFile ||
|
|
!(ast.IsEffectiveExternalModule(node, tx.compilerOptions) ||
|
|
node.SubtreeFacts()&ast.SubtreeContainsDynamicImport != 0) {
|
|
return node.AsNode()
|
|
}
|
|
|
|
tx.currentSourceFile = node
|
|
tx.currentModuleInfo = collectExternalModuleInfo(node, tx.compilerOptions, tx.EmitContext(), tx.resolver)
|
|
updated := tx.transformCommonJSModule(node)
|
|
tx.currentSourceFile = nil
|
|
tx.currentModuleInfo = nil
|
|
return updated
|
|
}
|
|
|
|
func (tx *CommonJSModuleTransformer) shouldEmitUnderscoreUnderscoreESModule() bool {
|
|
if tspath.FileExtensionIsOneOf(tx.currentSourceFile.FileName(), tspath.SupportedJSExtensionsFlat) &&
|
|
tx.currentSourceFile.CommonJSModuleIndicator != nil &&
|
|
(tx.currentSourceFile.ExternalModuleIndicator == nil /*|| tx.currentSourceFile.ExternalModuleIndicator == true*/) { // !!!
|
|
return false
|
|
}
|
|
if tx.currentModuleInfo.exportEquals == nil && ast.IsExternalModule(tx.currentSourceFile) {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (tx *CommonJSModuleTransformer) createUnderscoreUnderscoreESModule() *ast.Statement {
|
|
statement := tx.Factory().NewExpressionStatement(
|
|
tx.Factory().NewCallExpression(
|
|
tx.Factory().NewPropertyAccessExpression(
|
|
tx.Factory().NewIdentifier("Object"),
|
|
nil, /*questionDotToken*/
|
|
tx.Factory().NewIdentifier("defineProperty"),
|
|
ast.NodeFlagsNone,
|
|
),
|
|
nil, /*questionDotToken*/
|
|
nil, /*typeArguments*/
|
|
tx.Factory().NewNodeList([]*ast.Node{
|
|
tx.Factory().NewIdentifier("exports"),
|
|
tx.Factory().NewStringLiteral("__esModule"),
|
|
tx.Factory().NewObjectLiteralExpression(
|
|
tx.Factory().NewNodeList([]*ast.Node{
|
|
tx.Factory().NewPropertyAssignment(
|
|
nil, /*modifiers*/
|
|
tx.Factory().NewIdentifier("value"),
|
|
nil, /*postfixToken*/
|
|
nil, /*typeNode*/
|
|
tx.Factory().NewTrueExpression(),
|
|
),
|
|
}),
|
|
false, /*multiLine*/
|
|
),
|
|
}),
|
|
ast.NodeFlagsNone,
|
|
),
|
|
)
|
|
tx.EmitContext().SetEmitFlags(statement, printer.EFCustomPrologue)
|
|
return statement
|
|
}
|
|
|
|
func (tx *CommonJSModuleTransformer) transformCommonJSModule(node *ast.SourceFile) *ast.Node {
|
|
tx.EmitContext().StartVariableEnvironment()
|
|
|
|
// emit standard prologue directives (e.g. "use strict")
|
|
prologue, rest := tx.Factory().SplitStandardPrologue(node.Statements.Nodes)
|
|
statements := slices.Clone(prologue)
|
|
|
|
// ensure "use strict" if not present
|
|
if ast.IsExternalModule(tx.currentSourceFile) ||
|
|
tx.compilerOptions.AlwaysStrict.DefaultIfUnknown(tx.compilerOptions.Strict).IsTrue() {
|
|
statements = tx.Factory().EnsureUseStrict(statements)
|
|
}
|
|
|
|
// emit custom prologues from other transformations
|
|
custom, rest := tx.Factory().SplitCustomPrologue(rest)
|
|
statements = append(statements, core.FirstResult(tx.topLevelVisitor.VisitSlice(custom))...)
|
|
|
|
// emits `Object.defineProperty(exports, "__esModule", { value: true });` at the top of the file
|
|
if tx.shouldEmitUnderscoreUnderscoreESModule() {
|
|
statements = append(statements, tx.createUnderscoreUnderscoreESModule())
|
|
}
|
|
|
|
// initialize all exports to `undefined`, e.g.:
|
|
// exports.a = exports.b = void 0;
|
|
if len(tx.currentModuleInfo.exportedNames) > 0 {
|
|
const chunkSize = 50
|
|
l := len(tx.currentModuleInfo.exportedNames)
|
|
for i := 0; i < l; i += chunkSize {
|
|
right := tx.Factory().NewVoidZeroExpression()
|
|
for _, nextId := range tx.currentModuleInfo.exportedNames[i:min(i+chunkSize, l)] {
|
|
var left *ast.Expression
|
|
if nextId.Kind == ast.KindStringLiteral {
|
|
left = tx.Factory().NewElementAccessExpression(
|
|
tx.Factory().NewIdentifier("exports"),
|
|
nil, /*questionDotToken*/
|
|
tx.Factory().NewStringLiteralFromNode(nextId),
|
|
ast.NodeFlagsNone,
|
|
)
|
|
} else {
|
|
name := nextId.Clone(tx.Factory())
|
|
tx.EmitContext().SetEmitFlags(name, printer.EFNoSourceMap) // TODO: Strada emits comments here, but shouldn't
|
|
left = tx.Factory().NewPropertyAccessExpression(
|
|
tx.Factory().NewIdentifier("exports"),
|
|
nil, /*questionDotToken*/
|
|
name,
|
|
ast.NodeFlagsNone,
|
|
)
|
|
}
|
|
right = tx.Factory().NewAssignmentExpression(left, right)
|
|
}
|
|
statement := tx.Factory().NewExpressionStatement(right)
|
|
tx.EmitContext().AddEmitFlags(statement, printer.EFCustomPrologue)
|
|
statements = append(statements, statement)
|
|
}
|
|
}
|
|
|
|
// initialize exports for function declarations, e.g.:
|
|
// exports.f = f;
|
|
// function f() {}
|
|
for f := range tx.currentModuleInfo.exportedFunctions.Values() {
|
|
statements = tx.appendExportsOfClassOrFunctionDeclaration(statements, f.AsNode())
|
|
}
|
|
|
|
// visit the remaining statements in the source file
|
|
rest, _ = tx.topLevelVisitor.VisitSlice(rest)
|
|
statements = append(statements, rest...)
|
|
|
|
// emit `module.exports = ...` if needd
|
|
statements = tx.appendExportEqualsIfNeeded(statements)
|
|
|
|
// merge temp variables into the statement list
|
|
statements = tx.EmitContext().EndAndMergeVariableEnvironment(statements)
|
|
|
|
statementList := tx.Factory().NewNodeList(statements)
|
|
statementList.Loc = node.Statements.Loc
|
|
result := tx.Factory().UpdateSourceFile(node, statementList, node.EndOfFileToken).AsSourceFile()
|
|
tx.EmitContext().AddEmitHelper(result.AsNode(), tx.EmitContext().ReadEmitHelpers()...)
|
|
|
|
externalHelpersImportDeclaration := createExternalHelpersImportDeclarationIfNeeded(tx.EmitContext(), result, tx.compilerOptions, tx.getEmitModuleFormatOfFile(node), false /*hasExportStarsToExportValues*/, false /*hasImportStar*/, false /*hasImportDefault*/)
|
|
if externalHelpersImportDeclaration != nil {
|
|
prologue, rest := tx.Factory().SplitStandardPrologue(result.Statements.Nodes)
|
|
custom, rest := tx.Factory().SplitCustomPrologue(rest)
|
|
statements := slices.Clone(prologue)
|
|
statements = append(statements, custom...)
|
|
statements = append(statements, tx.topLevelVisitor.VisitNode(externalHelpersImportDeclaration))
|
|
statements = append(statements, rest...)
|
|
statementList := tx.Factory().NewNodeList(statements)
|
|
statementList.Loc = result.Statements.Loc
|
|
result = tx.Factory().UpdateSourceFile(result, statementList, node.EndOfFileToken).AsSourceFile()
|
|
}
|
|
|
|
return result.AsNode()
|
|
}
|
|
|
|
// Adds the down-level representation of `export=` to the statement list if one exists in the source file.
|
|
//
|
|
// - The `statements` parameter is a statement list to which the down-level export statements are to be appended.
|
|
func (tx *CommonJSModuleTransformer) appendExportEqualsIfNeeded(statements []*ast.Statement) []*ast.Statement {
|
|
if tx.currentModuleInfo.exportEquals != nil {
|
|
expressionResult := tx.Visitor().VisitNode(tx.currentModuleInfo.exportEquals.Expression)
|
|
if expressionResult != nil {
|
|
statement := tx.Factory().NewExpressionStatement(
|
|
tx.Factory().NewAssignmentExpression(
|
|
tx.Factory().NewPropertyAccessExpression(
|
|
tx.Factory().NewIdentifier("module"),
|
|
nil, /*questionDotToken*/
|
|
tx.Factory().NewIdentifier("exports"),
|
|
ast.NodeFlagsNone,
|
|
),
|
|
expressionResult,
|
|
),
|
|
)
|
|
|
|
tx.EmitContext().AssignCommentAndSourceMapRanges(statement, tx.currentModuleInfo.exportEquals.AsNode())
|
|
tx.EmitContext().AddEmitFlags(statement, printer.EFNoComments)
|
|
statements = append(statements, statement)
|
|
}
|
|
}
|
|
return statements
|
|
}
|
|
|
|
// Appends the exports of an ImportDeclaration to a statement list, returning the statement list.
|
|
//
|
|
// - The `statements` parameter is a statement list to which the down-level export statements are to be appended.
|
|
// - The `decl` parameter is the declaration whose exports are to be recorded.
|
|
func (tx *CommonJSModuleTransformer) appendExportsOfImportDeclaration(statements []*ast.Statement, decl *ast.ImportDeclaration) []*ast.Statement {
|
|
if tx.currentModuleInfo.exportEquals != nil {
|
|
return statements
|
|
}
|
|
|
|
importClause := decl.ImportClause
|
|
if importClause == nil {
|
|
return statements
|
|
}
|
|
|
|
seen := &collections.Set[string]{}
|
|
if importClause.Name() != nil {
|
|
statements = tx.appendExportsOfDeclaration(statements, importClause, seen, false /*liveBinding*/)
|
|
}
|
|
|
|
namedBindings := importClause.AsImportClause().NamedBindings
|
|
if namedBindings != nil {
|
|
switch namedBindings.Kind {
|
|
case ast.KindNamespaceImport:
|
|
statements = tx.appendExportsOfDeclaration(statements, namedBindings, seen, false /*liveBinding*/)
|
|
|
|
case ast.KindNamedImports:
|
|
for _, importBinding := range namedBindings.AsNamedImports().Elements.Nodes {
|
|
statements = tx.appendExportsOfDeclaration(statements, importBinding, seen, true /*liveBinding*/)
|
|
}
|
|
}
|
|
}
|
|
|
|
return statements
|
|
}
|
|
|
|
// Appends the exports of a VariableStatement to a statement list, returning the statement list.
|
|
//
|
|
// - The `statements` parameter is a statement list to which the down-level export statements are to be appended.
|
|
// - The `node` parameter is the VariableStatement whose exports are to be recorded.
|
|
func (tx *CommonJSModuleTransformer) appendExportsOfVariableStatement(statements []*ast.Statement, node *ast.VariableStatement) []*ast.Statement {
|
|
return tx.appendExportsOfVariableDeclarationList(statements, node.DeclarationList.AsVariableDeclarationList() /*isForInOrOfInitializer*/, false)
|
|
}
|
|
|
|
// Appends the exports of a VariableDeclarationList to a statement list, returning the statement list.
|
|
//
|
|
// - The `statements` parameter is a statement list to which the down-level export statements are to be appended.
|
|
// - The `node` parameter is the VariableDeclarationList whose exports are to be recorded.
|
|
func (tx *CommonJSModuleTransformer) appendExportsOfVariableDeclarationList(statements []*ast.Statement, node *ast.VariableDeclarationList, isForInOrOfInitializer bool) []*ast.Statement {
|
|
if tx.currentModuleInfo.exportEquals != nil {
|
|
return statements
|
|
}
|
|
|
|
for _, decl := range node.Declarations.Nodes {
|
|
statements = tx.appendExportsOfBindingElement(statements, decl, isForInOrOfInitializer)
|
|
}
|
|
|
|
return statements
|
|
}
|
|
|
|
// Appends the exports of a VariableDeclaration or BindingElement to a statement list, returning the statement list.
|
|
//
|
|
// - The `statements` parameter is a statement list to which the down-level export statements are to be appended.
|
|
// - The `decl` parameter is the declaration whose exports are to be recorded.
|
|
func (tx *CommonJSModuleTransformer) appendExportsOfBindingElement(statements []*ast.Statement, decl *ast.Node /*VariableDeclaration | BindingElement*/, isForInOrOfInitializer bool) []*ast.Statement {
|
|
if tx.currentModuleInfo.exportEquals != nil || decl.Name() == nil {
|
|
return statements
|
|
}
|
|
|
|
if ast.IsBindingPattern(decl.Name()) {
|
|
for _, element := range decl.Name().AsBindingPattern().Elements.Nodes {
|
|
e := element.AsBindingElement()
|
|
if e.DotDotDotToken == nil && e.Name() == nil {
|
|
statements = tx.appendExportsOfBindingElement(statements, element, isForInOrOfInitializer)
|
|
}
|
|
}
|
|
} else if !transformers.IsGeneratedIdentifier(tx.EmitContext(), decl.Name()) &&
|
|
(!ast.IsVariableDeclaration(decl) || decl.Initializer() != nil || isForInOrOfInitializer) {
|
|
statements = tx.appendExportsOfDeclaration(statements, decl, nil /*seen*/, false /*liveBinding*/)
|
|
}
|
|
|
|
return statements
|
|
}
|
|
|
|
// Appends the exports of a ClassDeclaration or FunctionDeclaration to a statement list, returning the statement list.
|
|
//
|
|
// - The `statements` parameter is a statement list to which the down-level export statements are to be appended.
|
|
// - The `decl` parameter is the declaration whose exports are to be recorded.
|
|
func (tx *CommonJSModuleTransformer) appendExportsOfClassOrFunctionDeclaration(statements []*ast.Statement, decl *ast.Declaration) []*ast.Statement {
|
|
if tx.currentModuleInfo.exportEquals != nil {
|
|
return statements
|
|
}
|
|
|
|
seen := &collections.Set[string]{}
|
|
if ast.HasSyntacticModifier(decl, ast.ModifierFlagsExport) {
|
|
var exportName *ast.IdentifierNode
|
|
if ast.HasSyntacticModifier(decl, ast.ModifierFlagsDefault) {
|
|
exportName = tx.Factory().NewIdentifier("default")
|
|
} else {
|
|
exportName = tx.Factory().GetDeclarationName(decl)
|
|
}
|
|
|
|
exportValue := tx.Factory().GetLocalName(decl)
|
|
statements = tx.appendExportStatement(statements, seen, exportName, exportValue, &decl.Loc, false /*allowComments*/, false /*liveBinding*/)
|
|
}
|
|
|
|
if decl.Name() != nil {
|
|
return tx.appendExportsOfDeclaration(statements, decl, seen, false /*liveBinding*/)
|
|
}
|
|
|
|
return statements
|
|
}
|
|
|
|
// Appends the exports of a declaration to a statement list, returning the statement list.
|
|
//
|
|
// - The `statements` parameter is a statement list to which the down-level export statements are to be appended.
|
|
// - The `decl` parameter is the declaration to export.
|
|
func (tx *CommonJSModuleTransformer) appendExportsOfDeclaration(statements []*ast.Statement, decl *ast.Declaration, seen *collections.Set[string], liveBinding bool) []*ast.Statement {
|
|
if tx.currentModuleInfo.exportEquals != nil {
|
|
return statements
|
|
}
|
|
|
|
if seen == nil {
|
|
seen = &collections.Set[string]{}
|
|
}
|
|
|
|
if name := decl.Name(); tx.currentModuleInfo.exportSpecifiers.Len() > 0 && name != nil && ast.IsIdentifier(name) {
|
|
name = tx.Factory().GetDeclarationName(decl)
|
|
exportSpecifiers := tx.currentModuleInfo.exportSpecifiers.Get(name.Text())
|
|
if len(exportSpecifiers) > 0 {
|
|
exportValue := tx.visitExpressionIdentifier(name)
|
|
for _, exportSpecifier := range exportSpecifiers {
|
|
statements = tx.appendExportStatement(statements, seen, exportSpecifier.Name(), exportValue, &exportSpecifier.Name().Loc /*location*/, false /*allowComments*/, liveBinding)
|
|
}
|
|
}
|
|
}
|
|
|
|
return statements
|
|
}
|
|
|
|
// Appends the down-level representation of an export to a statement list, returning the statement list.
|
|
//
|
|
// - The `statements` parameter is a statement list to which the down-level export statements are to be appended.
|
|
// - The `exportName` parameter is the name of the export.
|
|
// - The `expression` parameter is the expression to export.
|
|
// - The `location` parameter is the location to use for source maps and comments for the export.
|
|
// - The `allowComments` parameter indicates whether to allow comments on the export.
|
|
func (tx *CommonJSModuleTransformer) appendExportStatement(statements []*ast.Statement, seen *collections.Set[string], exportName *ast.ModuleExportName, expression *ast.Expression, location *core.TextRange, allowComments bool, liveBinding bool) []*ast.Statement {
|
|
if exportName.Kind != ast.KindStringLiteral {
|
|
if seen.Has(exportName.Text()) {
|
|
return statements
|
|
}
|
|
seen.Add(exportName.Text())
|
|
}
|
|
statements = append(statements, tx.createExportStatement(exportName, expression, location, allowComments, liveBinding))
|
|
return statements
|
|
}
|
|
|
|
// Creates a call to the current file's export function to export a value.
|
|
//
|
|
// - The `name` parameter is the bound name of the export.
|
|
// - The `value` parameter is the exported value.
|
|
// - The `location` parameter is the location to use for source maps and comments for the export.
|
|
// - The `allowComments` parameter indicates whether to emit comments for the statement.
|
|
func (tx *CommonJSModuleTransformer) createExportStatement(name *ast.ModuleExportName, value *ast.Expression, location *core.TextRange, allowComments bool, liveBinding bool) *ast.Statement {
|
|
statement := tx.Factory().NewExpressionStatement(tx.createExportExpression(name, value, nil /*location*/, liveBinding))
|
|
if location != nil {
|
|
tx.EmitContext().SetCommentRange(statement, *location)
|
|
}
|
|
tx.EmitContext().AddEmitFlags(statement, printer.EFStartOnNewLine)
|
|
if !allowComments {
|
|
tx.EmitContext().AddEmitFlags(statement, printer.EFNoComments)
|
|
}
|
|
return statement
|
|
}
|
|
|
|
// Creates a call to the current file's export function to export a value.
|
|
//
|
|
// - The `name` parameter is the bound name of the export.
|
|
// - The `value` parameter is the exported value.
|
|
// - The `location` parameter is the location to use for source maps and comments for the export.
|
|
func (tx *CommonJSModuleTransformer) createExportExpression(name *ast.ModuleExportName, value *ast.Expression, location *core.TextRange, liveBinding bool) *ast.Expression {
|
|
var expression *ast.Expression
|
|
if liveBinding {
|
|
// For a live binding we emit a getter on `exports` that returns the value:
|
|
// Object.defineProperty(exports, "<name>", { enumerable: true, get: function () { return <value>; } });
|
|
expression = tx.Factory().NewCallExpression(
|
|
tx.Factory().NewPropertyAccessExpression(
|
|
tx.Factory().NewIdentifier("Object"),
|
|
nil, /*questionDotToken*/
|
|
tx.Factory().NewIdentifier("defineProperty"),
|
|
ast.NodeFlagsNone,
|
|
),
|
|
nil, /*questionDotToken*/
|
|
nil, /*typeArguments*/
|
|
tx.Factory().NewNodeList([]*ast.Node{
|
|
tx.Factory().NewIdentifier("exports"),
|
|
tx.Factory().NewStringLiteralFromNode(name),
|
|
tx.Factory().NewObjectLiteralExpression(
|
|
tx.Factory().NewNodeList([]*ast.Node{
|
|
tx.Factory().NewPropertyAssignment(
|
|
nil, /*modifiers*/
|
|
tx.Factory().NewIdentifier("enumerable"),
|
|
nil, /*postfixToken*/
|
|
nil, /*typeNode*/
|
|
tx.Factory().NewTrueExpression(),
|
|
),
|
|
tx.Factory().NewPropertyAssignment(
|
|
nil, /*modifiers*/
|
|
tx.Factory().NewIdentifier("get"),
|
|
nil, /*postfixToken*/
|
|
nil, /*typeNode*/
|
|
tx.Factory().NewFunctionExpression(
|
|
nil, /*modifiers*/
|
|
nil, /*asteriskToken*/
|
|
nil, /*name*/
|
|
nil, /*typeParameters*/
|
|
tx.Factory().NewNodeList([]*ast.Node{}),
|
|
nil, /*type*/
|
|
nil, /*fullSignature*/
|
|
tx.Factory().NewBlock(
|
|
tx.Factory().NewNodeList([]*ast.Node{
|
|
tx.Factory().NewReturnStatement(value),
|
|
}),
|
|
false, /*multiLine*/
|
|
),
|
|
),
|
|
),
|
|
}),
|
|
false, /*multiLine*/
|
|
),
|
|
}),
|
|
ast.NodeFlagsNone,
|
|
)
|
|
} else {
|
|
// Otherwise, we emit a simple property assignment.
|
|
var left *ast.Expression
|
|
if name.Kind == ast.KindStringLiteral {
|
|
// emits:
|
|
// exports["<name>"] = <value>;
|
|
left = tx.Factory().NewElementAccessExpression(
|
|
tx.Factory().NewIdentifier("exports"),
|
|
nil, /*questionDotToken*/
|
|
tx.Factory().NewStringLiteralFromNode(name),
|
|
ast.NodeFlagsNone,
|
|
)
|
|
} else {
|
|
// emits:
|
|
// exports.<name> = <value>;
|
|
left = tx.Factory().NewPropertyAccessExpression(
|
|
tx.Factory().NewIdentifier("exports"),
|
|
nil, /*questionDotToken*/
|
|
name.Clone(tx.Factory()),
|
|
ast.NodeFlagsNone,
|
|
)
|
|
}
|
|
expression = tx.Factory().NewAssignmentExpression(left, value)
|
|
}
|
|
if location != nil {
|
|
tx.EmitContext().SetCommentRange(expression, *location)
|
|
}
|
|
return expression
|
|
}
|
|
|
|
// Creates a `require()` call to import an external module.
|
|
func (tx *CommonJSModuleTransformer) createRequireCall(node *ast.Node /*ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration*/) *ast.Node {
|
|
var args []*ast.Expression
|
|
moduleName := getExternalModuleNameLiteral(tx.Factory(), node, tx.currentSourceFile, nil /*host*/, nil /*resolver*/, tx.compilerOptions)
|
|
if moduleName != nil {
|
|
args = append(args, rewriteModuleSpecifier(tx.EmitContext(), moduleName, tx.compilerOptions))
|
|
}
|
|
return tx.Factory().NewCallExpression(
|
|
tx.Factory().NewIdentifier("require"),
|
|
nil, /*questionDotToken*/
|
|
nil, /*typeArguments*/
|
|
tx.Factory().NewNodeList(args),
|
|
ast.NodeFlagsNone)
|
|
}
|
|
|
|
func (tx *CommonJSModuleTransformer) getHelperExpressionForExport(node *ast.ExportDeclaration, innerExpr *ast.Expression) *ast.Expression {
|
|
if !tx.compilerOptions.GetESModuleInterop() || tx.EmitContext().EmitFlags(node.AsNode())&printer.EFNeverApplyImportHelper != 0 {
|
|
return innerExpr
|
|
}
|
|
if getExportNeedsImportStarHelper(node) {
|
|
return tx.Visitor().VisitNode(tx.Factory().NewImportStarHelper(innerExpr))
|
|
}
|
|
return innerExpr
|
|
}
|
|
|
|
func (tx *CommonJSModuleTransformer) getHelperExpressionForImport(node *ast.ImportDeclaration, innerExpr *ast.Expression) *ast.Expression {
|
|
if !tx.compilerOptions.GetESModuleInterop() || tx.EmitContext().EmitFlags(node.AsNode())&printer.EFNeverApplyImportHelper != 0 {
|
|
return innerExpr
|
|
}
|
|
if getImportNeedsImportStarHelper(node) {
|
|
return tx.Visitor().VisitNode(tx.Factory().NewImportStarHelper(innerExpr))
|
|
}
|
|
if getImportNeedsImportDefaultHelper(node) {
|
|
return tx.Visitor().VisitNode(tx.Factory().NewImportDefaultHelper(innerExpr))
|
|
}
|
|
return innerExpr
|
|
}
|
|
|
|
func (tx *CommonJSModuleTransformer) visitTopLevelImportDeclaration(node *ast.ImportDeclaration) *ast.Node {
|
|
if node.ImportClause == nil {
|
|
// import "mod";
|
|
statement := tx.Factory().NewExpressionStatement(tx.createRequireCall(node.AsNode()))
|
|
tx.EmitContext().SetOriginal(statement, node.AsNode())
|
|
tx.EmitContext().AssignCommentAndSourceMapRanges(statement, node.AsNode())
|
|
return statement
|
|
}
|
|
|
|
var statements []*ast.Statement
|
|
var variables []*ast.VariableDeclarationNode
|
|
namespaceDeclaration := ast.GetNamespaceDeclarationNode(node.AsNode())
|
|
if namespaceDeclaration != nil && !ast.IsDefaultImport(node.AsNode()) {
|
|
// import * as n from "mod";
|
|
variables = append(variables,
|
|
tx.Factory().NewVariableDeclaration(
|
|
namespaceDeclaration.Name().Clone(tx.Factory()),
|
|
nil, /*exclamationToken*/
|
|
nil, /*type*/
|
|
tx.getHelperExpressionForImport(node, tx.createRequireCall(node.AsNode())),
|
|
),
|
|
)
|
|
} else {
|
|
// import d from "mod";
|
|
// import { x, y } from "mod";
|
|
// import d, { x, y } from "mod";
|
|
// import d, * as n from "mod";
|
|
variables = append(variables,
|
|
tx.Factory().NewVariableDeclaration(
|
|
tx.Factory().NewGeneratedNameForNode(node.AsNode()),
|
|
nil, /*exclamationToken*/
|
|
nil, /*type*/
|
|
tx.getHelperExpressionForImport(node, tx.createRequireCall(node.AsNode())),
|
|
),
|
|
)
|
|
|
|
if namespaceDeclaration != nil && ast.IsDefaultImport(node.AsNode()) {
|
|
variables = append(variables,
|
|
tx.Factory().NewVariableDeclaration(
|
|
namespaceDeclaration.Name().Clone(tx.Factory()),
|
|
nil, /*exclamationToken*/
|
|
nil, /*type*/
|
|
tx.Factory().NewGeneratedNameForNode(node.AsNode()),
|
|
),
|
|
)
|
|
}
|
|
}
|
|
|
|
varStatement := tx.Factory().NewVariableStatement(
|
|
nil, /*modifiers*/
|
|
tx.Factory().NewVariableDeclarationList(
|
|
ast.NodeFlagsConst,
|
|
tx.Factory().NewNodeList(variables),
|
|
),
|
|
)
|
|
|
|
tx.EmitContext().SetOriginal(varStatement, node.AsNode())
|
|
tx.EmitContext().AssignCommentAndSourceMapRanges(varStatement, node.AsNode())
|
|
statements = append(statements, varStatement)
|
|
statements = tx.appendExportsOfImportDeclaration(statements, node)
|
|
return transformers.SingleOrMany(statements, tx.Factory())
|
|
}
|
|
|
|
func (tx *CommonJSModuleTransformer) visitTopLevelImportEqualsDeclaration(node *ast.ImportEqualsDeclaration) *ast.Node {
|
|
if !ast.IsExternalModuleImportEqualsDeclaration(node.AsNode()) {
|
|
// import m = n;
|
|
panic("import= for internal module references should be handled in an earlier transformer.")
|
|
}
|
|
|
|
var statements []*ast.Statement
|
|
if ast.HasSyntacticModifier(node.AsNode(), ast.ModifierFlagsExport) {
|
|
// export import m = require("mod");
|
|
statement := tx.Factory().NewExpressionStatement(
|
|
tx.createExportExpression(
|
|
node.Name(),
|
|
tx.createRequireCall(node.AsNode()),
|
|
&node.Loc,
|
|
false, /*liveBinding*/
|
|
),
|
|
)
|
|
|
|
tx.EmitContext().SetOriginal(statement, node.AsNode())
|
|
tx.EmitContext().AssignCommentAndSourceMapRanges(statement, node.AsNode())
|
|
statements = append(statements, statement)
|
|
} else {
|
|
// import m = require("mod");
|
|
statement := tx.Factory().NewVariableStatement(
|
|
nil, /*modifiers*/
|
|
tx.Factory().NewVariableDeclarationList(
|
|
ast.NodeFlagsConst,
|
|
tx.Factory().NewNodeList([]*ast.VariableDeclarationNode{
|
|
tx.Factory().NewVariableDeclaration(
|
|
node.Name().Clone(tx.Factory()),
|
|
nil, /*exclamationToken*/
|
|
nil, /*typeNode*/
|
|
tx.createRequireCall(node.AsNode()),
|
|
),
|
|
}),
|
|
),
|
|
)
|
|
tx.EmitContext().SetOriginal(statement, node.AsNode())
|
|
tx.EmitContext().AssignCommentAndSourceMapRanges(statement, node.AsNode())
|
|
statements = append(statements, statement)
|
|
}
|
|
|
|
statements = tx.appendExportsOfDeclaration(statements, node.AsNode(), nil /*seen*/, false /*liveBinding*/)
|
|
return transformers.SingleOrMany(statements, tx.Factory())
|
|
}
|
|
|
|
func (tx *CommonJSModuleTransformer) visitTopLevelExportDeclaration(node *ast.ExportDeclaration) *ast.Node {
|
|
if node.ModuleSpecifier == nil {
|
|
// Elide export declarations with no module specifier as they are handled
|
|
// elsewhere.
|
|
return nil
|
|
}
|
|
|
|
generatedName := tx.Factory().NewGeneratedNameForNode(node.AsNode())
|
|
if node.ExportClause != nil && ast.IsNamedExports(node.ExportClause) {
|
|
// export { x, y } from "mod";
|
|
var statements []*ast.Statement
|
|
varStatement := tx.Factory().NewVariableStatement(
|
|
nil, /*modifiers*/
|
|
tx.Factory().NewVariableDeclarationList(
|
|
ast.NodeFlagsConst,
|
|
tx.Factory().NewNodeList([]*ast.VariableDeclarationNode{
|
|
tx.Factory().NewVariableDeclaration(
|
|
generatedName,
|
|
nil, /*exclamationToken*/
|
|
nil, /*type*/
|
|
tx.createRequireCall(node.AsNode()),
|
|
),
|
|
}),
|
|
),
|
|
)
|
|
tx.EmitContext().SetOriginal(varStatement, node.AsNode())
|
|
tx.EmitContext().AssignCommentAndSourceMapRanges(varStatement, node.AsNode())
|
|
statements = append(statements, varStatement)
|
|
|
|
for _, specifier := range node.ExportClause.AsNamedExports().Elements.Nodes {
|
|
specifierName := specifier.PropertyNameOrName()
|
|
exportNeedsImportDefault := tx.compilerOptions.GetESModuleInterop() &&
|
|
tx.EmitContext().EmitFlags(node.AsNode())&printer.EFNeverApplyImportHelper == 0 &&
|
|
ast.ModuleExportNameIsDefault(specifierName)
|
|
|
|
var target *ast.Node
|
|
if exportNeedsImportDefault {
|
|
target = tx.Factory().NewImportDefaultHelper(generatedName)
|
|
} else {
|
|
target = generatedName
|
|
}
|
|
|
|
var exportName *ast.Node
|
|
if ast.IsStringLiteral(specifier.Name()) {
|
|
exportName = tx.Factory().NewStringLiteralFromNode(specifier.Name())
|
|
} else {
|
|
exportName = tx.Factory().GetExportName(specifier.AsNode())
|
|
}
|
|
|
|
var exportedValue *ast.Node
|
|
if ast.IsStringLiteral(specifierName) {
|
|
exportedValue = tx.Factory().NewElementAccessExpression(target, nil /*questionDotToken*/, specifierName, ast.NodeFlagsNone)
|
|
} else {
|
|
exportedValue = tx.Factory().NewPropertyAccessExpression(target, nil /*questionDotToken*/, specifierName, ast.NodeFlagsNone)
|
|
}
|
|
statement := tx.Factory().NewExpressionStatement(
|
|
tx.createExportExpression(
|
|
exportName,
|
|
exportedValue,
|
|
nil, /*location*/
|
|
true, /*liveBinding*/
|
|
),
|
|
)
|
|
tx.EmitContext().SetOriginal(statement, specifier.AsNode())
|
|
tx.EmitContext().AssignCommentAndSourceMapRanges(statement, specifier.AsNode())
|
|
statements = append(statements, statement)
|
|
}
|
|
|
|
return transformers.SingleOrMany(statements, tx.Factory())
|
|
}
|
|
|
|
if node.ExportClause != nil {
|
|
// export * as ns from "mod";
|
|
// export * as default from "mod";
|
|
var exportName *ast.Node
|
|
if ast.IsStringLiteral(node.ExportClause.Name()) {
|
|
exportName = tx.Factory().NewStringLiteralFromNode(node.ExportClause.Name())
|
|
} else {
|
|
exportName = node.ExportClause.Name().Clone(tx.Factory())
|
|
}
|
|
statement := tx.Factory().NewExpressionStatement(
|
|
tx.createExportExpression(
|
|
exportName,
|
|
tx.getHelperExpressionForExport(
|
|
node,
|
|
tx.createRequireCall(node.AsNode()),
|
|
),
|
|
nil, /*location*/
|
|
false, /*liveBinding*/
|
|
),
|
|
)
|
|
tx.EmitContext().SetOriginal(statement, node.AsNode())
|
|
tx.EmitContext().AssignCommentAndSourceMapRanges(statement, node.AsNode())
|
|
return statement
|
|
}
|
|
|
|
// export * from "mod";
|
|
statement := tx.Factory().NewExpressionStatement(
|
|
tx.Visitor().VisitNode(tx.Factory().NewExportStarHelper(tx.createRequireCall(node.AsNode()), tx.Factory().NewIdentifier("exports"))),
|
|
)
|
|
tx.EmitContext().SetOriginal(statement, node.AsNode())
|
|
tx.EmitContext().AssignCommentAndSourceMapRanges(statement, node.AsNode())
|
|
return statement
|
|
}
|
|
|
|
func (tx *CommonJSModuleTransformer) visitTopLevelExportAssignment(node *ast.ExportAssignment) *ast.Node {
|
|
if node.IsExportEquals {
|
|
return nil
|
|
}
|
|
|
|
return tx.createExportStatement(
|
|
tx.Factory().NewIdentifier("default"),
|
|
tx.Visitor().VisitNode(node.Expression),
|
|
&node.Loc, /*location*/
|
|
true, /*allowComments*/
|
|
false, /*liveBinding*/
|
|
)
|
|
}
|
|
|
|
func (tx *CommonJSModuleTransformer) visitTopLevelFunctionDeclaration(node *ast.FunctionDeclaration) *ast.Node {
|
|
if ast.HasSyntacticModifier(node.AsNode(), ast.ModifierFlagsExport) {
|
|
return tx.Factory().UpdateFunctionDeclaration(
|
|
node,
|
|
transformers.ExtractModifiers(tx.EmitContext(), node.Modifiers(), ^ast.ModifierFlagsExportDefault),
|
|
node.AsteriskToken,
|
|
tx.Factory().GetDeclarationName(node.AsNode()),
|
|
nil, /*typeParameters*/
|
|
tx.Visitor().VisitNodes(node.Parameters),
|
|
nil, /*type*/
|
|
nil, /*fullSignature*/
|
|
tx.Visitor().VisitNode(node.Body),
|
|
)
|
|
} else {
|
|
return tx.Visitor().VisitEachChild(node.AsNode())
|
|
}
|
|
}
|
|
|
|
func (tx *CommonJSModuleTransformer) visitTopLevelClassDeclaration(node *ast.ClassDeclaration) *ast.Node {
|
|
var statements []*ast.Statement
|
|
if ast.HasSyntacticModifier(node.AsNode(), ast.ModifierFlagsExport) {
|
|
statements = append(statements, tx.Factory().UpdateClassDeclaration(
|
|
node,
|
|
tx.Visitor().VisitModifiers(transformers.ExtractModifiers(tx.EmitContext(), node.Modifiers(), ^ast.ModifierFlagsExportDefault)),
|
|
tx.Factory().GetDeclarationName(node.AsNode()),
|
|
nil, /*typeParameters*/
|
|
tx.Visitor().VisitNodes(node.HeritageClauses),
|
|
tx.Visitor().VisitNodes(node.Members),
|
|
))
|
|
} else {
|
|
statements = append(statements, tx.Visitor().VisitEachChild(node.AsNode()))
|
|
}
|
|
statements = tx.appendExportsOfClassOrFunctionDeclaration(statements, node.AsNode())
|
|
return transformers.SingleOrMany(statements, tx.Factory())
|
|
}
|
|
|
|
func (tx *CommonJSModuleTransformer) visitTopLevelVariableStatement(node *ast.VariableStatement) *ast.Node {
|
|
var statements []*ast.Statement
|
|
if ast.HasSyntacticModifier(node.AsNode(), ast.ModifierFlagsExport) {
|
|
// export var a = b;
|
|
var variables []*ast.VariableDeclarationNode
|
|
var expressions []*ast.Expression
|
|
var modifiers *ast.ModifierList
|
|
|
|
commitPendingVariables := func() {
|
|
if len(variables) > 0 {
|
|
variableList := tx.Factory().NewNodeList(variables)
|
|
variableList.Loc = node.DeclarationList.AsVariableDeclarationList().Declarations.Loc
|
|
statement := tx.Factory().UpdateVariableStatement(
|
|
node,
|
|
modifiers,
|
|
tx.Factory().UpdateVariableDeclarationList(
|
|
node.DeclarationList.AsVariableDeclarationList(),
|
|
variableList,
|
|
),
|
|
)
|
|
if len(statements) > 0 {
|
|
tx.EmitContext().AddEmitFlags(statement, printer.EFNoComments)
|
|
}
|
|
statements = append(statements, statement)
|
|
variables = nil
|
|
}
|
|
}
|
|
|
|
commitPendingExpressions := func() {
|
|
if len(expressions) > 0 {
|
|
statement := tx.Factory().NewExpressionStatement(tx.Factory().InlineExpressions(expressions))
|
|
tx.EmitContext().AssignCommentAndSourceMapRanges(statement, node.AsNode())
|
|
if len(statements) > 0 {
|
|
tx.EmitContext().AddEmitFlags(statement, printer.EFNoComments)
|
|
}
|
|
statements = append(statements, statement)
|
|
expressions = nil
|
|
}
|
|
}
|
|
|
|
pushVariable := func(variable *ast.VariableDeclarationNode) {
|
|
commitPendingExpressions()
|
|
variables = append(variables, variable)
|
|
}
|
|
|
|
pushExpression := func(expression *ast.Expression) {
|
|
commitPendingVariables()
|
|
expressions = append(expressions, expression)
|
|
}
|
|
|
|
// If we're exporting these variables, then these just become assignments to 'exports.x'.
|
|
for _, variable := range node.DeclarationList.AsVariableDeclarationList().Declarations.Nodes {
|
|
v := variable.AsVariableDeclaration()
|
|
|
|
if ast.IsIdentifier(v.Name()) && transformers.IsLocalName(tx.EmitContext(), v.Name()) {
|
|
// A "local name" generally means a variable declaration that *shouldn't* be
|
|
// converted to `exports.x = ...`, even if the declaration is exported. This
|
|
// usually indicates a class or function declaration that was converted into
|
|
// a variable declaration, as most references to the declaration will remain
|
|
// untransformed (i.e., `new C` rather than `new exports.C`). In these cases,
|
|
// an `export { x }` declaration will follow.
|
|
|
|
if modifiers == nil {
|
|
modifiers = transformers.ExtractModifiers(tx.EmitContext(), node.Modifiers(), ^ast.ModifierFlagsExportDefault)
|
|
}
|
|
|
|
if v.Initializer != nil {
|
|
variable = tx.Factory().UpdateVariableDeclaration(
|
|
v,
|
|
v.Name(),
|
|
nil, /*exclamationToken*/
|
|
nil, /*type*/
|
|
tx.createExportExpression(
|
|
v.Name(),
|
|
tx.Visitor().VisitNode(v.Initializer),
|
|
nil,
|
|
false, /*liveBinding*/
|
|
),
|
|
)
|
|
}
|
|
|
|
pushVariable(variable)
|
|
} else if v.Initializer != nil && !ast.IsBindingPattern(v.Name()) && (ast.IsArrowFunction(v.Initializer) || (ast.IsFunctionExpression(v.Initializer) || ast.IsClassExpression(v.Initializer)) && v.Initializer.Name() == nil) {
|
|
// preserve variable declarations for functions and classes to assign names
|
|
|
|
pushVariable(tx.Factory().NewVariableDeclaration(
|
|
v.Name(),
|
|
v.ExclamationToken,
|
|
v.Type,
|
|
tx.Visitor().VisitNode(v.Initializer),
|
|
))
|
|
|
|
propertyAccess := tx.Factory().NewPropertyAccessExpression(
|
|
tx.Factory().NewIdentifier("exports"),
|
|
nil, /*questionDotToken*/
|
|
v.Name(),
|
|
ast.NodeFlagsNone,
|
|
)
|
|
tx.EmitContext().AssignCommentAndSourceMapRanges(propertyAccess, v.Name())
|
|
|
|
pushExpression(tx.Factory().NewAssignmentExpression(
|
|
propertyAccess,
|
|
v.Name().Clone(tx.Factory()),
|
|
))
|
|
} else {
|
|
expression := transformers.ConvertVariableDeclarationToAssignmentExpression(tx.EmitContext(), v)
|
|
if expression != nil {
|
|
pushExpression(tx.Visitor().VisitNode(expression))
|
|
}
|
|
}
|
|
}
|
|
|
|
commitPendingVariables()
|
|
commitPendingExpressions()
|
|
statements = tx.appendExportsOfVariableStatement(statements, node)
|
|
return transformers.SingleOrMany(statements, tx.Factory())
|
|
}
|
|
return tx.visitTopLevelNestedVariableStatement(node)
|
|
}
|
|
|
|
// Visits a top-level nested variable statement as it may contain `var` declarations that are hoisted and may still be
|
|
// exported with `export {}`.
|
|
func (tx *CommonJSModuleTransformer) visitTopLevelNestedVariableStatement(node *ast.VariableStatement) *ast.Node {
|
|
var statements []*ast.Statement
|
|
statements = append(statements, tx.Visitor().VisitEachChild(node.AsNode()))
|
|
statements = tx.appendExportsOfVariableStatement(statements, node)
|
|
return transformers.SingleOrMany(statements, tx.Factory())
|
|
}
|
|
|
|
// Visits a top-level nested `for` statement as it may contain `var` declarations that are hoisted and may still be
|
|
// exported with `export {}`.
|
|
func (tx *CommonJSModuleTransformer) visitTopLevelNestedForStatement(node *ast.ForStatement) *ast.Node {
|
|
if node.Initializer != nil && ast.IsVariableDeclarationList(node.Initializer) && node.Initializer.Flags&ast.NodeFlagsBlockScoped == 0 {
|
|
exportStatements := tx.appendExportsOfVariableDeclarationList(nil /*statements*/, node.Initializer.AsVariableDeclarationList(), false /*isForInOrOfInitializer*/)
|
|
if len(exportStatements) > 0 {
|
|
// given:
|
|
// export { x }
|
|
// for (var x = 0; ;) { }
|
|
// emits:
|
|
// var x = 0;
|
|
// exports.x = x;
|
|
// for (; ;) { }
|
|
|
|
var statements []*ast.Statement
|
|
varDeclList := tx.discardedValueVisitor.VisitNode(node.Initializer)
|
|
varStatement := tx.Factory().NewVariableStatement(nil /*modifiers*/, varDeclList)
|
|
statements = append(statements, varStatement)
|
|
statements = append(statements, exportStatements...)
|
|
|
|
condition := tx.Visitor().VisitNode(node.Condition)
|
|
incrementor := tx.discardedValueVisitor.VisitNode(node.Incrementor)
|
|
body := tx.EmitContext().VisitIterationBody(node.Statement, tx.topLevelNestedVisitor)
|
|
statements = append(statements, tx.Factory().UpdateForStatement(
|
|
node,
|
|
nil, /*initializer*/
|
|
condition,
|
|
incrementor,
|
|
body,
|
|
))
|
|
return transformers.SingleOrMany(statements, tx.Factory())
|
|
}
|
|
}
|
|
return tx.Factory().UpdateForStatement(
|
|
node,
|
|
tx.discardedValueVisitor.VisitNode(node.Initializer),
|
|
tx.Visitor().VisitNode(node.Condition),
|
|
tx.discardedValueVisitor.VisitNode(node.Incrementor),
|
|
tx.EmitContext().VisitIterationBody(node.Statement, tx.topLevelNestedVisitor),
|
|
)
|
|
}
|
|
|
|
// Visits a top-level nested `for..in` or `for..of` statement as it may contain `var` declarations that are hoisted and
|
|
// may still be exported with `export {}`.
|
|
func (tx *CommonJSModuleTransformer) visitTopLevelNestedForInOrOfStatement(node *ast.ForInOrOfStatement) *ast.Node {
|
|
if ast.IsVariableDeclarationList(node.Initializer) && node.Initializer.Flags&ast.NodeFlagsBlockScoped == 0 {
|
|
exportStatements := tx.appendExportsOfVariableDeclarationList(nil /*statements*/, node.Initializer.AsVariableDeclarationList(), true /*isForInOrOfInitializer*/)
|
|
if len(exportStatements) > 0 {
|
|
// given:
|
|
// export { x }
|
|
// for (var x in y) {
|
|
// ...
|
|
// }
|
|
// emits:
|
|
// for (var x in y) {
|
|
// exports.x = x;
|
|
// ...
|
|
// }
|
|
|
|
initializer := tx.discardedValueVisitor.VisitNode(node.Initializer)
|
|
expression := tx.Visitor().VisitNode(node.Expression)
|
|
body := tx.EmitContext().VisitIterationBody(node.Statement, tx.topLevelNestedVisitor)
|
|
if ast.IsBlock(body) {
|
|
block := body.AsBlock()
|
|
bodyStatements := append(exportStatements, block.Statements.Nodes...)
|
|
bodyStatementList := tx.Factory().NewNodeList(bodyStatements)
|
|
bodyStatementList.Loc = block.Statements.Loc
|
|
body = tx.Factory().UpdateBlock(block, bodyStatementList)
|
|
} else {
|
|
bodyStatements := append(exportStatements, body)
|
|
body = tx.Factory().NewBlock(tx.Factory().NewNodeList(bodyStatements), true /*multiLine*/)
|
|
}
|
|
return tx.Factory().UpdateForInOrOfStatement(node, node.AwaitModifier, initializer, expression, body)
|
|
}
|
|
}
|
|
return tx.Factory().UpdateForInOrOfStatement(
|
|
node,
|
|
node.AwaitModifier,
|
|
tx.discardedValueVisitor.VisitNode(node.Initializer),
|
|
tx.Visitor().VisitNode(node.Expression),
|
|
tx.EmitContext().VisitIterationBody(node.Statement, tx.topLevelNestedVisitor),
|
|
)
|
|
}
|
|
|
|
// Visits a top-level nested `do` statement as it may contain `var` declarations that are hoisted and may still be
|
|
// exported with `export {}`.
|
|
func (tx *CommonJSModuleTransformer) visitTopLevelNestedDoStatement(node *ast.DoStatement) *ast.Node {
|
|
return tx.Factory().UpdateDoStatement(
|
|
node,
|
|
tx.EmitContext().VisitIterationBody(node.Statement, tx.topLevelNestedVisitor),
|
|
tx.Visitor().VisitNode(node.Expression),
|
|
)
|
|
}
|
|
|
|
// Visits a top-level nested `while` statement as it may contain `var` declarations that are hoisted and may still be
|
|
// exported with `export {}`.
|
|
func (tx *CommonJSModuleTransformer) visitTopLevelNestedWhileStatement(node *ast.WhileStatement) *ast.Node {
|
|
return tx.Factory().UpdateWhileStatement(
|
|
node,
|
|
tx.Visitor().VisitNode(node.Expression),
|
|
tx.EmitContext().VisitIterationBody(node.Statement, tx.topLevelNestedVisitor),
|
|
)
|
|
}
|
|
|
|
// Visits a top-level nested labeled statement as it may contain `var` declarations that are hoisted and may still be
|
|
// exported with `export {}`.
|
|
func (tx *CommonJSModuleTransformer) visitTopLevelNestedLabeledStatement(node *ast.LabeledStatement) *ast.Node {
|
|
return tx.Factory().UpdateLabeledStatement(
|
|
node,
|
|
node.Label,
|
|
tx.topLevelNestedVisitor.VisitEmbeddedStatement(node.Statement),
|
|
)
|
|
}
|
|
|
|
// Visits a top-level nested `with` statement as it may contain `var` declarations that are hoisted and may still be
|
|
// exported with `export {}`.
|
|
func (tx *CommonJSModuleTransformer) visitTopLevelNestedWithStatement(node *ast.WithStatement) *ast.Node {
|
|
return tx.Factory().UpdateWithStatement(
|
|
node,
|
|
tx.Visitor().VisitNode(node.Expression),
|
|
tx.topLevelNestedVisitor.VisitEmbeddedStatement(node.Statement),
|
|
)
|
|
}
|
|
|
|
// Visits a top-level nested `if` statement as it may contain `var` declarations that are hoisted and may still be
|
|
// exported with `export {}`.
|
|
func (tx *CommonJSModuleTransformer) visitTopLevelNestedIfStatement(node *ast.IfStatement) *ast.Node {
|
|
return tx.Factory().UpdateIfStatement(
|
|
node,
|
|
tx.Visitor().VisitNode(node.Expression),
|
|
tx.topLevelNestedVisitor.VisitEmbeddedStatement(node.ThenStatement),
|
|
tx.topLevelNestedVisitor.VisitEmbeddedStatement(node.ElseStatement),
|
|
)
|
|
}
|
|
|
|
// Visits a top-level nested `switch` statement as it may contain `var` declarations that are hoisted and may still be
|
|
// exported with `export {}`.
|
|
func (tx *CommonJSModuleTransformer) visitTopLevelNestedSwitchStatement(node *ast.SwitchStatement) *ast.Node {
|
|
return tx.Factory().UpdateSwitchStatement(
|
|
node,
|
|
tx.Visitor().VisitNode(node.Expression),
|
|
tx.topLevelNestedVisitor.VisitNode(node.CaseBlock),
|
|
)
|
|
}
|
|
|
|
// Visits a top-level nested case block as it may contain `var` declarations that are hoisted and may still be exported
|
|
// with `export {}`.
|
|
func (tx *CommonJSModuleTransformer) visitTopLevelNestedCaseBlock(node *ast.CaseBlock) *ast.Node {
|
|
return tx.topLevelNestedVisitor.VisitEachChild(node.AsNode())
|
|
}
|
|
|
|
// Visits a top-level nested `case` or `default` clause as it may contain `var` declarations that are hoisted and may
|
|
// still be exported with `export {}`.
|
|
func (tx *CommonJSModuleTransformer) visitTopLevelNestedCaseOrDefaultClause(node *ast.CaseOrDefaultClause) *ast.Node {
|
|
return tx.Factory().UpdateCaseOrDefaultClause(
|
|
node,
|
|
tx.Visitor().VisitNode(node.Expression),
|
|
tx.topLevelNestedVisitor.VisitNodes(node.Statements),
|
|
)
|
|
}
|
|
|
|
// Visits a top-level nested `try` statement as it may contain `var` declarations that are hoisted and may still be
|
|
// exported with `export {}`.
|
|
func (tx *CommonJSModuleTransformer) visitTopLevelNestedTryStatement(node *ast.TryStatement) *ast.Node {
|
|
return tx.topLevelNestedVisitor.VisitEachChild(node.AsNode())
|
|
}
|
|
|
|
// Visits a top-level nested `catch` clause as it may contain `var` declarations that are hoisted and may still be
|
|
// exported with `export {}`.
|
|
func (tx *CommonJSModuleTransformer) visitTopLevelNestedCatchClause(node *ast.CatchClause) *ast.Node {
|
|
return tx.Factory().UpdateCatchClause(
|
|
node,
|
|
node.VariableDeclaration,
|
|
tx.topLevelNestedVisitor.VisitNode(node.Block),
|
|
)
|
|
}
|
|
|
|
// Visits a top-level nested block as it may contain `var` declarations that are hoisted and may still be exported with
|
|
// `export {}`.
|
|
func (tx *CommonJSModuleTransformer) visitTopLevelNestedBlock(node *ast.Block) *ast.Node {
|
|
return tx.topLevelNestedVisitor.VisitEachChild(node.AsNode())
|
|
}
|
|
|
|
func (tx *CommonJSModuleTransformer) visitForStatement(node *ast.ForStatement) *ast.Node {
|
|
return tx.Factory().UpdateForStatement(
|
|
node,
|
|
tx.discardedValueVisitor.VisitNode(node.Initializer),
|
|
tx.Visitor().VisitNode(node.Condition),
|
|
tx.discardedValueVisitor.VisitNode(node.Incrementor),
|
|
tx.EmitContext().VisitIterationBody(node.Statement, tx.topLevelNestedVisitor),
|
|
)
|
|
}
|
|
|
|
func (tx *CommonJSModuleTransformer) visitForInOrOfStatement(node *ast.ForInOrOfStatement) *ast.Node {
|
|
return tx.Factory().UpdateForInOrOfStatement(
|
|
node,
|
|
node.AwaitModifier,
|
|
tx.discardedValueVisitor.VisitNode(node.Initializer),
|
|
tx.Visitor().VisitNode(node.Expression),
|
|
tx.EmitContext().VisitIterationBody(node.Statement, tx.topLevelNestedVisitor),
|
|
)
|
|
}
|
|
|
|
// Visits an expression statement whose value will be discarded at runtime.
|
|
func (tx *CommonJSModuleTransformer) visitExpressionStatement(node *ast.ExpressionStatement) *ast.Node {
|
|
return tx.discardedValueVisitor.VisitEachChild(node.AsNode())
|
|
}
|
|
|
|
// Visits a `void` expression whose value will be discarded at runtime.
|
|
func (tx *CommonJSModuleTransformer) visitVoidExpression(node *ast.VoidExpression) *ast.Node {
|
|
return tx.discardedValueVisitor.VisitEachChild(node.AsNode())
|
|
}
|
|
|
|
// Visits a parenthesized expression whose value may be discarded at runtime.
|
|
func (tx *CommonJSModuleTransformer) visitParenthesizedExpression(node *ast.ParenthesizedExpression, resultIsDiscarded bool) *ast.Node {
|
|
expression := core.IfElse(resultIsDiscarded, tx.discardedValueVisitor, tx.Visitor()).VisitNode(node.Expression)
|
|
return tx.Factory().UpdateParenthesizedExpression(node, expression)
|
|
}
|
|
|
|
// Visits a partially emitted expression whose value may be discarded at runtime.
|
|
func (tx *CommonJSModuleTransformer) visitPartiallyEmittedExpression(node *ast.PartiallyEmittedExpression, resultIsDiscarded bool) *ast.Node {
|
|
expression := core.IfElse(resultIsDiscarded, tx.discardedValueVisitor, tx.Visitor()).VisitNode(node.Expression)
|
|
return tx.Factory().UpdatePartiallyEmittedExpression(node, expression)
|
|
}
|
|
|
|
// Visits a binary expression whose value may be discarded, or which might contain an assignment to an exported
|
|
// identifier.
|
|
func (tx *CommonJSModuleTransformer) visitBinaryExpression(node *ast.BinaryExpression, resultIsDiscarded bool) *ast.Node {
|
|
if ast.IsAssignmentExpression(node.AsNode(), false /*excludeCompoundAssignment*/) {
|
|
return tx.visitAssignmentExpression(node)
|
|
}
|
|
|
|
if ast.IsCommaExpression(node.AsNode()) {
|
|
return tx.visitCommaExpression(node, resultIsDiscarded)
|
|
}
|
|
|
|
return tx.Visitor().VisitEachChild(node.AsNode())
|
|
}
|
|
|
|
func (tx *CommonJSModuleTransformer) visitAssignmentExpression(node *ast.BinaryExpression) *ast.Node {
|
|
if ast.IsDestructuringAssignment(node.AsNode()) {
|
|
return tx.visitDestructuringAssignment(node)
|
|
}
|
|
|
|
// When we see an assignment expression whose left-hand side is an exported symbol,
|
|
// we should ensure all exports of that symbol are updated with the correct value.
|
|
//
|
|
// - We do not transform generated identifiers unless they are file-level reserved names.
|
|
// - We do not transform identifiers tagged with the LocalName flag.
|
|
// - We only transform identifiers that are exported at the top level.
|
|
if ast.IsIdentifier(node.Left) &&
|
|
(!transformers.IsGeneratedIdentifier(tx.EmitContext(), node.Left) || isFileLevelReservedGeneratedIdentifier(tx.EmitContext(), node.Left)) &&
|
|
!transformers.IsLocalName(tx.EmitContext(), node.Left) {
|
|
exportedNames := tx.getExports(node.Left)
|
|
if len(exportedNames) > 0 {
|
|
// For each additional export of the declaration, apply an export assignment.
|
|
expression := tx.Visitor().VisitEachChild(node.AsNode())
|
|
for _, exportName := range exportedNames {
|
|
expression = tx.createExportExpression(exportName, expression, &node.Loc /*location*/, false /*liveBinding*/)
|
|
}
|
|
return expression
|
|
}
|
|
}
|
|
|
|
return tx.Visitor().VisitEachChild(node.AsNode())
|
|
}
|
|
|
|
// Visits a destructuring assignment which might target an exported identifier.
|
|
func (tx *CommonJSModuleTransformer) visitDestructuringAssignment(node *ast.BinaryExpression) *ast.Node {
|
|
return tx.Factory().UpdateBinaryExpression(
|
|
node,
|
|
nil, /*modifiers*/
|
|
tx.assignmentPatternVisitor.VisitNode(node.Left),
|
|
nil, /*typeNode*/
|
|
node.OperatorToken,
|
|
tx.Visitor().VisitNode(node.Right),
|
|
)
|
|
}
|
|
|
|
func (tx *CommonJSModuleTransformer) visitAssignmentProperty(node *ast.PropertyAssignment) *ast.Node {
|
|
return tx.Factory().UpdatePropertyAssignment(
|
|
node,
|
|
nil, /*modifiers*/
|
|
tx.Visitor().VisitNode(node.Name()),
|
|
nil, /*postfixToken*/
|
|
nil, /*typeNode*/
|
|
tx.assignmentPatternVisitor.VisitNode(node.Initializer),
|
|
)
|
|
}
|
|
|
|
func (tx *CommonJSModuleTransformer) visitShorthandAssignmentProperty(node *ast.ShorthandPropertyAssignment) *ast.Node {
|
|
target := tx.visitDestructuringAssignmentTargetNoStack(node.Name())
|
|
if ast.IsIdentifier(target) {
|
|
return tx.Factory().UpdateShorthandPropertyAssignment(
|
|
node,
|
|
nil, /*modifiers*/
|
|
target,
|
|
nil, /*postfixToken*/
|
|
nil, /*typeNode*/
|
|
node.EqualsToken,
|
|
tx.Visitor().VisitNode(node.ObjectAssignmentInitializer),
|
|
)
|
|
}
|
|
if node.ObjectAssignmentInitializer != nil {
|
|
equalsToken := node.EqualsToken
|
|
if equalsToken == nil {
|
|
equalsToken = tx.Factory().NewToken(ast.KindEqualsToken)
|
|
}
|
|
target = tx.Factory().NewBinaryExpression(
|
|
nil, /*modifiers*/
|
|
target,
|
|
nil, /*typeNode*/
|
|
equalsToken,
|
|
tx.Visitor().VisitNode(node.ObjectAssignmentInitializer),
|
|
)
|
|
}
|
|
updated := tx.Factory().NewPropertyAssignment(
|
|
nil, /*modifiers*/
|
|
node.Name(),
|
|
nil, /*postfixToken*/
|
|
nil, /*typeNode*/
|
|
target,
|
|
)
|
|
tx.EmitContext().SetOriginal(updated, node.AsNode())
|
|
tx.EmitContext().AssignCommentAndSourceMapRanges(updated, node.AsNode())
|
|
return updated
|
|
}
|
|
|
|
func (tx *CommonJSModuleTransformer) visitAssignmentRestProperty(node *ast.SpreadAssignment) *ast.Node {
|
|
return tx.Factory().UpdateSpreadAssignment(
|
|
node,
|
|
tx.visitDestructuringAssignmentTarget(node.Expression),
|
|
)
|
|
}
|
|
|
|
func (tx *CommonJSModuleTransformer) visitAssignmentRestElement(node *ast.SpreadElement) *ast.Node {
|
|
return tx.Factory().UpdateSpreadElement(
|
|
node,
|
|
tx.visitDestructuringAssignmentTarget(node.Expression),
|
|
)
|
|
}
|
|
|
|
func (tx *CommonJSModuleTransformer) visitAssignmentElement(node *ast.Node) *ast.Node {
|
|
if ast.IsBinaryExpression(node) {
|
|
n := node.AsBinaryExpression()
|
|
if n.OperatorToken.Kind == ast.KindEqualsToken {
|
|
return tx.Factory().UpdateBinaryExpression(
|
|
n,
|
|
nil, /*modifiers*/
|
|
tx.visitDestructuringAssignmentTarget(n.Left),
|
|
nil, /*typeNode*/
|
|
n.OperatorToken,
|
|
tx.Visitor().VisitNode(n.Right),
|
|
)
|
|
}
|
|
}
|
|
|
|
return tx.visitDestructuringAssignmentTargetNoStack(node)
|
|
}
|
|
|
|
func (tx *CommonJSModuleTransformer) visitDestructuringAssignmentTarget(node *ast.Node) *ast.Node {
|
|
grandparentNode := tx.pushNode(node)
|
|
defer tx.popNode(grandparentNode)
|
|
|
|
switch node.Kind {
|
|
case ast.KindObjectLiteralExpression, ast.KindArrayLiteralExpression:
|
|
node = tx.visitAssignmentPatternNoStack(node)
|
|
default:
|
|
node = tx.visitDestructuringAssignmentTargetNoStack(node)
|
|
}
|
|
return node
|
|
}
|
|
|
|
func (tx *CommonJSModuleTransformer) visitDestructuringAssignmentTargetNoStack(node *ast.Node) *ast.Node {
|
|
if ast.IsIdentifier(node) &&
|
|
(!transformers.IsGeneratedIdentifier(tx.EmitContext(), node) || isFileLevelReservedGeneratedIdentifier(tx.EmitContext(), node)) &&
|
|
!transformers.IsLocalName(tx.EmitContext(), node) {
|
|
expression := tx.visitExpressionIdentifier(node)
|
|
exportedNames := tx.getExports(node)
|
|
if len(exportedNames) > 0 {
|
|
// transforms:
|
|
// var x;
|
|
// export { x }
|
|
// { x: x } = y
|
|
// to:
|
|
// { x: { set value(v) { exports.x = x = v; } }.value } = y
|
|
|
|
value := tx.Factory().NewUniqueNameEx("value", printer.AutoGenerateOptions{
|
|
Flags: printer.GeneratedIdentifierFlagsOptimistic,
|
|
})
|
|
expression = tx.Factory().NewAssignmentExpression(expression, value)
|
|
|
|
for _, exportName := range exportedNames {
|
|
expression = tx.createExportExpression(exportName, expression, nil /*location*/, false /*liveBinding*/)
|
|
}
|
|
|
|
statement := tx.Factory().NewExpressionStatement(expression)
|
|
statementList := tx.Factory().NewNodeList([]*ast.Node{statement})
|
|
param := tx.Factory().NewParameterDeclaration(
|
|
nil, /*modifiers*/
|
|
nil, /*dotDotDotToken*/
|
|
value,
|
|
nil, /*questionToken*/
|
|
nil, /*type*/
|
|
nil, /*initializer*/
|
|
)
|
|
valueSetter := tx.Factory().NewSetAccessorDeclaration(
|
|
nil, /*modifiers*/
|
|
tx.Factory().NewIdentifier("value"),
|
|
nil, /*typeParameters*/
|
|
tx.Factory().NewNodeList([]*ast.Node{param}),
|
|
nil, /*returnType*/
|
|
nil, /*fullSignature*/
|
|
tx.Factory().NewBlock(statementList, false /*multiLine*/),
|
|
)
|
|
propertyList := tx.Factory().NewNodeList([]*ast.Node{valueSetter})
|
|
expression = tx.Factory().NewObjectLiteralExpression(propertyList, false /*multiLine*/)
|
|
expression = tx.Factory().NewPropertyAccessExpression(expression, nil /*questionDotToken*/, tx.Factory().NewIdentifier("value"), ast.NodeFlagsNone)
|
|
}
|
|
return expression
|
|
}
|
|
|
|
return tx.visitNoStack(node, false /*resultIsDiscarded*/)
|
|
}
|
|
|
|
// Visits a comma expression whose left-hand value is always discard, and whose right-hand value may be discarded at runtime.
|
|
func (tx *CommonJSModuleTransformer) visitCommaExpression(node *ast.BinaryExpression, resultIsDiscarded bool) *ast.Node {
|
|
left := tx.discardedValueVisitor.VisitNode(node.Left)
|
|
right := core.IfElse(resultIsDiscarded, tx.discardedValueVisitor, tx.Visitor()).VisitNode(node.Right)
|
|
return tx.Factory().UpdateBinaryExpression(node, nil /*modifiers*/, left, nil /*typeNode*/, node.OperatorToken, right)
|
|
}
|
|
|
|
// Visits a prefix unary expression that might modify an exported identifier.
|
|
func (tx *CommonJSModuleTransformer) visitPrefixUnaryExpression(node *ast.PrefixUnaryExpression, resultIsDiscarded bool) *ast.Node {
|
|
// When we see a prefix increment expression whose operand is an exported
|
|
// symbol, we should ensure all exports of that symbol are updated with the correct
|
|
// value.
|
|
//
|
|
// - We do not transform generated identifiers for any reason.
|
|
// - We do not transform identifiers tagged with the LocalName flag.
|
|
// - We do not transform identifiers that were originally the name of an enum or
|
|
// namespace due to how they are transformed in TypeScript.
|
|
// - We only transform identifiers that are exported at the top level.
|
|
if (node.Operator == ast.KindPlusPlusToken || node.Operator == ast.KindMinusMinusToken) &&
|
|
ast.IsIdentifier(node.Operand) &&
|
|
!transformers.IsLocalName(tx.EmitContext(), node.Operand) {
|
|
exportedNames := tx.getExports(node.Operand)
|
|
if len(exportedNames) > 0 {
|
|
// given:
|
|
// var x = 0;
|
|
// export { x }
|
|
// ++x;
|
|
// emits:
|
|
// var x = 0;
|
|
// exports.x = x;
|
|
// exports.x = ++x;
|
|
// note:
|
|
// after the operation, `exports.x` will hold the value of `x` after the increment.
|
|
|
|
expression := tx.Factory().UpdatePrefixUnaryExpression(node, tx.Visitor().VisitNode(node.Operand))
|
|
for _, exportName := range exportedNames {
|
|
expression = tx.createExportExpression(exportName, expression, nil /*location*/, false /*liveBinding*/)
|
|
tx.EmitContext().AssignCommentAndSourceMapRanges(expression, node.AsNode())
|
|
}
|
|
return expression
|
|
}
|
|
}
|
|
return tx.Visitor().VisitEachChild(node.AsNode())
|
|
}
|
|
|
|
// Visits a postfix unary expression that might modify an exported identifier.
|
|
func (tx *CommonJSModuleTransformer) visitPostfixUnaryExpression(node *ast.PostfixUnaryExpression, resultIsDiscarded bool) *ast.Node {
|
|
// When we see a postfix increment expression whose operand is an exported
|
|
// symbol, we should ensure all exports of that symbol are updated with the correct
|
|
// value.
|
|
//
|
|
// - We do not transform generated identifiers for any reason.
|
|
// - We do not transform identifiers tagged with the LocalName flag.
|
|
// - We do not transform identifiers that were originally the name of an enum or
|
|
// namespace due to how they are transformed in TypeScript.
|
|
// - We only transform identifiers that are exported at the top level.
|
|
if (node.Operator == ast.KindPlusPlusToken || node.Operator == ast.KindMinusMinusToken) &&
|
|
ast.IsIdentifier(node.Operand) &&
|
|
!transformers.IsLocalName(tx.EmitContext(), node.Operand) {
|
|
exportedNames := tx.getExports(node.Operand)
|
|
if len(exportedNames) > 0 {
|
|
// given (value is discarded):
|
|
// var x = 0;
|
|
// export { x }
|
|
// x++;
|
|
// emits:
|
|
// var x = 0, y;
|
|
// exports.x = x;
|
|
// exports.x = (x++, x);
|
|
// note:
|
|
// after the operation, `exports.x` will hold the value of `x` after the increment.
|
|
//
|
|
// given (value is not discarded):
|
|
// var x = 0, y;
|
|
// export { x }
|
|
// y = x++;
|
|
// emits:
|
|
// var _a;
|
|
// var x = 0, y;
|
|
// exports.x = x;
|
|
// y = (exports.x = (_a = x++, x), _a);
|
|
// note:
|
|
// after the operation, `exports.x` will hold the value of `x` after the increment, while
|
|
// `y` will hold the value of `x` before the increment.
|
|
|
|
var temp *ast.IdentifierNode
|
|
expression := tx.Factory().UpdatePostfixUnaryExpression(node, tx.Visitor().VisitNode(node.Operand))
|
|
if !resultIsDiscarded {
|
|
temp = tx.Factory().NewTempVariable()
|
|
tx.EmitContext().AddVariableDeclaration(temp)
|
|
|
|
expression = tx.Factory().NewAssignmentExpression(temp, expression)
|
|
tx.EmitContext().AssignCommentAndSourceMapRanges(expression, node.AsNode())
|
|
}
|
|
|
|
expression = tx.Factory().NewCommaExpression(expression, node.Operand.Clone(tx.Factory()))
|
|
tx.EmitContext().AssignCommentAndSourceMapRanges(expression, node.AsNode())
|
|
|
|
for _, exportName := range exportedNames {
|
|
expression = tx.createExportExpression(exportName, expression, nil /*location*/, false /*liveBinding*/)
|
|
tx.EmitContext().AssignCommentAndSourceMapRanges(expression, node.AsNode())
|
|
}
|
|
|
|
if temp != nil {
|
|
expression = tx.Factory().NewCommaExpression(expression, temp.AsNode())
|
|
tx.EmitContext().AssignCommentAndSourceMapRanges(expression, node.AsNode())
|
|
}
|
|
|
|
return expression
|
|
}
|
|
}
|
|
|
|
return tx.Visitor().VisitEachChild(node.AsNode())
|
|
}
|
|
|
|
// Visits a call expression that might reference an imported symbol and thus require an indirect call, or that might
|
|
// be an `import()` or `require()` call that may need to be rewritten.
|
|
func (tx *CommonJSModuleTransformer) visitCallExpression(node *ast.CallExpression) *ast.Node {
|
|
needsRewrite := false
|
|
if tx.compilerOptions.RewriteRelativeImportExtensions.IsTrue() {
|
|
if ast.IsImportCall(node.AsNode()) && len(node.Arguments.Nodes) > 0 ||
|
|
ast.IsInJSFile(node.AsNode()) && ast.IsRequireCall(node.AsNode(), false /*requireStringLiteralLikeArgument*/) {
|
|
needsRewrite = true
|
|
}
|
|
}
|
|
if ast.IsImportCall(node.AsNode()) && tx.shouldTransformImportCall() {
|
|
return tx.visitImportCallExpression(node, needsRewrite)
|
|
}
|
|
if needsRewrite {
|
|
return tx.shimOrRewriteImportOrRequireCall(node.AsCallExpression())
|
|
}
|
|
if ast.IsIdentifier(node.Expression) &&
|
|
!transformers.IsGeneratedIdentifier(tx.EmitContext(), node.Expression) &&
|
|
!transformers.IsHelperName(tx.EmitContext(), node.Expression) {
|
|
// given:
|
|
// import { f } from "mod";
|
|
// f();
|
|
// emits:
|
|
// const mod_1 = require("mod");
|
|
// (0, mod_1.f)();
|
|
// note:
|
|
// the indirect call is applied by the printer by way of the `EFIndirectCall` emit flag.
|
|
expression := tx.visitExpressionIdentifier(node.Expression)
|
|
updated := tx.Factory().UpdateCallExpression(
|
|
node,
|
|
expression,
|
|
node.QuestionDotToken,
|
|
nil, /*typeArguments*/
|
|
tx.Visitor().VisitNodes(node.Arguments),
|
|
)
|
|
if !ast.IsIdentifier(expression) {
|
|
tx.EmitContext().AddEmitFlags(updated, printer.EFIndirectCall)
|
|
}
|
|
return updated
|
|
}
|
|
return tx.Visitor().VisitEachChild(node.AsNode())
|
|
}
|
|
|
|
func (tx *CommonJSModuleTransformer) shouldTransformImportCall() bool {
|
|
return ast.ShouldTransformImportCall(tx.currentSourceFile.FileName(), tx.compilerOptions, tx.getEmitModuleFormatOfFile(tx.currentSourceFile))
|
|
}
|
|
|
|
func (tx *CommonJSModuleTransformer) visitImportCallExpression(node *ast.CallExpression, rewriteOrShim bool) *ast.Node {
|
|
if tx.moduleKind == core.ModuleKindNone && tx.languageVersion >= core.ScriptTargetES2020 {
|
|
return tx.Visitor().VisitEachChild(node.AsNode())
|
|
}
|
|
|
|
externalModuleName := getExternalModuleNameLiteral(tx.Factory(), node.AsNode(), tx.currentSourceFile, nil /*host*/, nil /*resolver*/, tx.compilerOptions)
|
|
firstArgument := tx.Visitor().VisitNode(core.FirstOrNil(node.Arguments.Nodes))
|
|
|
|
// Only use the external module name if it differs from the first argument. This allows us to preserve the quote style of the argument on output.
|
|
var argument *ast.Expression
|
|
if externalModuleName != nil && (firstArgument == nil || !ast.IsStringLiteral(firstArgument) || firstArgument.Text() != externalModuleName.Text()) {
|
|
argument = externalModuleName
|
|
} else if firstArgument != nil && rewriteOrShim {
|
|
if ast.IsStringLiteral(firstArgument) {
|
|
argument = rewriteModuleSpecifier(tx.EmitContext(), firstArgument, tx.compilerOptions)
|
|
} else {
|
|
argument = tx.Factory().NewRewriteRelativeImportExtensionsHelper(firstArgument, tx.compilerOptions.Jsx == core.JsxEmitPreserve)
|
|
}
|
|
} else {
|
|
argument = firstArgument
|
|
}
|
|
return tx.createImportCallExpressionCommonJS(argument)
|
|
}
|
|
|
|
func (tx *CommonJSModuleTransformer) createImportCallExpressionCommonJS(arg *ast.Expression) *ast.Expression {
|
|
// import(x)
|
|
// emit as
|
|
// Promise.resolve(`${x}`).then((s) => require(s)) /*CommonJS Require*/
|
|
// We have to wrap require in then callback so that require is done in asynchronously
|
|
// if we simply do require in resolve callback in Promise constructor. We will execute the loading immediately
|
|
// If the arg is not inlineable, we have to evaluate and ToString() it in the current scope
|
|
// Otherwise, we inline it in require() so that it's statically analyzable
|
|
|
|
needSyncEval := arg != nil && !isSimpleInlineableExpression(arg)
|
|
|
|
var promiseResolveArguments []*ast.Expression
|
|
if needSyncEval {
|
|
promiseResolveArguments = []*ast.Expression{
|
|
tx.Factory().NewTemplateExpression(
|
|
tx.Factory().NewTemplateHead("", "", ast.TokenFlagsNone),
|
|
tx.Factory().NewNodeList([]*ast.TemplateSpanNode{
|
|
tx.Factory().NewTemplateSpan(arg, tx.Factory().NewTemplateTail("", "", ast.TokenFlagsNone)),
|
|
}),
|
|
),
|
|
}
|
|
}
|
|
promiseResolveCall := tx.Factory().NewCallExpression(
|
|
tx.Factory().NewPropertyAccessExpression(
|
|
tx.Factory().NewIdentifier("Promise"),
|
|
nil, /*questionDotToken*/
|
|
tx.Factory().NewIdentifier("resolve"),
|
|
ast.NodeFlagsNone,
|
|
),
|
|
nil, /*questionDotToken*/
|
|
nil, /*typeArguments*/
|
|
tx.Factory().NewNodeList(promiseResolveArguments),
|
|
ast.NodeFlagsNone,
|
|
)
|
|
|
|
var requireArguments []*ast.Expression
|
|
if needSyncEval {
|
|
requireArguments = []*ast.Expression{
|
|
tx.Factory().NewIdentifier("s"),
|
|
}
|
|
} else if arg != nil {
|
|
requireArguments = []*ast.Expression{arg}
|
|
}
|
|
|
|
requireCall := tx.Factory().NewCallExpression(
|
|
tx.Factory().NewIdentifier("require"),
|
|
nil, /*questionDotToken*/
|
|
nil, /*typeArguments*/
|
|
tx.Factory().NewNodeList(requireArguments),
|
|
ast.NodeFlagsNone,
|
|
)
|
|
|
|
if tx.compilerOptions.GetESModuleInterop() {
|
|
requireCall = tx.Factory().NewImportStarHelper(requireCall)
|
|
}
|
|
|
|
var parameters []*ast.ParameterDeclarationNode
|
|
if needSyncEval {
|
|
parameters = []*ast.ParameterDeclarationNode{
|
|
tx.Factory().NewParameterDeclaration(
|
|
nil, /*modifiers*/
|
|
nil, /*dotDotDotToken*/
|
|
tx.Factory().NewIdentifier("s"),
|
|
nil, /*questionToken*/
|
|
nil, /*type*/
|
|
nil, /*initializer*/
|
|
),
|
|
}
|
|
}
|
|
|
|
function := tx.Factory().NewArrowFunction(
|
|
nil, /*modifiers*/
|
|
nil, /*typeParameters*/
|
|
tx.Factory().NewNodeList(parameters),
|
|
nil, /*type*/
|
|
nil, /*fullSignature*/
|
|
tx.Factory().NewToken(ast.KindEqualsGreaterThanToken), /*equalsGreaterThanToken*/
|
|
requireCall,
|
|
)
|
|
|
|
downleveledImport := tx.Factory().NewCallExpression(
|
|
tx.Factory().NewPropertyAccessExpression(
|
|
promiseResolveCall,
|
|
nil, /*questionDotToken*/
|
|
tx.Factory().NewIdentifier("then"),
|
|
ast.NodeFlagsNone,
|
|
),
|
|
nil, /*questionDotToken*/
|
|
nil, /*typeArguments*/
|
|
tx.Factory().NewNodeList([]*ast.Expression{function}),
|
|
ast.NodeFlagsNone,
|
|
)
|
|
return downleveledImport
|
|
}
|
|
|
|
func (tx *CommonJSModuleTransformer) shimOrRewriteImportOrRequireCall(node *ast.CallExpression) *ast.Node {
|
|
expression := tx.Visitor().VisitNode(node.Expression)
|
|
argumentsList := node.Arguments
|
|
if len(node.Arguments.Nodes) > 0 {
|
|
firstArgument := node.Arguments.Nodes[0]
|
|
firstArgumentChanged := false
|
|
if ast.IsStringLiteralLike(firstArgument) {
|
|
rewritten := rewriteModuleSpecifier(tx.EmitContext(), firstArgument, tx.compilerOptions)
|
|
firstArgumentChanged = rewritten != firstArgument
|
|
firstArgument = rewritten
|
|
} else {
|
|
firstArgument = tx.Factory().NewRewriteRelativeImportExtensionsHelper(firstArgument, tx.compilerOptions.Jsx == core.JsxEmitPreserve)
|
|
firstArgumentChanged = true
|
|
}
|
|
|
|
rest, restChanged := tx.Visitor().VisitSlice(node.Arguments.Nodes[1:])
|
|
if firstArgumentChanged || restChanged {
|
|
arguments := append([]*ast.Expression{firstArgument}, rest...)
|
|
argumentsList = tx.Factory().NewNodeList(arguments)
|
|
argumentsList.Loc = node.Arguments.Loc
|
|
}
|
|
}
|
|
|
|
return tx.Factory().UpdateCallExpression(
|
|
node,
|
|
expression,
|
|
node.QuestionDotToken,
|
|
nil, /*typeArguments*/
|
|
argumentsList,
|
|
)
|
|
}
|
|
|
|
// Visits a tagged template expression that might reference an imported symbol and thus require an indirect call.
|
|
func (tx *CommonJSModuleTransformer) visitTaggedTemplateExpression(node *ast.TaggedTemplateExpression) *ast.Node {
|
|
if ast.IsIdentifier(node.Tag) && !transformers.IsGeneratedIdentifier(tx.EmitContext(), node.Tag) && !transformers.IsHelperName(tx.EmitContext(), node.Tag) {
|
|
// given:
|
|
// import { f } from "mod";
|
|
// f``;
|
|
// emits:
|
|
// const mod_1 = require("mod");
|
|
// (0, mod_1.f) ``;
|
|
// note:
|
|
// the indirect call is applied by the printer by way of the `EFIndirectCall` emit flag.
|
|
|
|
expression := tx.visitExpressionIdentifier(node.Tag)
|
|
updated := tx.Factory().UpdateTaggedTemplateExpression(
|
|
node,
|
|
expression,
|
|
nil, /*questionDotToken*/
|
|
nil, /*typeArguments*/
|
|
tx.Visitor().VisitNode(node.Template),
|
|
)
|
|
if !ast.IsIdentifier(expression) {
|
|
tx.EmitContext().AddEmitFlags(updated, printer.EFIndirectCall)
|
|
}
|
|
return updated
|
|
}
|
|
return tx.Visitor().VisitEachChild(node.AsNode())
|
|
}
|
|
|
|
// Visits a shorthand property assignment that might reference an imported or exported symbol.
|
|
func (tx *CommonJSModuleTransformer) visitShorthandPropertyAssignment(node *ast.ShorthandPropertyAssignment) *ast.Node {
|
|
name := node.Name()
|
|
exportedOrImportedName := tx.visitExpressionIdentifier(name)
|
|
if exportedOrImportedName != name {
|
|
// A shorthand property with an assignment initializer is probably part of a
|
|
// destructuring assignment
|
|
expression := exportedOrImportedName
|
|
if node.ObjectAssignmentInitializer != nil {
|
|
expression = tx.Factory().NewAssignmentExpression(
|
|
expression,
|
|
tx.Visitor().VisitNode(node.ObjectAssignmentInitializer),
|
|
)
|
|
}
|
|
assignment := tx.Factory().NewPropertyAssignment(nil /*modifiers*/, name, nil /*postfixToken*/, nil /*typeNode*/, expression)
|
|
assignment.Loc = node.Loc
|
|
tx.EmitContext().AssignCommentAndSourceMapRanges(assignment, node.AsNode())
|
|
return assignment
|
|
}
|
|
return tx.Factory().UpdateShorthandPropertyAssignment(node,
|
|
nil, /*modifiers*/
|
|
exportedOrImportedName,
|
|
nil, /*postfixToken*/
|
|
nil, /*typeNode*/
|
|
node.EqualsToken,
|
|
tx.Visitor().VisitNode(node.ObjectAssignmentInitializer),
|
|
)
|
|
}
|
|
|
|
// Visits an identifier that, if it is in an expression position, might reference an imported or exported symbol.
|
|
func (tx *CommonJSModuleTransformer) visitIdentifier(node *ast.IdentifierNode) *ast.Node {
|
|
if transformers.IsIdentifierReference(node, tx.parentNode) {
|
|
return tx.visitExpressionIdentifier(node)
|
|
}
|
|
return node
|
|
}
|
|
|
|
// Visits an identifier in an expression position that might reference an imported or exported symbol.
|
|
func (tx *CommonJSModuleTransformer) visitExpressionIdentifier(node *ast.IdentifierNode) *ast.Node {
|
|
if info := tx.EmitContext().GetAutoGenerateInfo(node); !(info != nil && !info.Flags.HasAllowNameSubstitution()) &&
|
|
!transformers.IsHelperName(tx.EmitContext(), node) &&
|
|
!transformers.IsLocalName(tx.EmitContext(), node) &&
|
|
!isDeclarationNameOfEnumOrNamespace(tx.EmitContext(), node) {
|
|
exportContainer := tx.resolver.GetReferencedExportContainer(tx.EmitContext().MostOriginal(node), transformers.IsExportName(tx.EmitContext(), node))
|
|
if exportContainer != nil && ast.IsSourceFile(exportContainer) {
|
|
reference := tx.Factory().NewPropertyAccessExpression(
|
|
tx.Factory().NewIdentifier("exports"),
|
|
nil, /*questionDotToken*/
|
|
node.Clone(tx.Factory()),
|
|
ast.NodeFlagsNone,
|
|
)
|
|
tx.EmitContext().AssignCommentAndSourceMapRanges(reference, node)
|
|
reference.Loc = node.Loc
|
|
return reference
|
|
}
|
|
|
|
importDeclaration := tx.resolver.GetReferencedImportDeclaration(tx.EmitContext().MostOriginal(node))
|
|
if importDeclaration != nil {
|
|
if ast.IsImportClause(importDeclaration) {
|
|
reference := tx.Factory().NewPropertyAccessExpression(
|
|
tx.Factory().NewGeneratedNameForNode(importDeclaration.Parent),
|
|
nil, /*questionDotToken*/
|
|
tx.Factory().NewIdentifier("default"),
|
|
ast.NodeFlagsNone,
|
|
)
|
|
tx.EmitContext().AssignCommentAndSourceMapRanges(reference, node)
|
|
reference.Loc = node.Loc
|
|
return reference
|
|
}
|
|
if ast.IsImportSpecifier(importDeclaration) {
|
|
name := importDeclaration.AsImportSpecifier().PropertyNameOrName()
|
|
decl := ast.FindAncestor(importDeclaration, ast.IsImportDeclaration)
|
|
target := tx.Factory().NewGeneratedNameForNode(core.Coalesce(decl, importDeclaration))
|
|
var reference *ast.Node
|
|
if ast.IsStringLiteral(name) {
|
|
reference = tx.Factory().NewElementAccessExpression(
|
|
target,
|
|
nil, /*questionDotToken*/
|
|
tx.Factory().NewStringLiteralFromNode(name),
|
|
ast.NodeFlagsNone,
|
|
)
|
|
} else {
|
|
referenceName := name.Clone(tx.Factory())
|
|
tx.EmitContext().AddEmitFlags(referenceName, printer.EFNoSourceMap|printer.EFNoComments)
|
|
reference = tx.Factory().NewPropertyAccessExpression(
|
|
target,
|
|
nil, /*questionDotToken*/
|
|
referenceName,
|
|
ast.NodeFlagsNone,
|
|
)
|
|
}
|
|
tx.EmitContext().AssignCommentAndSourceMapRanges(reference, node)
|
|
reference.Loc = node.Loc
|
|
return reference
|
|
}
|
|
}
|
|
}
|
|
return node
|
|
}
|
|
|
|
// Gets the exported names of an identifier, if it is exported.
|
|
func (tx *CommonJSModuleTransformer) getExports(name *ast.IdentifierNode) []*ast.ModuleExportName {
|
|
if !transformers.IsGeneratedIdentifier(tx.EmitContext(), name) {
|
|
importDeclaration := tx.resolver.GetReferencedImportDeclaration(tx.EmitContext().MostOriginal(name))
|
|
if importDeclaration != nil {
|
|
return tx.currentModuleInfo.exportedBindings.Get(importDeclaration)
|
|
}
|
|
|
|
// An exported namespace or enum may merge with an ambient declaration, which won't show up in .js emit, so
|
|
// we analyze all value exports of a symbol.
|
|
var bindingsSet collections.Set[*ast.ModuleExportName]
|
|
var bindings []*ast.ModuleExportName
|
|
declarations := tx.resolver.GetReferencedValueDeclarations(tx.EmitContext().MostOriginal(name))
|
|
if declarations != nil {
|
|
for _, declaration := range declarations {
|
|
exportedBindings := tx.currentModuleInfo.exportedBindings.Get(declaration)
|
|
for _, binding := range exportedBindings {
|
|
if !bindingsSet.Has(binding) {
|
|
bindingsSet.Add(binding)
|
|
bindings = append(bindings, binding)
|
|
}
|
|
}
|
|
}
|
|
return bindings
|
|
}
|
|
} else if isFileLevelReservedGeneratedIdentifier(tx.EmitContext(), name) {
|
|
exportSpecifiers := tx.currentModuleInfo.exportSpecifiers.Get(name.Text())
|
|
if exportSpecifiers != nil {
|
|
var exportedNames []*ast.ModuleExportName
|
|
for _, exportSpecifier := range exportSpecifiers {
|
|
exportedNames = append(exportedNames, exportSpecifier.Name())
|
|
}
|
|
return exportedNames
|
|
}
|
|
}
|
|
return nil
|
|
}
|