@prb/devkit 1.0.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/.prettierrc.js +13 -0
- package/.prettierrc.json +13 -0
- package/LICENSE.md +16 -0
- package/README.md +157 -0
- package/biome/base.jsonc +123 -0
- package/biome/ui.jsonc +80 -0
- package/just/_vercel_helpers.py +85 -0
- package/just/base.just +146 -0
- package/just/csv.just +126 -0
- package/just/npm.just +54 -0
- package/just/settings.just +6 -0
- package/just/utils.just +15 -0
- package/just/vercel.just +86 -0
- package/package.json +64 -0
- package/tsconfig/base.json +18 -0
- package/tsconfig/build.json +16 -0
- package/tsconfig/next.json +18 -0
- package/vitest/base.js +32 -0
package/.prettierrc.js
ADDED
package/.prettierrc.json
ADDED
package/LICENSE.md
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Paul Razvan Berg
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
|
|
6
|
+
documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
|
|
7
|
+
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
|
|
8
|
+
persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
|
|
11
|
+
Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
|
14
|
+
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
15
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
16
|
+
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# 🛠️ Devkit
|
|
2
|
+
|
|
3
|
+
[](https://opensource.org/licenses/MIT)
|
|
4
|
+
[](https://www.npmjs.com/package/@prb/devkit)
|
|
5
|
+
|
|
6
|
+
Personal configuration files and reusable scripts. Designed to be extended and customized as needed.
|
|
7
|
+
|
|
8
|
+
## 📦 Installation
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install @prb/devkit
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
Or with other package managers:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pnpm add @prb/devkit
|
|
18
|
+
bun add @prb/devkit
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## 🚀 Usage
|
|
22
|
+
|
|
23
|
+
### Biome
|
|
24
|
+
|
|
25
|
+
Extend the base Biome configuration in your `biome.jsonc`:
|
|
26
|
+
|
|
27
|
+
```jsonc
|
|
28
|
+
{
|
|
29
|
+
"$schema": "https://biomejs.dev/schemas/latest/schema.json",
|
|
30
|
+
"extends": ["@prb/devkit/biome"],
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
For UI projects, use the UI variant:
|
|
35
|
+
|
|
36
|
+
```jsonc
|
|
37
|
+
{
|
|
38
|
+
"extends": ["@prb/devkit/biome/ui"],
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Prettier
|
|
43
|
+
|
|
44
|
+
Reference the Prettier config in your `package.json`:
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"prettier": "@prb/devkit/prettier"
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### TypeScript
|
|
53
|
+
|
|
54
|
+
Extend TSConfig presets in your `tsconfig.json`:
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"extends": "@prb/devkit/tsconfig/base"
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Available presets:
|
|
63
|
+
|
|
64
|
+
- `@prb/devkit/tsconfig/base` — Base TypeScript configuration
|
|
65
|
+
- `@prb/devkit/tsconfig/build` — Build-optimized configuration
|
|
66
|
+
- `@prb/devkit/tsconfig/next` — Next.js configuration
|
|
67
|
+
|
|
68
|
+
### Vitest
|
|
69
|
+
|
|
70
|
+
Use the devkit vitest config factory in your `vitest.config.ts`:
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
import { defineDevkitConfig } from "@prb/devkit/vitest";
|
|
74
|
+
|
|
75
|
+
export default defineDevkitConfig({
|
|
76
|
+
environment: "jsdom", // or "node" (default), "happy-dom"
|
|
77
|
+
setupFiles: ["./tests/setup.ts"],
|
|
78
|
+
coverage: true,
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
The config provides CI-aware defaults:
|
|
83
|
+
|
|
84
|
+
- `globals: true`
|
|
85
|
+
- `retry: 2` in CI, `0` locally
|
|
86
|
+
- `testTimeout: 30s` in CI, `10s` locally
|
|
87
|
+
- `reporters: ["basic"]` in CI, `["verbose"]` locally
|
|
88
|
+
|
|
89
|
+
For merging with existing Vite configs:
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
import { defineDevkitConfig, mergeConfig } from "@prb/devkit/vitest";
|
|
93
|
+
import { defineConfig } from "vitest/config";
|
|
94
|
+
|
|
95
|
+
export default mergeConfig(
|
|
96
|
+
defineDevkitConfig({ environment: "jsdom" }),
|
|
97
|
+
defineConfig({
|
|
98
|
+
test: {
|
|
99
|
+
alias: { "@": "./src" },
|
|
100
|
+
},
|
|
101
|
+
}),
|
|
102
|
+
);
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Just
|
|
106
|
+
|
|
107
|
+
Import Just recipes in your `justfile`:
|
|
108
|
+
|
|
109
|
+
```just
|
|
110
|
+
import "@prb/devkit/just/base.just"
|
|
111
|
+
import "@prb/devkit/just/npm.just"
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Available modules:
|
|
115
|
+
|
|
116
|
+
| Module | Description |
|
|
117
|
+
| --------------- | ------------------------------- |
|
|
118
|
+
| `base.just` | Common development recipes |
|
|
119
|
+
| `csv.just` | CSV/TSV validation with qsv |
|
|
120
|
+
| `npm.just` | NPM package management |
|
|
121
|
+
| `settings.just` | Just settings and configuration |
|
|
122
|
+
| `vercel.just` | Vercel build and deploy |
|
|
123
|
+
|
|
124
|
+
## ⚙️ Available Configs
|
|
125
|
+
|
|
126
|
+
| Tool | Config File/Directory |
|
|
127
|
+
| ----------- | ---------------------------------------- |
|
|
128
|
+
| 🔍 Biome | [`biome/`](./biome/) |
|
|
129
|
+
| 🛠 Just | [`just/`](./just/) |
|
|
130
|
+
| ✨ Prettier | [`.prettierrc.json`](./.prettierrc.json) |
|
|
131
|
+
| 📦 TSConfig | [`tsconfig/`](./tsconfig/) |
|
|
132
|
+
| 🧪 Vitest | [`vitest/`](./vitest/) |
|
|
133
|
+
| 💻 VSCode | [`vscode/`](./vscode/) |
|
|
134
|
+
|
|
135
|
+
## 🐈⬛ GitHub Actions
|
|
136
|
+
|
|
137
|
+
Reusable composite actions for GitHub CI workflows.
|
|
138
|
+
|
|
139
|
+
| Action | Description |
|
|
140
|
+
| --------------------------------------------- | ------------------------------------------ |
|
|
141
|
+
| [`actions/setup`](./actions/setup/) | Install dependencies (Node.js, Just, etc.) |
|
|
142
|
+
| [`actions/node-cache`](./actions/node-cache/) | Cache Node.js dependencies |
|
|
143
|
+
|
|
144
|
+
```yaml
|
|
145
|
+
- uses: PaulRBerg/devkit/actions/setup@v1
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Dependency caching stores only the package-manager cache, not `node_modules`. Set `save-cache: true` in at most one
|
|
149
|
+
parallel job to publish a refreshed cache.
|
|
150
|
+
|
|
151
|
+
## 🤝 Contributing
|
|
152
|
+
|
|
153
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
154
|
+
|
|
155
|
+
## 📄 License
|
|
156
|
+
|
|
157
|
+
This project is licensed under MIT — see the [LICENSE](LICENSE.md) file for details.
|
package/biome/base.jsonc
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
// Devkit configuration for Biome v2: https://next.biomejs.dev
|
|
2
|
+
//
|
|
3
|
+
// This is a shared base configuration meant to be extended in consuming projects.
|
|
4
|
+
//
|
|
5
|
+
// IMPORTANT: Consumers must add their own `files.includes` patterns to specify
|
|
6
|
+
// which files to lint and format. For example:
|
|
7
|
+
//
|
|
8
|
+
// {
|
|
9
|
+
// "extends": ["@prb/devkit/biome/base"],
|
|
10
|
+
// "files": {
|
|
11
|
+
// "includes": ["**/*.{css,js,jsx,json,ts,tsx}", "!node_modules"]
|
|
12
|
+
// }
|
|
13
|
+
// }
|
|
14
|
+
//
|
|
15
|
+
// You may also add override blocks with custom `includes` patterns for
|
|
16
|
+
// granular rule customization.
|
|
17
|
+
{
|
|
18
|
+
"$schema": "https://biomejs.dev/schemas/latest/schema.json",
|
|
19
|
+
"assist": {
|
|
20
|
+
"enabled": true,
|
|
21
|
+
"actions": {
|
|
22
|
+
"source": {
|
|
23
|
+
"organizeImports": "on",
|
|
24
|
+
"useSortedKeys": {
|
|
25
|
+
"level": "on",
|
|
26
|
+
"options": {
|
|
27
|
+
"groupByNesting": true
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"files": {
|
|
34
|
+
"maxSize": 5242880 // 5MB
|
|
35
|
+
},
|
|
36
|
+
"formatter": {
|
|
37
|
+
"enabled": true,
|
|
38
|
+
"formatWithErrors": true,
|
|
39
|
+
"indentStyle": "space",
|
|
40
|
+
"lineWidth": 100
|
|
41
|
+
},
|
|
42
|
+
// Needed to override ultracite
|
|
43
|
+
"json": {
|
|
44
|
+
"formatter": {
|
|
45
|
+
"lineWidth": 100
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"linter": {
|
|
49
|
+
"enabled": true,
|
|
50
|
+
"rules": {
|
|
51
|
+
"preset": "recommended",
|
|
52
|
+
"complexity": {
|
|
53
|
+
"noImplicitCoercions": "error", // forbid `!!foo`, allow `Boolean(foo)`
|
|
54
|
+
"noVoid": "off", // void is useful in some cases e.g. `useEffect` callbacks
|
|
55
|
+
"useSimplifiedLogicExpression": "off", // the rule is cool but it gets agents stuck
|
|
56
|
+
"noUselessUndefined": "off" // we want explicit undefined return values
|
|
57
|
+
},
|
|
58
|
+
"correctness": {
|
|
59
|
+
"noUnusedImports": "off", // allow unused imports during development
|
|
60
|
+
"noUnusedVariables": "error"
|
|
61
|
+
},
|
|
62
|
+
"nursery": {
|
|
63
|
+
"noFloatingPromises": "error" // floating promises can lead to bugs
|
|
64
|
+
},
|
|
65
|
+
"performance": {
|
|
66
|
+
"noBarrelFile": "off", // barrel exports lead to cleaner imports
|
|
67
|
+
"noDelete": "off", // disabled due to https://github.com/biomejs/biome/issues/4093
|
|
68
|
+
"noNamespaceImport": "off" // namespaces are cool
|
|
69
|
+
},
|
|
70
|
+
"style": {
|
|
71
|
+
"noEnum": "off", // enums are useful in some cases e.g. UI states
|
|
72
|
+
"noNamespace": "off", // namespaces are cool
|
|
73
|
+
"useBlockStatements": "off", // turned off because it gets AIs stuck
|
|
74
|
+
"useDefaultSwitchClause": "off", // already handled by TypeScript
|
|
75
|
+
"useTemplate": "off", // allow string concatenation using `+`
|
|
76
|
+
"useConsistentTypeDefinitions": {
|
|
77
|
+
"level": "warn",
|
|
78
|
+
"options": {
|
|
79
|
+
"style": "type"
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
"useFilenamingConvention": {
|
|
83
|
+
"level": "error",
|
|
84
|
+
"options": {
|
|
85
|
+
"filenameCases": ["kebab-case", "camelCase", "PascalCase", "export"]
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
// disabled due to https://github.com/biomejs/biome/issues/8450
|
|
89
|
+
"useImportType": {
|
|
90
|
+
"level": "warn",
|
|
91
|
+
"options": {
|
|
92
|
+
"style": "separatedType"
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
"overrides": [
|
|
99
|
+
{
|
|
100
|
+
// Disabling sorted keys because ordering is often important in JSON files
|
|
101
|
+
"includes": ["**/*.{json,json5,jsonc}"],
|
|
102
|
+
"assist": {
|
|
103
|
+
"actions": {
|
|
104
|
+
"source": {
|
|
105
|
+
"useSortedKeys": "off"
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
// Needed to override ultracite
|
|
110
|
+
"json": {
|
|
111
|
+
"parser": {
|
|
112
|
+
"allowComments": true,
|
|
113
|
+
"allowTrailingCommas": true
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
],
|
|
118
|
+
"vcs": {
|
|
119
|
+
"clientKind": "git",
|
|
120
|
+
"enabled": true,
|
|
121
|
+
"useIgnoreFile": true
|
|
122
|
+
}
|
|
123
|
+
}
|
package/biome/ui.jsonc
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// Devkit UI configuration for Biome v2
|
|
2
|
+
//
|
|
3
|
+
// Extends base configuration with UI-specific rules for accessibility,
|
|
4
|
+
// Tailwind CSS class sorting, and CSS modules support.
|
|
5
|
+
//
|
|
6
|
+
// IMPORTANT: Consumers must extend BOTH configs and add their own
|
|
7
|
+
// `files.includes` patterns. For example:
|
|
8
|
+
//
|
|
9
|
+
// {
|
|
10
|
+
// "extends": ["@prb/devkit/biome/base", "@prb/devkit/biome/ui"],
|
|
11
|
+
// "files": {
|
|
12
|
+
// "includes": ["**/*.{css,js,jsx,json,ts,tsx}", "!node_modules"]
|
|
13
|
+
// }
|
|
14
|
+
// }
|
|
15
|
+
{
|
|
16
|
+
"$schema": "https://biomejs.dev/schemas/latest/schema.json",
|
|
17
|
+
"assist": {
|
|
18
|
+
"enabled": true,
|
|
19
|
+
"actions": {
|
|
20
|
+
"source": {
|
|
21
|
+
"noDuplicateClasses": "on",
|
|
22
|
+
"useSortedAttributes": "on"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"css": {
|
|
27
|
+
"formatter": {
|
|
28
|
+
"lineWidth": 100
|
|
29
|
+
},
|
|
30
|
+
"parser": {
|
|
31
|
+
"cssModules": true,
|
|
32
|
+
"tailwindDirectives": true
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"linter": {
|
|
36
|
+
"rules": {
|
|
37
|
+
"a11y": {
|
|
38
|
+
"noSvgWithoutTitle": "off",
|
|
39
|
+
"useKeyWithClickEvents": "off"
|
|
40
|
+
},
|
|
41
|
+
"correctness": {
|
|
42
|
+
"useExhaustiveDependencies": "off" // doesn't work with React Compiler: https://github.com/biomejs/biome/issues/5293
|
|
43
|
+
},
|
|
44
|
+
"nursery": {
|
|
45
|
+
// Tailwind class ordering is handled by ESLint (better-tailwindcss/enforce-consistent-class-order).
|
|
46
|
+
// Biome's useSortedClasses lacks a Tailwind v4 sort preset and sorts text-* utilities alphabetically,
|
|
47
|
+
// interleaving font-size (text-sm) with color (text-red-500) utilities.
|
|
48
|
+
// Tracked upstream: https://github.com/biomejs/biome/issues/1274
|
|
49
|
+
"useSortedClasses": "off"
|
|
50
|
+
},
|
|
51
|
+
"style": {
|
|
52
|
+
// Promoted out of nursery in Biome 2.5.0.
|
|
53
|
+
"useErrorCause": "error"
|
|
54
|
+
},
|
|
55
|
+
"suspicious": {
|
|
56
|
+
"noConsole": {
|
|
57
|
+
"level": "warn",
|
|
58
|
+
"options": {
|
|
59
|
+
// console.error allowed for error boundaries and critical failures
|
|
60
|
+
"allow": ["error"]
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"overrides": [
|
|
67
|
+
// ── Styles files ─────────────────────────────────────────────────────────
|
|
68
|
+
// Disable alphabetical sorting in styles files, where order is important for CSS
|
|
69
|
+
{
|
|
70
|
+
"includes": ["**/*.styles.ts"],
|
|
71
|
+
"assist": {
|
|
72
|
+
"actions": {
|
|
73
|
+
"source": {
|
|
74
|
+
"useSortedKeys": "off"
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
]
|
|
80
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""Shared helpers for Vercel Just recipes."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
import subprocess
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def run(*cmd):
|
|
10
|
+
"""Run a command, exiting on failure."""
|
|
11
|
+
result = subprocess.run(cmd)
|
|
12
|
+
if result.returncode != 0:
|
|
13
|
+
sys.exit(result.returncode)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def run_capture_url(*cmd) -> str:
|
|
17
|
+
"""Run a command, tee its stdout to the user, and return the last non-empty stdout line.
|
|
18
|
+
|
|
19
|
+
Used to capture the deployment URL printed by `vercel deploy` while still streaming
|
|
20
|
+
its output to the caller. Exits on non-zero return codes, mirroring `run`.
|
|
21
|
+
"""
|
|
22
|
+
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, text=True, bufsize=1)
|
|
23
|
+
assert proc.stdout is not None
|
|
24
|
+
last = ""
|
|
25
|
+
for line in proc.stdout:
|
|
26
|
+
sys.stdout.write(line)
|
|
27
|
+
sys.stdout.flush()
|
|
28
|
+
stripped = line.strip()
|
|
29
|
+
if stripped:
|
|
30
|
+
last = stripped
|
|
31
|
+
proc.wait()
|
|
32
|
+
if proc.returncode != 0:
|
|
33
|
+
sys.exit(proc.returncode)
|
|
34
|
+
return last
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def emit_deployment_url(url: str) -> None:
|
|
38
|
+
"""Publish the deployment URL to GitHub Actions and/or a caller-specified file.
|
|
39
|
+
|
|
40
|
+
- If `GITHUB_OUTPUT` is set, appends `deployment_url=<url>` so workflow steps can read it
|
|
41
|
+
via `steps.<id>.outputs.deployment_url`.
|
|
42
|
+
- If `VERCEL_DEPLOYMENT_URL_FILE` is set, writes the URL to that path (useful for local
|
|
43
|
+
scripts and non-GitHub CI systems).
|
|
44
|
+
- Always prints a human-readable `Deployment URL: <url>` line to stdout.
|
|
45
|
+
"""
|
|
46
|
+
github_output = os.environ.get("GITHUB_OUTPUT")
|
|
47
|
+
if github_output:
|
|
48
|
+
with open(github_output, "a", encoding="utf-8") as fh:
|
|
49
|
+
fh.write(f"deployment_url={url}\n")
|
|
50
|
+
|
|
51
|
+
url_file = os.environ.get("VERCEL_DEPLOYMENT_URL_FILE")
|
|
52
|
+
if url_file:
|
|
53
|
+
with open(url_file, "w", encoding="utf-8") as fh:
|
|
54
|
+
fh.write(url)
|
|
55
|
+
|
|
56
|
+
print(f"\nDeployment URL: {url}")
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def resolve_vercel_env(app, project_ids_json):
|
|
60
|
+
"""Resolve VERCEL_PROJECT_ID and VERCEL_TOKEN, setting them in os.environ.
|
|
61
|
+
|
|
62
|
+
Precedence for project ID:
|
|
63
|
+
1. VERCEL_PROJECT_ID already set in the environment
|
|
64
|
+
2. Lookup `app` in the VERCEL_PROJECT_IDS JSON map
|
|
65
|
+
|
|
66
|
+
Returns (project_id, token).
|
|
67
|
+
"""
|
|
68
|
+
project_id = os.environ.get("VERCEL_PROJECT_ID", "")
|
|
69
|
+
if not project_id:
|
|
70
|
+
project_ids = json.loads(project_ids_json)
|
|
71
|
+
if app not in project_ids:
|
|
72
|
+
print(
|
|
73
|
+
f"Error: unknown app '{app}'. Known apps: {', '.join(project_ids) or '(none)'}",
|
|
74
|
+
file=sys.stderr,
|
|
75
|
+
)
|
|
76
|
+
sys.exit(1)
|
|
77
|
+
project_id = project_ids[app]
|
|
78
|
+
os.environ["VERCEL_PROJECT_ID"] = project_id
|
|
79
|
+
|
|
80
|
+
token = os.environ.get("VERCEL_TOKEN", "")
|
|
81
|
+
if not token:
|
|
82
|
+
print("Error: VERCEL_TOKEN is not set.", file=sys.stderr)
|
|
83
|
+
sys.exit(1)
|
|
84
|
+
|
|
85
|
+
return project_id, token
|
package/just/base.just
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import "./settings.just"
|
|
2
|
+
import "./utils.just"
|
|
3
|
+
|
|
4
|
+
# ---------------------------------------------------------------------------- #
|
|
5
|
+
# DEPENDENCIES #
|
|
6
|
+
# ---------------------------------------------------------------------------- #
|
|
7
|
+
|
|
8
|
+
# Ni: https://github.com/antfu-collective/ni
|
|
9
|
+
na := require("na")
|
|
10
|
+
ni := require("ni")
|
|
11
|
+
nlx := require("nlx")
|
|
12
|
+
|
|
13
|
+
# ---------------------------------------------------------------------------- #
|
|
14
|
+
# CONSTANTS #
|
|
15
|
+
# ---------------------------------------------------------------------------- #
|
|
16
|
+
|
|
17
|
+
GLOBS_PRETTIER := "\"**/*.{md,mdx,yaml,yml}\""
|
|
18
|
+
|
|
19
|
+
# ---------------------------------------------------------------------------- #
|
|
20
|
+
# RECIPES #
|
|
21
|
+
# ---------------------------------------------------------------------------- #
|
|
22
|
+
|
|
23
|
+
# Clean files
|
|
24
|
+
[no-cd]
|
|
25
|
+
clean:
|
|
26
|
+
just _clean ".DS_Store"
|
|
27
|
+
|
|
28
|
+
[no-cd]
|
|
29
|
+
_clean +globs:
|
|
30
|
+
nlx del-cli "{{ globs }}"
|
|
31
|
+
|
|
32
|
+
# Clear node_modules recursively
|
|
33
|
+
[confirm("Are you sure you want to delete all node_modules, including in subdirectories? [y/N]"), no-cd]
|
|
34
|
+
@clean-modules +globs="**/node_modules":
|
|
35
|
+
echo "🧹 Deleting node_modules recursively..."
|
|
36
|
+
nlx del-cli --verbose {{ globs }}
|
|
37
|
+
|
|
38
|
+
# Install the Node.js dependencies; run with --frozen to install the dependencies with the frozen lockfile
|
|
39
|
+
[no-cd]
|
|
40
|
+
@install *args:
|
|
41
|
+
ni {{ args }}
|
|
42
|
+
|
|
43
|
+
# Build with TypeScript
|
|
44
|
+
[no-cd]
|
|
45
|
+
[arg("project", long, short="p", help="Path to tsconfig.json")]
|
|
46
|
+
@tsc-build project="tsconfig.json":
|
|
47
|
+
na tsc -p {{ project }}
|
|
48
|
+
alias tb := tsc-build
|
|
49
|
+
|
|
50
|
+
# ---------------------------------------------------------------------------- #
|
|
51
|
+
# CHECKS #
|
|
52
|
+
# ---------------------------------------------------------------------------- #
|
|
53
|
+
|
|
54
|
+
# Check code with Biome - runs both the checker and the linter
|
|
55
|
+
[group("checks"), no-cd]
|
|
56
|
+
@biome-check +globs=".":
|
|
57
|
+
na biome check {{ globs }}
|
|
58
|
+
alias bc := biome-check
|
|
59
|
+
|
|
60
|
+
# Lint code with Biome
|
|
61
|
+
[group("checks"), no-cd]
|
|
62
|
+
@biome-lint +globs=".":
|
|
63
|
+
na biome lint {{ globs }}
|
|
64
|
+
alias bl := biome-lint
|
|
65
|
+
|
|
66
|
+
# Fix code with Biome
|
|
67
|
+
# The `noUnusedImports` rule is disabled by default to allow unused imports during development
|
|
68
|
+
[group("checks"), no-cd]
|
|
69
|
+
@biome-write +globs=".":
|
|
70
|
+
na biome check --write {{ globs }}
|
|
71
|
+
na biome lint --unsafe --write --only correctness/noUnusedImports {{ globs }}
|
|
72
|
+
alias bw := biome-write
|
|
73
|
+
|
|
74
|
+
# Run all code checks
|
|
75
|
+
[group("checks"), no-cd]
|
|
76
|
+
@_full-check:
|
|
77
|
+
just _run-with-status biome-check
|
|
78
|
+
just _run-with-status prettier-check
|
|
79
|
+
just _run-with-status type-check
|
|
80
|
+
|
|
81
|
+
# Run all code checks
|
|
82
|
+
[group("checks"), no-cd]
|
|
83
|
+
@full-check: _full-check
|
|
84
|
+
echo ""
|
|
85
|
+
echo '{{ GREEN }}All code checks passed!{{ NORMAL }}'
|
|
86
|
+
alias fc := full-check
|
|
87
|
+
|
|
88
|
+
# Run all code fixes
|
|
89
|
+
[group("checks"), no-cd]
|
|
90
|
+
@_full-write:
|
|
91
|
+
just _run-with-status biome-write
|
|
92
|
+
just _run-with-status prettier-write
|
|
93
|
+
|
|
94
|
+
# Run all code fixes
|
|
95
|
+
[group("checks"), no-cd]
|
|
96
|
+
@full-write: _full-write
|
|
97
|
+
echo ""
|
|
98
|
+
echo '{{ GREEN }}All code fixes applied!{{ NORMAL }}'
|
|
99
|
+
alias fw := full-write
|
|
100
|
+
|
|
101
|
+
# Run knip checks
|
|
102
|
+
[group("checks"), no-cd]
|
|
103
|
+
@knip-check:
|
|
104
|
+
na knip
|
|
105
|
+
alias kc := knip-check
|
|
106
|
+
|
|
107
|
+
# Run knip fix
|
|
108
|
+
[group("checks"), no-cd]
|
|
109
|
+
@knip-write:
|
|
110
|
+
na knip --fix
|
|
111
|
+
alias kw := knip-write
|
|
112
|
+
|
|
113
|
+
# Check Prettier formatting
|
|
114
|
+
[group("checks"), no-cd]
|
|
115
|
+
@prettier-check +globs=GLOBS_PRETTIER:
|
|
116
|
+
na prettier \
|
|
117
|
+
--check \
|
|
118
|
+
--cache \
|
|
119
|
+
--log-level warn \
|
|
120
|
+
--no-error-on-unmatched-pattern \
|
|
121
|
+
{{ globs }}
|
|
122
|
+
alias pc := prettier-check
|
|
123
|
+
|
|
124
|
+
# Format using Prettier
|
|
125
|
+
[group("checks"), no-cd]
|
|
126
|
+
@prettier-write +globs=GLOBS_PRETTIER:
|
|
127
|
+
na prettier \
|
|
128
|
+
--write \
|
|
129
|
+
--cache \
|
|
130
|
+
--log-level warn \
|
|
131
|
+
--no-error-on-unmatched-pattern \
|
|
132
|
+
{{ globs }}
|
|
133
|
+
alias pw := prettier-write
|
|
134
|
+
|
|
135
|
+
# Type check with TypeScript (tsgo default, falls back to tsc if unavailable)
|
|
136
|
+
[group("checks"), no-cd, script("bash")]
|
|
137
|
+
[arg("compiler", long, short="c", help="TypeScript compiler (tsgo or tsc)")]
|
|
138
|
+
[arg("project", long, short="p", help="Path to tsconfig.json")]
|
|
139
|
+
type-check compiler="tsgo" project="tsconfig.json":
|
|
140
|
+
cmd="{{ compiler }}"
|
|
141
|
+
if [[ "$cmd" == "tsgo" ]] && [[ ! -x "node_modules/.bin/tsgo" ]]; then
|
|
142
|
+
cmd="tsc"
|
|
143
|
+
fi
|
|
144
|
+
na "$cmd" --noEmit --project {{ project }}
|
|
145
|
+
alias tc := type-check
|
|
146
|
+
alias tsc-check := type-check
|
package/just/csv.just
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import "./settings.just"
|
|
2
|
+
|
|
3
|
+
# ---------------------------------------------------------------------------- #
|
|
4
|
+
# SCRIPTS #
|
|
5
|
+
# ---------------------------------------------------------------------------- #
|
|
6
|
+
|
|
7
|
+
# Check CSV/TSV files using qsv: https://github.com/dathere/qsv
|
|
8
|
+
[group("checks"), script("python3")]
|
|
9
|
+
[arg("glob", long, short="g", help="Glob pattern for CSV/TSV files")]
|
|
10
|
+
[arg("schema", long, short="s", help="JSON schema file for validation")]
|
|
11
|
+
[arg("ignore", long, short="x", help="Space-separated ignore patterns")]
|
|
12
|
+
_csv-check glob="data/*/*.{csv,tsv}" schema="" ignore="*.invalid *.valid *validation-errors.*":
|
|
13
|
+
import fnmatch
|
|
14
|
+
import glob as globmod
|
|
15
|
+
import re
|
|
16
|
+
import subprocess
|
|
17
|
+
import sys
|
|
18
|
+
|
|
19
|
+
def expand_braces(pattern):
|
|
20
|
+
"""Expand brace patterns like {a,b} into multiple patterns."""
|
|
21
|
+
match = re.search(r'\{([^}]+)\}', pattern)
|
|
22
|
+
if not match:
|
|
23
|
+
return [pattern]
|
|
24
|
+
prefix, suffix = pattern[:match.start()], pattern[match.end():]
|
|
25
|
+
return [p for opt in match.group(1).split(',') for p in expand_braces(prefix + opt + suffix)]
|
|
26
|
+
|
|
27
|
+
# Check qsv is available
|
|
28
|
+
if subprocess.run(["which", "qsv"], capture_output=True).returncode != 0:
|
|
29
|
+
print("✗ qsv CLI not found")
|
|
30
|
+
print("Install it: https://github.com/dathere/qsv")
|
|
31
|
+
sys.exit(1)
|
|
32
|
+
|
|
33
|
+
ignore_patterns = "{{ ignore }}".split() if "{{ ignore }}" else []
|
|
34
|
+
schema = "{{ schema }}" or None
|
|
35
|
+
globs = "{{ glob }}"
|
|
36
|
+
|
|
37
|
+
# Infer extension label from glob pattern
|
|
38
|
+
ext_match = re.search(r'\.(\w+|\{[^}]+\})$', globs)
|
|
39
|
+
if ext_match:
|
|
40
|
+
ext = ext_match.group(1)
|
|
41
|
+
if ext.startswith('{') and ext.endswith('}'):
|
|
42
|
+
# Handle brace expansion like {csv,tsv}
|
|
43
|
+
ext_label = '.' + '/'.join(f'.{e}' for e in ext[1:-1].split(','))[1:]
|
|
44
|
+
else:
|
|
45
|
+
ext_label = f'.{ext}'
|
|
46
|
+
else:
|
|
47
|
+
ext_label = 'CSV/TSV'
|
|
48
|
+
|
|
49
|
+
print(f"Validating {ext_label} files...")
|
|
50
|
+
files = [f for pattern in expand_braces(globs) for f in globmod.glob(pattern, recursive=True)]
|
|
51
|
+
|
|
52
|
+
# Filter ignored files
|
|
53
|
+
files = [f for f in files if not any(fnmatch.fnmatch(f, p) for p in ignore_patterns)]
|
|
54
|
+
|
|
55
|
+
if not files:
|
|
56
|
+
print(f"ℹ️ No {ext_label} files found to validate")
|
|
57
|
+
sys.exit(0)
|
|
58
|
+
|
|
59
|
+
for file in files:
|
|
60
|
+
cmd = ["qsv", "validate", file]
|
|
61
|
+
if schema:
|
|
62
|
+
cmd.append(schema)
|
|
63
|
+
result = subprocess.run(cmd, capture_output=True)
|
|
64
|
+
if result.returncode != 0:
|
|
65
|
+
print(f"❌ Validation failed for: {file}")
|
|
66
|
+
subprocess.run(["just", "_csv-show-errors", file])
|
|
67
|
+
sys.exit(1)
|
|
68
|
+
|
|
69
|
+
print(f"✅ All {ext_label} files are valid")
|
|
70
|
+
|
|
71
|
+
# Show validation errors for a CSV/TSV file
|
|
72
|
+
[group("checks"), script("python3")]
|
|
73
|
+
_csv-show-errors file:
|
|
74
|
+
import os
|
|
75
|
+
import subprocess
|
|
76
|
+
import sys
|
|
77
|
+
|
|
78
|
+
file = "{{ file }}"
|
|
79
|
+
# qsv always produces .validation-errors.tsv regardless of input format
|
|
80
|
+
error_file = f"{file}.validation-errors.tsv"
|
|
81
|
+
|
|
82
|
+
if not os.path.exists(error_file):
|
|
83
|
+
print(f"Error file not found: {error_file}")
|
|
84
|
+
sys.exit(0)
|
|
85
|
+
|
|
86
|
+
# Count errors (qsv auto-detects delimiter based on file extension)
|
|
87
|
+
result = subprocess.run(["qsv", "count", error_file], capture_output=True, text=True)
|
|
88
|
+
try:
|
|
89
|
+
total = int(result.stdout.strip()) if result.returncode == 0 and result.stdout.strip() else 0
|
|
90
|
+
except ValueError:
|
|
91
|
+
total = 0
|
|
92
|
+
|
|
93
|
+
print()
|
|
94
|
+
if total > 0:
|
|
95
|
+
print(f"First 20 validation errors ({total} total):")
|
|
96
|
+
else:
|
|
97
|
+
print("Validation errors:")
|
|
98
|
+
print()
|
|
99
|
+
|
|
100
|
+
# Try qsv table for nice formatting, fall back to reading file directly
|
|
101
|
+
slice_result = subprocess.run(
|
|
102
|
+
["qsv", "slice", "--start", "0", "--len", "21", error_file],
|
|
103
|
+
capture_output=True, text=True
|
|
104
|
+
)
|
|
105
|
+
if slice_result.returncode == 0:
|
|
106
|
+
table_result = subprocess.run(
|
|
107
|
+
["qsv", "table"],
|
|
108
|
+
input=slice_result.stdout,
|
|
109
|
+
capture_output=True, text=True
|
|
110
|
+
)
|
|
111
|
+
if table_result.returncode == 0:
|
|
112
|
+
print(table_result.stdout)
|
|
113
|
+
else:
|
|
114
|
+
print(slice_result.stdout)
|
|
115
|
+
else:
|
|
116
|
+
with open(error_file) as f:
|
|
117
|
+
for i, line in enumerate(f):
|
|
118
|
+
if i >= 21:
|
|
119
|
+
break
|
|
120
|
+
print(line, end="")
|
|
121
|
+
|
|
122
|
+
if total > 20:
|
|
123
|
+
print()
|
|
124
|
+
print(f"... and {total - 20} more errors")
|
|
125
|
+
print()
|
|
126
|
+
print(f"Full details: {error_file}")
|
package/just/npm.just
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import "./settings.just"
|
|
2
|
+
|
|
3
|
+
# ---------------------------------------------------------------------------- #
|
|
4
|
+
# DEPENDENCIES #
|
|
5
|
+
# ---------------------------------------------------------------------------- #
|
|
6
|
+
|
|
7
|
+
# Jq
|
|
8
|
+
jq := require("jq")
|
|
9
|
+
|
|
10
|
+
# Npm
|
|
11
|
+
npm := require("npm")
|
|
12
|
+
|
|
13
|
+
# ---------------------------------------------------------------------------- #
|
|
14
|
+
# SCRIPTS #
|
|
15
|
+
# ---------------------------------------------------------------------------- #
|
|
16
|
+
|
|
17
|
+
# Publish the npm package, e.g. v1.0.0
|
|
18
|
+
[group("publish")]
|
|
19
|
+
[arg("tag", long, value="true")]
|
|
20
|
+
publish tag="false" *args:
|
|
21
|
+
npm publish {{ args }}
|
|
22
|
+
@{{ if tag == "true" { "just tag $(jq -r '.version' package.json)" } else { "true" } }}
|
|
23
|
+
|
|
24
|
+
# Publish using the `beta` tag, e.g. v1.0.0-beta.1
|
|
25
|
+
[group("publish")]
|
|
26
|
+
publish-beta *args:
|
|
27
|
+
@just check-beta-version
|
|
28
|
+
npm publish --tag beta {{ args }}
|
|
29
|
+
|
|
30
|
+
# Tag the new version
|
|
31
|
+
[group("publish")]
|
|
32
|
+
tag *version:
|
|
33
|
+
git tag -am "{{ version }}" {{ version }}
|
|
34
|
+
git push origin --tags
|
|
35
|
+
|
|
36
|
+
# ---------------------------------------------------------------------------- #
|
|
37
|
+
# PRIVATE HELPERS #
|
|
38
|
+
# ---------------------------------------------------------------------------- #
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# Check that package.json version includes -beta.x suffix
|
|
42
|
+
[private, script("bash")]
|
|
43
|
+
@check-beta-version:
|
|
44
|
+
# Extract version from package.json using jq for reliable JSON parsing
|
|
45
|
+
version=$(jq -r '.version' package.json)
|
|
46
|
+
|
|
47
|
+
# Check if version contains -beta suffix
|
|
48
|
+
if [[ "$version" =~ -beta\.[0-9]+$ ]]; then
|
|
49
|
+
echo "✓ Version $version includes beta suffix"
|
|
50
|
+
else
|
|
51
|
+
echo "✗ Error: Version $version does not include -beta.x suffix"
|
|
52
|
+
echo "Please update package.json version to include -beta.x (e.g., 1.0.0-beta.1)"
|
|
53
|
+
exit 1
|
|
54
|
+
fi
|
package/just/utils.just
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import "./settings.just"
|
|
2
|
+
|
|
3
|
+
# ---------------------------------------------------------------------------- #
|
|
4
|
+
# UTILITIES #
|
|
5
|
+
# ---------------------------------------------------------------------------- #
|
|
6
|
+
|
|
7
|
+
# Private recipe to run a check with formatted output
|
|
8
|
+
[no-cd]
|
|
9
|
+
@_run-with-status recipe *args:
|
|
10
|
+
echo ""
|
|
11
|
+
echo '{{ CYAN }}→ Running {{ recipe }}...{{ NORMAL }}'
|
|
12
|
+
just {{ recipe }} {{ args }}
|
|
13
|
+
echo '{{ GREEN }}✓ {{ recipe }} completed{{ NORMAL }}'
|
|
14
|
+
alias rws := _run-with-status
|
|
15
|
+
alias _rws := _run-with-status
|
package/just/vercel.just
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import "./settings.just"
|
|
2
|
+
|
|
3
|
+
# ---------------------------------------------------------------------------- #
|
|
4
|
+
# DEPENDENCIES #
|
|
5
|
+
# ---------------------------------------------------------------------------- #
|
|
6
|
+
|
|
7
|
+
# Ni: https://github.com/antfu-collective/ni
|
|
8
|
+
na := require("na")
|
|
9
|
+
|
|
10
|
+
# ---------------------------------------------------------------------------- #
|
|
11
|
+
# CONSTANTS #
|
|
12
|
+
# ---------------------------------------------------------------------------- #
|
|
13
|
+
|
|
14
|
+
# Consumer MUST override with a JSON map of app name → Vercel project ID.
|
|
15
|
+
# Example: '{"portal": "prj_xxx", "landing": "prj_yyy"}'
|
|
16
|
+
# Note: if VERCEL_PROJECT_ID is already set in the environment, this map is ignored.
|
|
17
|
+
VERCEL_PROJECT_IDS := '{}'
|
|
18
|
+
|
|
19
|
+
# ---------------------------------------------------------------------------- #
|
|
20
|
+
# RECIPES #
|
|
21
|
+
# ---------------------------------------------------------------------------- #
|
|
22
|
+
|
|
23
|
+
# Build website for Vercel deployment
|
|
24
|
+
[arg("app", long)]
|
|
25
|
+
[arg("env", long)]
|
|
26
|
+
[group("vercel")]
|
|
27
|
+
build app env="staging":
|
|
28
|
+
@just _vercel-build "{{ app }}" "{{ env }}"
|
|
29
|
+
alias vb := build
|
|
30
|
+
|
|
31
|
+
# Deploy website to Vercel. Trailing args are forwarded to `vercel deploy`.
|
|
32
|
+
[arg("app", long)]
|
|
33
|
+
[arg("env", long)]
|
|
34
|
+
[arg("skip_build", long="skip-build", value="true")]
|
|
35
|
+
[confirm("Are you sure you want to deploy? [y/N]")]
|
|
36
|
+
[group("vercel")]
|
|
37
|
+
deploy app env="staging" skip_build="false" *args="":
|
|
38
|
+
@just _vercel-deploy "{{ app }}" "{{ env }}" "{{ skip_build }}" {{ args }}
|
|
39
|
+
alias vd := deploy
|
|
40
|
+
|
|
41
|
+
# ---------------------------------------------------------------------------- #
|
|
42
|
+
# PRIVATE HELPERS #
|
|
43
|
+
# ---------------------------------------------------------------------------- #
|
|
44
|
+
|
|
45
|
+
# Core build: vercel pull + vercel build
|
|
46
|
+
[private, script("python3")]
|
|
47
|
+
_vercel-build app env:
|
|
48
|
+
import sys; sys.path.insert(0, "{{ source_directory() }}")
|
|
49
|
+
import os
|
|
50
|
+
from _vercel_helpers import resolve_vercel_env, run
|
|
51
|
+
|
|
52
|
+
# Workaround for https://github.com/vercel/vercel/issues/14666
|
|
53
|
+
os.environ["VERCEL_TARGET_ENV"] = "{{ env }}"
|
|
54
|
+
os.environ["NEXT_PUBLIC_VERCEL_TARGET_ENV"] = "{{ env }}"
|
|
55
|
+
|
|
56
|
+
project_id, token = resolve_vercel_env("{{ app }}", '{{ VERCEL_PROJECT_IDS }}')
|
|
57
|
+
|
|
58
|
+
# Pull the environment from the Vercel project
|
|
59
|
+
run("na", "vercel", "pull", "--environment={{ env }}", f"--token={token}", "--yes")
|
|
60
|
+
|
|
61
|
+
# Build the project
|
|
62
|
+
run("na", "vercel", "build", "--target={{ env }}", f"--token={token}")
|
|
63
|
+
|
|
64
|
+
# Core deploy: optionally build, then deploy prebuilt artifacts
|
|
65
|
+
[private, script("python3")]
|
|
66
|
+
_vercel-deploy app env skip_build="false" *args="":
|
|
67
|
+
import shlex, sys; sys.path.insert(0, "{{ source_directory() }}")
|
|
68
|
+
import os
|
|
69
|
+
from _vercel_helpers import emit_deployment_url, resolve_vercel_env, run, run_capture_url
|
|
70
|
+
|
|
71
|
+
# Build the project if not skipped (calls the PUBLIC recipe so consumer overrides apply)
|
|
72
|
+
if "{{ skip_build }}" != "true":
|
|
73
|
+
run("just", "build", "--app={{ app }}", "--env={{ env }}")
|
|
74
|
+
|
|
75
|
+
# Workaround for https://github.com/vercel/vercel/issues/14666
|
|
76
|
+
os.environ["VERCEL_TARGET_ENV"] = "{{ env }}"
|
|
77
|
+
os.environ["NEXT_PUBLIC_VERCEL_TARGET_ENV"] = "{{ env }}"
|
|
78
|
+
|
|
79
|
+
project_id, token = resolve_vercel_env("{{ app }}", '{{ VERCEL_PROJECT_IDS }}')
|
|
80
|
+
|
|
81
|
+
# Deploy the project to Vercel, capturing the deployment URL from stdout
|
|
82
|
+
extra = shlex.split("""{{ args }}""")
|
|
83
|
+
url = run_capture_url(
|
|
84
|
+
"na", "vercel", "deploy", "--prebuilt", "--target={{ env }}", f"--token={token}", *extra
|
|
85
|
+
)
|
|
86
|
+
emit_deployment_url(url)
|
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@prb/devkit",
|
|
3
|
+
"description": "Personal configuration files and reusable scripts",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"version": "1.0.0",
|
|
7
|
+
"author": {
|
|
8
|
+
"name": "Paul Razvan Berg",
|
|
9
|
+
"url": "https://github.com/PaulRBerg"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/PaulRBerg/devkit/issues"
|
|
13
|
+
},
|
|
14
|
+
"devDependencies": {
|
|
15
|
+
"@biomejs/biome": "^2.5.0",
|
|
16
|
+
"prettier": "^3.8.3",
|
|
17
|
+
"vitest": "^3.2.4"
|
|
18
|
+
},
|
|
19
|
+
"peerDependencies": {
|
|
20
|
+
"vitest": ">=2.0.0"
|
|
21
|
+
},
|
|
22
|
+
"peerDependenciesMeta": {
|
|
23
|
+
"vitest": {
|
|
24
|
+
"optional": true
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"exports": {
|
|
28
|
+
"./biome": "./biome/base.jsonc",
|
|
29
|
+
"./biome/base": "./biome/base.jsonc",
|
|
30
|
+
"./biome/ui": "./biome/ui.jsonc",
|
|
31
|
+
"./prettier": "./.prettierrc.js",
|
|
32
|
+
"./tsconfig/base": "./tsconfig/base.json",
|
|
33
|
+
"./tsconfig/build": "./tsconfig/build.json",
|
|
34
|
+
"./tsconfig/next": "./tsconfig/next.json",
|
|
35
|
+
"./vitest": "./vitest/base.js"
|
|
36
|
+
},
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=20"
|
|
39
|
+
},
|
|
40
|
+
"files": [
|
|
41
|
+
"biome/",
|
|
42
|
+
"just/",
|
|
43
|
+
"tsconfig/",
|
|
44
|
+
"vitest/",
|
|
45
|
+
".prettierrc.js",
|
|
46
|
+
".prettierrc.json"
|
|
47
|
+
],
|
|
48
|
+
"keywords": [
|
|
49
|
+
"biome",
|
|
50
|
+
"config",
|
|
51
|
+
"devkit",
|
|
52
|
+
"prettier",
|
|
53
|
+
"tsconfig",
|
|
54
|
+
"typescript",
|
|
55
|
+
"vitest"
|
|
56
|
+
],
|
|
57
|
+
"publishConfig": {
|
|
58
|
+
"access": "public"
|
|
59
|
+
},
|
|
60
|
+
"repository": {
|
|
61
|
+
"type": "git",
|
|
62
|
+
"url": "git+https://github.com/PaulRBerg/devkit.git"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/tsconfig",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"esModuleInterop": true,
|
|
5
|
+
"forceConsistentCasingInFileNames": true,
|
|
6
|
+
"lib": ["ESNext"],
|
|
7
|
+
"module": "NodeNext",
|
|
8
|
+
"moduleResolution": "NodeNext",
|
|
9
|
+
"noEmit": true,
|
|
10
|
+
"noFallthroughCasesInSwitch": true,
|
|
11
|
+
"noImplicitReturns": true,
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"skipLibCheck": true,
|
|
14
|
+
"sourceMap": true,
|
|
15
|
+
"strict": true,
|
|
16
|
+
"target": "ESNext"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/tsconfig",
|
|
3
|
+
"extends": "./base.json",
|
|
4
|
+
"compilerOptions": {
|
|
5
|
+
"declaration": true,
|
|
6
|
+
"inlineSources": true,
|
|
7
|
+
"declarationMap": true,
|
|
8
|
+
"emitDecoratorMetadata": true,
|
|
9
|
+
"experimentalDecorators": true,
|
|
10
|
+
"forceConsistentCasingInFileNames": true,
|
|
11
|
+
"noEmit": false,
|
|
12
|
+
"removeComments": true,
|
|
13
|
+
"skipLibCheck": true,
|
|
14
|
+
"stripInternal": true
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/tsconfig",
|
|
3
|
+
"extends": "./base.json",
|
|
4
|
+
"compilerOptions": {
|
|
5
|
+
"allowJs": true,
|
|
6
|
+
"isolatedModules": true,
|
|
7
|
+
"incremental": true,
|
|
8
|
+
"jsx": "preserve",
|
|
9
|
+
"lib": ["dom", "dom.iterable", "ESNext"],
|
|
10
|
+
"module": "ESNext",
|
|
11
|
+
"moduleResolution": "bundler",
|
|
12
|
+
"plugins": [
|
|
13
|
+
{
|
|
14
|
+
"name": "next"
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
}
|
|
18
|
+
}
|
package/vitest/base.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { defineConfig, mergeConfig } from "vitest/config";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @typedef {Object} DevkitVitestOptions
|
|
5
|
+
* @property {"node" | "jsdom" | "happy-dom"} [environment]
|
|
6
|
+
* @property {string[]} [setupFiles]
|
|
7
|
+
* @property {boolean} [coverage]
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @param {DevkitVitestOptions} [options]
|
|
12
|
+
*/
|
|
13
|
+
export function defineDevkitConfig(options = {}) {
|
|
14
|
+
const isCI = Boolean(process.env.CI);
|
|
15
|
+
|
|
16
|
+
const baseConfig = {
|
|
17
|
+
test: {
|
|
18
|
+
coverage: options.coverage ? { provider: "v8" } : undefined,
|
|
19
|
+
environment: options.environment ?? "node",
|
|
20
|
+
globals: true,
|
|
21
|
+
reporters: isCI ? ["basic"] : ["verbose"],
|
|
22
|
+
retry: isCI ? 2 : 0,
|
|
23
|
+
setupFiles: options.setupFiles,
|
|
24
|
+
testTimeout: isCI ? 30_000 : 10_000,
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
return defineConfig(baseConfig);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Re-export for merging with existing vite configs
|
|
32
|
+
export { mergeConfig };
|