Skip to content

Commit 5051c67

Browse files
committed
Simplify our building packages tool (drop Rollup for tsdown)
1 parent 45f31cc commit 5051c67

File tree

5 files changed

+485
-368
lines changed

5 files changed

+485
-368
lines changed

bin/build_package.ts

Lines changed: 96 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,8 @@ import * as fs from 'node:fs';
66
import * as path from 'node:path';
77
import { parseArgs } from 'node:util';
88
import * as LightningCSS from 'lightningcss';
9-
import * as rollup from 'rollup';
109
import { globSync } from 'tinyglobby';
11-
import { getRollupConfiguration } from './rollup.ts';
10+
import { build } from 'tsdown';
1211

1312
const args = parseArgs({
1413
allowPositionals: true,
@@ -34,7 +33,7 @@ async function main() {
3433
process.exit(1);
3534
}
3635

37-
const packageData = await import(path.join(packageRoot, 'package.json'), {with: { type: 'json'}});
36+
const packageData = await import(path.join(packageRoot, 'package.json'), { with: { type: 'json' } });
3837
const packageName = packageData.name;
3938
const srcDir = path.join(packageRoot, 'src');
4039
const distDir = path.join(packageRoot, 'dist');
@@ -44,107 +43,115 @@ async function main() {
4443
process.exit(1);
4544
}
4645

47-
if (fs.existsSync(distDir)) {
48-
console.log(`Cleaning up the "${distDir}" directory...`);
49-
await fs.promises.rm(distDir, { recursive: true });
50-
await fs.promises.mkdir(distDir);
51-
}
52-
53-
const inputScriptFiles = [
46+
const inputFiles = [
5447
...globSync(path.join(srcDir, '*controller.ts')),
5548
...(['@symfony/ux-react', '@symfony/ux-vue', '@symfony/ux-svelte'].includes(packageName)
5649
? [path.join(srcDir, 'loader.ts'), path.join(srcDir, 'components.ts')]
5750
: []),
5851
...(packageName === '@symfony/stimulus-bundle'
5952
? [path.join(srcDir, 'loader.ts'), path.join(srcDir, 'controllers.ts')]
6053
: []),
54+
...(packageData?.config?.css_source ? [packageData.config.css_source] : []),
55+
];
56+
57+
const peerDependencies = [
58+
'@hotwired/stimulus',
59+
...(packageData.peerDependencies ? Object.keys(packageData.peerDependencies) : []),
6160
];
6261

63-
const inputStyleFile = packageData.config?.css_source;
64-
const buildCss = async () => {
65-
if (!inputStyleFile) {
66-
return;
62+
inputFiles.forEach((file) => {
63+
// custom handling for StimulusBundle
64+
if (file.includes('StimulusBundle/assets/src/loader.ts')) {
65+
peerDependencies.push('./controllers.js');
6766
}
68-
const inputStyleFileDist = path.resolve(distDir, `${path.basename(inputStyleFile, '.css')}.min.css`);
69-
70-
console.log('Minifying CSS...');
71-
const css = await fs.promises.readFile(inputStyleFile, 'utf-8');
72-
const { code: minified } = LightningCSS.transform({
73-
filename: path.basename(inputStyleFile, '.css'),
74-
code: Buffer.from(css),
75-
minify: true,
76-
sourceMap: false, // TODO: Maybe we can add source maps later? :)
77-
});
78-
await fs.promises.writeFile(inputStyleFileDist, minified);
79-
};
80-
81-
if (inputScriptFiles.length === 0) {
82-
console.error(
83-
`No input files found for package "${packageName}" (directory "${packageRoot}").\nEnsure you have at least a file matching the pattern "src/*_controller.ts", or manually specify input files in "${import.meta.filename}" file.`
84-
);
85-
process.exit(1);
86-
}
8767

88-
const rollupConfig = getRollupConfiguration({
89-
packageRoot,
90-
inputFiles: inputScriptFiles,
91-
isWatch,
92-
additionalPlugins: [
93-
...(isWatch && inputStyleFile
94-
? [
95-
{
96-
name: 'watcher',
97-
buildStart(this: rollup.PluginContext) {
98-
this.addWatchFile(inputStyleFile);
99-
},
100-
},
101-
]
102-
: []),
103-
],
68+
// React, Vue
69+
if (file.includes('assets/src/loader.ts')) {
70+
peerDependencies.push('./components.js');
71+
}
10472
});
10573

106-
if (isWatch) {
107-
console.log(
108-
`Watching for JavaScript${inputStyleFile ? ' and CSS' : ''} files modifications in "${srcDir}" directory...`
109-
);
110-
111-
const watcher = rollup.watch(rollupConfig);
112-
watcher.on('event', (event) => {
113-
if (event.code === 'ERROR') {
114-
console.error('Error during build:', event.error);
115-
}
116-
117-
if ((event.code === 'BUNDLE_END' || event.code === 'ERROR') && event.result) {
118-
event.result.close();
119-
}
120-
});
121-
watcher.on('change', async (id, { event }) => {
122-
if (event === 'update') {
123-
console.log('Files were modified, rebuilding...');
124-
}
125-
126-
if (inputStyleFile && id === inputStyleFile) {
127-
await buildCss();
74+
build({
75+
entry: inputFiles,
76+
outDir: distDir,
77+
clean: true,
78+
outputOptions: {
79+
cssEntryFileNames: '[name].min.css',
80+
},
81+
external: peerDependencies,
82+
format: 'esm',
83+
platform: 'browser',
84+
tsconfig: path.join(import.meta.dirname, '../tsconfig.packages.json'),
85+
// The target should be kept in sync with `tsconfig.packages.json` file.
86+
// In the future, I hope the target will be read from the `tsconfig.packages.json` file, but for now we need to specify it manually.
87+
target: 'es2021',
88+
watch: isWatch,
89+
plugins: [
90+
91+
/**
92+
* Guarantees that any files imported from a peer dependency are treated as an external.
93+
*
94+
* For example, if we import `chart.js/auto`, that would not normally
95+
* match the "chart.js" we pass to the "externals" config. This plugin
96+
* catches that case and adds it as an external.
97+
*
98+
* Inspired by https://github.com/oat-sa/rollup-plugin-wildcard-external
99+
*/
100+
{
101+
name: 'wildcard-externals',
102+
resolveId(source: string, importer: string) {
103+
if (!importer) {
104+
return null; // other ids should be handled as usually
105+
}
106+
107+
const matchesExternal = peerDependencies.some((peerDependency) => {
108+
return source.includes(`/${peerDependency}/`)
109+
});
110+
111+
if (matchesExternal) {
112+
return {
113+
id: source,
114+
external: true,
115+
moduleSideEffects: true,
116+
};
117+
}
118+
119+
return null; // other ids should be handled as usually
120+
},
121+
},
122+
// Since minifying files is not configurable per file, we need to use a custom plugin to handle CSS minification.
123+
{
124+
name: 'minimize-css',
125+
transform: {
126+
filter: {
127+
id: /\.css$/,
128+
},
129+
handler (code, id) {
130+
const { code: minifiedCode } = LightningCSS.transform({
131+
filename: path.basename(id),
132+
code: Buffer.from(code),
133+
minify: true,
134+
sourceMap: false,
135+
});
136+
137+
return { code: minifiedCode.toString(), map: null };
138+
}
139+
},
140+
},
141+
],
142+
hooks: {
143+
async 'build:done'() {
144+
// TODO: Idk why, but when we build a CSS file (e.g. `style.css`), it also generate an empty JS file (e.g. `style.js`).
145+
if (packageData?.config?.css_source) {
146+
const unwantedJsFile = path.join(distDir, path.basename(packageData.config.css_source, '.css') + '.js');
147+
await fs.promises.rm(unwantedJsFile, { force: true });
148+
}
128149
}
129-
});
130-
} else {
131-
console.log(`Building JavaScript files from ${packageName} package...`);
132-
const start = Date.now();
133-
134-
if (typeof rollupConfig.output === 'undefined' || Array.isArray(rollupConfig.output)) {
135-
console.error(
136-
`The rollup configuration for package "${packageName}" does not contain a valid output configuration.`
137-
);
138-
process.exit(1);
139150
}
140-
141-
const bundle = await rollup.rollup(rollupConfig);
142-
await bundle.write(rollupConfig.output);
143-
144-
await buildCss();
145-
146-
console.log(`Done in ${((Date.now() - start) / 1000).toFixed(3)} seconds.`);
147-
}
151+
}).catch((error) => {
152+
console.error('Error during build:', error);
153+
process.exit(1);
154+
});
148155
}
149156

150157
main();

bin/rollup.ts

Lines changed: 0 additions & 143 deletions
This file was deleted.

package.json

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,14 @@
1414
},
1515
"devDependencies": {
1616
"@biomejs/biome": "^2.0.4",
17-
"@rollup/plugin-commonjs": "^28.0.6",
18-
"@rollup/plugin-node-resolve": "^16.0.1",
19-
"@rollup/plugin-typescript": "^12.1.4",
17+
"@oxc-project/runtime": "^0.77.3",
2018
"@testing-library/dom": "catalog:",
2119
"@testing-library/jest-dom": "catalog:",
2220
"@types/node": "^22.6.0",
2321
"lightningcss": "^1.28.2",
2422
"playwright": "^1.47.0",
25-
"rollup": "^4.44.1",
2623
"tinyglobby": "^0.2.14",
24+
"tsdown": "^0.12.9",
2725
"vitest": "catalog:"
2826
},
2927
"version": "2.27.0"

0 commit comments

Comments
 (0)