@@ -29,57 +29,170 @@ const path = require('path')
2929
3030const opts = { stdio : 'inherit' }
3131
32- function runInParallel ( commands ) {
33- return Promise . all (
34- commands . map ( ( { name, command } ) => {
35- return new Promise ( ( resolve , reject ) => {
36- console . info ( `${ name } ...` )
37- const child = spawn ( 'pnpm' , [ 'run' , command ] , {
38- stdio : 'inherit' ,
39- shell : true
40- } )
41- child . on ( 'close' , ( code ) => {
42- if ( code !== 0 ) {
43- reject ( new Error ( `'${ command } ' failed with exit code ${ code } ` ) )
44- } else {
45- resolve ( )
46- }
47- } )
48- child . on ( 'error' , reject )
49- } )
50- } )
51- )
52- }
32+ // Track phases for summary
33+ const phases = [ ]
5334
54- function buildProject ( ) {
55- execSync ( 'pnpm --filter @instructure/ui-icons prepare-build' , opts )
35+ function trackPhase ( name , fn ) {
36+ const start = Date . now ( )
37+ const phase = { name, start, end : null , duration : null , status : 'running' , error : null }
38+ phases . push ( phase )
39+
40+ const finish = ( error = null ) => {
41+ phase . end = Date . now ( )
42+ phase . duration = ( ( phase . end - phase . start ) / 1000 ) . toFixed ( 1 )
43+ phase . status = error ? 'failed' : 'success'
44+ phase . error = error ? error . message : null
45+ }
5646
57- console . info ( 'Building packages with SWC...' )
5847 try {
59- execSync ( 'pnpm run build' , opts )
48+ const result = fn ( )
49+ if ( result && typeof result . then === 'function' ) {
50+ return result . then (
51+ ( ) => {
52+ finish ( )
53+ return Promise . resolve ( )
54+ } ,
55+ ( error ) => {
56+ finish ( error )
57+ return Promise . reject ( error )
58+ }
59+ )
60+ }
61+ finish ( )
62+ return result
6063 } catch ( error ) {
61- console . error ( "'pnpm run build' failed" , error )
62- process . exit ( 1 )
64+ finish ( error )
65+ throw error
6366 }
67+ }
6468
65- console . info ( 'Running token generation and TypeScript compilation in parallel...' )
66- return runInParallel ( [
67- { name : 'Generating tokens' , command : 'build:tokens' } ,
68- { name : 'Building TypeScript declarations' , command : 'build:types' }
69- ] ) . catch ( ( error ) => {
70- console . error ( 'Parallel build failed:' , error )
71- process . exit ( 1 )
69+ function printSummary ( actualTotalDuration ) {
70+ const summedDuration = phases . reduce ( ( sum , p ) => sum + parseFloat ( p . duration ) , 0 ) . toFixed ( 1 )
71+ const totalDuration = actualTotalDuration || summedDuration
72+ const hasErrors = phases . some ( p => p . status === 'failed' )
73+
74+ console . log ( '\n' + '═' . repeat ( 80 ) )
75+ console . log ( 'Bootstrap Summary' )
76+ console . log ( '═' . repeat ( 80 ) )
77+ console . log (
78+ '│ ' +
79+ 'Phase' . padEnd ( 30 ) +
80+ '│ ' +
81+ 'Duration' . padEnd ( 10 ) +
82+ '│ ' +
83+ 'Status' . padEnd ( 10 ) +
84+ '│'
85+ )
86+ console . log ( '├' + '─' . repeat ( 31 ) + '┼' + '─' . repeat ( 11 ) + '┼' + '─' . repeat ( 11 ) + '┤' )
87+
88+ phases . forEach ( phase => {
89+ const status = phase . status === 'success' ? '✓ Success' : '✗ Failed'
90+ console . log (
91+ '│ ' +
92+ phase . name . padEnd ( 30 ) +
93+ '│ ' +
94+ ( phase . duration + 's' ) . padEnd ( 10 ) +
95+ '│ ' +
96+ status . padEnd ( 10 ) +
97+ '│'
98+ )
99+ if ( phase . error ) {
100+ console . log ( '│ ' + 'Error: ' . padEnd ( 30 ) + '│ ' + phase . error . padEnd ( 21 ) + '│' )
101+ }
72102 } )
103+
104+ console . log ( '├' + '─' . repeat ( 31 ) + '┼' + '─' . repeat ( 11 ) + '┼' + '─' . repeat ( 11 ) + '┤' )
105+ console . log (
106+ '│ ' +
107+ 'TOTAL' . padEnd ( 30 ) +
108+ '│ ' +
109+ ( totalDuration + 's' ) . padEnd ( 10 ) +
110+ '│ ' +
111+ ( hasErrors ? '✗ Failed' : '✓ Success' ) . padEnd ( 10 ) +
112+ '│'
113+ )
114+ console . log ( '└' + '─' . repeat ( 31 ) + '┴' + '─' . repeat ( 11 ) + '┴' + '─' . repeat ( 11 ) + '┘' )
115+
116+ if ( hasErrors ) {
117+ console . log ( '\n⚠️ Bootstrap completed with errors. See details above.' )
118+ } else {
119+ console . log ( '\n✓ Bootstrap completed successfully!' )
120+ }
121+ }
122+
123+ async function buildProject ( ) {
124+ await trackPhase ( 'Icon build' , ( ) => {
125+ execSync ( 'pnpm --filter @instructure/ui-icons prepare-build' , opts )
126+ } )
127+
128+ await trackPhase ( 'SWC compilation' , ( ) => {
129+ console . info ( 'Building packages with SWC...' )
130+ execSync ( 'pnpm run build' , opts )
131+ } )
132+
133+ console . info ( 'Running token generation and TypeScript compilation in parallel...' )
134+
135+ const parallelPhases = [
136+ { trackName : 'Token generation' , name : 'Generating tokens' , command : 'build:tokens' } ,
137+ { trackName : 'TypeScript declarations' , name : 'Building TypeScript declarations' , command : 'build:types' }
138+ ]
139+
140+ await Promise . all (
141+ parallelPhases . map ( ( { trackName, name, command } ) => {
142+ return trackPhase ( trackName , ( ) => {
143+ return new Promise ( ( resolve , reject ) => {
144+ console . info ( `${ name } ...` )
145+ const child = spawn ( 'pnpm' , [ 'run' , command ] , {
146+ stdio : 'inherit' ,
147+ shell : true
148+ } )
149+ child . on ( 'close' , ( code ) => {
150+ if ( code !== 0 ) {
151+ reject ( new Error ( `'${ command } ' failed with exit code ${ code } ` ) )
152+ } else {
153+ resolve ( )
154+ }
155+ } )
156+ child . on ( 'error' , reject )
157+ } )
158+ } )
159+ } )
160+ )
73161}
74162async function bootstrap ( ) {
163+ const bootstrapStart = Date . now ( )
164+
165+ await trackPhase ( 'Clean' , ( ) => {
166+ return new Promise ( ( resolve , reject ) => {
167+ const child = fork ( path . resolve ( 'scripts/clean.js' ) , opts )
168+ child . on ( 'exit' , ( code ) => {
169+ if ( code !== 0 ) {
170+ reject ( new Error ( `clean script failed with exit code ${ code } ` ) )
171+ } else {
172+ resolve ( )
173+ }
174+ } )
175+ child . on ( 'error' , reject )
176+ } )
177+ } )
178+
75179 try {
76- fork ( path . resolve ( 'scripts/clean.js' ) , opts )
180+ await buildProject ( )
77181 } catch ( error ) {
78- console . error ( 'clean failed with error:' , error )
79- process . exit ( 1 )
182+ // Error already tracked, just print summary
80183 }
81184
82- await buildProject ( )
185+ const actualTotalDuration = ( ( Date . now ( ) - bootstrapStart ) / 1000 ) . toFixed ( 1 )
186+ printSummary ( actualTotalDuration )
187+
188+ const hasErrors = phases . some ( p => p . status === 'failed' )
189+ if ( hasErrors ) {
190+ process . exit ( 1 )
191+ }
83192}
84193
85- bootstrap ( )
194+ bootstrap ( ) . catch ( ( error ) => {
195+ console . error ( 'Bootstrap failed:' , error )
196+ printSummary ( ) // No actual time available in error case
197+ process . exit ( 1 )
198+ } )
0 commit comments