jiek 2.1.12 → 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,4 +1,4 @@
1
- import fs, { existsSync, statSync } 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
4
  import process from 'node:process'
@@ -7,18 +7,21 @@ import { confirm } from '@inquirer/prompts'
7
7
  import { MultiBar, Presets } from 'cli-progress'
8
8
  import { program } from 'commander'
9
9
  import { execaCommand } from 'execa'
10
+ import type { renderView } from 'vite-bundle-analyzer'
10
11
 
12
+ import type { RollupBuildEvent } from '#~/bridge.ts'
11
13
  import { entriesDescription, filterDescription, outdirDescription } from '#~/commands/descriptions.ts'
12
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'
13
19
  import type { ProjectsGraph } from '#~/utils/filterSupport.ts'
14
20
  import { filterPackagesGraph, getSelectedProjectsGraph } from '#~/utils/filterSupport.ts'
15
21
  import { getWD } from '#~/utils/getWD.ts'
16
22
  import { loadConfig } from '#~/utils/loadConfig.ts'
17
23
  import { tsRegisterName } from '#~/utils/tsRegister.ts'
18
24
 
19
- import type { RollupProgressEvent, TemplateOptions } from '../rollup/base'
20
- import { BUILDER_TYPES, BUILDER_TYPE_PACKAGE_NAME_MAP } from '../rollup/base'
21
-
22
25
  declare module 'jiek' {
23
26
  export interface Config {
24
27
  build?: TemplateOptions & {
@@ -56,7 +59,6 @@ interface BuildOptions {
56
59
  * @default 'server'
57
60
  */
58
61
  'ana.mode': string
59
- 'ana.port'?: number
60
62
  'ana.open'?: boolean
61
63
  /**
62
64
  * @default 'parsed'
@@ -77,6 +79,12 @@ interface BuildOptions {
77
79
  */
78
80
  outdir: string
79
81
  watch: boolean
82
+ /**
83
+ * The port of the server.
84
+ *
85
+ * @default 8888
86
+ */
87
+ port: number
80
88
  silent: boolean
81
89
  verbose: boolean
82
90
  entries?: string
@@ -207,12 +215,12 @@ command = command
207
215
 
208
216
  command = command
209
217
  .option('-w, --watch', 'Watch the file changes.', parseBoolean)
218
+ .option('-p, --port <PORT>', 'The port of the server.', Number.parseInt, 8888)
210
219
 
211
220
  command = command
212
221
  .option('--ana', 'Enable the bundle analyzer.', parseBoolean)
213
222
  .option('--ana.dir <DIR>', 'The directory of the bundle analyzer.', '.jk-analyses')
214
223
  .option('--ana.mode <MODE>', 'The mode of the bundle analyzer, support "static", "json" and "server".', 'server')
215
- .option('--ana.port <PORT>', 'The port of the bundle analyzer.', Number.parseInt)
216
224
  .option('--ana.open', 'Open the bundle analyzer in the browser.', parseBoolean)
217
225
  .option(
218
226
  '--ana.size <SIZE>',
@@ -274,18 +282,47 @@ command
274
282
  [] as string[]
275
283
  )
276
284
 
285
+ const modules: Module[] = []
286
+ const cjsModules: Module[] = []
287
+ const esmModules: Module[] = []
288
+ let render: typeof renderView | undefined
277
289
  const analyzer = options.ana
278
290
  ? {
279
291
  dir: options['ana.dir'],
280
292
  mode: options['ana.mode'],
281
- port: options['ana.port'],
282
293
  open: options['ana.open'],
283
294
  size: options['ana.size']
284
295
  }
285
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')
286
308
 
287
309
  if (analyzer) {
288
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
+ )
289
326
  }
290
327
 
291
328
  const { build } = loadConfig()
@@ -336,12 +373,12 @@ command
336
373
  throw new Error('no package found')
337
374
  }
338
375
  const wdNodeModules = path.resolve(wd, 'node_modules')
339
- if (!fs.existsSync(wdNodeModules)) {
340
- fs.mkdirSync(wdNodeModules)
376
+ if (!existsSync(wdNodeModules)) {
377
+ mkdirSync(wdNodeModules)
341
378
  }
342
379
  const jiekTempDir = (...paths: string[]) => path.resolve(wdNodeModules, '.jiek', ...paths)
343
- if (!fs.existsSync(jiekTempDir())) {
344
- fs.mkdirSync(jiekTempDir())
380
+ if (!existsSync(jiekTempDir())) {
381
+ mkdirSync(jiekTempDir())
345
382
  }
346
383
 
347
384
  const rollupBinaryPath = require.resolve('rollup')
@@ -355,15 +392,15 @@ command
355
392
  if (analyzer) {
356
393
  const anaDir = path.resolve(dir, analyzer.dir)
357
394
  if (!existsSync(anaDir)) {
358
- fs.mkdirSync(anaDir, { recursive: true })
395
+ mkdirSync(anaDir, { recursive: true })
359
396
  }
360
397
  const gitIgnorePath = path.resolve(anaDir, '.gitignore')
361
398
  if (!existsSync(gitIgnorePath)) {
362
- fs.writeFileSync(gitIgnorePath, '*\n!.gitignore\n')
399
+ writeFileSync(gitIgnorePath, '*\n!.gitignore\n')
363
400
  }
364
401
  const npmIgnorePath = path.resolve(anaDir, '.npmignore')
365
402
  if (!existsSync(npmIgnorePath)) {
366
- fs.writeFileSync(npmIgnorePath, '*\n')
403
+ writeFileSync(npmIgnorePath, '*\n')
367
404
  }
368
405
  if (!statSync(anaDir).isDirectory()) {
369
406
  throw new Error(`The directory '${anaDir}' is not a directory.`)
@@ -375,7 +412,7 @@ command
375
412
  const configFile = jiekTempDir(
376
413
  `${escapeManifestName ?? `anonymous-${i++}`}.rollup.config.js`
377
414
  )
378
- fs.writeFileSync(configFile, FILE_TEMPLATE(manifest))
415
+ writeFileSync(configFile, FILE_TEMPLATE(manifest))
379
416
  const command = [rollupBinaryPath, '--silent', '-c', configFile]
380
417
  if (tsRegisterName != null) {
381
418
  command.unshift(`node -r ${tsRegisterName}`)
@@ -397,97 +434,152 @@ command
397
434
  const times: Record<string, number> = {}
398
435
  const locks: Record<string, boolean> = {}
399
436
  let inputMaxLen = 10
400
- child.on('message', (e: RollupProgressEvent) => {
401
- // eslint-disable-next-line no-console,ts/no-unsafe-argument
402
- if (e.type === 'debug') console.log(...(Array.isArray(e.data) ? e.data : [e.data]))
403
- })
404
- !silent && child.on('message', (e: RollupProgressEvent) => {
405
- if (e.type === 'init') {
406
- const { leafMap, targetsLength } = e.data
407
- const leafs = Array
408
- .from(leafMap.entries())
409
- .flatMap(([input, pathAndCondiions]) =>
410
- pathAndCondiions.map(([path, ...conditions]) => ({
411
- input,
412
- path,
413
- conditions
414
- }))
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
+ }
415
508
  )
416
- let initMessage = `Package '${manifest.name}' has ${targetsLength} targets to build`
417
- if (watch) {
418
- initMessage += ' and watching...'
509
+ break
419
510
  }
420
- // eslint-disable-next-line no-console
421
- console.log(initMessage)
422
- leafs.forEach(({ input }) => {
423
- inputMaxLen = Math.max(inputMaxLen, input.length)
424
- })
425
- leafs.forEach(({ input, path }) => {
511
+ case 'watchChange': {
512
+ const {
513
+ path,
514
+ input
515
+ } = e.data
426
516
  const key = `${input}:${path}`
517
+ const bar = bars[key]
427
518
  // eslint-disable-next-line ts/strict-boolean-expressions
428
- if (bars[key]) return
429
- bars[key] = multiBars.create(50, 0, {
430
- pkgName: manifest.name,
431
- input: input.padEnd(inputMaxLen + 5),
432
- status: 'waiting'.padEnd(10)
433
- }, {
434
- barsize: 20,
435
- linewrap: true
436
- })
437
- })
438
- }
439
- if (e.type === 'progress') {
440
- const {
441
- path,
442
- tags,
443
- input,
444
- event,
445
- message
446
- } = e.data
447
- const bar = bars[`${input}:${path}`]
448
- // eslint-disable-next-line ts/strict-boolean-expressions
449
- if (!bar) return
450
- const time = times[`${input}:${path}`]
451
- bar.update(
452
- {
453
- start: 0,
454
- resolve: 20,
455
- end: 50
456
- }[event ?? 'start'] ?? 0,
457
- {
458
- input: (
459
- time
460
- ? `${input}(x${time.toString().padStart(2, '0')})`
461
- : input
462
- ).padEnd(inputMaxLen + 5),
463
- status: event?.padEnd(10),
464
- message: `${tags?.join(', ')}: ${message}`
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
+ })
465
532
  }
466
- )
467
- }
468
- if (e.type === 'watchChange') {
469
- const {
470
- path,
471
- input
472
- } = e.data
473
- const key = `${input}:${path}`
474
- const bar = bars[key]
475
- // eslint-disable-next-line ts/strict-boolean-expressions
476
- if (!bar) return
477
- let time = times[key] ?? 1
478
- if (!locks[key]) {
479
- time += 1
480
- times[key] = time
481
- setTimeout(() => {
482
- locks[key] = false
483
- }, 100)
484
- bar.update(0, {
485
- input: `${input}(x${time.toString().padStart(2, '0')})`.padEnd(inputMaxLen + 5),
486
- status: 'watching'.padEnd(10),
487
- 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
+ }
488
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
489
576
  }
490
- 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:
491
583
  }
492
584
  })
493
585
  await new Promise<void>((resolve, reject) => {
@@ -504,6 +596,7 @@ command
504
596
  })
505
597
  )
506
598
  }
599
+
507
600
  const commandFilters = IS_WORKSPACE ? commandFiltersOrEntries : undefined
508
601
  const filters = [
509
602
  ...new Set([
@@ -528,5 +621,20 @@ command
528
621
  }
529
622
  } finally {
530
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)
531
639
  }
532
640
  })
@@ -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
+ }