@@ -19,17 +19,43 @@ const isWatchOnce = !!process.env.BUILD_WATCH_ONCE
19
19
// Cache compression control: default none; allow override via env
20
20
const cacheCompressionEnv = process . env . BUILD_CACHE_COMPRESSION
21
21
let cacheCompressionOption
22
- if ( cacheCompressionEnv ) {
23
- const v = String ( cacheCompressionEnv ) . toLowerCase ( )
24
- if ( v === '0' || v === 'false' || v === 'none' ) cacheCompressionOption = false
25
- else if ( v === 'brotli' ) cacheCompressionOption = 'brotli'
26
- else cacheCompressionOption = 'gzip' // treat any truthy/unknown as gzip
22
+ if ( cacheCompressionEnv != null ) {
23
+ const v = String ( cacheCompressionEnv ) . trim ( ) . toLowerCase ( )
24
+ if ( v === '' || v === '0' || v === 'false' || v === 'none' ) {
25
+ cacheCompressionOption = false
26
+ } else if ( v === 'gzip' || v === 'brotli' ) {
27
+ cacheCompressionOption = v
28
+ } else {
29
+ console . warn (
30
+ `[build] Unknown BUILD_CACHE_COMPRESSION="${ cacheCompressionEnv } ", defaulting to no compression` ,
31
+ )
32
+ cacheCompressionOption = false
33
+ }
27
34
}
28
- const cpuCount = os . cpus ( ) ?. length || 1
29
- const envWorkers = process . env . BUILD_THREAD_WORKERS
35
+ const cpuCountRaw = os . cpus ( ) ?. length
36
+ const cpuCount = Number . isInteger ( cpuCountRaw ) && cpuCountRaw > 0 ? cpuCountRaw : 1
37
+ const rawWorkers = process . env . BUILD_THREAD_WORKERS
30
38
? parseInt ( process . env . BUILD_THREAD_WORKERS , 10 )
31
39
: undefined
32
- const threadWorkers = Number . isInteger ( envWorkers ) && envWorkers > 0 ? envWorkers : cpuCount
40
+ let threadWorkers
41
+ if ( Number . isInteger ( rawWorkers ) && rawWorkers > 0 ) {
42
+ const maxWorkers = Math . max ( 1 , cpuCount )
43
+ if ( rawWorkers > maxWorkers ) {
44
+ console . warn (
45
+ `[build] BUILD_THREAD_WORKERS=${ rawWorkers } exceeds CPU count (${ cpuCount } ); capping to ${ maxWorkers } ` ,
46
+ )
47
+ }
48
+ threadWorkers = Math . min ( rawWorkers , maxWorkers )
49
+ } else {
50
+ if ( process . env . BUILD_THREAD_WORKERS ) {
51
+ console . warn (
52
+ `[build] Invalid BUILD_THREAD_WORKERS="${ process . env . BUILD_THREAD_WORKERS } ", defaulting to CPU count (${ cpuCount } )` ,
53
+ )
54
+ }
55
+ threadWorkers = cpuCount
56
+ }
57
+ // Thread-loader pool timeout constants
58
+ const PRODUCTION_POOL_TIMEOUT_MS = 2000
33
59
// Enable threads by default; allow disabling via BUILD_THREAD=0
34
60
const enableThread = process . env . BUILD_THREAD === '0' ? false : true
35
61
@@ -52,7 +78,16 @@ async function runWebpack(isWithoutKatex, isWithoutTiktoken, minimal, sourceBuil
52
78
]
53
79
if ( isWithoutKatex ) shared . push ( './src/components' )
54
80
55
- const sassImpl = await import ( 'sass-embedded' )
81
+ // Use the default export from sass-embedded; sass-loader expects the implementation object
82
+ const { default : sassImpl } = await import ( 'sass-embedded' )
83
+
84
+ const variantId = [
85
+ isWithoutKatex ? 'no-katex' : 'with-katex' ,
86
+ isWithoutTiktoken ? 'no-tiktoken' : 'with-tiktoken' ,
87
+ minimal ? 'minimal' : 'full' ,
88
+ sourceBuildDir || outdir ,
89
+ isProduction ? 'prod' : 'dev' ,
90
+ ] . join ( '|' )
56
91
57
92
const compiler = webpack ( {
58
93
entry : {
@@ -81,6 +116,7 @@ async function runWebpack(isWithoutKatex, isWithoutTiktoken, minimal, sourceBuil
81
116
devtool : isProduction ? false : 'cheap-module-source-map' ,
82
117
cache : {
83
118
type : 'filesystem' ,
119
+ name : `webpack-${ variantId } ` ,
84
120
// default none; override via BUILD_CACHE_COMPRESSION=gzip|brotli
85
121
compression : cacheCompressionOption ?? false ,
86
122
buildDependencies : {
@@ -164,7 +200,11 @@ async function runWebpack(isWithoutKatex, isWithoutTiktoken, minimal, sourceBuil
164
200
options : {
165
201
workers : threadWorkers ,
166
202
// Ensure one-off dev build exits quickly
167
- poolTimeout : isProduction ? 2000 : isWatchOnce ? 0 : Infinity ,
203
+ poolTimeout : isProduction
204
+ ? PRODUCTION_POOL_TIMEOUT_MS
205
+ : isWatchOnce
206
+ ? 0
207
+ : Infinity ,
168
208
} ,
169
209
} ,
170
210
]
@@ -174,13 +214,9 @@ async function runWebpack(isWithoutKatex, isWithoutTiktoken, minimal, sourceBuil
174
214
options : {
175
215
cacheDirectory : true ,
176
216
cacheCompression : false ,
177
- presets : [
178
- '@babel/preset-env' ,
179
- {
180
- plugins : [ '@babel/plugin-transform-runtime' ] ,
181
- } ,
182
- ] ,
217
+ presets : [ '@babel/preset-env' ] ,
183
218
plugins : [
219
+ [ '@babel/plugin-transform-runtime' ] ,
184
220
[
185
221
'@babel/plugin-transform-react-jsx' ,
186
222
{
@@ -309,7 +345,11 @@ async function runWebpack(isWithoutKatex, isWithoutTiktoken, minimal, sourceBuil
309
345
else {
310
346
const watching = compiler . watch ( { } , ( err , stats ) => {
311
347
callback ( err , stats )
312
- if ( process . env . BUILD_WATCH_ONCE ) watching . close ( ( ) => { } )
348
+ if ( process . env . BUILD_WATCH_ONCE ) {
349
+ watching . close ( ( closeErr ) => {
350
+ if ( closeErr ) console . error ( 'Error closing watcher:' , closeErr )
351
+ } )
352
+ }
313
353
} )
314
354
}
315
355
}
@@ -325,11 +365,23 @@ async function zipFolder(dir) {
325
365
}
326
366
327
367
async function copyFiles ( entryPoints , targetDir ) {
328
- if ( ! fs . existsSync ( targetDir ) ) await fs . mkdir ( targetDir )
368
+ if ( ! fs . existsSync ( targetDir ) ) await fs . mkdir ( targetDir , { recursive : true } )
329
369
await Promise . all (
330
370
entryPoints . map ( async ( entryPoint ) => {
331
- if ( await fs . pathExists ( entryPoint . src ) ) {
332
- await fs . copy ( entryPoint . src , `${ targetDir } /${ entryPoint . dst } ` )
371
+ try {
372
+ if ( await fs . pathExists ( entryPoint . src ) ) {
373
+ await fs . copy ( entryPoint . src , `${ targetDir } /${ entryPoint . dst } ` )
374
+ } else {
375
+ // Skip missing CSS in development (placeholders will be created later)
376
+ const isCss = String ( entryPoint . dst ) . endsWith ( '.css' )
377
+ if ( ! isProduction || isCss ) {
378
+ if ( ! isProduction && isCss ) return
379
+ }
380
+ throw new Error ( `Missing build artifact: ${ entryPoint . src } ` )
381
+ }
382
+ } catch ( e ) {
383
+ console . error ( 'Copy failed:' , entryPoint , e )
384
+ throw e
333
385
}
334
386
} ) ,
335
387
)
0 commit comments