Skip to content

Commit 3103307

Browse files
committed
minor #2944 Modernize and simplify our packages building tools, replace Rollup by tsup (Kocal)
This PR was squashed before being merged into the 2.x branch. Discussion ---------- Modernize and simplify our packages building tools, replace Rollup by tsup | Q | A | ------------- | --- | Bug fix? | no | New feature? | yes <!-- please update src/**/CHANGELOG.md files --> | Docs? | yes <!-- required for new features --> | Issues | Fix #... <!-- prefix each issue number with "Fix #", no need to create an issue if none exist, explain below instead --> | License | MIT Following #2935. Same goals, but less frictions than with tsdown, [tsup](https://github.com/egoist/tsup): - `target` is correctly read from our `tsconfig.packages.json` - no `//#region` comments - less polyfills than oxc - the `target` **stays** `es2021` without any issues with `static` and Stimulus Two things: 1. About the `react-dom/client` import, yes it will impact AssetMapper users that don't use Flex, but it will positively impact other users (AssetMapper with Flex, Webpack Encore...) by removing useless code and making the file smaller 2. About the tons of `.d.ts` files removed, that's still fine, there is no point to generate `dist/<file>.d.ts` files when `dist/<file>.js` do not exist, they are not part of the public API. The code review must be easier, since less code has been touched than with tsdown. --- # Demo When building LiveComponent assets: ``` ➜ assets git:(tsup) pnpm build > `@symfony`/[email protected] build /Users/kocal/workspace-os/symfony-ux/src/LiveComponent/assets > tsx ../../../bin/build_package.ts . CLI Building entry: src/live.css, src/live_controller.ts CLI Using tsconfig: ../../../tsconfig.packages.json CLI tsup v8.5.0 CLI Target: es2021 CLI Cleaning output folder ESM Build start [Symfony UX] Minified CSS file: /Users/kocal/workspace-os/symfony-ux/src/LiveComponent/assets/dist/live.css [Symfony UX] Renamed dist/live.css to dist/live.min.css ESM dist/live_controller.js 12.25 KB ESM dist/live.css 74.00 B ESM ⚡️ Build success in 26ms DTS Build start DTS ⚡️ Build success in 1276ms DTS dist/live_controller.d.ts 7.96 KB ``` When watching LiveComponent assets, the CSS is easily watched too! https://github.com/user-attachments/assets/a246f278-8bf4-40f6-887e-685be7509af0 Commits ------- 78fa229 Rebuild packages with tsup 897aaa8 Modernize and simplify our packages building tools, replace Rollup by tsup
2 parents 61d79b6 + 78fa229 commit 3103307

File tree

102 files changed

+6464
-6381
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

102 files changed

+6464
-6381
lines changed

bin/build_package.ts

Lines changed: 93 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,9 @@
55
import * as fs from 'node:fs';
66
import * as path from 'node:path';
77
import { parseArgs } from 'node:util';
8-
import * as LightningCSS from 'lightningcss';
9-
import * as rollup from 'rollup';
108
import { globSync } from 'tinyglobby';
11-
import { getRollupConfiguration } from './rollup.ts';
9+
import { build } from 'tsup';
10+
import { readPackageJSON } from "pkg-types";
1211

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

37-
const packageData = await import(path.join(packageRoot, 'package.json'), {with: { type: 'json'}});
38-
const packageName = packageData.name;
39-
const srcDir = path.join(packageRoot, 'src');
40-
const distDir = path.join(packageRoot, 'dist');
36+
const packageData = await readPackageJSON(path.join(packageRoot, 'package.json'));
37+
const isStimulusBundle = '@symfony/stimulus-bundle' === packageData.name;
38+
const isReactOrVueOrSvelte = ['@symfony/ux-react', '@symfony/ux-vue', '@symfony/ux-svelte'].some(name => packageData.name.startsWith(name));
4139

42-
if (!fs.existsSync(srcDir)) {
43-
console.error(`The package directory "${packageRoot}" does not contain a "src" directory.`);
44-
process.exit(1);
45-
}
46-
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 = [
54-
...globSync(path.join(srcDir, '*controller.ts')),
55-
...(['@symfony/ux-react', '@symfony/ux-vue', '@symfony/ux-svelte'].includes(packageName)
56-
? [path.join(srcDir, 'loader.ts'), path.join(srcDir, 'components.ts')]
57-
: []),
58-
...(packageName === '@symfony/stimulus-bundle'
59-
? [path.join(srcDir, 'loader.ts'), path.join(srcDir, 'controllers.ts')]
60-
: []),
40+
const inputCssFile = packageData?.config?.css_source;
41+
const inputFiles = [
42+
...globSync('src/*controller.ts'),
43+
...(isStimulusBundle ? ['src/loader.ts', 'src/controllers.ts'] : []),
44+
...(isReactOrVueOrSvelte ? ['src/loader.ts', 'src/components.ts'] : []),
45+
...(inputCssFile ? [inputCssFile] : []),
6146
];
6247

63-
const inputStyleFile = packageData.config?.css_source;
64-
const buildCss = async () => {
65-
if (!inputStyleFile) {
66-
return;
48+
const external = new Set([
49+
// We force "dependencies" and "peerDependencies" to be external to avoid bundling them.
50+
...Object.keys(packageData.dependencies || {}),
51+
...Object.keys(packageData.peerDependencies || {}),
52+
]);
53+
54+
inputFiles.forEach((file) => {
55+
// custom handling for StimulusBundle
56+
if (file.includes('StimulusBundle/assets/src/loader.ts')) {
57+
external.add('./controllers.js');
6758
}
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-
}
8759

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-
],
60+
// React, Vue, Svelte
61+
if (file.includes('assets/src/loader.ts')) {
62+
external.add('./components.js');
63+
}
10464
});
10565

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();
66+
await build({
67+
entry: inputFiles,
68+
outDir: path.join(packageRoot, 'dist'),
69+
clean: true,
70+
external: Array.from(external),
71+
format: 'esm',
72+
platform: 'browser',
73+
tsconfig: path.join(import.meta.dirname, '../tsconfig.packages.json'),
74+
dts: {
75+
entry: inputFiles.filter(inputFile => !inputFile.endsWith('.css')),
76+
},
77+
watch: isWatch,
78+
splitting: false,
79+
esbuildOptions(options) {
80+
// Disabling `bundle` option prevent esbuild to inline relative (but external) imports (like "./components.js" for React, Vue, Svelte).
81+
options.bundle = !(isStimulusBundle || isReactOrVueOrSvelte);
82+
},
83+
plugins: [
84+
{
85+
/**
86+
* This plugin is used to minify CSS files using LightningCSS.
87+
*
88+
* Even if tsup supports CSS minification through ESBuild by setting the `minify: true` option,
89+
* it also minifies JS files but we don't want that.
90+
*/
91+
name: 'symfony-ux:minify-css',
92+
async renderChunk(code, chunkInfo) {
93+
if (!/\.css$/.test(chunkInfo.path)) {
94+
return null;
95+
}
96+
97+
const { transform } = await import('lightningcss');
98+
const result = transform({
99+
filename: chunkInfo.path,
100+
code: Buffer.from(code),
101+
minify: true,
102+
});
103+
104+
console.log(`[Symfony UX] Minified CSS file: ${chunkInfo.path}`);
105+
106+
return {
107+
code: result.code.toString(),
108+
map: result.map ? result.map.toString() : null,
109+
}
110+
},
111+
},
112+
113+
/**
114+
* Unlike tsdown/rolldown and the option "cssEntryFileNames", tsup does not support
115+
* customizing the output file names for CSS files.
116+
* A plugin is needed to rename the written CSS files to add the ".min" suffix.
117+
*/
118+
{
119+
name: 'symfony-ux:append-min-to-css',
120+
async buildEnd({ writtenFiles }) {
121+
for (const writtenFile of writtenFiles) {
122+
if (!writtenFile.name.endsWith('.css')) {
123+
continue;
124+
}
125+
126+
const newName = writtenFile.name.replace(/\.css$/, '.min.css');
127+
await fs.promises.rename(writtenFile.name, newName);
128+
129+
console.info(`[Symfony UX] Renamed ${writtenFile.name} to ${newName}`);
130+
}
131+
}
128132
}
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);
139-
}
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-
}
133+
],
134+
});
148135
}
149136

150137
main();

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",
2017
"@testing-library/dom": "catalog:",
2118
"@testing-library/jest-dom": "catalog:",
2219
"@types/node": "^22.6.0",
2320
"lightningcss": "^1.28.2",
21+
"pkg-types": "^2.2.0",
2422
"playwright": "^1.47.0",
25-
"rollup": "^4.44.1",
2623
"tinyglobby": "^0.2.14",
24+
"tsup": "^8.5.0",
2725
"vitest": "catalog:"
2826
},
2927
"version": "2.27.0"

0 commit comments

Comments
 (0)