diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 99ff7cf71..b78e0814b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,24 +16,6 @@ on: permissions: {} jobs: - lint-and-check: - runs-on: blacksmith-4vcpu-ubuntu-2404 - permissions: - contents: read - steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - with: - persist-credentials: false - - uses: pnpm/action-setup@26f6d4f2c533a43e6b5da0b4a5dd983f98f7b49a # v6.0.4 - - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 - with: - node-version-file: .node-version - cache: pnpm - - run: pnpm install --frozen-lockfile - - run: pnpm lint - - run: pnpm build:all - - run: pnpm check - test: runs-on: ${{ matrix.os }} permissions: @@ -44,57 +26,27 @@ jobs: # Windows runs on main pushes and on PRs labeled "test: windows", # unless a reusable workflow caller explicitly disables it. os: '${{ (((github.event_name == ''push'' && startsWith(github.ref, ''refs/heads/'')) || contains(github.event.pull_request.labels.*.name, ''test: windows'')) && !(github.event_name == ''workflow_call'' && inputs.skip_windows)) && fromJSON(''["blacksmith-4vcpu-ubuntu-2404","windows-latest"]'') || fromJSON(''["blacksmith-4vcpu-ubuntu-2404"]'') }}' - suite: [unit, core-markdown, metaframeworks, pages-react-vue, pages-svelte-solid] - include: - - suite: unit - script: test:unit - - suite: core-markdown - script: test:integration - - suite: metaframeworks - script: test:metaframeworks - retry: 2 - - suite: pages-react-vue - script: test:pages:react-vue - retry: 2 - prebuild: playground/react-pages playground/vue-pages - - suite: pages-svelte-solid - script: test:pages:svelte-solid - retry: 2 - prebuild: playground/svelte-pages playground/solid-pages steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - - uses: pnpm/action-setup@26f6d4f2c533a43e6b5da0b4a5dd983f98f7b49a # v6.0.4 - - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + - uses: voidzero-dev/setup-vp@45e5c098f1095cc6b65fd92534603e7be70386c1 # v1 with: node-version-file: .node-version - cache: pnpm - - run: pnpm install --frozen-lockfile - - run: pnpm build - - - name: Pre-build playgrounds for preview tests - if: matrix.prebuild - shell: bash - run: | - for pg in ${{ matrix.prebuild }}; do - pnpm --dir "$pg" build & - done - wait + cache: true - - run: pnpm exec playwright install --with-deps chromium - if: matrix.suite != 'unit' && !contains(matrix.os, 'windows') + - run: vp exec playwright install --with-deps chromium + if: ${{ !contains(matrix.os, 'windows') }} - - run: pnpm exec playwright install chromium - if: matrix.suite != 'unit' && contains(matrix.os, 'windows') + - run: vp exec playwright install chromium + if: ${{ contains(matrix.os, 'windows') }} - - run: pnpm ${{ matrix.script }}${{ matrix.retry && format(' --retry {0}', matrix.retry) || '' }} + - run: vp run test:all done: runs-on: ubuntu-latest if: always() needs: - - lint-and-check - test steps: - run: exit 1 diff --git a/.github/workflows/deploy-docs-staging.yml b/.github/workflows/deploy-docs-staging.yml index cde58e7b4..f41891f39 100644 --- a/.github/workflows/deploy-docs-staging.yml +++ b/.github/workflows/deploy-docs-staging.yml @@ -19,14 +19,12 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - - uses: pnpm/action-setup@26f6d4f2c533a43e6b5da0b4a5dd983f98f7b49a # v6.0.4 - - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + - uses: voidzero-dev/setup-vp@45e5c098f1095cc6b65fd92534603e7be70386c1 # v1 with: node-version-file: .node-version - cache: pnpm - - run: pnpm install --frozen-lockfile - - run: pnpm build:core - - run: pnpm install - - run: pnpm deploy:staging:docs + cache: true + - run: vp run build:core + - run: vp install + - run: vp run deploy:staging:docs env: VOID_TOKEN: ${{ secrets.VOID_TOKEN }} diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index 11477d2b0..fae511627 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -19,14 +19,12 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - - uses: pnpm/action-setup@26f6d4f2c533a43e6b5da0b4a5dd983f98f7b49a # v6.0.4 - - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + - uses: voidzero-dev/setup-vp@45e5c098f1095cc6b65fd92534603e7be70386c1 # v1 with: node-version-file: .node-version - cache: pnpm - - run: pnpm install --frozen-lockfile - - run: pnpm build:core - - run: pnpm install - - run: pnpm deploy:docs + cache: true + - run: vp run build:core + - run: vp install + - run: vp run deploy:docs env: VOID_TOKEN: ${{ secrets.VOID_TOKEN }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 77e162bee..0ae649581 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -26,19 +26,12 @@ jobs: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - - uses: pnpm/action-setup@26f6d4f2c533a43e6b5da0b4a5dd983f98f7b49a # v6.0.4 - - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 # zizmor: ignore[cache-poisoning] - with: - node-version-file: .node-version - - run: pnpm install --frozen-lockfile - - run: pnpm build - - # Configure the publish registry after install so docs dependencies under - # @voidzero-dev continue to resolve from npm during installation. - - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 # zizmor: ignore[cache-poisoning] + - uses: voidzero-dev/setup-vp@45e5c098f1095cc6b65fd92534603e7be70386c1 # v1 with: node-version-file: .node-version + cache: true registry-url: https://registry.npmjs.org + - run: vp run build - run: npm install -g npm@latest - - run: pnpm publish -r --access public --tag latest --provenance --no-git-checks + - run: vp pm publish -r --access public --tag latest --provenance --no-git-checks diff --git a/.husky/pre-commit b/.husky/pre-commit deleted file mode 100755 index a240e2831..000000000 --- a/.husky/pre-commit +++ /dev/null @@ -1 +0,0 @@ -./node_modules/.bin/lint-staged diff --git a/.oxfmtrc.json b/.oxfmtrc.json deleted file mode 100644 index 663f1bf4b..000000000 --- a/.oxfmtrc.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "$schema": "./node_modules/oxfmt/configuration_schema.json", - "singleQuote": true, - "experimentalSortImports": { - "newlinesBetween": false - }, - "experimentalSortPackageJson": { - "sortScripts": true - }, - "ignorePatterns": [ - "packages/void/docs/**", - "packages/void/skills/void/docs/**", - "playground/**/.astro/**", - "playground/**/.nuxt/**", - "playground/**/.output/**", - "playground/**/.react-router/**", - "playground/**/.svelte-kit/**", - "playground/**/build/**", - "playground/**/src/routeTree.gen.ts", - "playground/**/wrangler.json", - "playground/**/wrangler.jsonc" - ] -} diff --git a/.oxlintrc.json b/.oxlintrc.json deleted file mode 100644 index 01eaca74d..000000000 --- a/.oxlintrc.json +++ /dev/null @@ -1,239 +0,0 @@ -{ - "$schema": "./node_modules/oxlint/configuration_schema.json", - "categories": { - "correctness": "off" - }, - "env": { - "browser": true, - "builtin": true, - "es2024": true, - "node": true - }, - "jsPlugins": [ - { - "name": "react-hooks-js", - "specifier": "eslint-plugin-react-hooks" - } - ], - "plugins": ["typescript", "import", "react", "unicorn", "vitest", "node"], - "ignorePatterns": [ - "**/node_modules/**", - "**/dist/**", - "**/.void/**", - "**/.vite-cloud/**", - "**/.wrangler/**", - "**/.tanstack/**", - "**/.vitepress/cache/**", - "**/*.tsbuildinfo", - ".pnpm-store/**", - "playground-local/**", - ".worktrees/**" - ], - "overrides": [ - { - "files": ["**/*.ts", "**/*.tsx", "**/*.mts", "**/*.cts"], - "rules": { - "constructor-super": "off", - "getter-return": "off", - "no-class-assign": "off", - "no-const-assign": "off", - "no-dupe-class-members": "off", - "no-dupe-keys": "off", - "no-func-assign": "off", - "no-import-assign": "off", - "no-new-native-nonconstructor": "off", - "no-obj-calls": "off", - "no-redeclare": "off", - "no-setter-return": "off", - "no-this-before-super": "off", - "no-undef": "off", - "no-unreachable": "off", - "no-unsafe-negation": "off", - "no-with": "off", - "prefer-rest-params": "error", - "prefer-spread": "error" - } - }, - { - "files": [ - "packages/react/**/*", - "playground/named-layouts/**/*", - "playground/node-app/**/*", - "playground/node-static/**/*", - "playground/pg-test/**/*", - "playground/react-pages/**/*", - "playground/react-router/**/*", - "playground/spa/**/*", - "playground/ssr/**/*", - "playground/tanstack-start/**/*", - "playground/vinext-app/**/*", - "playground/vinext-pages/**/*", - "playground/websockets/**/*" - ], - "rules": { - "react-hooks-js/component-hook-factories": "error", - "react-hooks-js/config": "error", - "react-hooks-js/error-boundaries": "error", - "react-hooks-js/gating": "error", - "react-hooks-js/globals": "error", - "react-hooks-js/immutability": "error", - "react-hooks-js/incompatible-library": "error", - "react-hooks-js/preserve-manual-memoization": "error", - "react-hooks-js/purity": "error", - "react-hooks-js/refs": "error", - "react-hooks-js/set-state-in-effect": "error", - "react-hooks-js/set-state-in-render": "error", - "react-hooks-js/static-components": "error", - "react-hooks-js/unsupported-syntax": "error", - "react-hooks-js/use-memo": "error" - } - }, - { - "files": ["**/*.svelte"], - "rules": { - "no-undef": "off", - "prefer-const": "off" - } - }, - { - "files": ["**/*.vue"], - "rules": { - "no-undef": "off" - } - } - ], - "rules": { - "@typescript-eslint/array-type": [ - "error", - { - "default": "generic" - } - ], - "@typescript-eslint/no-duplicate-enum-values": "error", - "@typescript-eslint/no-explicit-any": "error", - "@typescript-eslint/no-extra-non-null-assertion": "error", - "@typescript-eslint/no-misused-new": "error", - "@typescript-eslint/no-non-null-asserted-optional-chain": "error", - "@typescript-eslint/no-require-imports": "error", - "@typescript-eslint/no-unnecessary-type-constraint": "error", - "@typescript-eslint/no-unsafe-declaration-merging": "error", - "@typescript-eslint/no-wrapper-object-types": "error", - "@typescript-eslint/prefer-as-const": "error", - "@typescript-eslint/prefer-namespace-keyword": "error", - "@typescript-eslint/triple-slash-reference": "error", - "curly": "error", - "for-direction": "error", - "getter-return": "error", - "import/default": "error", - "import/namespace": "error", - "jest/no-focused-tests": "error", - "no-array-constructor": "error", - "no-async-promise-executor": "error", - "no-case-declarations": "error", - "no-class-assign": "error", - "no-compare-neg-zero": "error", - "no-cond-assign": "error", - "no-const-assign": "error", - "no-constant-binary-expression": "error", - "no-constant-condition": "error", - "no-control-regex": "error", - "no-debugger": "error", - "no-delete-var": "error", - "no-dupe-class-members": "error", - "no-dupe-else-if": "error", - "no-dupe-keys": "error", - "no-duplicate-case": "error", - "no-empty-character-class": "error", - "no-empty-pattern": "error", - "no-empty-static-block": "error", - "no-empty": "error", - "no-ex-assign": "error", - "no-extra-boolean-cast": "error", - "no-fallthrough": "error", - "no-func-assign": "error", - "no-global-assign": "error", - "no-import-assign": "error", - "no-invalid-regexp": "error", - "no-irregular-whitespace": "error", - "no-loss-of-precision": "error", - "no-misleading-character-class": "error", - "no-new-native-nonconstructor": "error", - "no-nonoctal-decimal-escape": "error", - "no-obj-calls": "error", - "no-prototype-builtins": "error", - "no-redeclare": "error", - "no-regex-spaces": "error", - "no-restricted-imports": [ - "error", - { - "paths": [ - { - "name": "node:path", - "message": "Use `pathe` instead. It re-exports the same API but always emits forward slashes, which avoids cross-platform breakage when paths are embedded in module specifiers, generated source, or compared against Vite's normalized watcher events. If you genuinely need format-specific behavior, import `node:path/win32` or `node:path/posix` explicitly." - }, - { - "name": "path", - "message": "Use `pathe` instead of the unprefixed `path` import (and prefer `node:path/win32` / `node:path/posix` for explicit format-specific behavior)." - } - ] - } - ], - "no-self-assign": "error", - "no-setter-return": "error", - "no-shadow-restricted-names": "error", - "no-sparse-arrays": "error", - "no-this-before-super": "error", - "no-undef": "error", - "no-unexpected-multiline": "error", - "no-unreachable": "error", - "no-unsafe-finally": "error", - "no-unsafe-negation": "error", - "no-unsafe-optional-chaining": "error", - "no-unused-expressions": "error", - "no-unused-labels": "error", - "no-unused-private-class-members": "error", - "no-useless-backreference": "error", - "no-useless-catch": "error", - "no-useless-escape": "error", - "no-useless-rename": "error", - "no-var": "error", - "no-warning-comments": [ - "error", - { - "terms": ["@nocommit"] - } - ], - "no-with": "error", - "prefer-const": "error", - "react/exhaustive-deps": "error", - "react/jsx-key": "error", - "react/jsx-no-duplicate-props": "error", - "react/jsx-no-target-blank": "error", - "react/jsx-no-undef": "error", - "react/no-children-prop": "error", - "react/no-danger-with-children": "error", - "react/no-direct-mutation-state": "error", - "react/no-find-dom-node": "error", - "react/no-is-mounted": "error", - "react/no-render-return-value": "error", - "react/no-string-refs": "error", - "react/require-render-return": "error", - "require-yield": "error", - "unicorn/consistent-empty-array-spread": "error", - "unicorn/no-abusive-eslint-disable": "error", - "unicorn/no-invalid-fetch-options": "error", - "unicorn/no-typeof-undefined": "error", - "unicorn/no-unnecessary-slice-end": "error", - "unicorn/no-useless-promise-resolve-reject": "error", - "unicorn/no-useless-spread": "error", - "unicorn/prefer-array-flat-map": "error", - "unicorn/prefer-array-index-of": "error", - "unicorn/prefer-array-some": "error", - "unicorn/prefer-native-coercion-functions": "error", - "unicorn/prefer-node-protocol": "error", - "unicorn/prefer-optional-catch-binding": "error", - "unicorn/prefer-set-size": "error", - "use-isnan": "error", - "valid-typeof": "error" - } -} diff --git a/.vite-hooks/pre-commit b/.vite-hooks/pre-commit new file mode 100755 index 000000000..85fb65b4f --- /dev/null +++ b/.vite-hooks/pre-commit @@ -0,0 +1 @@ +vp staged diff --git a/CLAUDE.md b/CLAUDE.md index 8811c4d9d..783e4169d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -6,7 +6,7 @@ Full-stack framework for Cloudflare Workers, powered by Vite. - **Important**: The source of truth for agent instructions is `CLAUDE.md` (this file). `AGENTS.md` is a symlink to this file. `README.md` is a separate public-facing file. When asked to update agent instructions, always update `CLAUDE.md`. - Documentation source lives in `docs/`. - - Docs is a Vitepress project using our custom theme. When asked to "link the theme", temporarily edit `docs/package.json` so that `@voidzero-dev/vitepress-theme` point to `link:../../../theme`. Rerun `pnpm install` to link it. Revert these when asked to "unlink the theme". + - Docs is a Vitepress project using our custom theme. When asked to "link the theme", temporarily edit `docs/package.json` so that `@voidzero-dev/vitepress-theme` point to `link:../../../theme`. Rerun `vp install` to link it. Revert these when asked to "unlink the theme". - Packages publish to npm as `void` and `@void/*`. Consumer-facing code, docs examples, playground source, and package manifests should use only those public package names. ## Important Note for Release Workflows @@ -20,8 +20,8 @@ The `void` package on npm has these previous versions: '0.0.1', '0.1.0', '0.1.2' - Every implementation plan should include the following: - During plan generation: ask clarifying questions when necessary, especially around developer-experience defining design decisions. - Verification: - - Run `pnpm check` for typechecking after completing a batch of tasks. - - Run `pnpm test` on tests relevant to the current feature only during feature development. Only run the full test suite when everything is done for final verification. + - Run `vp run check` for package typechecking after completing a batch of tasks. + - Run `vp test run` on tests relevant to the current feature only during feature development. Only run the full test suite when everything is done for final verification. - If there are tests hanging or running particularly slow, investigate right there and attempt to fix. Testing velocity is something that should be maintained or improved at every opportunity. - Documentation: after verification, update all docs if relevant and applicable: - `README.md` (this file) @@ -38,19 +38,20 @@ The `void` package on npm has these previous versions: '0.0.1', '0.1.0', '0.1.2' ## Quick Reference ``` -pnpm -r build # Build all packages -pnpm -C packages/void build # Build only the core plugin -pnpm test # Run vitest (unit + integration tests) -pnpm test packages/void # Only test the core plugin -pnpm lint # oxlint -pnpm fmt # oxfmt --write . -pnpm check # Typecheck all packages (tsgo --noEmit) -pnpm -C packages/vue typecheck # Typecheck a specific package +vp run build # Build all packages +vp run build:all # Build packages and playgrounds +vp run build:core # Build only the core plugin +vp test run # Run tests (unit + integration tests) +vp test run packages/void # Only test the core plugin +vp lint # Oxlint through Vite+ +vp fmt # Oxfmt through Vite+ +vp run check # Typecheck all packages (tsgo/vue-tsc) +vp run --filter ./packages/vue typecheck # Typecheck a specific package ``` ## Monorepo Structure -pnpm workspaces. Shared dependency versions use the **pnpm catalog** in `pnpm-workspace.yaml` (`@typescript/native-preview`, tsdown, vite, @cloudflare/workers-types, wrangler). Make sure to run `pnpm install` after making package structure changes. +pnpm workspaces managed through Vite+. Shared dependency versions use the **pnpm catalog** in `pnpm-workspace.yaml` (`@typescript/native-preview`, `vite-plus`, Vite+/Vitest aliases, `@cloudflare/workers-types`, wrangler). Make sure to run `vp install` after making package structure changes. | Package | npm name | Purpose | | ------------------------------------------------ | -------------- | -------------------------------------------------------------- | @@ -128,24 +129,25 @@ See [`packages/solid/README.md`](./packages/solid/README.md) for details. ## Tooling -| Tool | Version | Purpose | -| -------- | ------------- | ---------------------------------------------------------------------------------------------- | -| Node.js | 24.13.1 | Runtime (TS type stripping enabled natively — no `tsx` or `--experimental-strip-types` needed) | -| pnpm | 10.x | Package manager with workspaces + catalog | -| tsdown | 0.20.3 | Build (rolldown-based, ESM, DTS) | -| wrangler | 4.x | CF Worker dev/deploy | -| vitest | 4.x | Unit + integration tests | -| oxlint | 1.47.x | Linting | -| oxfmt | 0.32.x | Formatting (double quotes, semis) | -| vite | 8.0.0-beta.14 | Dev server + build | +| Tool | Version | Purpose | +| -------- | --------- | ---------------------------------------------------------------------------------------------- | +| Node.js | 24.13.1 | Runtime (TS type stripping enabled natively — no `tsx` or `--experimental-strip-types` needed) | +| Vite+ | 0.1.x | Unified toolchain (`vp`, Vite, Vitest, Oxlint, Oxfmt, tsdown) | +| pnpm | 10.x | Package manager used by Vite+ for this workspace | +| tsdown | via Vite+ | Library packaging through `vp pack` | +| wrangler | 4.x | CF Worker dev/deploy | +| vitest | via Vite+ | Unit + integration tests via `vp test` | +| oxlint | via Vite+ | Linting via `vp lint` / `vp check` | +| oxfmt | via Vite+ | Formatting via `vp fmt` / `vp check` | +| vite | via Vite+ | Dev server + build | ## Testing -Tests run from root with `pnpm test` (vitest). See each package README for detailed test listings. +Tests run from root with `vp test run`. See each package README for detailed test listings. ## Playground -Local apps for testing features end-to-end. Run scripts with `pnpm -C playground/