@@ -12,6 +12,7 @@ import {normalizePathToPosix} from './path-normalize.js';
1212import { readFileSync } from 'fs' ;
1313import { testApiGolden } from './test_api_report.js' ;
1414import * as fs from 'fs' ;
15+ import { Piscina } from 'piscina' ;
1516
1617/** Interface describing contents of a `package.json`. */
1718export interface PackageJson {
@@ -40,11 +41,11 @@ async function main(
4041 const packageJsonPath = path . join ( npmPackageDir , 'package.json' ) ;
4142 const packageJson = JSON . parse ( readFileSync ( packageJsonPath , 'utf8' ) ) as PackageJson ;
4243 const entryPoints = findEntryPointsWithinNpmPackage ( npmPackageDir , packageJson ) ;
43- const outdatedGoldens : string [ ] = [ ] ;
44-
45- let allTestsSucceeding = true ;
44+ const worker = new Piscina < Parameters < typeof testApiGolden > , string > ( {
45+ filename : path . resolve ( __dirname , './test_api_report.js' ) ,
46+ } ) ;
4647
47- for ( const { subpath, typesEntryPointPath} of entryPoints ) {
48+ const processEntryPoint = async ( subpath : string , typesEntryPointPath : string ) => {
4849 // API extractor generates API reports as markdown files. For each types
4950 // entry-point we maintain a separate golden file. These golden files are
5051 // based on the name of the defining NodeJS exports subpath in the NPM package,
@@ -53,32 +54,56 @@ async function main(
5354 const goldenFilePath = path . join ( goldenDir , goldenName ) ;
5455 const moduleName = normalizePathToPosix ( path . join ( packageJson . name , subpath ) ) ;
5556
56- const actual = await testApiGolden (
57+ // Run API extractor in child processes. This is because API extractor is very
58+ // synchronous. This allows us to significantly speed up golden testing.
59+ const actual = await worker . run ( [
5760 typesEntryPointPath ,
5861 stripExportPattern ,
5962 typeNames ,
6063 packageJsonPath ,
6164 moduleName ,
62- ) ;
65+ ] ) ;
6366
6467 if ( actual === null ) {
6568 console . error ( `Could not generate API golden for subpath: "${ subpath } ". See errors above.` ) ;
6669 process . exit ( 1 ) ;
6770 }
6871
6972 if ( approveGolden ) {
70- fs . mkdirSync ( path . dirname ( goldenFilePath ) , { recursive : true } ) ;
71- fs . writeFileSync ( goldenFilePath , actual , 'utf8' ) ;
73+ await fs . promises . mkdir ( path . dirname ( goldenFilePath ) , { recursive : true } ) ;
74+ await fs . promises . writeFile ( goldenFilePath , actual , 'utf8' ) ;
7275 } else {
73- const expected = fs . readFileSync ( goldenFilePath , 'utf8' ) ;
76+ const expected = await fs . promises . readFile ( goldenFilePath , 'utf8' ) ;
7477 if ( actual !== expected ) {
7578 // Keep track of outdated goldens for error message.
7679 outdatedGoldens . push ( goldenName ) ;
77- allTestsSucceeding = false ;
80+ return false ;
7881 }
7982 }
83+
84+ return true ;
85+ } ;
86+
87+ const outdatedGoldens : string [ ] = [ ] ;
88+ const tasks : Promise < boolean > [ ] = [ ] ;
89+ // Process in batches. Otherwise we risk out of memory errors.
90+ const batchSize = 10 ;
91+
92+ for ( let i = 0 ; i < entryPoints . length ; i += batchSize ) {
93+ const batchEntryPoints = entryPoints . slice ( i , i + batchSize ) ;
94+
95+ for ( const { subpath, typesEntryPointPath} of batchEntryPoints ) {
96+ tasks . push ( processEntryPoint ( subpath , typesEntryPointPath ) ) ;
97+ }
98+
99+ // Wait for new batch.
100+ await Promise . all ( tasks ) ;
80101 }
81102
103+ // Wait for final batch/retrieve all results.
104+ const results = await Promise . all ( tasks ) ;
105+ const allTestsSucceeding = results . every ( ( r ) => r === true ) ;
106+
82107 if ( outdatedGoldens . length ) {
83108 console . error ( chalk . red ( `The following goldens are outdated:` ) ) ;
84109 outdatedGoldens . forEach ( ( name ) => console . info ( `- ${ name } ` ) ) ;
0 commit comments