From 34fb58adcfa62c415921d0d19e20faef74f9e768 Mon Sep 17 00:00:00 2001 From: fi3ework Date: Tue, 18 Nov 2025 16:09:55 +0800 Subject: [PATCH] fix: correctly handle scope packages on win32 --- .github/workflows/ci.yml | 40 ++++ AGENTS.md | 31 +++ bin.js | 26 +- package.json | 7 +- pnpm-lock.yaml | 291 +++++++++++++++++++++++ src/helper.ts | 33 ++- src/index.ts | 15 +- src/prebundle.ts | 6 - tests/fixtures/prebundle.test.config.mjs | 13 + tests/prebundle.test.ts | 129 ++++++++++ 10 files changed, 568 insertions(+), 23 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 AGENTS.md create mode 100644 tests/fixtures/prebundle.test.config.mjs create mode 100644 tests/prebundle.test.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..f40a2c4 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,40 @@ +name: CI + +on: + push: + branches: + - main + - master + pull_request: + +jobs: + test: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: + - ubuntu-latest + - macos-latest + - windows-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 10.20.0 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Run tests + run: pnpm test diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..edac879 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,31 @@ +# Repository Guidelines + +## Project Structure & Module Organization +- `src/` hosts all TypeScript sources (`index.ts`, `prebundle.ts`, helpers, shared `types.ts`); export public symbols through `src/index.ts`. +- `dist/` holds `rslib` output and must stay generated-only, while `compiled/` stores bundled dependencies produced by the CLI; never edit either manually. +- `bin.js` is the CAC-based CLI entry; root configs (`prebundle.config.ts`, `rslib.config.ts`, `tsconfig.json`) govern build targets and should evolve together. +- `tests/` contains `@rstest/core` suites plus fixture configs under `tests/fixtures/` used to exercise the CLI. + +## Build, Test, and Development Commands +- `pnpm install` respects `pnpm-lock.yaml`; mixing package managers is forbidden to avoid dependency drift. +- `pnpm dev` runs `rslib build --watch` for tight feedback while hacking on `src/`. +- `pnpm build` generates production artifacts and doubles as a pre-publish smoke test; run it before every PR or release. +- `pnpm test` triggers `pnpm build && rstest`, which runs the CLI integration suite. +- `pnpm prebundle [pkg1 pkg2 ...] --config path/to/config` executes the CLI for all configured dependencies or a filtered subset; use `--config` to point at custom fixtures. +- `pnpm bump` wraps `npx bumpp` for releases; only call after `dist/` and configs are committed. + +## Coding Style & Naming Conventions +- The repo is pure ESM (`type: module`), so keep relative imports with explicit `.js` extensions that mirror emitted files. +- Apply `Prettier` defaults (2-space indent, single quotes, trailing commas) before committing; configure your editor to format on save. +- Prefer `camelCase` for functions, `PascalCase` for types/interfaces, and SCREAMING_SNAKE_CASE for constants stored in `constant.ts`. +- Keep modules focused; common utilities belong in `helper.ts`, and shared configuration goes through `src/types.ts` for better type inference. + +## Testing Guidelines +- Automated coverage uses `@rstest/core` in `tests/prebundle.test.ts` to snapshot bundled output and execute the emitted modules; add new cases there or create additional suites under `tests/`. +- Prefer end-to-end checks that run `bin.js --config ` so the CLI path stays covered across platforms. +- When adding manual validation steps (e.g., testing new dependencies), document the exact `pnpm prebundle ...` invocation and observed artifacts in the PR description. + +## Commit & Pull Request Guidelines +- Follow Conventional Commits mirroring existing history (`feat:`, `fix(deps):`, `chore:`); add scopes like `feat(cli):` when touching a specific module. +- Each PR should describe the motivation, list the commands you ran (`pnpm build`, `pnpm prebundle commander`, etc.), and reference related issues. +- Separate mechanical refactors from behavior changes to keep diffs reviewable, and ensure CI or manual checks are linked before requesting review. diff --git a/bin.js b/bin.js index c53aed4..9427a7e 100755 --- a/bin.js +++ b/bin.js @@ -1,4 +1,28 @@ #!/usr/bin/env node +import cac from 'cac'; +import { createRequire } from 'node:module'; import { run } from './dist/index.js'; -run(); +const require = createRequire(import.meta.url); +const pkg = require('./package.json'); + +const cli = cac('prebundle'); + +cli + .command('[...packages]', 'Prebundle configured dependencies') + .option('--config ', 'Path to a custom config file') + .action(async (packages = [], options) => { + try { + await run({ + config: options.config, + packages, + }); + } catch (error) { + console.error(error); + process.exitCode = 1; + } + }); + +cli.help(); +cli.version(pkg.version); +cli.parse(); diff --git a/package.json b/package.json index bfa685c..eabb340 100644 --- a/package.json +++ b/package.json @@ -20,9 +20,11 @@ "dev": "rslib build --watch", "prebundle": "node ./bin.js", "prepare": "npm run build", - "bump": "npx bumpp" + "bump": "npx bumpp", + "test": "rstest" }, "dependencies": { + "cac": "^6.7.14", "@vercel/ncc": "0.38.4", "prettier": "^3.6.2", "rollup": "^4.52.5", @@ -30,9 +32,12 @@ "terser": "^5.44.0" }, "devDependencies": { + "@astrojs/sitemap": "^3.6.0", "@rslib/core": "0.17.0", + "@rstest/core": "^0.6.5", "@types/fs-extra": "^11.0.4", "@types/node": "22.18.13", + "chalk": "^5.6.2", "fast-glob": "^3.3.3", "fs-extra": "^11.3.2", "rslog": "^1.3.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3ab5bf6..8557b2a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: '@vercel/ncc': specifier: 0.38.4 version: 0.38.4 + cac: + specifier: ^6.7.14 + version: 6.7.14 prettier: specifier: ^3.6.2 version: 3.6.2 @@ -24,15 +27,24 @@ importers: specifier: ^5.44.0 version: 5.44.0 devDependencies: + '@astrojs/sitemap': + specifier: ^3.6.0 + version: 3.6.0 '@rslib/core': specifier: 0.17.0 version: 0.17.0(typescript@5.9.3) + '@rstest/core': + specifier: ^0.6.5 + version: 0.6.5 '@types/fs-extra': specifier: ^11.0.4 version: 11.0.4 '@types/node': specifier: 22.18.13 version: 22.18.13 + chalk: + specifier: ^5.6.2 + version: 5.6.2 fast-glob: specifier: ^3.3.3 version: 3.3.3 @@ -106,6 +118,9 @@ packages: resolution: {integrity: sha512-Hb4o6h1Pf6yRUAX07DR4JVY7dmQw+RVQMW5/m55GoiAT/VRoKCWBtIUPPOnqDVhbx1Cjfil9b6EDrgJsUAujEQ==} engines: {node: '>= 10'} + '@astrojs/sitemap@3.6.0': + resolution: {integrity: sha512-4aHkvcOZBWJigRmMIAJwRQXBS+ayoP5z40OklTXYXhUDhwusz+DyDl+nSshY6y9DvkVEavwNcFO8FD81iGhXjg==} + '@babel/code-frame@7.27.1': resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} @@ -144,21 +159,39 @@ packages: '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@module-federation/error-codes@0.21.1': + resolution: {integrity: sha512-h1brnwR9AbwMu1P7ZoJJ9j2O2XWkuMh5p03WhXI1vNEdl3xJheSAvH8RjG8FoKRccVgMnUNDQ+vDVwevUBms/A==} + '@module-federation/error-codes@0.21.2': resolution: {integrity: sha512-mGbPAAApgjmQUl4J7WAt20aV04a26TyS21GDEpOGXFEQG5FqmZnSJ6FqB8K19HgTKioBT1+fF/Ctl5bGGao/EA==} + '@module-federation/runtime-core@0.21.1': + resolution: {integrity: sha512-COob5bepqDc9mKjTziXbQd4WQMCTzhc0cuXyraZhYddYcjcepzZrMpDIXG1x5p+gdg5p1vsGNWt/ZcU8cFh/pg==} + '@module-federation/runtime-core@0.21.2': resolution: {integrity: sha512-LtDnccPxjR8Xqa3daRYr1cH/6vUzK3mQSzgvnfsUm1fXte5syX4ftWw3Eu55VdqNY3yREFRn77AXdu9PfPEZRw==} + '@module-federation/runtime-tools@0.21.1': + resolution: {integrity: sha512-uQmammw3Osg8370yiRqZwKo7eA5zkyml9pAX9x4oS9QAkEBvQpDogERlF9f7gAgcP2P3v+xLg3/bCdquD0gt8A==} + '@module-federation/runtime-tools@0.21.2': resolution: {integrity: sha512-SgG9NWTYGNYcHSd5MepO3AXf6DNXriIo4sKKM4mu4RqfYhHyP+yNjnF/gvYJl52VD61g0nADmzLWzBqxOqk2tg==} + '@module-federation/runtime@0.21.1': + resolution: {integrity: sha512-sfBrP0gEPwXPEiREVKVd0IjEWXtr3G/i7EUZVWTt4D491nNpswog/kuKFatGmhcBb+9uD5v9rxFgmIbgL9njnQ==} + '@module-federation/runtime@0.21.2': resolution: {integrity: sha512-97jlOx4RAnAHMBTfgU5FBK6+V/pfT6GNX0YjSf8G+uJ3lFy74Y6kg/BevEkChTGw5waCLAkw/pw4LmntYcNN7g==} + '@module-federation/sdk@0.21.1': + resolution: {integrity: sha512-1cHMrmCCao3NMFM4BkA0GDt4rbYbyneHct5E4z68cu5UBUnI3L/UboP5VNM8lkYMO1nCR8M0FcLkLhK35Nt48A==} + '@module-federation/sdk@0.21.2': resolution: {integrity: sha512-t2vHSJ1a9zjg7LLJoEghcytNLzeFCqOat5TbXTav5dgU0xXw82Cf0EfLrxiJL6uUpgbtyvUdqqa2DVAvMPjiiA==} + '@module-federation/webpack-bundler-runtime@0.21.1': + resolution: {integrity: sha512-yyXX6ugTV07pMxMzAHt6/JDwblS3f1NDyUI7l44CyYgXpl2ItEEUs5aj5h/5xU1c9Px7M//KkY3qW+InW4tR/A==} + '@module-federation/webpack-bundler-runtime@0.21.2': resolution: {integrity: sha512-06R/NDY6Uh5RBIaBOFwYWzJCf1dIiQd/DFHToBVhejUT3ZFG7GzHEPIIsAGqMzne/JSmVsvjlXiJu7UthQ6rFA==} @@ -292,6 +325,11 @@ packages: engines: {node: '>=18.12.0'} hasBin: true + '@rsbuild/core@1.6.0-beta.1': + resolution: {integrity: sha512-UjQnvXDW9m/hS4DP66ubGIMVjK2PzYx8tzgiinrO0kjNCr9i8KWuJSJGUWyczFMpSsXxp20LnuTxtx7kiGiYdA==} + engines: {node: '>=18.12.0'} + hasBin: true + '@rslib/core@0.17.0': resolution: {integrity: sha512-8oQTnyxeqHB0vFJf8eihxsvpizSdwFOhuEYHWPLjGNvGxvlujt6nhzms6UeopLvtYh7ZMZAzSj0hAIlc/G1u6w==} engines: {node: '>=18.12.0'} @@ -310,53 +348,105 @@ packages: cpu: [arm64] os: [darwin] + '@rspack/binding-darwin-arm64@1.6.0-beta.1': + resolution: {integrity: sha512-RXQ97iVXgvQAb/cq265z/txdHOOJ6fQQRBfnn0IfMNk7gT4W2rvsLrOqQpwtMKxYV4N/mfWnycfAVa0OOf22Gg==} + cpu: [arm64] + os: [darwin] + '@rspack/binding-darwin-x64@1.6.0': resolution: {integrity: sha512-UYz+Y1XqbHGnkUOsaZRuwiuQaQaQ5rEPSboBPlIVDtblwmB71yxo3ET0nSoUhz8L/WXqQoARiraTCxUP6bvSIg==} cpu: [x64] os: [darwin] + '@rspack/binding-darwin-x64@1.6.0-beta.1': + resolution: {integrity: sha512-Ulb7Jyyvuf28BwPXZKSbglaSK/19b32ItWT+pgswhbFsnfhzAQQd7Jo7TUEvHNHAdVDiES8VFlrnOhOSnwEOLg==} + cpu: [x64] + os: [darwin] + '@rspack/binding-linux-arm64-gnu@1.6.0': resolution: {integrity: sha512-Jr7aaxrtwOnh7ge7tZP+Mjpo6uNltvQisL25WcjpP+8PnPT0C9jziKDJso7KxeOINXnQ2yRn2h65+HBNb7FQig==} cpu: [arm64] os: [linux] + '@rspack/binding-linux-arm64-gnu@1.6.0-beta.1': + resolution: {integrity: sha512-UyUoh5RXHTWCktqPVnqoc5rwlWyLkWqGu6ga+iyJHDxdxlrHFfwJnTSnCd4y8cRadf7CrmjHElxE61GU3WCYhw==} + cpu: [arm64] + os: [linux] + '@rspack/binding-linux-arm64-musl@1.6.0': resolution: {integrity: sha512-hl17reUhkjgkcLao6ZvNiSRQFGFykqUpIj1//v/XtVd/2XAZ0Kt7jv9UUeaR+2zY8piH+tgCkwgefmjmajMeFg==} cpu: [arm64] os: [linux] + '@rspack/binding-linux-arm64-musl@1.6.0-beta.1': + resolution: {integrity: sha512-JAXVKHQieN4Ruvs7MstvsPUtRBSAROqJ0abCh4rXdV+FzncKp/ZkdfjQploDhBWtWfU8rPvIjaxeZcPfHMI5/A==} + cpu: [arm64] + os: [linux] + '@rspack/binding-linux-x64-gnu@1.6.0': resolution: {integrity: sha512-xdlb+ToerFU/YggndCfIrZI/S/C80CP9ZFw6lhnEFSTJDAG88KptxstsoKUh8YzyPTD45CYaOjYNtUtiv0nScg==} cpu: [x64] os: [linux] + '@rspack/binding-linux-x64-gnu@1.6.0-beta.1': + resolution: {integrity: sha512-LqAos71CJS5/V4knX9T7T68oGz0XPRZ2IJmI3jEByRlNcyZdxYeQ7Dw09JO9Y5Xj0T+0cudOeL2MxHcD3gTF/w==} + cpu: [x64] + os: [linux] + '@rspack/binding-linux-x64-musl@1.6.0': resolution: {integrity: sha512-IkXEW/FBPPz4EJJTLNZvA+94aLaW2HgUMYu7zCIw5YMc9JJ/UXexY1zjX/A7yidsCiZCRy/ZrB+veFJ5FkZv7w==} cpu: [x64] os: [linux] + '@rspack/binding-linux-x64-musl@1.6.0-beta.1': + resolution: {integrity: sha512-E4dRMzIHYaoYkgmDTFLrgnGtdspbAuVbLfaPF9AWW5YkQn52obGAgbbNb1wi1JJ5f29nTBoLauYCucEO5IGFvA==} + cpu: [x64] + os: [linux] + '@rspack/binding-wasm32-wasi@1.6.0': resolution: {integrity: sha512-XGwX35XXnoTYVUGwDBsKNOkkk/yUsT/RF59u9BwT3QBM5eSXk767xVw/ZeiiyJf5YfI/52HDW2E4QZyvlYyv7g==} cpu: [wasm32] + '@rspack/binding-wasm32-wasi@1.6.0-beta.1': + resolution: {integrity: sha512-PaKEjXOkYprSFlgdgVm/P3pv2E8nAQx9WSGgPmMVIAtxo3Cyz0wwFf0f1Bp9wCw0KkIWgi+9lz8oXNkgKZilug==} + cpu: [wasm32] + '@rspack/binding-win32-arm64-msvc@1.6.0': resolution: {integrity: sha512-HOA/U7YC6EB74CpIrT2GrvPgd+TLr0anNuOp/8omw9hH1jjsP5cjUMgWeAGmWyXWxwoS8rRJ0xhRA+UIe3cL3g==} cpu: [arm64] os: [win32] + '@rspack/binding-win32-arm64-msvc@1.6.0-beta.1': + resolution: {integrity: sha512-HWz9Qxrjf3TKLCwiFPJaqw+STvEsBvFYZvBXZ8umIZXqtdfgQP5d91V8JRG4Gg1J6xnGC/KhZexxBuR/y64aBA==} + cpu: [arm64] + os: [win32] + '@rspack/binding-win32-ia32-msvc@1.6.0': resolution: {integrity: sha512-ThczdltBOFcq+IrTflCE+8q0GvKoISt6pTupkuGnI1/bCnqhCxPP6kx8Z06fdJUFMhvBtpZa0gDJvhh3JBZrKA==} cpu: [ia32] os: [win32] + '@rspack/binding-win32-ia32-msvc@1.6.0-beta.1': + resolution: {integrity: sha512-alAZHRuyPzCH3rJpEC9EBE60EZPnQjzltZ6HN8lsCidACMFTzaLBvuzZyYQah+Zm58O22ok2Eon4BpP1Coizgg==} + cpu: [ia32] + os: [win32] + '@rspack/binding-win32-x64-msvc@1.6.0': resolution: {integrity: sha512-Bhyvsh1m6kIpr1vqZlcdUDUTh0bheRe9SF+f6jw0kPDPbh8FfrRbshPKmRHpRZAUHt20NqgUKR2z2BaKb0IJvQ==} cpu: [x64] os: [win32] + '@rspack/binding-win32-x64-msvc@1.6.0-beta.1': + resolution: {integrity: sha512-/WBzhed0Cu0o9XQ9caGgWwzyNnnPKlENlExa2aGbRCbB14/+CwfhCyETyKlc/ID+dtlV/eHKTC9cckUNI8NpTQ==} + cpu: [x64] + os: [win32] + '@rspack/binding@1.6.0': resolution: {integrity: sha512-RqlCjvWg/LkJjHpsbI48ebo2SYpIBJsV1eh9SEMfXo1batAPvB5grhAbLX0MRUOtzuQOnZMCDGdr2v7l2L8Siw==} + '@rspack/binding@1.6.0-beta.1': + resolution: {integrity: sha512-r3L60ekkDLM5qoRjCMrqsgwU9SQ5e8oA/Omltu/FEEUspIVHawPvAqNZvAXnGB+FoNxM8YgdRRh12PAwXJww0A==} + '@rspack/core@1.6.0': resolution: {integrity: sha512-u2GDSToEhmgIsy0QbOPA81i9tu87J2HgSsRA3HHZfWIR8Vt8KdlAriQnG8CatDnvFSY/UQEumVf5Z1HUAQwxCg==} engines: {node: '>=18.12.0'} @@ -366,16 +456,44 @@ packages: '@swc/helpers': optional: true + '@rspack/core@1.6.0-beta.1': + resolution: {integrity: sha512-2ff8XWonPPHyQ6mEWogMspg+Sul3lXZUfNQVrbYSjfNpi8CeDV0/ZtRbHHbAXiy6pz5fvBFL6X+i/ATckjTYBw==} + engines: {node: '>=18.12.0'} + peerDependencies: + '@swc/helpers': '>=0.5.1' + peerDependenciesMeta: + '@swc/helpers': + optional: true + '@rspack/lite-tapable@1.0.1': resolution: {integrity: sha512-VynGOEsVw2s8TAlLf/uESfrgfrq2+rcXB1muPJYBWbsm1Oa6r5qVQhjA5ggM6z/coYPrsVMgovl3Ff7Q7OCp1w==} engines: {node: '>=16.0.0'} + '@rstest/core@0.6.5': + resolution: {integrity: sha512-nnJkWso5H4JetdHoK6gBPAcgN727xxrCrTC++5oo42lmmk1HRvHYOtOVi3uCdcNUaESrgjKYF4L5PDpowZUy/A==} + engines: {node: '>=18.12.0'} + hasBin: true + peerDependencies: + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + happy-dom: + optional: true + jsdom: + optional: true + '@swc/helpers@0.5.17': resolution: {integrity: sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==} '@tybys/wasm-util@0.10.1': resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} @@ -385,9 +503,15 @@ packages: '@types/jsonfile@6.1.4': resolution: {integrity: sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==} + '@types/node@17.0.45': + resolution: {integrity: sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==} + '@types/node@22.18.13': resolution: {integrity: sha512-Bo45YKIjnmFtv6I1TuC8AaHBbqXtIo+Om5fE4QiU1Tj8QR/qt+8O3BAtOimG5IFmwaWiPmB3Mv3jtYzBA4Us2A==} + '@types/sax@1.2.7': + resolution: {integrity: sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==} + '@vercel/ncc@0.38.4': resolution: {integrity: sha512-8LwjnlP39s08C08J5NstzriPvW1SP8Zfpp1BvC2sI35kPeZnHfxVkCwu4/+Wodgnd60UtT1n8K8zw+Mp7J9JmQ==} hasBin: true @@ -397,6 +521,13 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} @@ -404,6 +535,14 @@ packages: buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + + chalk@5.6.2: + resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + commander@2.20.3: resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} @@ -523,6 +662,14 @@ packages: run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + sax@1.4.3: + resolution: {integrity: sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ==} + + sitemap@8.0.2: + resolution: {integrity: sha512-LwktpJcyZDoa0IL6KT++lQ53pbSrx2c9ge41/SeLTyqy2XUNA6uR4+P9u5IVo5lPeL2arAcOKn1aZAxoYbCKlQ==} + engines: {node: '>=14.0.0', npm: '>=6.0.0'} + hasBin: true + source-map-support@0.5.21: resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} @@ -530,11 +677,18 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} + stream-replace-string@2.0.0: + resolution: {integrity: sha512-TlnjJ1C0QrmxRNrON00JvaFFlNh5TTG00APw23j74ET7gkQpTASi6/L2fuiav8pzK715HXtUeClpBTw2NPSn6w==} + terser@5.44.0: resolution: {integrity: sha512-nIVck8DK+GM/0Frwd+nIhZ84pR/BX7rmXMfYwyg+Sri5oGVE99/E3KvXqpC2xHFxyqXyGHTKBSioxxplrO4I4w==} engines: {node: '>=10'} hasBin: true + tinypool@1.1.1: + resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} + engines: {node: ^18.0.0 || >=20.0.0} + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -554,6 +708,9 @@ packages: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + snapshots: '@ast-grep/napi-darwin-arm64@0.37.0': @@ -595,6 +752,12 @@ snapshots: '@ast-grep/napi-win32-ia32-msvc': 0.37.0 '@ast-grep/napi-win32-x64-msvc': 0.37.0 + '@astrojs/sitemap@3.6.0': + dependencies: + sitemap: 8.0.2 + stream-replace-string: 2.0.0 + zod: 3.25.76 + '@babel/code-frame@7.27.1': dependencies: '@babel/helper-validator-identifier': 7.27.1 @@ -643,26 +806,51 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 + '@module-federation/error-codes@0.21.1': {} + '@module-federation/error-codes@0.21.2': {} + '@module-federation/runtime-core@0.21.1': + dependencies: + '@module-federation/error-codes': 0.21.1 + '@module-federation/sdk': 0.21.1 + '@module-federation/runtime-core@0.21.2': dependencies: '@module-federation/error-codes': 0.21.2 '@module-federation/sdk': 0.21.2 + '@module-federation/runtime-tools@0.21.1': + dependencies: + '@module-federation/runtime': 0.21.1 + '@module-federation/webpack-bundler-runtime': 0.21.1 + '@module-federation/runtime-tools@0.21.2': dependencies: '@module-federation/runtime': 0.21.2 '@module-federation/webpack-bundler-runtime': 0.21.2 + '@module-federation/runtime@0.21.1': + dependencies: + '@module-federation/error-codes': 0.21.1 + '@module-federation/runtime-core': 0.21.1 + '@module-federation/sdk': 0.21.1 + '@module-federation/runtime@0.21.2': dependencies: '@module-federation/error-codes': 0.21.2 '@module-federation/runtime-core': 0.21.2 '@module-federation/sdk': 0.21.2 + '@module-federation/sdk@0.21.1': {} + '@module-federation/sdk@0.21.2': {} + '@module-federation/webpack-bundler-runtime@0.21.1': + dependencies: + '@module-federation/runtime': 0.21.1 + '@module-federation/sdk': 0.21.1 + '@module-federation/webpack-bundler-runtime@0.21.2': dependencies: '@module-federation/runtime': 0.21.2 @@ -761,6 +949,14 @@ snapshots: core-js: 3.46.0 jiti: 2.6.1 + '@rsbuild/core@1.6.0-beta.1': + dependencies: + '@rspack/core': 1.6.0-beta.1(@swc/helpers@0.5.17) + '@rspack/lite-tapable': 1.0.1 + '@swc/helpers': 0.5.17 + core-js: 3.46.0 + jiti: 2.6.1 + '@rslib/core@0.17.0(typescript@5.9.3)': dependencies: '@rsbuild/core': 1.6.0 @@ -773,35 +969,67 @@ snapshots: '@rspack/binding-darwin-arm64@1.6.0': optional: true + '@rspack/binding-darwin-arm64@1.6.0-beta.1': + optional: true + '@rspack/binding-darwin-x64@1.6.0': optional: true + '@rspack/binding-darwin-x64@1.6.0-beta.1': + optional: true + '@rspack/binding-linux-arm64-gnu@1.6.0': optional: true + '@rspack/binding-linux-arm64-gnu@1.6.0-beta.1': + optional: true + '@rspack/binding-linux-arm64-musl@1.6.0': optional: true + '@rspack/binding-linux-arm64-musl@1.6.0-beta.1': + optional: true + '@rspack/binding-linux-x64-gnu@1.6.0': optional: true + '@rspack/binding-linux-x64-gnu@1.6.0-beta.1': + optional: true + '@rspack/binding-linux-x64-musl@1.6.0': optional: true + '@rspack/binding-linux-x64-musl@1.6.0-beta.1': + optional: true + '@rspack/binding-wasm32-wasi@1.6.0': dependencies: '@napi-rs/wasm-runtime': 1.0.7 optional: true + '@rspack/binding-wasm32-wasi@1.6.0-beta.1': + dependencies: + '@napi-rs/wasm-runtime': 1.0.7 + optional: true + '@rspack/binding-win32-arm64-msvc@1.6.0': optional: true + '@rspack/binding-win32-arm64-msvc@1.6.0-beta.1': + optional: true + '@rspack/binding-win32-ia32-msvc@1.6.0': optional: true + '@rspack/binding-win32-ia32-msvc@1.6.0-beta.1': + optional: true + '@rspack/binding-win32-x64-msvc@1.6.0': optional: true + '@rspack/binding-win32-x64-msvc@1.6.0-beta.1': + optional: true + '@rspack/binding@1.6.0': optionalDependencies: '@rspack/binding-darwin-arm64': 1.6.0 @@ -815,6 +1043,19 @@ snapshots: '@rspack/binding-win32-ia32-msvc': 1.6.0 '@rspack/binding-win32-x64-msvc': 1.6.0 + '@rspack/binding@1.6.0-beta.1': + optionalDependencies: + '@rspack/binding-darwin-arm64': 1.6.0-beta.1 + '@rspack/binding-darwin-x64': 1.6.0-beta.1 + '@rspack/binding-linux-arm64-gnu': 1.6.0-beta.1 + '@rspack/binding-linux-arm64-musl': 1.6.0-beta.1 + '@rspack/binding-linux-x64-gnu': 1.6.0-beta.1 + '@rspack/binding-linux-x64-musl': 1.6.0-beta.1 + '@rspack/binding-wasm32-wasi': 1.6.0-beta.1 + '@rspack/binding-win32-arm64-msvc': 1.6.0-beta.1 + '@rspack/binding-win32-ia32-msvc': 1.6.0-beta.1 + '@rspack/binding-win32-x64-msvc': 1.6.0-beta.1 + '@rspack/core@1.6.0(@swc/helpers@0.5.17)': dependencies: '@module-federation/runtime-tools': 0.21.2 @@ -823,8 +1064,22 @@ snapshots: optionalDependencies: '@swc/helpers': 0.5.17 + '@rspack/core@1.6.0-beta.1(@swc/helpers@0.5.17)': + dependencies: + '@module-federation/runtime-tools': 0.21.1 + '@rspack/binding': 1.6.0-beta.1 + '@rspack/lite-tapable': 1.0.1 + optionalDependencies: + '@swc/helpers': 0.5.17 + '@rspack/lite-tapable@1.0.1': {} + '@rstest/core@0.6.5': + dependencies: + '@rsbuild/core': 1.6.0-beta.1 + '@types/chai': 5.2.3 + tinypool: 1.1.1 + '@swc/helpers@0.5.17': dependencies: tslib: 2.8.0 @@ -834,6 +1089,13 @@ snapshots: tslib: 2.8.0 optional: true + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + + '@types/deep-eql@4.0.2': {} + '@types/estree@1.0.8': {} '@types/fs-extra@11.0.4': @@ -845,20 +1107,34 @@ snapshots: dependencies: '@types/node': 22.18.13 + '@types/node@17.0.45': {} + '@types/node@22.18.13': dependencies: undici-types: 6.21.0 + '@types/sax@1.2.7': + dependencies: + '@types/node': 22.18.13 + '@vercel/ncc@0.38.4': {} acorn@8.15.0: {} + arg@5.0.2: {} + + assertion-error@2.0.1: {} + braces@3.0.3: dependencies: fill-range: 7.1.1 buffer-from@1.1.2: {} + cac@6.7.14: {} + + chalk@5.6.2: {} + commander@2.20.3: {} core-js@3.46.0: {} @@ -984,6 +1260,15 @@ snapshots: dependencies: queue-microtask: 1.2.3 + sax@1.4.3: {} + + sitemap@8.0.2: + dependencies: + '@types/node': 17.0.45 + '@types/sax': 1.2.7 + arg: 5.0.2 + sax: 1.4.3 + source-map-support@0.5.21: dependencies: buffer-from: 1.1.2 @@ -991,6 +1276,8 @@ snapshots: source-map@0.6.1: {} + stream-replace-string@2.0.0: {} + terser@5.44.0: dependencies: '@jridgewell/source-map': 0.3.6 @@ -998,6 +1285,8 @@ snapshots: commander: 2.20.3 source-map-support: 0.5.21 + tinypool@1.1.1: {} + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 @@ -1009,3 +1298,5 @@ snapshots: undici-types@6.21.0: {} universalify@2.0.1: {} + + zod@3.25.76: {} diff --git a/src/helper.ts b/src/helper.ts index 656f5e1..33a9ab1 100644 --- a/src/helper.ts +++ b/src/helper.ts @@ -1,4 +1,4 @@ -import { dirname, extname, join } from 'node:path'; +import { dirname, extname, join, resolve } from 'node:path'; import fs from '../compiled/fs-extra/index.js'; import { cwd, DIST_DIR } from './constant.js'; import type { Config, DependencyConfig, ParsedTask } from './types.js'; @@ -9,7 +9,7 @@ const require = createRequire(import.meta.url); export function findDepPath(name: string) { try { - let entry = dirname(require.resolve(join(name), { paths: [cwd] })); + let entry = dirname(require.resolve(name, { paths: [cwd] })); while (!dirname(entry).endsWith('node_modules')) { entry = dirname(entry); @@ -25,22 +25,29 @@ export function findDepPath(name: string) { } } -export const resolveConfig = async () => { - const configFiles = [ - 'prebundle.config.ts', - 'prebundle.config.mts', - 'prebundle.config.mjs', - 'prebundle.config.js', - ] as const; +export const resolveConfig = async (configFile?: string) => { + const configFiles = configFile + ? [resolve(cwd, configFile)] + : ( + [ + 'prebundle.config.ts', + 'prebundle.config.mts', + 'prebundle.config.mjs', + 'prebundle.config.js', + ] as const + ).map((filename) => join(cwd, filename)); for (const filename of configFiles) { - const configPath = join(cwd, filename); - if (fs.existsSync(configPath)) { - const config = await import(pathToFileURL(configPath).href); - return config.default as Config; + if (fs.existsSync(filename)) { + const config = await import(pathToFileURL(filename).href); + return (config.default ?? config) as Config; } } + if (configFile) { + throw new Error(`Unable to locate prebundle config file at: ${configFile}`); + } + throw new Error('Unable to locate prebundle config file.'); }; diff --git a/src/index.ts b/src/index.ts index ab91c5d..897cc53 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,11 +1,22 @@ import { parseTasks, resolveConfig } from './helper.js'; import { prebundle } from './prebundle.js'; -export async function run() { - const config = await resolveConfig(); +export interface RunOptions { + config?: string; + packages?: string[]; +} + +export async function run(options: RunOptions = {}) { + const config = await resolveConfig(options.config); const parsedTasks = parseTasks(config.dependencies, config.prettier); + const filters = options.packages?.length + ? new Set(options.packages) + : null; for (const task of parsedTasks) { + if (filters && !filters.has(task.depName)) { + continue; + } await prebundle(task, config.externals); } } diff --git a/src/prebundle.ts b/src/prebundle.ts index 1c16dea..e6de3be 100644 --- a/src/prebundle.ts +++ b/src/prebundle.ts @@ -246,16 +246,10 @@ function renameDistFolder(task: ParsedTask) { fs.writeJSONSync(pkgPath, pkgJson); } -const pkgName = process.argv[2]; - export async function prebundle( task: ParsedTask, commonExternals: Record = {}, ) { - if (pkgName && task.depName !== pkgName) { - return; - } - logger.start(`prebundle: ${task.depName}`); fs.removeSync(task.distPath); diff --git a/tests/fixtures/prebundle.test.config.mjs b/tests/fixtures/prebundle.test.config.mjs new file mode 100644 index 0000000..ab2a1fd --- /dev/null +++ b/tests/fixtures/prebundle.test.config.mjs @@ -0,0 +1,13 @@ +export default { + prettier: true, + dependencies: [ + { + name: 'chalk', + target: 'es2020', + }, + { + name: '@astrojs/sitemap', + target: 'es2020', + }, + ], +}; diff --git a/tests/prebundle.test.ts b/tests/prebundle.test.ts new file mode 100644 index 0000000..a967d1f --- /dev/null +++ b/tests/prebundle.test.ts @@ -0,0 +1,129 @@ +import { afterAll, beforeAll, describe, expect, it } from '@rstest/core'; +import { execFile } from 'node:child_process'; +import { promises as fs, readdirSync } from 'node:fs'; +import { join, relative, sep } from 'node:path'; +import { pathToFileURL } from 'node:url'; +import { promisify } from 'node:util'; + +type TargetPackage = { + name: string; + verify: (distPath: string) => Promise | void; +}; + +const execFileAsync = promisify(execFile); +const testConfigPath = join( + process.cwd(), + 'tests/fixtures/prebundle.test.config.mjs', +); +const targetPackages: TargetPackage[] = [ + { + name: 'chalk', + verify: async (distPath: string) => { + const mod = await loadBundledModule(distPath); + const chalkInstance = mod.default ?? mod; + const message = chalkInstance.hex('#00ff88')('prebundle-ready'); + expect(message).toContain('prebundle-ready'); + expect(message).toContain('\u001b['); + }, + }, + { + name: '@astrojs/sitemap', + verify: async (distPath: string) => { + const mod = await loadBundledModule(distPath); + const createPlugin = mod.default ?? mod; + const integration = createPlugin(); + expect(integration.name).toBe('@astrojs/sitemap'); + expect(typeof integration.hooks['astro:build:done']).toBe('function'); + }, + }, +]; + +const bundledPaths = new Map(); + +beforeAll(async () => { + for (const target of targetPackages) { + const distPath = join(process.cwd(), 'compiled', target.name); + bundledPaths.set(target.name, distPath); + await resetDist(distPath); + } + await runPrebundle(); +}); + +afterAll(async () => { + for (const distPath of bundledPaths.values()) { + await resetDist(distPath); + } +}); + +describe('prebundle integration smoke tests', () => { + it('chalk bundles the expected artifacts', async () => { + await assertBundledPackage('chalk'); + }); + + it('@astrojs/sitemap bundles the expected artifacts', async () => { + await assertBundledPackage('@astrojs/sitemap'); + }); +}); + +async function resetDist(distPath?: string) { + if (!distPath) { + return; + } + await fs.rm(distPath, { recursive: true, force: true }); +} + +function readRelativeFileTree(distPath: string) { + const results: string[] = []; + const walk = (dir: string) => { + const entries = readdirSync(dir, { withFileTypes: true }); + for (const entry of entries) { + const entryPath = join(dir, entry.name); + if (entry.isDirectory()) { + walk(entryPath); + continue; + } + if (entry.isFile()) { + const rel = relative(distPath, entryPath).split(sep).join('/'); + results.push(rel); + } + } + }; + walk(distPath); + return results.sort(); +} + +async function loadBundledModule(distPath: string) { + const moduleUrl = pathToFileURL(join(distPath, 'index.js')); + return import(moduleUrl.href); +} + +async function runPrebundle() { + const args = [join(process.cwd(), 'bin.js'), '--config', testConfigPath]; + await execFileAsync(process.execPath, args, { + env: { + ...process.env, + FORCE_COLOR: '0', + }, + }); +} + +async function assertBundledPackage(packageName: string) { + const distPath = bundledPaths.get(packageName); + expect(distPath).toBeDefined(); + const files = readRelativeFileTree(distPath!); + const requiredFiles = ['index.d.ts', 'index.js', 'package.json']; + for (const file of requiredFiles) { + expect(files).toContain(file); + } + const extras = files.filter((file) => { + return !requiredFiles.includes(file.toLowerCase()); + }); + if (extras.length) { + expect(extras.map((file) => file.toLowerCase())).toEqual(['license']); + } + const target = targetPackages.find((item) => item.name === packageName); + if (!target) { + throw new Error(`Unknown target package: ${packageName}`); + } + await target.verify(distPath!); +}