-
-
Notifications
You must be signed in to change notification settings - Fork 367
[DX] Add per-package Yarn scripts (build, watch, test, lint, ...) #2326
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
5517388
[DX] Add lint/format/check-lint/check-format scripts to packages pack…
Kocal 1873b3e
[DX] Rework building process, and add build/watch scripts to packages…
Kocal bc1d63c
[DX] Reconfigure lint/format tasks to not run the command for each wo…
Kocal 8397b52
[DX] Rework testing process, add `test` script to packages package.json
Kocal b8dc5fd
Ignore `var` folder in Biome.js
Kocal File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
/** | ||
* This file is used to compile the assets from an UX package. | ||
*/ | ||
|
||
const { parseArgs } = require('node:util'); | ||
const path = require('node:path'); | ||
const fs = require('node:fs'); | ||
const glob = require('glob'); | ||
const rollup = require('rollup'); | ||
const CleanCSS = require('clean-css'); | ||
const { getRollupConfiguration } = require('./rollup'); | ||
|
||
const args = parseArgs({ | ||
allowPositionals: true, | ||
options: { | ||
watch: { | ||
type: 'boolean', | ||
description: 'Watch the source files for changes and rebuild when necessary.', | ||
}, | ||
}, | ||
}); | ||
|
||
async function main() { | ||
const packageRoot = path.resolve(process.cwd(), args.positionals[0]); | ||
|
||
if (!fs.existsSync(packageRoot)) { | ||
console.error(`The package directory "${packageRoot}" does not exist.`); | ||
process.exit(1); | ||
} | ||
|
||
if (!fs.existsSync(path.join(packageRoot, 'package.json'))) { | ||
console.error(`The package directory "${packageRoot}" does not contain a package.json file.`); | ||
process.exit(1); | ||
} | ||
|
||
const packageData = require(path.join(packageRoot, 'package.json')); | ||
const packageName = packageData.name; | ||
const srcDir = path.join(packageRoot, 'src'); | ||
const distDir = path.join(packageRoot, 'dist'); | ||
|
||
if (!fs.existsSync(srcDir)) { | ||
console.error(`The package directory "${packageRoot}" does not contain a "src" directory.`); | ||
process.exit(1); | ||
} | ||
|
||
if (fs.existsSync(distDir)) { | ||
console.log(`Cleaning up the "${distDir}" directory...`); | ||
await fs.promises.rm(distDir, { recursive: true }); | ||
await fs.promises.mkdir(distDir); | ||
} | ||
|
||
const inputScriptFiles = [ | ||
...glob.sync(path.join(srcDir, '*controller.ts')), | ||
...(['@symfony/ux-react', '@symfony/ux-vue', '@symfony/ux-svelte'].includes(packageName) | ||
? [path.join(srcDir, 'loader.ts'), path.join(srcDir, 'components.ts')] | ||
: []), | ||
...(packageName === '@symfony/stimulus-bundle' | ||
? [path.join(srcDir, 'loader.ts'), path.join(srcDir, 'controllers.ts')] | ||
: []), | ||
]; | ||
|
||
const inputStyleFile = packageData.config?.css_source; | ||
const buildCss = async () => { | ||
const inputStyleFileDist = inputStyleFile | ||
? path.resolve(distDir, `${path.basename(inputStyleFile, '.css')}.min.css`) | ||
: undefined; | ||
if (!inputStyleFile) { | ||
return; | ||
} | ||
|
||
console.log('Minifying CSS...'); | ||
const css = await fs.promises.readFile(inputStyleFile, 'utf-8'); | ||
const minified = new CleanCSS().minify(css).styles; | ||
await fs.promises.writeFile(inputStyleFileDist, minified); | ||
}; | ||
smnandre marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
if (inputScriptFiles.length === 0) { | ||
console.error( | ||
`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 "${__filename}" file.` | ||
); | ||
process.exit(1); | ||
} | ||
|
||
const rollupConfig = getRollupConfiguration({ packageRoot, inputFiles: inputScriptFiles }); | ||
|
||
if (args.values.watch) { | ||
console.log( | ||
`Watching for JavaScript${inputStyleFile ? ' and CSS' : ''} files modifications in "${srcDir}" directory...` | ||
); | ||
|
||
if (inputStyleFile) { | ||
rollupConfig.plugins = (rollupConfig.plugins || []).concat({ | ||
name: 'watcher', | ||
buildStart() { | ||
this.addWatchFile(inputStyleFile); | ||
}, | ||
}); | ||
} | ||
|
||
const watcher = rollup.watch(rollupConfig); | ||
watcher.on('event', ({ result }) => { | ||
if (result) { | ||
result.close(); | ||
} | ||
}); | ||
watcher.on('change', async (id, { event }) => { | ||
if (event === 'update') { | ||
console.log('Files were modified, rebuilding...'); | ||
} | ||
|
||
if (inputStyleFile && id === inputStyleFile) { | ||
await buildCss(); | ||
} | ||
}); | ||
} else { | ||
console.log(`Building JavaScript files from ${packageName} package...`); | ||
const start = Date.now(); | ||
|
||
const bundle = await rollup.rollup(rollupConfig); | ||
await bundle.write(rollupConfig.output); | ||
|
||
await buildCss(); | ||
|
||
console.log(`Done in ${((Date.now() - start) / 1000).toFixed(3)} seconds.`); | ||
} | ||
} | ||
|
||
main(); |
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
const fs = require('node:fs'); | ||
const path = require('node:path'); | ||
const resolve = require('@rollup/plugin-node-resolve'); | ||
const commonjs = require('@rollup/plugin-commonjs'); | ||
const typescript = require('@rollup/plugin-typescript'); | ||
const glob = require('glob'); | ||
|
||
/** | ||
* Guarantees that any files imported from a peer dependency are treated as an external. | ||
* | ||
* For example, if we import `chart.js/auto`, that would not normally | ||
* match the "chart.js" we pass to the "externals" config. This plugin | ||
* catches that case and adds it as an external. | ||
* | ||
* Inspired by https://github.com/oat-sa/rollup-plugin-wildcard-external | ||
*/ | ||
const wildcardExternalsPlugin = (peerDependencies) => ({ | ||
name: 'wildcard-externals', | ||
resolveId(source, importer) { | ||
if (importer) { | ||
let matchesExternal = false; | ||
peerDependencies.forEach((peerDependency) => { | ||
if (source.includes(`/${peerDependency}/`)) { | ||
matchesExternal = true; | ||
} | ||
}); | ||
|
||
if (matchesExternal) { | ||
return { | ||
id: source, | ||
external: true, | ||
moduleSideEffects: true, | ||
}; | ||
} | ||
} | ||
|
||
return null; // other ids should be handled as usually | ||
}, | ||
}); | ||
|
||
/** | ||
* Moves the generated TypeScript declaration files to the correct location. | ||
* | ||
* This could probably be configured in the TypeScript plugin. | ||
*/ | ||
const moveTypescriptDeclarationsPlugin = (packageRoot) => ({ | ||
name: 'move-ts-declarations', | ||
writeBundle: async () => { | ||
const isBridge = packageRoot.includes('src/Bridge'); | ||
const globPattern = path.join('dist', '**', 'assets', 'src', '**/*.d.ts'); | ||
const files = glob.sync(globPattern); | ||
|
||
files.forEach((file) => { | ||
const relativePath = file; | ||
// a bit odd, but remove first 7 or 4 directories, which will leave only the relative path to the file | ||
// ex: dist/Chartjs/assets/src/controller.d.ts' => 'dist/controller.d.ts' | ||
const targetFile = relativePath.replace( | ||
`${relativePath | ||
.split('/') | ||
.slice(1, isBridge ? 7 : 4) | ||
.join('/')}/`, | ||
'' | ||
); | ||
if (!fs.existsSync(path.dirname(targetFile))) { | ||
fs.mkdirSync(path.dirname(targetFile), { recursive: true }); | ||
} | ||
fs.renameSync(file, targetFile); | ||
}); | ||
}, | ||
}); | ||
|
||
/** | ||
* @param {String} packageRoot | ||
* @param {Array<String>} inputFiles | ||
*/ | ||
function getRollupConfiguration({ packageRoot, inputFiles }) { | ||
const packagePath = path.join(packageRoot, 'package.json'); | ||
const packageData = JSON.parse(fs.readFileSync(packagePath, 'utf8')); | ||
const peerDependencies = [ | ||
'@hotwired/stimulus', | ||
...(packageData.peerDependencies ? Object.keys(packageData.peerDependencies) : []), | ||
]; | ||
|
||
inputFiles.forEach((file) => { | ||
// custom handling for StimulusBundle | ||
if (file.includes('StimulusBundle/assets/src/loader.ts')) { | ||
peerDependencies.push('./controllers.js'); | ||
} | ||
|
||
// React, Vue | ||
if (file.includes('assets/src/loader.ts')) { | ||
peerDependencies.push('./components.js'); | ||
} | ||
}); | ||
|
||
const outDir = path.join(packageRoot, 'dist'); | ||
|
||
return { | ||
input: inputFiles, | ||
smnandre marked this conversation as resolved.
Show resolved
Hide resolved
|
||
output: { | ||
dir: outDir, | ||
entryFileNames: '[name].js', | ||
format: 'esm', | ||
}, | ||
external: peerDependencies, | ||
plugins: [ | ||
resolve(), | ||
typescript({ | ||
filterRoot: '.', | ||
tsconfig: path.join(__dirname, '..', 'tsconfig.json'), | ||
include: [ | ||
'src/**/*.ts', | ||
// TODO: Remove for the next major release | ||
// "@rollup/plugin-typescript" v11.0.0 fixed an issue (https://github.com/rollup/plugins/pull/1310) that | ||
// cause a breaking change for UX React users, the dist file requires "react-dom/client" instead of "react-dom" | ||
// and it will break for users using the Symfony AssetMapper without Symfony Flex (for automatic "importmap.php" upgrade). | ||
'**/node_modules/react-dom/client.js', | ||
], | ||
compilerOptions: { | ||
outDir: outDir, | ||
declaration: true, | ||
emitDeclarationOnly: true, | ||
}, | ||
}), | ||
commonjs(), | ||
wildcardExternalsPlugin(peerDependencies), | ||
moveTypescriptDeclarationsPlugin(packageRoot), | ||
], | ||
}; | ||
} | ||
|
||
module.exports = { | ||
getRollupConfiguration, | ||
}; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.