Repository navigation
Expand file tree
/
Copy pathrolldown.base.mjs
More file actions
198 lines (182 loc) · 8.25 KB
/
Copy pathrolldown.base.mjs
File metadata and controls
198 lines (182 loc) · 8.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
// Shared rolldown build for every member package. Call from packages/<name>/rolldown.config.mjs
// with `import.meta.url`. Mirrors the rollup configs this replaces; see the option docs below.
import fs from 'fs'
import path from 'path'
import {fileURLToPath} from 'url'
import {dts} from 'rolldown-plugin-dts'
const OUT_DIR = process.env.ROLLDOWN_OUT ?? 'lib'
/**
* The members built by the shared toolchain, which are exactly the packages whose Makefile
* includes common.mk. Of those, the ones that emit a browser test bundle are the ones that do
* not blank BROWSER_OUT.
*
* @param {string} root the workspace root
* @param {object} [o]
* @param {boolean} [o.browser] restrict to members with a browser test bundle
* @returns {string[]}
*/
export function members(root, o = {}) {
return fs
.readdirSync(path.join(root, 'packages'))
.filter((name) => {
const makefile = path.join(root, 'packages', name, 'Makefile')
if (!fs.existsSync(makefile)) return false
const text = fs.readFileSync(makefile, 'utf8')
if (!/^include \.\.\/\.\.\/common\.mk$/m.test(text)) return false
return !o.browser || !/^BROWSER_OUT :=\s*$/m.test(text)
})
.sort()
}
/**
* The member to build: the one whose directory rolldown was invoked from, or every member when
* it was invoked from the workspace root. Keeps `make -C packages/<name>` building one member.
*
* @param {string} root the workspace root
* @param {string[]} all candidate member names
* @returns {string[]}
*/
export function selected(root, all) {
const cwd = process.cwd()
const here = path.basename(cwd)
return path.dirname(cwd) === path.join(root, 'packages') && all.includes(here) ? [here] : all
}
// In a .d.ts with no export declaration TypeScript exports every top-level declaration.
// rolldown-plugin-dts emits inline export modifiers only, so without a trailing export list the
// published type surface widens to include local helper interfaces that rollup kept private.
function closeDtsExports() {
return {
name: 'close-dts-exports',
renderChunk(code, chunk) {
if (!chunk.fileName.endsWith('.d.ts')) return null
return `${code}\nexport {};\n`
},
}
}
/**
* @param {string} dir the member's package directory
* @param {object} [o]
* @param {string} [o.bannerText] literal prefix emitted above the licence notice
* @param {boolean} [o.comments] keep comments (default false only where cleanup ran)
* @param {boolean} [o.named] output.exports 'named' on the CJS build
* @param {boolean} [o.esModule] output.esModule on the CJS build (default true)
* @param {boolean} [o.stripInternal] drop @internal from the declaration build
* @param {boolean} [o.replaceVersion] substitute __ver with pkg.version
* @param {boolean} [o.browser] platform browser
* @param {boolean} [o.bundleDeps] resolve node_modules; external is the declared list only
* @param {object} [o.alias] resolve.alias entries applied to every build
* @param {string[]} [o.cjsExternal] override the CJS external list
* @param {boolean} [o.dir] emit to output.dir rather than output.file
* @param {boolean} [o.types] emit declarations (default true)
* @param {boolean} [o.esm] emit the ESM build (default true)
* @param {string[]} [o.externalExtra] extra ids treated as external
* @param {object[]} [o.extraOutputs] additional {file, format, alias} builds
* @returns {import('rolldown').RolldownOptions[]}
*/
export function libraryConfig(dir, o = {}) {
const pkg = JSON.parse(fs.readFileSync(path.join(dir, 'package.json')))
// OUT_DIR replaces the manifest's own top-level output directory, so a member that emits
// into lib/cjs, lib/esm and lib/types keeps those subdirectories
const relocate = (f) => path.join(dir, f.replace(/^\.\//, '').replace(/^[^/]+/, OUT_DIR))
const out = (f) => relocate(f)
const outDir = (f) => path.dirname(relocate(f))
// A one-line notice rather than the full licence text: it survives minification by the same
// rule the full block did, at 106 bytes instead of 1,640, and the text itself ships as
// LICENSE in every tarball. The copyright line is read rather than composed so that the
// members attributing FFF00 Agents AB keep saying so.
const copyright = fs
.readFileSync(path.join(dir, 'LICENSE'), 'utf-8')
.split('\n')
.find((line) => /^copyright/i.test(line))
.trim()
// the repository rather than the homepage: this points at the source the licence covers, and
// homepage is due to become a documentation link
const source = pkg.repository.url.replace(/^git\+/, '').replace(/\.git$/, '')
const notice = `/*! ${pkg.name} v${pkg.version} | ${pkg.license} | ${copyright} | ${source} */`
const banner = o.bannerText ? `${o.bannerText}\n${notice}` : notice
const declared = [...Object.keys(pkg.dependencies ?? {}), ...(o.externalExtra ?? [])]
// rolldown resolves node_modules where these rollup configs did not, so anything not
// relative or absolute has to be named external or it is silently vendored into the bundle.
const externalAll = (id) =>
!id.startsWith('.') && !path.isAbsolute(id) && !id.startsWith('@oxc-project/')
const external = o.bundleDeps ? declared : externalAll
const comments = o.comments === true ? undefined : false
const target = {target: 'es2020'}
const define = o.replaceVersion ? {__ver: JSON.stringify(pkg.version)} : undefined
const shared = {
input: path.join(dir, 'src/index.ts'),
external,
tsconfig: path.join(dir, 'tsconfig.json'),
transform: define ? {...target, define} : target,
...(o.browser ? {platform: 'browser'} : {}),
...(o.alias ? {resolve: {alias: o.alias}} : {}),
}
// stable chunk names so a rebuild overwrites rather than leaving the old hash behind to publish
const place = o.dir
? (f) => ({dir: outDir(f), chunkFileNames: '[name].js'})
: (f) => ({file: out(f)})
const configs = [
{
...shared,
...(o.cjsExternal ? {external: o.cjsExternal} : {}),
output: {
banner,
...place(pkg.main),
format: 'cjs',
esModule: o.esModule !== false,
sourcemap: true,
exports: 'named',
...(comments === false ? {comments: false} : {}),
},
},
]
if (o.esm !== false) {
configs.push({
...shared,
output: {
banner,
...place(pkg.module),
format: 'esm',
sourcemap: true,
...(comments === false ? {comments: false} : {}),
},
})
}
for (const extra of o.extraOutputs ?? []) {
configs.push({
...shared,
...(extra.alias ? {resolve: {alias: {...o.alias, ...extra.alias}}} : {}),
output: {
banner,
file: out(extra.file),
format: extra.format ?? 'esm',
sourcemap: true,
...(comments === false ? {comments: false} : {}),
},
})
}
if (o.types !== false) {
configs.push({
...shared,
plugins: [
dts({
cwd: dir,
tsconfig: path.join(dir, 'tsconfig.json'),
generator: 'tsc',
emitDtsOnly: true,
...(o.stripInternal ? {compilerOptions: {stripInternal: true}} : {}),
}),
closeDtsExports(),
],
output: {
banner,
dir: outDir(pkg.types),
// rolldown-plugin-dts names the declaration entry '<entry>.d'; the manifests
// expect <name>.d.ts, and a plain string would collide with the JS entry
entryFileNames: (chunk) =>
chunk.name.endsWith('.d') ? path.basename(pkg.types) : '[name].js',
format: 'esm',
},
})
}
return configs
}