@evertondgn/solid-tooling 0.5.0 → 0.6.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 +193 -634
  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 +415 -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,50 @@
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:
34
-
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 |
5
+ # @evertondgn/solid-tooling
42
6
 
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.
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 and Vite+. 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>
50
12
 
51
- ## Install
13
+ | Available package | Role |
14
+ | ------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------- |
15
+ | [`@evertondgn/solid-tooling`](https://github.com/everton-dgn/solidjs_base/blob/main/packages/solid-tooling/README.md) | Shared config, tools, catalog generation, icons and fonts |
16
+ | [`@evertondgn/solid-runtime`](https://github.com/everton-dgn/solidjs_base/blob/main/packages/solid-runtime/README.md), `0.1.0` | Optional locale and theme behavior, middleware, error protection, env, SEO and sitemap |
52
17
 
53
- `vp` is the Vite+ CLI. An application built with Vite+ already has `vite-plus`
54
- and `typescript`, so it only adds this package:
18
+ Solid Base supplies lint rules, application config, test setups and Git hooks
19
+ for SolidJS 2 with [Vite+](https://viteplus.dev/guide/) as one versioned
20
+ dependency, so you stop maintaining the same tooling in every Solid app. In an
21
+ application that already uses Vite+:
55
22
 
56
23
  ```bash
57
24
  vp add -D @evertondgn/solid-tooling
58
25
  ```
59
26
 
60
- An application without `@inlang/paraglide-js` adds it too, in the version the
61
- package asks for:
27
+ Built for applications that follow the
28
+ [boilerplate_solidjs](https://github.com/everton-dgn/boilerplate_solidjs)
29
+ layout. SolidJS 2 is still a release candidate, and the package is tested with
30
+ it. Start with lint and formatting, and adopt the full application config when
31
+ you are ready.
62
32
 
63
- ```bash
64
- npm view @evertondgn/solid-tooling peerDependencies
65
- vp add -D @inlang/paraglide-js@<version>
66
- ```
33
+ ## Requirements
34
+
35
+ Install the exact Vite+ version listed in the package's `peerDependencies`. The
36
+ package supplies its build, test, type-check and hook tools. Applications keep
37
+ their own direct imports and the Vite+ overrides; see the
38
+ [dependency requirements](https://github.com/everton-dgn/solidjs_base/blob/main/docs/tooling/limits.md#requirements).
39
+ An application with translations also installs the required version of
40
+ `@inlang/paraglide-js` and its message-format plugin.
41
+
42
+ ## Quick start
67
43
 
68
- ## Usage
44
+ Add `fmt` and `lint` to your existing Vite+ config. The example shows only those
45
+ settings; keep your plugins and other options:
69
46
 
70
47
  ```ts
71
- // vite.config.ts
72
48
  import { fmt } from '@evertondgn/solid-tooling/fmt'
73
49
  import { lint } from '@evertondgn/solid-tooling/lint'
74
50
  import { defineConfig } from 'vite-plus'
@@ -76,8 +52,9 @@ import { defineConfig } from 'vite-plus'
76
52
  export default defineConfig({ fmt, lint })
77
53
  ```
78
54
 
55
+ In `tsconfig.json`:
56
+
79
57
  ```json
80
- // tsconfig.json
81
58
  {
82
59
  "extends": "@evertondgn/solid-tooling/tsconfig.base.json",
83
60
  "compilerOptions": {
@@ -87,83 +64,70 @@ export default defineConfig({ fmt, lint })
87
64
  }
88
65
  ```
89
66
 
90
- `vp check`, `vp lint` and `vp fmt` read both configs from `vite.config.ts`.
91
-
92
- ## Tests
93
-
94
- `solidTest` returns the `test` block of `vite.config.ts`, with three Vitest
95
- projects:
67
+ Check your code and format your files:
96
68
 
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 |
69
+ ```bash
70
+ vp check
71
+ vp fmt
72
+ ```
102
73
 
103
- `defineSolidApp` from `./vite` already calls `solidTest`. An application that
104
- builds its own Vite config calls it directly:
74
+ To see a rule of the package fire, create `src/helpers/export-list.ts`:
105
75
 
106
76
  ```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'
77
+ const example = 1
111
78
 
112
- export default defineConfig({
113
- test: solidTest({
114
- root: import.meta.dirname,
115
- plugins: () => [solid({ serverFunctions: true })]
116
- })
117
- })
79
+ export { example }
118
80
  ```
119
81
 
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.
82
+ Run `vp lint`. The preset reports the export list:
127
83
 
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).
84
+ ```text
85
+ 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.
86
+ ```
133
87
 
134
- `definePlaywrightConfig` builds the Playwright config from the application root:
88
+ ## Why
135
89
 
136
- ```ts
137
- // playwright.config.ts
138
- import { definePlaywrightConfig } from '@evertondgn/solid-tooling/playwright'
90
+ Applications copied from a starter carry its tooling with them, and every fix to
91
+ the base has to be repeated by hand in each copy. With this package, the fix
92
+ arrives when you update a dependency.
139
93
 
140
- export default definePlaywrightConfig({ root: import.meta.dirname })
141
- ```
94
+ <picture>
95
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/before-after-dark.webp" />
96
+ <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/before-after-light.webp" />
97
+ <img src="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/before-after-light.webp" alt="About 86 percent fewer lines across five configuration areas, from 945 to 136: Vite 427 to 59, 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" />
98
+ </picture>
99
+
100
+ Measured on the original starter and the same application using this package.
101
+ Compare the
102
+ [original config](https://github.com/everton-dgn/boilerplate_solidjs/blob/61454937472d0faa1b4fab985fd7c5da968aa9a6/vite.config.ts)
103
+ with the
104
+ [application config](https://github.com/everton-dgn/solidjs_base/blob/main/apps/boilerplate/vite.config.ts),
105
+ or read the
106
+ [full comparison](https://github.com/everton-dgn/solidjs_base/blob/main/README.md#before-and-after).
142
107
 
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.
108
+ ## What you get
147
109
 
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` |
110
+ | Area | Entry | Guide |
111
+ | ------------------------------- | --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
112
+ | Oxlint preset and project rules | `./lint` | [Lint](https://github.com/everton-dgn/solidjs_base/blob/main/docs/tooling/lint.md) |
113
+ | Oxfmt settings | `./fmt` | [Formatting](https://github.com/everton-dgn/solidjs_base/blob/main/docs/tooling/typescript-and-format.md#formatter) |
114
+ | TypeScript configs | `./tsconfig.base.json`, `./tsconfig.app.json` | [TypeScript](https://github.com/everton-dgn/solidjs_base/blob/main/docs/tooling/typescript-and-format.md#typescript-base) |
115
+ | Whole application config | `./vite` | [Application config](https://github.com/everton-dgn/solidjs_base/blob/main/docs/tooling/app-config.md) |
116
+ | Vitest and Playwright | `./vitest`, `./playwright` | [Testing](https://github.com/everton-dgn/solidjs_base/blob/main/docs/tooling/testing.md) |
117
+ | Unused code and CSS policy | `./knip`, `./stylelint` | [Knip and CSS](https://github.com/everton-dgn/solidjs_base/blob/main/docs/tooling/knip-and-css.md) |
118
+ | Commit messages and Git hooks | `./commitlint`, `./lefthook.yml` | [Commits and hooks](https://github.com/everton-dgn/solidjs_base/blob/main/docs/tooling/commits-and-hooks.md) |
153
119
 
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`.
120
+ Your application keeps its pages, components, translations, theme, `App`,
121
+ `Document`, entries and middleware. The
122
+ [entry-point reference](https://github.com/everton-dgn/solidjs_base/blob/main/docs/tooling/README.md#entry-points)
123
+ also lists the plugins and test setup exports.
157
124
 
158
- ## Application config
125
+ ## Your application config in one call
159
126
 
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.
127
+ With the application dependencies installed, `defineSolidApp()` supplies the
128
+ plugins, build settings, tasks, lint, format and three Vitest projects:
164
129
 
165
130
  ```ts
166
- // vite.config.ts
167
131
  import { defineSolidApp } from '@evertondgn/solid-tooling/vite'
168
132
 
169
133
  import { SECURITY_HEADERS } from './src/middleware/securityHeaders/constants.ts'
@@ -171,530 +135,125 @@ import { SECURITY_HEADERS } from './src/middleware/securityHeaders/constants.ts'
171
135
  export default defineSolidApp({
172
136
  root: import.meta.dirname,
173
137
  middleware: './src/middleware/index.ts',
174
- securityHeaders: SECURITY_HEADERS,
175
- staticPaths: ['/favicon/**']
138
+ securityHeaders: SECURITY_HEADERS
176
139
  })
177
140
  ```
178
141
 
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.
142
+ You keep your middleware and header values. SSR uses Nitro with the Vercel
143
+ preset. Its plugins are package dependencies pinned to the tested versions. The
144
+ `knip`, `tsc`, `playwright`, `lefthook` and `commitlint` tasks run the
145
+ package-owned binaries, including with an isolated installation.
146
+
147
+ | Feature | Selection |
148
+ | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
149
+ | Client-only build | Set `ssr: false` and omit SSR options such as `middleware` and `securityHeaders`; the build has no Nitro or server bundle |
150
+ | Publication headers | Set `seo.markdown`, `seo.llms`, `seo.llmsFull` and `seo.sitemap` independently; static artifacts come from compiled tooling |
151
+ | Translations | `project.inlang/settings.json` enables the generation task and browser test setup; the lint plugin loads only where Paraglide resolves |
152
+
153
+ See
154
+ [all options and extension points](https://github.com/everton-dgn/solidjs_base/blob/main/docs/tooling/app-config.md)
155
+ and the
156
+ [known limits](https://github.com/everton-dgn/solidjs_base/blob/main/docs/tooling/limits.md#known-limits).
157
+
158
+ ## Optional application fonts
159
+
160
+ Pass a data-only `fonts` array to `defineSolidApp` to generate external
161
+ `@font-face` CSS and matching preloads for application-owned files. SSR uses
162
+ `FontLinks` from `@evertondgn/solid-runtime/fonts/font-links` in the Document
163
+ head; client-only applications receive both links in `index.html`. Without
164
+ `fonts`, nothing is generated. Typography tokens stay in the app. See the
165
+ [font API and migration examples](https://github.com/everton-dgn/solidjs_base/blob/main/docs/tooling/fonts.md).
166
+
167
+ ## Optional SEO configuration
168
+
169
+ For SSR,
170
+ `seo: { robots: { groups, sitemapPaths, cacheControl }, sitemap: true }`
171
+ configures the runtime robots route using serializable data. `originEnv`
172
+ defaults to `VITE_SITE_URL`; the value must be an HTTP(S) origin without a path,
173
+ query, fragment or credentials. The preset validates it when SEO is enabled or
174
+ the runtime origin entry is imported. No SEO means no required origin.
175
+
176
+ Pass `site` directly to the runtime `createSeo` primitive. A meta/JSON-LD-only
177
+ application can omit `seo`, provide its own `site.url` and omit robots entirely.
178
+ Client-only applications omit both the option and runtime SEO imports. There are
179
+ no `siteModule`, `sourcesModule` or `editorialModule` path options.
180
+ `seo.sitemap` controls only the static `/sitemap.xml` and `/sitemaps/pages/**`
181
+ header rules. Install the runtime factory in explicit application route files,
182
+ and list the root in `robots.sitemapPaths` to advertise it. To remove sitemap,
183
+ remove its routes and server binding, omit its array metadata, omit
184
+ `sitemapPaths`, and omit or disable `seo.sitemap`. Robots can stay enabled. The
185
+ preset does not discover executable source registries or install routes. See the
186
+ [runtime SEO API](../solid-runtime/README.md#optional-seo) for factories, the
187
+ one-line route and examples with and without translations.
188
+
189
+ ## Optional environment validation
190
+
191
+ An SSR application can add `env.ts` with the preset's optional validators:
237
192
 
238
193
  ```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
194
+ import { serverEnv } from '@evertondgn/solid-runtime/env'
250
195
 
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 |
196
+ const env = { server: { ...serverEnv } }
260
197
 
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
- }
198
+ export default env
275
199
  ```
276
200
 
277
- ### Changing the result
278
-
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:
282
-
283
- ```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 })
288
-
289
- export default defineConfig(env =>
290
- mergeConfig(app(env), { server: { strictPort: true } })
291
- )
292
- ```
293
-
294
- ## Knip
295
-
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:
298
-
299
- ```ts
300
- // tooling/knip.ts
301
- import path from 'node:path'
302
-
303
- import { defineKnipConfig } from '@evertondgn/solid-tooling/knip'
304
-
305
- export default defineKnipConfig({
306
- root: path.resolve(import.meta.dirname, '..'),
307
- ignoreDependencies: ['server-only']
308
- })
309
- ```
310
-
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.
408
-
409
- ## Commit messages and Git hooks
410
-
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:
416
-
417
- ```json
418
- // .commitlintrc
419
- { "extends": ["@evertondgn/solid-tooling/commitlint"] }
420
- ```
421
-
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`:
424
-
425
- ```json
426
- // package.json
427
- { "trustedDependencies": ["lefthook"] }
428
- ```
429
-
430
- ```yaml
431
- # .lefthook.yml
432
- extends:
433
- - node_modules/@evertondgn/solid-tooling/lefthook.yml
434
- ```
435
-
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:
561
-
562
- ```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'
567
-
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.
580
-
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
- ]
592
- ```
593
-
594
- ## Entry points
595
-
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
621
- loads plugins by specifier, from the application; the setup and teardown entries
622
- are loaded by the test presets.
623
-
624
- ## Formatter
625
-
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.
629
-
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
201
+ `HOST`, `PORT` and `BASE_URL_TEST` are optional; `PORT` produces a number.
202
+ Declare the runtime package in `dependencies` and keep the tooling in
203
+ `devDependencies`: Knip treats `env.ts` as production code. Extend `server` and
204
+ `client` with the application's own Standard Schema validators. The runtime
205
+ supplies Valibot for its fragment; declare a validator library in the
206
+ application only when its own code imports it. The optional SEO configuration
207
+ validates `VITE_SITE_URL` separately. An application without `env.ts` keeps
208
+ working, including client-only applications with no site URL.
209
+
210
+ The Solid plugin generates inferred types and rejects server env imports in the
211
+ client graph. The server virtual module bundles the fragment and validator; env
212
+ validation adds neither to the client. See the
213
+ [environment guide](https://github.com/everton-dgn/solidjs_base/blob/main/docs/tooling/app-config.md#optional-environment-validation)
214
+ for extension examples, boot validation and type generation.
215
+
216
+ ## How it is verified
217
+
218
+ <picture>
219
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/verified-dark.webp" />
220
+ <source media="(prefers-color-scheme: light)" srcset="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/verified-light.webp" />
221
+ <img src="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/verified-light.webp" alt="At this revision: 775 runtime and 394 application Vitest tests, 1169 combined; 164 fixture E2E cases and 155 production E2E cases, 319 enumerated. Installed tarballs check exports and builds with both Bun linkers. Release workflows use npm trusted publishing." width="800" />
222
+ </picture>
223
+
224
+ Package changes run against the complete boilerplate and the runtime package in
225
+ CI, with more than 1,000 Vitest tests and more than 300 end-to-end tests. Before
226
+ each release, the tarball test installs the packed package in a separate
227
+ application outside the workspace and checks its exports and lint rules.
228
+
229
+ The workflow publishes to npm with signed provenance through trusted publishing,
230
+ without a stored npm token. See the
231
+ [CI coverage](https://github.com/everton-dgn/solidjs_base/blob/main/docs/development.md#ci)
232
+ and the
233
+ [release process](https://github.com/everton-dgn/solidjs_base/blob/main/docs/releasing.md).
234
+
235
+ ## Reference
236
+
237
+ - [Lint rules and customization](https://github.com/everton-dgn/solidjs_base/blob/main/docs/tooling/lint.md)
238
+ - [Application config, plugins and tasks](https://github.com/everton-dgn/solidjs_base/blob/main/docs/tooling/app-config.md)
239
+ - [Vitest and Playwright](https://github.com/everton-dgn/solidjs_base/blob/main/docs/tooling/testing.md)
240
+ - [Knip and CSS](https://github.com/everton-dgn/solidjs_base/blob/main/docs/tooling/knip-and-css.md)
241
+ - [Commit messages and Git hooks](https://github.com/everton-dgn/solidjs_base/blob/main/docs/tooling/commits-and-hooks.md)
242
+ - [TypeScript and formatting](https://github.com/everton-dgn/solidjs_base/blob/main/docs/tooling/typescript-and-format.md)
243
+ - [Requirements, limits and versions](https://github.com/everton-dgn/solidjs_base/blob/main/docs/tooling/limits.md)
244
+
245
+ ## Versions and license
693
246
 
694
247
  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
699
-
700
- MIT
248
+ the minor version. Read the
249
+ [changelog](https://github.com/everton-dgn/solidjs_base/blob/main/packages/solid-tooling/CHANGELOG.md)
250
+ for each release.
251
+
252
+ [MIT](https://github.com/everton-dgn/solidjs_base/blob/main/LICENSE)
253
+
254
+ Publication is compiled into the preset and uses one build identity for the SSR
255
+ and artifact manifests. It no longer needs an application `extend` callback.
256
+ `publication/read-llms-index` exposes the compiled index reader for artifact
257
+ checks in Node. See
258
+ [ADR 0034](../../docs/ADRs/0034-runtime-markdown-publication.md) for independent
259
+ feature options, defaults, disabled-resource behavior and scale.