jiek 2.1.11 → 2.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,23 +1,27 @@
1
- import fs from 'node:fs'
1
+ import { existsSync, mkdirSync, statSync, writeFileSync } from 'node:fs'
2
2
  import { createRequire } from 'node:module'
3
3
  import path from 'node:path'
4
+ import process from 'node:process'
4
5
 
5
6
  import { confirm } from '@inquirer/prompts'
6
7
  import { MultiBar, Presets } from 'cli-progress'
7
8
  import { program } from 'commander'
8
9
  import { execaCommand } from 'execa'
10
+ import type { renderView } from 'vite-bundle-analyzer'
9
11
 
12
+ import type { RollupBuildEvent } from '#~/bridge.ts'
10
13
  import { entriesDescription, filterDescription, outdirDescription } from '#~/commands/descriptions.ts'
11
14
  import { IS_WORKSPACE } from '#~/commands/meta.ts'
15
+ import type { TemplateOptions } from '#~/rollup/base.ts'
16
+ import { BUILDER_TYPES, BUILDER_TYPE_PACKAGE_NAME_MAP } from '#~/rollup/base.ts'
17
+ import type { Module } from '#~/rollup/bundle-analyzer.ts'
18
+ import { createServer } from '#~/server.ts'
12
19
  import type { ProjectsGraph } from '#~/utils/filterSupport.ts'
13
20
  import { filterPackagesGraph, getSelectedProjectsGraph } from '#~/utils/filterSupport.ts'
14
21
  import { getWD } from '#~/utils/getWD.ts'
15
22
  import { loadConfig } from '#~/utils/loadConfig.ts'
16
23
  import { tsRegisterName } from '#~/utils/tsRegister.ts'
17
24
 
18
- import type { RollupProgressEvent, TemplateOptions } from '../rollup/base'
19
- import { BUILDER_TYPE_PACKAGE_NAME_MAP, BUILDER_TYPES } from '../rollup/base'
20
-
21
25
  declare module 'jiek' {
22
26
  export interface Config {
23
27
  build?: TemplateOptions & {
@@ -46,6 +50,20 @@ ${isDefault ? 'This command is the default command.' : ''}
46
50
  `.trim()
47
51
 
48
52
  interface BuildOptions {
53
+ ana?: boolean
54
+ /**
55
+ * @default '.jk-analyses'
56
+ */
57
+ 'ana.dir': string
58
+ /**
59
+ * @default 'server'
60
+ */
61
+ 'ana.mode': string
62
+ 'ana.open'?: boolean
63
+ /**
64
+ * @default 'parsed'
65
+ */
66
+ 'ana.size': string
49
67
  /**
50
68
  * Auto-detect the builder from the installed dependencies.
51
69
  * If the builder is not installed, it will prompt the user to install it.
@@ -61,6 +79,12 @@ interface BuildOptions {
61
79
  */
62
80
  outdir: string
63
81
  watch: boolean
82
+ /**
83
+ * The port of the server.
84
+ *
85
+ * @default 8888
86
+ */
87
+ port: number
64
88
  silent: boolean
65
89
  verbose: boolean
66
90
  entries?: string
@@ -99,7 +123,7 @@ interface BuildOptions {
99
123
  async function checkDependency(dependency: string) {
100
124
  try {
101
125
  require.resolve(dependency)
102
- } catch (e) {
126
+ } catch {
103
127
  console.error(`The package '${dependency}' is not installed, please install it first.`)
104
128
  const { notWorkspace } = getWD()
105
129
  const command = `pnpm install -${notWorkspace ? '' : 'w'}D ${dependency}`
@@ -138,7 +162,7 @@ ${entriesDescription}
138
162
  If you pass the --entries option, it will merge into the entries of the command.
139
163
  `.trim()
140
164
 
141
- const command = isDefault
165
+ let command = isDefault
142
166
  ? (() => {
143
167
  const c = program
144
168
  .name('jb/jiek-build')
@@ -153,9 +177,10 @@ const command = isDefault
153
177
  : program
154
178
  .command(`build [${IS_WORKSPACE ? 'filters' : 'entries'}]`)
155
179
 
156
- command
180
+ command = command
157
181
  .description(description)
158
182
  .option('-t, --type <TYPE>', `The type of build, support ${BUILDER_TYPES.map(s => `"${s}"`).join(', ')}.`, v => {
183
+ // eslint-disable-next-line ts/no-unsafe-argument
159
184
  if (!BUILDER_TYPES.includes(v as any)) {
160
185
  throw new Error(`The value of 'type' must be ${BUILDER_TYPES.map(s => `"${s}"`).join(', ')}`)
161
186
  }
@@ -183,13 +208,32 @@ command
183
208
  'Only output minify files, but dts files will still be output, it only replaces the js files.',
184
209
  parseBoolean
185
210
  )
211
+
212
+ command = command
186
213
  .option('--tsconfig <TSCONFIG>', 'The path of the tsconfig file which is used to generate js and dts files.', String)
187
214
  .option('--dtsconfig <DTSCONFIG>', 'The path of the tsconfig file which is used to generate dts files.', String)
215
+
216
+ command = command
188
217
  .option('-w, --watch', 'Watch the file changes.', parseBoolean)
218
+ .option('-p, --port <PORT>', 'The port of the server.', Number.parseInt, 8888)
219
+
220
+ command = command
221
+ .option('--ana', 'Enable the bundle analyzer.', parseBoolean)
222
+ .option('--ana.dir <DIR>', 'The directory of the bundle analyzer.', '.jk-analyses')
223
+ .option('--ana.mode <MODE>', 'The mode of the bundle analyzer, support "static", "json" and "server".', 'server')
224
+ .option('--ana.open', 'Open the bundle analyzer in the browser.', parseBoolean)
225
+ .option(
226
+ '--ana.size <SIZE>',
227
+ 'The default size of the bundle analyzer, support "stat", "parsed" and "gzip".',
228
+ 'parsed'
229
+ )
230
+
231
+ command = command
189
232
  .option('-s, --silent', "Don't display logs.", parseBoolean)
190
233
  .option('-v, --verbose', 'Display debug logs.', parseBoolean)
234
+
235
+ command
191
236
  .action(async (commandFiltersOrEntries: string | undefined, options: BuildOptions) => {
192
- /* eslint-disable prefer-const */
193
237
  let {
194
238
  type,
195
239
  outdir,
@@ -207,10 +251,9 @@ command
207
251
  tsconfig,
208
252
  dtsconfig
209
253
  } = options
210
- /* eslint-enable prefer-const */
211
254
  const resolvedType = type ?? DEFAULT_BUILDER_TYPE
212
255
  if (!withoutJs) {
213
- await checkDependency(BUILDER_TYPE_PACKAGE_NAME_MAP[resolvedType]!)
256
+ await checkDependency(BUILDER_TYPE_PACKAGE_NAME_MAP[resolvedType])
214
257
  if (minifyType === 'builder') {
215
258
  minifyType = resolvedType
216
259
  }
@@ -220,7 +263,7 @@ command
220
263
  {
221
264
  ...BUILDER_TYPE_PACKAGE_NAME_MAP,
222
265
  terser: '@rollup/plugin-terser'
223
- }[resolvedType]!
266
+ }[resolvedType]
224
267
  )
225
268
  }
226
269
  let shouldPassThrough = false
@@ -238,6 +281,50 @@ command
238
281
  },
239
282
  [] as string[]
240
283
  )
284
+
285
+ const modules: Module[] = []
286
+ const cjsModules: Module[] = []
287
+ const esmModules: Module[] = []
288
+ let render: typeof renderView | undefined
289
+ const analyzer = options.ana
290
+ ? {
291
+ dir: options['ana.dir'],
292
+ mode: options['ana.mode'],
293
+ open: options['ana.open'],
294
+ size: options['ana.size']
295
+ }
296
+ : undefined
297
+ if (
298
+ options.ana
299
+ && ![
300
+ 'stat',
301
+ 'parsed',
302
+ 'gzip'
303
+ ].includes(analyzer?.size ?? '')
304
+ ) {
305
+ throw new Error('The value of `ana.size` must be "stat", "parsed" or "gzip"')
306
+ }
307
+ const server = analyzer && createServer(options.port, 'localhost')
308
+
309
+ if (analyzer) {
310
+ await checkDependency('vite-bundle-analyzer')
311
+ const { renderView } = await import('vite-bundle-analyzer')
312
+ render = renderView
313
+ }
314
+ const anaPaths = new Set<string>()
315
+ const refreshAnalyzer = async (subPath = '', renderModules = modules) => {
316
+ if (!(analyzer && server && render)) return
317
+ const p = `/ana${subPath}`
318
+ anaPaths.add(p)
319
+ void server.renderTo(
320
+ p,
321
+ await render(renderModules, {
322
+ title: `Jiek Analyzer - ${subPath}`,
323
+ mode: analyzer.size as 'stat' | 'parsed' | 'gzip'
324
+ })
325
+ )
326
+ }
327
+
241
328
  const { build } = loadConfig()
242
329
  silent = silent ?? build?.silent ?? false
243
330
 
@@ -256,7 +343,7 @@ command
256
343
  entries = undefined
257
344
  }
258
345
  const env = {
259
- ...process.env,
346
+ JIEK_ANALYZER: analyzer && JSON.stringify(analyzer),
260
347
  JIEK_BUILDER: type,
261
348
  JIEK_OUT_DIR: outdir,
262
349
  JIEK_CLEAN: String(!noClean),
@@ -268,7 +355,8 @@ command
268
355
  JIEK_ONLY_MINIFY: String(onlyMin),
269
356
  JIEK_MINIFY_TYPE: minifyType,
270
357
  JIEK_TSCONFIG: tsconfig,
271
- JIEK_DTSCONFIG: dtsconfig
358
+ JIEK_DTSCONFIG: dtsconfig,
359
+ ...process.env
272
360
  }
273
361
 
274
362
  const multiBars = new MultiBar({
@@ -285,12 +373,12 @@ command
285
373
  throw new Error('no package found')
286
374
  }
287
375
  const wdNodeModules = path.resolve(wd, 'node_modules')
288
- if (!fs.existsSync(wdNodeModules)) {
289
- fs.mkdirSync(wdNodeModules)
376
+ if (!existsSync(wdNodeModules)) {
377
+ mkdirSync(wdNodeModules)
290
378
  }
291
379
  const jiekTempDir = (...paths: string[]) => path.resolve(wdNodeModules, '.jiek', ...paths)
292
- if (!fs.existsSync(jiekTempDir())) {
293
- fs.mkdirSync(jiekTempDir())
380
+ if (!existsSync(jiekTempDir())) {
381
+ mkdirSync(jiekTempDir())
294
382
  }
295
383
 
296
384
  const rollupBinaryPath = require.resolve('rollup')
@@ -298,18 +386,35 @@ command
298
386
  let i = 0
299
387
  await Promise.all(
300
388
  Object.entries(value).map(async ([dir, manifest]) => {
301
- if (!manifest.name) {
389
+ if (manifest.name == null) {
302
390
  throw new Error('package.json must have a name field')
303
391
  }
392
+ if (analyzer) {
393
+ const anaDir = path.resolve(dir, analyzer.dir)
394
+ if (!existsSync(anaDir)) {
395
+ mkdirSync(anaDir, { recursive: true })
396
+ }
397
+ const gitIgnorePath = path.resolve(anaDir, '.gitignore')
398
+ if (!existsSync(gitIgnorePath)) {
399
+ writeFileSync(gitIgnorePath, '*\n!.gitignore\n')
400
+ }
401
+ const npmIgnorePath = path.resolve(anaDir, '.npmignore')
402
+ if (!existsSync(npmIgnorePath)) {
403
+ writeFileSync(npmIgnorePath, '*\n')
404
+ }
405
+ if (!statSync(anaDir).isDirectory()) {
406
+ throw new Error(`The directory '${anaDir}' is not a directory.`)
407
+ }
408
+ }
304
409
 
305
410
  // TODO support auto build child packages in workspaces
306
411
  const escapeManifestName = manifest.name.replace(/^@/g, '').replace(/\//g, '+')
307
412
  const configFile = jiekTempDir(
308
413
  `${escapeManifestName ?? `anonymous-${i++}`}.rollup.config.js`
309
414
  )
310
- fs.writeFileSync(configFile, FILE_TEMPLATE(manifest))
415
+ writeFileSync(configFile, FILE_TEMPLATE(manifest))
311
416
  const command = [rollupBinaryPath, '--silent', '-c', configFile]
312
- if (tsRegisterName) {
417
+ if (tsRegisterName != null) {
313
418
  command.unshift(`node -r ${tsRegisterName}`)
314
419
  }
315
420
  if (watch) {
@@ -329,92 +434,152 @@ command
329
434
  const times: Record<string, number> = {}
330
435
  const locks: Record<string, boolean> = {}
331
436
  let inputMaxLen = 10
332
- child.on('message', (e: RollupProgressEvent) => {
333
- if (e.type === 'debug') console.log(...(Array.isArray(e.data) ? e.data : [e.data]))
334
- })
335
- !silent && child.on('message', (e: RollupProgressEvent) => {
336
- if (e.type === 'init') {
337
- const { leafMap, targetsLength } = e.data
338
- const leafs = Array
339
- .from(leafMap.entries())
340
- .flatMap(([input, pathAndCondiions]) =>
341
- pathAndCondiions.map(([path, ...conditions]) => ({
342
- input,
343
- path,
344
- conditions
345
- }))
437
+ child.on('message', (e: RollupBuildEvent) => {
438
+ if (
439
+ silent && [
440
+ 'init',
441
+ 'progress',
442
+ 'watchChange'
443
+ ].includes(e.type)
444
+ ) return
445
+ switch (e.type) {
446
+ case 'init': {
447
+ const { leafMap, targetsLength } = e.data
448
+ const leafs = Array
449
+ .from(leafMap.entries())
450
+ .flatMap(([input, pathAndCondiions]) =>
451
+ pathAndCondiions.map(([path, ...conditions]) => ({
452
+ input,
453
+ path,
454
+ conditions
455
+ }))
456
+ )
457
+ let initMessage = `Package '${manifest.name}' has ${targetsLength} targets to build`
458
+ if (watch) {
459
+ initMessage += ' and watching...'
460
+ }
461
+ // eslint-disable-next-line no-console
462
+ console.log(initMessage)
463
+ leafs.forEach(({ input }) => {
464
+ inputMaxLen = Math.max(inputMaxLen, input.length)
465
+ })
466
+ leafs.forEach(({ input, path }) => {
467
+ const key = `${input}:${path}`
468
+ // eslint-disable-next-line ts/strict-boolean-expressions
469
+ if (bars[key]) return
470
+ bars[key] = multiBars.create(50, 0, {
471
+ pkgName: manifest.name,
472
+ input: input.padEnd(inputMaxLen + 5),
473
+ status: 'waiting'.padEnd(10)
474
+ }, {
475
+ barsize: 20,
476
+ linewrap: true
477
+ })
478
+ })
479
+ break
480
+ }
481
+ case 'progress': {
482
+ const {
483
+ path,
484
+ tags,
485
+ input,
486
+ event,
487
+ message
488
+ } = e.data
489
+ const bar = bars[`${input}:${path}`]
490
+ // eslint-disable-next-line ts/strict-boolean-expressions
491
+ if (!bar) return
492
+ const time = times[`${input}:${path}`]
493
+ bar.update(
494
+ {
495
+ start: 0,
496
+ resolve: 20,
497
+ end: 50
498
+ }[event ?? 'start'] ?? 0,
499
+ {
500
+ input: (
501
+ time
502
+ ? `${input}(x${time.toString().padStart(2, '0')})`
503
+ : input
504
+ ).padEnd(inputMaxLen + 5),
505
+ status: event?.padEnd(10),
506
+ message: `${tags?.join(', ')}: ${message}`
507
+ }
346
508
  )
347
- let initMessage = `Package '${manifest.name}' has ${targetsLength} targets to build`
348
- if (watch) {
349
- initMessage += ' and watching...'
509
+ break
350
510
  }
351
- console.log(initMessage)
352
- leafs.forEach(({ input }) => {
353
- inputMaxLen = Math.max(inputMaxLen, input.length)
354
- })
355
- leafs.forEach(({ input, path }) => {
511
+ case 'watchChange': {
512
+ const {
513
+ path,
514
+ input
515
+ } = e.data
356
516
  const key = `${input}:${path}`
357
- if (bars[key]) return
358
- bars[key] = multiBars.create(50, 0, {
359
- pkgName: manifest.name,
360
- input: input.padEnd(inputMaxLen + 5),
361
- status: 'waiting'.padEnd(10)
362
- }, {
363
- barsize: 20,
364
- linewrap: true
365
- })
366
- })
367
- }
368
- if (e.type === 'progress') {
369
- const {
370
- path,
371
- tags,
372
- input,
373
- event,
374
- message
375
- } = e.data
376
- const bar = bars[`${input}:${path}`]
377
- if (!bar) return
378
- const time = times[`${input}:${path}`]
379
- bar.update(
380
- {
381
- start: 0,
382
- resolve: 20,
383
- end: 50
384
- }[event ?? 'start'] ?? 0,
385
- {
386
- input: (
387
- time
388
- ? `${input}(x${time.toString().padStart(2, '0')})`
389
- : input
390
- ).padEnd(inputMaxLen + 5),
391
- status: event?.padEnd(10),
392
- message: `${tags?.join(', ')}: ${message}`
517
+ const bar = bars[key]
518
+ // eslint-disable-next-line ts/strict-boolean-expressions
519
+ if (!bar) return
520
+ let time = times[key] ?? 1
521
+ if (!locks[key]) {
522
+ time += 1
523
+ times[key] = time
524
+ setTimeout(() => {
525
+ locks[key] = false
526
+ }, 100)
527
+ bar.update(0, {
528
+ input: `${input}(x${time.toString().padStart(2, '0')})`.padEnd(inputMaxLen + 5),
529
+ status: 'watching'.padEnd(10),
530
+ message: 'watching...'
531
+ })
393
532
  }
394
- )
395
- }
396
- if (e.type === 'watchChange') {
397
- const {
398
- path,
399
- input
400
- } = e.data
401
- const key = `${input}:${path}`
402
- const bar = bars[key]
403
- if (!bar) return
404
- let time = times[key] ?? 1
405
- if (!locks[key]) {
406
- time += 1
407
- times[key] = time
408
- setTimeout(() => {
409
- locks[key] = false
410
- }, 100)
411
- bar.update(0, {
412
- input: `${input}(x${time.toString().padStart(2, '0')})`.padEnd(inputMaxLen + 5),
413
- status: 'watching'.padEnd(10),
414
- message: 'watching...'
533
+ locks[key] = true
534
+ break
535
+ }
536
+ case 'modulesAnalyze': {
537
+ const {
538
+ data: {
539
+ type,
540
+ path,
541
+ modules: pkgModules
542
+ }
543
+ } = e
544
+ pkgModules.forEach(m => {
545
+ const newM = {
546
+ ...m,
547
+ filename: `${manifest.name}/${m.filename}`,
548
+ label: `${manifest.name}/${m.label}`
549
+ }
550
+ const pushOrReplace = (arr: Module[]) => {
551
+ const index = arr.findIndex(({ filename }) => filename === newM.filename)
552
+ if (index === -1) {
553
+ arr.push(newM)
554
+ } else {
555
+ arr[index] = newM
556
+ }
557
+ }
558
+ pushOrReplace(modules)
559
+ if (type === 'esm') {
560
+ pushOrReplace(esmModules)
561
+ }
562
+ if (type === 'cjs') {
563
+ pushOrReplace(cjsModules)
564
+ }
415
565
  })
566
+ void refreshAnalyzer()
567
+ void refreshAnalyzer(
568
+ `/${type}`,
569
+ {
570
+ cjs: cjsModules,
571
+ esm: esmModules
572
+ }[type]
573
+ )
574
+ void refreshAnalyzer(`/${type}/${manifest.name}/${path.slice(2)}`, pkgModules)
575
+ break
416
576
  }
417
- locks[key] = true
577
+ case 'debug': {
578
+ // eslint-disable-next-line no-console,ts/no-unsafe-argument
579
+ console.log(...(Array.isArray(e.data) ? e.data : [e.data]))
580
+ break
581
+ }
582
+ default:
418
583
  }
419
584
  })
420
585
  await new Promise<void>((resolve, reject) => {
@@ -431,6 +596,7 @@ command
431
596
  })
432
597
  )
433
598
  }
599
+
434
600
  const commandFilters = IS_WORKSPACE ? commandFiltersOrEntries : undefined
435
601
  const filters = [
436
602
  ...new Set([
@@ -455,5 +621,20 @@ command
455
621
  }
456
622
  } finally {
457
623
  multiBars.stop()
624
+ let message = 'The build is complete'
625
+ if (analyzer) {
626
+ message += ` and the analyzer is running at http://localhost:${options.port}/ana in ${analyzer.mode} mode.\n`
627
+ message += analyzer.open ? ' The browser will open automatically.\n' : ''
628
+ if (anaPaths.size > 0) {
629
+ message += `The analyzer has ${anaPaths.size} pages:\n${
630
+ Array
631
+ .from(anaPaths)
632
+ .map(p => `http://localhost:${options.port}${p}`)
633
+ .join('\n')
634
+ }`
635
+ }
636
+ }
637
+ // eslint-disable-next-line no-console
638
+ !silent && console.log(message)
458
639
  }
459
640
  })
@@ -216,6 +216,17 @@ async function prepublish() {
216
216
  }
217
217
  }
218
218
  }
219
+ if (oldJSON['devDependencies']) {
220
+ newJSONString = applyEdits(
221
+ newJSONString,
222
+ modify(
223
+ newJSONString,
224
+ ['devDependencies'],
225
+ undefined,
226
+ { formattingOptions }
227
+ )
228
+ )
229
+ }
219
230
  if (oldJSON['peerDependencies']) {
220
231
  const peerDependenciesMeta = Object.keys(oldJSON['peerDependencies']).reduce(
221
232
  (acc, key) => {
@@ -0,0 +1,26 @@
1
+ import { program } from 'commander'
2
+ import process from 'node:process'
3
+
4
+ const { argv } = process
5
+ const env: Record<string, string> = {}
6
+ let isPassThrough = false
7
+ const newArgv = argv.filter((arg) => {
8
+ if (isPassThrough) {
9
+ return true
10
+ }
11
+ if (arg === '--') {
12
+ isPassThrough = true
13
+ return false
14
+ }
15
+ const m = /^--env\.(\w+)=(.*)$/.exec(arg)
16
+ if (m) {
17
+ env[m[1]] = m[2]
18
+ return false
19
+ }
20
+ return true
21
+ })
22
+ for (const [key, value] of Object.entries(env)) {
23
+ process.env[key] = value
24
+ }
25
+
26
+ export default () => program.parse(newArgv)
@@ -100,38 +100,3 @@ export interface TemplateOptions {
100
100
  dts: InputPluginOption
101
101
  }
102
102
  }
103
-
104
- export type RollupProgressEvent =
105
- | {
106
- type: 'init'
107
- data: {
108
- leafMap: Map<string, string[][]>
109
- targetsLength: number
110
- }
111
- }
112
- | {
113
- type: 'watchChange'
114
- data: {
115
- id: string
116
- name: string
117
- path: string
118
- input: string
119
- }
120
- }
121
- | {
122
- type: 'debug'
123
- data: unknown
124
- }
125
- | {
126
- type: 'progress'
127
- data: {
128
- // name, path, exportConditions, input
129
- name: string
130
- path: string
131
- exportConditions: string[]
132
- input: string
133
- tags?: string[]
134
- event?: string
135
- message?: string
136
- }
137
- }
@@ -0,0 +1,62 @@
1
+ import process from 'node:process'
2
+
3
+ import type { InputPluginOption, OutputPlugin, Plugin } from 'rollup'
4
+ import type { AnalyzerPluginInternalAPI } from 'vite-bundle-analyzer'
5
+
6
+ export type Module = ReturnType<AnalyzerPluginInternalAPI['processModule']>[number]
7
+
8
+ const {
9
+ JIEK_ANALYZER
10
+ } = process.env
11
+
12
+ const ANALYZER = (JIEK_ANALYZER != null) && JSON.parse(JIEK_ANALYZER) as {
13
+ dir?: string
14
+ mode?: string
15
+ size?: string
16
+ port?: number
17
+ open?: boolean
18
+ }
19
+
20
+ export function bundleAnalyzer(modulesResolved: (modules: Module[]) => void) {
21
+ // eslint-disable-next-line ts/strict-boolean-expressions
22
+ if (!ANALYZER) {
23
+ return []
24
+ }
25
+
26
+ const defaultSizes = ({
27
+ parsed: 'parsed',
28
+ stat: 'stat',
29
+ gzip: 'gzip'
30
+ } as const)[ANALYZER.size ?? 'stat'] ?? 'parsed'
31
+
32
+ let module: typeof import('vite-bundle-analyzer') | undefined
33
+ let ana: Plugin | undefined
34
+ async function initAna() {
35
+ const { adapter, analyzer } = module ?? await import('vite-bundle-analyzer')
36
+ ana = ana ?? adapter(analyzer({
37
+ defaultSizes,
38
+ analyzerMode: modulesResolved
39
+ }))
40
+ }
41
+
42
+ return [
43
+ (async () => {
44
+ await initAna()
45
+ return {
46
+ name: 'jiek:bundle-analyzer',
47
+ async closeBundle(...args) {
48
+ if (typeof ana!.closeBundle !== 'function') return
49
+
50
+ return ana!.closeBundle?.call(this, ...args)
51
+ }
52
+ } satisfies InputPluginOption
53
+ })(),
54
+ (async () => {
55
+ await initAna()
56
+ return {
57
+ ...ana,
58
+ name: 'jiek:bundle-analyzer-output'
59
+ } satisfies OutputPlugin
60
+ })()
61
+ ] as const
62
+ }