@integr8-media/config 0.0.0-stage → 1.1.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/LICENSE +21 -0
- package/README.md +330 -2
- package/biome/biome.json +112 -0
- package/dist/configError-Cap5gwBB.mjs +31 -0
- package/dist/configError-DPT5pJ9Z.d.mts +34 -0
- package/dist/sizeLimit.d.mts +34 -0
- package/dist/sizeLimit.mjs +39 -0
- package/dist/tsdown.d.mts +48 -0
- package/dist/tsdown.mjs +79 -0
- package/dist/vitest.d.mts +36 -0
- package/dist/vitest.mjs +57 -0
- package/lefthook/base.yml +25 -0
- package/package.json +114 -4
- package/tsconfig/base.json +19 -0
- package/tsconfig/library.json +12 -0
- package/tsconfig/node-app.json +11 -0
- package/tsconfig/react-app.json +12 -0
- package/tsconfig/react-library.json +14 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mina Louis Leon
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,331 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @integr8-media/config
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Shared tooling presets for every `@integr8-media` repo: TypeScript, Biome, Vitest, tsdown, Lefthook,
|
|
4
|
+
size-limit and publint, plus reusable GitHub Actions workflows. No runtime code.
|
|
5
|
+
|
|
6
|
+
The full spec is in [docs/SPEC.md](docs/SPEC.md).
|
|
7
|
+
|
|
8
|
+
## Requirements
|
|
9
|
+
|
|
10
|
+
- Node 24 LTS
|
|
11
|
+
- TypeScript 5.7 or newer (the presets target ES2024, which TypeScript added in 5.7)
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
npm install --save-dev @integr8-media/config
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Then install the tools you use (all are optional peer dependencies):
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
npm install --save-dev typescript @biomejs/biome vitest @vitest/coverage-v8 tsdown lefthook \
|
|
23
|
+
size-limit @size-limit/file publint @arethetypeswrong/cli @types/node
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Set up a new package repo
|
|
27
|
+
|
|
28
|
+
### TypeScript
|
|
29
|
+
|
|
30
|
+
```jsonc
|
|
31
|
+
// tsconfig.json
|
|
32
|
+
{
|
|
33
|
+
"extends": "@integr8-media/config/tsconfig/react-library.json",
|
|
34
|
+
"include": ["src"],
|
|
35
|
+
"exclude": ["**/*.config.ts", "playground/dist"]
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`include` and `exclude` always go in the repo's own `tsconfig.json`. A preset can't carry them,
|
|
40
|
+
because TypeScript resolves those paths relative to the preset file, which sits inside
|
|
41
|
+
`node_modules`.
|
|
42
|
+
|
|
43
|
+
| Preset | Use for |
|
|
44
|
+
| --- | --- |
|
|
45
|
+
| `@integr8-media/config/tsconfig/library.json` | Node or universal library: NodeNext, declarations, isolatedDeclarations |
|
|
46
|
+
| `@integr8-media/config/tsconfig/react-library.json` | React library: Bundler resolution, DOM types, react-jsx, isolatedDeclarations |
|
|
47
|
+
| `@integr8-media/config/tsconfig/node-app.json` | Node application: NodeNext, no declarations, Node types |
|
|
48
|
+
| `@integr8-media/config/tsconfig/react-app.json` | React application: Bundler, DOM, noEmit |
|
|
49
|
+
|
|
50
|
+
All presets use ES2024, `strict`, `noUncheckedIndexedAccess`, `exactOptionalPropertyTypes`,
|
|
51
|
+
`noImplicitOverride`, `noFallthroughCasesInSwitch`, `verbatimModuleSyntax`,
|
|
52
|
+
`resolveJsonModule`, `forceConsistentCasingInFileNames` and `skipLibCheck`.
|
|
53
|
+
|
|
54
|
+
Notes:
|
|
55
|
+
|
|
56
|
+
- `node-app.json` loads Node's types (`"types": ["node"]`), so install `@types/node`. A Node
|
|
57
|
+
library that needs Node's types adds `"compilerOptions": { "types": ["node"] }` itself;
|
|
58
|
+
`library.json` leaves them out so it also works for universal libraries.
|
|
59
|
+
- With `isolatedDeclarations`, keep `*.config.ts` files out of `include` (their
|
|
60
|
+
`export default someCall()` has no explicit type).
|
|
61
|
+
|
|
62
|
+
### Biome
|
|
63
|
+
|
|
64
|
+
```jsonc
|
|
65
|
+
// biome.json
|
|
66
|
+
{ "extends": ["@integr8-media/config/biome"] }
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
- Formatter: 2-space indent, line width 100, single quotes in TS, double quotes in JSX,
|
|
70
|
+
semicolons always, trailing commas everywhere.
|
|
71
|
+
- Linter: recommended rules, plus as errors: no default exports (allowed in `*.config.ts`),
|
|
72
|
+
camelCase or PascalCase file names, no explicit `any`, `import type` for type-only imports,
|
|
73
|
+
no non-null assertions. `console` is a warning.
|
|
74
|
+
- Ignores `dist/`, `coverage/`, `playground/dist/`, `docs/api/` (TypeDoc output),
|
|
75
|
+
`test-results/` and `playwright-report/` (Playwright output), `.vscode/` and Markdown files
|
|
76
|
+
(Biome 2.6 formats Markdown, which would rewrite docs such as `docs/SPEC.md`). Needs Biome
|
|
77
|
+
2.6 or newer.
|
|
78
|
+
- `vcs` is on (`clientKind: "git"`, `useIgnoreFile: true`), so Biome also skips everything in
|
|
79
|
+
`.gitignore`. Biome fails if there is no `.gitignore` in the folder where it runs, so every
|
|
80
|
+
repo needs one (any git repo has one already).
|
|
81
|
+
- The CSS parser accepts Tailwind v4 directives (`@theme`, `@apply`, `@source`...). Repos
|
|
82
|
+
without Tailwind are not affected.
|
|
83
|
+
- Overrides that turn rules off:
|
|
84
|
+
- `*.config.{ts,mts,js,mjs}`: `style.noDefaultExport`.
|
|
85
|
+
- `.size-limit.{js,mjs,ts}`: `style.noDefaultExport` and `style.useFilenamingConvention`
|
|
86
|
+
(size-limit needs a default export, and the file name starts with a dot).
|
|
87
|
+
- `playground/config.{js,mjs,ts}`: `style.noDefaultExport` (the Ladle config).
|
|
88
|
+
- `scripts/**`: `suspicious.noConsole` (build scripts print progress).
|
|
89
|
+
- If your `biome.json` sets `files.includes`, Biome adds it to the preset's list, so list only
|
|
90
|
+
your extra ignores (for example `"!!.idea"`) and don't repeat `"**"`.
|
|
91
|
+
|
|
92
|
+
### Vitest
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
// vitest.config.ts
|
|
96
|
+
import { integr8Vitest } from '@integr8-media/config/vitest';
|
|
97
|
+
|
|
98
|
+
export default integr8Vitest({
|
|
99
|
+
environment: 'node',
|
|
100
|
+
coverageThreshold: 80,
|
|
101
|
+
setupFiles: './test/setup.ts', // optional: string or list of strings
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
v8 coverage on `src/**` (tests, `src/index.ts` and `playground/` excluded). The run fails when
|
|
106
|
+
line coverage is below the threshold. `environment: 'jsdom'` needs `jsdom` installed.
|
|
107
|
+
|
|
108
|
+
- `setupFiles` goes to `test.setupFiles` (for example a Testing Library setup file). It takes a
|
|
109
|
+
non-empty string or a non-empty list of non-empty strings; anything else throws
|
|
110
|
+
`ConfigError` with `INVALID_OPTION`.
|
|
111
|
+
- Test collection excludes Vitest's defaults plus `playground/**`, so Playwright specs such as
|
|
112
|
+
`playground/smoke.spec.ts` are not run by `vitest run`.
|
|
113
|
+
|
|
114
|
+
For settings the preset doesn't have, wrap it with `mergeConfig` from `vitest/config`:
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
// vitest.config.ts
|
|
118
|
+
import { integr8Vitest } from '@integr8-media/config/vitest';
|
|
119
|
+
import { defineConfig, mergeConfig } from 'vitest/config';
|
|
120
|
+
|
|
121
|
+
export default mergeConfig(
|
|
122
|
+
integr8Vitest({ environment: 'jsdom', setupFiles: './test/setup.ts' }),
|
|
123
|
+
defineConfig({
|
|
124
|
+
test: {
|
|
125
|
+
// Process CSS files that tests import with ?raw.
|
|
126
|
+
css: { include: [/\.css$/] },
|
|
127
|
+
},
|
|
128
|
+
}),
|
|
129
|
+
);
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### tsdown
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
// tsdown.config.ts
|
|
136
|
+
import { integr8Tsdown } from '@integr8-media/config/tsdown';
|
|
137
|
+
|
|
138
|
+
export default integr8Tsdown({ entry: 'src/index.ts', platform: 'browser' });
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
ESM only, `.d.ts` generation (fast through isolatedDeclarations), no source maps, clean output,
|
|
142
|
+
tree-shaking. Target `node24` for `platform: 'node'`, `es2024` otherwise. Dependencies and peer
|
|
143
|
+
dependencies stay external. Files are always named `.mjs` / `.d.mts`, so point `exports` there:
|
|
144
|
+
|
|
145
|
+
```json
|
|
146
|
+
"exports": {
|
|
147
|
+
".": { "types": "./dist/index.d.mts", "default": "./dist/index.mjs" }
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Option `unbundle?: boolean` (default `false`): with `true`, every source file is built as its own
|
|
152
|
+
output file instead of one bundle. Use it for React libraries whose files start with
|
|
153
|
+
`'use client'`. Each file keeps its own directive, so a Next.js server component can import the
|
|
154
|
+
non-client exports from the same package. Rolldown warns `MODULE_LEVEL_DIRECTIVE` for every such
|
|
155
|
+
file; with `unbundle: true` the preset hides that warning, because the directive is kept. With
|
|
156
|
+
`false` the warning stays, because it is real there. A value that is not a boolean throws
|
|
157
|
+
`ConfigError` with `INVALID_OPTION`.
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
export default integr8Tsdown({
|
|
161
|
+
entry: ['src/index.ts', 'src/Button.tsx'],
|
|
162
|
+
platform: 'browser',
|
|
163
|
+
unbundle: true,
|
|
164
|
+
});
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### size-limit
|
|
168
|
+
|
|
169
|
+
```js
|
|
170
|
+
// .size-limit.mjs
|
|
171
|
+
import { integr8SizeLimit } from '@integr8-media/config/size-limit';
|
|
172
|
+
|
|
173
|
+
export default integr8SizeLimit([{ path: 'dist/index.mjs', limit: '10 kB' }]);
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Use `.size-limit.mjs`: it loads without a TypeScript loader, and the Biome preset has an override
|
|
177
|
+
for it (a default export and a dotted file name).
|
|
178
|
+
|
|
179
|
+
Each entry takes `path`, `limit`, and optionally `name` and `import`. size-limit needs a
|
|
180
|
+
plugin: `@size-limit/file` measures built files; `@size-limit/esbuild` is needed for `import`.
|
|
181
|
+
|
|
182
|
+
### Lefthook
|
|
183
|
+
|
|
184
|
+
```yaml
|
|
185
|
+
# lefthook.yml
|
|
186
|
+
extends:
|
|
187
|
+
- node_modules/@integr8-media/config/lefthook/base.yml
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
- pre-commit: `biome check --write` on staged files, fixes re-staged.
|
|
191
|
+
- pre-push, in order, stopping at the first failure: `typecheck`, `test:coverage`, `build`.
|
|
192
|
+
|
|
193
|
+
`playground:smoke` is not in the base: it builds the whole playground and runs Playwright, which
|
|
194
|
+
is too slow for every push. A repo that wants it adds it back in its own `lefthook.yml`; the
|
|
195
|
+
base's `piped: true` then runs it last:
|
|
196
|
+
|
|
197
|
+
```yaml
|
|
198
|
+
# lefthook.yml
|
|
199
|
+
extends:
|
|
200
|
+
- node_modules/@integr8-media/config/lefthook/base.yml
|
|
201
|
+
pre-push:
|
|
202
|
+
commands:
|
|
203
|
+
playground-smoke:
|
|
204
|
+
priority: 4
|
|
205
|
+
run: npm run playground:smoke
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Add `"prepare": "lefthook install"` to your scripts so hooks are installed after `npm install`.
|
|
209
|
+
|
|
210
|
+
### Standard scripts
|
|
211
|
+
|
|
212
|
+
```json
|
|
213
|
+
"scripts": {
|
|
214
|
+
"lint": "biome check",
|
|
215
|
+
"format": "biome format --write",
|
|
216
|
+
"typecheck": "tsc --noEmit",
|
|
217
|
+
"test": "vitest run",
|
|
218
|
+
"test:coverage": "vitest run --coverage",
|
|
219
|
+
"build": "tsdown",
|
|
220
|
+
"playground": "ladle serve --config playground",
|
|
221
|
+
"playground:build": "ladle build --config playground --outDir playground/dist",
|
|
222
|
+
"playground:smoke": "npm run playground:build && playwright test",
|
|
223
|
+
"size": "size-limit",
|
|
224
|
+
"check:exports": "publint && attw --pack --profile esm-only"
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
`--profile esm-only` tells attw not to report the CommonJS problems that every ESM-only package
|
|
229
|
+
has.
|
|
230
|
+
|
|
231
|
+
A package that exports CSS files must exclude them from attw, which only knows JavaScript and
|
|
232
|
+
types: `attw --pack --profile esm-only --exclude-entrypoints ./styles.css`.
|
|
233
|
+
|
|
234
|
+
### GitHub Actions
|
|
235
|
+
|
|
236
|
+
The workflows live in this GitHub repo, not in the npm package. Until a `v1` tag exists, point
|
|
237
|
+
callers at `@main` (`.../pr-checks.yml@main`); once it exists, switch them to `@v1`. The examples
|
|
238
|
+
below show `@v1`. Because the repo is private, its
|
|
239
|
+
**Settings → Actions → General → Access** must allow access from your other repositories.
|
|
240
|
+
|
|
241
|
+
```yaml
|
|
242
|
+
# .github/workflows/ci.yml
|
|
243
|
+
on:
|
|
244
|
+
pull_request:
|
|
245
|
+
branches: [dev]
|
|
246
|
+
jobs:
|
|
247
|
+
checks:
|
|
248
|
+
uses: <owner>/integr8-config/.github/workflows/pr-checks.yml@v1
|
|
249
|
+
with:
|
|
250
|
+
os-matrix: false
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
```yaml
|
|
254
|
+
# .github/workflows/release.yml
|
|
255
|
+
on:
|
|
256
|
+
push:
|
|
257
|
+
tags: ["v*.*.*"]
|
|
258
|
+
permissions:
|
|
259
|
+
contents: read
|
|
260
|
+
id-token: write # npm Trusted Publishing
|
|
261
|
+
jobs:
|
|
262
|
+
publish:
|
|
263
|
+
uses: <owner>/integr8-config/.github/workflows/publish.yml@v1
|
|
264
|
+
with:
|
|
265
|
+
provenance: false # true only for public repos
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
- `pr-checks.yml`: install, lint, typecheck, test:coverage, build, size, check:exports.
|
|
269
|
+
`os-matrix: true` runs on Linux, macOS and Windows.
|
|
270
|
+
- `publish.yml`: checks the tag is `vX.Y.Z`, is on main and matches `package.json`; then
|
|
271
|
+
install, build, test and `npm publish --access public`.
|
|
272
|
+
|
|
273
|
+
#### Publishing with npm Trusted Publishing
|
|
274
|
+
|
|
275
|
+
No npm token is stored in GitHub. The first version of a package has to be published by hand
|
|
276
|
+
(`npm publish --access public`), because npm only offers Trusted Publishing for existing
|
|
277
|
+
packages. Then, once per package:
|
|
278
|
+
|
|
279
|
+
1. On npmjs.com open the package → **Settings → Trusted Publisher → GitHub Actions**.
|
|
280
|
+
2. Enter the GitHub owner, the repo, and the workflow file the tag starts (`release.yml`).
|
|
281
|
+
3. After one release works, set **Publishing access** to "Require two-factor authentication
|
|
282
|
+
and disallow tokens".
|
|
283
|
+
|
|
284
|
+
To release: bump `version` in `package.json` by hand, merge to `main`, then push the tag:
|
|
285
|
+
|
|
286
|
+
```sh
|
|
287
|
+
git tag v1.0.1
|
|
288
|
+
git push origin v1.0.1
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
A package without Trusted Publishing can still pass `secrets: { NPM_TOKEN: ... }` to
|
|
292
|
+
`publish.yml`.
|
|
293
|
+
|
|
294
|
+
## API
|
|
295
|
+
|
|
296
|
+
| Import | Exports |
|
|
297
|
+
| --- | --- |
|
|
298
|
+
| `@integr8-media/config/vitest` | `integr8Vitest(options?)`, `ConfigError` |
|
|
299
|
+
| `@integr8-media/config/tsdown` | `integr8Tsdown(options)`, `ConfigError` |
|
|
300
|
+
| `@integr8-media/config/size-limit` | `integr8SizeLimit(entries)`, `ConfigError` |
|
|
301
|
+
|
|
302
|
+
Invalid options throw `ConfigError` with `code: 'INVALID_OPTION'`. The API reference is
|
|
303
|
+
generated from TSDoc with `npm run docs` (output in `docs/api/`).
|
|
304
|
+
|
|
305
|
+
## Non-goals
|
|
306
|
+
|
|
307
|
+
- No runtime code and nothing app-specific
|
|
308
|
+
- No ESLint or Prettier presets
|
|
309
|
+
- No versioning tool (versions are bumped by hand)
|
|
310
|
+
|
|
311
|
+
## Working on this repo
|
|
312
|
+
|
|
313
|
+
```sh
|
|
314
|
+
npm install
|
|
315
|
+
npm run lint
|
|
316
|
+
npm run typecheck
|
|
317
|
+
npm run test:coverage # unit tests + integration tests that run each tool on fixtures/
|
|
318
|
+
npm run build
|
|
319
|
+
npm run size
|
|
320
|
+
npm run check:exports
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
- Unit tests sit next to the code in `src/`.
|
|
324
|
+
- `test/` holds integration tests that run tsc, Biome, tsdown, Vitest, publint, attw and
|
|
325
|
+
Lefthook against `fixtures/`. They need `git` on the PATH, and on Windows Lefthook needs Git
|
|
326
|
+
Bash's `sh`.
|
|
327
|
+
- The React fixtures use a small JSX type stub instead of installing React.
|
|
328
|
+
|
|
329
|
+
## License
|
|
330
|
+
|
|
331
|
+
MIT
|
package/biome/biome.json
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
{
|
|
2
|
+
"files": {
|
|
3
|
+
"includes": [
|
|
4
|
+
"**",
|
|
5
|
+
"!**/dist",
|
|
6
|
+
"!**/coverage",
|
|
7
|
+
"!**/playground/dist",
|
|
8
|
+
"!**/docs/api",
|
|
9
|
+
"!**/test-results",
|
|
10
|
+
"!**/playwright-report",
|
|
11
|
+
"!**/.vscode",
|
|
12
|
+
"!**/*.md"
|
|
13
|
+
]
|
|
14
|
+
},
|
|
15
|
+
"vcs": {
|
|
16
|
+
"enabled": true,
|
|
17
|
+
"clientKind": "git",
|
|
18
|
+
"useIgnoreFile": true
|
|
19
|
+
},
|
|
20
|
+
"css": {
|
|
21
|
+
"parser": {
|
|
22
|
+
"tailwindDirectives": true
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"formatter": {
|
|
26
|
+
"enabled": true,
|
|
27
|
+
"indentStyle": "space",
|
|
28
|
+
"indentWidth": 2,
|
|
29
|
+
"lineWidth": 100
|
|
30
|
+
},
|
|
31
|
+
"javascript": {
|
|
32
|
+
"formatter": {
|
|
33
|
+
"quoteStyle": "single",
|
|
34
|
+
"jsxQuoteStyle": "double",
|
|
35
|
+
"semicolons": "always",
|
|
36
|
+
"trailingCommas": "all"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"linter": {
|
|
40
|
+
"enabled": true,
|
|
41
|
+
"rules": {
|
|
42
|
+
"preset": "recommended",
|
|
43
|
+
"style": {
|
|
44
|
+
"noDefaultExport": "error",
|
|
45
|
+
"noNonNullAssertion": "error",
|
|
46
|
+
"useImportType": "error",
|
|
47
|
+
"useFilenamingConvention": {
|
|
48
|
+
"level": "error",
|
|
49
|
+
"options": {
|
|
50
|
+
"filenameCases": ["camelCase", "PascalCase"],
|
|
51
|
+
"requireAscii": true
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"suspicious": {
|
|
56
|
+
"noExplicitAny": "error",
|
|
57
|
+
"noConsole": "warn"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"assist": {
|
|
62
|
+
"enabled": true,
|
|
63
|
+
"actions": {
|
|
64
|
+
"source": {
|
|
65
|
+
"organizeImports": "on"
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
"overrides": [
|
|
70
|
+
{
|
|
71
|
+
"includes": ["**/*.config.ts", "**/*.config.mts", "**/*.config.js", "**/*.config.mjs"],
|
|
72
|
+
"linter": {
|
|
73
|
+
"rules": {
|
|
74
|
+
"style": {
|
|
75
|
+
"noDefaultExport": "off"
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"includes": ["**/.size-limit.js", "**/.size-limit.mjs", "**/.size-limit.ts"],
|
|
82
|
+
"linter": {
|
|
83
|
+
"rules": {
|
|
84
|
+
"style": {
|
|
85
|
+
"noDefaultExport": "off",
|
|
86
|
+
"useFilenamingConvention": "off"
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
"includes": ["**/playground/config.js", "**/playground/config.mjs", "**/playground/config.ts"],
|
|
93
|
+
"linter": {
|
|
94
|
+
"rules": {
|
|
95
|
+
"style": {
|
|
96
|
+
"noDefaultExport": "off"
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"includes": ["**/scripts/**"],
|
|
103
|
+
"linter": {
|
|
104
|
+
"rules": {
|
|
105
|
+
"suspicious": {
|
|
106
|
+
"noConsole": "off"
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
]
|
|
112
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// src/errors/configError.ts
|
|
2
|
+
/**
|
|
3
|
+
* Thrown by the preset functions when they receive options they cannot turn into a config.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```ts
|
|
7
|
+
* try {
|
|
8
|
+
* integr8Vitest({ coverageThreshold: 120 });
|
|
9
|
+
* } catch (error) {
|
|
10
|
+
* if (error instanceof ConfigError && error.code === 'INVALID_OPTION') {
|
|
11
|
+
* // handle it
|
|
12
|
+
* }
|
|
13
|
+
* }
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
var ConfigError = class extends Error {
|
|
17
|
+
/** Always `'ConfigError'`. */
|
|
18
|
+
name = "ConfigError";
|
|
19
|
+
/** Machine-readable reason for the error. */
|
|
20
|
+
code;
|
|
21
|
+
/**
|
|
22
|
+
* @param code - Machine-readable reason for the error.
|
|
23
|
+
* @param message - Human-readable description.
|
|
24
|
+
* @param options - Standard error options, such as `cause`.
|
|
25
|
+
*/
|
|
26
|
+
constructor(code, message, options) {
|
|
27
|
+
super(message, options);
|
|
28
|
+
this.code = code;
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
export { ConfigError as t };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codes carried by {@link ConfigError}.
|
|
3
|
+
*
|
|
4
|
+
* - `INVALID_OPTION`: an option passed to a preset function is missing, has the wrong type,
|
|
5
|
+
* or is out of range.
|
|
6
|
+
*/
|
|
7
|
+
type ConfigErrorCode = "INVALID_OPTION";
|
|
8
|
+
/**
|
|
9
|
+
* Thrown by the preset functions when they receive options they cannot turn into a config.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* try {
|
|
14
|
+
* integr8Vitest({ coverageThreshold: 120 });
|
|
15
|
+
* } catch (error) {
|
|
16
|
+
* if (error instanceof ConfigError && error.code === 'INVALID_OPTION') {
|
|
17
|
+
* // handle it
|
|
18
|
+
* }
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
declare class ConfigError extends Error {
|
|
23
|
+
/** Always `'ConfigError'`. */
|
|
24
|
+
override readonly name: "ConfigError";
|
|
25
|
+
/** Machine-readable reason for the error. */
|
|
26
|
+
readonly code: ConfigErrorCode;
|
|
27
|
+
/**
|
|
28
|
+
* @param code - Machine-readable reason for the error.
|
|
29
|
+
* @param message - Human-readable description.
|
|
30
|
+
* @param options - Standard error options, such as `cause`.
|
|
31
|
+
*/
|
|
32
|
+
constructor(code: ConfigErrorCode, message: string, options?: ErrorOptions);
|
|
33
|
+
}
|
|
34
|
+
export { ConfigErrorCode as n, ConfigError as t };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { n as ConfigErrorCode, t as ConfigError } from "./configError-DPT5pJ9Z.mjs";
|
|
2
|
+
import { SizeLimitConfig } from "size-limit";
|
|
3
|
+
/** One file (or group of files) to measure. */
|
|
4
|
+
interface Integr8SizeLimitEntry {
|
|
5
|
+
/** Built file(s) to measure, for example `'dist/index.mjs'`. */
|
|
6
|
+
path: string | string[];
|
|
7
|
+
/** Largest allowed size, for example `'5 kB'`. */
|
|
8
|
+
limit: string;
|
|
9
|
+
/** Label in the report. Defaults to the path. */
|
|
10
|
+
name?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Named imports to measure instead of the whole file (tree-shaking check), for example
|
|
13
|
+
* `'{ Button }'`. Needs a bundler plugin such as `@size-limit/esbuild`.
|
|
14
|
+
*/
|
|
15
|
+
import?: string | Record<string, string>;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Builds size-limit checks from a short list of entries. Each entry gets a readable name
|
|
19
|
+
* (the path when none is given) and is validated before size-limit runs.
|
|
20
|
+
*
|
|
21
|
+
* @param entries - Files to measure and their limits. At least one is required.
|
|
22
|
+
* @returns size-limit checks to default-export from `.size-limit.js` (or `.size-limit.ts`).
|
|
23
|
+
* @throws {@link ConfigError} with code `INVALID_OPTION` for an empty list, a missing path or
|
|
24
|
+
* an invalid limit.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* // .size-limit.ts
|
|
29
|
+
* import { integr8SizeLimit } from '@integr8-media/config/size-limit';
|
|
30
|
+
* export default integr8SizeLimit([{ path: 'dist/index.mjs', limit: '10 kB' }]);
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export declare function integr8SizeLimit(entries: readonly Integr8SizeLimitEntry[]): SizeLimitConfig;
|
|
34
|
+
export { ConfigError, type ConfigErrorCode, type Integr8SizeLimitEntry };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { t as ConfigError } from "./configError-Cap5gwBB.mjs";
|
|
2
|
+
// src/sizeLimit/integr8SizeLimit.ts
|
|
3
|
+
const LIMIT_PATTERN = /^\d+(\.\d+)?\s*[a-z]+$/i;
|
|
4
|
+
/**
|
|
5
|
+
* Builds size-limit checks from a short list of entries. Each entry gets a readable name
|
|
6
|
+
* (the path when none is given) and is validated before size-limit runs.
|
|
7
|
+
*
|
|
8
|
+
* @param entries - Files to measure and their limits. At least one is required.
|
|
9
|
+
* @returns size-limit checks to default-export from `.size-limit.js` (or `.size-limit.ts`).
|
|
10
|
+
* @throws {@link ConfigError} with code `INVALID_OPTION` for an empty list, a missing path or
|
|
11
|
+
* an invalid limit.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* // .size-limit.ts
|
|
16
|
+
* import { integr8SizeLimit } from '@integr8-media/config/size-limit';
|
|
17
|
+
* export default integr8SizeLimit([{ path: 'dist/index.mjs', limit: '10 kB' }]);
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
function integr8SizeLimit(entries) {
|
|
21
|
+
if (!Array.isArray(entries) || entries.length === 0) throw new ConfigError("INVALID_OPTION", "integr8SizeLimit: \"entries\" must be a non-empty list.");
|
|
22
|
+
return entries.map((entry, index) => toCheck(entry, index));
|
|
23
|
+
}
|
|
24
|
+
function toCheck(entry, index) {
|
|
25
|
+
const where = `integr8SizeLimit: entry ${index}`;
|
|
26
|
+
if (typeof entry !== "object" || entry === null) throw new ConfigError("INVALID_OPTION", `${where} must be an object.`);
|
|
27
|
+
const paths = typeof entry.path === "string" ? [entry.path] : entry.path;
|
|
28
|
+
if (!Array.isArray(paths) || paths.length === 0 || !paths.every((path) => typeof path === "string" && path.trim() !== "")) throw new ConfigError("INVALID_OPTION", `${where}: "path" must be a non-empty path or list of paths.`);
|
|
29
|
+
if (typeof entry.limit !== "string" || !LIMIT_PATTERN.test(entry.limit.trim())) throw new ConfigError("INVALID_OPTION", `${where}: "limit" must look like "10 kB"; got ${String(entry.limit)}.`);
|
|
30
|
+
if (entry.name !== void 0 && (typeof entry.name !== "string" || entry.name.trim() === "")) throw new ConfigError("INVALID_OPTION", `${where}: "name" must be a non-empty string.`);
|
|
31
|
+
const check = {
|
|
32
|
+
name: entry.name ?? paths.join(", "),
|
|
33
|
+
path: typeof entry.path === "string" ? entry.path : [...paths],
|
|
34
|
+
limit: entry.limit.trim()
|
|
35
|
+
};
|
|
36
|
+
if (entry.import !== void 0) check.import = typeof entry.import === "string" ? entry.import : { ...entry.import };
|
|
37
|
+
return check;
|
|
38
|
+
}
|
|
39
|
+
export { ConfigError, integr8SizeLimit };
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { n as ConfigErrorCode, t as ConfigError } from "./configError-DPT5pJ9Z.mjs";
|
|
2
|
+
import { UserConfig } from "tsdown";
|
|
3
|
+
/** Platforms the tsdown preset can build for. */
|
|
4
|
+
type Integr8TsdownPlatform = "node" | "browser" | "neutral";
|
|
5
|
+
/** Entry points: one path, a list of paths, or a map of output name to path. */
|
|
6
|
+
type Integr8TsdownEntry = string | string[] | Record<string, string>;
|
|
7
|
+
/** Options for {@link integr8Tsdown}. */
|
|
8
|
+
interface Integr8TsdownOptions {
|
|
9
|
+
/** Entry file(s) to build. */
|
|
10
|
+
entry: Integr8TsdownEntry;
|
|
11
|
+
/** Target platform. `node` builds for `node24`; the others build for `es2024`. */
|
|
12
|
+
platform: Integr8TsdownPlatform;
|
|
13
|
+
/**
|
|
14
|
+
* Build every source file as its own output file instead of one bundle. Defaults to `false`.
|
|
15
|
+
*
|
|
16
|
+
* Use it for React libraries whose files start with `'use client'`: each file is built on its
|
|
17
|
+
* own, so the directive stays on it and a Next.js server component can import the
|
|
18
|
+
* non-client exports from the same package. The preset then hides rolldown's
|
|
19
|
+
* `MODULE_LEVEL_DIRECTIVE` warning, which is only noise in that mode. With `false` the
|
|
20
|
+
* warning stays on, because there it points at a directive that the bundle drops.
|
|
21
|
+
*/
|
|
22
|
+
unbundle?: boolean;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Builds the shared tsdown config: ESM only, `.d.ts` generation (fast path through
|
|
26
|
+
* isolatedDeclarations), no source maps, a clean output folder and tree-shaking.
|
|
27
|
+
*
|
|
28
|
+
* With `unbundle: true` each source file becomes its own output file (see
|
|
29
|
+
* {@link Integr8TsdownOptions.unbundle}).
|
|
30
|
+
*
|
|
31
|
+
* Output files always use `.mjs` and `.d.mts`, whatever the platform, so a package's
|
|
32
|
+
* `exports` map is the same everywhere. Dependencies and peer dependencies stay external
|
|
33
|
+
* (tsdown's default).
|
|
34
|
+
*
|
|
35
|
+
* @param options - Entry points, target platform and the optional `unbundle` switch.
|
|
36
|
+
* @returns A tsdown config to default-export from `tsdown.config.ts`.
|
|
37
|
+
* @throws {@link ConfigError} with code `INVALID_OPTION` for an empty entry, an unknown
|
|
38
|
+
* platform or an `unbundle` that is not a boolean.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```ts
|
|
42
|
+
* // tsdown.config.ts
|
|
43
|
+
* import { integr8Tsdown } from '@integr8-media/config/tsdown';
|
|
44
|
+
* export default integr8Tsdown({ entry: 'src/index.ts', platform: 'browser' });
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export declare function integr8Tsdown(options: Integr8TsdownOptions): UserConfig;
|
|
48
|
+
export { ConfigError, type ConfigErrorCode, type Integr8TsdownEntry, type Integr8TsdownOptions, type Integr8TsdownPlatform };
|
package/dist/tsdown.mjs
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { t as ConfigError } from "./configError-Cap5gwBB.mjs";
|
|
2
|
+
// src/tsdown/integr8Tsdown.ts
|
|
3
|
+
const PLATFORMS = [
|
|
4
|
+
"node",
|
|
5
|
+
"browser",
|
|
6
|
+
"neutral"
|
|
7
|
+
];
|
|
8
|
+
/**
|
|
9
|
+
* Builds the shared tsdown config: ESM only, `.d.ts` generation (fast path through
|
|
10
|
+
* isolatedDeclarations), no source maps, a clean output folder and tree-shaking.
|
|
11
|
+
*
|
|
12
|
+
* With `unbundle: true` each source file becomes its own output file (see
|
|
13
|
+
* {@link Integr8TsdownOptions.unbundle}).
|
|
14
|
+
*
|
|
15
|
+
* Output files always use `.mjs` and `.d.mts`, whatever the platform, so a package's
|
|
16
|
+
* `exports` map is the same everywhere. Dependencies and peer dependencies stay external
|
|
17
|
+
* (tsdown's default).
|
|
18
|
+
*
|
|
19
|
+
* @param options - Entry points, target platform and the optional `unbundle` switch.
|
|
20
|
+
* @returns A tsdown config to default-export from `tsdown.config.ts`.
|
|
21
|
+
* @throws {@link ConfigError} with code `INVALID_OPTION` for an empty entry, an unknown
|
|
22
|
+
* platform or an `unbundle` that is not a boolean.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* // tsdown.config.ts
|
|
27
|
+
* import { integr8Tsdown } from '@integr8-media/config/tsdown';
|
|
28
|
+
* export default integr8Tsdown({ entry: 'src/index.ts', platform: 'browser' });
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
function integr8Tsdown(options) {
|
|
32
|
+
if (typeof options !== "object" || options === null) throw new ConfigError("INVALID_OPTION", "integr8Tsdown: options are required.");
|
|
33
|
+
const { platform } = options;
|
|
34
|
+
if (!PLATFORMS.includes(platform)) throw new ConfigError("INVALID_OPTION", `integr8Tsdown: "platform" must be one of ${PLATFORMS.join(", ")}; got ${String(platform)}.`);
|
|
35
|
+
const { unbundle = false } = options;
|
|
36
|
+
if (typeof unbundle !== "boolean") throw new ConfigError("INVALID_OPTION", `integr8Tsdown: "unbundle" must be a boolean; got ${String(unbundle)}.`);
|
|
37
|
+
return {
|
|
38
|
+
entry: copyEntry(options.entry),
|
|
39
|
+
platform,
|
|
40
|
+
target: platform === "node" ? "node24" : "es2024",
|
|
41
|
+
format: "esm",
|
|
42
|
+
fixedExtension: true,
|
|
43
|
+
dts: true,
|
|
44
|
+
sourcemap: false,
|
|
45
|
+
clean: true,
|
|
46
|
+
treeshake: true,
|
|
47
|
+
...unbundle ? {
|
|
48
|
+
unbundle: true,
|
|
49
|
+
inputOptions: { onLog: dropDirectiveWarning }
|
|
50
|
+
} : {}
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function dropDirectiveWarning(...[level, log, defaultHandler]) {
|
|
54
|
+
if (log.code === "MODULE_LEVEL_DIRECTIVE") return;
|
|
55
|
+
defaultHandler(level, log);
|
|
56
|
+
}
|
|
57
|
+
function copyEntry(entry) {
|
|
58
|
+
if (typeof entry === "string") {
|
|
59
|
+
if (entry.trim() === "") throw invalidEntry();
|
|
60
|
+
return entry;
|
|
61
|
+
}
|
|
62
|
+
if (Array.isArray(entry)) {
|
|
63
|
+
if (entry.length === 0 || !entry.every(isNonEmptyString)) throw invalidEntry();
|
|
64
|
+
return [...entry];
|
|
65
|
+
}
|
|
66
|
+
if (typeof entry === "object" && entry !== null) {
|
|
67
|
+
const values = Object.values(entry);
|
|
68
|
+
if (values.length === 0 || !values.every(isNonEmptyString)) throw invalidEntry();
|
|
69
|
+
return { ...entry };
|
|
70
|
+
}
|
|
71
|
+
throw invalidEntry();
|
|
72
|
+
}
|
|
73
|
+
function isNonEmptyString(value) {
|
|
74
|
+
return typeof value === "string" && value.trim() !== "";
|
|
75
|
+
}
|
|
76
|
+
function invalidEntry() {
|
|
77
|
+
return new ConfigError("INVALID_OPTION", "integr8Tsdown: \"entry\" must be a non-empty path, a non-empty list of paths, or a non-empty map of name to path.");
|
|
78
|
+
}
|
|
79
|
+
export { ConfigError, integr8Tsdown };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { n as ConfigErrorCode, t as ConfigError } from "./configError-DPT5pJ9Z.mjs";
|
|
2
|
+
import { ViteUserConfig } from "vitest/config";
|
|
3
|
+
/** Test environments the Vitest preset supports. */
|
|
4
|
+
type Integr8VitestEnvironment = "node" | "jsdom";
|
|
5
|
+
/** Options for {@link integr8Vitest}. */
|
|
6
|
+
interface Integr8VitestOptions {
|
|
7
|
+
/** Test environment. Defaults to `'node'`. `'jsdom'` needs the `jsdom` package installed. */
|
|
8
|
+
environment?: Integr8VitestEnvironment;
|
|
9
|
+
/** Minimum line coverage in percent (0–100). Defaults to `80`. */
|
|
10
|
+
coverageThreshold?: number;
|
|
11
|
+
/**
|
|
12
|
+
* Setup file(s) run before each test file, passed to `test.setupFiles` (for example
|
|
13
|
+
* `'./test/setup.ts'` for Testing Library). Non-empty strings only.
|
|
14
|
+
*/
|
|
15
|
+
setupFiles?: string | string[];
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Builds the shared Vitest config: v8 coverage on `src/**` (tests, `src/index.ts` and
|
|
19
|
+
* `playground/` excluded) that fails when line coverage is below the threshold. Test collection
|
|
20
|
+
* skips `playground/**` too, so Playwright specs there are not run by Vitest.
|
|
21
|
+
*
|
|
22
|
+
* @param options - Environment, coverage threshold and setup files.
|
|
23
|
+
* @returns A Vitest config to default-export from `vitest.config.ts`.
|
|
24
|
+
* @throws {@link ConfigError} with code `INVALID_OPTION` for an unknown environment or a
|
|
25
|
+
* threshold outside 0–100, or for `setupFiles` that is not a non-empty string or a non-empty
|
|
26
|
+
* list of non-empty strings.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```ts
|
|
30
|
+
* // vitest.config.ts
|
|
31
|
+
* import { integr8Vitest } from '@integr8-media/config/vitest';
|
|
32
|
+
* export default integr8Vitest({ environment: 'jsdom' });
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export declare function integr8Vitest(options?: Integr8VitestOptions): ViteUserConfig;
|
|
36
|
+
export { ConfigError, type ConfigErrorCode, type Integr8VitestEnvironment, type Integr8VitestOptions };
|
package/dist/vitest.mjs
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { t as ConfigError } from "./configError-Cap5gwBB.mjs";
|
|
2
|
+
import { configDefaults } from "vitest/config";
|
|
3
|
+
// src/vitest/integr8Vitest.ts
|
|
4
|
+
const ENVIRONMENTS = ["node", "jsdom"];
|
|
5
|
+
const DEFAULT_COVERAGE_THRESHOLD = 80;
|
|
6
|
+
/**
|
|
7
|
+
* Builds the shared Vitest config: v8 coverage on `src/**` (tests, `src/index.ts` and
|
|
8
|
+
* `playground/` excluded) that fails when line coverage is below the threshold. Test collection
|
|
9
|
+
* skips `playground/**` too, so Playwright specs there are not run by Vitest.
|
|
10
|
+
*
|
|
11
|
+
* @param options - Environment, coverage threshold and setup files.
|
|
12
|
+
* @returns A Vitest config to default-export from `vitest.config.ts`.
|
|
13
|
+
* @throws {@link ConfigError} with code `INVALID_OPTION` for an unknown environment or a
|
|
14
|
+
* threshold outside 0–100, or for `setupFiles` that is not a non-empty string or a non-empty
|
|
15
|
+
* list of non-empty strings.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* // vitest.config.ts
|
|
20
|
+
* import { integr8Vitest } from '@integr8-media/config/vitest';
|
|
21
|
+
* export default integr8Vitest({ environment: 'jsdom' });
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
function integr8Vitest(options = {}) {
|
|
25
|
+
const environment = options.environment ?? "node";
|
|
26
|
+
const coverageThreshold = options.coverageThreshold ?? DEFAULT_COVERAGE_THRESHOLD;
|
|
27
|
+
if (!ENVIRONMENTS.includes(environment)) throw new ConfigError("INVALID_OPTION", `integr8Vitest: "environment" must be one of ${ENVIRONMENTS.join(", ")}; got ${String(environment)}.`);
|
|
28
|
+
if (typeof coverageThreshold !== "number" || !Number.isFinite(coverageThreshold) || coverageThreshold < 0 || coverageThreshold > 100) throw new ConfigError("INVALID_OPTION", `integr8Vitest: "coverageThreshold" must be a number from 0 to 100; got ${String(coverageThreshold)}.`);
|
|
29
|
+
const setupFiles = copySetupFiles(options.setupFiles);
|
|
30
|
+
return { test: {
|
|
31
|
+
environment,
|
|
32
|
+
exclude: [...configDefaults.exclude, "playground/**"],
|
|
33
|
+
...setupFiles === void 0 ? {} : { setupFiles },
|
|
34
|
+
coverage: {
|
|
35
|
+
provider: "v8",
|
|
36
|
+
include: ["src/**/*.{ts,tsx}"],
|
|
37
|
+
exclude: [
|
|
38
|
+
"src/**/*.test.ts",
|
|
39
|
+
"src/**/*.test.tsx",
|
|
40
|
+
"src/**/*.d.ts",
|
|
41
|
+
"src/index.ts",
|
|
42
|
+
"playground/**"
|
|
43
|
+
],
|
|
44
|
+
thresholds: { lines: coverageThreshold }
|
|
45
|
+
}
|
|
46
|
+
} };
|
|
47
|
+
}
|
|
48
|
+
function copySetupFiles(setupFiles) {
|
|
49
|
+
if (setupFiles === void 0) return;
|
|
50
|
+
if (isNonEmptyString(setupFiles)) return setupFiles;
|
|
51
|
+
if (Array.isArray(setupFiles) && setupFiles.length > 0 && setupFiles.every(isNonEmptyString)) return [...setupFiles];
|
|
52
|
+
throw new ConfigError("INVALID_OPTION", "integr8Vitest: \"setupFiles\" must be a non-empty path or a non-empty list of non-empty paths.");
|
|
53
|
+
}
|
|
54
|
+
function isNonEmptyString(value) {
|
|
55
|
+
return typeof value === "string" && value.trim() !== "";
|
|
56
|
+
}
|
|
57
|
+
export { ConfigError, integr8Vitest };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# @integr8-media/config Lefthook base.
|
|
2
|
+
# Use it from a repo's lefthook.yml:
|
|
3
|
+
# extends:
|
|
4
|
+
# - node_modules/@integr8-media/config/lefthook/base.yml
|
|
5
|
+
|
|
6
|
+
pre-commit:
|
|
7
|
+
commands:
|
|
8
|
+
biome:
|
|
9
|
+
glob: "*.{js,jsx,mjs,cjs,ts,tsx,mts,cts,json,jsonc,css}"
|
|
10
|
+
run: npx --no biome check --write --no-errors-on-unmatched --files-ignore-unknown=true {staged_files}
|
|
11
|
+
stage_fixed: true
|
|
12
|
+
|
|
13
|
+
pre-push:
|
|
14
|
+
# piped: run in order and stop at the first failure.
|
|
15
|
+
piped: true
|
|
16
|
+
commands:
|
|
17
|
+
typecheck:
|
|
18
|
+
priority: 1
|
|
19
|
+
run: npm run typecheck
|
|
20
|
+
test-coverage:
|
|
21
|
+
priority: 2
|
|
22
|
+
run: npm run test:coverage
|
|
23
|
+
build:
|
|
24
|
+
priority: 3
|
|
25
|
+
run: npm run build
|
package/package.json
CHANGED
|
@@ -1,6 +1,116 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@integr8-media/config",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "Shared tooling presets for every @integr8-media repo: TypeScript, Biome, Vitest, tsdown, Lefthook and size-limit.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Mina Louis Leon",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/MinaLouisLeon/integr8-config.git"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/MinaLouisLeon/integr8-config#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/MinaLouisLeon/integr8-config/issues"
|
|
14
|
+
},
|
|
15
|
+
"type": "module",
|
|
16
|
+
"sideEffects": false,
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=24"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"tsconfig",
|
|
23
|
+
"biome",
|
|
24
|
+
"lefthook"
|
|
25
|
+
],
|
|
26
|
+
"exports": {
|
|
27
|
+
"./tsconfig/library.json": "./tsconfig/library.json",
|
|
28
|
+
"./tsconfig/react-library.json": "./tsconfig/react-library.json",
|
|
29
|
+
"./tsconfig/node-app.json": "./tsconfig/node-app.json",
|
|
30
|
+
"./tsconfig/react-app.json": "./tsconfig/react-app.json",
|
|
31
|
+
"./biome": "./biome/biome.json",
|
|
32
|
+
"./vitest": {
|
|
33
|
+
"types": "./dist/vitest.d.mts",
|
|
34
|
+
"default": "./dist/vitest.mjs"
|
|
35
|
+
},
|
|
36
|
+
"./tsdown": {
|
|
37
|
+
"types": "./dist/tsdown.d.mts",
|
|
38
|
+
"default": "./dist/tsdown.mjs"
|
|
39
|
+
},
|
|
40
|
+
"./lefthook/base.yml": "./lefthook/base.yml",
|
|
41
|
+
"./size-limit": {
|
|
42
|
+
"types": "./dist/sizeLimit.d.mts",
|
|
43
|
+
"default": "./dist/sizeLimit.mjs"
|
|
44
|
+
},
|
|
45
|
+
"./package.json": "./package.json"
|
|
46
|
+
},
|
|
47
|
+
"publishConfig": {
|
|
48
|
+
"access": "public"
|
|
49
|
+
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"lint": "biome check",
|
|
52
|
+
"format": "biome format --write",
|
|
53
|
+
"typecheck": "tsc --noEmit",
|
|
54
|
+
"test": "vitest run",
|
|
55
|
+
"test:coverage": "vitest run --coverage",
|
|
56
|
+
"build": "tsdown",
|
|
57
|
+
"size": "size-limit",
|
|
58
|
+
"check:exports": "publint && attw --pack --profile esm-only --entrypoints ./vitest ./tsdown ./size-limit",
|
|
59
|
+
"docs": "typedoc",
|
|
60
|
+
"prepare": "lefthook install"
|
|
61
|
+
},
|
|
62
|
+
"peerDependencies": {
|
|
63
|
+
"@arethetypeswrong/cli": ">=0.17.0",
|
|
64
|
+
"@biomejs/biome": ">=2.6.0 <3",
|
|
65
|
+
"@vitest/coverage-v8": ">=4.0.0",
|
|
66
|
+
"lefthook": ">=1.10.0",
|
|
67
|
+
"publint": ">=0.3.0",
|
|
68
|
+
"size-limit": ">=11.0.0",
|
|
69
|
+
"tsdown": ">=0.23.0",
|
|
70
|
+
"typescript": ">=5.7.0",
|
|
71
|
+
"vitest": ">=4.0.0"
|
|
72
|
+
},
|
|
73
|
+
"peerDependenciesMeta": {
|
|
74
|
+
"@arethetypeswrong/cli": {
|
|
75
|
+
"optional": true
|
|
76
|
+
},
|
|
77
|
+
"@biomejs/biome": {
|
|
78
|
+
"optional": true
|
|
79
|
+
},
|
|
80
|
+
"@vitest/coverage-v8": {
|
|
81
|
+
"optional": true
|
|
82
|
+
},
|
|
83
|
+
"lefthook": {
|
|
84
|
+
"optional": true
|
|
85
|
+
},
|
|
86
|
+
"publint": {
|
|
87
|
+
"optional": true
|
|
88
|
+
},
|
|
89
|
+
"size-limit": {
|
|
90
|
+
"optional": true
|
|
91
|
+
},
|
|
92
|
+
"tsdown": {
|
|
93
|
+
"optional": true
|
|
94
|
+
},
|
|
95
|
+
"typescript": {
|
|
96
|
+
"optional": true
|
|
97
|
+
},
|
|
98
|
+
"vitest": {
|
|
99
|
+
"optional": true
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
"devDependencies": {
|
|
103
|
+
"@arethetypeswrong/cli": "^0.18.5",
|
|
104
|
+
"@biomejs/biome": "^2.6.5",
|
|
105
|
+
"@size-limit/file": "^14.3.0",
|
|
106
|
+
"@types/node": "^24.0.0",
|
|
107
|
+
"@vitest/coverage-v8": "^5.1.0",
|
|
108
|
+
"lefthook": "^2.2.2",
|
|
109
|
+
"publint": "^0.3.25",
|
|
110
|
+
"size-limit": "^14.3.0",
|
|
111
|
+
"tsdown": "^0.23.5",
|
|
112
|
+
"typedoc": "^0.28.20",
|
|
113
|
+
"typescript": "~6.0.0",
|
|
114
|
+
"vitest": "^5.1.0"
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/tsconfig",
|
|
3
|
+
"display": "@integr8-media/config base",
|
|
4
|
+
"compilerOptions": {
|
|
5
|
+
"target": "ES2024",
|
|
6
|
+
"lib": ["ES2024"],
|
|
7
|
+
|
|
8
|
+
"strict": true,
|
|
9
|
+
"noUncheckedIndexedAccess": true,
|
|
10
|
+
"exactOptionalPropertyTypes": true,
|
|
11
|
+
"noImplicitOverride": true,
|
|
12
|
+
"noFallthroughCasesInSwitch": true,
|
|
13
|
+
|
|
14
|
+
"verbatimModuleSyntax": true,
|
|
15
|
+
"resolveJsonModule": true,
|
|
16
|
+
"forceConsistentCasingInFileNames": true,
|
|
17
|
+
"skipLibCheck": true
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/tsconfig",
|
|
3
|
+
"display": "@integr8-media/config library (Node or universal)",
|
|
4
|
+
"extends": "./base.json",
|
|
5
|
+
"compilerOptions": {
|
|
6
|
+
"module": "NodeNext",
|
|
7
|
+
"moduleResolution": "NodeNext",
|
|
8
|
+
"declaration": true,
|
|
9
|
+
"isolatedDeclarations": true,
|
|
10
|
+
"declarationMap": false
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/tsconfig",
|
|
3
|
+
"display": "@integr8-media/config Node application",
|
|
4
|
+
"extends": "./base.json",
|
|
5
|
+
"compilerOptions": {
|
|
6
|
+
"module": "NodeNext",
|
|
7
|
+
"moduleResolution": "NodeNext",
|
|
8
|
+
"types": ["node"],
|
|
9
|
+
"declaration": false
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/tsconfig",
|
|
3
|
+
"display": "@integr8-media/config React application",
|
|
4
|
+
"extends": "./base.json",
|
|
5
|
+
"compilerOptions": {
|
|
6
|
+
"lib": ["ES2024", "DOM", "DOM.Iterable"],
|
|
7
|
+
"module": "ESNext",
|
|
8
|
+
"moduleResolution": "Bundler",
|
|
9
|
+
"jsx": "react-jsx",
|
|
10
|
+
"noEmit": true
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/tsconfig",
|
|
3
|
+
"display": "@integr8-media/config React library",
|
|
4
|
+
"extends": "./base.json",
|
|
5
|
+
"compilerOptions": {
|
|
6
|
+
"lib": ["ES2024", "DOM", "DOM.Iterable"],
|
|
7
|
+
"module": "ESNext",
|
|
8
|
+
"moduleResolution": "Bundler",
|
|
9
|
+
"jsx": "react-jsx",
|
|
10
|
+
"declaration": true,
|
|
11
|
+
"isolatedDeclarations": true,
|
|
12
|
+
"declarationMap": false
|
|
13
|
+
}
|
|
14
|
+
}
|