11import crypto from 'crypto' ;
22import vm from 'vm' ;
33import fs from 'fs' ;
4+ import os from 'os' ;
45import path from 'path' ;
56import syntaxCheck from 'syntax-error' ;
67import { parse } from '@babel/parser' ;
@@ -21,6 +22,20 @@ const moduleFileCache = {};
2122
2223const JINJA_SYNTAX = / { % | % } | { { | } } / ig;
2324
25+ const getThreadsCount = ( ) => {
26+ const envThreads = getEnv ( 'transpilationWorkerThreadsCount' ) ;
27+ if ( envThreads > 0 ) {
28+ return envThreads ;
29+ }
30+
31+ const cpuCount = os . cpus ( ) ?. length ;
32+ if ( cpuCount ) {
33+ return Math . max ( 1 , cpuCount - 1 ) ;
34+ }
35+
36+ return 3 ; // Default (like the workerpool do)
37+ } ;
38+
2439export class DataSchemaCompiler {
2540 constructor ( repository , options = { } ) {
2641 this . repository = repository ;
@@ -100,6 +115,7 @@ export class DataSchemaCompiler {
100115
101116 const transpilationWorkerThreads = getEnv ( 'transpilationWorkerThreads' ) ;
102117 const transpilationNative = getEnv ( 'transpilationNative' ) ;
118+ const transpilationNativeThreadsCount = getThreadsCount ( ) ;
103119 const { compilerId } = this ;
104120
105121 if ( ! transpilationNative && transpilationWorkerThreads ) {
@@ -151,7 +167,26 @@ export class DataSchemaCompiler {
151167
152168 await this . transpileJsFile ( dummyFile , errorsReport , { cubeNames, cubeSymbols, transpilerNames, contextSymbols : CONTEXT_SYMBOLS , compilerId, stage } ) ;
153169
154- results = await Promise . all ( toCompile . map ( f => this . transpileFile ( f , errorsReport , { transpilerNames, compilerId } ) ) ) ;
170+ const nonJsFilesTasks = toCompile . filter ( file => ! file . fileName . endsWith ( '.js' ) )
171+ . map ( f => this . transpileFile ( f , errorsReport , { transpilerNames, compilerId } ) ) ;
172+
173+ const jsFiles = toCompile . filter ( file => file . fileName . endsWith ( '.js' ) ) ;
174+ let jsChunks ;
175+ if ( jsFiles . length < transpilationNativeThreadsCount * transpilationNativeThreadsCount ) {
176+ jsChunks = [ jsFiles ] ;
177+ } else {
178+ const baseSize = Math . floor ( jsFiles . length / transpilationNativeThreadsCount ) ;
179+ jsChunks = [ ] ;
180+ for ( let i = 0 ; i < transpilationNativeThreadsCount ; i ++ ) {
181+ // For the last part, we take the remaining files so we don't lose the extra ones.
182+ const start = i * baseSize ;
183+ const end = ( i === transpilationNativeThreadsCount - 1 ) ? jsFiles . length : start + baseSize ;
184+ jsChunks . push ( jsFiles . slice ( start , end ) ) ;
185+ }
186+ }
187+ const JsFilesTasks = jsChunks . map ( chunk => this . transpileJsFilesBulk ( chunk , errorsReport , { transpilerNames, compilerId } ) ) ;
188+
189+ results = ( await Promise . all ( [ ...nonJsFilesTasks , ...JsFilesTasks ] ) ) . flat ( ) ;
155190 } else if ( transpilationWorkerThreads ) {
156191 results = await Promise . all ( toCompile . map ( f => this . transpileFile ( f , errorsReport , { cubeNames, cubeSymbols, transpilerNames } ) ) ) ;
157192 } else {
@@ -231,6 +266,44 @@ export class DataSchemaCompiler {
231266 }
232267 }
233268
269+ /**
270+ * Right now it is used only for transpilation in native,
271+ * so no checks for transpilation type inside this method
272+ */
273+ async transpileJsFilesBulk ( files , errorsReport , { cubeNames, cubeSymbols, contextSymbols, transpilerNames, compilerId, stage } ) {
274+ // for bulk processing this data may be optimized even more by passing transpilerNames, compilerId only once for a bulk
275+ // but this requires more complex logic to be implemented in the native side.
276+ // And comparing to the file content sizes, a few bytes of JSON data is not a big deal here
277+ const reqDataArr = files . map ( file => ( {
278+ fileName : file . fileName ,
279+ fileContent : file . content ,
280+ transpilers : transpilerNames ,
281+ compilerId,
282+ ...( cubeNames && {
283+ metaData : {
284+ cubeNames,
285+ cubeSymbols,
286+ contextSymbols,
287+ stage
288+ } ,
289+ } ) ,
290+ } ) ) ;
291+ const res = await transpileJs ( reqDataArr ) ;
292+
293+ return files . map ( ( file , index ) => {
294+ errorsReport . inFile ( file ) ;
295+ if ( ! res [ index ] ) { // This should not happen in theory but just to be safe
296+ errorsReport . error ( `No transpilation result received for the file ${ file . fileName } .` ) ;
297+ return undefined ;
298+ }
299+ errorsReport . addErrors ( res [ index ] . errors ) ;
300+ errorsReport . addWarnings ( res [ index ] . warnings ) ;
301+ errorsReport . exitFile ( ) ;
302+
303+ return { ...file , content : res [ index ] . code } ;
304+ } ) ;
305+ }
306+
234307 async transpileJsFile ( file , errorsReport , { cubeNames, cubeSymbols, contextSymbols, transpilerNames, compilerId, stage } ) {
235308 try {
236309 if ( getEnv ( 'transpilationNative' ) ) {
0 commit comments