@@ -6,8 +6,9 @@ import { parse } from '@babel/parser';
66import babelGenerator from '@babel/generator' ;
77import babelTraverse from '@babel/traverse' ;
88import R from 'ramda' ;
9+ import workerpool from 'workerpool' ;
910
10- import { isNativeSupported } from '@cubejs-backend/shared' ;
11+ import { getEnv , isNativeSupported } from '@cubejs-backend/shared' ;
1112import { UserError } from './UserError' ;
1213import { ErrorReporter } from './ErrorReporter' ;
1314
@@ -26,6 +27,8 @@ export class DataSchemaCompiler {
2627 this . preTranspileCubeCompilers = options . preTranspileCubeCompilers || [ ] ;
2728 this . cubeNameCompilers = options . cubeNameCompilers || [ ] ;
2829 this . extensions = options . extensions || { } ;
30+ this . cubeDictionary = options . cubeDictionary ;
31+ this . cubeSymbols = options . cubeSymbols ;
2932 this . cubeFactory = options . cubeFactory ;
3033 this . filesToCompile = options . filesToCompile ;
3134 this . omitErrors = options . omitErrors ;
@@ -38,6 +41,7 @@ export class DataSchemaCompiler {
3841 this . yamlCompiler = options . yamlCompiler ;
3942 this . yamlCompiler . dataSchemaCompiler = this ;
4043 this . pythonContext = null ;
44+ this . workerPool = null ;
4145 }
4246
4347 compileObjects ( compileServices , objects , errorsReport ) {
@@ -87,17 +91,52 @@ export class DataSchemaCompiler {
8791 const errorsReport = new ErrorReporter ( null , [ ] , this . errorReport ) ;
8892 this . errorsReport = errorsReport ;
8993
90- // TODO: required in order to get pre transpile compilation work
91- const transpile = ( ) => toCompile . map ( f => this . transpileFile ( f , errorsReport ) ) . filter ( f => ! ! f ) ;
94+ if ( getEnv ( 'transpilationWorkerThreads' ) ) {
95+ const wc = getEnv ( 'transpilationWorkerThreadsCount' ) ;
96+ this . workerPool = workerpool . pool (
97+ path . join ( __dirname , 'transpilers/transpiler_worker' ) ,
98+ wc > 0 ? { maxWorkers : wc } : undefined ,
99+ ) ;
100+ }
101+
102+ const transpile = async ( ) => {
103+ let cubeNames ;
104+ let cubeSymbolsNames ;
105+
106+ if ( getEnv ( 'transpilationWorkerThreads' ) ) {
107+ cubeNames = Object . keys ( this . cubeDictionary . byId ) ;
108+ // We need only cubes and all its member names for transpiling.
109+ // Cubes doesn't change during transpiling, but are changed during compilation phase,
110+ // so we can prepare them once for every phase.
111+ // Communication between main and worker threads uses
112+ // The structured clone algorithm (@see https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm)
113+ // which doesn't allow passing any function objects, so we need to sanitize the symbols.
114+ cubeSymbolsNames = Object . fromEntries (
115+ Object . entries ( this . cubeSymbols . symbols )
116+ . map (
117+ ( [ key , value ] ) => [ key , Object . fromEntries (
118+ Object . keys ( value ) . map ( ( k ) => [ k , true ] ) ,
119+ ) ] ,
120+ ) ,
121+ ) ;
122+ }
123+ const results = await Promise . all ( toCompile . map ( f => this . transpileFile ( f , errorsReport , { cubeNames, cubeSymbolsNames } ) ) ) ;
124+ return results . filter ( f => ! ! f ) ;
125+ } ;
92126
93- const compilePhase = ( compilers ) => this . compileCubeFiles ( compilers , transpile ( ) , errorsReport ) ;
127+ const compilePhase = async ( compilers ) => this . compileCubeFiles ( compilers , await transpile ( ) , errorsReport ) ;
94128
95129 return compilePhase ( { cubeCompilers : this . cubeNameCompilers } )
96130 . then ( ( ) => compilePhase ( { cubeCompilers : this . preTranspileCubeCompilers } ) )
97131 . then ( ( ) => compilePhase ( {
98132 cubeCompilers : this . cubeCompilers ,
99133 contextCompilers : this . contextCompilers ,
100- } ) ) ;
134+ } ) )
135+ . then ( ( ) => {
136+ if ( this . workerPool ) {
137+ this . workerPool . terminate ( ) ;
138+ }
139+ } ) ;
101140 }
102141
103142 compile ( ) {
@@ -113,7 +152,7 @@ export class DataSchemaCompiler {
113152 return this . compilePromise ;
114153 }
115154
116- transpileFile ( file , errorsReport ) {
155+ async transpileFile ( file , errorsReport , options ) {
117156 if ( R . endsWith ( '.jinja' , file . fileName ) ||
118157 ( R . endsWith ( '.yml' , file . fileName ) || R . endsWith ( '.yaml' , file . fileName ) )
119158 // TODO do Jinja syntax check with jinja compiler
@@ -132,31 +171,47 @@ export class DataSchemaCompiler {
132171 } else if ( R . endsWith ( '.yml' , file . fileName ) || R . endsWith ( '.yaml' , file . fileName ) ) {
133172 return file ;
134173 } else if ( R . endsWith ( '.js' , file . fileName ) ) {
135- return this . transpileJsFile ( file , errorsReport ) ;
174+ return this . transpileJsFile ( file , errorsReport , options ) ;
136175 } else {
137176 return file ;
138177 }
139178 }
140179
141- transpileJsFile ( file , errorsReport ) {
180+ async transpileJsFile ( file , errorsReport , { cubeNames , cubeSymbolsNames } ) {
142181 try {
143- const ast = parse (
144- file . content ,
145- {
146- sourceFilename : file . fileName ,
147- sourceType : 'module' ,
148- plugins : [ 'objectRestSpread' ]
149- } ,
150- ) ;
182+ if ( getEnv ( 'transpilationWorkerThreads' ) ) {
183+ const data = {
184+ fileName : file . fileName ,
185+ content : file . content ,
186+ transpilers : this . transpilers . map ( t => t . constructor . name ) ,
187+ cubeNames,
188+ cubeSymbolsNames,
189+ } ;
190+
191+ const res = await this . workerPool . exec ( 'transpile' , [ data ] ) ;
192+ errorsReport . addErrors ( res . errors ) ;
193+ errorsReport . addWarnings ( res . warnings ) ;
194+
195+ return Object . assign ( { } , file , { content : res . content } ) ;
196+ } else {
197+ const ast = parse (
198+ file . content ,
199+ {
200+ sourceFilename : file . fileName ,
201+ sourceType : 'module' ,
202+ plugins : [ 'objectRestSpread' ] ,
203+ } ,
204+ ) ;
151205
152- this . transpilers . forEach ( ( t ) => {
153- errorsReport . inFile ( file ) ;
154- babelTraverse ( ast , t . traverseObject ( errorsReport ) ) ;
155- errorsReport . exitFile ( ) ;
156- } ) ;
206+ this . transpilers . forEach ( ( t ) => {
207+ errorsReport . inFile ( file ) ;
208+ babelTraverse ( ast , t . traverseObject ( errorsReport ) ) ;
209+ errorsReport . exitFile ( ) ;
210+ } ) ;
157211
158- const content = babelGenerator ( ast , { } , file . content ) . code ;
159- return Object . assign ( { } , file , { content } ) ;
212+ const content = babelGenerator ( ast , { } , file . content ) . code ;
213+ return Object . assign ( { } , file , { content } ) ;
214+ }
160215 } catch ( e ) {
161216 if ( e . toString ( ) . indexOf ( 'SyntaxError' ) !== - 1 ) {
162217 const line = file . content . split ( '\n' ) [ e . loc . line - 1 ] ;
0 commit comments