@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,375 +0,0 @@
1
- import { beforeEach, describe, expect, it, vi } from 'vitest'
2
-
3
- const { mockPKG } = vi.hoisted(() => ({
4
- mockPKG: {
5
- type: 'commonjs',
6
- name: '@test/pkg',
7
- main: 'lib/index.cjs',
8
- module: 'lib/index.js',
9
- exports: { import: './lib/index.js', require: './lib/index.cjs' },
10
- } as Record<string, any>,
11
- }))
12
-
13
- vi.mock('../config/index.js', () => ({
14
- PKG: mockPKG,
15
- CONFIG: { sourceDir: 'src' },
16
- PLATFORMS: ['browser', 'node', 'web', 'native'],
17
- }))
18
-
19
- describe('createBuildPipeline', () => {
20
- const defaultPKG = { ...mockPKG }
21
-
22
- beforeEach(() => {
23
- Object.assign(mockPKG, defaultPKG)
24
- // Remove any extra keys added by specific tests
25
- for (const key of Object.keys(mockPKG)) {
26
- if (!(key in defaultPKG)) delete mockPKG[key]
27
- }
28
- })
29
-
30
- describe('with CJS package', () => {
31
- let createBuildPipeline: () => any[]
32
-
33
- beforeEach(async () => {
34
- vi.resetModules()
35
- mockPKG.type = 'commonjs'
36
- const mod = await import('./createBuildPipeline.js')
37
- createBuildPipeline = mod.default
38
- })
39
-
40
- it('should create builds from package.json fields', () => {
41
- const builds = createBuildPipeline()
42
-
43
- expect(builds.length).toBeGreaterThan(0)
44
- const mainBuild = builds.find((b) => b.file === 'lib/index.cjs')
45
- expect(mainBuild).toBeDefined()
46
- expect(mainBuild.format).toBe('cjs')
47
- })
48
-
49
- it('should include module build as ES format', () => {
50
- const builds = createBuildPipeline()
51
-
52
- const moduleBuild = builds.find((b) => b.file === 'lib/index.js')
53
- expect(moduleBuild).toBeDefined()
54
- expect(moduleBuild.format).toBe('es')
55
- })
56
- })
57
-
58
- describe('with ES module package', () => {
59
- let createBuildPipeline: () => any[]
60
-
61
- beforeEach(async () => {
62
- vi.resetModules()
63
- mockPKG.type = 'module'
64
- const mod = await import('./createBuildPipeline.js')
65
- createBuildPipeline = mod.default
66
- })
67
-
68
- it('should use ES format for main build', () => {
69
- const builds = createBuildPipeline()
70
-
71
- const mainBuild = builds.find((b) => b.file === 'lib/index.cjs')
72
- if (mainBuild) {
73
- expect(mainBuild.format).toBe('es')
74
- }
75
- })
76
-
77
- it('should include exports options', () => {
78
- const builds = createBuildPipeline()
79
-
80
- expect(builds.length).toBeGreaterThan(0)
81
- })
82
- })
83
-
84
- describe('with string exports', () => {
85
- let createBuildPipeline: () => any[]
86
-
87
- beforeEach(async () => {
88
- vi.resetModules()
89
- mockPKG.type = 'module'
90
- mockPKG.exports = './lib/index.js'
91
- const mod = await import('./createBuildPipeline.js')
92
- createBuildPipeline = mod.default
93
- })
94
-
95
- it('should handle string exports', () => {
96
- const builds = createBuildPipeline()
97
-
98
- const exportBuild = builds.find((b) => b.file === './lib/index.js')
99
- expect(exportBuild).toBeDefined()
100
- expect(exportBuild.format).toBe('es')
101
- })
102
- })
103
-
104
- describe('with object exports having node field', () => {
105
- let createBuildPipeline: () => any[]
106
-
107
- beforeEach(async () => {
108
- vi.resetModules()
109
- mockPKG.type = 'module'
110
- mockPKG.exports = {
111
- import: './lib/index.js',
112
- require: './lib/index.cjs',
113
- node: './lib/node.js',
114
- default: './lib/default.js',
115
- }
116
- const mod = await import('./createBuildPipeline.js')
117
- createBuildPipeline = mod.default
118
- })
119
-
120
- it('should include node export and skip default when import exists', () => {
121
- const builds = createBuildPipeline()
122
-
123
- const nodeBuild = builds.find((b) => b.file === './lib/node.js')
124
- expect(nodeBuild).toBeDefined()
125
- expect(nodeBuild.platform).toBe('node')
126
-
127
- // default is skipped when import is present (avoids duplicate builds)
128
- const defaultBuild = builds.find((b) => b.file === './lib/default.js')
129
- expect(defaultBuild).toBeUndefined()
130
- })
131
- })
132
-
133
- describe('with no exports', () => {
134
- let createBuildPipeline: () => any[]
135
-
136
- beforeEach(async () => {
137
- vi.resetModules()
138
- mockPKG.type = 'module'
139
- delete mockPKG.exports
140
- const mod = await import('./createBuildPipeline.js')
141
- createBuildPipeline = mod.default
142
- })
143
-
144
- it('should return builds based on main/module fields only', () => {
145
- const builds = createBuildPipeline()
146
-
147
- expect(builds.length).toBeGreaterThan(0)
148
- })
149
- })
150
-
151
- describe('with passthrough exports like package.json', () => {
152
- let createBuildPipeline: () => any[]
153
-
154
- beforeEach(async () => {
155
- vi.resetModules()
156
- mockPKG.type = 'module'
157
- delete mockPKG.main
158
- delete mockPKG.module
159
- mockPKG.exports = {
160
- '.': {
161
- import: './lib/index.js',
162
- },
163
- './package.json': './package.json',
164
- }
165
- const mod = await import('./createBuildPipeline.js')
166
- createBuildPipeline = mod.default
167
- })
168
-
169
- it('should skip passthrough exports that are not JS files', () => {
170
- const builds = createBuildPipeline()
171
-
172
- expect(builds).toHaveLength(1)
173
- expect(builds[0].file).toBe('./lib/index.js')
174
- })
175
- })
176
-
177
- describe('with react-native build', () => {
178
- let createBuildPipeline: () => any[]
179
-
180
- beforeEach(async () => {
181
- vi.resetModules()
182
- mockPKG.type = 'commonjs'
183
- mockPKG['react-native'] = 'lib/native.js'
184
- mockPKG.module = 'lib/index.js'
185
- const mod = await import('./createBuildPipeline.js')
186
- createBuildPipeline = mod.default
187
- })
188
-
189
- it('should add native build when path differs from module', () => {
190
- const builds = createBuildPipeline()
191
-
192
- const nativeBuild = builds.find((b) => b.platform === 'native')
193
- expect(nativeBuild).toBeDefined()
194
- expect(nativeBuild.file).toBe('lib/native.js')
195
- })
196
- })
197
-
198
- describe('with browser-specific builds', () => {
199
- let createBuildPipeline: () => any[]
200
-
201
- beforeEach(async () => {
202
- vi.resetModules()
203
- mockPKG.type = 'commonjs'
204
- mockPKG.main = 'lib/index.cjs'
205
- mockPKG.module = 'lib/index.js'
206
- // browser remaps module output — hasDifferentBrowserBuild('main') returns true
207
- // because source ('lib/index.js') !== PKG['main'] ('lib/index.cjs')
208
- mockPKG.browser = {
209
- './lib/index.js': './lib/browser.js',
210
- }
211
- const mod = await import('./createBuildPipeline.js')
212
- createBuildPipeline = mod.default
213
- })
214
-
215
- it('should create browser-specific build variants', () => {
216
- const builds = createBuildPipeline()
217
-
218
- const browserBuild = builds.find((b) => b.platform === 'browser')
219
- expect(browserBuild).toBeDefined()
220
- expect(browserBuild.file).toBe('lib/browser.js')
221
- })
222
-
223
- it('should set node platform for main build when browser variant exists', () => {
224
- const builds = createBuildPipeline()
225
-
226
- const nodeBuild = builds.find(
227
- (b) => b.file === 'lib/index.cjs' && b.platform === 'node',
228
- )
229
- expect(nodeBuild).toBeDefined()
230
- })
231
- })
232
-
233
- describe('with subpath exports', () => {
234
- let createBuildPipeline: () => any[]
235
-
236
- beforeEach(async () => {
237
- vi.resetModules()
238
- mockPKG.type = 'module'
239
- delete mockPKG.main
240
- delete mockPKG.module
241
- mockPKG.exports = {
242
- '.': {
243
- types: './lib/types/index.d.ts',
244
- import: './lib/index.js',
245
- },
246
- './devtools': {
247
- types: './lib/types/devtools/index.d.ts',
248
- import: './lib/devtools/index.js',
249
- },
250
- './validation/zod': {
251
- types: './lib/types/validation/zod.d.ts',
252
- import: './lib/validation/zod.js',
253
- },
254
- }
255
- const mod = await import('./createBuildPipeline.js')
256
- createBuildPipeline = mod.default
257
- })
258
-
259
- it('should create builds for all subpath exports', () => {
260
- const builds = createBuildPipeline()
261
-
262
- expect(builds).toHaveLength(3)
263
- })
264
-
265
- it('should set correct input for root export', () => {
266
- const builds = createBuildPipeline()
267
- const root = builds.find((b) => b.file === './lib/index.js')
268
-
269
- expect(root).toBeDefined()
270
- expect(root.input).toBe('src/index.ts')
271
- expect(root.format).toBe('es')
272
- })
273
-
274
- it('should set correct input for subpath export', () => {
275
- const builds = createBuildPipeline()
276
- const devtools = builds.find((b) => b.file === './lib/devtools/index.js')
277
-
278
- expect(devtools).toBeDefined()
279
- expect(devtools.input).toBe('src/devtools')
280
- })
281
-
282
- it('should set correct input for nested subpath export', () => {
283
- const builds = createBuildPipeline()
284
- const zod = builds.find((b) => b.file === './lib/validation/zod.js')
285
-
286
- expect(zod).toBeDefined()
287
- expect(zod.input).toBe('src/validation/zod')
288
- })
289
- })
290
-
291
- describe('with subpath exports having multiple conditions', () => {
292
- let createBuildPipeline: () => any[]
293
-
294
- beforeEach(async () => {
295
- vi.resetModules()
296
- mockPKG.type = 'module'
297
- delete mockPKG.main
298
- delete mockPKG.module
299
- mockPKG.exports = {
300
- '.': {
301
- import: './lib/index.js',
302
- require: './lib/index.cjs',
303
- },
304
- }
305
- const mod = await import('./createBuildPipeline.js')
306
- createBuildPipeline = mod.default
307
- })
308
-
309
- it('should create builds for each condition', () => {
310
- const builds = createBuildPipeline()
311
-
312
- const esBuild = builds.find((b) => b.file === './lib/index.js')
313
- expect(esBuild).toBeDefined()
314
- expect(esBuild.format).toBe('es')
315
-
316
- const cjsBuild = builds.find((b) => b.file === './lib/index.cjs')
317
- expect(cjsBuild).toBeDefined()
318
- expect(cjsBuild.format).toBe('cjs')
319
- })
320
- })
321
-
322
- describe('with subpath string export', () => {
323
- let createBuildPipeline: () => any[]
324
-
325
- beforeEach(async () => {
326
- vi.resetModules()
327
- mockPKG.type = 'module'
328
- delete mockPKG.main
329
- delete mockPKG.module
330
- mockPKG.exports = {
331
- '.': './lib/index.js',
332
- './utils': './lib/utils.js',
333
- }
334
- const mod = await import('./createBuildPipeline.js')
335
- createBuildPipeline = mod.default
336
- })
337
-
338
- it('should handle string subpath exports', () => {
339
- const builds = createBuildPipeline()
340
-
341
- const root = builds.find((b) => b.file === './lib/index.js')
342
- expect(root).toBeDefined()
343
- expect(root.input).toBe('src/index.ts')
344
-
345
- const utils = builds.find((b) => b.file === './lib/utils.js')
346
- expect(utils).toBeDefined()
347
- expect(utils.input).toBe('src/utils')
348
- })
349
- })
350
-
351
- describe('with UMD builds', () => {
352
- let createBuildPipeline: () => any[]
353
-
354
- beforeEach(async () => {
355
- vi.resetModules()
356
- mockPKG.type = 'commonjs'
357
- mockPKG['umd:main'] = 'lib/index.umd.js'
358
- mockPKG.unpkg = 'lib/index.umd.min.js'
359
- const mod = await import('./createBuildPipeline.js')
360
- createBuildPipeline = mod.default
361
- })
362
-
363
- it('should include UMD build variants', () => {
364
- const builds = createBuildPipeline()
365
-
366
- const umdDev = builds.find((b) => b.file === 'lib/index.umd.js')
367
- expect(umdDev).toBeDefined()
368
- expect(umdDev.format).toBe('umd')
369
-
370
- const umdProd = builds.find((b) => b.file === 'lib/index.umd.min.js')
371
- expect(umdProd).toBeDefined()
372
- expect(umdProd.env).toBe('production')
373
- })
374
- })
375
- })
@@ -1,218 +0,0 @@
1
- import { CONFIG, PKG } from '../config/index.js'
2
-
3
- const isESModuleOnly = PKG.type === 'module'
4
-
5
- const hasDifferentNativeBuild = () => {
6
- return PKG['react-native'] !== PKG.module
7
- }
8
-
9
- const hasDifferentBrowserBuild = (type: string) => {
10
- if (!PKG.browser) return false
11
-
12
- return Object.entries(PKG.browser as Record<string, string>).some(
13
- ([key, value]) => {
14
- const source = key.substring(2)
15
- const output = value.substring(2)
16
-
17
- return source !== PKG[type] && source !== output
18
- },
19
- )
20
- }
21
-
22
- const BUILD_VARIANTS: Record<
23
- string,
24
- { format: string; env: string; platform?: string }
25
- > = {
26
- main: {
27
- format: isESModuleOnly ? 'es' : 'cjs',
28
- env: 'development',
29
- platform: 'universal',
30
- },
31
- module: {
32
- format: 'es',
33
- env: 'development',
34
- platform: 'universal',
35
- },
36
- 'react-native': {
37
- format: 'es',
38
- env: 'development',
39
- platform: 'native',
40
- },
41
- 'umd:main': { format: 'umd', env: 'development' },
42
- unpkg: { format: 'umd', env: 'production' },
43
- }
44
-
45
- /** Check if an exports object uses subpath keys (e.g. ".", "./devtools") */
46
- const isSubpathExports = (obj: Record<string, any>): boolean =>
47
- Object.keys(obj).some((k) => k === '.' || k.startsWith('./'))
48
-
49
- /** Resolve the source input file for a subpath export using convention:
50
- * "." → "src/index.ts", "./devtools" → "src/devtools" */
51
- const resolveSubpathInput = (exportPath: string): string => {
52
- if (exportPath === '.') return `${CONFIG.sourceDir}/index.ts`
53
- const subpath = exportPath.slice(2) // strip "./"
54
- return `${CONFIG.sourceDir}/${subpath}`
55
- }
56
-
57
- /** Extract build variants from a single export's condition object */
58
- const parseConditions = (
59
- conditions: Record<string, any>,
60
- input?: string,
61
- ): Record<string, any>[] => {
62
- const result: Record<string, any>[] = []
63
- const base = input ? { input } : {}
64
-
65
- if (conditions.import) {
66
- result.push({ file: conditions.import, ...BUILD_VARIANTS.module, ...base })
67
- }
68
- if (conditions.require) {
69
- result.push({
70
- file: conditions.require,
71
- format: 'cjs',
72
- env: 'development',
73
- platform: 'universal',
74
- ...base,
75
- })
76
- }
77
- if (conditions.node) {
78
- result.push({
79
- file: conditions.node,
80
- ...BUILD_VARIANTS.module,
81
- platform: 'node',
82
- ...base,
83
- })
84
- }
85
- if (conditions.default && !conditions.import) {
86
- result.push({ file: conditions.default, ...BUILD_VARIANTS.module, ...base })
87
- }
88
-
89
- return result
90
- }
91
-
92
- /** Parse subpath exports object into build variants */
93
- const parseSubpathExports = (
94
- exportsOptions: Record<string, any>,
95
- ): Record<string, any>[] => {
96
- const result: Record<string, any>[] = []
97
-
98
- for (const [exportPath, exportConfig] of Object.entries(exportsOptions)) {
99
- if (typeof exportConfig === 'string') {
100
- // Skip passthrough exports (e.g. "./package.json": "./package.json")
101
- if (!exportConfig.endsWith('.js') && !exportConfig.endsWith('.mjs')) {
102
- continue
103
- }
104
- result.push({
105
- file: exportConfig,
106
- input: resolveSubpathInput(exportPath),
107
- ...BUILD_VARIANTS.module,
108
- })
109
- } else if (typeof exportConfig === 'object' && exportConfig !== null) {
110
- // Skip exports without build conditions (import/require/node/default)
111
- if (
112
- !exportConfig.import &&
113
- !exportConfig.require &&
114
- !exportConfig.node &&
115
- !exportConfig.default
116
- ) {
117
- continue
118
- }
119
- const input = resolveSubpathInput(exportPath)
120
- result.push(...parseConditions(exportConfig, input))
121
- }
122
- }
123
-
124
- return result
125
- }
126
-
127
- const getExportsOptions = () => {
128
- const exportsOptions = PKG.exports
129
-
130
- if (!exportsOptions) return []
131
-
132
- if (typeof exportsOptions === 'string') {
133
- return [{ file: PKG.exports, ...BUILD_VARIANTS.module }]
134
- }
135
-
136
- if (typeof exportsOptions === 'object') {
137
- return isSubpathExports(exportsOptions)
138
- ? parseSubpathExports(exportsOptions)
139
- : parseConditions(exportsOptions)
140
- }
141
-
142
- return []
143
- }
144
-
145
- const createBasicBuildVariants = () => {
146
- let result: Record<string, any>[] = []
147
-
148
- if (isESModuleOnly) result = [...getExportsOptions()]
149
-
150
- Object.keys(BUILD_VARIANTS).forEach((key) => {
151
- const PKGOutDir = PKG[key]
152
-
153
- if (PKGOutDir) {
154
- const hasBrowserBuild = hasDifferentBrowserBuild(key)
155
- const hasNativeBuild = hasDifferentNativeBuild()
156
-
157
- // create a helper function for adding a build variant to an array
158
- const add = (props = {}) => {
159
- result.push({ ...BUILD_VARIANTS[key], file: PKGOutDir, ...props })
160
- }
161
-
162
- if (key === 'react-native') {
163
- // add a separate RN build only if output path differs from module path
164
- if (hasNativeBuild) {
165
- add()
166
- }
167
- } else if (hasBrowserBuild) {
168
- // if has a different browser build, set default platform to node
169
- // as there is going to be created a separate browser build as well
170
- add({ platform: 'node' })
171
- } else {
172
- add()
173
- }
174
- }
175
- })
176
-
177
- return result
178
- }
179
-
180
- const createBrowserBuildVariants = () => {
181
- const result: Record<string, any>[] = []
182
- if (!PKG.browser) return result
183
-
184
- Object.entries(PKG.browser as Record<string, string>).forEach(
185
- ([key, value]) => {
186
- const source = key.substring(2) // strip './' from the beginning of path
187
- const output = value.substring(2) // strip './' from the beginning of path
188
-
189
- Object.keys(BUILD_VARIANTS).forEach((item) => {
190
- if (PKG[item] === source && source !== output) {
191
- result.push({
192
- ...BUILD_VARIANTS[item],
193
- file: output,
194
- platform: 'browser',
195
- })
196
- }
197
- })
198
- },
199
- )
200
-
201
- return result
202
- }
203
-
204
- const createBuildPipeline = () => {
205
- if (Array.isArray(CONFIG.entries) && CONFIG.entries.length > 0) {
206
- return CONFIG.entries.map((entry: Record<string, string | undefined>) => ({
207
- format: entry.format || 'es',
208
- env: entry.env || 'development',
209
- platform: entry.platform || 'universal',
210
- file: entry.file,
211
- input: entry.input,
212
- }))
213
- }
214
-
215
- return [...createBasicBuildVariants(), ...createBrowserBuildVariants()]
216
- }
217
-
218
- export default createBuildPipeline
@@ -1,4 +0,0 @@
1
- import config, { buildAllDts, buildDts } from './config.js'
2
- import createBuildPipeline from './createBuildPipeline.js'
3
-
4
- export { buildAllDts, buildDts, config, createBuildPipeline }