jiek 2.2.1 → 2.2.3-alpha.1

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,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': {
@@ -586,14 +471,16 @@ command
586
471
  }
587
472
  })
588
473
  await new Promise<void>((resolve, reject) => {
589
- let errorStr = ''
474
+ let errorStr = `rollup build failed\n`
475
+ + `package name: ${manifest.name}\n`
476
+ + `cwd: ${pkgCWD}\n\n`
590
477
  child.stderr?.on('data', (data) => {
591
478
  errorStr += data
592
479
  })
593
480
  child.once('exit', (code) =>
594
481
  code === 0
595
482
  ? resolve()
596
- : reject(new Error(`rollup build failed:\n${errorStr}`)))
483
+ : reject(new Error(errorStr)))
597
484
  verbose && child.stdout?.pipe(process.stdout)
598
485
  })
599
486
  })
@@ -624,20 +511,7 @@ command
624
511
  }
625
512
  } finally {
626
513
  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
514
  // eslint-disable-next-line no-console
641
- !silent && console.log(message)
515
+ !silent && console.log('Build complete')
642
516
  }
643
517
  })
@@ -0,0 +1,4 @@
1
+ export function parseBoolean(v?: unknown) {
2
+ if (v === undefined) return true
3
+ return Boolean(v)
4
+ }
@@ -49,6 +49,17 @@ export interface TemplateOptions {
49
49
  | ({
50
50
  type: 'swc'
51
51
  } & import('rollup-plugin-swc3').PluginOptions)
52
+ features?: {
53
+ /**
54
+ * When use esbuild type builder, it will inject `supported.import-attributes` option.
55
+ * When use swc type builder, it will inject `jsc.experimental.keepImportAttributes` option.
56
+ *
57
+ * And it will auto set the rollup output externalImportAttributes and importAttributesKey options.
58
+ *
59
+ * @default true
60
+ */
61
+ keepImportAttributes?: boolean | 'assert'
62
+ }
52
63
  output?: {
53
64
  /**
54
65
  * @default true
@@ -219,7 +219,7 @@ const withMinify = (
219
219
  return minify === 'only-minify'
220
220
  ? [{
221
221
  ...output,
222
- // 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'
223
223
  // TODO resolve dts output file name
224
224
  entryFileNames: chunkInfo =>
225
225
  typeof output.entryFileNames === 'function'
@@ -359,18 +359,51 @@ const generateConfigs = (context: ConfigGenerateContext, options: TemplateOption
359
359
  const rollupOptions: RollupOptions[] = []
360
360
 
361
361
  const commonPlugins: Plugin[] = [
362
- 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
+ })
363
379
  ]
364
380
  if (jsOutput && !WITHOUT_JS) {
365
381
  const sourcemap = typeof options?.output?.sourcemap === 'object'
366
382
  ? options.output.sourcemap.js
367
383
  : options?.output?.sourcemap
384
+ const features = Object.assign({
385
+ keepImportAttributes: true
386
+ }, build.features)
368
387
  const builder = resolvedBuilderOptions.type === 'esbuild'
369
388
  ? import('rollup-plugin-esbuild').then(({ default: esbuild }) =>
370
389
  esbuild({
371
390
  sourceMap: sourcemap === 'hidden' ? false : !!sourcemap,
372
391
  tsconfig: buildTSConfigPath,
373
- ...noTypeResolvedBuilderOptions
392
+ loaders: {
393
+ cts: 'ts',
394
+ ctsx: 'tsx',
395
+ mts: 'ts',
396
+ mtsx: 'tsx',
397
+ cjs: 'js',
398
+ cjsx: 'jsx',
399
+ mjs: 'js',
400
+ mjsx: 'jsx'
401
+ },
402
+ ...noTypeResolvedBuilderOptions,
403
+ supported: {
404
+ 'import-attributes': features.keepImportAttributes !== false,
405
+ ...resolvedBuilderOptions.supported
406
+ }
374
407
  })
375
408
  )
376
409
  : import('rollup-plugin-swc3').then(({ default: swc }) =>
@@ -384,7 +417,14 @@ const generateConfigs = (context: ConfigGenerateContext, options: TemplateOption
384
417
  inline: 'inline'
385
418
  } as const)[sourcemap] ?? undefined,
386
419
  tsconfig: buildTSConfigPath,
387
- ...noTypeResolvedBuilderOptions
420
+ ...noTypeResolvedBuilderOptions,
421
+ jsc: {
422
+ ...resolvedBuilderOptions.jsc,
423
+ experimental: {
424
+ ...resolvedBuilderOptions.jsc?.experimental,
425
+ keepImportAttributes: features.keepImportAttributes !== false
426
+ }
427
+ }
388
428
  })
389
429
  )
390
430
  const [ana, anaOutputPlugin] = bundleAnalyzer(modules => void publishInEntry('modulesAnalyze', { modules }))
@@ -411,6 +451,15 @@ const generateConfigs = (context: ConfigGenerateContext, options: TemplateOption
411
451
  strict: typeof options?.output?.strict === 'object'
412
452
  ? options.output.strict.js
413
453
  : options?.output?.strict,
454
+ externalImportAttributes: features.keepImportAttributes !== false,
455
+ importAttributesKey: (
456
+ features.keepImportAttributes === false
457
+ || features.keepImportAttributes === undefined
458
+ )
459
+ ? undefined
460
+ : features.keepImportAttributes === true
461
+ ? 'with'
462
+ : features.keepImportAttributes,
414
463
  plugins: [
415
464
  isFormatEsm(format === 'esm')
416
465
  ]
@@ -529,6 +578,7 @@ export function template(packageJSON: PackageJSON): RollupOptions[] {
529
578
  return false
530
579
  })
531
580
 
581
+ console.log(exports)
532
582
  const configs: RollupOptions[] = []
533
583
  leafMap.forEach((keysArr, input) =>
534
584
  keysArr.forEach((keys) => {
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 { spawnSync } from 'node:child_process'
2
+ import process from 'node:process'
3
+
4
+ import { confirm } from '@inquirer/prompts'
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
+ spawnSync(command)
17
+ } else {
18
+ console.warn(`You can run the command '${command}' to install it manually.`)
19
+ process.exit(1)
20
+ }
21
+ }
22
+ }
@@ -1,11 +1,11 @@
1
1
  import { isAbsolute, relative, resolve } from 'node:path'
2
2
 
3
3
  import {
4
+ type Entrypoints2ExportsOptions,
5
+ type RecursiveRecord,
4
6
  DEFAULT_SKIP_VALUES,
5
7
  entrypoints2Exports,
6
- type Entrypoints2ExportsOptions,
7
8
  filterLeafs,
8
- type RecursiveRecord,
9
9
  resolveEntrypoints
10
10
  } from '@jiek/pkger/entrypoints'
11
11
  import type { Config } from 'jiek'
@@ -109,20 +109,30 @@ export function getExports({
109
109
  )
110
110
  const crossModuleWithConditional: Entrypoints2ExportsOptions['withConditional'] = crossModuleConvertor
111
111
  ? {
112
- import: opts =>
113
- !pkgIsModule && intersection(
114
- new Set(opts.conditionals),
115
- new Set(['import', 'module'])
116
- ).size === 0
117
- ? opts.dist.replace(/\.js$/, '.mjs')
118
- : false,
119
- require: opts =>
120
- pkgIsModule && intersection(
121
- new Set(opts.conditionals),
122
- new Set(['require', 'node'])
123
- ).size === 0
124
- ? opts.dist.replace(/\.js$/, '.cjs')
125
- : false
112
+ import: opts => {
113
+ if (pkgIsModule) return false
114
+ if (opts.src.endsWith('.cts')) return false
115
+ if (
116
+ intersection(
117
+ new Set(opts.conditionals),
118
+ new Set(['import', 'module'])
119
+ ).size !== 0
120
+ ) return false
121
+
122
+ return opts.dist.replace(/\.js$/, '.mjs')
123
+ },
124
+ require: opts => {
125
+ if (!pkgIsModule) return false
126
+ if (opts.src.endsWith('.mts')) return false
127
+ if (
128
+ intersection(
129
+ new Set(opts.conditionals),
130
+ new Set(['require', 'node'])
131
+ ).size !== 0
132
+ ) return false
133
+
134
+ return opts.dist.replace(/\.js$/, '.cjs')
135
+ }
126
136
  }
127
137
  : {}
128
138
  return [
package/src/utils/ts.ts CHANGED
@@ -4,7 +4,7 @@ import { dirname, resolve } from 'node:path'
4
4
  import { parse } from 'jsonc-parser'
5
5
  import { isMatch } from 'micromatch'
6
6
 
7
- type TSConfig = {
7
+ interface TSConfig {
8
8
  extends?: string | string[]
9
9
  compilerOptions?: Record<string, unknown>
10
10
  references?: { path: string }[]
@@ -16,7 +16,7 @@ type TSConfig = {
16
16
  const getTSConfig = (p: string): TSConfig =>
17
17
  !fs.existsSync(p) || !fs.statSync(p).isFile()
18
18
  ? {}
19
- : parse(fs.readFileSync(p, 'utf-8'), [], { allowTrailingComma: true, allowEmptyContent: true })
19
+ : parse(fs.readFileSync(p, 'utf-8'), [], { allowTrailingComma: true, allowEmptyContent: true }) as TSConfig
20
20
 
21
21
  const getExtendTSConfig = (tsconfigPath: string): TSConfig => {
22
22
  tsconfigPath = resolve(tsconfigPath)
@@ -25,7 +25,7 @@ const getExtendTSConfig = (tsconfigPath: string): TSConfig => {
25
25
  const resolvePaths = (paths: string[] | undefined) => paths?.map(p => resolve(tsconfigPathDirname, p)) ?? []
26
26
 
27
27
  const extendsPaths = resolvePaths(
28
- exts ? Array.isArray(exts) ? exts : [exts] : []
28
+ exts !== undefined ? Array.isArray(exts) ? exts : [exts] : []
29
29
  )
30
30
  if (extendsPaths.length === 0) return tsconfig
31
31
  return extendsPaths