jiek 2.2.0 → 2.2.2

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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "jiek",
3
3
  "type": "module",
4
- "version": "2.2.0",
4
+ "version": "2.2.2",
5
5
  "description": "A lightweight toolkit for compiling and managing libraries based on `package.json` metadata and suitable for `Monorepo`.",
6
6
  "author": "YiJie <yijie4188@gmail.com>",
7
7
  "homepage": "https://github.com/NWYLZW/jiek/tree/master/packages/jiek#readme",
@@ -48,7 +48,11 @@
48
48
  }
49
49
  },
50
50
  "imports": {
51
- "#~/*": "./src/*"
51
+ "#~/*": [
52
+ "./src/*",
53
+ "./src/*/index.ts",
54
+ "./src/*/index.tsx"
55
+ ]
52
56
  },
53
57
  "bin": {
54
58
  "jiek": "bin/jiek.js",
@@ -65,7 +69,7 @@
65
69
  "rollup-plugin-postcss": "^4.0.2",
66
70
  "rollup-plugin-swc3": "^0.12.1",
67
71
  "typescript": "^4.0.0||^5.0.0",
68
- "vite-bundle-analyzer": "^0.15.2"
72
+ "vite-bundle-analyzer": "0.16.0-beta.1"
69
73
  },
70
74
  "dependencies": {
71
75
  "@inquirer/prompts": "^7.1.0",
@@ -0,0 +1,122 @@
1
+ import type { Command } from 'commander'
2
+
3
+ import { CLIENT_CUSTOM_RENDER_SCRIPT } from '#~/commands/build/client/index.ts'
4
+ import { parseBoolean } from '#~/commands/utils/optionParser.ts'
5
+ import type { Module } from '#~/rollup/bundle-analyzer.ts'
6
+ import type { createServer } from '#~/server.ts'
7
+ import { checkDependency } from '#~/utils/checkDependency.ts'
8
+ import { existsSync, mkdirSync, statSync, writeFileSync } from 'node:fs'
9
+ import path from 'node:path'
10
+
11
+ export interface AnalyzerBuildOptions {
12
+ ana?: boolean
13
+ /**
14
+ * @default '.jk-analyses'
15
+ */
16
+ 'ana.dir': string
17
+ /**
18
+ * @default 'server'
19
+ */
20
+ 'ana.mode': string
21
+ 'ana.open'?: boolean
22
+ /**
23
+ * @default 'parsed'
24
+ */
25
+ 'ana.size': string
26
+ }
27
+
28
+ export const registerAnalyzerCommandOptions = (command: Command) =>
29
+ command
30
+ .option('--ana', 'Enable the bundle analyzer.', parseBoolean)
31
+ .option('--ana.dir <DIR>', 'The directory of the bundle analyzer.', '.jk-analyses')
32
+ .option(
33
+ '--ana.mode <MODE>',
34
+ 'The mode of the bundle analyzer, support "static", "json" and "server".',
35
+ 'server'
36
+ )
37
+ .option('--ana.open', 'Open the bundle analyzer in the browser.', parseBoolean)
38
+ .option(
39
+ '--ana.size <SIZE>',
40
+ 'The default size of the bundle analyzer, support "stat", "parsed" and "gzip".',
41
+ 'parsed'
42
+ )
43
+
44
+ export const useAnalyzer = async (options: AnalyzerBuildOptions, server?: ReturnType<typeof createServer>) => {
45
+ const modules: Module[] = []
46
+ let bundleAnalyzerModule: typeof import('vite-bundle-analyzer') | undefined
47
+ const analyzer = options.ana
48
+ ? {
49
+ dir: options['ana.dir'],
50
+ mode: options['ana.mode'],
51
+ open: options['ana.open'],
52
+ size: options['ana.size']
53
+ }
54
+ : undefined
55
+ if (
56
+ options.ana
57
+ && ![
58
+ 'stat',
59
+ 'parsed',
60
+ 'gzip'
61
+ ].includes(analyzer?.size ?? '')
62
+ ) {
63
+ throw new Error('The value of `ana.size` must be "stat", "parsed" or "gzip"')
64
+ }
65
+
66
+ if (analyzer) {
67
+ await checkDependency('vite-bundle-analyzer')
68
+ bundleAnalyzerModule = await import('vite-bundle-analyzer')
69
+ }
70
+
71
+ const refreshAnalyzer = async (cwd: string, applyModules: typeof modules) => {
72
+ if (!(analyzer && server && bundleAnalyzerModule)) return
73
+
74
+ if (analyzer.mode === 'json') {
75
+ const anaDir = path.resolve(cwd, analyzer.dir)
76
+ if (!existsSync(anaDir)) {
77
+ mkdirSync(anaDir, { recursive: true })
78
+ }
79
+ const gitIgnorePath = path.resolve(anaDir, '.gitignore')
80
+ if (!existsSync(gitIgnorePath)) {
81
+ writeFileSync(gitIgnorePath, '*\n!.gitignore\n')
82
+ }
83
+ const npmIgnorePath = path.resolve(anaDir, '.npmignore')
84
+ if (!existsSync(npmIgnorePath)) {
85
+ writeFileSync(npmIgnorePath, '*\n')
86
+ }
87
+ if (!statSync(anaDir).isDirectory()) {
88
+ throw new Error(`The directory '${anaDir}' is not a directory.`)
89
+ }
90
+ }
91
+
92
+ const { renderView, injectHTMLTag } = bundleAnalyzerModule
93
+ applyModules.forEach(m => {
94
+ const index = modules.findIndex(({ filename }) => filename === m.filename)
95
+ if (index === -1) {
96
+ modules.push(m)
97
+ } else {
98
+ modules[index] = m
99
+ }
100
+ })
101
+ let html = await renderView(modules, {
102
+ title: `Jiek Analyzer`,
103
+ mode: analyzer.size as 'stat' | 'parsed' | 'gzip'
104
+ })
105
+ html = injectHTMLTag({
106
+ html,
107
+ injectTo: 'body',
108
+ descriptors: [
109
+ { kind: 'script', text: CLIENT_CUSTOM_RENDER_SCRIPT }
110
+ ]
111
+ })
112
+ void server.renderTo('/ana', html)
113
+ }
114
+
115
+ return {
116
+ modules,
117
+ refreshAnalyzer,
118
+ ANALYZER_ENV: {
119
+ JIEK_ANALYZER: analyzer ? JSON.stringify(analyzer) : undefined
120
+ }
121
+ }
122
+ }
@@ -0,0 +1,121 @@
1
+ import type { Module } from '#~/rollup/bundle-analyzer.ts'
2
+
3
+ interface Node {
4
+ id: string
5
+ filename: string
6
+ parent?: Node
7
+ }
8
+
9
+ declare global {
10
+ // @ts-ignore
11
+ // eslint-disable-next-line no-var,vars-on-top
12
+ var React: typeof import('react')
13
+ // eslint-disable-next-line no-var,vars-on-top
14
+ var analyzeModule: Module[]
15
+ interface WindowEventMap {
16
+ 'graph:click': CustomEvent<
17
+ | undefined
18
+ | { node: Node }
19
+ >
20
+ 'send:filter': CustomEvent<{
21
+ analyzeModule: Module[]
22
+ }>
23
+ }
24
+ }
25
+
26
+ export function Main() {
27
+ const { useState, useMemo, useEffect, useCallback } = React
28
+ const [path, setPath] = useState(() => location.pathname.replace(/^\/ana\/?/, ''))
29
+ const [pkgName, entry] = useMemo(() => {
30
+ const pkgName = /^(@[^/]+\/[^/]+|[^/]+)\/?/.exec(path)?.[1]
31
+ return [
32
+ pkgName,
33
+ (pkgName != null) ? path.replace(`${pkgName}/`, '') : undefined
34
+ ]
35
+ }, [path])
36
+ const push = useCallback((newPath: string) => {
37
+ setPath(newPath)
38
+ document.title = `${document.title.replace(/ - \/.*/, '')} - \/${newPath}`
39
+ history.pushState(null, '', `/ana/${newPath}`)
40
+ }, [])
41
+ const filterModules = useCallback((startWith: string) => {
42
+ const modules = analyzeModule.filter(m => m.filename.startsWith(startWith))
43
+ dispatchEvent(new CustomEvent('send:filter', { detail: { analyzeModule: modules } }))
44
+ }, [])
45
+ useEffect(() => {
46
+ if (path !== '') {
47
+ document.title = `${document.title.replace(/ - \/.*/, '')} - \/${path}`
48
+ } else {
49
+ document.title = document.title.replace(/ - \/.*/, '')
50
+ }
51
+ filterModules(path)
52
+ }, [path, filterModules])
53
+ useEffect(() => {
54
+ const offGraphClick = listen('graph:click', ({ detail }) => {
55
+ if (!detail) return
56
+
57
+ let root = detail.node
58
+ while (root.parent) {
59
+ root = root.parent
60
+ }
61
+ if (root.filename === path) return
62
+ push(root.filename)
63
+ })
64
+ return () => {
65
+ offGraphClick()
66
+ }
67
+ }, [push])
68
+ function listen<T extends keyof WindowEventMap>(type: T, listener: (this: Window, ev: WindowEventMap[T]) => any) {
69
+ window.addEventListener(type, listener)
70
+ return () => {
71
+ window.removeEventListener(type, listener)
72
+ }
73
+ }
74
+ return (
75
+ <div
76
+ style={{
77
+ padding: '12px 55px'
78
+ }}
79
+ >
80
+ /
81
+ <select
82
+ style={{
83
+ appearance: 'none',
84
+ border: 'none',
85
+ background: 'none'
86
+ }}
87
+ value={pkgName}
88
+ onChange={e => push(e.target.value)}
89
+ >
90
+ <option value=''>All</option>
91
+ {analyzeModule
92
+ .map(m => /^(@[^/]+\/[^/]+|[^/]+)\/?/.exec(m.filename)?.[1])
93
+ .filter((v, i, a) => a.indexOf(v) === i)
94
+ .map(v => (
95
+ <option key={v} value={v}>{v}</option>
96
+ ))}
97
+ </select>
98
+ {pkgName != null && <>
99
+ /
100
+ <select
101
+ style={{
102
+ appearance: 'none',
103
+ border: 'none',
104
+ background: 'none'
105
+ }}
106
+ value={entry}
107
+ onChange={e => push(`${pkgName}/${e.target.value}`)}
108
+ >
109
+ <option value=''>All</option>
110
+ {analyzeModule
111
+ .filter(m => m.filename.startsWith(`${pkgName}/`))
112
+ .map(m => m.filename.replace(`${pkgName}/`, ''))
113
+ .filter((v, i, a) => a.indexOf(v) === i)
114
+ .map(v => (
115
+ <option key={v} value={v}>{v}</option>
116
+ ))}
117
+ </select>
118
+ </>}
119
+ </div>
120
+ )
121
+ }
@@ -0,0 +1,28 @@
1
+ import { Main } from './analyzer'
2
+
3
+ declare global {
4
+ // eslint-disable-next-line no-var,vars-on-top
5
+ var CUSTOM_SIDE_BAR: boolean
6
+ // eslint-disable-next-line no-var,vars-on-top
7
+ var __REPLACE_INJECT__: string
8
+ }
9
+
10
+ function render() {
11
+ CUSTOM_SIDE_BAR = true
12
+ window.addEventListener('client:ready', () =>
13
+ setTimeout(() => {
14
+ window.dispatchEvent(
15
+ new CustomEvent('send:ui', {
16
+ detail: { type: 'Main', Component: __REPLACE_INJECT__ }
17
+ })
18
+ )
19
+ }, 0))
20
+ }
21
+
22
+ export const CLIENT_CUSTOM_RENDER_SCRIPT = [
23
+ Main.toString(),
24
+ render
25
+ .toString()
26
+ .replace('__REPLACE_INJECT__', Main.name),
27
+ `(${render.name})()`
28
+ ].join('\n')
@@ -1,24 +1,24 @@
1
- import { existsSync, mkdirSync, statSync, writeFileSync } from 'node:fs'
1
+ import { existsSync, mkdirSync, 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'
5
5
 
6
- import { confirm } from '@inquirer/prompts'
7
6
  import { MultiBar, Presets } from 'cli-progress'
8
7
  import { program } from 'commander'
9
8
  import { execaCommand } from 'execa'
10
- import type { renderView } from 'vite-bundle-analyzer'
11
9
 
12
10
  import type { RollupBuildEvent } from '#~/bridge.ts'
11
+ import type { AnalyzerBuildOptions } from '#~/commands/build/analyzer.ts'
12
+ import { registerAnalyzerCommandOptions, useAnalyzer } from '#~/commands/build/analyzer.ts'
13
13
  import { entriesDescription, filterDescription, outdirDescription } from '#~/commands/descriptions.ts'
14
14
  import { IS_WORKSPACE } from '#~/commands/meta.ts'
15
+ import { parseBoolean } from '#~/commands/utils/optionParser.ts'
15
16
  import type { TemplateOptions } from '#~/rollup/base.ts'
16
17
  import { BUILDER_TYPES, BUILDER_TYPE_PACKAGE_NAME_MAP } from '#~/rollup/base.ts'
17
- import type { Module } from '#~/rollup/bundle-analyzer.ts'
18
18
  import { createServer } from '#~/server.ts'
19
+ import { checkDependency } from '#~/utils/checkDependency.ts'
19
20
  import type { ProjectsGraph } from '#~/utils/filterSupport.ts'
20
21
  import { filterPackagesGraph, getSelectedProjectsGraph } from '#~/utils/filterSupport.ts'
21
- import { getWD } from '#~/utils/getWD.ts'
22
22
  import { loadConfig } from '#~/utils/loadConfig.ts'
23
23
  import { tsRegisterName } from '#~/utils/tsRegister.ts'
24
24
 
@@ -49,21 +49,7 @@ If you want to through the options to the \`rollup\` command, you can pass the o
49
49
  ${isDefault ? 'This command is the default command.' : ''}
50
50
  `.trim()
51
51
 
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
52
+ interface BuildOptions extends AnalyzerBuildOptions {
67
53
  /**
68
54
  * Auto-detect the builder from the installed dependencies.
69
55
  * If the builder is not installed, it will prompt the user to install it.
@@ -120,22 +106,6 @@ interface BuildOptions {
120
106
  dtsconfig?: string
121
107
  }
122
108
 
123
- async function checkDependency(dependency: string) {
124
- try {
125
- require.resolve(dependency)
126
- } catch {
127
- console.error(`The package '${dependency}' is not installed, please install it first.`)
128
- const { notWorkspace } = getWD()
129
- const command = `pnpm install -${notWorkspace ? '' : 'w'}D ${dependency}`
130
- if (await confirm({ message: 'Do you want to install it now?' })) {
131
- await execaCommand(command)
132
- } else {
133
- console.warn(`You can run the command '${command}' to install it manually.`)
134
- process.exit(1)
135
- }
136
- }
137
- }
138
-
139
109
  let DEFAULT_BUILDER_TYPE: typeof BUILDER_TYPES[number]
140
110
  Object.entries(BUILDER_TYPE_PACKAGE_NAME_MAP).forEach(([type, packageName]) => {
141
111
  try {
@@ -147,11 +117,6 @@ if (!DEFAULT_BUILDER_TYPE!) {
147
117
  DEFAULT_BUILDER_TYPE = 'esbuild'
148
118
  }
149
119
 
150
- function parseBoolean(v?: unknown) {
151
- if (v === undefined) return true
152
- return Boolean(v)
153
- }
154
-
155
120
  const buildFilterDescription = `
156
121
  ${filterDescription}
157
122
  If you pass the --filter option, it will merge into the filters of the command.
@@ -217,16 +182,7 @@ command = command
217
182
  .option('-w, --watch', 'Watch the file changes.', parseBoolean)
218
183
  .option('-p, --port <PORT>', 'The port of the server.', Number.parseInt, 8888)
219
184
 
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
- )
185
+ command = registerAnalyzerCommandOptions(command)
230
186
 
231
187
  command = command
232
188
  .option('-s, --silent', "Don't display logs.", parseBoolean)
@@ -282,48 +238,17 @@ command
282
238
  [] as string[]
283
239
  )
284
240
 
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
- }
241
+ const shouldCreateServer = [
242
+ options.ana === true && options['ana.mode'] === 'server'
243
+ ].some(Boolean)
244
+ const server = shouldCreateServer
245
+ ? createServer(options.port, 'localhost')
296
246
  : 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
247
 
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
- }
248
+ const {
249
+ ANALYZER_ENV,
250
+ refreshAnalyzer
251
+ } = await useAnalyzer(options, server)
327
252
 
328
253
  const { build } = loadConfig()
329
254
  silent = silent ?? build?.silent ?? false
@@ -343,7 +268,7 @@ command
343
268
  entries = undefined
344
269
  }
345
270
  const env = {
346
- JIEK_ANALYZER: analyzer && JSON.stringify(analyzer),
271
+ ...ANALYZER_ENV,
347
272
  JIEK_BUILDER: type,
348
273
  JIEK_OUT_DIR: outdir,
349
274
  JIEK_CLEAN: String(!noClean),
@@ -388,27 +313,10 @@ command
388
313
  .replace(/dist\/rollup.js$/, 'dist/bin/rollup')
389
314
  let i = 0
390
315
  await Promise.all(
391
- Object.entries(value).map(async ([dir, manifest]) => {
316
+ Object.entries(value).map(async ([pkgCWD, manifest]) => {
392
317
  if (manifest.name == null) {
393
318
  throw new Error('package.json must have a name field')
394
319
  }
395
- if (analyzer) {
396
- const anaDir = path.resolve(dir, analyzer.dir)
397
- if (!existsSync(anaDir)) {
398
- mkdirSync(anaDir, { recursive: true })
399
- }
400
- const gitIgnorePath = path.resolve(anaDir, '.gitignore')
401
- if (!existsSync(gitIgnorePath)) {
402
- writeFileSync(gitIgnorePath, '*\n!.gitignore\n')
403
- }
404
- const npmIgnorePath = path.resolve(anaDir, '.npmignore')
405
- if (!existsSync(npmIgnorePath)) {
406
- writeFileSync(npmIgnorePath, '*\n')
407
- }
408
- if (!statSync(anaDir).isDirectory()) {
409
- throw new Error(`The directory '${anaDir}' is not a directory.`)
410
- }
411
- }
412
320
 
413
321
  // TODO support auto build child packages in workspaces
414
322
  const escapeManifestName = manifest.name.replace(/^@/g, '').replace(/\//g, '+')
@@ -426,7 +334,7 @@ command
426
334
  command.push(...passThroughOptions)
427
335
  const child = execaCommand(command.join(' '), {
428
336
  ipc: true,
429
- cwd: dir,
337
+ cwd: pkgCWD,
430
338
  env: {
431
339
  ...env,
432
340
  JIEK_NAME: manifest.name,
@@ -540,41 +448,18 @@ command
540
448
  const {
541
449
  data: {
542
450
  type,
543
- path,
544
451
  modules: pkgModules
545
452
  }
546
453
  } = e
547
- pkgModules.forEach(m => {
548
- const newM = {
454
+ void refreshAnalyzer(
455
+ pkgCWD,
456
+ pkgModules.map(m => ({
549
457
  ...m,
458
+ type,
550
459
  filename: `${manifest.name}/${m.filename}`,
551
460
  label: `${manifest.name}/${m.label}`
552
- }
553
- const pushOrReplace = (arr: Module[]) => {
554
- const index = arr.findIndex(({ filename }) => filename === newM.filename)
555
- if (index === -1) {
556
- arr.push(newM)
557
- } else {
558
- arr[index] = newM
559
- }
560
- }
561
- pushOrReplace(modules)
562
- if (type === 'esm') {
563
- pushOrReplace(esmModules)
564
- }
565
- if (type === 'cjs') {
566
- pushOrReplace(cjsModules)
567
- }
568
- })
569
- void refreshAnalyzer()
570
- void refreshAnalyzer(
571
- `/${type}`,
572
- {
573
- cjs: cjsModules,
574
- esm: esmModules
575
- }[type]
461
+ }))
576
462
  )
577
- void refreshAnalyzer(`/${type}/${manifest.name}/${path.slice(2)}`, pkgModules)
578
463
  break
579
464
  }
580
465
  case 'debug': {
@@ -624,20 +509,7 @@ command
624
509
  }
625
510
  } finally {
626
511
  multiBars.stop()
627
- let message = 'The build is complete'
628
- if (analyzer) {
629
- message += ` and the analyzer is running at http://localhost:${options.port}/ana in ${analyzer.mode} mode.\n`
630
- message += analyzer.open ? ' The browser will open automatically.\n' : ''
631
- if (anaPaths.size > 0) {
632
- message += `The analyzer has ${anaPaths.size} pages:\n${
633
- Array
634
- .from(anaPaths)
635
- .map(p => `http://localhost:${options.port}${p}`)
636
- .join('\n')
637
- }`
638
- }
639
- }
640
512
  // eslint-disable-next-line no-console
641
- !silent && console.log(message)
513
+ !silent && console.log('Build complete')
642
514
  }
643
515
  })
@@ -0,0 +1,4 @@
1
+ export function parseBoolean(v?: unknown) {
2
+ if (v === undefined) return true
3
+ return Boolean(v)
4
+ }
@@ -199,9 +199,12 @@ const withMinify = (
199
199
  onlyOncePlugins: OutputPluginOption[] = []
200
200
  ): OutputOptions[] => {
201
201
  const minify = build?.output?.minify ?? MINIFY_DEFAULT_VALUE
202
+
202
203
  output.plugins = output.plugins ?? []
204
+ const notOnlyOncePlugins = [...output.plugins]
205
+ output.plugins.push(...onlyOncePlugins)
206
+
203
207
  if (minify === false) {
204
- output.plugins.push(...onlyOncePlugins)
205
208
  return [output]
206
209
  }
207
210
 
@@ -213,11 +216,10 @@ const withMinify = (
213
216
  ? import('rollup-plugin-swc3').then(({ minify }) => minify(noTypeResolvedMinifyOptions as any))
214
217
  // eslint-disable-next-line ts/no-unsafe-argument
215
218
  : import('@rollup/plugin-terser').then(({ default: minify }) => minify(noTypeResolvedMinifyOptions as any))
216
- const notOnlyOncePlugins = output.plugins
217
219
  return minify === 'only-minify'
218
220
  ? [{
219
221
  ...output,
220
- // TODO replace suffix when pubish to npm and the `build.output.minify` is 'only-minify'
222
+ // TODO replace suffix when publish to npm and the `build.output.minify` is 'only-minify'
221
223
  // TODO resolve dts output file name
222
224
  entryFileNames: chunkInfo =>
223
225
  typeof output.entryFileNames === 'function'
@@ -226,6 +228,7 @@ const withMinify = (
226
228
  throw new Error('entryFileNames must be a function')
227
229
  })(),
228
230
  plugins: [
231
+ ...output.plugins,
229
232
  ...notOnlyOncePlugins,
230
233
  minifyPlugin
231
234
  ]
@@ -356,7 +359,23 @@ const generateConfigs = (context: ConfigGenerateContext, options: TemplateOption
356
359
  const rollupOptions: RollupOptions[] = []
357
360
 
358
361
  const commonPlugins: Plugin[] = [
359
- nodeResolve({ exportConditions })
362
+ nodeResolve({
363
+ exportConditions,
364
+ extensions: [
365
+ '.js',
366
+ '.cjs',
367
+ '.mjs',
368
+ '.jsx',
369
+ '.cjsx',
370
+ '.mjsx',
371
+ '.ts',
372
+ '.cts',
373
+ '.mts',
374
+ '.tsx',
375
+ '.ctsx',
376
+ '.mtsx'
377
+ ]
378
+ })
360
379
  ]
361
380
  if (jsOutput && !WITHOUT_JS) {
362
381
  const sourcemap = typeof options?.output?.sourcemap === 'object'
package/src/server.ts CHANGED
@@ -5,7 +5,15 @@ export const createServer = (port: number, host: string) => {
5
5
  app.listen(port, host)
6
6
  const streams = new Map<string, string>()
7
7
  app.use(async (ctx) => {
8
- const stream = streams.get(ctx.path)
8
+ let stream = streams.get(ctx.path)
9
+ if (stream == null) {
10
+ const maybeKey = streams
11
+ .keys()
12
+ .find(p => ctx.path.startsWith(p))
13
+ stream = maybeKey != null
14
+ ? streams.get(maybeKey)
15
+ : undefined
16
+ }
9
17
  if (stream != null) {
10
18
  ctx.body = stream
11
19
  }
@@ -0,0 +1,22 @@
1
+ import process from 'node:process'
2
+
3
+ import { confirm } from '@inquirer/prompts'
4
+ import { execaCommand } from 'execa'
5
+
6
+ import { getWD } from '#~/utils/getWD.ts'
7
+
8
+ export async function checkDependency(dependency: string) {
9
+ try {
10
+ require.resolve(dependency)
11
+ } catch {
12
+ console.error(`The package '${dependency}' is not installed, please install it first.`)
13
+ const { notWorkspace } = getWD()
14
+ const command = `pnpm install -${notWorkspace ? '' : 'w'}D ${dependency}`
15
+ if (await confirm({ message: 'Do you want to install it now?' })) {
16
+ await execaCommand(command)
17
+ } else {
18
+ console.warn(`You can run the command '${command}' to install it manually.`)
19
+ process.exit(1)
20
+ }
21
+ }
22
+ }