vendor tsgo

This commit is contained in:
2026-07-09 16:50:43 -04:00
parent c06ea2e5a4
commit 98978e4930
5804 changed files with 1556156 additions and 101 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,372 @@
package moduletransforms
import (
"slices"
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/binder"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/printer"
"github.com/microsoft/typescript-go/internal/transformers"
)
type ESModuleTransformer struct {
transformers.Transformer
compilerOptions *core.CompilerOptions
resolver binder.ReferenceResolver
getEmitModuleFormatOfFile func(file ast.HasFileName) core.ModuleKind
currentSourceFile *ast.SourceFile
importRequireStatements *importRequireStatements
helperNameSubstitutions map[string]*ast.IdentifierNode
}
type importRequireStatements struct {
statements []*ast.Statement
requireHelperName *ast.IdentifierNode
}
func NewESModuleTransformer(opts *transformers.TransformOptions) *transformers.Transformer {
compilerOptions := opts.CompilerOptions
tx := &ESModuleTransformer{compilerOptions: compilerOptions, resolver: opts.Resolver, getEmitModuleFormatOfFile: opts.GetEmitModuleFormatOfFile}
return tx.NewTransformer(tx.visit, opts.Context)
}
// Visits source elements that are not top-level or top-level nested statements.
func (tx *ESModuleTransformer) visit(node *ast.Node) *ast.Node {
switch node.Kind {
case ast.KindSourceFile:
node = tx.visitSourceFile(node.AsSourceFile())
case ast.KindImportDeclaration:
node = tx.visitImportDeclaration(node.AsImportDeclaration())
case ast.KindImportEqualsDeclaration:
node = tx.visitImportEqualsDeclaration(node.AsImportEqualsDeclaration())
case ast.KindExportAssignment:
node = tx.visitExportAssignment(node.AsExportAssignment())
case ast.KindExportDeclaration:
node = tx.visitExportDeclaration(node.AsExportDeclaration())
case ast.KindCallExpression:
node = tx.visitCallExpression(node.AsCallExpression())
default:
node = tx.Visitor().VisitEachChild(node)
}
return node
}
func (tx *ESModuleTransformer) visitSourceFile(node *ast.SourceFile) *ast.Node {
if node.IsDeclarationFile ||
!(ast.IsExternalModule(node) || tx.compilerOptions.GetIsolatedModules()) {
return node.AsNode()
}
tx.currentSourceFile = node
tx.importRequireStatements = nil
result := tx.Visitor().VisitEachChild(node.AsNode()).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 || tx.importRequireStatements != nil {
prologue, rest := tx.Factory().SplitStandardPrologue(result.Statements.Nodes)
custom, rest := tx.Factory().SplitCustomPrologue(rest)
statements := slices.Clone(prologue)
statements = append(statements, custom...)
if externalHelpersImportDeclaration != nil {
// The helpers import must be visited so that `import x = require("tslib")`
// (TypeScript-only syntax) is transformed to `const x = require("tslib")`
// for CJS output files via visitImportEqualsDeclaration.
statements = append(statements, tx.Visitor().VisitNode(externalHelpersImportDeclaration))
}
if tx.importRequireStatements != nil {
statements = append(statements, tx.importRequireStatements.statements...)
}
statements = append(statements, rest...)
statementList := tx.Factory().NewNodeList(statements)
statementList.Loc = result.Statements.Loc
result = tx.Factory().UpdateSourceFile(result, statementList, node.EndOfFileToken).AsSourceFile()
}
if ast.IsExternalModule(result) &&
tx.compilerOptions.GetEmitModuleKind() != core.ModuleKindPreserve &&
!core.Some(result.Statements.Nodes, ast.IsExternalModuleIndicator) {
statements := slices.Clone(result.Statements.Nodes)
statements = append(statements, createEmptyImports(tx.Factory()))
statementList := tx.Factory().NewNodeList(statements)
statementList.Loc = result.Statements.Loc
result = tx.Factory().UpdateSourceFile(result, statementList, node.EndOfFileToken).AsSourceFile()
}
tx.importRequireStatements = nil
tx.currentSourceFile = nil
return result.AsNode()
}
func (tx *ESModuleTransformer) visitImportDeclaration(node *ast.ImportDeclaration) *ast.Node {
if !tx.compilerOptions.RewriteRelativeImportExtensions.IsTrue() {
return node.AsNode()
}
updatedModuleSpecifier := rewriteModuleSpecifier(tx.EmitContext(), node.ModuleSpecifier, tx.compilerOptions)
return tx.Factory().UpdateImportDeclaration(
node,
nil, /*modifiers*/
tx.Visitor().VisitNode(node.ImportClause),
updatedModuleSpecifier,
tx.Visitor().VisitNode(node.Attributes),
)
}
func (tx *ESModuleTransformer) visitImportEqualsDeclaration(node *ast.ImportEqualsDeclaration) *ast.Node {
// Though an error in es2020 modules, in node-flavor es2020 modules, we can helpfully transform this to a synthetic `require` call
// To give easy access to a synchronous `require` in node-flavor esm. We do the transform even in scenarios where we error, but `import.meta.url`
// is available, just because the output is reasonable for a node-like runtime.
if tx.compilerOptions.GetEmitModuleKind() < core.ModuleKindNode16 {
return nil
}
if !ast.IsExternalModuleImportEqualsDeclaration(node.AsNode()) {
panic("import= for internal module references should be handled in an earlier transformer.")
}
varStatement := tx.Factory().NewVariableStatement(
nil, /*modifiers*/
tx.Factory().NewVariableDeclarationList(
tx.Factory().NewNodeList([]*ast.Node{
tx.Factory().NewVariableDeclaration(
node.Name().Clone(tx.Factory()),
nil, /*exclamationToken*/
nil, /*type*/
tx.createRequireCall(node.AsNode()),
),
}),
ast.NodeFlagsConst,
),
)
tx.EmitContext().SetOriginal(varStatement, node.AsNode())
tx.EmitContext().AssignCommentAndSourceMapRanges(varStatement, node.AsNode())
var statements []*ast.Statement
statements = append(statements, varStatement)
statements = tx.appendExportsOfImportEqualsDeclaration(statements, node)
return transformers.SingleOrMany(statements, tx.Factory())
}
func (tx *ESModuleTransformer) appendExportsOfImportEqualsDeclaration(statements []*ast.Statement, node *ast.ImportEqualsDeclaration) []*ast.Statement {
if ast.HasSyntacticModifier(node.AsNode(), ast.ModifierFlagsExport) {
statements = append(statements, tx.Factory().NewExportDeclaration(
nil, /*modifiers*/
false, /*isTypeOnly*/
tx.Factory().NewNamedExports(
tx.Factory().NewNodeList([]*ast.Node{
tx.Factory().NewExportSpecifier(
false, /*isTypeOnly*/
nil, /*propertyName*/
node.Name().Clone(tx.Factory()),
),
}),
),
nil, /*moduleSpecifier*/
nil, /*attributes*/
))
}
return statements
}
func (tx *ESModuleTransformer) visitExportAssignment(node *ast.ExportAssignment) *ast.Node {
if !node.IsExportEquals {
return tx.Visitor().VisitEachChild(node.AsNode())
}
if tx.compilerOptions.GetEmitModuleKind() != core.ModuleKindPreserve {
// Elide `export=` as it is not legal with --module ES6
return nil
}
statement := tx.Factory().NewExpressionStatement(
tx.Factory().NewAssignmentExpression(
tx.Factory().NewPropertyAccessExpression(
tx.Factory().NewIdentifier("module"),
nil, /*questionDotToken*/
tx.Factory().NewIdentifier("exports"),
ast.NodeFlagsNone,
),
tx.Visitor().VisitNode(node.Expression),
),
)
tx.EmitContext().SetOriginal(statement, node.AsNode())
return statement
}
func (tx *ESModuleTransformer) visitExportDeclaration(node *ast.ExportDeclaration) *ast.Node {
if node.ModuleSpecifier == nil {
return node.AsNode()
}
updatedModuleSpecifier := rewriteModuleSpecifier(tx.EmitContext(), node.ModuleSpecifier, tx.compilerOptions)
if tx.compilerOptions.Module > core.ModuleKindES2015 || node.ExportClause == nil || !ast.IsNamespaceExport(node.ExportClause) {
// Either ill-formed or don't need to be transformed.
return tx.Factory().UpdateExportDeclaration(
node,
nil, /*modifiers*/
false, /*isTypeOnly*/
node.ExportClause,
updatedModuleSpecifier,
tx.Visitor().VisitNode(node.Attributes),
)
}
oldIdentifier := node.ExportClause.Name()
synthName := tx.Factory().NewGeneratedNameForNode(oldIdentifier)
importDecl := tx.Factory().NewImportDeclaration(
nil, /*modifiers*/
tx.Factory().NewImportClause(
ast.KindUnknown, /*phaseModifier*/
nil, /*name*/
tx.Factory().NewNamespaceImport(synthName),
),
updatedModuleSpecifier,
tx.Visitor().VisitNode(node.Attributes),
)
tx.EmitContext().SetOriginal(importDecl, node.ExportClause)
var exportDecl *ast.Node
if ast.IsExportNamespaceAsDefaultDeclaration(node.AsNode()) {
exportDecl = tx.Factory().NewExportAssignment(nil /*modifiers*/, false /*isExportEquals*/, nil /*typeNode*/, synthName)
} else {
exportDecl = tx.Factory().NewExportDeclaration(
nil, /*modifiers*/
false, /*isTypeOnly*/
tx.Factory().NewNamedExports(
tx.Factory().NewNodeList([]*ast.Node{
tx.Factory().NewExportSpecifier(false /*isTypeOnly*/, synthName, oldIdentifier),
}),
),
nil, /*moduleSpecifier*/
nil, /*attributes*/
)
}
tx.EmitContext().SetOriginal(exportDecl, node.AsNode())
return transformers.SingleOrMany([]*ast.Statement{importDecl, exportDecl}, tx.Factory())
}
func (tx *ESModuleTransformer) visitCallExpression(node *ast.CallExpression) *ast.Node {
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*/) {
return tx.visitImportOrRequireCall(node)
}
}
return tx.Visitor().VisitEachChild(node.AsNode())
}
func (tx *ESModuleTransformer) visitImportOrRequireCall(node *ast.CallExpression) *ast.Node {
if len(node.Arguments.Nodes) == 0 {
return tx.Visitor().VisitEachChild(node.AsNode())
}
expression := tx.Visitor().VisitNode(node.Expression)
var argument *ast.Expression
if ast.IsStringLiteralLike(node.Arguments.Nodes[0]) {
argument = rewriteModuleSpecifier(tx.EmitContext(), node.Arguments.Nodes[0], tx.compilerOptions)
} else {
argument = tx.Factory().NewRewriteRelativeImportExtensionsHelper(node.Arguments.Nodes[0], tx.compilerOptions.Jsx == core.JsxEmitPreserve)
}
var arguments []*ast.Expression
arguments = append(arguments, argument)
rest := core.FirstResult(tx.Visitor().VisitSlice(node.Arguments.Nodes[1:]))
arguments = append(arguments, rest...)
argumentList := tx.Factory().NewNodeList(arguments)
argumentList.Loc = node.Arguments.Loc
return tx.Factory().UpdateCallExpression(
node,
expression,
node.QuestionDotToken,
nil, /*typeArguments*/
argumentList,
node.Flags,
)
}
func (tx *ESModuleTransformer) createRequireCall(node *ast.Node /*ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration*/) *ast.Expression {
moduleName := getExternalModuleNameLiteral(tx.Factory(), node, tx.currentSourceFile, nil /*host*/, nil /*emitResolver*/, tx.compilerOptions)
var args []*ast.Expression
if moduleName != nil {
args = append(args, rewriteModuleSpecifier(tx.EmitContext(), moduleName, tx.compilerOptions))
}
if tx.compilerOptions.GetEmitModuleKind() == core.ModuleKindPreserve {
return tx.Factory().NewCallExpression(
tx.Factory().NewIdentifier("require"),
nil, /*questionDotToken*/
nil, /*typeArguments*/
tx.Factory().NewNodeList(args),
ast.NodeFlagsNone,
)
}
if tx.importRequireStatements == nil {
createRequireName := tx.Factory().NewUniqueNameEx("_createRequire", printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsOptimistic | printer.GeneratedIdentifierFlagsFileLevel})
importStatement := tx.Factory().NewImportDeclaration(
nil, /*modifiers*/
tx.Factory().NewImportClause(
ast.KindUnknown, /*phaseModifier*/
nil, /*name*/
tx.Factory().NewNamedImports(
tx.Factory().NewNodeList([]*ast.Node{
tx.Factory().NewImportSpecifier(
false, /*isTypeOnly*/
tx.Factory().NewIdentifier("createRequire"),
createRequireName,
),
}),
),
),
tx.Factory().NewStringLiteral("module", ast.TokenFlagsNone),
nil, /*attributes*/
)
tx.EmitContext().AddEmitFlags(importStatement, printer.EFCustomPrologue)
requireHelperName := tx.Factory().NewUniqueNameEx("__require", printer.AutoGenerateOptions{Flags: printer.GeneratedIdentifierFlagsOptimistic | printer.GeneratedIdentifierFlagsFileLevel})
requireStatement := tx.Factory().NewVariableStatement(
nil, /*modifiers*/
tx.Factory().NewVariableDeclarationList(
tx.Factory().NewNodeList([]*ast.Node{
tx.Factory().NewVariableDeclaration(
requireHelperName,
nil, /*exclamationToken*/
nil, /*type*/
tx.Factory().NewCallExpression(
createRequireName.Clone(tx.Factory()),
nil, /*questionDotToken*/
nil, /*typeArguments*/
tx.Factory().NewNodeList([]*ast.Expression{
tx.Factory().NewPropertyAccessExpression(
tx.Factory().NewMetaProperty(ast.KindImportKeyword, tx.Factory().NewIdentifier("meta")),
nil, /*questionDotToken*/
tx.Factory().NewIdentifier("url"),
ast.NodeFlagsNone,
),
}),
ast.NodeFlagsNone,
),
),
}),
ast.NodeFlagsConst,
),
)
tx.EmitContext().AddEmitFlags(requireStatement, printer.EFCustomPrologue)
tx.importRequireStatements = &importRequireStatements{
statements: []*ast.Statement{importStatement, requireStatement},
requireHelperName: requireHelperName,
}
}
return tx.Factory().NewCallExpression(
tx.importRequireStatements.requireHelperName.Clone(tx.Factory()),
nil, /*questionDotToken*/
nil, /*typeArguments*/
tx.Factory().NewNodeList(args),
ast.NodeFlagsNone,
)
}

View File

@@ -0,0 +1,390 @@
package moduletransforms
import (
"slices"
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/binder"
"github.com/microsoft/typescript-go/internal/collections"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/printer"
"github.com/microsoft/typescript-go/internal/stringutil"
"github.com/microsoft/typescript-go/internal/transformers"
)
type externalModuleInfo struct {
externalImports []*ast.Declaration // ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration. imports and reexports of other external modules
exportSpecifiers collections.MultiMap[string, *ast.ExportSpecifier] // Maps local names to their associated export specifiers (excludes reexports)
exportedBindings collections.MultiMap[*ast.Declaration, *ast.ModuleExportName] // Maps local declarations to their associated export aliases
exportedNames []*ast.ModuleExportName // all exported names in the module, both local and re-exported, excluding the names of locally exported function declarations
exportedFunctions collections.OrderedSet[*ast.FunctionDeclarationNode] // all of the top-level exported function declarations
exportEquals *ast.ExportAssignment // an export=/module.exports= declaration if one was present
hasExportStarsToExportValues bool // whether this module contains export*
}
type externalModuleInfoCollector struct {
sourceFile *ast.SourceFile
compilerOptions *core.CompilerOptions
emitContext *printer.EmitContext
resolver binder.ReferenceResolver
uniqueExports collections.Set[string]
hasExportDefault bool
output *externalModuleInfo
}
func collectExternalModuleInfo(sourceFile *ast.SourceFile, compilerOptions *core.CompilerOptions, emitContext *printer.EmitContext, resolver binder.ReferenceResolver) *externalModuleInfo {
c := externalModuleInfoCollector{
sourceFile: sourceFile,
compilerOptions: compilerOptions,
emitContext: emitContext,
resolver: resolver,
output: &externalModuleInfo{},
}
return c.collect()
}
func (c *externalModuleInfoCollector) collect() *externalModuleInfo {
hasImportStar := false
hasImportDefault := false
for _, node := range c.sourceFile.Statements.Nodes {
// Look through NotEmittedStatement to find elided export= declarations
// (e.g., `declare export = x` is elided by the type eraser but must still be collected)
if ast.IsNotEmittedStatement(node) {
original := c.emitContext.MostOriginal(node)
if original != nil && ast.IsExportAssignment(original) {
n := original.AsExportAssignment()
if n.IsExportEquals && c.output.exportEquals == nil {
c.output.exportEquals = n
}
}
continue
}
switch node.Kind {
case ast.KindImportDeclaration:
// import "mod"
// import x from "mod"
// import * as x from "mod"
// import { x, y } from "mod"
n := node.AsImportDeclaration()
c.addExternalImport(node)
if !hasImportStar && getImportNeedsImportStarHelper(n) {
hasImportStar = true
}
if !hasImportDefault && getImportNeedsImportDefaultHelper(n) {
hasImportDefault = true
}
case ast.KindImportEqualsDeclaration:
n := node.AsImportEqualsDeclaration()
if ast.IsExternalModuleReference(n.ModuleReference) {
// import x = require("mod")
c.addExternalImport(node)
}
case ast.KindExportDeclaration:
n := node.AsExportDeclaration()
if n.ModuleSpecifier != nil {
// export * from "mod"
// export * as ns from "mod"
// export { x, y } from "mod"
c.addExternalImport(node)
if n.ExportClause == nil {
// export * from "mod"
c.output.hasExportStarsToExportValues = true
} else if ast.IsNamedExports(n.ExportClause) {
// export { x, y } from "mod"
c.addExportedNamesForExportDeclaration(n)
if !hasImportDefault {
hasImportDefault = containsDefaultReference(n.ExportClause)
}
} else {
// export * as ns from "mod"
name := n.ExportClause.AsNamespaceExport().Name()
nameText := name.Text()
if c.addUniqueExport(nameText) {
c.addExportedBinding(node, name)
c.addExportedName(name)
}
// we use the same helpers for `export * as ns` as we do for `import * as ns`
hasImportStar = true
}
} else {
// export { x, y }
c.addExportedNamesForExportDeclaration(node.AsExportDeclaration())
}
case ast.KindExportAssignment:
n := node.AsExportAssignment()
if n.IsExportEquals && c.output.exportEquals == nil {
// export = x
c.output.exportEquals = n
}
case ast.KindVariableStatement:
n := node.AsVariableStatement()
if ast.HasSyntacticModifier(node, ast.ModifierFlagsExport) {
for _, decl := range n.DeclarationList.AsVariableDeclarationList().Declarations.Nodes {
c.collectExportedVariableInfo(decl)
}
}
case ast.KindFunctionDeclaration:
n := node.AsFunctionDeclaration()
if ast.HasSyntacticModifier(node, ast.ModifierFlagsExport) {
c.addExportedFunctionDeclaration(n, nil /*name*/, ast.HasSyntacticModifier(node, ast.ModifierFlagsDefault))
}
case ast.KindClassDeclaration:
n := node.AsClassDeclaration()
if ast.HasSyntacticModifier(node, ast.ModifierFlagsExport) {
if ast.HasSyntacticModifier(node, ast.ModifierFlagsDefault) {
// export default class { }
if !c.hasExportDefault {
name := n.Name()
if name == nil {
name = c.emitContext.Factory.NewGeneratedNameForNode(node)
}
c.addExportedBinding(node, name)
c.hasExportDefault = true
}
} else {
// export class x { }
name := n.Name()
if name != nil {
if c.addUniqueExport(name.Text()) {
c.addExportedBinding(node, name)
c.addExportedName(name)
}
}
}
}
}
}
return c.output
}
func (c *externalModuleInfoCollector) addUniqueExport(name string) bool {
if !c.uniqueExports.Has(name) {
c.uniqueExports.Add(name)
return true
}
return false
}
func (c *externalModuleInfoCollector) addExportedBinding(decl *ast.Declaration, name *ast.ModuleExportName) {
c.output.exportedBindings.Add(c.emitContext.MostOriginal(decl), name)
}
func (c *externalModuleInfoCollector) addExternalImport(node *ast.Node /*ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration*/) {
c.output.externalImports = append(c.output.externalImports, node)
}
func (c *externalModuleInfoCollector) addExportedName(name *ast.ModuleExportName) {
c.output.exportedNames = append(c.output.exportedNames, name)
}
func (c *externalModuleInfoCollector) addExportedNamesForExportDeclaration(node *ast.ExportDeclaration) {
for _, specifier := range node.ExportClause.Elements() {
specifierNameText := specifier.Name().Text()
if c.addUniqueExport(specifierNameText) {
name := specifier.PropertyNameOrName()
if name.Kind != ast.KindStringLiteral {
if node.ModuleSpecifier == nil {
c.output.exportSpecifiers.Add(name.Text(), specifier.AsExportSpecifier())
}
decl := c.resolver.GetReferencedImportDeclaration(c.emitContext.MostOriginal(name))
if decl == nil {
decl = c.resolver.GetReferencedValueDeclaration(c.emitContext.MostOriginal(name))
}
if decl != nil {
if decl.Kind == ast.KindFunctionDeclaration {
c.uniqueExports.Delete(specifierNameText)
c.addExportedFunctionDeclaration(decl.AsFunctionDeclaration(), specifier.Name(), ast.ModuleExportNameIsDefault(specifier.Name()))
continue
}
c.addExportedBinding(decl, specifier.Name())
}
}
c.addExportedName(specifier.Name())
}
}
}
func (c *externalModuleInfoCollector) addExportedFunctionDeclaration(node *ast.FunctionDeclaration, name *ast.ModuleExportName, isDefault bool) {
c.output.exportedFunctions.Add(c.emitContext.MostOriginal(node.AsNode()))
if isDefault {
// export default function() { }
// function x() { } + export { x as default };
if !c.hasExportDefault {
if name == nil {
name = c.emitContext.Factory.NewGeneratedNameForNode(node.AsNode())
}
c.addExportedBinding(node.AsNode(), name)
c.hasExportDefault = true
}
} else {
// export function x() { }
// function x() { } + export { x }
if name == nil {
name = node.Name()
}
nameText := name.Text()
if c.addUniqueExport(nameText) {
c.addExportedBinding(node.AsNode(), name)
}
}
}
func (c *externalModuleInfoCollector) collectExportedVariableInfo(decl *ast.Node /*VariableDeclaration | BindingElement*/) {
if ast.IsBindingPattern(decl.Name()) {
for _, element := range decl.Name().Elements() {
e := element.AsBindingElement()
if e.Name() != nil {
c.collectExportedVariableInfo(element)
}
}
} else if !c.emitContext.HasAutoGenerateInfo(decl.Name()) {
text := decl.Name().Text()
if c.addUniqueExport(text) {
c.addExportedName(decl.Name())
if transformers.IsLocalName(c.emitContext, decl.Name()) {
c.addExportedBinding(decl, decl.Name())
}
}
}
}
const externalHelpersModuleNameText = "tslib"
func createExternalHelpersImportDeclarationIfNeeded(emitContext *printer.EmitContext, sourceFile *ast.SourceFile, compilerOptions *core.CompilerOptions, fileModuleKind core.ModuleKind, hasExportStarsToExportValues bool, hasImportStar bool, hasImportDefault bool) *ast.Node /*ImportDeclaration | ImportEqualsDeclaration*/ {
if compilerOptions.ImportHelpers.IsTrue() && ast.IsEffectiveExternalModule(sourceFile, compilerOptions) {
moduleKind := compilerOptions.GetEmitModuleKind()
helpers := getImportedHelpers(emitContext, sourceFile)
if fileModuleKind == core.ModuleKindCommonJS || fileModuleKind == core.ModuleKindNone && moduleKind == core.ModuleKindCommonJS {
// When we emit to a non-ES module, generate a synthetic `import tslib = require("tslib")` to be further transformed.
externalHelpersModuleName := getOrCreateExternalHelpersModuleNameIfNeeded(emitContext, sourceFile, compilerOptions, helpers, hasExportStarsToExportValues, hasImportStar || hasImportDefault, fileModuleKind)
if externalHelpersModuleName != nil {
externalHelpersImportDeclaration := emitContext.Factory.NewImportEqualsDeclaration(
nil, /*modifiers*/
false, /*isTypeOnly*/
externalHelpersModuleName,
emitContext.Factory.NewExternalModuleReference(emitContext.Factory.NewStringLiteral(externalHelpersModuleNameText, ast.TokenFlagsNone)),
)
emitContext.AddEmitFlags(externalHelpersImportDeclaration, printer.EFCustomPrologue)
return externalHelpersImportDeclaration
}
} else {
// When we emit as an ES module, generate an `import` declaration that uses named imports for helpers.
// If we cannot determine the implied module kind under `module: preserve` we assume ESM.
var helperNames []string
for _, helper := range helpers {
importName := helper.ImportName
if len(importName) > 0 {
helperNames = core.AppendIfUnique(helperNames, importName)
}
}
if len(helperNames) > 0 {
slices.SortFunc(helperNames, stringutil.CompareStringsCaseSensitive)
// Alias the imports if the names are used somewhere in the file.
// NOTE: We don't need to care about global import collisions as this is a module.
importSpecifiers := core.Map(helperNames, func(name string) *ast.ImportSpecifierNode {
if printer.IsFileLevelUniqueName(sourceFile, name, nil /*hasGlobalName*/) {
return emitContext.Factory.NewImportSpecifier(false /*isTypeOnly*/, nil /*propertyName*/, emitContext.Factory.NewIdentifier(name))
} else {
return emitContext.Factory.NewImportSpecifier(false /*isTypeOnly*/, emitContext.Factory.NewIdentifier(name), emitContext.Factory.NewUnscopedHelperName(name))
}
})
namedBindings := emitContext.Factory.NewNamedImports(emitContext.Factory.NewNodeList(importSpecifiers))
parseNode := emitContext.MostOriginal(sourceFile.AsNode())
emitContext.AddEmitFlags(parseNode, printer.EFExternalHelpers)
externalHelpersImportDeclaration := emitContext.Factory.NewImportDeclaration(
nil, /*modifiers*/
emitContext.Factory.NewImportClause(ast.KindUnknown /*phaseModifier*/, nil /*name*/, namedBindings),
emitContext.Factory.NewStringLiteral(externalHelpersModuleNameText, ast.TokenFlagsNone),
nil, /*attributes*/
)
emitContext.AddEmitFlags(externalHelpersImportDeclaration, printer.EFCustomPrologue)
return externalHelpersImportDeclaration
}
}
}
return nil
}
func getImportedHelpers(emitContext *printer.EmitContext, sourceFile *ast.SourceFile) []*printer.EmitHelper {
var helpers []*printer.EmitHelper
for _, helper := range emitContext.GetEmitHelpers(sourceFile.AsNode()) {
if !helper.Scoped {
helpers = append(helpers, helper)
}
}
return helpers
}
func getOrCreateExternalHelpersModuleNameIfNeeded(emitContext *printer.EmitContext, node *ast.SourceFile, compilerOptions *core.CompilerOptions, helpers []*printer.EmitHelper, hasExportStarsToExportValues bool, hasImportStarOrImportDefault bool, fileModuleKind core.ModuleKind) *ast.IdentifierNode {
externalHelpersModuleName := emitContext.GetExternalHelpersModuleName(node)
if externalHelpersModuleName != nil {
return externalHelpersModuleName
}
create := len(helpers) > 0 ||
(hasExportStarsToExportValues || hasImportStarOrImportDefault) &&
fileModuleKind < core.ModuleKindSystem
if create {
externalHelpersModuleName = emitContext.Factory.NewUniqueName(externalHelpersModuleNameText)
emitContext.SetExternalHelpersModuleName(node, externalHelpersModuleName)
}
return externalHelpersModuleName
}
func isNamedDefaultReference(e *ast.Node /*ImportSpecifier | ExportSpecifier*/) bool {
return ast.ModuleExportNameIsDefault(e.PropertyNameOrName())
}
func containsDefaultReference(node *ast.Node /*NamedImportBindings | NamedExportBindings*/) bool {
return node != nil && (ast.IsNamedImports(node) || ast.IsNamedExports(node)) && core.Some(node.Elements(), isNamedDefaultReference)
}
func getExportNeedsImportStarHelper(node *ast.ExportDeclaration) bool {
return ast.GetNamespaceDeclarationNode(node.AsNode()) != nil
}
func getImportNeedsImportStarHelper(node *ast.ImportDeclaration) bool {
if ast.GetNamespaceDeclarationNode(node.AsNode()) != nil {
return true
}
if node.ImportClause == nil {
return false
}
bindings := node.ImportClause.AsImportClause().NamedBindings
if bindings == nil {
return false
}
if !ast.IsNamedImports(bindings) {
return false
}
namedImports := bindings.AsNamedImports()
defaultRefCount := 0
for _, binding := range namedImports.Elements.Nodes {
if isNamedDefaultReference(binding) {
defaultRefCount++
}
}
// Import star is required if there's default named refs mixed with non-default refs, or if theres non-default refs and it has a default import
return (defaultRefCount > 0 && defaultRefCount != len(namedImports.Elements.Nodes)) || ((len(namedImports.Elements.Nodes)-defaultRefCount) != 0 && ast.IsDefaultImport(node.AsNode()))
}
func getImportNeedsImportDefaultHelper(node *ast.ImportDeclaration) bool {
// Import default is needed if there's a default import or a default ref and no other refs (meaning an import star helper wasn't requested)
return !getImportNeedsImportStarHelper(node) && (ast.IsDefaultImport(node.AsNode()) || (node.ImportClause != nil &&
ast.IsNamedImports(node.ImportClause.AsImportClause().NamedBindings) &&
containsDefaultReference(node.ImportClause.AsImportClause().NamedBindings)))
}

View File

@@ -0,0 +1,53 @@
package moduletransforms
import (
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/binder"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/transformers"
)
type ImpliedModuleTransformer struct {
transformers.Transformer
opts *transformers.TransformOptions
resolver binder.ReferenceResolver
getEmitModuleFormatOfFile func(file ast.HasFileName) core.ModuleKind
cjsTransformer *transformers.Transformer
esmTransformer *transformers.Transformer
}
func NewImpliedModuleTransformer(opts *transformers.TransformOptions) *transformers.Transformer {
tx := &ImpliedModuleTransformer{opts: opts, resolver: opts.Resolver, getEmitModuleFormatOfFile: opts.GetEmitModuleFormatOfFile}
return tx.NewTransformer(tx.visit, opts.Context)
}
func (tx *ImpliedModuleTransformer) visit(node *ast.Node) *ast.Node {
switch node.Kind {
case ast.KindSourceFile:
node = tx.visitSourceFile(node.AsSourceFile())
}
return node
}
func (tx *ImpliedModuleTransformer) visitSourceFile(node *ast.SourceFile) *ast.Node {
if node.IsDeclarationFile {
return node.AsNode()
}
format := tx.getEmitModuleFormatOfFile(node)
var transformer *transformers.Transformer
if format >= core.ModuleKindES2015 {
if tx.esmTransformer == nil {
tx.esmTransformer = NewESModuleTransformer(tx.opts)
}
transformer = tx.esmTransformer
} else {
if tx.cjsTransformer == nil {
tx.cjsTransformer = NewCommonJSModuleTransformer(tx.opts)
}
transformer = tx.cjsTransformer
}
return transformer.TransformSourceFile(node).AsNode()
}

View File

@@ -0,0 +1,118 @@
package moduletransforms
import (
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/outputpaths"
"github.com/microsoft/typescript-go/internal/printer"
"github.com/microsoft/typescript-go/internal/transformers"
"github.com/microsoft/typescript-go/internal/tspath"
)
func isDeclarationNameOfEnumOrNamespace(emitContext *printer.EmitContext, node *ast.IdentifierNode) bool {
if original := emitContext.MostOriginal(node); original != nil && original.Parent != nil { //nolint:customlint // MostOriginal yields parse-tree nodes and this helper intentionally inspects parse-tree parents.
switch original.Parent.Kind { //nolint:customlint // MostOriginal yields parse-tree nodes and this helper intentionally inspects parse-tree parents.
case ast.KindEnumDeclaration, ast.KindModuleDeclaration:
return original == original.Parent.Name() //nolint:customlint // MostOriginal yields parse-tree nodes and this helper intentionally inspects parse-tree parents.
}
}
return false
}
func rewriteModuleSpecifier(emitContext *printer.EmitContext, node *ast.Expression, compilerOptions *core.CompilerOptions) *ast.Expression {
if node == nil || !ast.IsStringLiteral(node) || !core.ShouldRewriteModuleSpecifier(node.Text(), compilerOptions) {
return node
}
updatedText := tspath.ChangeExtension(node.Text(), outputpaths.GetOutputExtension(node.Text(), compilerOptions.Jsx))
if updatedText != node.Text() {
updated := emitContext.Factory.NewStringLiteral(updatedText, node.AsStringLiteral().TokenFlags)
emitContext.SetOriginal(updated, node)
emitContext.AssignCommentAndSourceMapRanges(updated, node)
return updated
}
return node
}
func createEmptyImports(factory *printer.NodeFactory) *ast.Statement {
return factory.NewExportDeclaration(
nil, /*modifiers*/
false, /*isTypeOnly*/
factory.NewNamedExports(factory.NewNodeList(nil)),
nil, /*moduleSpecifier*/
nil, /*attributes*/
)
}
// Get the name of a target module from an import/export declaration as should be written in the emitted output.
// The emitted output name can be different from the input if:
// 1. The module has a /// <amd-module name="<new name>" />
// 2. --out or --outFile is used, making the name relative to the rootDir
// 3- The containing SourceFile has an entry in renamedDependencies for the import as requested by some module loaders (e.g. System).
//
// Otherwise, a new StringLiteral node representing the module name will be returned.
func getExternalModuleNameLiteral(factory *printer.NodeFactory, importNode *ast.Node /*ImportDeclaration | ExportDeclaration | ImportEqualsDeclaration | ImportCall*/, sourceFile *ast.SourceFile, host any /*EmitHost*/, resolver printer.EmitResolver, compilerOptions *core.CompilerOptions) *ast.StringLiteralNode {
moduleName := ast.GetExternalModuleName(importNode)
if moduleName != nil && ast.IsStringLiteral(moduleName) {
name := tryGetModuleNameFromDeclaration(importNode, host, factory, resolver, compilerOptions)
if name == nil {
name = tryRenameExternalModule(factory, moduleName, sourceFile)
}
if name == nil { // !!! propagate token flags (will produce new diffs)
name = factory.NewStringLiteral(moduleName.Text(), ast.TokenFlagsNone)
}
return name
}
return nil
}
// Get the name of a module as should be written in the emitted output.
// The emitted output name can be different from the input if:
// 1. The module has a /// <amd-module name="<new name>" />
// 2. --out or --outFile is used, making the name relative to the rootDir
//
// Otherwise, a new StringLiteral node representing the module name will be returned.
func tryGetModuleNameFromFile(factory *printer.NodeFactory, file *ast.SourceFile, host any /*EmitHost*/, options *core.CompilerOptions) *ast.StringLiteralNode {
if file == nil {
return nil
}
// !!!
// if file.moduleName {
// return factory.createStringLiteral(file.moduleName)
// }
return nil
}
func tryGetModuleNameFromDeclaration(declaration *ast.Node /*ImportEqualsDeclaration | ImportDeclaration | ExportDeclaration | ImportCall*/, host any /*EmitHost*/, factory *printer.NodeFactory, resolver printer.EmitResolver, compilerOptions *core.CompilerOptions) *ast.StringLiteralNode {
if resolver == nil {
return nil
}
return tryGetModuleNameFromFile(factory, resolver.GetExternalModuleFileFromDeclaration(declaration), host, compilerOptions)
}
// Resolves a local path to a path which is absolute to the base of the emit
func getExternalModuleNameFromPath(host any /*ResolveModuleNameResolutionHost*/, fileName string, referencePath string) string {
// !!!
return ""
}
// Some bundlers (SystemJS builder) sometimes want to rename dependencies.
// Here we check if alternative name was provided for a given moduleName and return it if possible.
func tryRenameExternalModule(factory *printer.NodeFactory, moduleName *ast.LiteralExpression, sourceFile *ast.SourceFile) *ast.StringLiteralNode {
// !!!
return nil
}
func isFileLevelReservedGeneratedIdentifier(emitContext *printer.EmitContext, name *ast.IdentifierNode) bool {
info := emitContext.GetAutoGenerateInfo(name)
return info != nil &&
info.Flags.IsFileLevel() &&
info.Flags.IsOptimistic() &&
info.Flags.IsReservedInNestedScopes()
}
// A simple inlinable expression is an expression which can be copied into multiple locations
// without risk of repeating any sideeffects and whose value could not possibly change between
// any such locations
func isSimpleInlineableExpression(expression *ast.Expression) bool {
return !ast.IsIdentifier(expression) && transformers.IsSimpleCopiableExpression(expression)
}