@@ -15,7 +15,8 @@ export interface TransformJavascriptOptions {
1515 outputFilePath ?: string ;
1616 emitSourceMap ?: boolean ;
1717 strict ?: boolean ;
18- getTransforms : Array < ( program : ts . Program ) => ts . TransformerFactory < ts . SourceFile > > ;
18+ typeCheck ?: boolean ;
19+ getTransforms : Array < ( program ?: ts . Program ) => ts . TransformerFactory < ts . SourceFile > > ;
1920}
2021
2122export interface TransformJavascriptOutput {
@@ -24,6 +25,42 @@ export interface TransformJavascriptOutput {
2425 emitSkipped : boolean ;
2526}
2627
28+ interface DiagnosticSourceFile extends ts . SourceFile {
29+ readonly parseDiagnostics ?: ReadonlyArray < ts . Diagnostic > ;
30+ }
31+
32+ function validateDiagnostics ( diagnostics : ReadonlyArray < ts . Diagnostic > , strict ?: boolean ) : boolean {
33+ // Print error diagnostics.
34+ const checkDiagnostics = ( diagnostics : ReadonlyArray < ts . Diagnostic > ) => {
35+ if ( diagnostics && diagnostics . length > 0 ) {
36+ let errors = '' ;
37+ errors = errors + '\n' + ts . formatDiagnostics ( diagnostics , {
38+ getCurrentDirectory : ( ) => ts . sys . getCurrentDirectory ( ) ,
39+ getNewLine : ( ) => ts . sys . newLine ,
40+ getCanonicalFileName : ( f : string ) => f ,
41+ } ) ;
42+
43+ return errors ;
44+ }
45+ } ;
46+
47+ const hasError = diagnostics . some ( diag => diag . category === ts . DiagnosticCategory . Error ) ;
48+ if ( hasError ) {
49+ // Throw only if we're in strict mode, otherwise return original content.
50+ if ( strict ) {
51+ throw new Error ( `
52+ TS failed with the following error messages:
53+
54+ ${ checkDiagnostics ( diagnostics ) }
55+ ` ) ;
56+ } else {
57+ return false ;
58+ }
59+ }
60+
61+ return true ;
62+ }
63+
2764export function transformJavascript (
2865 options : TransformJavascriptOptions ,
2966) : TransformJavascriptOutput {
@@ -46,23 +83,68 @@ export function transformJavascript(
4683 } ;
4784 }
4885
49- // Print error diagnostics.
50- const checkDiagnostics = ( diagnostics : ReadonlyArray < ts . Diagnostic > ) => {
51- if ( diagnostics && diagnostics . length > 0 ) {
52- let errors = '' ;
53- errors = errors + '\n' + ts . formatDiagnostics ( diagnostics , {
54- getCurrentDirectory : ( ) => ts . sys . getCurrentDirectory ( ) ,
55- getNewLine : ( ) => ts . sys . newLine ,
56- getCanonicalFileName : ( f : string ) => f ,
57- } ) ;
86+ const allowFastPath = options . typeCheck === false && ! emitSourceMap ;
87+ const outputs = new Map < string , string > ( ) ;
88+ const tempFilename = 'bo-default-file.js' ;
89+ const tempSourceFile = ts . createSourceFile (
90+ tempFilename ,
91+ content ,
92+ ts . ScriptTarget . Latest ,
93+ allowFastPath ,
94+ ) ;
95+ const parseDiagnostics = ( tempSourceFile as DiagnosticSourceFile ) . parseDiagnostics ;
5896
59- return errors ;
60- }
97+ const tsOptions : ts . CompilerOptions = {
98+ // We target latest so that there is no downleveling.
99+ target : ts . ScriptTarget . Latest ,
100+ isolatedModules : true ,
101+ suppressOutputPathCheck : true ,
102+ allowNonTsExtensions : true ,
103+ noLib : true ,
104+ noResolve : true ,
105+ sourceMap : emitSourceMap ,
106+ inlineSources : emitSourceMap ,
107+ inlineSourceMap : false ,
61108 } ;
62109
63- const outputs = new Map < string , string > ( ) ;
64- const tempFilename = 'bo-default-file.js' ;
65- const tempSourceFile = ts . createSourceFile ( tempFilename , content , ts . ScriptTarget . Latest ) ;
110+ if ( allowFastPath && parseDiagnostics ) {
111+ if ( ! validateDiagnostics ( parseDiagnostics , strict ) ) {
112+ return {
113+ content : null ,
114+ sourceMap : null ,
115+ emitSkipped : true ,
116+ } ;
117+ }
118+
119+ const transforms = getTransforms . map ( ( getTf ) => getTf ( undefined ) ) ;
120+
121+ const result = ts . transform ( tempSourceFile , transforms , tsOptions ) ;
122+ if ( result . transformed . length === 0 || result . transformed [ 0 ] === tempSourceFile ) {
123+ return {
124+ content : null ,
125+ sourceMap : null ,
126+ emitSkipped : true ,
127+ } ;
128+ }
129+
130+ const printer = ts . createPrinter (
131+ undefined ,
132+ {
133+ onEmitNode : result . emitNodeWithNotification ,
134+ substituteNode : result . substituteNode ,
135+ } ,
136+ ) ;
137+
138+ const output = printer . printFile ( result . transformed [ 0 ] ) ;
139+
140+ result . dispose ( ) ;
141+
142+ return {
143+ content : output ,
144+ sourceMap : null ,
145+ emitSkipped : false ,
146+ } ;
147+ }
66148
67149 const host : ts . CompilerHost = {
68150 getSourceFile : ( fileName ) => {
@@ -83,39 +165,15 @@ export function transformJavascript(
83165 writeFile : ( fileName , text ) => outputs . set ( fileName , text ) ,
84166 } ;
85167
86- const tsOptions : ts . CompilerOptions = {
87- // We target latest so that there is no downleveling.
88- target : ts . ScriptTarget . Latest ,
89- isolatedModules : true ,
90- suppressOutputPathCheck : true ,
91- allowNonTsExtensions : true ,
92- noLib : true ,
93- noResolve : true ,
94- sourceMap : emitSourceMap ,
95- inlineSources : emitSourceMap ,
96- inlineSourceMap : false ,
97- } ;
98-
99168 const program = ts . createProgram ( [ tempFilename ] , tsOptions , host ) ;
100169
101170 const diagnostics = program . getSyntacticDiagnostics ( tempSourceFile ) ;
102- const hasError = diagnostics . some ( diag => diag . category === ts . DiagnosticCategory . Error ) ;
103-
104- if ( hasError ) {
105- // Throw only if we're in strict mode, otherwise return original content.
106- if ( strict ) {
107- throw new Error ( `
108- TS failed with the following error messages:
109-
110- ${ checkDiagnostics ( diagnostics ) }
111- ` ) ;
112- } else {
113- return {
114- content : null ,
115- sourceMap : null ,
116- emitSkipped : true ,
117- } ;
118- }
171+ if ( ! validateDiagnostics ( diagnostics , strict ) ) {
172+ return {
173+ content : null ,
174+ sourceMap : null ,
175+ emitSkipped : true ,
176+ } ;
119177 }
120178
121179 // We need the checker inside transforms.
0 commit comments