@vitus-labs/tools-rolldown 1.15.5 → 2.1.0

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,336 +0,0 @@
1
- import { beforeEach, describe, expect, it, vi } from 'vitest'
2
-
3
- const {
4
- mockRolldown,
5
- mockBundleWrite,
6
- mockBundleClose,
7
- mockReaddirSync,
8
- mockCreateBuildPipeline,
9
- mockRolldownConfig,
10
- mockBuildAllDts,
11
- } = vi.hoisted(() => ({
12
- mockRolldown: vi.fn(),
13
- mockBundleWrite: vi.fn(),
14
- mockBundleClose: vi.fn(),
15
- mockReaddirSync: vi.fn(),
16
- mockCreateBuildPipeline: vi.fn(),
17
- mockRolldownConfig: vi.fn(),
18
- mockBuildAllDts: vi.fn(),
19
- }))
20
-
21
- vi.mock('rolldown', () => ({ rolldown: mockRolldown }))
22
- vi.mock('rimraf', () => ({ rimraf: { sync: vi.fn() } }))
23
- vi.mock('node:fs', () => ({
24
- readdirSync: mockReaddirSync,
25
- renameSync: vi.fn(),
26
- statSync: vi.fn(),
27
- unlinkSync: vi.fn(),
28
- cpSync: vi.fn(),
29
- mkdirSync: vi.fn(),
30
- }))
31
-
32
- vi.mock('../config/index.js', () => ({
33
- CONFIG: { outputDir: 'lib' },
34
- PKG: { name: '@test/pkg', version: '1.0.0' },
35
- }))
36
-
37
- vi.mock('../rolldown/index.js', () => ({
38
- createBuildPipeline: mockCreateBuildPipeline,
39
- config: mockRolldownConfig,
40
- buildAllDts: mockBuildAllDts,
41
- }))
42
-
43
- describe('build', () => {
44
- beforeEach(() => {
45
- vi.clearAllMocks()
46
-
47
- mockBundleWrite.mockResolvedValue(undefined)
48
- mockBundleClose.mockResolvedValue(undefined)
49
- mockRolldown.mockResolvedValue({
50
- write: mockBundleWrite,
51
- close: mockBundleClose,
52
- })
53
- mockReaddirSync.mockReturnValue([])
54
-
55
- mockCreateBuildPipeline.mockReturnValue([
56
- {
57
- file: 'lib/index.js',
58
- format: 'es',
59
- env: 'development',
60
- platform: 'universal',
61
- },
62
- ])
63
- mockRolldownConfig.mockImplementation((item: any) => ({
64
- input: 'src',
65
- output: {
66
- dir: 'lib',
67
- entryFileNames: 'index.js',
68
- format: item.format,
69
- },
70
- }))
71
- mockBuildAllDts.mockReturnValue([])
72
- })
73
-
74
- it('should execute build pipeline successfully', async () => {
75
- vi.resetModules()
76
- const { runBuild } = await import('./build.js')
77
-
78
- await runBuild()
79
-
80
- expect(mockRolldown).toHaveBeenCalled()
81
- expect(mockBundleWrite).toHaveBeenCalled()
82
- expect(mockBundleClose).toHaveBeenCalled()
83
- })
84
-
85
- it('should generate DTS when buildAllDts returns configs', async () => {
86
- mockBuildAllDts.mockReturnValue([
87
- {
88
- file: './lib/index.d.ts',
89
- input: 'src/index.ts',
90
- output: {
91
- dir: 'lib',
92
- entryFileNames: 'index.d.ts',
93
- format: 'es',
94
- },
95
- },
96
- ])
97
-
98
- vi.resetModules()
99
- const { runBuild } = await import('./build.js')
100
- vi.clearAllMocks()
101
-
102
- // Re-setup after clear
103
- mockRolldown.mockResolvedValue({
104
- write: mockBundleWrite,
105
- close: mockBundleClose,
106
- })
107
- mockBundleWrite.mockResolvedValue(undefined)
108
- mockBundleClose.mockResolvedValue(undefined)
109
- mockReaddirSync.mockReturnValue([])
110
- mockRolldownConfig.mockImplementation((item: any) => ({
111
- input: 'src',
112
- output: {
113
- dir: 'lib',
114
- entryFileNames: 'index.js',
115
- format: item.format,
116
- },
117
- }))
118
- mockBuildAllDts.mockReturnValue([
119
- {
120
- file: './lib/index.d.ts',
121
- input: 'src/index.ts',
122
- output: {
123
- dir: 'lib',
124
- entryFileNames: 'index.d.ts',
125
- format: 'es',
126
- },
127
- },
128
- ])
129
- // buildDtsIsolated reads temp dir to find largest .d.ts
130
- mockReaddirSync.mockReturnValue(['index.d.ts'])
131
- const { statSync } = await import('node:fs')
132
- vi.mocked(statSync).mockReturnValue({ size: 100 } as ReturnType<
133
- typeof statSync
134
- >)
135
-
136
- await runBuild()
137
-
138
- // main build + DTS build = 2 rolldown calls
139
- expect(mockRolldown).toHaveBeenCalledTimes(2)
140
- })
141
-
142
- it('should pick largest DTS file when code-split occurs', async () => {
143
- mockBuildAllDts.mockReturnValue([
144
- {
145
- file: './lib/index.d.ts',
146
- input: 'src/index.ts',
147
- output: {
148
- dir: 'lib',
149
- entryFileNames: 'index.d.ts',
150
- format: 'es',
151
- },
152
- },
153
- ])
154
-
155
- vi.resetModules()
156
- const { runBuild } = await import('./build.js')
157
- const { statSync, renameSync, mkdirSync } = await import('node:fs')
158
- vi.clearAllMocks()
159
-
160
- // Re-setup
161
- mockRolldown.mockResolvedValue({
162
- write: mockBundleWrite,
163
- close: mockBundleClose,
164
- })
165
- mockBundleWrite.mockResolvedValue(undefined)
166
- mockBundleClose.mockResolvedValue(undefined)
167
- mockRolldownConfig.mockImplementation((item: any) => ({
168
- input: 'src',
169
- output: {
170
- dir: 'lib',
171
- entryFileNames: 'index.js',
172
- format: item.format,
173
- },
174
- }))
175
- mockBuildAllDts.mockReturnValue([
176
- {
177
- file: './lib/index.d.ts',
178
- input: 'src/index.ts',
179
- output: {
180
- dir: 'lib',
181
- entryFileNames: 'index.d.ts',
182
- format: 'es',
183
- },
184
- },
185
- ])
186
- // Temp dir has entry stub + larger chunk — largest should be picked
187
- mockReaddirSync.mockReturnValue(['index.d.ts', 'index2.d.ts'])
188
- vi.mocked(statSync).mockImplementation((p: any) => {
189
- if (String(p).includes('index2'))
190
- return { size: 1000 } as ReturnType<typeof statSync>
191
- return { size: 10 } as ReturnType<typeof statSync>
192
- })
193
-
194
- await runBuild()
195
-
196
- // buildDtsIsolated creates temp dir, picks largest, moves to final
197
- expect(mkdirSync).toHaveBeenCalled()
198
- expect(renameSync).toHaveBeenCalled()
199
- })
200
-
201
- it('should handle build failure gracefully', async () => {
202
- vi.resetModules()
203
- const { runBuild } = await import('./build.js')
204
- vi.clearAllMocks()
205
-
206
- const buildError = new Error('rolldown failed')
207
- mockRolldown.mockRejectedValue(buildError)
208
- mockReaddirSync.mockReturnValue([])
209
- mockRolldownConfig.mockImplementation((item: any) => ({
210
- input: 'src',
211
- output: {
212
- dir: 'lib',
213
- entryFileNames: 'index.js',
214
- format: item.format,
215
- },
216
- }))
217
- mockBuildAllDts.mockReturnValue([])
218
-
219
- await expect(runBuild()).rejects.toThrow('rolldown failed')
220
- })
221
-
222
- it('should handle multiple builds in sequence', async () => {
223
- mockCreateBuildPipeline.mockReturnValue([
224
- {
225
- file: 'lib/index.js',
226
- format: 'es',
227
- env: 'development',
228
- platform: 'universal',
229
- },
230
- {
231
- file: 'lib/index.cjs',
232
- format: 'cjs',
233
- env: 'development',
234
- platform: 'universal',
235
- },
236
- ])
237
-
238
- vi.resetModules()
239
- const { runBuild } = await import('./build.js')
240
- vi.clearAllMocks()
241
-
242
- mockRolldown.mockResolvedValue({
243
- write: mockBundleWrite,
244
- close: mockBundleClose,
245
- })
246
- mockBundleWrite.mockResolvedValue(undefined)
247
- mockBundleClose.mockResolvedValue(undefined)
248
- mockReaddirSync.mockReturnValue([])
249
- mockRolldownConfig.mockImplementation((item: any) => ({
250
- input: 'src',
251
- output: {
252
- dir: 'lib',
253
- entryFileNames: 'index.js',
254
- format: item.format,
255
- },
256
- }))
257
- mockBuildAllDts.mockReturnValue([])
258
-
259
- await runBuild()
260
-
261
- expect(mockRolldownConfig).toHaveBeenCalledTimes(2)
262
- expect(mockRolldown).toHaveBeenCalledTimes(2)
263
- })
264
-
265
- it('should generate multiple DTS for subpath exports', async () => {
266
- mockBuildAllDts.mockReturnValue([
267
- {
268
- file: './lib/types/index.d.ts',
269
- input: 'src/index.ts',
270
- output: {
271
- dir: 'lib/types',
272
- entryFileNames: 'index.d.ts',
273
- format: 'es',
274
- },
275
- },
276
- {
277
- file: './lib/types/devtools/index.d.ts',
278
- input: 'src/devtools/index.ts',
279
- output: {
280
- dir: 'lib/types/devtools',
281
- entryFileNames: 'index.d.ts',
282
- format: 'es',
283
- },
284
- },
285
- ])
286
-
287
- vi.resetModules()
288
- const { runBuild } = await import('./build.js')
289
- const { statSync } = await import('node:fs')
290
- vi.clearAllMocks()
291
-
292
- mockRolldown.mockResolvedValue({
293
- write: mockBundleWrite,
294
- close: mockBundleClose,
295
- })
296
- mockBundleWrite.mockResolvedValue(undefined)
297
- mockBundleClose.mockResolvedValue(undefined)
298
- mockReaddirSync.mockReturnValue(['index.d.ts'])
299
- vi.mocked(statSync).mockReturnValue({ size: 100 } as ReturnType<
300
- typeof statSync
301
- >)
302
- mockRolldownConfig.mockImplementation((item: any) => ({
303
- input: 'src',
304
- output: {
305
- dir: 'lib',
306
- entryFileNames: 'index.js',
307
- format: item.format,
308
- },
309
- }))
310
- mockBuildAllDts.mockReturnValue([
311
- {
312
- file: './lib/types/index.d.ts',
313
- input: 'src/index.ts',
314
- output: {
315
- dir: 'lib/types',
316
- entryFileNames: 'index.d.ts',
317
- format: 'es',
318
- },
319
- },
320
- {
321
- file: './lib/types/devtools/index.d.ts',
322
- input: 'src/devtools/index.ts',
323
- output: {
324
- dir: 'lib/types/devtools',
325
- entryFileNames: 'index.d.ts',
326
- format: 'es',
327
- },
328
- },
329
- ])
330
-
331
- await runBuild()
332
-
333
- // 1 main build + 2 isolated DTS builds = 3 rolldown calls
334
- expect(mockRolldown).toHaveBeenCalledTimes(3)
335
- })
336
- })
@@ -1,172 +0,0 @@
1
- import { cpSync, mkdirSync, readdirSync, renameSync, statSync } from 'node:fs'
2
- import { join } from 'node:path'
3
- import chalk from 'chalk'
4
- import { rimraf } from 'rimraf'
5
- import { rolldown } from 'rolldown'
6
- import { CONFIG, PKG } from '../config/index.js'
7
- import {
8
- buildAllDts,
9
- createBuildPipeline,
10
- config as rolldownConfig,
11
- } from '../rolldown/index.js'
12
-
13
- const { log } = console
14
- const allBuilds = createBuildPipeline()
15
- const allBuildsCount = allBuilds.length
16
-
17
- const FORMAT_LABEL: Record<string, string> = {
18
- cjs: 'CJS',
19
- es: 'ESM',
20
- umd: 'UMD',
21
- iife: 'IIFE',
22
- }
23
-
24
- const label = (text: string) => chalk.bold.bgCyan.black(` ${text} `)
25
- const dim = chalk.dim
26
- const bold = chalk.bold
27
-
28
- async function build({
29
- inputOptions,
30
- outputOptions,
31
- }: {
32
- inputOptions: any
33
- outputOptions: any
34
- }) {
35
- const bundle = await rolldown(inputOptions)
36
- await bundle.write(outputOptions)
37
- await bundle.close()
38
- }
39
-
40
- const createBuilds = async () => {
41
- let p = Promise.resolve()
42
-
43
- allBuilds.forEach((item: Record<string, any>) => {
44
- const { output, ...input } = rolldownConfig(item)
45
- const format = FORMAT_LABEL[output.format] || output.format
46
- p = p.then(() => {
47
- const start = performance.now()
48
-
49
- return build({ inputOptions: input, outputOptions: output })
50
- .then(() => {
51
- const duration = Math.round(performance.now() - start)
52
- log(
53
- ` ${chalk.green('+')} ${bold(format)} ${dim('->')} ${dim(`${output.dir}/${output.entryFileNames}`)} ${dim(`(${duration}ms)`)}`,
54
- )
55
- })
56
- .catch((e) => {
57
- log(`\n${chalk.bold.red('Build failed')}`)
58
- log(chalk.gray(` Format: ${format}`))
59
- log(chalk.gray(` File: ${output.dir}/${output.entryFileNames}`))
60
- log(e)
61
- throw e
62
- })
63
- })
64
- })
65
-
66
- return p
67
- }
68
-
69
- const copyStaticFiles = () => {
70
- if (!Array.isArray(CONFIG.copyFiles) || CONFIG.copyFiles.length === 0) return
71
-
72
- log(`\n${dim('Copying')} static files...\n`)
73
- for (const { from, to } of CONFIG.copyFiles as {
74
- from: string
75
- to: string
76
- }[]) {
77
- cpSync(from, to, { recursive: true })
78
- log(` ${chalk.green('+')} ${dim(from)} ${dim('->')} ${dim(to)}`)
79
- }
80
- }
81
-
82
- /**
83
- * Build a single DTS entry in an isolated temp directory, then move the
84
- * largest .d.ts file (the real declarations) to the final output path.
85
- *
86
- * Rolldown code-splits DTS output: the entry file is a tiny re-export stub,
87
- * and the actual types go into a chunk. Building into a temp dir avoids
88
- * collisions when multiple DTS entries share the same output directory.
89
- */
90
- const buildDtsIsolated = async (
91
- dtsConfig: ReturnType<typeof buildAllDts>[number],
92
- ) => {
93
- const { output, file, ...input } = dtsConfig
94
- const finalDir = output.dir as string
95
- const entryName = output.entryFileNames as string
96
- const tempDir = join(finalDir, `__dts_tmp_${entryName.replace(/\W/g, '_')}`)
97
-
98
- // Build into isolated temp directory
99
- const tempOutput = { ...output, dir: tempDir }
100
- await build({ inputOptions: input, outputOptions: tempOutput })
101
-
102
- // Find the largest .d.ts file — that's the real declarations
103
- const absTempDir = join(process.cwd(), tempDir)
104
- const dtsFiles = readdirSync(absTempDir).filter((f) => f.endsWith('.d.ts'))
105
-
106
- let bestFile = dtsFiles[0] || entryName
107
- let bestSize = 0
108
- for (const f of dtsFiles) {
109
- const size = statSync(join(absTempDir, f)).size
110
- if (size > bestSize) {
111
- bestSize = size
112
- bestFile = f
113
- }
114
- }
115
-
116
- // Move the best file to the final location
117
- const absFinalDir = join(process.cwd(), finalDir)
118
- mkdirSync(absFinalDir, { recursive: true })
119
- renameSync(join(absTempDir, bestFile), join(absFinalDir, entryName))
120
-
121
- // Move sourcemap if it exists
122
- const mapName = `${bestFile}.map`
123
- try {
124
- renameSync(join(absTempDir, mapName), join(absFinalDir, `${entryName}.map`))
125
- } catch {
126
- // sourcemap may not exist
127
- }
128
-
129
- // Clean up temp directory
130
- rimraf.sync(absTempDir)
131
- }
132
-
133
- const generateDeclarations = async () => {
134
- const dtsConfigs = buildAllDts()
135
- if (dtsConfigs.length === 0) return
136
-
137
- log(`\n${dim('Generating')} declarations...`)
138
-
139
- for (const dtsFile of dtsConfigs) {
140
- const tscStart = performance.now()
141
- await buildDtsIsolated(dtsFile)
142
-
143
- const tscDuration = Math.round(performance.now() - tscStart)
144
- log(
145
- ` ${chalk.green('+')} ${bold('DTS')} ${dim('->')} ${dim(dtsFile.file)} ${dim(`(${tscDuration}ms)`)}`,
146
- )
147
- }
148
- }
149
-
150
- const runBuild = async () => {
151
- const start = performance.now()
152
-
153
- log(
154
- `\n${label('rolldown')} ${bold(PKG.name || '')} ${dim(`v${PKG.version || '0.0.0'}`)}\n`,
155
- )
156
-
157
- log(`${dim('Cleaning')} ${CONFIG.outputDir}/`)
158
- rimraf.sync(`${process.cwd()}/${CONFIG.outputDir}`)
159
-
160
- log(
161
- `${dim('Building')} ${bold(String(allBuildsCount))} bundle${allBuildsCount > 1 ? 's' : ''}...\n`,
162
- )
163
-
164
- await createBuilds()
165
- copyStaticFiles()
166
- await generateDeclarations()
167
-
168
- const total = Math.round(performance.now() - start)
169
- log(`\n${chalk.green('Done')} ${dim(`in ${total}ms`)}\n`)
170
- }
171
-
172
- export { runBuild }
package/tsconfig.json DELETED
@@ -1,15 +0,0 @@
1
- {
2
- "extends": "@vitus-labs/tools-typescript/lib",
3
- "compilerOptions": {
4
- "noEmit": false,
5
- "outDir": "lib",
6
- "rootDir": "src",
7
- "baseUrl": ".",
8
- "declarationDir": "./lib/types",
9
- "paths": {
10
- "~/*": ["src/*"]
11
- }
12
- },
13
- "include": ["typings", "src"],
14
- "exclude": ["node_modules", "__stories__", "lib", "**/*.test.ts"]
15
- }
package/vitest.config.ts DELETED
@@ -1,3 +0,0 @@
1
- import { createVitestConfig } from '@vitus-labs/tools-vitest'
2
-
3
- export default createVitestConfig(['src/config/baseConfig.ts'])