|
| 1 | +package compiler |
| 2 | + |
| 3 | +import ( |
| 4 | + "github.com/microsoft/typescript-go/internal/ast" |
| 5 | + "github.com/microsoft/typescript-go/internal/compiler/diagnostics" |
| 6 | + "github.com/microsoft/typescript-go/internal/core" |
| 7 | + "github.com/microsoft/typescript-go/internal/printer" |
| 8 | + "github.com/microsoft/typescript-go/internal/tspath" |
| 9 | +) |
| 10 | + |
| 11 | +type emitOnly byte |
| 12 | + |
| 13 | +const ( |
| 14 | + emitAll emitOnly = iota |
| 15 | + emitOnlyJs |
| 16 | + emitOnlyDts |
| 17 | + emitOnlyBuildInfo |
| 18 | +) |
| 19 | + |
| 20 | +type emitter struct { |
| 21 | + host EmitHost |
| 22 | + emitOnly emitOnly |
| 23 | + emittedFilesList []string |
| 24 | + emitterDiagnostics DiagnosticsCollection |
| 25 | + emitSkipped bool |
| 26 | + sourceMapDataList []*sourceMapEmitResult |
| 27 | + writer printer.EmitTextWriter |
| 28 | + paths *outputPaths |
| 29 | + sourceFile *ast.SourceFile |
| 30 | +} |
| 31 | + |
| 32 | +func (e *emitter) emit() { |
| 33 | + // !!! tracing |
| 34 | + e.emitJsFile(e.sourceFile, e.paths.jsFilePath, e.paths.sourceMapFilePath) |
| 35 | + e.emitDeclarationFile(e.sourceFile, e.paths.declarationFilePath, e.paths.declarationMapPath) |
| 36 | + e.emitBuildInfo(e.paths.buildInfoPath) |
| 37 | +} |
| 38 | + |
| 39 | +func (e *emitter) emitJsFile(sourceFile *ast.SourceFile, jsFilePath string, sourceMapFilePath string) { |
| 40 | + options := e.host.Options() |
| 41 | + |
| 42 | + if sourceFile == nil || e.emitOnly != emitAll && e.emitOnly != emitOnlyJs || len(jsFilePath) == 0 { |
| 43 | + return |
| 44 | + } |
| 45 | + if options.NoEmit == core.TSTrue || e.host.IsEmitBlocked(jsFilePath) { |
| 46 | + return |
| 47 | + } |
| 48 | + |
| 49 | + // !!! mark linked references |
| 50 | + // !!! transform the source files? |
| 51 | + |
| 52 | + printerOptions := printer.PrinterOptions{ |
| 53 | + NewLine: options.NewLine, |
| 54 | + // !!! |
| 55 | + } |
| 56 | + |
| 57 | + // create a printer to print the nodes |
| 58 | + printer := printer.NewPrinter(printerOptions, printer.PrintHandlers{ |
| 59 | + // !!! |
| 60 | + }) |
| 61 | + |
| 62 | + e.printSourceFile(jsFilePath, sourceMapFilePath, sourceFile, printer) |
| 63 | + |
| 64 | + if e.emittedFilesList != nil { |
| 65 | + e.emittedFilesList = append(e.emittedFilesList, jsFilePath) |
| 66 | + if sourceMapFilePath != "" { |
| 67 | + e.emittedFilesList = append(e.emittedFilesList, sourceMapFilePath) |
| 68 | + } |
| 69 | + } |
| 70 | +} |
| 71 | + |
| 72 | +func (e *emitter) emitDeclarationFile(sourceFile *ast.SourceFile, declarationFilePath string, declarationMapPath string) { |
| 73 | + // !!! |
| 74 | +} |
| 75 | + |
| 76 | +func (e *emitter) emitBuildInfo(buildInfoPath string) { |
| 77 | + // !!! |
| 78 | +} |
| 79 | + |
| 80 | +func (e *emitter) printSourceFile(jsFilePath string, sourceMapFilePath string, sourceFile *ast.SourceFile, printer *printer.Printer) bool { |
| 81 | + // !!! sourceMapGenerator |
| 82 | + // !!! bundles not implemented, may be deprecated |
| 83 | + sourceFiles := []*ast.SourceFile{sourceFile} |
| 84 | + |
| 85 | + printer.Write(sourceFile.AsNode(), sourceFile, e.writer /*, sourceMapGenerator*/) |
| 86 | + |
| 87 | + // !!! add sourceMapGenerator to sourceMapDataList |
| 88 | + // !!! append sourceMappingURL to output |
| 89 | + // !!! write the source map |
| 90 | + e.writer.WriteLine() |
| 91 | + |
| 92 | + // Write the output file |
| 93 | + text := e.writer.String() |
| 94 | + data := &WriteFileData{} // !!! |
| 95 | + err := e.host.WriteFile(jsFilePath, text, e.host.Options().EmitBOM == core.TSTrue, sourceFiles, data) |
| 96 | + if err != nil { |
| 97 | + e.emitterDiagnostics.add(ast.NewCompilerDiagnostic(diagnostics.Could_not_write_file_0_Colon_1, jsFilePath, err.Error())) |
| 98 | + } |
| 99 | + |
| 100 | + // Reset state |
| 101 | + e.writer.Clear() |
| 102 | + return !data.SkippedDtsWrite |
| 103 | +} |
| 104 | + |
| 105 | +func getOutputExtension(fileName string, jsx core.JsxEmit) string { |
| 106 | + switch { |
| 107 | + case tspath.FileExtensionIs(fileName, tspath.ExtensionJson): |
| 108 | + return tspath.ExtensionJson |
| 109 | + case jsx == core.JsxEmitPreserve && tspath.FileExtensionIsOneOf(fileName, []string{tspath.ExtensionJsx, tspath.ExtensionTsx}): |
| 110 | + return tspath.ExtensionJsx |
| 111 | + case tspath.FileExtensionIsOneOf(fileName, []string{tspath.ExtensionMts, tspath.ExtensionMjs}): |
| 112 | + return tspath.ExtensionMjs |
| 113 | + case tspath.FileExtensionIsOneOf(fileName, []string{tspath.ExtensionCts, tspath.ExtensionCjs}): |
| 114 | + return tspath.ExtensionCjs |
| 115 | + default: |
| 116 | + return tspath.ExtensionJs |
| 117 | + } |
| 118 | +} |
| 119 | + |
| 120 | +func getSourceFilePathInNewDir(fileName string, newDirPath string, currentDirectory string, commonSourceDirectory string, useCaseSensitiveFileNames bool) string { |
| 121 | + sourceFilePath := tspath.GetNormalizedAbsolutePath(fileName, currentDirectory) |
| 122 | + commonSourceDirectory = tspath.EnsureTrailingDirectorySeparator(commonSourceDirectory) |
| 123 | + isSourceFileInCommonSourceDirectory := tspath.ContainsPath(commonSourceDirectory, sourceFilePath, tspath.ComparePathsOptions{ |
| 124 | + UseCaseSensitiveFileNames: useCaseSensitiveFileNames, |
| 125 | + CurrentDirectory: currentDirectory, |
| 126 | + }) |
| 127 | + if isSourceFileInCommonSourceDirectory { |
| 128 | + sourceFilePath = sourceFilePath[len(commonSourceDirectory):] |
| 129 | + } |
| 130 | + return tspath.CombinePaths(newDirPath, sourceFilePath) |
| 131 | +} |
| 132 | + |
| 133 | +func getOwnEmitOutputFilePath(fileName string, host EmitHost, extension string) string { |
| 134 | + compilerOptions := host.Options() |
| 135 | + var emitOutputFilePathWithoutExtension string |
| 136 | + if len(compilerOptions.OutDir) > 0 { |
| 137 | + currentDirectory := host.GetCurrentDirectory() |
| 138 | + emitOutputFilePathWithoutExtension = tspath.RemoveFileExtension(getSourceFilePathInNewDir( |
| 139 | + fileName, |
| 140 | + compilerOptions.OutDir, |
| 141 | + currentDirectory, |
| 142 | + host.CommonSourceDirectory(), |
| 143 | + host.UseCaseSensitiveFileNames(), |
| 144 | + )) |
| 145 | + } else { |
| 146 | + emitOutputFilePathWithoutExtension = tspath.RemoveFileExtension(fileName) |
| 147 | + } |
| 148 | + return emitOutputFilePathWithoutExtension + extension |
| 149 | +} |
| 150 | + |
| 151 | +func getSourceMapFilePath(jsFilePath string, options *core.CompilerOptions) string { |
| 152 | + // !!! |
| 153 | + return "" |
| 154 | +} |
| 155 | + |
| 156 | +func getDeclarationEmitOutputFilePath(file string, host EmitHost) string { |
| 157 | + // !!! |
| 158 | + return "" |
| 159 | +} |
| 160 | + |
| 161 | +type outputPaths struct { |
| 162 | + jsFilePath string |
| 163 | + sourceMapFilePath string |
| 164 | + declarationFilePath string |
| 165 | + declarationMapPath string |
| 166 | + buildInfoPath string |
| 167 | +} |
| 168 | + |
| 169 | +func getOutputPathsFor(sourceFile *ast.SourceFile, host EmitHost, forceDtsEmit bool) *outputPaths { |
| 170 | + options := host.Options() |
| 171 | + // !!! bundle not implemented, may be deprecated |
| 172 | + ownOutputFilePath := getOwnEmitOutputFilePath(sourceFile.FileName(), host, getOutputExtension(sourceFile.FileName(), options.Jsx)) |
| 173 | + isJsonFile := isJsonSourceFile(sourceFile) |
| 174 | + // If json file emits to the same location skip writing it, if emitDeclarationOnly skip writing it |
| 175 | + isJsonEmittedToSameLocation := isJsonFile && |
| 176 | + tspath.ComparePaths(sourceFile.FileName(), ownOutputFilePath, tspath.ComparePathsOptions{ |
| 177 | + CurrentDirectory: host.GetCurrentDirectory(), |
| 178 | + UseCaseSensitiveFileNames: host.UseCaseSensitiveFileNames(), |
| 179 | + }) == 0 |
| 180 | + paths := &outputPaths{} |
| 181 | + if options.EmitDeclarationOnly != core.TSTrue && !isJsonEmittedToSameLocation { |
| 182 | + paths.jsFilePath = ownOutputFilePath |
| 183 | + if !isJsonSourceFile(sourceFile) { |
| 184 | + paths.sourceMapFilePath = getSourceMapFilePath(paths.jsFilePath, options) |
| 185 | + } |
| 186 | + } |
| 187 | + if forceDtsEmit || options.GetEmitDeclarations() && !isJsonFile { |
| 188 | + paths.declarationFilePath = getDeclarationEmitOutputFilePath(sourceFile.FileName(), host) |
| 189 | + if options.GetAreDeclarationMapsEnabled() { |
| 190 | + paths.declarationMapPath = paths.declarationFilePath + ".map" |
| 191 | + } |
| 192 | + } |
| 193 | + return paths |
| 194 | +} |
| 195 | + |
| 196 | +func forEachEmittedFile(host EmitHost, action func(emitFileNames *outputPaths, sourceFile *ast.SourceFile) bool, sourceFiles []*ast.SourceFile, options *EmitOptions) bool { |
| 197 | + // !!! outFile not yet implemented, may be deprecated |
| 198 | + for _, sourceFile := range sourceFiles { |
| 199 | + if action(getOutputPathsFor(sourceFile, host, options.forceDtsEmit), sourceFile) { |
| 200 | + return true |
| 201 | + } |
| 202 | + } |
| 203 | + return false |
| 204 | +} |
| 205 | + |
| 206 | +func sourceFileMayBeEmitted(sourceFile *ast.SourceFile, host EmitHost, forceDtsEmit bool) bool { |
| 207 | + // !!! Js files are emitted only if option is enabled |
| 208 | + |
| 209 | + // Declaration files are not emitted |
| 210 | + if sourceFile.IsDeclarationFile { |
| 211 | + return false |
| 212 | + } |
| 213 | + |
| 214 | + // !!! Source file from node_modules are not emitted |
| 215 | + |
| 216 | + // forcing dts emit => file needs to be emitted |
| 217 | + if forceDtsEmit { |
| 218 | + return true |
| 219 | + } |
| 220 | + |
| 221 | + // !!! Source files from referenced projects are not emitted |
| 222 | + |
| 223 | + // Any non json file should be emitted |
| 224 | + if !isJsonSourceFile(sourceFile) { |
| 225 | + return true |
| 226 | + } |
| 227 | + |
| 228 | + // !!! Should JSON input files be emitted |
| 229 | + return false |
| 230 | +} |
| 231 | + |
| 232 | +func getSourceFilesToEmit(host EmitHost, targetSourceFile *ast.SourceFile, forceDtsEmit bool) []*ast.SourceFile { |
| 233 | + // !!! outFile not yet implemented, may be deprecated |
| 234 | + var sourceFiles []*ast.SourceFile |
| 235 | + if targetSourceFile != nil { |
| 236 | + sourceFiles = []*ast.SourceFile{targetSourceFile} |
| 237 | + } else { |
| 238 | + sourceFiles = host.SourceFiles() |
| 239 | + } |
| 240 | + return core.Filter(sourceFiles, func(sourceFile *ast.SourceFile) bool { |
| 241 | + return sourceFileMayBeEmitted(sourceFile, host, forceDtsEmit) |
| 242 | + }) |
| 243 | +} |
0 commit comments