@evertondgn/solid-tooling 0.3.0 → 0.4.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.
- package/README.md +300 -16
- package/dist/has-match-BITlj0EE.mjs +9 -0
- package/dist/knip.d.mts +12 -0
- package/dist/knip.mjs +92 -0
- package/dist/lint-UTfyg7AR.mjs +983 -0
- package/dist/lint.mjs +1 -983
- package/dist/playwright-teardown.mjs +1 -1
- package/dist/playwright.mjs +3 -3
- package/dist/stylelint.d.mts +8 -0
- package/dist/stylelint.mjs +180 -0
- package/dist/vite.d.mts +40 -0
- package/dist/vite.mjs +341 -0
- package/dist/vitest.mjs +1 -1
- package/package.json +35 -5
package/README.md
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
# @evertondgn/solid-tooling
|
|
2
2
|
|
|
3
|
-
Lint preset, formatter config, TypeScript configs
|
|
4
|
-
|
|
3
|
+
Lint preset, formatter config, TypeScript configs, test configs, the Knip and
|
|
4
|
+
Stylelint configs and the Vite+ application config for SolidJS 2 applications
|
|
5
|
+
built with [Vite+](https://viteplus.dev/guide/).
|
|
5
6
|
|
|
6
|
-
The package contains the lint, format, TypeScript
|
|
7
|
+
The package contains the lint, format, TypeScript, test, build, dead code and
|
|
8
|
+
CSS lint setup of
|
|
7
9
|
[boilerplate_solidjs](https://github.com/everton-dgn/boilerplate_solidjs), a
|
|
8
10
|
SolidJS application template. Applications created from it install the package
|
|
9
11
|
instead of copying the files, so a rule change reaches them through a version
|
|
@@ -24,15 +26,24 @@ exact versions the package is tested with. Install the versions listed in its
|
|
|
24
26
|
`peerDependencies`. `typescript` is not a peer dependency: the package is tested
|
|
25
27
|
with the version in its `devDependencies`.
|
|
26
28
|
|
|
27
|
-
`eslint-plugin-solid`
|
|
28
|
-
declare
|
|
29
|
+
`eslint-plugin-solid`, `stylelint` and `stylelint-use-nesting` come with the
|
|
30
|
+
package, so the application does not declare them.
|
|
29
31
|
|
|
30
|
-
|
|
32
|
+
Some entries need tools the application already runs:
|
|
31
33
|
|
|
32
|
-
| Entry | The application installs
|
|
33
|
-
| -------------- |
|
|
34
|
-
| `./vitest` | `@vitest/browser-playwright`, `@vitest/coverage-v8`, `happy-dom`, `playwright`
|
|
35
|
-
| `./playwright` | `@playwright/test`, an optional peer dependency pinned like the others
|
|
34
|
+
| Entry | The application installs |
|
|
35
|
+
| -------------- | ------------------------------------------------------------------------------- |
|
|
36
|
+
| `./vitest` | `@vitest/browser-playwright`, `@vitest/coverage-v8`, `happy-dom`, `playwright` |
|
|
37
|
+
| `./playwright` | `@playwright/test`, an optional peer dependency pinned like the others |
|
|
38
|
+
| `./knip` | `knip` and `filesystem-routing`, optional peer dependencies pinned the same way |
|
|
39
|
+
|
|
40
|
+
`./vite` also needs the test tools of `./vitest`, and loads four plugin
|
|
41
|
+
packages: `@solidjs/vite-plugin`, `nitro`, `filesystem-routing` and
|
|
42
|
+
`unplugin-icons`. They are optional peer dependencies pinned like the others.
|
|
43
|
+
Install the versions listed in `peerDependencies` as direct dependencies of the
|
|
44
|
+
application: Bun applies `overrides` and `patchedDependencies` only from the
|
|
45
|
+
root `package.json`, and the Knip plugin of `unplugin-icons` only turns on with
|
|
46
|
+
a direct dependency.
|
|
36
47
|
|
|
37
48
|
## Install
|
|
38
49
|
|
|
@@ -86,12 +97,20 @@ projects:
|
|
|
86
97
|
| `dom` | `src/**/*.dom.test.{ts,tsx}` | happy-dom at `http://localhost/en` |
|
|
87
98
|
| `browser` | `src/**/*.browser.test.{ts,tsx}` | Chromium through Playwright, with the CSS loaded |
|
|
88
99
|
|
|
100
|
+
`defineSolidApp` from `./vite` already calls `solidTest`. An application that
|
|
101
|
+
builds its own Vite config calls it directly:
|
|
102
|
+
|
|
89
103
|
```ts
|
|
90
104
|
// vite.config.ts
|
|
91
105
|
import { solidTest } from '@evertondgn/solid-tooling/vitest'
|
|
106
|
+
import solid from '@solidjs/vite-plugin'
|
|
107
|
+
import { defineConfig } from 'vite-plus'
|
|
92
108
|
|
|
93
109
|
export default defineConfig({
|
|
94
|
-
test: solidTest({
|
|
110
|
+
test: solidTest({
|
|
111
|
+
root: import.meta.dirname,
|
|
112
|
+
plugins: () => [solid({ serverFunctions: true })]
|
|
113
|
+
})
|
|
95
114
|
})
|
|
96
115
|
```
|
|
97
116
|
|
|
@@ -106,7 +125,8 @@ before each browser test.
|
|
|
106
125
|
The `node` project does not load the Solid and routing plugins, so a plugin of
|
|
107
126
|
the preset serves `server-only`, `virtual:file-routes` and
|
|
108
127
|
`virtual:solid-server-function-handler`. Knip does not know `server-only`, a
|
|
109
|
-
virtual module of `@solidjs/vite-plugin`:
|
|
128
|
+
virtual module of `@solidjs/vite-plugin`: pass it in the `ignoreDependencies` of
|
|
129
|
+
[`defineKnipConfig`](#knip).
|
|
110
130
|
|
|
111
131
|
`definePlaywrightConfig` builds the Playwright config from the application root:
|
|
112
132
|
|
|
@@ -132,6 +152,257 @@ Each serial suite gets its own project: `markdown` runs with one worker,
|
|
|
132
152
|
`serverLogs` runs last as the teardown of the others, and `publicationLoad` runs
|
|
133
153
|
alone, against two servers, with `TEST_PUBLICATION_LOAD=true`.
|
|
134
154
|
|
|
155
|
+
## Application config
|
|
156
|
+
|
|
157
|
+
`defineSolidApp` returns the whole `vite.config.ts` of a boilerplate
|
|
158
|
+
application: the Solid, routing, icons and Nitro plugins, the headers of the
|
|
159
|
+
static files, the build, CSS, server and preview blocks, the `vp run` tasks, and
|
|
160
|
+
`lint`, `fmt` and `test` from the presets above.
|
|
161
|
+
|
|
162
|
+
```ts
|
|
163
|
+
// vite.config.ts
|
|
164
|
+
import { defineSolidApp } from '@evertondgn/solid-tooling/vite'
|
|
165
|
+
|
|
166
|
+
import { SECURITY_HEADERS } from './src/middleware/securityHeaders/constants.ts'
|
|
167
|
+
|
|
168
|
+
export default defineSolidApp({
|
|
169
|
+
root: import.meta.dirname,
|
|
170
|
+
middleware: './src/middleware/index.ts',
|
|
171
|
+
securityHeaders: SECURITY_HEADERS,
|
|
172
|
+
staticPaths: ['/favicon/**']
|
|
173
|
+
})
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
| Option | Default | Use |
|
|
177
|
+
| ----------------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
|
|
178
|
+
| `root` | required | Application folder. `.env`, `.env.test`, `project.inlang/settings.json` and the icon folders resolve from it |
|
|
179
|
+
| `securityHeaders` | required | Values of `strict-transport-security`, `x-content-type-options` and `cross-origin-resource-policy` for the static files |
|
|
180
|
+
| `middleware` | none | `start.middleware` of the Solid plugin. It has to sit inside the application folder |
|
|
181
|
+
| `serverSetup` | `serverFunctions: true` | `serverFunctions.serverSetup` of the Solid plugin |
|
|
182
|
+
| `e2e` | production paths | `{ middleware, routes }` for `--mode e2e`: the middleware chain and the route folder of the fixtures, without route types |
|
|
183
|
+
| `icons` | none | Own `unplugin-icons` collections, as name to folder |
|
|
184
|
+
| `publicAssets` | none | `publicAssets` of Nitro. Each entry needs a `baseURL`, which also gets the static headers |
|
|
185
|
+
| `staticPaths` | none | Other static paths that get the headers, such as `/favicon/**` |
|
|
186
|
+
| `routeRules` | none | More Nitro route rules, applied after the static ones. A rule with the same path replaces the static one |
|
|
187
|
+
| `extend` | none | `(context) => ({ plugins, nitroModules })`, for plugins and Nitro modules the application still owns |
|
|
188
|
+
| `tasks` | none | Adds or replaces `vp run` tasks by name |
|
|
189
|
+
| `workspace` | none | `{ dependsOn, inputs }` for an application inside a monorepo, whose `bun.lock`, `bunfig.toml` and package builds sit at the root |
|
|
190
|
+
|
|
191
|
+
The preset fixes the rest:
|
|
192
|
+
|
|
193
|
+
- Plugins, in this order: the `extend` plugins, `unplugin-icons` (Solid
|
|
194
|
+
compiler, `aria-hidden` on every icon), `@solidjs/vite-plugin` with SSR and
|
|
195
|
+
server functions, `filesystem-routing` with HTTP methods and the route types
|
|
196
|
+
in `src/@types/routes.d.ts`, and Nitro with the `vercel` preset. The `dom` and
|
|
197
|
+
`browser` test projects get the `extend` plugins, the icons, Solid with server
|
|
198
|
+
functions only and the routes. The `test` mode loads no application plugin and
|
|
199
|
+
reads `.env.test` into `process.env`.
|
|
200
|
+
- The static headers go to `/assets/**`, each `publicAssets` folder, the
|
|
201
|
+
`staticPaths`, `/<locale>/**.md` for each locale of
|
|
202
|
+
`project.inlang/settings.json`, `/llms.txt`, `/llms-full.txt`, `/sitemap.xml`,
|
|
203
|
+
`/sitemaps/pages/**` and `/_markdown/static.json`. The static files leave the
|
|
204
|
+
CDN without going through the middleware, so these rules give them the headers
|
|
205
|
+
the middleware writes on the other responses.
|
|
206
|
+
- `build.manifest` only in the `e2e` mode, which the `TEST_PREBUILT` check of
|
|
207
|
+
`./playwright` relies on, and no comments in the output.
|
|
208
|
+
- Lightning CSS, with CSS Modules classes named `[content-hash]_[local]`.
|
|
209
|
+
- `resolve.tsconfigPaths`, and the dev server and the preview on the `HOST` and
|
|
210
|
+
`PORT` of `.env`. The dev server forwards the browser console to the terminal.
|
|
211
|
+
|
|
212
|
+
The plugins are imported inside `lazyPlugins`, so `vp lint`, `vp fmt` and
|
|
213
|
+
`vp check` read the config without loading them.
|
|
214
|
+
|
|
215
|
+
Nitro compiles the route rules with the `preMerge` of h3, which refuses rules
|
|
216
|
+
that overlap in part, such as `/pt/blog/**` next to `/pt/**.md`, and falls back
|
|
217
|
+
to its simple mode, with the same result and a warning. `defineSolidApp` throws
|
|
218
|
+
when a `publicAssets` entry has no `baseURL`, or one with a trailing slash, and
|
|
219
|
+
when a static path does not start with `/`. Build, dev and preview fail to load
|
|
220
|
+
the plugins when a `publicAssets` folder or a static path sits inside a locale
|
|
221
|
+
folder.
|
|
222
|
+
|
|
223
|
+
### `extend`
|
|
224
|
+
|
|
225
|
+
`extend` runs inside `lazyPlugins`: once for the application in build, dev and
|
|
226
|
+
preview (`target: 'app'`), and once for each test project with a Vite server
|
|
227
|
+
(`target: 'test'`). Every load of the config calls it again, so a build also
|
|
228
|
+
calls it for the two test projects, whose plugins Vite does not use; keep side
|
|
229
|
+
effects out of it. It never runs while Vite+ only reads the config. Return new
|
|
230
|
+
plugin instances on each call. The `plugins` go before the fixed ones, and the
|
|
231
|
+
`nitroModules` go to Nitro; the `test` target ignores them. One call returns
|
|
232
|
+
both, so a value that a plugin and a module share, such as a build id, stays the
|
|
233
|
+
same.
|
|
234
|
+
|
|
235
|
+
```ts
|
|
236
|
+
export default defineSolidApp({
|
|
237
|
+
root: import.meta.dirname,
|
|
238
|
+
securityHeaders: SECURITY_HEADERS,
|
|
239
|
+
async extend() {
|
|
240
|
+
const { i18nPlugin } = await import('./tooling/i18n/i18nPlugin/index.ts')
|
|
241
|
+
return { plugins: [i18nPlugin()] }
|
|
242
|
+
}
|
|
243
|
+
})
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Tasks
|
|
247
|
+
|
|
248
|
+
| Task | Command |
|
|
249
|
+
| ------------------- | ------------------------------------------------------------------------------------------------------- |
|
|
250
|
+
| `i18n-generate` | `bun tooling/i18n/generate.ts` |
|
|
251
|
+
| `lint-css` | `bun tooling/css/check.ts` |
|
|
252
|
+
| `dead-code-project` | `knip-bun --config tooling/knip.ts`, then again with `--strict` |
|
|
253
|
+
| `lint-project` | `vp lint`, after `lint-css` |
|
|
254
|
+
| `check-project` | `vp fmt --check && vp lint`, after `lint-css` |
|
|
255
|
+
| `typecheck-node` | `tsc --project tsconfig.node.json` |
|
|
256
|
+
| `test-tooling` | `node --test` over `tooling/**/__tests__` except `tooling/release/`, three workers on CI, with no cache |
|
|
257
|
+
|
|
258
|
+
The commands run the scripts of the boilerplate layout. An application without
|
|
259
|
+
one of them replaces the task through `tasks`. Knip reads task commands only
|
|
260
|
+
from a `run.tasks` block written in `vite.config.ts`, so it does not see these:
|
|
261
|
+
list the scripts they run as Knip entries.
|
|
262
|
+
|
|
263
|
+
With `workspace`, the cache inputs read `bun.lock`, `bunfig.toml` and the
|
|
264
|
+
`inputs` from the workspace root, and `lint-css`, `dead-code-project` and
|
|
265
|
+
`typecheck-node` depend on the tasks in `dependsOn`:
|
|
266
|
+
|
|
267
|
+
```ts
|
|
268
|
+
workspace: {
|
|
269
|
+
dependsOn: ['@evertondgn/solid-tooling#build'],
|
|
270
|
+
inputs: ['packages/solid-tooling/dist/**']
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Changing the result
|
|
275
|
+
|
|
276
|
+
Wrap the result in `defineConfig` and merge what the application needs. Vite+
|
|
277
|
+
removes the plugins it injected before it injects them again, so they are not
|
|
278
|
+
duplicated:
|
|
279
|
+
|
|
280
|
+
```ts
|
|
281
|
+
import { defineSolidApp } from '@evertondgn/solid-tooling/vite'
|
|
282
|
+
import { defineConfig, mergeConfig } from 'vite-plus'
|
|
283
|
+
|
|
284
|
+
const app = defineSolidApp({ root: import.meta.dirname, securityHeaders })
|
|
285
|
+
|
|
286
|
+
export default defineConfig(env =>
|
|
287
|
+
mergeConfig(app(env), { server: { strictPort: true } })
|
|
288
|
+
)
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
## Knip
|
|
292
|
+
|
|
293
|
+
`defineKnipConfig` returns the Knip config of the application as a function, the
|
|
294
|
+
form Knip accepts as the default export of its config file:
|
|
295
|
+
|
|
296
|
+
```ts
|
|
297
|
+
// tooling/knip.ts
|
|
298
|
+
import path from 'node:path'
|
|
299
|
+
|
|
300
|
+
import { defineKnipConfig } from '@evertondgn/solid-tooling/knip'
|
|
301
|
+
|
|
302
|
+
export default defineKnipConfig({
|
|
303
|
+
root: path.resolve(import.meta.dirname, '..'),
|
|
304
|
+
ignoreDependencies: ['server-only']
|
|
305
|
+
})
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
Run it with `knip-bun --config tooling/knip.ts`, then with `--strict`. The
|
|
309
|
+
config lists:
|
|
310
|
+
|
|
311
|
+
- `src/entry-server.tsx`, `src/entry-client.tsx`, `src/middleware/index.ts`,
|
|
312
|
+
`env.ts` and `paraglide/runtime.js` as production entries. The Solid plugin
|
|
313
|
+
and the Paraglide runtime load them by path, and Knip does not read the Solid
|
|
314
|
+
plugin when `vite.config.ts` imports it with `import()`, as `./vite` does. An
|
|
315
|
+
application that imports `solid()` statically, with `start`, passes
|
|
316
|
+
`solidPluginImport: 'static'`: Knip's Solid plugin then finds the first four
|
|
317
|
+
itself, and listing them again would be a configuration hint.
|
|
318
|
+
- Every page and HTTP handler that `filesystem-routing` finds in `src/routes/`,
|
|
319
|
+
as production entries, and in `src/tests/fixtures/routes/` and
|
|
320
|
+
`src/tests/fixtures/e2e/routes/`, as test entries. The `filesystem-routing`
|
|
321
|
+
plugin of Knip is turned off, because it takes every file of `src/routes/` as
|
|
322
|
+
an entry and would hide unused helpers next to the routes.
|
|
323
|
+
- `src/**/*.{ts,tsx}` and `tooling/**/*.ts` as the project, with the tests out
|
|
324
|
+
of the production files.
|
|
325
|
+
|
|
326
|
+
The config turns on `treatConfigHintsAsErrors`, and Knip reports an entry or a
|
|
327
|
+
project pattern without files as a configuration hint. So each default above
|
|
328
|
+
enters only when the application has the file or the folder.
|
|
329
|
+
|
|
330
|
+
| Option | Default | Use |
|
|
331
|
+
| -------------------- | ----------------- | -------------------------------------------------------------- |
|
|
332
|
+
| `root` | required | The application folder |
|
|
333
|
+
| `configFile` | `tooling/knip.ts` | Path of the config file, relative to `root` |
|
|
334
|
+
| `entry` | none | More entries, such as a public API that only test fixtures use |
|
|
335
|
+
| `solidPluginImport` | `'lazy'` | `'static'` when `vite.config.ts` imports `solid()` statically |
|
|
336
|
+
| `ignoreBinaries` | none | Passed to Knip as is |
|
|
337
|
+
| `ignoreDependencies` | none | Passed to Knip as is |
|
|
338
|
+
| `ignoreIssues` | none | Passed to Knip, merged with the entry for `configFile` |
|
|
339
|
+
| `exclude` | none | Issue types to leave out, such as `catalogReferences` |
|
|
340
|
+
|
|
341
|
+
Knip adds the file given to `--config` to the entries in production mode, and
|
|
342
|
+
`--strict` only accepts `dependencies` there, so it reports this package as an
|
|
343
|
+
unlisted dependency of the config file. The config ignores `unlisted` issues in
|
|
344
|
+
`configFile` only: an import of this package from `src/` is still reported. An
|
|
345
|
+
application that keeps the config somewhere else passes its path in
|
|
346
|
+
`configFile`.
|
|
347
|
+
|
|
348
|
+
Nothing that depends on the application is a default. An ignore that matches
|
|
349
|
+
nothing is a configuration hint too, so `server-only` and the binaries of the
|
|
350
|
+
scripts are options. For a key without an option, wrap the function:
|
|
351
|
+
|
|
352
|
+
```ts
|
|
353
|
+
const configure = defineKnipConfig({ root })
|
|
354
|
+
|
|
355
|
+
export default async function configureKnip() {
|
|
356
|
+
return { ...(await configure()), ignoreUnresolved: ['virtual:example'] }
|
|
357
|
+
}
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
## CSS lint
|
|
361
|
+
|
|
362
|
+
`checkCss(root)` lints `src/**/*.css` with Stylelint and the CSS policy of the
|
|
363
|
+
boilerplate, and checks the name of every CSS file in `src/` as
|
|
364
|
+
`project/css-filename` does for imports: `styles.module.css`, or `camelCase.css`
|
|
365
|
+
inside `src/theme/`. It returns the report and whether anything failed:
|
|
366
|
+
|
|
367
|
+
```ts
|
|
368
|
+
// tooling/css/check.ts
|
|
369
|
+
import path from 'node:path'
|
|
370
|
+
import process from 'node:process'
|
|
371
|
+
|
|
372
|
+
import { checkCss } from '@evertondgn/solid-tooling/stylelint'
|
|
373
|
+
|
|
374
|
+
const { report, hasErrors } = await checkCss(
|
|
375
|
+
path.resolve(import.meta.dirname, '../..')
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
process.stdout.write(report)
|
|
379
|
+
if (hasErrors) process.exitCode = 1
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
`defineStylelintConfig(root)` returns the Stylelint config that `checkCss` uses.
|
|
383
|
+
The policy:
|
|
384
|
+
|
|
385
|
+
- Classes use snake_case, nesting goes at most two levels deep, and adjacent
|
|
386
|
+
state selectors and media queries are nested (`stylelint-use-nesting`).
|
|
387
|
+
- Unknown or invalid properties, values, units, selectors, at-rules and media
|
|
388
|
+
features are errors, as are deprecated and unmatchable selectors.
|
|
389
|
+
- Outside `src/theme/`, colors, gradients, radii, shadows, font sizes and
|
|
390
|
+
weights and `z-index` come from tokens with `var()`, from the matching token
|
|
391
|
+
family. Literal colors, color functions and system colors are errors.
|
|
392
|
+
- Inside `src/theme/`, the token rules are off, so the theme can define the
|
|
393
|
+
values with `oklch()`, `oklab()`, `color-mix()`, `light-dark()` and system
|
|
394
|
+
colors. Hex, named colors and the `rgb()`, `hsl()`, `hwb()`, `lab()`, `lch()`
|
|
395
|
+
and `color()` functions are still errors there.
|
|
396
|
+
- `var()` and animation names resolve against `src/theme/tokens/**/*.css` and
|
|
397
|
+
`src/theme/class/animation.css`. Stylelint stops the whole run when a
|
|
398
|
+
reference matches no file, so each one is passed only when it exists.
|
|
399
|
+
- An exception needs a `stylelint-disable` comment with the reason after `--`.
|
|
400
|
+
|
|
401
|
+
`stylelint` and `stylelint-use-nesting` are dependencies of this package. The
|
|
402
|
+
config passes the plugin by a path resolved from the package, because Stylelint
|
|
403
|
+
looks up a plugin name from the application and its working directory, where it
|
|
404
|
+
is not installed.
|
|
405
|
+
|
|
135
406
|
## Project layout the preset expects
|
|
136
407
|
|
|
137
408
|
The rules match file paths relative to the folder where lint runs, so run it
|
|
@@ -275,6 +546,7 @@ overrides: [
|
|
|
275
546
|
| `./fmt` | `fmt`: the formatter config |
|
|
276
547
|
| `./tsconfig.base.json` | Compiler options, without application paths |
|
|
277
548
|
| `./tsconfig.app.json` | The base plus the `types` and `include` of the boilerplate layout |
|
|
549
|
+
| `./vite` | `defineSolidApp(options)`, the whole `vite.config.ts` of an application |
|
|
278
550
|
| `./vitest` | `solidTest(options)`, the `test` block of `vite.config.ts` |
|
|
279
551
|
| `./playwright` | `definePlaywrightConfig(options)`, the Playwright config |
|
|
280
552
|
| `./vitest-setup/browser` | Setup file of the browser project, loaded by `./vitest` |
|
|
@@ -284,11 +556,14 @@ overrides: [
|
|
|
284
556
|
| `./architecture-plugin` | The `architecture` rule |
|
|
285
557
|
| `./solid-plugin` | Re-export of `eslint-plugin-solid` |
|
|
286
558
|
| `./css-filename` | `cssFilenameError(path)`, the CSS naming check, for a script outside lint |
|
|
559
|
+
| `./knip` | `defineKnipConfig(options)`, the Knip config |
|
|
560
|
+
| `./stylelint` | `checkCss(root)` and `defineStylelintConfig(root)`, the CSS lint |
|
|
287
561
|
|
|
288
|
-
An application imports `./
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
the
|
|
562
|
+
An application imports `./vite`, which brings `./lint`, `./fmt` and `./vitest`
|
|
563
|
+
along, `./playwright`, `./knip`, `./stylelint` and one of the TypeScript
|
|
564
|
+
configs. An application that only lints imports `./lint` and `./fmt`. The plugin
|
|
565
|
+
entries exist because Oxlint loads plugins by specifier, from the application;
|
|
566
|
+
the setup and teardown entries are loaded by the test presets.
|
|
292
567
|
|
|
293
568
|
## Formatter
|
|
294
569
|
|
|
@@ -348,6 +623,15 @@ break the `@/` imports of the end-to-end tests.
|
|
|
348
623
|
(`.node.test`, `.dom.test`, `.browser.test`, `.e2e.test`), page tests in
|
|
349
624
|
`src/tests/pages/`, Paraglide output in `paraglide/` and `.env.test` at the
|
|
350
625
|
root.
|
|
626
|
+
- The Knip and CSS configs expect the same layout: pages in `src/routes/`,
|
|
627
|
+
fixture routes in `src/tests/fixtures/` and the theme in `src/theme/`.
|
|
628
|
+
- The Knip config ignores every unlisted dependency in its own file, not only
|
|
629
|
+
this package.
|
|
630
|
+
- `./vite` expects the same layout: its tasks run scripts in `tooling/`, and the
|
|
631
|
+
Markdown rules need the locale prefix of the Paraglide `urlPatterns` in every
|
|
632
|
+
URL. Nitro always uses the `vercel` preset. The plugin packages are pinned, so
|
|
633
|
+
a new version of Solid, Nitro, the router or the icons plugin needs a package
|
|
634
|
+
version tested with it.
|
|
351
635
|
|
|
352
636
|
## Versions
|
|
353
637
|
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { glob } from "node:fs/promises";
|
|
2
|
+
// src/has-match.ts
|
|
3
|
+
async function hasMatch({ root, pattern }) {
|
|
4
|
+
const matches = glob(pattern, { cwd: root });
|
|
5
|
+
const { done } = await matches.next();
|
|
6
|
+
await matches.return?.();
|
|
7
|
+
return !done;
|
|
8
|
+
}
|
|
9
|
+
export { hasMatch as t };
|
package/dist/knip.d.mts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { KnipConfiguration } from "knip";
|
|
2
|
+
type AppSettings = Pick<KnipConfiguration, 'ignoreBinaries' | 'ignoreDependencies' | 'ignoreIssues' | 'exclude'>;
|
|
3
|
+
type SolidPluginImport = 'lazy' | 'static';
|
|
4
|
+
type KnipOptions = AppSettings & {
|
|
5
|
+
root: string;
|
|
6
|
+
configFile?: string;
|
|
7
|
+
entry?: string[];
|
|
8
|
+
solidPluginImport?: SolidPluginImport;
|
|
9
|
+
};
|
|
10
|
+
type ConfigureKnip = () => Promise<KnipConfiguration>;
|
|
11
|
+
export declare function defineKnipConfig({ root, configFile, entry, solidPluginImport, ignoreBinaries, ignoreDependencies, ignoreIssues, exclude }: KnipOptions): ConfigureKnip;
|
|
12
|
+
export {};
|
package/dist/knip.mjs
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { t as hasMatch } from "./has-match-BITlj0EE.mjs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { PageFileSystemRouter } from "filesystem-routing";
|
|
4
|
+
import { DEFAULT_EXTENSIONS } from "filesystem-routing/vite";
|
|
5
|
+
// src/knip/index.ts
|
|
6
|
+
const DEFAULT_CONFIG_FILE = "tooling/knip.ts";
|
|
7
|
+
const SOLID_ENTRIES = [
|
|
8
|
+
"src/entry-server.tsx",
|
|
9
|
+
"src/entry-client.tsx",
|
|
10
|
+
"src/middleware/index.ts",
|
|
11
|
+
"env.ts"
|
|
12
|
+
];
|
|
13
|
+
const RUNTIME_ENTRIES = ["paraglide/runtime.js"];
|
|
14
|
+
const ROUTE_DIRECTORIES = [
|
|
15
|
+
{
|
|
16
|
+
directory: "src/routes",
|
|
17
|
+
isProduction: true
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
directory: "src/tests/fixtures/routes",
|
|
21
|
+
isProduction: false
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
directory: "src/tests/fixtures/e2e/routes",
|
|
25
|
+
isProduction: false
|
|
26
|
+
}
|
|
27
|
+
];
|
|
28
|
+
const TOOLING_PROJECT = "tooling/**/*.ts";
|
|
29
|
+
function isModuleRef(value) {
|
|
30
|
+
return typeof value === "object" && value !== null && "src" in value && typeof value.src === "string";
|
|
31
|
+
}
|
|
32
|
+
function routeSources(route) {
|
|
33
|
+
return Object.entries(route).flatMap(([key, value]) => key.startsWith("$") && isModuleRef(value) ? [value.src] : []);
|
|
34
|
+
}
|
|
35
|
+
async function discoverRouteModules({ root, directory, isProduction }) {
|
|
36
|
+
const routes = await new PageFileSystemRouter({
|
|
37
|
+
dir: path.join(root, directory),
|
|
38
|
+
extensions: DEFAULT_EXTENSIONS,
|
|
39
|
+
httpMethods: true
|
|
40
|
+
}).getRoutes();
|
|
41
|
+
return [...new Set(routes.flatMap((route) => routeSources(route)))].map((source) => path.relative(root, source) + (isProduction ? "!" : ""));
|
|
42
|
+
}
|
|
43
|
+
async function conventionEntries({ root, patterns }) {
|
|
44
|
+
return (await Promise.all(patterns.map(async (pattern) => await hasMatch({
|
|
45
|
+
root,
|
|
46
|
+
pattern
|
|
47
|
+
}) ? [`${pattern}!`] : []))).flat();
|
|
48
|
+
}
|
|
49
|
+
function defineKnipConfig({ root, configFile = DEFAULT_CONFIG_FILE, entry = [], solidPluginImport = "lazy", ignoreBinaries, ignoreDependencies, ignoreIssues = {}, exclude }) {
|
|
50
|
+
const patterns = solidPluginImport === "static" ? RUNTIME_ENTRIES : [...SOLID_ENTRIES, ...RUNTIME_ENTRIES];
|
|
51
|
+
return async function configureKnip() {
|
|
52
|
+
const [conventions, routes, hasTooling] = await Promise.all([
|
|
53
|
+
conventionEntries({
|
|
54
|
+
root,
|
|
55
|
+
patterns
|
|
56
|
+
}),
|
|
57
|
+
Promise.all(ROUTE_DIRECTORIES.map((route) => discoverRouteModules({
|
|
58
|
+
root,
|
|
59
|
+
...route
|
|
60
|
+
}))),
|
|
61
|
+
hasMatch({
|
|
62
|
+
root,
|
|
63
|
+
pattern: TOOLING_PROJECT
|
|
64
|
+
})
|
|
65
|
+
]);
|
|
66
|
+
const configIssues = ignoreIssues[configFile] ?? [];
|
|
67
|
+
return {
|
|
68
|
+
entry: [.../* @__PURE__ */ new Set([
|
|
69
|
+
...conventions,
|
|
70
|
+
...entry,
|
|
71
|
+
...routes.flat()
|
|
72
|
+
])],
|
|
73
|
+
project: [
|
|
74
|
+
"src/**/*.{ts,tsx}!",
|
|
75
|
+
...hasTooling ? [TOOLING_PROJECT] : [],
|
|
76
|
+
"!src/tests/**!",
|
|
77
|
+
"!src/**/__tests__/**!",
|
|
78
|
+
"!src/**/*.test.{ts,tsx}!"
|
|
79
|
+
],
|
|
80
|
+
"filesystem-routing": false,
|
|
81
|
+
...ignoreBinaries ? { ignoreBinaries } : {},
|
|
82
|
+
...ignoreDependencies ? { ignoreDependencies } : {},
|
|
83
|
+
ignoreIssues: {
|
|
84
|
+
...ignoreIssues,
|
|
85
|
+
[configFile]: [.../* @__PURE__ */ new Set([...configIssues, "unlisted"])]
|
|
86
|
+
},
|
|
87
|
+
...exclude ? { exclude } : {},
|
|
88
|
+
treatConfigHintsAsErrors: true
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
export { defineKnipConfig };
|