@evertondgn/solid-tooling 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +543 -598
  2. package/app-env.d.ts +7 -0
  3. package/dist/architecture-plugin.mjs +1 -1
  4. package/dist/backend-plugin.mjs +139 -4
  5. package/dist/cli.d.mts +1 -0
  6. package/dist/cli.mjs +42 -0
  7. package/dist/{constants-Jqvtbvxu.mjs → constants-BZJm7ouk.mjs} +1 -1
  8. package/dist/css-filename-CEHQd2gz.mjs +40 -0
  9. package/dist/css-filename.mjs +1 -40
  10. package/dist/fmt-9dlUYOMk.mjs +34 -0
  11. package/dist/fmt.mjs +1 -34
  12. package/dist/i18n-generate.d.mts +1 -0
  13. package/dist/i18n-generate.mjs +23 -0
  14. package/dist/i18nPlugin-CS7Gob6R.mjs +109 -0
  15. package/dist/knip.d.mts +2 -1
  16. package/dist/knip.mjs +7 -2
  17. package/dist/{lint-UTfyg7AR.mjs → lint-ehdtTKfw.mjs} +33 -86
  18. package/dist/lint.mjs +1 -1
  19. package/dist/playwright-teardown.mjs +1 -1
  20. package/dist/playwright.d.mts +2 -1
  21. package/dist/playwright.mjs +4 -3
  22. package/dist/prepareMessages-BXmY6Ko2.mjs +222 -0
  23. package/dist/project-plugin.mjs +2 -2
  24. package/dist/protocol-DSNNUYja.mjs +18 -0
  25. package/dist/publication/read-llms-index.d.mts +12 -0
  26. package/dist/publication/read-llms-index.mjs +1 -0
  27. package/dist/publication-CRUTOB0B.mjs +885 -0
  28. package/dist/readLlmsIndex-Gj4iTxAk.mjs +27 -0
  29. package/dist/stylelint.mjs +2 -2
  30. package/dist/test-locale--tuH0Np5.mjs +3 -0
  31. package/dist/types-DHu2Z_ot.d.mts +49 -0
  32. package/dist/vite.d.mts +33 -7
  33. package/dist/vite.mjs +449 -88
  34. package/dist/vitest-BjLr7iLf.mjs +362 -0
  35. package/dist/vitest.d.mts +3 -7
  36. package/dist/vitest.mjs +1 -129
  37. package/lefthook.yml +3 -1
  38. package/node-env.d.ts +1 -0
  39. package/package.json +32 -39
  40. package/tsconfig.app.json +4 -9
  41. package/virtual-modules.d.ts +90 -0
package/README.md CHANGED
@@ -1,74 +1,71 @@
1
- # @evertondgn/solid-tooling
2
-
3
- Lint preset, formatter config, TypeScript configs, test configs, the Knip and
4
- Stylelint configs, the Vite+ application config, the commitlint config and the
5
- Git hooks for SolidJS 2 applications built with
6
- [Vite+](https://viteplus.dev/guide/).
7
-
8
- The package contains the lint, format, TypeScript, test, build, dead code, CSS
9
- lint, commit message and Git hook setup of
10
- [boilerplate_solidjs](https://github.com/everton-dgn/boilerplate_solidjs), a
11
- SolidJS application template. Applications created from it install the package
12
- instead of copying the files, so a rule change reaches them through a version
13
- update.
14
-
15
- ## Requirements
16
-
17
- The application installs three packages next to this one:
18
-
19
- | Package | Why |
20
- | ---------------------- | ----------------------------------------------------------- |
21
- | `vite-plus` | Runs lint and format, and provides the Oxlint plugin API |
22
- | `@inlang/paraglide-js` | The preset registers its lint plugin, resolved from the app |
23
- | `typescript` | The preset turns on type-aware linting and type checking |
1
+ [![Latest solid-tooling version on npm](https://img.shields.io/npm/v/@evertondgn/solid-tooling)](https://www.npmjs.com/package/@evertondgn/solid-tooling)
2
+ [![CI workflow status](https://github.com/everton-dgn/solidjs_base/actions/workflows/ci.yml/badge.svg)](https://github.com/everton-dgn/solidjs_base/actions/workflows/ci.yml)
3
+ [![MIT license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/everton-dgn/solidjs_base/blob/main/LICENSE)
24
4
 
25
- `vite-plus` and `@inlang/paraglide-js` are peer dependencies, pinned to the
26
- exact versions the package is tested with. Install the versions listed in its
27
- `peerDependencies`. `typescript` is not a peer dependency: the package is tested
28
- with the version in its `devDependencies`.
29
-
30
- `eslint-plugin-solid`, `stylelint` and `stylelint-use-nesting` come with the
31
- package, so the application does not declare them.
32
-
33
- Some entries need tools the application already runs:
5
+ # @evertondgn/solid-tooling
34
6
 
35
- | Entry | The application installs |
36
- | ---------------- | ------------------------------------------------------------------------------- |
37
- | `./vitest` | `@vitest/browser-playwright`, `@vitest/coverage-v8`, `happy-dom`, `playwright` |
38
- | `./playwright` | `@playwright/test`, an optional peer dependency pinned like the others |
39
- | `./knip` | `knip` and `filesystem-routing`, optional peer dependencies pinned the same way |
40
- | `./commitlint` | `@commitlint/cli`, an optional peer dependency pinned the same way |
41
- | `./lefthook.yml` | `lefthook`, an optional peer dependency pinned the same way |
7
+ <picture>
8
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/hero-dark.webp" />
9
+ <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/hero-light.webp" />
10
+ <img src="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/hero-light.webp" alt="Solid Base for SolidJS 2 RC and Vite+. Build your Solid app. Stop rebuilding its foundation. Shared tooling and runtime beneath the SolidJS mark: languages, theme, server protection, SEO, assets and development tools. Install solid-tooling for development and solid-runtime for application features." width="800" />
11
+ </picture>
42
12
 
43
- `./vite` also needs the test tools of `./vitest`, and loads four plugin
44
- packages: `@solidjs/vite-plugin`, `nitro`, `filesystem-routing` and
45
- `unplugin-icons`. They are optional peer dependencies pinned like the others.
46
- Install the versions listed in `peerDependencies` as direct dependencies of the
47
- application: Bun applies `overrides` and `patchedDependencies` only from the
48
- root `package.json`, and the Knip plugin of `unplugin-icons` only turns on with
49
- a direct dependency.
13
+ **The lint rules, tests, Git hooks and whole Vite+ config of your SolidJS 2
14
+ apps, as one dev dependency.**
50
15
 
51
- ## Install
16
+ Applications copied from a starter carry its tooling with them, and every fix to
17
+ the base has to be repeated by hand in each copy. This package holds that
18
+ tooling: an Oxlint preset with rules for your project layout, Oxfmt, TypeScript
19
+ configs, three Vitest projects, Playwright, Knip, a Stylelint policy,
20
+ commitlint, Git hooks and `defineSolidApp`, which returns the whole
21
+ `vite.config.ts` of a [Vite+](https://viteplus.dev/guide/) application. The fix
22
+ arrives when you update the dependency.
52
23
 
53
- `vp` is the Vite+ CLI. An application built with Vite+ already has `vite-plus`
54
- and `typescript`, so it only adds this package:
24
+ > SolidJS 2 is still a release candidate. The package is tested with
25
+ > `2.0.0-rc.21`, and its Solid, Nitro, router and icon plugins are pinned to the
26
+ > versions tested together. A new version of one of them needs a package version
27
+ > tested with it.
55
28
 
56
29
  ```bash
57
30
  vp add -D @evertondgn/solid-tooling
58
31
  ```
59
32
 
60
- An application without `@inlang/paraglide-js` adds it too, in the version the
61
- package asks for:
33
+ Built for applications that follow the
34
+ [boilerplate_solidjs](https://github.com/everton-dgn/boilerplate_solidjs)
35
+ layout. Start with lint and formatting in your existing Vite+ config, and adopt
36
+ the full application config when you are ready.
37
+
38
+ | Package | Role |
39
+ | --------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
40
+ | [`@evertondgn/solid-tooling`](https://github.com/everton-dgn/solidjs_base/blob/main/packages/solid-tooling/README.md) | Config, lint rules, tests, Git hooks, catalog generation, icons and fonts |
41
+ | [`@evertondgn/solid-runtime`](https://github.com/everton-dgn/solidjs_base/blob/main/packages/solid-runtime/README.md) | Security headers, server errors, locale, theme, SEO, sitemaps, Markdown, env and fonts |
42
+
43
+ [Quick start](#quick-start) ·
44
+ [Less config](#the-configuration-you-stop-maintaining) ·
45
+ [Fewer dependencies](#fewer-tool-versions-in-each-manifest) ·
46
+ [App config](#your-application-config-in-one-call) ·
47
+ [Lint](#lint-rules-that-know-your-project) ·
48
+ [Tests](#tests-in-the-environment-each-one-needs) ·
49
+ [E2E](#end-to-end-tests-against-a-real-build) ·
50
+ [Knip](#unused-files-exports-and-dependencies) ·
51
+ [CSS](#a-css-policy-built-on-tokens) · [Hooks](#commit-messages-and-git-hooks) ·
52
+ [Entry points](#entry-points)
62
53
 
63
- ```bash
64
- npm view @evertondgn/solid-tooling peerDependencies
65
- vp add -D @inlang/paraglide-js@<version>
66
- ```
54
+ ## Requirements
67
55
 
68
- ## Usage
56
+ Install the exact Vite+ version listed in the package's `peerDependencies`. The
57
+ package supplies its build, test, type-check and hook tools. Applications keep
58
+ their own direct imports and the Vite+ overrides; see the
59
+ [dependency requirements](https://github.com/everton-dgn/solidjs_base/blob/main/docs/tooling/limits.md#requirements).
60
+ An application with translations also installs the required version of
61
+ `@inlang/paraglide-js` and its message-format plugin.
62
+
63
+ ## Quick start
64
+
65
+ Add `fmt` and `lint` to your existing Vite+ config. The example shows only those
66
+ settings; keep your plugins and other options:
69
67
 
70
68
  ```ts
71
- // vite.config.ts
72
69
  import { fmt } from '@evertondgn/solid-tooling/fmt'
73
70
  import { lint } from '@evertondgn/solid-tooling/lint'
74
71
  import { defineConfig } from 'vite-plus'
@@ -76,8 +73,9 @@ import { defineConfig } from 'vite-plus'
76
73
  export default defineConfig({ fmt, lint })
77
74
  ```
78
75
 
76
+ In `tsconfig.json`:
77
+
79
78
  ```json
80
- // tsconfig.json
81
79
  {
82
80
  "extends": "@evertondgn/solid-tooling/tsconfig.base.json",
83
81
  "compilerOptions": {
@@ -87,83 +85,112 @@ export default defineConfig({ fmt, lint })
87
85
  }
88
86
  ```
89
87
 
90
- `vp check`, `vp lint` and `vp fmt` read both configs from `vite.config.ts`.
91
-
92
- ## Tests
88
+ Check your code and format your files:
93
89
 
94
- `solidTest` returns the `test` block of `vite.config.ts`, with three Vitest
95
- projects:
96
-
97
- | Project | Files | Environment |
98
- | --------- | -------------------------------- | ------------------------------------------------ |
99
- | `node` | `src/**/*.node.test.{ts,tsx}` | Node, with stand-ins for the build-only modules |
100
- | `dom` | `src/**/*.dom.test.{ts,tsx}` | happy-dom at `http://localhost/en` |
101
- | `browser` | `src/**/*.browser.test.{ts,tsx}` | Chromium through Playwright, with the CSS loaded |
102
-
103
- `defineSolidApp` from `./vite` already calls `solidTest`. An application that
104
- builds its own Vite config calls it directly:
105
-
106
- ```ts
107
- // vite.config.ts
108
- import { solidTest } from '@evertondgn/solid-tooling/vitest'
109
- import solid from '@solidjs/vite-plugin'
110
- import { defineConfig } from 'vite-plus'
111
-
112
- export default defineConfig({
113
- test: solidTest({
114
- root: import.meta.dirname,
115
- plugins: () => [solid({ serverFunctions: true })]
116
- })
117
- })
90
+ ```bash
91
+ vp check
92
+ vp fmt
118
93
  ```
119
94
 
120
- `plugins` is a function that returns the Vite plugins the `dom` and `browser`
121
- projects need to compile components: Solid, routes, icons and i18n. The preset
122
- calls it once per project, because each project starts its own Vite server and a
123
- plugin instance keeps the state of one server. The preset also sets the coverage
124
- thresholds, the reporters and the workers on CI, passes `BASE_URL_TEST`, `HOST`
125
- and `PORT` to the tests when they are set, and resets the Paraglide locale
126
- before each browser test.
127
-
128
- The `node` project does not load the Solid and routing plugins, so a plugin of
129
- the preset serves `server-only`, `virtual:file-routes` and
130
- `virtual:solid-server-function-handler`. Knip does not know `server-only`, a
131
- virtual module of `@solidjs/vite-plugin`: pass it in the `ignoreDependencies` of
132
- [`defineKnipConfig`](#knip).
133
-
134
- `definePlaywrightConfig` builds the Playwright config from the application root:
95
+ To see a rule of the package fire, create `src/helpers/export-list.ts`:
135
96
 
136
97
  ```ts
137
- // playwright.config.ts
138
- import { definePlaywrightConfig } from '@evertondgn/solid-tooling/playwright'
98
+ const example = 1
139
99
 
140
- export default definePlaywrightConfig({ root: import.meta.dirname })
100
+ export { example }
141
101
  ```
142
102
 
143
- It reads `BASE_URL_TEST` from `.env.test`, builds the application (`e2e` mode
144
- for the fixture routes, `production` with `TEST_PRODUCTION_ROUTES=true`), starts
145
- the preview, and checks the server log for private markers when the suite ends.
146
- `TEST_PREBUILT=true` reuses an existing production build.
147
-
148
- | Option | Default | Use |
149
- | ---------------- | ----------------- | ---------------------------------------------------------------------- |
150
- | `testDir` | `src/tests/pages` | Folder of the `*.e2e.test.ts` files |
151
- | `fixtureServers` | none | Extra servers for the fixture routes, as `{ command, url }` |
152
- | `suites` | none | Globs of serial suites: `serverLogs`, `markdown` and `publicationLoad` |
153
-
154
- Each serial suite gets its own project: `markdown` runs with one worker,
155
- `serverLogs` runs last as the teardown of the others, and `publicationLoad` runs
156
- alone, against two servers, with `TEST_PUBLICATION_LOAD=true`.
103
+ Run `vp lint`. The preset reports the export list:
157
104
 
158
- ## Application config
105
+ ```text
106
+ src/helpers/export-list.ts:3:1: error project(no-export-list): Export on the declaration itself (export function, export const, export type) instead of using export { ... }. An export list is only allowed in a re-export with from.
107
+ ```
159
108
 
160
- `defineSolidApp` returns the whole `vite.config.ts` of a boilerplate
161
- application: the Solid, routing, icons and Nitro plugins, the headers of the
162
- static files, the build, CSS, server and preview blocks, the `vp run` tasks, and
163
- `lint`, `fmt` and `test` from the presets above.
109
+ <picture>
110
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/first-result-dark.webp" />
111
+ <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/first-result-light.webp" />
112
+ <img src="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/first-result-light.webp" alt="The example ends with export { example } at line 3. vp lint reports project(no-export-list) at line 3, column 1 and asks for an export on the declaration. The corrected export const example = 1 passes that rule." width="800" />
113
+ </picture>
114
+
115
+ Replace the list with `export const example = 1` and the rule passes. `fmt` sets
116
+ 80 columns, single quotes, no semicolons, no trailing commas, wrapped Markdown
117
+ prose and sorted imports. See the
118
+ [formatter](https://github.com/everton-dgn/solidjs_base/blob/main/docs/tooling/typescript-and-format.md#formatter)
119
+ and the
120
+ [TypeScript configs](https://github.com/everton-dgn/solidjs_base/blob/main/docs/tooling/typescript-and-format.md#typescript-base).
121
+
122
+ ## The configuration you stop maintaining
123
+
124
+ <picture>
125
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/before-after-dark.webp" />
126
+ <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/before-after-light.webp" />
127
+ <img src="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/before-after-light.webp" alt="About 87 percent fewer lines across five configuration areas, from 945 to 125: Vite 427 to 48, Playwright 173 to 17, Knip 129 to 40, TypeScript 45 to 9, and Stylelint 171 to an 11-line script. Each bar shows the fraction retained in the app." width="800" />
128
+ </picture>
129
+
130
+ The original starter kept 945 lines of configuration in five areas. The same
131
+ application on this package keeps 125, about 87% fewer. The shared
132
+ implementation lives in the package, where one fix serves every application.
133
+
134
+ | Configuration | Original lines | With the package |
135
+ | --------------------------------------------------------------------------------------------------------------- | -------------: | ---------------: |
136
+ | [Vite](https://github.com/everton-dgn/solidjs_base/blob/main/apps/boilerplate/vite.config.ts) | 427 | 48 |
137
+ | [Playwright](https://github.com/everton-dgn/solidjs_base/blob/main/apps/boilerplate/playwright.config.ts) | 173 | 17 |
138
+ | [Knip](https://github.com/everton-dgn/solidjs_base/blob/main/apps/boilerplate/tooling/knip.ts) | 129 | 40 |
139
+ | [TypeScript](https://github.com/everton-dgn/solidjs_base/blob/main/apps/boilerplate/tsconfig.json) | 45 | 9 |
140
+ | [Stylelint caller](https://github.com/everton-dgn/solidjs_base/blob/main/apps/boilerplate/tooling/css/check.ts) | 171 | 11 |
141
+
142
+ Counts include blank lines and comments. The baseline is the
143
+ [original starter at 6145493](https://github.com/everton-dgn/boilerplate_solidjs/tree/61454937472d0faa1b4fab985fd7c5da968aa9a6),
144
+ whose Playwright and Stylelint configs lived in `tooling/playwright.ts` and
145
+ `tooling/css/stylelint.config.mjs`. Compare its
146
+ [`vite.config.ts`](https://github.com/everton-dgn/boilerplate_solidjs/blob/61454937472d0faa1b4fab985fd7c5da968aa9a6/vite.config.ts)
147
+ with the application's. The Stylelint replacement is an application script that
148
+ calls the packaged policy.
149
+
150
+ <picture>
151
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/fix-once-dark.webp" />
152
+ <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/fix-once-light.webp" />
153
+ <img src="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/fix-once-light.webp" alt="Copied tooling and runtime drift between applications A, B and C. With Solid Base, the apps keep thin configs and consume solid-tooling and solid-runtime. Shared fixes arrive through vp update; application-specific code stays with each app." width="800" />
154
+ </picture>
155
+
156
+ ## Fewer tool versions in each manifest
157
+
158
+ <picture>
159
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/libraries-dark.webp" />
160
+ <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/libraries-light.webp" />
161
+ <img src="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/libraries-light.webp" alt="Direct manifest declarations before and after: boilerplate 28 to 14, client-only 16 to 5, SSR plain 17 to 5, SSR i18n 19 to 7, lint consumer 6 to 3." width="800" />
162
+ </picture>
163
+
164
+ The package installs the build, test, type-check and hook tools it calls, at the
165
+ versions it tests, and runs them from its own installation, including with Bun's
166
+ isolated linker. Your manifest keeps only what your own code imports, plus Vite+
167
+ and its Vite alias.
168
+
169
+ | Application | Direct declarations before | Now | Fewer |
170
+ | ----------------------------------------------------------------------------------------------------------- | -------------------------: | --: | ----: |
171
+ | [Boilerplate copy](https://github.com/everton-dgn/solidjs_base/blob/main/apps/boilerplate/package.json) | 28 | 14 | 14 |
172
+ | [Client-only](https://github.com/everton-dgn/solidjs_base/blob/main/fixtures/apps/client-only/package.json) | 16 | 5 | 11 |
173
+ | [SSR plain](https://github.com/everton-dgn/solidjs_base/blob/main/fixtures/apps/ssr-plain/package.json) | 17 | 5 | 12 |
174
+ | [SSR i18n](https://github.com/everton-dgn/solidjs_base/blob/main/fixtures/apps/ssr-i18n/package.json) | 19 | 7 | 12 |
175
+ | [Lint consumer](https://github.com/everton-dgn/solidjs_base/blob/main/fixtures/consumer/package.json) | 6 | 3 | 3 |
176
+
177
+ Counts combine `dependencies` and `devDependencies`, without overrides or
178
+ transitive packages. The fixture counts leave out the two package tarballs that
179
+ the installed test harness adds.
180
+
181
+ Fewer declarations do not mean a smaller install. The tooling also installs the
182
+ tools of features you may not use: in the
183
+ [measurement of that decision](https://github.com/everton-dgn/solidjs_base/blob/main/docs/ADRs/0027-packaged-tool-dependencies.md),
184
+ the `node_modules` of a fresh client-only application grew by 8.41% with the
185
+ hoisted linker and 8.69% with the isolated one. Build audits check that these
186
+ tools stay out of the application bundles.
187
+
188
+ ## Your application config in one call
189
+
190
+ With the application dependencies installed, `defineSolidApp()` supplies the
191
+ plugins, build settings, tasks, lint, format and three Vitest projects:
164
192
 
165
193
  ```ts
166
- // vite.config.ts
167
194
  import { defineSolidApp } from '@evertondgn/solid-tooling/vite'
168
195
 
169
196
  import { SECURITY_HEADERS } from './src/middleware/securityHeaders/constants.ts'
@@ -171,130 +198,160 @@ import { SECURITY_HEADERS } from './src/middleware/securityHeaders/constants.ts'
171
198
  export default defineSolidApp({
172
199
  root: import.meta.dirname,
173
200
  middleware: './src/middleware/index.ts',
174
- securityHeaders: SECURITY_HEADERS,
175
- staticPaths: ['/favicon/**']
201
+ securityHeaders: SECURITY_HEADERS
176
202
  })
177
203
  ```
178
204
 
179
- | Option | Default | Use |
180
- | ----------------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
181
- | `root` | required | Application folder. `.env`, `.env.test`, `project.inlang/settings.json` and the icon folders resolve from it |
182
- | `securityHeaders` | required | Values of `strict-transport-security`, `x-content-type-options` and `cross-origin-resource-policy` for the static files |
183
- | `middleware` | none | `start.middleware` of the Solid plugin. It has to sit inside the application folder |
184
- | `serverSetup` | `serverFunctions: true` | `serverFunctions.serverSetup` of the Solid plugin |
185
- | `e2e` | production paths | `{ middleware, routes }` for `--mode e2e`: the middleware chain and the route folder of the fixtures, without route types |
186
- | `icons` | none | Own `unplugin-icons` collections, as name to folder |
187
- | `publicAssets` | none | `publicAssets` of Nitro. Each entry needs a `baseURL`, which also gets the static headers |
188
- | `staticPaths` | none | Other static paths that get the headers, such as `/favicon/**` |
189
- | `routeRules` | none | More Nitro route rules, applied after the static ones. A rule with the same path replaces the static one |
190
- | `extend` | none | `(context) => ({ plugins, nitroModules })`, for plugins and Nitro modules the application still owns |
191
- | `tasks` | none | Adds or replaces `vp run` tasks by name |
192
- | `workspace` | none | `{ dependsOn, inputs }` for an application inside a monorepo, whose `bun.lock`, `bunfig.toml` and package builds sit at the root |
193
-
194
- The preset fixes the rest:
195
-
196
- - Plugins, in this order: the `extend` plugins, `unplugin-icons` (Solid
197
- compiler, `aria-hidden` on every icon), `@solidjs/vite-plugin` with SSR and
198
- server functions, `filesystem-routing` with HTTP methods and the route types
199
- in `src/@types/routes.d.ts`, and Nitro with the `vercel` preset. The `dom` and
200
- `browser` test projects get the `extend` plugins, the icons, Solid with server
201
- functions only and the routes. The `test` mode loads no application plugin and
202
- reads `.env.test` into `process.env`.
203
- - The static headers go to `/assets/**`, each `publicAssets` folder, the
204
- `staticPaths`, `/<locale>/**.md` for each locale of
205
- `project.inlang/settings.json`, `/llms.txt`, `/llms-full.txt`, `/sitemap.xml`,
206
- `/sitemaps/pages/**` and `/_markdown/static.json`. The static files leave the
207
- CDN without going through the middleware, so these rules give them the headers
208
- the middleware writes on the other responses.
209
- - `build.manifest` only in the `e2e` mode, which the `TEST_PREBUILT` check of
210
- `./playwright` relies on, and no comments in the output.
211
- - Lightning CSS, with CSS Modules classes named `[content-hash]_[local]`.
212
- - `resolve.tsconfigPaths`, and the dev server and the preview on the `HOST` and
213
- `PORT` of `.env`. The dev server forwards the browser console to the terminal.
214
-
215
- The plugins are imported inside `lazyPlugins`, so `vp lint`, `vp fmt` and
216
- `vp check` read the config without loading them.
217
-
218
- Nitro compiles the route rules with the `preMerge` of h3, which refuses rules
219
- that overlap in part, such as `/pt/blog/**` next to `/pt/**.md`, and falls back
220
- to its simple mode, with the same result and a warning. `defineSolidApp` throws
221
- when a `publicAssets` entry has no `baseURL`, or one with a trailing slash, and
222
- when a static path does not start with `/`. Build, dev and preview fail to load
223
- the plugins when a `publicAssets` folder or a static path sits inside a locale
224
- folder.
225
-
226
- ### `extend`
227
-
228
- `extend` runs inside `lazyPlugins`: once for the application in build, dev and
229
- preview (`target: 'app'`), and once for each test project with a Vite server
230
- (`target: 'test'`). Every load of the config calls it again, so a build also
231
- calls it for the two test projects, whose plugins Vite does not use; keep side
232
- effects out of it. It never runs while Vite+ only reads the config. Return new
233
- plugin instances on each call. The `plugins` go before the fixed ones, and the
234
- `nitroModules` go to Nitro; the `test` target ignores them. One call returns
235
- both, so a value that a plugin and a module share, such as a build id, stays the
236
- same.
237
-
238
- ```ts
239
- export default defineSolidApp({
240
- root: import.meta.dirname,
241
- securityHeaders: SECURITY_HEADERS,
242
- async extend() {
243
- const { i18nPlugin } = await import('./tooling/i18n/i18nPlugin/index.ts')
244
- return { plugins: [i18nPlugin()] }
245
- }
246
- })
247
- ```
248
-
249
- ### Tasks
250
-
251
- | Task | Command |
252
- | ------------------- | ------------------------------------------------------------------------------------------------------- |
253
- | `i18n-generate` | `bun tooling/i18n/generate.ts` |
254
- | `lint-css` | `bun tooling/css/check.ts` |
255
- | `dead-code-project` | `knip-bun --config tooling/knip.ts`, then again with `--strict` |
256
- | `lint-project` | `vp lint`, after `lint-css` |
257
- | `check-project` | `vp fmt --check && vp lint`, after `lint-css` |
258
- | `typecheck-node` | `tsc --project tsconfig.node.json` |
259
- | `test-tooling` | `node --test` over `tooling/**/__tests__` except `tooling/release/`, three workers on CI, with no cache |
260
-
261
- The commands run the scripts of the boilerplate layout. An application without
262
- one of them replaces the task through `tasks`. Knip reads task commands only
263
- from a `run.tasks` block written in `vite.config.ts`, so it does not see these:
264
- list the scripts they run as Knip entries.
265
-
266
- With `workspace`, the cache inputs read `bun.lock`, `bunfig.toml` and the
267
- `inputs` from the workspace root, and `lint-css`, `dead-code-project` and
268
- `typecheck-node` depend on the tasks in `dependsOn`:
269
-
270
- ```ts
271
- workspace: {
272
- dependsOn: ['@evertondgn/solid-tooling#build'],
273
- inputs: ['packages/solid-tooling/dist/**']
274
- }
275
- ```
205
+ That call registers the Solid plugin with SSR and server functions, file-system
206
+ routing with route types, icons, Nitro with the Vercel preset, the headers of
207
+ the static files, Lightning CSS with content-hashed CSS Modules class names, and
208
+ the dev server and preview on the `HOST` and `PORT` of `.env`. You keep your
209
+ middleware and header values. The plugins are package dependencies pinned to the
210
+ tested versions.
211
+
212
+ <picture>
213
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/only-what-you-use-dark.webp" />
214
+ <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/only-what-you-use-light.webp" />
215
+ <img src="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/only-what-you-use-light.webp" alt="A client-only vite.config.ts with defineSolidApp, root and ssr: false. No translation project, theme imports, env.ts, fonts option or SEO are needed, and the build has client output only, with no Nitro or server bundle." width="800" />
216
+ </picture>
217
+
218
+ Each feature turns on only when the application uses it:
219
+
220
+ | Feature | Selection |
221
+ | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
222
+ | Client-only build | Set `ssr: false` and omit SSR options such as `middleware` and `securityHeaders`; the build has no Nitro or server bundle and accepts a relative `./` base |
223
+ | Publication headers | Set `seo.markdown`, `seo.llms` and `seo.sitemap` one by one; `seo.llmsFull` also needs `seo.llms` and `seo.markdown.static` |
224
+ | Translations | `project.inlang/settings.json` enables the generation task and browser test setup; the lint plugin loads only where Paraglide resolves |
225
+
226
+ When the standard does not fit, change it without a fork: `extend` adds plugins
227
+ and Nitro modules, `tasks` adds or replaces a `vp run` task, `routeRules`,
228
+ `publicAssets` and `staticPaths` adjust Nitro and the static headers,
229
+ `workspace` places the app in a monorepo, and `mergeConfig` merges anything else
230
+ over the result.
231
+
232
+ The preset also defines the `vp run` tasks of the boilerplate layout:
233
+ `i18n-generate`, `lint-css`, `dead-code-project`, `lint-project`,
234
+ `check-project`, `typecheck-node` and `test-tooling`. The `knip`, `tsc`,
235
+ `playwright`, `lefthook` and `commitlint` tasks run the package-owned binaries,
236
+ including with an isolated installation. See
237
+ [all options and extension points](https://github.com/everton-dgn/solidjs_base/blob/main/docs/tooling/app-config.md)
238
+ and the
239
+ [known limits](https://github.com/everton-dgn/solidjs_base/blob/main/docs/tooling/limits.md#known-limits).
240
+
241
+ ## Lint rules that know your project
242
+
243
+ The preset turns on the Oxlint plugins `import`, `jsx-a11y`, `node`, `oxc`,
244
+ `promise`, `typescript`, `unicorn` and `vitest`, the `solid` and `vite-plus`
245
+ plugins and three plugins of this package, plus `paraglide` when it resolves.
246
+ Type-aware rules and type checking are on. Warnings fail the run, and an unused
247
+ disable directive is an error. Every rule is listed by name, so an Oxlint update
248
+ does not add rules on its own.
249
+
250
+ The rules of this package catch what a generic preset cannot see:
251
+
252
+ | Rule | What it enforces |
253
+ | ---------------------------- | --------------------------------------------------------------------------------------------------- |
254
+ | `project/css-modules-import` | A CSS Module is imported as the default binding `S` |
255
+ | `project/css-filename` | A CSS file is named `styles.module.css`, or `camelCase.css` inside `src/theme/` |
256
+ | `project/test-filename` | A test sits in `__tests__` next to its module, named after it and its environment |
257
+ | `project/no-export-list` | Exports sit on the declaration |
258
+ | `project/seo-module` | A route `seo.ts` exports only `seo` and imports no route values |
259
+ | `architecture/layer-imports` | Lower layers do not import upper ones, and production code does not import tests or `tooling/` |
260
+ | `backend/*` | No `console`, `fetch` only in `requestJson`, no Node transports, no `node_modules` paths in imports |
261
+
262
+ <picture>
263
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/layers-dark.webp" />
264
+ <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/layers-light.webp" />
265
+ <img src="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/layers-light.webp" alt="architecture/layer-imports: imports only point down, from routes and entry files through organisms, molecules, atoms and primitives to the base layer. Below, vp lint on a copy of the boilerplate where the PageBadge atom imports a molecule: src/components/atoms/PageBadge/index.tsx:3:1: error architecture(layer-imports): Layer atoms must not depend on molecules (src/components/molecules/PageHero/index.tsx). See the layer rules in docs/architecture.md. Production code cannot import tests or tooling/ either." width="800" />
266
+ </picture>
267
+
268
+ The layer rule keeps the dependency direction of the UI. The base layer
269
+ (`@types`, `constants`, `helpers`, `data`, `infra`, `theme`, `i18n` and
270
+ `paraglide/`) imports no primitives, components, routes or entry files.
271
+ Primitives import no components or routes. Atoms do not import molecules or
272
+ organisms, and molecules do not import organisms. An import in the wrong
273
+ direction fails `vp lint` before review.
274
+
275
+ The backend rules keep outgoing requests in one module. Inside `src/`, `fetch`,
276
+ `XMLHttpRequest`, `WebSocket` and `EventSource` are refused outside
277
+ `src/infra/server/requestJson/index.ts`, `console` is refused, and the server
278
+ error configurator can only be imported in its setup file. See
279
+ [all rules and customization](https://github.com/everton-dgn/solidjs_base/blob/main/docs/tooling/lint.md).
280
+
281
+ ## Tests in the environment each one needs
282
+
283
+ <picture>
284
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/vitest-projects-dark.webp" />
285
+ <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/vitest-projects-light.webp" />
286
+ <img src="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/vitest-projects-light.webp" alt="The three Vitest projects of solidTest with their file globs and environments. In apps/boilerplate, 185 tests passed: 68 node tests in 12 files, 87 dom tests with happy-dom in 13 files and 30 browser tests with Chromium in 7 files. Coverage thresholds: 80% for lines, functions and statements, 75% for branches." width="800" />
287
+ </picture>
288
+
289
+ The file name picks the environment, so a server test, a component test and a
290
+ real-browser test live in the same repository with no extra config. In the
291
+ boilerplate, `vp test run` passed 185 tests in 32 files: 68 `node` tests in 12
292
+ files, 87 `dom` in 13 and 30 `browser` in 7.
276
293
 
277
- ### Changing the result
294
+ | Project | Files | Environment |
295
+ | --------- | -------------------------------- | ------------------------------------------------ |
296
+ | `node` | `src/**/*.node.test.{ts,tsx}` | Node, with stand-ins for the build-only modules |
297
+ | `dom` | `src/**/*.dom.test.{ts,tsx}` | happy-dom, at the URL of `domUrl` |
298
+ | `browser` | `src/**/*.browser.test.{ts,tsx}` | Chromium through Playwright, with the CSS loaded |
278
299
 
279
- Wrap the result in `defineConfig` and merge what the application needs. Vite+
280
- removes the plugins it injected before it injects them again, so they are not
281
- duplicated:
300
+ The preset sets coverage thresholds of 80% for lines, functions and statements
301
+ and 75% for branches, the reporters and the CI workers. It resets the Paraglide
302
+ locale before each browser test and resolves packages that ship TypeScript
303
+ source, such as the runtime, under the `solid` condition. `defineSolidApp`
304
+ already calls `solidTest`; an application with its own Vite config imports it
305
+ from `./vitest`. See
306
+ [Vitest and Playwright](https://github.com/everton-dgn/solidjs_base/blob/main/docs/tooling/testing.md).
307
+
308
+ ## End-to-end tests against a real build
309
+
310
+ <picture>
311
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/e2e-coverage-dark.webp" />
312
+ <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/e2e-coverage-light.webp" />
313
+ <img src="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/e2e-coverage-light.webp" alt="The Playwright inventory of apps/boilerplate: 319 E2E cases across 15 areas, 164 in the E2E build and 155 in the production build. AgentReadiness 77, I18n 40, Home 36, SeoIndexes 29, Markdown 27, BackendError 25, BuildOutput 22, OutsideError 18, Hydration 18, UiKit 8, OutdatedPage 7, StructuredData 6, FragmentCss 2, LateRedirect 2 and NotFound 2. Enumerated with playwright test --list, without running them." width="800" />
314
+ </picture>
315
+
316
+ `definePlaywrightConfig` reads `BASE_URL_TEST` from `.env.test`, builds the
317
+ application, starts the preview and runs the suite against it: `e2e` mode for
318
+ the fixture routes, or the production build with `TEST_PRODUCTION_ROUTES=true`.
319
+ `TEST_PREBUILT=true` reuses an existing production build. Serial suites run in
320
+ their own projects: `markdown` with one worker, `publicationLoad` alone against
321
+ two servers, and `serverLogs` last, as the teardown of the others.
322
+
323
+ When the suite ends, `./playwright-teardown` checks the server log for private
324
+ markers, so a test run also fails when a server error leaks its details into the
325
+ log.
326
+
327
+ In the boilerplate, `vp run playwright test --list` counts 319 cases in 15 page
328
+ areas, 164 in 26 files for the E2E build and 155 in 12 files for the production
329
+ build; the figure above breaks them down by area.
282
330
 
283
331
  ```ts
284
- import { defineSolidApp } from '@evertondgn/solid-tooling/vite'
285
- import { defineConfig, mergeConfig } from 'vite-plus'
286
-
287
- const app = defineSolidApp({ root: import.meta.dirname, securityHeaders })
332
+ // playwright.config.ts
333
+ import { definePlaywrightConfig } from '@evertondgn/solid-tooling/playwright'
288
334
 
289
- export default defineConfig(env =>
290
- mergeConfig(app(env), { server: { strictPort: true } })
291
- )
335
+ export default definePlaywrightConfig({ root: import.meta.dirname })
292
336
  ```
293
337
 
294
- ## Knip
338
+ ## Unused files, exports and dependencies
339
+
340
+ <picture>
341
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/dead-code-dark.webp" />
342
+ <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/dead-code-light.webp" />
343
+ <img src="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/dead-code-light.webp" alt="Knip output on a copy of the boilerplate with an unused file, dependency and export added. Unused files (1): src/helpers/formatPrice/index.ts. Unused dependencies (1): date-fns at package.json:45:6. Unused exports (1): THEME_COOKIE_NAME at src/constants/theme.ts:6:14. The first pass fails, so vp run dead-code-project stops before the --strict pass." width="800" />
344
+ </picture>
295
345
 
296
- `defineKnipConfig` returns the Knip config of the application as a function, the
297
- form Knip accepts as the default export of its config file:
346
+ A helper nobody calls, an export nobody imports and a dependency nobody uses
347
+ stop the push, and the report says where each one is. `defineKnipConfig` from
348
+ `./knip` gives Knip the entries of a Solid app: the server and client entries,
349
+ the middleware, `env.ts`, the Paraglide runtime, and every page and HTTP handler
350
+ that file-system routing finds. It turns off Knip's own routing plugin, which
351
+ takes every file of `src/routes/` as an entry, so an unused helper next to a
352
+ route is reported. Configuration hints are errors, and
353
+ `vp run dead-code-project` runs Knip twice, the second time with `--strict`. The
354
+ `pre-push` hook below runs it before the code leaves your machine.
298
355
 
299
356
  ```ts
300
357
  // tooling/knip.ts
@@ -308,124 +365,50 @@ export default defineKnipConfig({
308
365
  })
309
366
  ```
310
367
 
311
- Run it with `knip-bun --config tooling/knip.ts`, then with `--strict`. The
312
- config lists:
313
-
314
- - `src/entry-server.tsx`, `src/entry-client.tsx`, `src/middleware/index.ts`,
315
- `env.ts` and `paraglide/runtime.js` as production entries. The Solid plugin
316
- and the Paraglide runtime load them by path, and Knip does not read the Solid
317
- plugin when `vite.config.ts` imports it with `import()`, as `./vite` does. An
318
- application that imports `solid()` statically, with `start`, passes
319
- `solidPluginImport: 'static'`: Knip's Solid plugin then finds the first four
320
- itself, and listing them again would be a configuration hint.
321
- - Every page and HTTP handler that `filesystem-routing` finds in `src/routes/`,
322
- as production entries, and in `src/tests/fixtures/routes/` and
323
- `src/tests/fixtures/e2e/routes/`, as test entries. The `filesystem-routing`
324
- plugin of Knip is turned off, because it takes every file of `src/routes/` as
325
- an entry and would hide unused helpers next to the routes.
326
- - `src/**/*.{ts,tsx}` and `tooling/**/*.ts` as the project, with the tests out
327
- of the production files.
328
-
329
- The config turns on `treatConfigHintsAsErrors`, and Knip reports an entry or a
330
- project pattern without files as a configuration hint. So each default above
331
- enters only when the application has the file or the folder.
332
-
333
- | Option | Default | Use |
334
- | -------------------- | ----------------- | -------------------------------------------------------------- |
335
- | `root` | required | The application folder |
336
- | `configFile` | `tooling/knip.ts` | Path of the config file, relative to `root` |
337
- | `entry` | none | More entries, such as a public API that only test fixtures use |
338
- | `solidPluginImport` | `'lazy'` | `'static'` when `vite.config.ts` imports `solid()` statically |
339
- | `ignoreBinaries` | none | Passed to Knip as is |
340
- | `ignoreDependencies` | none | Passed to Knip as is |
341
- | `ignoreIssues` | none | Passed to Knip, merged with the entry for `configFile` |
342
- | `exclude` | none | Issue types to leave out, such as `catalogReferences` |
343
-
344
- Knip adds the file given to `--config` to the entries in production mode, and
345
- `--strict` only accepts `dependencies` there, so it reports this package as an
346
- unlisted dependency of the config file. The config ignores `unlisted` issues in
347
- `configFile` only: an import of this package from `src/` is still reported. An
348
- application that keeps the config somewhere else passes its path in
349
- `configFile`.
350
-
351
- Nothing that depends on the application is a default. An ignore that matches
352
- nothing is a configuration hint too, so `server-only` and the binaries of the
353
- scripts are options. For a key without an option, wrap the function:
354
-
355
- ```ts
356
- const configure = defineKnipConfig({ root })
357
-
358
- export default async function configureKnip() {
359
- return { ...(await configure()), ignoreUnresolved: ['virtual:example'] }
360
- }
361
- ```
362
-
363
- ## CSS lint
364
-
365
- `checkCss(root)` lints `src/**/*.css` with Stylelint and the CSS policy of the
366
- boilerplate, and checks the name of every CSS file in `src/` as
367
- `project/css-filename` does for imports: `styles.module.css`, or `camelCase.css`
368
- inside `src/theme/`. It returns the report and whether anything failed:
369
-
370
- ```ts
371
- // tooling/css/check.ts
372
- import path from 'node:path'
373
- import process from 'node:process'
374
-
375
- import { checkCss } from '@evertondgn/solid-tooling/stylelint'
376
-
377
- const { report, hasErrors } = await checkCss(
378
- path.resolve(import.meta.dirname, '../..')
379
- )
380
-
381
- process.stdout.write(report)
382
- if (hasErrors) process.exitCode = 1
383
- ```
384
-
385
- `defineStylelintConfig(root)` returns the Stylelint config that `checkCss` uses.
386
- The policy:
387
-
388
- - Classes use snake_case, nesting goes at most two levels deep, and adjacent
389
- state selectors and media queries are nested (`stylelint-use-nesting`).
390
- - Unknown or invalid properties, values, units, selectors, at-rules and media
391
- features are errors, as are deprecated and unmatchable selectors.
392
- - Outside `src/theme/`, colors, gradients, radii, shadows, font sizes and
393
- weights and `z-index` come from tokens with `var()`, from the matching token
394
- family. Literal colors, color functions and system colors are errors.
395
- - Inside `src/theme/`, the token rules are off, so the theme can define the
396
- values with `oklch()`, `oklab()`, `color-mix()`, `light-dark()` and system
397
- colors. Hex, named colors and the `rgb()`, `hsl()`, `hwb()`, `lab()`, `lch()`
398
- and `color()` functions are still errors there.
399
- - `var()` and animation names resolve against `src/theme/tokens/**/*.css` and
400
- `src/theme/class/animation.css`. Stylelint stops the whole run when a
401
- reference matches no file, so each one is passed only when it exists.
402
- - An exception needs a `stylelint-disable` comment with the reason after `--`.
403
-
404
- `stylelint` and `stylelint-use-nesting` are dependencies of this package. The
405
- config passes the plugin by a path resolved from the package, because Stylelint
406
- looks up a plugin name from the application and its working directory, where it
407
- is not installed.
368
+ ## A CSS policy built on tokens
369
+
370
+ <picture>
371
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/css-tokens-dark.webp" />
372
+ <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/css-tokens-light.webp" />
373
+ <img src="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/css-tokens-light.webp" alt="In styles.module.css, color: #0b1525 is refused with the Stylelint message to use only var() tokens, and color: var(--color-foreground) passes. The token files of the boilerplate define 36 color, 11 font, 7 shadow, 6 grid, 5 radius and 3 z-index custom properties." width="800" />
374
+ </picture>
375
+
376
+ Outside `src/theme/`, colors, gradients, radii, shadows, font sizes and weights
377
+ and `z-index` must come from tokens with `var()`, from the matching token
378
+ family. A literal color is an error, so the palette is defined in the theme
379
+ files. Inside `src/theme/`, the theme defines the values with `oklch()`,
380
+ `oklab()`, `color-mix()` and `light-dark()`.
381
+
382
+ The policy also requires snake_case classes and at most two levels of nesting,
383
+ refuses unknown properties, values, units and selectors, checks that each
384
+ `var()` resolves to a token file, and accepts an exception only with a
385
+ `stylelint-disable` comment that gives the reason. `checkCss(root)` from
386
+ `./stylelint` runs it and checks CSS file names; the boilerplate's caller has 11
387
+ lines. The boilerplate's token files define 36 color, 11 font, 7 shadow, 6 grid,
388
+ 5 radius and 3 z-index custom properties. See
389
+ [Knip and CSS](https://github.com/everton-dgn/solidjs_base/blob/main/docs/tooling/knip-and-css.md).
408
390
 
409
391
  ## Commit messages and Git hooks
410
392
 
411
- `./commitlint` is the commitlint config: the Conventional Commits rules of
412
- `@commitlint/config-conventional`, with the header limited to 100 characters, a
413
- blank line required before the footer, and a warning for body and footer lines
414
- over 100 characters. The application installs `@commitlint/cli` and points its
415
- config to the package:
393
+ <picture>
394
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/git-hooks-dark.webp" />
395
+ <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/git-hooks-light.webp" />
396
+ <img src="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/git-hooks-light.webp" alt="The shared lefthook.yml: commit-msg runs commitlint with Conventional Commits and a 100-character header; pre-commit runs check:fix on the staged files and the kingfisher secret scan when installed, and skips merge and rebase; pre-push runs check:ci, typecheck:node and test:ci, then test:tooling and dead-code in parallel, and stops at the first failure." width="800" />
397
+ </picture>
416
398
 
417
- ```json
418
- // .commitlintrc
419
- { "extends": ["@evertondgn/solid-tooling/commitlint"] }
420
- ```
399
+ The shared hooks check a change before it reaches the remote:
421
400
 
422
- `./lefthook.yml` holds the Git hooks. The application installs `lefthook`,
423
- allows its install script and extends the file from its own `.lefthook.yml`:
401
+ | Hook | Jobs |
402
+ | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
403
+ | `commit-msg` | `commitlint` on the message |
404
+ | `pre-commit` | `check:fix` on the staged files, staged again after the fix, and `scan:secrets --staged` when `kingfisher` is installed. Skipped in merge and rebase |
405
+ | `pre-push` | `check:ci`, `typecheck:node` and `test:ci` in order, then `test:tooling` and `dead-code` together; the first failure stops |
424
406
 
425
- ```json
426
- // package.json
427
- { "trustedDependencies": ["lefthook"] }
428
- ```
407
+ `./commitlint` applies the Conventional Commits rules with the header limited to
408
+ 100 characters and a blank line before the footer. Lefthook and the commitlint
409
+ CLI come with the package, so the application declares neither. Extend the
410
+ shared file from `.lefthook.yml`, and override a shared job by name in
411
+ `lefthook-local.yml`, committed for everyone or kept on one machine:
429
412
 
430
413
  ```yaml
431
414
  # .lefthook.yml
@@ -433,268 +416,230 @@ extends:
433
416
  - node_modules/@evertondgn/solid-tooling/lefthook.yml
434
417
  ```
435
418
 
436
- | Hook | Jobs |
437
- | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
438
- | `commit-msg` | `commitlint` on the message |
439
- | `pre-commit` | `check:fix` on the staged files, which are staged again after the fix, and `scan:secrets --staged` when `kingfisher` is installed. Skipped in merge and rebase |
440
- | `pre-push` | `check:ci`, `typecheck:node` and `test:ci` in order, then `test:tooling` and `dead-code` together. The first failure stops the rest |
441
-
442
- The jobs call `package.json` scripts by name, so the application defines
443
- `check:fix`, `scan:secrets`, `check:ci`, `typecheck:node`, `test:ci`,
444
- `test:tooling` and `dead-code`.
445
-
446
- Lefthook applies the files in `extends` over `.lefthook.yml`, so:
447
-
448
- - A job added in `.lefthook.yml` runs before the shared jobs of the same hook.
449
- - A job declared there with the name of a shared job does not change it.
450
- - To change or skip a shared job, declare a job with the same name in
451
- `lefthook-local.yml`, which Lefthook applies last, also inside a group. The
452
- rest keeps its order, and new jobs in that file run after the shared ones.
453
- Commit the file to change the job for everyone, or keep it out of Git to
454
- change it on one machine.
455
-
456
- Create `.lefthook.yml` before the first install. Without it, the install script
457
- of Lefthook writes a `lefthook.yml` of its own, which Lefthook reads first.
458
-
459
- ## Project layout the preset expects
460
-
461
- The rules match file paths relative to the folder where lint runs, so run it
462
- from the application root. They know these locations:
463
-
464
- | Path | Meaning |
465
- | --------------------------------------------- | -------------------------------------------------------- |
466
- | `src/` | Application code. The `@/` alias points here |
467
- | `src/routes/` | Pages. Each page may have a `seo.ts` module |
468
- | `src/components/{atoms,molecules,organisms}/` | UI layers |
469
- | `src/primitives/` | Shared primitives |
470
- | `src/infra/server/` | Backend adapters and server error handling |
471
- | `src/theme/` | Global CSS |
472
- | `src/tests/pages/<Page>/` | End-to-end tests, one folder per page |
473
- | `paraglide/` | Generated i18n modules. The `@paraglide/` alias |
474
- | `tooling/` | Project scripts and their tests, written for `node:test` |
475
-
476
- An application with another layout can still use the preset, but it has to
477
- override the rules that do not fit. See
478
- [Changing the preset](#changing-the-preset).
479
-
480
- ## What the lint preset turns on
481
-
482
- - Oxlint plugins: `import`, `jsx-a11y`, `node`, `oxc`, `promise`, `typescript`,
483
- `unicorn` and `vitest`.
484
- - JavaScript plugins: `solid`, `paraglide`, `vite-plus` and the three plugins of
485
- this package (`project`, `backend`, `architecture`).
486
- - Type-aware rules and type checking (`typeAware`, `typeCheck`).
487
- - Warnings fail the run (`denyWarnings`), and an unused disable directive is an
488
- error.
489
-
490
- Every rule is listed by name in the preset. No Oxlint category is enabled, so an
491
- Oxlint update does not add rules on its own.
492
-
493
- ### Rules of this package
494
-
495
- | Rule | What it enforces |
496
- | ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
497
- | `project/css-modules-import` | A CSS Module is imported as the default binding `S` |
498
- | `project/css-filename` | A CSS file imported from `src/` is named `styles.module.css`, or `camelCase.css` inside `src/theme/` |
499
- | `project/test-filename` | A test file sits in `__tests__` next to its module and is named after it, plus the environment: `Button/__tests__/Button.dom.test.tsx` |
500
- | `project/no-export-list` | Exports sit on the declaration. `export { a, b }` is only allowed with `from` |
501
- | `project/seo-module` | A route `seo.ts` exports only `seo`. It has no relative value import, no value import from `src/routes/`, no computed `import()` and no `import.meta.glob` |
502
- | `backend/static-solid-imports` | No `import()` of `@solidjs/web` or its subpaths |
503
- | `backend/static-render-integrity-imports` | No `import()` of the `renderIntegrity` module |
504
- | `architecture/layer-imports` | Lower layers do not import upper ones, and production code does not import tests or `tooling/` |
505
-
506
- These rules read import specifiers as text. They resolve relative paths and the
507
- `@/`, `@paraglide/` and `/src/` prefixes. An `import()` with a computed path and
508
- any other alias are not checked, except in `seo.ts`, where a computed `import()`
509
- is an error.
510
-
511
- `architecture/layer-imports` checks these directions:
512
-
513
- - The base layer (`src/@types`, `constants`, `helpers`, `data`, `infra`,
514
- `theme`, `i18n` and `paraglide/`) does not import primitives, components,
515
- routes or entry files.
516
- - `src/primitives` does not import components, routes or entry files.
517
- - Atoms do not import molecules or organisms, and molecules do not import
518
- organisms.
519
- - No production file imports a test or `tooling/`.
520
-
521
- The entry files are `App`, `Document`, `entry-server`, `entry-client`, `router`
522
- and `middleware`, at the root of `src/`.
523
-
524
- `project/test-filename` accepts these names:
525
-
526
- - `Name.<environment>.test.ts(x)` in `src/`, where the environment is `node`,
527
- `dom`, `browser` or `e2e`.
528
- - `Name.test-d.ts(x)` in `src/`, for type tests.
529
- - `Name.test.ts` in `tooling/`.
530
-
531
- `Name` is the folder that holds `__tests__`. A qualifier may follow it:
532
- `Button.focus.dom.test.tsx`. End-to-end tests are the exception: they live in
533
- `src/tests/pages/<Page>/` and are named after the page.
534
-
535
- ### Backend restrictions
536
-
537
- Inside `src/`, the preset also restricts what code can reach:
538
-
539
- - `console` is not allowed. `src/infra/server/logServerFailure/index.ts` is the
540
- only module that may call `console.error`.
541
- - `fetch`, `XMLHttpRequest`, `WebSocket` and `EventSource` are not allowed.
542
- `src/infra/server/requestJson/index.ts` is the only module that may call
543
- `fetch`.
544
- - The Node transports (`http`, `https`, `http2`, `net`, `tls`, `undici`) cannot
545
- be imported.
546
- - `markSafeError` and `SAFE_ERROR` can only be imported in
547
- `src/infra/server/publicErrors/index.ts`.
548
- - `configureServerErrors` from `@solidjs/web`, the
549
- `@solidjs/web/server-functions` subpath and `observeRenderFailures` from the
550
- application's `renderIntegrity` module can only be imported in
551
- `src/infra/server/configureServerErrors/index.ts`.
552
- - A relative import goes up at most three levels. Use `@/` beyond that.
553
-
554
- Test files and `.d.ts` files are exempt from all of these except the relative
555
- import limit.
556
-
557
- ## Changing the preset
558
-
559
- `lint` and `fmt` are plain objects. Spread them and replace what the application
560
- needs:
419
+ See
420
+ [Commit messages and Git hooks](https://github.com/everton-dgn/solidjs_base/blob/main/docs/tooling/commits-and-hooks.md).
421
+
422
+ ## Translations, icons and fonts as data
423
+
424
+ <picture>
425
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/i18n-dark.webp" />
426
+ <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/i18n-light.webp" />
427
+ <img src="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/i18n-light.webp" alt="A project.inlang settings excerpt with base locale fr and locales fr, de, it and ja. Catalogs stay beside each feature; validation, compilation and localization produce translated pages, URL helpers, redirects, cookies and navigation. Without a translation project, URLs stay unprefixed." width="800" />
428
+ </picture>
429
+
430
+ With `project.inlang/settings.json`, the packaged generator reads the catalogs
431
+ beside each feature, `src/**/i18n/<locale>.json`, and compiles Paraglide in
432
+ development, builds and tests. A missing translation, a duplicate ID or an
433
+ invalid message fails with the file and the key. The locales come from your
434
+ settings, with no fixed set. Without the file, no generator or i18n plugin
435
+ enters the build.
436
+
437
+ <picture>
438
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/icons-dark.webp" />
439
+ <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/icons-light.webp" />
440
+ <img src="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/icons-light.webp" alt="solid.svg in src/assets/images becomes a Solid component. The icons option maps the my-images collection to that folder, the component imports ~icons/my-images/solid, and the preset adds aria-hidden=true; the icon-only control supplies its accessible label." width="800" />
441
+ </picture>
442
+
443
+ Icons from Iconify collections, or your own SVG folders mapped with the `icons`
444
+ option, become Solid components through `unplugin-icons`, with `aria-hidden` on
445
+ every icon. The application does not declare the plugin.
446
+
447
+ <picture>
448
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/fonts-dark.webp" />
449
+ <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/fonts-light.webp" />
450
+ <img src="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/fonts-light.webp" alt="One font asset list in defineSolidApp produces external @font-face CSS with content-hashed URLs, deduplicated files and matching preload links. SSR renders FontLinks in the Document; client-only apps receive the links in index.html; without fonts, nothing is generated." width="800" />
451
+ </picture>
452
+
453
+ Pass a data-only `fonts` array to `defineSolidApp` to generate external
454
+ `@font-face` CSS and matching preloads for application-owned files. SSR uses
455
+ `FontLinks` from `@evertondgn/solid-runtime/fonts/font-links` in the Document
456
+ head; client-only applications receive both links in `index.html`, also with a
457
+ relative `./` base for `file://` and app shells. Leave `fonts` out and nothing
458
+ is generated. Typography tokens stay in the app. See the
459
+ [font API and migration examples](https://github.com/everton-dgn/solidjs_base/blob/main/docs/tooling/fonts.md).
460
+
461
+ ## SEO configured as data
462
+
463
+ <picture>
464
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/seo-dark.webp" />
465
+ <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/seo-light.webp" />
466
+ <img src="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/seo-light.webp" alt="Three independent SEO parts. Head: createSeo with inherited route metadata, canonical and social tags and escaped JSON-LD. Robots: an explicit route with a serializable crawl policy. Sitemap: createSitemapRoutes with arrays or bounded partition reads under the shared publication controller. All work with or without i18n." width="800" />
467
+ </picture>
468
+
469
+ Robots, sitemap headers, Markdown and the llms indexes are options of
470
+ `defineSolidApp` written as plain data, validated when the config loads. None of
471
+ them turns on another. Only `llmsFull` has prerequisites: the full file is built
472
+ from the static Markdown, so it needs `llms` and `markdown: { static: true }`,
473
+ and the config fails to load without them. The site URL becomes required once
474
+ `seo` is set, as an HTTP(S) origin with no path, query, fragment or credentials;
475
+ leave `seo` out and no origin is read.
476
+
477
+ For SSR,
478
+ `seo: { robots: { groups, sitemapPaths, cacheControl }, sitemap: true }`
479
+ configures the runtime robots route using serializable data. `originEnv`
480
+ defaults to `VITE_SITE_URL`. The preset validates it when SEO is enabled or the
481
+ runtime origin entry is imported.
482
+
483
+ Pass `site` directly to the runtime `createSeo` primitive. A meta/JSON-LD-only
484
+ application can omit `seo`, provide its own `site.url` and omit robots entirely.
485
+ Client-only applications omit both the option and runtime SEO imports. There are
486
+ no `siteModule`, `sourcesModule` or `editorialModule` path options.
487
+ `seo.sitemap` controls only the static `/sitemap.xml` and `/sitemaps/pages/**`
488
+ header rules. Install the runtime factory in explicit application route files,
489
+ and list the root in `robots.sitemapPaths` to advertise it. To remove sitemap,
490
+ remove its routes and server binding, omit its array metadata, omit
491
+ `sitemapPaths`, and omit or disable `seo.sitemap`. Robots can stay enabled. The
492
+ preset does not discover executable source registries or install routes. See the
493
+ [runtime SEO API](https://github.com/everton-dgn/solidjs_base/blob/main/packages/solid-runtime/README.md#optional-seo)
494
+ for factories, the one-line route and examples with and without translations.
495
+
496
+ ### Publication artifacts
497
+
498
+ Crawlers get files, not renders. The build writes what you turn on: a `.md` copy
499
+ of each opted-in page with `markdown: { static: true }`, `llms.txt` with `llms`,
500
+ `llms-full.txt` with `llmsFull`, and the sitemap XML with
501
+ `sitemap: { static: true }`. A bad artifact fails the build instead of reaching
502
+ production: a `.md` file that is empty, not UTF-8 or still an HTML document, an
503
+ index or sitemap file over its byte budget, an `llms.txt` whose link to
504
+ `llms-full.txt` disagrees with the files built, a static page that resolves to
505
+ another route, or a Markdown list or sitemap inventory from a different build
506
+ than the SSR bundle. Your config needs no `extend` callback for this.
507
+ `publication/read-llms-index` exposes the compiled index reader for artifact
508
+ checks in Node. See
509
+ [ADR 0034](https://github.com/everton-dgn/solidjs_base/blob/main/docs/ADRs/0034-runtime-markdown-publication.md)
510
+ for the feature options, defaults, disabled-resource behavior and scale.
511
+
512
+ ## Optional environment validation
513
+
514
+ <picture>
515
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/env-dark.webp" />
516
+ <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/env-light.webp" />
517
+ <img src="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/env-light.webp" alt="An optional env.ts spreads serverEnv: HOST string or undefined, PORT number or undefined, BASE_URL_TEST string or undefined. Server virtual imports validate at initialization and fail the build from client code. Public client values validate at build time with inferred types and no validator shipped. No env requirements? Omit env.ts." width="800" />
518
+ </picture>
519
+
520
+ A missing or malformed value fails where it enters: when the server code that
521
+ imports it loads, or at build time for a public value. An SSR application can
522
+ add `env.ts` with the preset's optional validators:
561
523
 
562
524
  ```ts
563
- // vite.config.ts
564
- import { fmt } from '@evertondgn/solid-tooling/fmt'
565
- import { lint } from '@evertondgn/solid-tooling/lint'
566
- import { defineConfig } from 'vite-plus'
525
+ import { serverEnv } from '@evertondgn/solid-runtime/env'
567
526
 
568
- export default defineConfig({
569
- fmt,
570
- lint: {
571
- ...lint,
572
- ignorePatterns: [...(lint.ignorePatterns ?? []), 'generated/**'],
573
- rules: { ...lint.rules, 'eslint/max-params': 'off' }
574
- }
575
- })
576
- ```
577
-
578
- Do not replace `lint.jsPlugins`. It registers the JavaScript plugins listed
579
- above, and a rule whose plugin is missing from it fails to resolve.
527
+ const env = { server: { ...serverEnv } }
580
528
 
581
- To turn a rule off for some files only, add an entry to `lint.overrides` and
582
- keep the existing ones:
583
-
584
- ```ts
585
- overrides: [
586
- ...(lint.overrides ?? []),
587
- {
588
- files: ['src/tests/pages/Checkout/**'],
589
- rules: { 'project/test-filename': 'off' }
590
- }
591
- ]
529
+ export default env
592
530
  ```
593
531
 
532
+ `HOST`, `PORT` and `BASE_URL_TEST` are optional; `PORT` produces a number.
533
+ Declare the runtime package in `dependencies` and keep the tooling in
534
+ `devDependencies`: Knip treats `env.ts` as production code. Extend `server` and
535
+ `client` with the application's own Standard Schema validators. The runtime
536
+ supplies Valibot for its fragment; declare a validator library in the
537
+ application only when its own code imports it. The optional SEO configuration
538
+ validates `VITE_SITE_URL` separately. An application without `env.ts` keeps
539
+ working, including client-only applications with no site URL.
540
+
541
+ The Solid plugin generates inferred types and rejects server env imports in the
542
+ client graph. The server virtual module bundles the fragment and validator; env
543
+ validation adds neither to the client. See the
544
+ [environment guide](https://github.com/everton-dgn/solidjs_base/blob/main/docs/tooling/app-config.md#optional-environment-validation)
545
+ for extension examples, boot validation and type generation.
546
+
547
+ ## How it is verified
548
+
549
+ <picture>
550
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/verified-dark.webp" />
551
+ <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/verified-light.webp" />
552
+ <img src="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/verified-light.webp" alt="Current revision: 1,186 Vitest tests passed, 1,001 in the runtime and 185 in the boilerplate app; 319 E2E cases listed, 164 for the E2E build and 155 for the production build. Before each release the tarball is installed in a separate application and its exports and lint rules are checked. Release workflows publish with trusted publishing and signed npm provenance, with no stored npm token." width="800" />
553
+ </picture>
554
+
555
+ At this revision, 1,186 Vitest tests passed: 1,001 in 100 files of the runtime
556
+ package and 185 in 32 files of the boilerplate app. `playwright test --list`
557
+ counts 319 E2E cases in the app, 164 for the E2E build and 155 for the
558
+ production build.
559
+
560
+ - The package's own tests, `vp run test`, run with `node --test` against
561
+ fixtures in temporary folders: 268 `test` and `it` calls in 39 files, counted
562
+ in source.
563
+ - The tarball test packs the package, installs it in a separate application
564
+ outside the workspace and checks that lint applies the plugins, that the
565
+ application extends the shared hooks and commitlint config, and that the
566
+ fixture declares the workspace versions.
567
+ - The installed scenarios install the packed tooling and runtime in the
568
+ [three fixture applications](https://github.com/everton-dgn/solidjs_base/tree/main/fixtures/apps)
569
+ with Bun's `hoisted` and `isolated` linkers and build them. The matrix
570
+ enumerates 14 application cases: 3 client-only, 9 SSR and 2 translated SSR.
571
+ They fail when a disabled feature leaves its modules in the bundle.
572
+ - The runtime package is linted with this package's preset, loaded from its
573
+ sources.
574
+
575
+ `vp run validate` at the root runs the build, checks, dead code, package tests,
576
+ tarball tests and installed scenarios, as the publish job does. CI selects jobs
577
+ by changed paths, and a package change runs the packages and the complete
578
+ boilerplate app. The workflow publishes to npm through trusted publishing, with
579
+ signed provenance and no stored npm token. See the
580
+ [CI coverage](https://github.com/everton-dgn/solidjs_base/blob/main/docs/development.md#ci)
581
+ and the
582
+ [release process](https://github.com/everton-dgn/solidjs_base/blob/main/docs/releasing.md).
583
+
594
584
  ## Entry points
595
585
 
596
- | Entry | Content |
597
- | ------------------------ | ------------------------------------------------------------------------- |
598
- | `./lint` | `lint`: the lint preset, with the plugins below registered |
599
- | `./fmt` | `fmt`: the formatter config |
600
- | `./tsconfig.base.json` | Compiler options, without application paths |
601
- | `./tsconfig.app.json` | The base plus the `types` and `include` of the boilerplate layout |
602
- | `./vite` | `defineSolidApp(options)`, the whole `vite.config.ts` of an application |
603
- | `./vitest` | `solidTest(options)`, the `test` block of `vite.config.ts` |
604
- | `./playwright` | `definePlaywrightConfig(options)`, the Playwright config |
605
- | `./vitest-setup/browser` | Setup file of the browser project, loaded by `./vitest` |
606
- | `./playwright-teardown` | Server log check, loaded by `./playwright` |
607
- | `./project-plugin` | The `project` rules |
608
- | `./backend-plugin` | The `backend` rules |
609
- | `./architecture-plugin` | The `architecture` rule |
610
- | `./solid-plugin` | Re-export of `eslint-plugin-solid` |
611
- | `./css-filename` | `cssFilenameError(path)`, the CSS naming check, for a script outside lint |
612
- | `./knip` | `defineKnipConfig(options)`, the Knip config |
613
- | `./stylelint` | `checkCss(root)` and `defineStylelintConfig(root)`, the CSS lint |
614
- | `./commitlint` | The commitlint config |
615
- | `./lefthook.yml` | The Git hooks, extended from the application's `.lefthook.yml` |
616
-
617
- An application imports `./vite`, which brings `./lint`, `./fmt` and `./vitest`
618
- along, `./playwright`, `./knip`, `./stylelint` and one of the TypeScript
619
- configs, and extends `./commitlint` and `./lefthook.yml`. An application that
620
- only lints imports `./lint` and `./fmt`. The plugin entries exist because Oxlint
586
+ | Entry | Content |
587
+ | ------------------------------- | -------------------------------------------------------------------------------- |
588
+ | `./vite` | `defineSolidApp(options)`, the whole `vite.config.ts` of an application |
589
+ | `./lint` | `lint`: the lint preset, with the plugins below registered |
590
+ | `./fmt` | `fmt`: the formatter config |
591
+ | `./tsconfig.base.json` | Compiler options, without application paths |
592
+ | `./tsconfig.app.json` | The base plus packaged type references and the boilerplate `include` |
593
+ | `./app-env` | Type references loaded by `tsconfig.app.json` |
594
+ | `./virtual-modules` | Declarations of the `virtual:solid-app/*` modules, loaded by `tsconfig.app.json` |
595
+ | `./vitest` | `solidTest(options)`, the `test` block of `vite.config.ts` |
596
+ | `./vitest-setup/browser` | Setup file of the browser project, loaded by `./vitest` |
597
+ | `./playwright` | `definePlaywrightConfig(options)`, the Playwright config |
598
+ | `./playwright-teardown` | Server log check, loaded by `./playwright` |
599
+ | `./project-plugin` | The `project` rules |
600
+ | `./backend-plugin` | The `backend` rules |
601
+ | `./architecture-plugin` | The `architecture` rule |
602
+ | `./solid-plugin` | Re-export of `eslint-plugin-solid`, resolved from the package |
603
+ | `./css-filename` | `cssFilenameError(path)`, the CSS naming check, for a script outside lint |
604
+ | `./knip` | `defineKnipConfig(options)`, the Knip config |
605
+ | `./stylelint` | `checkCss(root)` and `defineStylelintConfig(root)`, the CSS lint |
606
+ | `./commitlint` | The commitlint config |
607
+ | `./lefthook.yml` | The Git hooks, extended from the application's `.lefthook.yml` |
608
+ | `./cli` | Command entry for packaged Knip, TypeScript, Playwright, Lefthook and commitlint |
609
+ | `./i18n-generate` | Compiled catalog generator executed by the preset's `i18n-generate` task |
610
+ | `./publication/read-llms-index` | Compiled reader of the llms index, for artifact checks in Node |
611
+ | `./package.json` | The package manifest |
612
+
613
+ Your editor and `tsc` know the test globals, the jest-dom matchers, the Solid
614
+ plugin and routing modules and every `virtual:solid-app/*` module with no local
615
+ `.d.ts`: `tsconfig.app.json` loads `./app-env`, which references
616
+ `./virtual-modules`. An application with its own TypeScript config adds
617
+ `@evertondgn/solid-tooling/app-env` to `types`.
618
+
619
+ Your application keeps its pages, components, translations, theme, `App`,
620
+ `Document`, entries and middleware. The plugin entries exist because Oxlint
621
621
  loads plugins by specifier, from the application; the setup and teardown entries
622
- are loaded by the test presets.
622
+ are loaded by the test presets. The
623
+ [entry-point reference](https://github.com/everton-dgn/solidjs_base/blob/main/docs/tooling/README.md#entry-points)
624
+ describes each one.
623
625
 
624
- ## Formatter
626
+ ## Guides
625
627
 
626
- `fmt` sets 80 columns, single quotes, no semicolons, no trailing commas, wrapped
627
- prose in Markdown and sorted imports. It ignores `public/`, `src/assets/`,
628
- generated route types, SVG and lock files.
628
+ - [Lint rules and customization](https://github.com/everton-dgn/solidjs_base/blob/main/docs/tooling/lint.md)
629
+ - [Application config, plugins and tasks](https://github.com/everton-dgn/solidjs_base/blob/main/docs/tooling/app-config.md)
630
+ - [Vitest and Playwright](https://github.com/everton-dgn/solidjs_base/blob/main/docs/tooling/testing.md)
631
+ - [Knip and CSS](https://github.com/everton-dgn/solidjs_base/blob/main/docs/tooling/knip-and-css.md)
632
+ - [Commit messages and Git hooks](https://github.com/everton-dgn/solidjs_base/blob/main/docs/tooling/commits-and-hooks.md)
633
+ - [TypeScript and formatting](https://github.com/everton-dgn/solidjs_base/blob/main/docs/tooling/typescript-and-format.md)
634
+ - [Requirements, limits and versions](https://github.com/everton-dgn/solidjs_base/blob/main/docs/tooling/limits.md)
629
635
 
630
- ## TypeScript base
631
-
632
- `tsconfig.base.json` targets ES2024 with bundler resolution, keeps JSX for the
633
- Solid compiler (`jsxImportSource: @solidjs/web`), emits nothing and turns on the
634
- unused code and unchecked index checks. It has no `paths`, `include` or `types`.
635
-
636
- `tsconfig.app.json` extends the base with the `types` of a boilerplate
637
- application and an `include` resolved from the application folder with
638
- `${configDir}`, which needs TypeScript 5.5 or later. Each entry of `types` comes
639
- from a package the application installs:
640
-
641
- | Entry of `types` | Package |
642
- | --------------------------------------- | --------------------------- |
643
- | `node` | `@types/node` |
644
- | `vite/client` | `vite` |
645
- | `vite-plus/test/globals` | `vite-plus` |
646
- | `@testing-library/jest-dom/vitest` | `@testing-library/jest-dom` |
647
- | `@solidjs/vite-plugin/boundary-modules` | `@solidjs/vite-plugin` |
648
- | `@solidjs/vite-plugin/virtual-modules` | `@solidjs/vite-plugin` |
649
-
650
- The application still declares its `paths`:
651
-
652
- ```json
653
- {
654
- "extends": "@evertondgn/solid-tooling/tsconfig.app.json",
655
- "compilerOptions": {
656
- "paths": { "@/*": ["./src/*"], "@paraglide/*": ["./paraglide/*"] }
657
- }
658
- }
659
- ```
660
-
661
- Playwright reads the `tsconfig.json` of the test files with its own loader,
662
- which does not expand `${configDir}`, so paths declared in the package would
663
- break the `@/` imports of the end-to-end tests.
664
-
665
- ## Known limits
666
-
667
- - The package ships compiled JavaScript. Node refuses to run TypeScript from
668
- `node_modules`, and Vite+ loads `vite.config.ts` in Node.
669
- - `project/test-filename` has no options. Test folders without a module, such as
670
- the page tests in `src/tests/pages/`, are listed inside the package with the
671
- names used in boilerplate_solidjs. For any other folder of that kind, turn the
672
- rule off in `lint.overrides`, as shown in
673
- [Changing the preset](#changing-the-preset).
674
- - The Paraglide plugin is always registered, so `@inlang/paraglide-js` has to be
675
- installed even in an application without translations.
676
- - Some rule messages point to `docs/architecture.md` and `AGENTS.md`. Those
677
- files belong to the application created from boilerplate_solidjs.
678
- - The test presets expect the boilerplate layout: tests named by suffix
679
- (`.node.test`, `.dom.test`, `.browser.test`, `.e2e.test`), page tests in
680
- `src/tests/pages/`, Paraglide output in `paraglide/` and `.env.test` at the
681
- root.
682
- - The Knip and CSS configs expect the same layout: pages in `src/routes/`,
683
- fixture routes in `src/tests/fixtures/` and the theme in `src/theme/`.
684
- - The Knip config ignores every unlisted dependency in its own file, not only
685
- this package.
686
- - `./vite` expects the same layout: its tasks run scripts in `tooling/`, and the
687
- Markdown rules need the locale prefix of the Paraglide `urlPatterns` in every
688
- URL. Nitro always uses the `vercel` preset. The plugin packages are pinned, so
689
- a new version of Solid, Nitro, the router or the icons plugin needs a package
690
- version tested with it.
691
-
692
- ## Versions
636
+ ## Versions and license
693
637
 
694
638
  The package follows semantic versioning. Below 1.0.0, a breaking change raises
695
- the minor version. Each release is listed in the
696
- [changelog](https://github.com/everton-dgn/solidjs_base/blob/main/packages/solid-tooling/CHANGELOG.md).
697
-
698
- ## License
639
+ the minor version. Read the
640
+ [changelog](https://github.com/everton-dgn/solidjs_base/blob/main/packages/solid-tooling/CHANGELOG.md)
641
+ for each release, and the
642
+ [Solid Base overview](https://github.com/everton-dgn/solidjs_base/blob/main/README.md)
643
+ for both packages together.
699
644
 
700
- MIT
645
+ [MIT](https://github.com/everton-dgn/solidjs_base/blob/main/LICENSE)