jiek 2.0.2-alpha.13 → 2.0.2-alpha.15

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,565 @@
1
+ import fs from 'node:fs'
2
+ import { dirname, extname, relative, resolve } from 'node:path'
3
+
4
+ import type { RecursiveRecord } from '@jiek/pkger/entrypoints'
5
+ import { getAllLeafs } from '@jiek/pkger/entrypoints'
6
+ import { dts } from '@jiek/rollup-plugin-dts'
7
+ import { getWorkspaceDir } from '@jiek/utils/getWorkspaceDir'
8
+ import commonjs from '@rollup/plugin-commonjs'
9
+ import json from '@rollup/plugin-json'
10
+ import { nodeResolve } from '@rollup/plugin-node-resolve'
11
+ import { sendMessage } from 'execa'
12
+ import { isMatch } from 'micromatch'
13
+ import type { InputPluginOption, OutputOptions, OutputPlugin, Plugin, RollupOptions } from 'rollup'
14
+ import ts from 'typescript'
15
+
16
+ import { recusiveListFiles } from '#~/utils/recusiveListFiles.ts'
17
+
18
+ import { getExports, getOutDirs } from '../utils/getExports'
19
+ import { loadConfig } from '../utils/loadConfig'
20
+ import { getCompilerOptionsByFilePath } from '../utils/ts'
21
+ import type { ConfigGenerateContext, RollupProgressEvent, TemplateOptions } from './base'
22
+ import progress from './plugins/progress'
23
+ import skip from './plugins/skip'
24
+ import externalResolver from './utils/externalResolver'
25
+
26
+ interface PackageJSON {
27
+ name?: string
28
+ type?: string
29
+ exports?: Record<string, unknown> | string | string[]
30
+ }
31
+
32
+ const {
33
+ JIEK_ROOT,
34
+ JIEK_NAME,
35
+ JIEK_BUILDER,
36
+ JIEK_ENTRIES,
37
+ JIEK_EXTERNAL,
38
+ JIEK_WITHOUT_JS,
39
+ JIEK_WITHOUT_DTS,
40
+ JIEK_WITHOUT_MINIFY,
41
+ JIEK_MINIFY_TYPE,
42
+ JIEK_CLEAN,
43
+ JIEK_ONLY_MINIFY,
44
+ JIEK_TSCONFIG,
45
+ JIEK_DTSCONFIG
46
+ } = process.env
47
+
48
+ const resolveArrayString = (str: string | undefined) => {
49
+ const arr = [
50
+ ...new Set(
51
+ str
52
+ ?.split(',')
53
+ .map(e => e.trim())
54
+ .filter(e => e.length > 0)
55
+ ?? []
56
+ )
57
+ ]
58
+ return arr?.length ? arr : undefined
59
+ }
60
+
61
+ const entries = resolveArrayString(JIEK_ENTRIES)?.map(e => ({ 'index': '.' }[e] ?? e))
62
+
63
+ const commandExternal = resolveArrayString(JIEK_EXTERNAL)?.map(e => new RegExp(`^${e}$`))
64
+
65
+ const WORKSPACE_ROOT = JIEK_ROOT ?? getWorkspaceDir()
66
+ const COMMON_OPTIONS = {} satisfies RollupOptions
67
+ const COMMON_PLUGINS = [
68
+ json()
69
+ ]
70
+
71
+ const WITHOUT_JS = JIEK_WITHOUT_JS === 'true'
72
+ const WITHOUT_DTS = JIEK_WITHOUT_DTS === 'true'
73
+ const WITHOUT_MINIFY = JIEK_WITHOUT_MINIFY === 'true'
74
+
75
+ const ONLY_MINIFY = JIEK_ONLY_MINIFY === 'true'
76
+
77
+ const CLEAN = JIEK_CLEAN === 'true'
78
+
79
+ const MINIFY_DEFAULT_VALUE = WITHOUT_MINIFY
80
+ ? false
81
+ : ONLY_MINIFY
82
+ ? 'only-minify'
83
+ : true
84
+
85
+ type BuilderOptions = NonNullable<TemplateOptions['builder']>
86
+
87
+ const BUILDER_OPTIONS = {
88
+ type: JIEK_BUILDER ?? 'esbuild'
89
+ } as NonNullable<Exclude<BuilderOptions, string>>
90
+
91
+ type MinifyOptions = NonNullable<TemplateOptions['output']>['minifyOptions']
92
+
93
+ const MINIFY_OPTIONS = {
94
+ type: JIEK_MINIFY_TYPE ?? 'esbuild'
95
+ } as NonNullable<Exclude<MinifyOptions, string>>
96
+
97
+ const config = loadConfig({
98
+ root: WORKSPACE_ROOT
99
+ }) ?? {}
100
+ const { build = {} } = config
101
+ const { js: jsOutdir, dts: dtsOutdir } = getOutDirs({
102
+ config,
103
+ pkgName: JIEK_NAME
104
+ })
105
+
106
+ if (CLEAN) {
107
+ fs.existsSync(jsOutdir) && fs.rmdirSync(jsOutdir, { recursive: true })
108
+ fs.existsSync(dtsOutdir) && fs.rmdirSync(dtsOutdir, { recursive: true })
109
+ }
110
+
111
+ const STYLE_REGEXP = /\.(css|s[ac]ss|less|styl)$/
112
+
113
+ const resolveBuildPlugins = (context: ConfigGenerateContext, plugins: TemplateOptions['plugins']): {
114
+ js: InputPluginOption
115
+ dts: InputPluginOption
116
+ } => {
117
+ if (plugins === false || plugins === undefined || plugins === null) {
118
+ return { js: [], dts: [] }
119
+ }
120
+ let js: InputPluginOption = []
121
+ let dts: InputPluginOption = []
122
+ switch (typeof plugins) {
123
+ case 'function':
124
+ js = plugins('js', context)
125
+ dts = plugins('dts', context)
126
+ break
127
+ case 'object':
128
+ if ('js' in plugins || 'dts' in plugins) {
129
+ js = plugins.js ?? []
130
+ dts = plugins.dts ?? []
131
+ } else {
132
+ js = plugins
133
+ dts = plugins
134
+ }
135
+ break
136
+ }
137
+ return { js, dts }
138
+ }
139
+
140
+ const resolveOutputControls = (
141
+ context: ConfigGenerateContext,
142
+ output: TemplateOptions['output']
143
+ ): { js: boolean; dts: boolean } => ({
144
+ js: typeof output?.js === 'boolean'
145
+ ? output.js
146
+ : typeof output?.js === 'function'
147
+ ? output.js(context)
148
+ : true,
149
+ dts: typeof output?.dts === 'boolean'
150
+ ? output.dts
151
+ : typeof output?.dts === 'function'
152
+ ? output.dts(context)
153
+ : true
154
+ })
155
+
156
+ const resolveWorkspacePath = (p: string) => resolve(WORKSPACE_ROOT, p)
157
+
158
+ const pascalCase = (str: string) =>
159
+ str
160
+ .replace(/[@|/-](\w)/g, (_, $1) => $1.toUpperCase())
161
+ .replace(/(?:^|-)(\w)/g, (_, $1) => $1.toUpperCase())
162
+
163
+ const reveal = (obj: string | Record<string, unknown>, keys: string[]) =>
164
+ keys.reduce((acc, key) => {
165
+ if (typeof acc === 'string') throw new Error('key not found in exports')
166
+ if (!(key in acc)) throw new Error(`key ${key} not found in exports`)
167
+ return acc[key] as string | Record<string, unknown>
168
+ }, obj)
169
+
170
+ const resolveMinifyOptions = (minifyOptions: MinifyOptions): typeof MINIFY_OPTIONS =>
171
+ typeof minifyOptions === 'string'
172
+ ? { type: minifyOptions }
173
+ : minifyOptions ?? { type: 'esbuild' }
174
+
175
+ const resolveBuilderOptions = (
176
+ builder: TemplateOptions['builder']
177
+ ): Exclude<TemplateOptions['builder'], string | undefined> =>
178
+ typeof builder === 'string'
179
+ ? { type: builder }
180
+ : builder ?? { type: 'esbuild' }
181
+
182
+ const resolvedMinifyOptions = resolveMinifyOptions(build.output?.minifyOptions ?? MINIFY_OPTIONS)
183
+ const { type: _resolvedMinifyOptionsType, ...noTypeResolvedMinifyOptions } = resolvedMinifyOptions
184
+ const resolvedBuilderOptions = resolveBuilderOptions(build.builder ?? BUILDER_OPTIONS)
185
+ const { type: _resolvedBuilderOptionsType, ...noTypeResolvedBuilderOptions } = resolvedBuilderOptions
186
+
187
+ const withMinify = (
188
+ output: OutputOptions & {
189
+ plugins?: OutputPlugin[]
190
+ },
191
+ minify = build?.output?.minify ?? MINIFY_DEFAULT_VALUE
192
+ ): OutputOptions[] => {
193
+ if (minify === false) return [output]
194
+
195
+ const minifyPlugin = resolvedMinifyOptions.type === 'esbuild'
196
+ ? import('rollup-plugin-esbuild').then(({ minify }) => minify(noTypeResolvedMinifyOptions as any))
197
+ : resolvedMinifyOptions.type === 'swc'
198
+ ? import('rollup-plugin-swc3').then(({ minify }) => minify(noTypeResolvedMinifyOptions as any))
199
+ : import('@rollup/plugin-terser').then(({ default: minify }) => minify(noTypeResolvedMinifyOptions as any))
200
+ return minify === 'only-minify'
201
+ ? [{
202
+ ...output,
203
+ // TODO replace suffix when pubish to npm and the `build.output.minify` is 'only-minify'
204
+ // TODO resolve dts output file name
205
+ entryFileNames: chunkInfo =>
206
+ typeof output.entryFileNames === 'function'
207
+ ? output.entryFileNames(chunkInfo)
208
+ : (() => {
209
+ throw new Error('entryFileNames must be a function')
210
+ })(),
211
+ plugins: [
212
+ ...(output.plugins ?? []),
213
+ minifyPlugin
214
+ ]
215
+ }]
216
+ : [
217
+ output,
218
+ {
219
+ ...output,
220
+ entryFileNames: chunkInfo =>
221
+ typeof output.entryFileNames === 'function'
222
+ ? output.entryFileNames(chunkInfo).replace(/(\.[cm]?js)$/, '.min$1')
223
+ : (() => {
224
+ throw new Error('entryFileNames must be a function')
225
+ })(),
226
+ file: output.file?.replace(/(\.[cm]?js)$/, '.min$1'),
227
+ plugins: [
228
+ ...(output.plugins ?? []),
229
+ minifyPlugin
230
+ ]
231
+ }
232
+ ]
233
+ }
234
+
235
+ const generateConfigs = (context: ConfigGenerateContext, options: TemplateOptions = {}): RollupOptions[] => {
236
+ const {
237
+ path,
238
+ name,
239
+ input,
240
+ output,
241
+ external: inputExternal,
242
+ pkgIsModule,
243
+ conditionals
244
+ } = context
245
+ const external = [...inputExternal, ...(options.external ?? []), ...(commandExternal ?? [])]
246
+ const isModule = conditionals.includes('import')
247
+ const isCommonJS = conditionals.includes('require')
248
+ const isBrowser = conditionals.includes('browser')
249
+ const dtsTSConfigPaths = [
250
+ resolveWorkspacePath('tsconfig.json'),
251
+ resolveWorkspacePath('tsconfig.dts.json')
252
+ ]
253
+ JIEK_TSCONFIG && dtsTSConfigPaths.push(resolveWorkspacePath(JIEK_TSCONFIG))
254
+ JIEK_DTSCONFIG && dtsTSConfigPaths.push(resolveWorkspacePath(JIEK_DTSCONFIG))
255
+ const buildTSConfigPaths = [
256
+ ...dtsTSConfigPaths,
257
+ resolveWorkspacePath('tsconfig.build.json')
258
+ ]
259
+ // 这里重复写了俩次 JIEK_TSCONFIG 到 tsconfig 的加载列表中
260
+ // 目的是保证在 build 的时候,JIEK_TSCONFIG 的优先级高于 JIEK_DTSCONFIG
261
+ JIEK_TSCONFIG && buildTSConfigPaths.push(resolveWorkspacePath(JIEK_TSCONFIG))
262
+ let dtsTSConfigPath: string | undefined
263
+ dtsTSConfigPaths.forEach(p => {
264
+ if (fs.existsSync(p) && fs.statSync(p).isFile()) {
265
+ dtsTSConfigPath = p
266
+ }
267
+ })
268
+ let buildTSConfigPath: string | undefined
269
+ buildTSConfigPaths.forEach(p => {
270
+ if (fs.existsSync(p) && fs.statSync(p).isFile()) {
271
+ buildTSConfigPath = p
272
+ }
273
+ })
274
+ let compilerOptions: ts.CompilerOptions = {}
275
+ if (dtsTSConfigPath) {
276
+ const jsonCompilerOptions = getCompilerOptionsByFilePath(dtsTSConfigPath, resolve(input))
277
+ const { options, errors } = ts.convertCompilerOptionsFromJson(
278
+ jsonCompilerOptions,
279
+ dirname(dtsTSConfigPath)
280
+ )
281
+ if (errors.length > 0) {
282
+ throw new Error(errors.map(e => e.messageText).join('\n'))
283
+ }
284
+ compilerOptions = options
285
+ delete compilerOptions.composite
286
+ }
287
+ const exportConditions = [...conditionals, ...(compilerOptions.customConditions ?? [])]
288
+ const throughEventProps: RollupProgressEvent & { type: 'progress' } = {
289
+ type: 'progress',
290
+ data: { name, path, exportConditions, input }
291
+ }
292
+ const { js: jsPlugins, dts: dtsPlugins } = resolveBuildPlugins(context, build.plugins)
293
+ if (input.includes('**')) {
294
+ throw new Error(
295
+ 'input should not include "**", please read the [documentation](https://nodejs.org/api/packages.html#subpath-patterns).'
296
+ )
297
+ }
298
+ const inputObj = !input.includes('*')
299
+ ? input
300
+ : recusiveListFiles(process.cwd())
301
+ .filter(p => /(?<!\.d)\.[cm]?tsx?$/.test(p))
302
+ .map(p => relative(process.cwd(), p))
303
+ .filter(p => isMatch(p, input.slice(2)))
304
+ const globCommonDir = input.includes('*')
305
+ ? input.split('*')[0].replace('./', '')
306
+ : ''
307
+ const pathCommonDir = path.includes('*')
308
+ ? path.split('*')[0].replace('./', '')
309
+ : ''
310
+ if (
311
+ (globCommonDir.length > 0 && pathCommonDir.length === 0)
312
+ || (globCommonDir.length === 0 && pathCommonDir.length > 0)
313
+ ) {
314
+ throw new Error('input and path should both include "*" or not include "*"')
315
+ }
316
+ const jsOutputSuffix = extname(output)
317
+ const tsOutputSuffix = jsOutputSuffix.replace(/(\.[cm]?)js$/, '.d$1ts')
318
+ const { js: jsOutput, dts: dtsOutput } = resolveOutputControls(context, build.output)
319
+ const rollupOptions: RollupOptions[] = []
320
+
321
+ const commonPlugins: Plugin[] = []
322
+ if (jsOutput && !WITHOUT_JS) {
323
+ const sourcemap = typeof options?.output?.sourcemap === 'object'
324
+ ? options.output.sourcemap.js
325
+ : options?.output?.sourcemap
326
+ const builder = resolvedBuilderOptions.type === 'esbuild'
327
+ ? import('rollup-plugin-esbuild').then(({ default: esbuild }) =>
328
+ esbuild({
329
+ sourceMap: sourcemap === 'hidden' ? false : !!sourcemap,
330
+ tsconfig: buildTSConfigPath,
331
+ ...noTypeResolvedBuilderOptions
332
+ })
333
+ )
334
+ : import('rollup-plugin-swc3').then(({ default: swc }) =>
335
+ swc({
336
+ sourceMaps: typeof sourcemap === 'boolean'
337
+ ? sourcemap
338
+ : typeof sourcemap === 'undefined'
339
+ ? undefined
340
+ : ({
341
+ hidden: false,
342
+ inline: 'inline'
343
+ } as const)[sourcemap] ?? undefined,
344
+ tsconfig: buildTSConfigPath,
345
+ ...noTypeResolvedBuilderOptions
346
+ })
347
+ )
348
+ rollupOptions.push({
349
+ input: inputObj,
350
+ external,
351
+ output: [
352
+ ...withMinify({
353
+ dir: jsOutdir,
354
+ name,
355
+ interop: 'auto',
356
+ entryFileNames: (chunkInfo) => (
357
+ Array.isArray(inputObj)
358
+ ? chunkInfo.facadeModuleId!.replace(`${process.cwd()}/`, '')
359
+ .replace(globCommonDir, pathCommonDir)
360
+ .replace(/(\.[cm]?)ts$/, jsOutputSuffix)
361
+ : output.replace(`${jsOutdir}/`, '')
362
+ ),
363
+ sourcemap,
364
+ format: isModule ? 'esm' : (
365
+ isCommonJS ? 'cjs' : (
366
+ isBrowser ? 'umd' : (
367
+ pkgIsModule ? 'esm' : 'cjs'
368
+ )
369
+ )
370
+ ),
371
+ strict: typeof options?.output?.strict === 'object'
372
+ ? options.output.strict.js
373
+ : options?.output?.strict
374
+ })
375
+ ],
376
+ plugins: [
377
+ ...commonPlugins,
378
+ nodeResolve({ exportConditions }),
379
+ import('rollup-plugin-postcss')
380
+ .then(({ default: postcss }) =>
381
+ postcss({
382
+ extract: resolve(output.replace(/\.[cm]?js$/, '.css')),
383
+ minimize: true
384
+ })
385
+ )
386
+ .catch(() => void 0),
387
+ builder,
388
+ commonjs(),
389
+ progress({
390
+ onEvent: (event, message) =>
391
+ sendMessage(
392
+ {
393
+ ...throughEventProps,
394
+ data: { ...throughEventProps.data, event, message, tags: ['js'] }
395
+ } satisfies RollupProgressEvent
396
+ )
397
+ }),
398
+ jsPlugins
399
+ ]
400
+ })
401
+ }
402
+
403
+ if (dtsOutput && !WITHOUT_DTS) {
404
+ rollupOptions.push({
405
+ input: inputObj,
406
+ external,
407
+ output: [
408
+ {
409
+ dir: dtsOutdir,
410
+ sourcemap: typeof options?.output?.sourcemap === 'object'
411
+ ? options.output.sourcemap.dts
412
+ : options?.output?.sourcemap,
413
+ entryFileNames: (chunkInfo) => (
414
+ Array.isArray(inputObj)
415
+ ? chunkInfo.facadeModuleId!.replace(`${process.cwd()}/`, '')
416
+ .replace(globCommonDir, pathCommonDir)
417
+ .replace(/(\.[cm]?)ts$/, tsOutputSuffix)
418
+ : output
419
+ .replace(`${jsOutdir}/`, '')
420
+ .replace(/(\.[cm]?)js$/, tsOutputSuffix)
421
+ ),
422
+ strict: typeof options?.output?.strict === 'object'
423
+ ? options.output.strict.dts
424
+ : options?.output?.strict
425
+ }
426
+ ],
427
+ plugins: [
428
+ ...commonPlugins,
429
+ nodeResolve({ exportConditions }),
430
+ skip({ patterns: [STYLE_REGEXP] }),
431
+ dts({
432
+ respectExternal: true,
433
+ compilerOptions: {
434
+ // temp directory, it not affect the output
435
+ // but if the user not set it and `declaration`, inputs can't generate any dts files when the input relative imports of `package.json`
436
+ outDir: 'dist',
437
+ declaration: true,
438
+ // https://github.com/Swatinem/rollup-plugin-dts/issues/143
439
+ preserveSymlinks: false,
440
+ // Expected '{', got 'type' (Note that you need plugins to import files that are not JavaScript)
441
+ // https://github.com/Swatinem/rollup-plugin-dts/issues/96
442
+ noEmit: false
443
+ },
444
+ tsconfig: dtsTSConfigPath
445
+ }),
446
+ progress({
447
+ onEvent: (event, message) =>
448
+ sendMessage(
449
+ {
450
+ ...throughEventProps,
451
+ data: { ...throughEventProps.data, event, message, tags: ['dts'] }
452
+ } satisfies RollupProgressEvent
453
+ )
454
+ }),
455
+ dtsPlugins
456
+ ]
457
+ })
458
+ }
459
+ if (rollupOptions.length > 0) {
460
+ // only push the first one a watcher plugin
461
+ rollupOptions[0].plugins = [
462
+ {
463
+ name: 'jiek-plugin-watcher',
464
+ watchChange: (id) =>
465
+ sendMessage(
466
+ {
467
+ type: 'watchChange',
468
+ data: { id, name: JIEK_NAME!, path, input }
469
+ } satisfies RollupProgressEvent
470
+ )
471
+ },
472
+ ...(rollupOptions[0].plugins as any)
473
+ ]
474
+ }
475
+ return rollupOptions
476
+ }
477
+
478
+ export function template(packageJSON: PackageJSON): RollupOptions[] {
479
+ const { name, type, exports: entrypoints } = packageJSON
480
+ const pkgIsModule = type === 'module'
481
+ if (!name) throw new Error('package.json name is required')
482
+ if (!entrypoints) throw new Error('package.json exports is required')
483
+
484
+ const packageName = pascalCase(name)
485
+
486
+ const external = externalResolver(packageJSON as Record<string, unknown>)
487
+
488
+ const [filteredResolvedEntrypoints, exports] = getExports({
489
+ entrypoints,
490
+ pkgIsModule,
491
+ entries,
492
+ pkgName: JIEK_NAME!,
493
+ outdir: jsOutdir,
494
+ config
495
+ })
496
+
497
+ const leafMap = new Map<string, string[][]>()
498
+ getAllLeafs(filteredResolvedEntrypoints as RecursiveRecord<string>, ({ keys, value }) => {
499
+ if (typeof value === 'string') {
500
+ const keysArr = leafMap.get(value) ?? []
501
+ leafMap.set(value, keysArr)
502
+ keysArr.push(keys)
503
+ }
504
+ return false
505
+ })
506
+
507
+ const configs: RollupOptions[] = []
508
+ leafMap.forEach((keysArr, input) =>
509
+ keysArr.forEach((keys) => {
510
+ const [path, ...conditionals] = keys
511
+
512
+ const name = packageName + (path === '.' ? '' : pascalCase(path))
513
+ const keyExports = reveal(exports, keys)
514
+ const commonOptions = {
515
+ path,
516
+ name,
517
+ input,
518
+ external,
519
+ pkgIsModule
520
+ }
521
+
522
+ switch (typeof keyExports) {
523
+ case 'string': {
524
+ configs.push(...generateConfigs({
525
+ ...commonOptions,
526
+ output: keyExports,
527
+ conditionals
528
+ }, build))
529
+ break
530
+ }
531
+ case 'object': {
532
+ getAllLeafs(keyExports as RecursiveRecord<string>, ({ keys: nextKeys, value }) => {
533
+ const allConditionals = [...new Set([...conditionals, ...nextKeys])]
534
+ if (typeof value === 'string') {
535
+ configs.push(...generateConfigs({
536
+ ...commonOptions,
537
+ output: value,
538
+ conditionals: allConditionals
539
+ }, build))
540
+ }
541
+ return false
542
+ })
543
+ break
544
+ }
545
+ }
546
+ })
547
+ )
548
+ sendMessage(
549
+ {
550
+ type: 'init',
551
+ data: {
552
+ leafMap,
553
+ targetsLength: configs.length
554
+ }
555
+ } satisfies RollupProgressEvent
556
+ )
557
+ return configs.map(c => ({
558
+ ...COMMON_OPTIONS,
559
+ ...c,
560
+ plugins: [
561
+ ...COMMON_PLUGINS,
562
+ c.plugins
563
+ ]
564
+ }))
565
+ }
@@ -0,0 +1,26 @@
1
+ import type { PluginImpl } from 'rollup'
2
+
3
+ interface Options {
4
+ onEvent?: (event: string, message?: string) => void
5
+ }
6
+
7
+ export default ((options = {}) => {
8
+ const { onEvent } = options
9
+ return {
10
+ name: 'progress',
11
+ buildStart: () => onEvent?.('start', 'Start building...'),
12
+ buildEnd: () => onEvent?.('end', 'Build completed!'),
13
+ resolveId: {
14
+ order: 'post',
15
+ handler: source => onEvent?.('resolve', `Resolving ${source}...`)
16
+ },
17
+ load: {
18
+ order: 'post',
19
+ handler: id => onEvent?.('load', `Loading ${id}...`)
20
+ },
21
+ transform: {
22
+ order: 'post',
23
+ handler: (_, id) => onEvent?.('transform', `Transforming ${id}...`)
24
+ }
25
+ }
26
+ }) as PluginImpl<Options>
@@ -0,0 +1,21 @@
1
+ import type { PluginImpl } from 'rollup'
2
+
3
+ interface Options {
4
+ patterns?: (string | RegExp)[]
5
+ }
6
+
7
+ export default ((options = {}) => ({
8
+ name: 'skip',
9
+ // skip the specified files by `options.patterns`
10
+ load(id) {
11
+ if (
12
+ options.patterns?.some((pattern) =>
13
+ typeof pattern === 'string'
14
+ ? id.includes(pattern)
15
+ : pattern.test(id)
16
+ )
17
+ ) {
18
+ return ''
19
+ }
20
+ }
21
+ })) as PluginImpl<Options>
@@ -0,0 +1,9 @@
1
+ import type { OutputOptions } from 'rollup'
2
+
3
+ const defineOutput = <O extends OutputOptions>(output: O) => output
4
+
5
+ export const commonOutputOptions = defineOutput({
6
+ exports: 'named',
7
+ interop: 'auto',
8
+ sourcemap: true
9
+ })
@@ -0,0 +1,35 @@
1
+ import fs from 'node:fs'
2
+ import { builtinModules } from 'node:module'
3
+
4
+ const EXCLUDE_SUFFIX = [
5
+ 'te?xt',
6
+ 'json',
7
+ '(css|s[ac]ss|less|styl)'
8
+ ]
9
+
10
+ export default function(json: Record<string, unknown>): (string | RegExp)[]
11
+ export default function(path?: string): (string | RegExp)[]
12
+ export default function(jsonOrPath: string | Record<string, unknown> = process.cwd()): (string | RegExp)[] {
13
+ const pkg = typeof jsonOrPath === 'string'
14
+ ? fs.existsSync(`${jsonOrPath}/package.json`)
15
+ ? JSON.parse(fs.readFileSync(`${jsonOrPath}/package.json`, 'utf-8'))
16
+ : {}
17
+ : jsonOrPath
18
+ const { name, dependencies = {}, peerDependencies = {}, optionalDependencies = {} } = pkg
19
+ if (!name) {
20
+ throw new Error('package.json must have a name field')
21
+ }
22
+
23
+ const external = <(string | RegExp)[]> Object
24
+ .keys(dependencies)
25
+ .concat(Object.keys(peerDependencies))
26
+ .concat(Object.keys(optionalDependencies))
27
+ .concat(builtinModules)
28
+
29
+ return [...new Set(external)]
30
+ .map(dep => new RegExp(`^${dep}(/.*)?$`))
31
+ .concat([
32
+ new RegExp(`^${name}(/.*)?(?<!${EXCLUDE_SUFFIX.map(suffix => `\\.${suffix}`).join('|')})$`),
33
+ /^node:/
34
+ ])
35
+ }
@@ -0,0 +1,13 @@
1
+ export default function (external: string) {
2
+ // a/b => AB
3
+ // a-b => AB
4
+ // a@b => AB
5
+ // a@b/c => ABC
6
+ // node:a => a
7
+ // node:a_b => a_b
8
+ if (external.startsWith('node:')) {
9
+ return external.slice(5)
10
+ }
11
+ return external
12
+ .replace(/[@|/-](\w)/g, (_, $1) => $1.toUpperCase())
13
+ }
@@ -0,0 +1,18 @@
1
+ import terser from '@rollup/plugin-terser'
2
+ import type { OutputOptions, OutputPlugin } from 'rollup'
3
+
4
+ export default function(
5
+ output: OutputOptions & {
6
+ entryFileNames?: string
7
+ plugins?: OutputPlugin[]
8
+ }
9
+ ): OutputOptions[] {
10
+ return [
11
+ output,
12
+ {
13
+ ...output,
14
+ entryFileNames: output.entryFileNames?.replace(/(\.[cm]?js)$/, '.min$1'),
15
+ plugins: [...(output.plugins ?? []), terser()]
16
+ }
17
+ ]
18
+ }