483 lines
18 KiB
Go
483 lines
18 KiB
Go
package compiler
|
|
|
|
import (
|
|
"encoding/base64"
|
|
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/binder"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/core"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/diagnostics"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/outputpaths"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/printer"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/sourcemap"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/stringutil"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/transformers"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/transformers/declarations"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/transformers/estransforms"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/transformers/inliners"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/transformers/jsxtransforms"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/transformers/moduletransforms"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/transformers/tstransforms"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/tsoptions"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath"
|
|
)
|
|
|
|
type EmitOnly byte
|
|
|
|
const (
|
|
EmitAll EmitOnly = iota
|
|
EmitOnlyJs
|
|
EmitOnlyDts
|
|
EmitOnlyForcedDts
|
|
)
|
|
|
|
type emitter struct {
|
|
host EmitHost
|
|
emitOnly EmitOnly
|
|
emitterDiagnostics ast.DiagnosticsCollection
|
|
writer printer.EmitTextWriter
|
|
paths *outputpaths.OutputPaths
|
|
sourceFile *ast.SourceFile
|
|
emitResult EmitResult
|
|
writeFile func(fileName string, text string, writeByteOrderMark bool, data *WriteFileData) error
|
|
}
|
|
|
|
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.emitResult.Diagnostics = e.emitterDiagnostics.GetDiagnostics()
|
|
}
|
|
|
|
func (e *emitter) getDeclarationTransformers(emitContext *printer.EmitContext, declarationFilePath string, declarationMapPath string) []*declarations.DeclarationTransformer {
|
|
transform := declarations.NewDeclarationTransformer(e.host, emitContext, e.host.Options(), declarationFilePath, declarationMapPath)
|
|
return []*declarations.DeclarationTransformer{transform}
|
|
}
|
|
|
|
func getModuleTransformer(opts *transformers.TransformOptions) *transformers.Transformer {
|
|
switch opts.CompilerOptions.GetEmitModuleKind() {
|
|
case core.ModuleKindPreserve:
|
|
// `ESModuleTransformer` contains logic for preserving CJS input syntax in `--module preserve`
|
|
return moduletransforms.NewESModuleTransformer(opts)
|
|
|
|
case core.ModuleKindESNext,
|
|
core.ModuleKindES2022,
|
|
core.ModuleKindES2020,
|
|
core.ModuleKindES2015,
|
|
core.ModuleKindNode20,
|
|
core.ModuleKindNode18,
|
|
core.ModuleKindNode16,
|
|
core.ModuleKindNodeNext,
|
|
core.ModuleKindCommonJS:
|
|
return moduletransforms.NewImpliedModuleTransformer(opts)
|
|
|
|
default:
|
|
return moduletransforms.NewCommonJSModuleTransformer(opts)
|
|
}
|
|
}
|
|
|
|
func getScriptTransformers(emitContext *printer.EmitContext, host printer.EmitHost, sourceFile *ast.SourceFile) []*transformers.Transformer {
|
|
var tx []*transformers.Transformer
|
|
options := host.Options()
|
|
|
|
// JS files don't use reference calculations as they don't do import elision, no need to calculate it
|
|
importElisionEnabled := !options.VerbatimModuleSyntax.IsTrue() && !ast.IsInJSFile(sourceFile.AsNode())
|
|
|
|
var emitResolver printer.EmitResolver
|
|
var referenceResolver binder.ReferenceResolver
|
|
if importElisionEnabled || options.GetJSXTransformEnabled() || !options.GetIsolatedModules() { // full emit resolver is needed for import ellision and const enum inlining
|
|
emitResolver = host.GetEmitResolver()
|
|
emitResolver.MarkLinkedReferencesRecursively(sourceFile)
|
|
referenceResolver = emitResolver
|
|
} else {
|
|
referenceResolver = binder.NewReferenceResolver(options, binder.ReferenceResolverHooks{})
|
|
}
|
|
|
|
opts := transformers.TransformOptions{
|
|
Context: emitContext,
|
|
CompilerOptions: options,
|
|
Resolver: referenceResolver,
|
|
EmitResolver: emitResolver,
|
|
GetEmitModuleFormatOfFile: host.GetEmitModuleFormatOfFile,
|
|
}
|
|
|
|
// transform TypeScript syntax
|
|
{
|
|
// erase types
|
|
tx = append(tx, tstransforms.NewTypeEraserTransformer(&opts))
|
|
|
|
// elide imports
|
|
if importElisionEnabled {
|
|
tx = append(tx, tstransforms.NewImportElisionTransformer(&opts))
|
|
}
|
|
|
|
// transform `enum`, `namespace`, and parameter properties
|
|
tx = append(tx, tstransforms.NewRuntimeSyntaxTransformer(&opts))
|
|
}
|
|
|
|
// !!! transform legacy decorator syntax
|
|
if options.GetJSXTransformEnabled() {
|
|
tx = append(tx, jsxtransforms.NewJSXTransformer(&opts))
|
|
}
|
|
|
|
downleveler := estransforms.GetESTransformer(&opts)
|
|
if downleveler != nil {
|
|
tx = append(tx, downleveler)
|
|
}
|
|
|
|
// transform module syntax
|
|
tx = append(tx, getModuleTransformer(&opts))
|
|
|
|
// inlining (formerly done via substitutions)
|
|
if !options.GetIsolatedModules() {
|
|
tx = append(tx, inliners.NewConstEnumInliningTransformer(&opts))
|
|
}
|
|
return tx
|
|
}
|
|
|
|
func (e *emitter) emitJSFile(sourceFile *ast.SourceFile, jsFilePath string, sourceMapFilePath string) {
|
|
options := e.host.Options()
|
|
|
|
if sourceFile == nil || e.emitOnly != EmitAll && e.emitOnly != EmitOnlyJs || len(jsFilePath) == 0 {
|
|
return
|
|
}
|
|
|
|
if options.NoEmit == core.TSTrue || e.host.IsEmitBlocked(jsFilePath) {
|
|
e.emitResult.EmitSkipped = true
|
|
return
|
|
}
|
|
|
|
emitContext, putEmitContext := printer.GetEmitContext()
|
|
defer putEmitContext()
|
|
|
|
for _, transformer := range getScriptTransformers(emitContext, e.host, sourceFile) {
|
|
sourceFile = transformer.TransformSourceFile(sourceFile)
|
|
}
|
|
|
|
printerOptions := printer.PrinterOptions{
|
|
RemoveComments: options.RemoveComments.IsTrue(),
|
|
NewLine: options.NewLine,
|
|
NoEmitHelpers: options.NoEmitHelpers.IsTrue(),
|
|
SourceMap: options.SourceMap.IsTrue(),
|
|
InlineSourceMap: options.InlineSourceMap.IsTrue(),
|
|
InlineSources: options.InlineSources.IsTrue(),
|
|
// !!!
|
|
}
|
|
|
|
// create a printer to print the nodes
|
|
printer := printer.NewPrinter(printerOptions, printer.PrintHandlers{
|
|
// !!!
|
|
}, emitContext)
|
|
|
|
e.printSourceFile(jsFilePath, sourceMapFilePath, sourceFile, printer, shouldEmitSourceMaps(options, sourceFile))
|
|
}
|
|
|
|
func (e *emitter) emitDeclarationFile(sourceFile *ast.SourceFile, declarationFilePath string, declarationMapPath string) {
|
|
options := e.host.Options()
|
|
|
|
if sourceFile == nil || e.emitOnly == EmitOnlyJs || len(declarationFilePath) == 0 {
|
|
return
|
|
}
|
|
|
|
if e.emitOnly != EmitOnlyForcedDts && (options.NoEmit == core.TSTrue || e.host.IsEmitBlocked(declarationFilePath)) {
|
|
e.emitResult.EmitSkipped = true
|
|
return
|
|
}
|
|
|
|
var diags []*ast.Diagnostic
|
|
emitContext, putEmitContext := printer.GetEmitContext()
|
|
defer putEmitContext()
|
|
for _, transformer := range e.getDeclarationTransformers(emitContext, declarationFilePath, declarationMapPath) {
|
|
sourceFile = transformer.TransformSourceFile(sourceFile)
|
|
diags = append(diags, transformer.GetDiagnostics()...)
|
|
}
|
|
|
|
// !!! strada skipped emit if there were diagnostics
|
|
|
|
printerOptions := printer.PrinterOptions{
|
|
RemoveComments: options.RemoveComments.IsTrue(),
|
|
OnlyPrintJSDocStyle: true,
|
|
NewLine: options.NewLine,
|
|
NoEmitHelpers: options.NoEmitHelpers.IsTrue(),
|
|
SourceMap: options.DeclarationMap.IsTrue(),
|
|
InlineSourceMap: options.InlineSourceMap.IsTrue(),
|
|
InlineSources: options.InlineSources.IsTrue(),
|
|
// !!!
|
|
}
|
|
|
|
// create a printer to print the nodes
|
|
printer := printer.NewPrinter(printerOptions, printer.PrintHandlers{
|
|
// !!!
|
|
}, emitContext)
|
|
|
|
for _, elem := range diags {
|
|
// Add declaration transform diagnostics to emit diagnostics
|
|
e.emitterDiagnostics.Add(elem)
|
|
}
|
|
e.printSourceFile(declarationFilePath, declarationMapPath, sourceFile, printer, e.emitOnly != EmitOnlyForcedDts && shouldEmitDeclarationSourceMaps(options, sourceFile))
|
|
}
|
|
|
|
func (e *emitter) printSourceFile(jsFilePath string, sourceMapFilePath string, sourceFile *ast.SourceFile, printer_ *printer.Printer, shouldEmitSourceMaps bool) {
|
|
// !!! sourceMapGenerator
|
|
options := e.host.Options()
|
|
var sourceMapGenerator *sourcemap.Generator
|
|
if shouldEmitSourceMaps {
|
|
sourceMapGenerator = sourcemap.NewGenerator(
|
|
tspath.GetBaseFileName(tspath.NormalizeSlashes(jsFilePath)),
|
|
getSourceRoot(options),
|
|
e.getSourceMapDirectory(options, jsFilePath, sourceFile),
|
|
tspath.ComparePathsOptions{
|
|
UseCaseSensitiveFileNames: e.host.UseCaseSensitiveFileNames(),
|
|
CurrentDirectory: e.host.GetCurrentDirectory(),
|
|
},
|
|
)
|
|
}
|
|
|
|
printer_.Write(sourceFile.AsNode(), sourceFile, e.writer, sourceMapGenerator)
|
|
|
|
sourceMapUrlPos := -1
|
|
if sourceMapGenerator != nil {
|
|
if options.SourceMap.IsTrue() || options.InlineSourceMap.IsTrue() || options.GetAreDeclarationMapsEnabled() {
|
|
e.emitResult.SourceMaps = append(e.emitResult.SourceMaps, &SourceMapEmitResult{
|
|
InputSourceFileNames: sourceMapGenerator.Sources(),
|
|
SourceMap: sourceMapGenerator.RawSourceMap(),
|
|
GeneratedFile: jsFilePath,
|
|
})
|
|
}
|
|
|
|
sourceMappingURL := e.getSourceMappingURL(
|
|
options,
|
|
sourceMapGenerator,
|
|
jsFilePath,
|
|
sourceMapFilePath,
|
|
sourceFile,
|
|
)
|
|
|
|
if len(sourceMappingURL) > 0 {
|
|
if !e.writer.IsAtStartOfLine() {
|
|
e.writer.RawWrite(core.IfElse(options.NewLine == core.NewLineKindCRLF, "\r\n", "\n"))
|
|
}
|
|
sourceMapUrlPos = e.writer.GetTextPos()
|
|
e.writer.WriteComment("//# sourceMappingURL=" + sourceMappingURL)
|
|
}
|
|
|
|
// Write the source map
|
|
if len(sourceMapFilePath) > 0 {
|
|
sourceMap := sourceMapGenerator.String()
|
|
err := e.writeText(sourceMapFilePath, sourceMap, false /*writeByteOrderMark*/, nil)
|
|
if err != nil {
|
|
e.emitterDiagnostics.Add(ast.NewCompilerDiagnostic(diagnostics.Could_not_write_file_0_Colon_1, jsFilePath, err.Error()))
|
|
} else {
|
|
e.emitResult.EmittedFiles = append(e.emitResult.EmittedFiles, sourceMapFilePath)
|
|
}
|
|
}
|
|
} else {
|
|
e.writer.WriteLine()
|
|
}
|
|
|
|
// Write the output file
|
|
text := e.writer.String()
|
|
data := &WriteFileData{
|
|
SourceMapUrlPos: sourceMapUrlPos,
|
|
Diagnostics: e.emitterDiagnostics.GetDiagnostics(),
|
|
}
|
|
err := e.writeText(jsFilePath, text, options.EmitBOM.IsTrue(), data)
|
|
skippedDtsWrite := data.SkippedDtsWrite
|
|
if err != nil {
|
|
e.emitterDiagnostics.Add(ast.NewCompilerDiagnostic(diagnostics.Could_not_write_file_0_Colon_1, jsFilePath, err.Error()))
|
|
} else if !skippedDtsWrite {
|
|
e.emitResult.EmittedFiles = append(e.emitResult.EmittedFiles, jsFilePath)
|
|
}
|
|
|
|
// Reset state
|
|
e.writer.Clear()
|
|
}
|
|
|
|
func (e *emitter) writeText(fileName string, text string, writeByteOrderMark bool, data *WriteFileData) error {
|
|
if e.writeFile != nil {
|
|
return e.writeFile(fileName, text, writeByteOrderMark, data)
|
|
}
|
|
return e.host.WriteFile(fileName, text, writeByteOrderMark)
|
|
}
|
|
|
|
func shouldEmitSourceMaps(mapOptions *core.CompilerOptions, sourceFile *ast.SourceFile) bool {
|
|
return (mapOptions.SourceMap.IsTrue() || mapOptions.InlineSourceMap.IsTrue()) &&
|
|
!tspath.FileExtensionIs(sourceFile.FileName(), tspath.ExtensionJson)
|
|
}
|
|
|
|
func shouldEmitDeclarationSourceMaps(mapOptions *core.CompilerOptions, sourceFile *ast.SourceFile) bool {
|
|
return mapOptions.DeclarationMap.IsTrue() &&
|
|
!tspath.FileExtensionIs(sourceFile.FileName(), tspath.ExtensionJson)
|
|
}
|
|
|
|
func getSourceRoot(mapOptions *core.CompilerOptions) string {
|
|
// Normalize source root and make sure it has trailing "/" so that it can be used to combine paths with the
|
|
// relative paths of the sources list in the sourcemap
|
|
sourceRoot := tspath.NormalizeSlashes(mapOptions.SourceRoot)
|
|
if len(sourceRoot) > 0 {
|
|
sourceRoot = tspath.EnsureTrailingDirectorySeparator(sourceRoot)
|
|
}
|
|
return sourceRoot
|
|
}
|
|
|
|
func (e *emitter) getSourceMapDirectory(mapOptions *core.CompilerOptions, filePath string, sourceFile *ast.SourceFile) string {
|
|
if len(mapOptions.SourceRoot) > 0 {
|
|
return e.host.CommonSourceDirectory()
|
|
}
|
|
if len(mapOptions.MapRoot) > 0 {
|
|
sourceMapDir := tspath.NormalizeSlashes(mapOptions.MapRoot)
|
|
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(outputpaths.GetSourceFilePathInNewDir(
|
|
sourceFile.FileName(),
|
|
sourceMapDir,
|
|
e.host.GetCurrentDirectory(),
|
|
e.host.CommonSourceDirectory(),
|
|
e.host.UseCaseSensitiveFileNames(),
|
|
))
|
|
}
|
|
if tspath.GetRootLength(sourceMapDir) == 0 {
|
|
// The relative paths are relative to the common directory
|
|
sourceMapDir = tspath.CombinePaths(e.host.CommonSourceDirectory(), sourceMapDir)
|
|
}
|
|
return sourceMapDir
|
|
}
|
|
return tspath.GetDirectoryPath(tspath.NormalizePath(filePath))
|
|
}
|
|
|
|
func (e *emitter) getSourceMappingURL(mapOptions *core.CompilerOptions, sourceMapGenerator *sourcemap.Generator, filePath string, sourceMapFilePath string, sourceFile *ast.SourceFile) string {
|
|
if mapOptions.InlineSourceMap.IsTrue() {
|
|
// Encode the sourceMap into the sourceMap url
|
|
sourceMapText := sourceMapGenerator.String()
|
|
base64SourceMapText := base64.StdEncoding.EncodeToString([]byte(sourceMapText))
|
|
return "data:application/json;base64," + base64SourceMapText
|
|
}
|
|
|
|
sourceMapFile := tspath.GetBaseFileName(tspath.NormalizeSlashes(sourceMapFilePath))
|
|
if len(mapOptions.MapRoot) > 0 {
|
|
sourceMapDir := tspath.NormalizeSlashes(mapOptions.MapRoot)
|
|
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(outputpaths.GetSourceFilePathInNewDir(
|
|
sourceFile.FileName(),
|
|
sourceMapDir,
|
|
e.host.GetCurrentDirectory(),
|
|
e.host.CommonSourceDirectory(),
|
|
e.host.UseCaseSensitiveFileNames(),
|
|
))
|
|
}
|
|
if tspath.GetRootLength(sourceMapDir) == 0 {
|
|
// The relative paths are relative to the common directory
|
|
sourceMapDir = tspath.CombinePaths(e.host.CommonSourceDirectory(), sourceMapDir)
|
|
return stringutil.EncodeURI(
|
|
tspath.GetRelativePathToDirectoryOrUrl(
|
|
tspath.GetDirectoryPath(tspath.NormalizePath(filePath)), // get the relative sourceMapDir path based on jsFilePath
|
|
tspath.CombinePaths(sourceMapDir, sourceMapFile), // this is where user expects to see sourceMap
|
|
/*isAbsolutePathAnUrl*/ true,
|
|
tspath.ComparePathsOptions{
|
|
UseCaseSensitiveFileNames: e.host.UseCaseSensitiveFileNames(),
|
|
CurrentDirectory: e.host.GetCurrentDirectory(),
|
|
},
|
|
),
|
|
)
|
|
} else {
|
|
return stringutil.EncodeURI(tspath.CombinePaths(sourceMapDir, sourceMapFile))
|
|
}
|
|
}
|
|
return stringutil.EncodeURI(sourceMapFile)
|
|
}
|
|
|
|
type SourceFileMayBeEmittedHost interface {
|
|
Options() *core.CompilerOptions
|
|
GetProjectReferenceFromSource(path tspath.Path) *tsoptions.SourceOutputAndProjectReference
|
|
IsSourceFileFromExternalLibrary(file *ast.SourceFile) bool
|
|
GetCurrentDirectory() string
|
|
UseCaseSensitiveFileNames() bool
|
|
SourceFiles() []*ast.SourceFile
|
|
}
|
|
|
|
func sourceFileMayBeEmitted(sourceFile *ast.SourceFile, host SourceFileMayBeEmittedHost, forceDtsEmit bool) bool {
|
|
// TODO: move this to outputpaths?
|
|
|
|
options := host.Options()
|
|
// Js files are emitted only if option is enabled
|
|
if options.NoEmitForJsFiles.IsTrue() && ast.IsSourceFileJS(sourceFile) {
|
|
return false
|
|
}
|
|
|
|
// Declaration files are not emitted
|
|
if sourceFile.IsDeclarationFile {
|
|
return false
|
|
}
|
|
|
|
// Source file from node_modules are not emitted
|
|
if host.IsSourceFileFromExternalLibrary(sourceFile) {
|
|
return false
|
|
}
|
|
|
|
// forcing dts emit => file needs to be emitted
|
|
if forceDtsEmit {
|
|
return true
|
|
}
|
|
|
|
// Check other conditions for file emit
|
|
// Source files from referenced projects are not emitted
|
|
if host.GetProjectReferenceFromSource(sourceFile.Path()) != nil {
|
|
return false
|
|
}
|
|
|
|
// Any non json file should be emitted
|
|
if !ast.IsJsonSourceFile(sourceFile) {
|
|
return true
|
|
}
|
|
|
|
// Json file is not emitted if outDir is not specified
|
|
if options.OutDir == "" {
|
|
return false
|
|
}
|
|
|
|
// Otherwise if rootDir or composite config file, we know common sourceDir and can check if file would be emitted in same location
|
|
if options.RootDir != "" || (options.Composite.IsTrue() && options.ConfigFilePath != "") {
|
|
commonDir := tspath.GetNormalizedAbsolutePath(outputpaths.GetCommonSourceDirectory(options, func() []string { return nil }, host.GetCurrentDirectory(), host.UseCaseSensitiveFileNames()), host.GetCurrentDirectory())
|
|
outputPath := outputpaths.GetSourceFilePathInNewDirWorker(sourceFile.FileName(), options.OutDir, host.GetCurrentDirectory(), commonDir, host.UseCaseSensitiveFileNames())
|
|
if tspath.ComparePaths(sourceFile.FileName(), outputPath, tspath.ComparePathsOptions{
|
|
UseCaseSensitiveFileNames: host.UseCaseSensitiveFileNames(),
|
|
CurrentDirectory: host.GetCurrentDirectory(),
|
|
}) == 0 {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func getSourceFilesToEmit(host SourceFileMayBeEmittedHost, targetSourceFile *ast.SourceFile, forceDtsEmit bool) []*ast.SourceFile {
|
|
var sourceFiles []*ast.SourceFile
|
|
if targetSourceFile != nil {
|
|
sourceFiles = []*ast.SourceFile{targetSourceFile}
|
|
} else {
|
|
sourceFiles = host.SourceFiles()
|
|
}
|
|
return core.Filter(sourceFiles, func(sourceFile *ast.SourceFile) bool {
|
|
return sourceFileMayBeEmitted(sourceFile, host, forceDtsEmit)
|
|
})
|
|
}
|
|
|
|
func isSourceFileNotJson(file *ast.SourceFile) bool {
|
|
return !ast.IsJsonSourceFile(file)
|
|
}
|
|
|
|
func getDeclarationDiagnostics(host EmitHost, file *ast.SourceFile) []*ast.Diagnostic {
|
|
// TODO: use p.getSourceFilesToEmit cache
|
|
fullFiles := core.Filter(getSourceFilesToEmit(host, file, false), isSourceFileNotJson)
|
|
if !core.Some(fullFiles, func(f *ast.SourceFile) bool { return f == file }) {
|
|
return []*ast.Diagnostic{}
|
|
}
|
|
options := host.Options()
|
|
transform := declarations.NewDeclarationTransformer(host, nil, options, "", "")
|
|
transform.TransformSourceFile(file)
|
|
return transform.GetDiagnostics()
|
|
}
|