Skip to content

More module specifier generation #1051

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jun 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
5 changes: 5 additions & 0 deletions internal/ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -9985,6 +9985,11 @@ type CheckJsDirective struct {
Range CommentRange
}

type HasFileName interface {
FileName() string
Path() tspath.Path
}

type SourceFile struct {
NodeBase
DeclarationBase
Expand Down
14 changes: 11 additions & 3 deletions internal/ast/utilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -2414,15 +2414,15 @@ func GetImpliedNodeFormatForFile(path string, packageJsonType string) core.Modul
return impliedNodeFormat
}

func GetEmitModuleFormatOfFileWorker(sourceFile *SourceFile, options *core.CompilerOptions, sourceFileMetaData *SourceFileMetaData) core.ModuleKind {
result := GetImpliedNodeFormatForEmitWorker(sourceFile.FileName(), options, sourceFileMetaData)
func GetEmitModuleFormatOfFileWorker(fileName string, options *core.CompilerOptions, sourceFileMetaData *SourceFileMetaData) core.ModuleKind {
result := GetImpliedNodeFormatForEmitWorker(fileName, options, sourceFileMetaData)
if result != core.ModuleKindNone {
return result
}
return options.GetEmitModuleKind()
}

func GetImpliedNodeFormatForEmitWorker(fileName string, options *core.CompilerOptions, sourceFileMetaData *SourceFileMetaData) core.ModuleKind {
func GetImpliedNodeFormatForEmitWorker(fileName string, options *core.CompilerOptions, sourceFileMetaData *SourceFileMetaData) core.ResolutionMode {
moduleKind := options.GetEmitModuleKind()
if core.ModuleKindNode16 <= moduleKind && moduleKind <= core.ModuleKindNodeNext {
if sourceFileMetaData == nil {
Expand Down Expand Up @@ -3390,6 +3390,14 @@ func IsRightSideOfQualifiedNameOrPropertyAccess(node *Node) bool {
return false
}

func ShouldTransformImportCall(fileName string, options *core.CompilerOptions, impliedNodeFormatForEmit core.ModuleKind) bool {
moduleKind := options.GetEmitModuleKind()
if core.ModuleKindNode16 <= moduleKind && moduleKind <= core.ModuleKindNodeNext || moduleKind == core.ModuleKindPreserve {
return false
}
return impliedNodeFormatForEmit < core.ModuleKindES2015
}

func HasQuestionToken(node *Node) bool {
switch node.Kind {
case KindParameter:
Expand Down
11 changes: 5 additions & 6 deletions internal/checker/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -527,14 +527,13 @@ type Program interface {
BindSourceFiles()
FileExists(fileName string) bool
GetSourceFile(fileName string) *ast.SourceFile
GetEmitModuleFormatOfFile(sourceFile *ast.SourceFile) core.ModuleKind
GetImpliedNodeFormatForEmit(sourceFile *ast.SourceFile) core.ModuleKind
GetResolvedModule(currentSourceFile *ast.SourceFile, moduleReference string, mode core.ResolutionMode) *module.ResolvedModule
GetEmitModuleFormatOfFile(sourceFile ast.HasFileName) core.ModuleKind
GetImpliedNodeFormatForEmit(sourceFile ast.HasFileName) core.ModuleKind
GetResolvedModule(currentSourceFile ast.HasFileName, moduleReference string, mode core.ResolutionMode) *module.ResolvedModule
GetResolvedModules() map[tspath.Path]module.ModeAwareCache[*module.ResolvedModule]
GetSourceFileMetaData(path tspath.Path) *ast.SourceFileMetaData
GetJSXRuntimeImportSpecifier(path tspath.Path) (moduleReference string, specifier *ast.Node)
GetImportHelpersImportSpecifier(path tspath.Path) *ast.Node
GetModeForUsageLocation(sourceFile *ast.SourceFile, location *ast.Node) core.ResolutionMode
}

type Host interface {
Expand Down Expand Up @@ -14381,7 +14380,7 @@ func (c *Checker) resolveExternalModule(location *ast.Node, moduleReference stri

var sourceFile *ast.SourceFile
resolvedModule := c.program.GetResolvedModule(importingSourceFile, moduleReference, mode)
if resolvedModule != nil && resolvedModule.IsResolved() {
if resolvedModule.IsResolved() {
sourceFile = c.program.GetSourceFile(resolvedModule.ResolvedFileName)
}

Expand Down Expand Up @@ -14473,7 +14472,7 @@ func (c *Checker) resolveExternalModule(location *ast.Node, moduleReference stri
return nil
}

if resolvedModule != nil && resolvedModule.IsResolved() && !(tspath.ExtensionIsTs(resolvedModule.Extension) || resolvedModule.Extension == tspath.ExtensionJson) {
if resolvedModule.IsResolved() && !(tspath.ExtensionIsTs(resolvedModule.Extension) || resolvedModule.Extension == tspath.ExtensionJson) {
if isForAugmentation {
c.error(
errorNode,
Expand Down
23 changes: 16 additions & 7 deletions internal/compiler/emitHost.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import (

"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/module"
"github.com/microsoft/typescript-go/internal/modulespecifiers"
"github.com/microsoft/typescript-go/internal/outputpaths"
"github.com/microsoft/typescript-go/internal/printer"
"github.com/microsoft/typescript-go/internal/transformers/declarations"
"github.com/microsoft/typescript-go/internal/tspath"
Expand All @@ -29,7 +31,6 @@ type EmitHost interface {
GetCurrentDirectory() string
CommonSourceDirectory() string
IsEmitBlocked(file string) bool
GetSourceFileMetaData(path tspath.Path) *ast.SourceFileMetaData
GetEmitResolver(file *ast.SourceFile, skipDiagnostics bool) printer.EmitResolver
}

Expand All @@ -40,10 +41,22 @@ type emitHost struct {
program *Program
}

func (host *emitHost) GetDefaultResolutionModeForFile(file modulespecifiers.SourceFileForSpecifierGeneration) core.ResolutionMode {
func (host *emitHost) GetModeForUsageLocation(file ast.HasFileName, moduleSpecifier *ast.StringLiteralLike) core.ResolutionMode {
return host.program.GetModeForUsageLocation(file, moduleSpecifier)
}

func (host *emitHost) GetResolvedModuleFromModuleSpecifier(file ast.HasFileName, moduleSpecifier *ast.StringLiteralLike) *module.ResolvedModule {
return host.program.GetResolvedModuleFromModuleSpecifier(file, moduleSpecifier)
}

func (host *emitHost) GetDefaultResolutionModeForFile(file ast.HasFileName) core.ResolutionMode {
return host.program.GetDefaultResolutionModeForFile(file)
}

func (host *emitHost) GetEmitModuleFormatOfFile(file ast.HasFileName) core.ModuleKind {
return host.program.GetEmitModuleFormatOfFile(file)
}

func (host *emitHost) FileExists(path string) bool {
return host.program.FileExists(path)
}
Expand Down Expand Up @@ -78,7 +91,7 @@ func (host *emitHost) GetEffectiveDeclarationFlags(node *ast.Node, flags ast.Mod

func (host *emitHost) GetOutputPathsFor(file *ast.SourceFile, forceDtsPaths bool) declarations.OutputPaths {
// TODO: cache
return getOutputPathsFor(file, host, forceDtsPaths)
return outputpaths.GetOutputPathsFor(file, host.Options(), host, forceDtsPaths)
}

func (host *emitHost) GetResolutionModeOverride(node *ast.Node) core.ResolutionMode {
Expand Down Expand Up @@ -114,7 +127,3 @@ func (host *emitHost) GetEmitResolver(file *ast.SourceFile, skipDiagnostics bool
defer done()
return checker.GetEmitResolver(file, skipDiagnostics)
}

func (host *emitHost) GetSourceFileMetaData(path tspath.Path) *ast.SourceFileMetaData {
return host.program.GetSourceFileMetaData(path)
}
136 changes: 7 additions & 129 deletions internal/compiler/emitter.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/diagnostics"
"github.com/microsoft/typescript-go/internal/outputpaths"
"github.com/microsoft/typescript-go/internal/printer"
"github.com/microsoft/typescript-go/internal/sourcemap"
"github.com/microsoft/typescript-go/internal/stringutil"
Expand All @@ -32,15 +33,15 @@ type emitter struct {
emitSkipped bool
sourceMapDataList []*SourceMapEmitResult
writer printer.EmitTextWriter
paths *outputPaths
paths *outputpaths.OutputPaths
sourceFile *ast.SourceFile
}

func (e *emitter) emit() {
// !!! tracing
e.emitJSFile(e.sourceFile, e.paths.jsFilePath, e.paths.sourceMapFilePath)
e.emitDeclarationFile(e.sourceFile, e.paths.declarationFilePath, e.paths.declarationMapPath)
e.emitBuildInfo(e.paths.buildInfoPath)
e.emitJSFile(e.sourceFile, e.paths.JsFilePath(), e.paths.SourceMapFilePath())
e.emitDeclarationFile(e.sourceFile, e.paths.DeclarationFilePath(), e.paths.DeclarationMapPath())
e.emitBuildInfo(e.paths.BuildInfoPath())
}

func (e *emitter) getDeclarationTransformers(emitContext *printer.EmitContext, sourceFile *ast.SourceFile, declarationFilePath string, declarationMapPath string) []*declarations.DeclarationTransformer {
Expand Down Expand Up @@ -207,44 +208,6 @@ func (e *emitter) printSourceFile(jsFilePath string, sourceMapFilePath string, s
return !data.SkippedDtsWrite
}

func getSourceFilePathInNewDir(fileName string, newDirPath string, currentDirectory string, commonSourceDirectory string, useCaseSensitiveFileNames bool) string {
sourceFilePath := tspath.GetNormalizedAbsolutePath(fileName, currentDirectory)
commonSourceDirectory = tspath.EnsureTrailingDirectorySeparator(commonSourceDirectory)
isSourceFileInCommonSourceDirectory := tspath.ContainsPath(commonSourceDirectory, sourceFilePath, tspath.ComparePathsOptions{
UseCaseSensitiveFileNames: useCaseSensitiveFileNames,
CurrentDirectory: currentDirectory,
})
if isSourceFileInCommonSourceDirectory {
sourceFilePath = sourceFilePath[len(commonSourceDirectory):]
}
return tspath.CombinePaths(newDirPath, sourceFilePath)
}

func getOwnEmitOutputFilePath(fileName string, host printer.EmitHost, extension string) string {
compilerOptions := host.Options()
var emitOutputFilePathWithoutExtension string
if len(compilerOptions.OutDir) > 0 {
currentDirectory := host.GetCurrentDirectory()
emitOutputFilePathWithoutExtension = tspath.RemoveFileExtension(getSourceFilePathInNewDir(
fileName,
compilerOptions.OutDir,
currentDirectory,
host.CommonSourceDirectory(),
host.UseCaseSensitiveFileNames(),
))
} else {
emitOutputFilePathWithoutExtension = tspath.RemoveFileExtension(fileName)
}
return emitOutputFilePathWithoutExtension + extension
}

func getSourceMapFilePath(jsFilePath string, options *core.CompilerOptions) string {
if options.SourceMap.IsTrue() && !options.InlineSourceMap.IsTrue() {
return jsFilePath + ".map"
}
return ""
}

func shouldEmitSourceMaps(mapOptions *core.CompilerOptions, sourceFile *ast.SourceFile) bool {
return (mapOptions.SourceMap.IsTrue() || mapOptions.InlineSourceMap.IsTrue()) &&
!tspath.FileExtensionIs(sourceFile.FileName(), tspath.ExtensionJson)
Expand Down Expand Up @@ -274,7 +237,7 @@ func (e *emitter) getSourceMapDirectory(mapOptions *core.CompilerOptions, filePa
if sourceFile != nil {
// For modules or multiple emit files the mapRoot will have directory structure like the sources
// So if src\a.ts and src\lib\b.ts are compiled together user would be moving the maps into mapRoot\a.js.map and mapRoot\lib\b.js.map
sourceMapDir = tspath.GetDirectoryPath(getSourceFilePathInNewDir(
sourceMapDir = tspath.GetDirectoryPath(outputpaths.GetSourceFilePathInNewDir(
sourceFile.FileName(),
sourceMapDir,
e.host.GetCurrentDirectory(),
Expand Down Expand Up @@ -305,7 +268,7 @@ func (e *emitter) getSourceMappingURL(mapOptions *core.CompilerOptions, sourceMa
if sourceFile != nil {
// For modules or multiple emit files the mapRoot will have directory structure like the sources
// So if src\a.ts and src\lib\b.ts are compiled together user would be moving the maps into mapRoot\a.js.map and mapRoot\lib\b.js.map
sourceMapDir = tspath.GetDirectoryPath(getSourceFilePathInNewDir(
sourceMapDir = tspath.GetDirectoryPath(outputpaths.GetSourceFilePathInNewDir(
sourceFile.FileName(),
sourceMapDir,
e.host.GetCurrentDirectory(),
Expand Down Expand Up @@ -334,91 +297,6 @@ func (e *emitter) getSourceMappingURL(mapOptions *core.CompilerOptions, sourceMa
return stringutil.EncodeURI(sourceMapFile)
}

func getDeclarationEmitOutputFilePath(file string, host EmitHost) string {
options := host.Options()
var outputDir *string
if len(options.DeclarationDir) > 0 {
outputDir = &options.DeclarationDir
} else if len(options.OutDir) > 0 {
outputDir = &options.OutDir
}

var path string
if outputDir != nil {
path = getSourceFilePathInNewDirWorker(file, *outputDir, host.GetCurrentDirectory(), host.CommonSourceDirectory(), host.UseCaseSensitiveFileNames())
} else {
path = file
}
declarationExtension := tspath.GetDeclarationEmitExtensionForPath(path)
return tspath.RemoveFileExtension(path) + declarationExtension
}

func getSourceFilePathInNewDirWorker(fileName string, newDirPath string, currentDirectory string, commonSourceDirectory string, useCaseSensitiveFileNames bool) string {
sourceFilePath := tspath.GetNormalizedAbsolutePath(fileName, currentDirectory)
commonDir := tspath.GetCanonicalFileName(commonSourceDirectory, useCaseSensitiveFileNames)
canonFile := tspath.GetCanonicalFileName(sourceFilePath, useCaseSensitiveFileNames)
isSourceFileInCommonSourceDirectory := strings.HasPrefix(canonFile, commonDir)
if isSourceFileInCommonSourceDirectory {
sourceFilePath = sourceFilePath[len(commonSourceDirectory):]
}
return tspath.CombinePaths(newDirPath, sourceFilePath)
}

type outputPaths struct {
jsFilePath string
sourceMapFilePath string
declarationFilePath string
declarationMapPath string
buildInfoPath string
}

// DeclarationFilePath implements declarations.OutputPaths.
func (o *outputPaths) DeclarationFilePath() string {
return o.declarationFilePath
}

// JsFilePath implements declarations.OutputPaths.
func (o *outputPaths) JsFilePath() string {
return o.jsFilePath
}

func getOutputPathsFor(sourceFile *ast.SourceFile, host EmitHost, forceDtsEmit bool) *outputPaths {
options := host.Options()
// !!! bundle not implemented, may be deprecated
ownOutputFilePath := getOwnEmitOutputFilePath(sourceFile.FileName(), host, core.GetOutputExtension(sourceFile.FileName(), options.Jsx))
isJsonFile := ast.IsJsonSourceFile(sourceFile)
// If json file emits to the same location skip writing it, if emitDeclarationOnly skip writing it
isJsonEmittedToSameLocation := isJsonFile &&
tspath.ComparePaths(sourceFile.FileName(), ownOutputFilePath, tspath.ComparePathsOptions{
CurrentDirectory: host.GetCurrentDirectory(),
UseCaseSensitiveFileNames: host.UseCaseSensitiveFileNames(),
}) == 0
paths := &outputPaths{}
if options.EmitDeclarationOnly != core.TSTrue && !isJsonEmittedToSameLocation {
paths.jsFilePath = ownOutputFilePath
if !ast.IsJsonSourceFile(sourceFile) {
paths.sourceMapFilePath = getSourceMapFilePath(paths.jsFilePath, options)
}
}
if forceDtsEmit || options.GetEmitDeclarations() && !isJsonFile {
paths.declarationFilePath = getDeclarationEmitOutputFilePath(sourceFile.FileName(), host)
if options.GetAreDeclarationMapsEnabled() {
paths.declarationMapPath = paths.declarationFilePath + ".map"
}
}
return paths
}

func forEachEmittedFile(host EmitHost, action func(emitFileNames *outputPaths, sourceFile *ast.SourceFile) bool, sourceFiles []*ast.SourceFile, options *EmitOptions) bool {
// !!! outFile not yet implemented, may be deprecated
for _, sourceFile := range sourceFiles {
if action(getOutputPathsFor(sourceFile, host, options.forceDtsEmit), sourceFile) {
return true
}
}
return false
}

func sourceFileMayBeEmitted(sourceFile *ast.SourceFile, host printer.EmitHost, forceDtsEmit bool) bool {
// !!! Js files are emitted only if option is enabled

Expand Down
Loading
Loading