@evertondgn/solid-tooling 0.6.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 (3) hide show
  1. package/README.md +463 -77
  2. package/dist/vite.mjs +40 -6
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -7,18 +7,24 @@
7
7
  <picture>
8
8
  <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/hero-dark.webp" />
9
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" />
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
11
  </picture>
12
12
 
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 |
13
+ **The lint rules, tests, Git hooks and whole Vite+ config of your SolidJS 2
14
+ apps, as one dev dependency.**
17
15
 
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+:
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.
23
+
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.
22
28
 
23
29
  ```bash
24
30
  vp add -D @evertondgn/solid-tooling
@@ -26,9 +32,24 @@ vp add -D @evertondgn/solid-tooling
26
32
 
27
33
  Built for applications that follow the
28
34
  [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.
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)
32
53
 
33
54
  ## Requirements
34
55
 
@@ -85,42 +106,84 @@ Run `vp lint`. The preset reports the export list:
85
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.
86
107
  ```
87
108
 
88
- ## Why
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>
89
114
 
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.
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
93
123
 
94
124
  <picture>
95
125
  <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/before-after-dark.webp" />
96
126
  <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" />
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" />
98
128
  </picture>
99
129
 
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).
107
-
108
- ## What you get
109
-
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) |
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.
119
149
 
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.
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.
124
187
 
125
188
  ## Your application config in one call
126
189
 
@@ -139,39 +202,283 @@ export default defineSolidApp({
139
202
  })
140
203
  ```
141
204
 
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.
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.
146
211
 
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 |
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>
152
217
 
153
- See
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
154
237
  [all options and extension points](https://github.com/everton-dgn/solidjs_base/blob/main/docs/tooling/app-config.md)
155
238
  and the
156
239
  [known limits](https://github.com/everton-dgn/solidjs_base/blob/main/docs/tooling/limits.md#known-limits).
157
240
 
158
- ## Optional application fonts
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.
293
+
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 |
299
+
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.
330
+
331
+ ```ts
332
+ // playwright.config.ts
333
+ import { definePlaywrightConfig } from '@evertondgn/solid-tooling/playwright'
334
+
335
+ export default definePlaywrightConfig({ root: import.meta.dirname })
336
+ ```
337
+
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>
345
+
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.
355
+
356
+ ```ts
357
+ // tooling/knip.ts
358
+ import path from 'node:path'
359
+
360
+ import { defineKnipConfig } from '@evertondgn/solid-tooling/knip'
361
+
362
+ export default defineKnipConfig({
363
+ root: path.resolve(import.meta.dirname, '..'),
364
+ ignoreDependencies: ['server-only']
365
+ })
366
+ ```
367
+
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).
390
+
391
+ ## Commit messages and Git hooks
392
+
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>
398
+
399
+ The shared hooks check a change before it reaches the remote:
400
+
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 |
406
+
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:
412
+
413
+ ```yaml
414
+ # .lefthook.yml
415
+ extends:
416
+ - node_modules/@evertondgn/solid-tooling/lefthook.yml
417
+ ```
418
+
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>
159
452
 
160
453
  Pass a data-only `fonts` array to `defineSolidApp` to generate external
161
454
  `@font-face` CSS and matching preloads for application-owned files. SSR uses
162
455
  `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
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
165
459
  [font API and migration examples](https://github.com/everton-dgn/solidjs_base/blob/main/docs/tooling/fonts.md).
166
460
 
167
- ## Optional SEO configuration
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.
168
476
 
169
477
  For SSR,
170
478
  `seo: { robots: { groups, sitemapPaths, cacheControl }, sitemap: true }`
171
479
  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.
480
+ defaults to `VITE_SITE_URL`. The preset validates it when SEO is enabled or the
481
+ runtime origin entry is imported.
175
482
 
176
483
  Pass `site` directly to the runtime `createSeo` primitive. A meta/JSON-LD-only
177
484
  application can omit `seo`, provide its own `site.url` and omit robots entirely.
@@ -183,12 +490,36 @@ and list the root in `robots.sitemapPaths` to advertise it. To remove sitemap,
183
490
  remove its routes and server binding, omit its array metadata, omit
184
491
  `sitemapPaths`, and omit or disable `seo.sitemap`. Robots can stay enabled. The
185
492
  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.
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.
188
511
 
189
512
  ## Optional environment validation
190
513
 
191
- An SSR application can add `env.ts` with the preset's optional validators:
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:
192
523
 
193
524
  ```ts
194
525
  import { serverEnv } from '@evertondgn/solid-runtime/env'
@@ -218,21 +549,81 @@ for extension examples, boot validation and type generation.
218
549
  <picture>
219
550
  <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/everton-dgn/solidjs_base/main/docs/assets/readme/verified-dark.webp" />
220
551
  <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" />
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" />
222
553
  </picture>
223
554
 
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
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
231
580
  [CI coverage](https://github.com/everton-dgn/solidjs_base/blob/main/docs/development.md#ci)
232
581
  and the
233
582
  [release process](https://github.com/everton-dgn/solidjs_base/blob/main/docs/releasing.md).
234
583
 
235
- ## Reference
584
+ ## Entry points
585
+
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
+ loads plugins by specifier, from the application; the setup and teardown entries
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.
625
+
626
+ ## Guides
236
627
 
237
628
  - [Lint rules and customization](https://github.com/everton-dgn/solidjs_base/blob/main/docs/tooling/lint.md)
238
629
  - [Application config, plugins and tasks](https://github.com/everton-dgn/solidjs_base/blob/main/docs/tooling/app-config.md)
@@ -247,13 +638,8 @@ and the
247
638
  The package follows semantic versioning. Below 1.0.0, a breaking change raises
248
639
  the minor version. Read the
249
640
  [changelog](https://github.com/everton-dgn/solidjs_base/blob/main/packages/solid-tooling/CHANGELOG.md)
250
- for each release.
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.
251
644
 
252
645
  [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.
package/dist/vite.mjs CHANGED
@@ -9,6 +9,12 @@ import { defineConfig, lazyPlugins, loadEnv } from "vite-plus";
9
9
  import * as v from "valibot";
10
10
  import { createHash } from "node:crypto";
11
11
  // src/fonts/assets.ts
12
+ const RELATIVE_BASE = "./";
13
+ const PARENT_SEGMENT = /^(?:\.|%2e){2}$/iu;
14
+ const URL_IGNORED = /[\t\n\r]/gu;
15
+ const PERCENT_ESCAPE = /%[\da-f]{2}/giu;
16
+ const HEX_RADIX = 16;
17
+ const OUTPUT_URL = "file:///output/";
12
18
  const HASH_LENGTH = 16;
13
19
  const MAX_WEIGHT = 1e3;
14
20
  const FORMATS = {
@@ -56,16 +62,44 @@ function isInside(root, file) {
56
62
  const relative = path.relative(root, file);
57
63
  return relative !== "" && !relative.startsWith(`..${path.sep}`) && relative !== ".." && !path.isAbsolute(relative);
58
64
  }
59
- async function readFontAssets({ root, fonts, base, assetsDir }) {
65
+ function isRelativeBase({ base, ssr }) {
66
+ if (base !== RELATIVE_BASE) return false;
67
+ if (ssr !== false) throw new Error("defineSolidApp fonts: a relative base needs ssr: false.");
68
+ return true;
69
+ }
70
+ function fontUrls({ base, ssr }) {
71
+ if (isRelativeBase({
72
+ base,
73
+ ssr
74
+ })) return {
75
+ css: (fileName) => path.posix.basename(fileName),
76
+ html: (fileName) => `${RELATIVE_BASE}${fileName}`
77
+ };
60
78
  if (!base.startsWith("/") || base.startsWith("//") || /[?#]/u.test(base)) throw new Error("defineSolidApp fonts: base must be a same-origin absolute pathname.");
61
- if (path.isAbsolute(assetsDir) || assetsDir.split(/[\\/]/u).includes("..")) throw new Error("defineSolidApp fonts: assetsDir must stay inside the client output.");
79
+ const url = (fileName) => `${base.replace(/\/$/u, "")}/${fileName}`;
80
+ return {
81
+ css: url,
82
+ html: url
83
+ };
84
+ }
85
+ function decodeOnce(value) {
86
+ return value.replaceAll(PERCENT_ESCAPE, (escape) => String.fromCodePoint(Number.parseInt(escape.slice(1), HEX_RADIX)));
87
+ }
88
+ function hasParentSegment(value) {
89
+ return value.split(/[\\/]/u).some((segment) => PARENT_SEGMENT.test(segment.replaceAll(URL_IGNORED, "")));
90
+ }
91
+ async function readFontAssets({ root, fonts, base, assetsDir, ssr }) {
92
+ const urls = fontUrls({
93
+ base,
94
+ ssr
95
+ });
96
+ if (path.isAbsolute(assetsDir) || hasParentSegment(assetsDir) || hasParentSegment(decodeOnce(assetsDir)) || !new URL(`${OUTPUT_URL}${assetsDir}/file`).href.startsWith(OUTPUT_URL)) throw new Error("defineSolidApp fonts: assetsDir must stay inside the client output.");
62
97
  const families = v.parse(familiesSchema, fonts);
63
98
  const directory = await realpath(root);
64
99
  const assets = /* @__PURE__ */ new Map();
65
100
  const files = /* @__PURE__ */ new Set();
66
101
  const preloads = /* @__PURE__ */ new Map();
67
102
  const rules = [];
68
- const url = (fileName) => `${base.replace(/\/$/u, "")}/${fileName}`;
69
103
  for (const { family, files: faces } of families) for (const face of faces) {
70
104
  if (path.isAbsolute(face.path) || /[?#]/u.test(face.path)) throw new Error(`defineSolidApp fonts: ${face.path} must be relative to the application root, without a query or fragment.`);
71
105
  const file = await realpath(path.resolve(directory, face.path));
@@ -86,12 +120,12 @@ async function readFontAssets({ root, fonts, base, assetsDir }) {
86
120
  assets.set(key, asset);
87
121
  }
88
122
  files.add(file);
89
- const href = url(asset.fileName);
123
+ const href = urls.html(asset.fileName);
90
124
  if (face.preload) preloads.set(href, {
91
125
  href,
92
126
  type: asset.type
93
127
  });
94
- rules.push(`@font-face {\n font-family: ${cssString(family)};\n font-style: ${face.style};\n font-display: ${face.display};\n font-weight: ${face.weight};\n src: url(${cssString(href)}) format(${cssString(format)});${face.unicodeRange ? `\n unicode-range: ${face.unicodeRange};` : ""}\n}`);
128
+ rules.push(`@font-face {\n font-family: ${cssString(family)};\n font-style: ${face.style};\n font-display: ${face.display};\n font-weight: ${face.weight};\n src: url(${cssString(urls.css(asset.fileName))}) format(${cssString(format)});${face.unicodeRange ? `\n unicode-range: ${face.unicodeRange};` : ""}\n}`);
95
129
  }
96
130
  const source = `${rules.join("\n\n")}\n`;
97
131
  const fileName = path.posix.join(assetsDir, `solid-fonts-${hash(source)}.css`);
@@ -102,7 +136,7 @@ async function readFontAssets({ root, fonts, base, assetsDir }) {
102
136
  type: "text/css"
103
137
  }],
104
138
  files: [...files],
105
- stylesheet: url(fileName),
139
+ stylesheet: urls.html(fileName),
106
140
  preloads: [...preloads.values()]
107
141
  };
108
142
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evertondgn/solid-tooling",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "Lint, formatter, TypeScript, test and Vite app presets for SolidJS applications built with Vite+.",
5
5
  "author": "Éverton Toffanetto",
6
6
  "license": "MIT",