@k8o/create 0.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 +72 -0
- package/bin/index.ts +7 -0
- package/package.json +60 -0
- package/src/library.ts +328 -0
- package/src/shared.ts +149 -0
- package/src/template.ts +33 -0
- package/src/web.ts +585 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) k8o
|
|
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
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# @k8o/create
|
|
2
|
+
|
|
3
|
+
k8o's project generator for [Vite+](https://viteplus.dev). One command spins up
|
|
4
|
+
a new repo with the conventions already wired in — supply-chain rules, oxlint /
|
|
5
|
+
oxfmt via [`@k8o/oxc-config`](https://github.com/k35o/oxc-config), Changesets,
|
|
6
|
+
GitHub Actions CI/release, mise, and Renovate.
|
|
7
|
+
|
|
8
|
+
## Usage
|
|
9
|
+
|
|
10
|
+
```sh
|
|
11
|
+
vp create @k8o # interactive: pick library / web, enter a name
|
|
12
|
+
vp create @k8o -- --kind library --name @k8o/foo --description "..."
|
|
13
|
+
vp create @k8o -- --kind web --name @k8o/foo
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
`vp create @k8o` resolves this package (`@k8o/create`) and runs its generator.
|
|
17
|
+
|
|
18
|
+
## Kinds
|
|
19
|
+
|
|
20
|
+
| `--kind` | Emits |
|
|
21
|
+
| --------- | -------------------------------------------------------------------------------- |
|
|
22
|
+
| `library` | Single-package library: `src` + `tests`, `vp pack` → ESM + `.d.mts`. |
|
|
23
|
+
| `web` | Monorepo: `packages/<name>` React lib + `apps/playground`, on @k8o/arte-odyssey. |
|
|
24
|
+
|
|
25
|
+
Both generated repos pass `check` (fmt + lint + types), `test`, and `build`
|
|
26
|
+
out of the box.
|
|
27
|
+
|
|
28
|
+
### What every generated repo gets
|
|
29
|
+
|
|
30
|
+
- **Supply-chain rules** — `pnpm-workspace.yaml` with `blockExoticSubdeps`,
|
|
31
|
+
`minimumReleaseAge`, `strictDepBuilds`, `verifyDepsBeforeRun`, `saveExact`,
|
|
32
|
+
`autoInstallPeers: false`.
|
|
33
|
+
- **Lint / format** — `@k8o/oxc-config` presets via `vp check`.
|
|
34
|
+
- **Versioning** — Changesets + a `release.yml` that publishes to npm with
|
|
35
|
+
provenance using the `K35O_BOT` GitHub App.
|
|
36
|
+
- **CI** — `ci.yml` (lint / types / tests / changeset) on a composite
|
|
37
|
+
mise-based install action.
|
|
38
|
+
- **Toolchain** — pinned `mise.toml`, `renovate.json` extending
|
|
39
|
+
`github>k35o/renovate-config`.
|
|
40
|
+
|
|
41
|
+
## Layout
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
bin/index.ts # Bingo runTemplateCLI entry
|
|
45
|
+
src/
|
|
46
|
+
template.ts # createTemplate: { kind, name, description } → dispatch
|
|
47
|
+
library.ts # produceLibrary()
|
|
48
|
+
web.ts # produceWeb()
|
|
49
|
+
shared.ts # base files shared by both kinds (single source)
|
|
50
|
+
tests/produce.test.ts
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Each kind is built by a `produce()` that returns the file tree. The shared base
|
|
54
|
+
files (security config, CI, license, mise, …) live once in `src/shared.ts` and
|
|
55
|
+
are imported by both — change a convention in one place and both kinds follow.
|
|
56
|
+
|
|
57
|
+
## Adding a kind
|
|
58
|
+
|
|
59
|
+
Add a value to the `kind` enum in [`src/template.ts`](src/template.ts), write a
|
|
60
|
+
`produce*()` in a new `src/<kind>.ts` reusing `src/shared.ts`, and dispatch to
|
|
61
|
+
it. No new package to publish.
|
|
62
|
+
|
|
63
|
+
## Develop
|
|
64
|
+
|
|
65
|
+
```sh
|
|
66
|
+
pnpm install
|
|
67
|
+
pnpm check # fmt + lint
|
|
68
|
+
pnpm typecheck
|
|
69
|
+
pnpm test # generator smoke tests
|
|
70
|
+
pnpm dev -- --kind library --name @k8o/scratch --directory /tmp/scratch --offline
|
|
71
|
+
pnpm changeset # describe a change before merging
|
|
72
|
+
```
|
package/bin/index.ts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@k8o/create",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "k8o's Vite+ project generator — run `vp create @k8o`.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"bingo-template",
|
|
7
|
+
"create",
|
|
8
|
+
"k8o",
|
|
9
|
+
"template",
|
|
10
|
+
"vite-plus-generator"
|
|
11
|
+
],
|
|
12
|
+
"homepage": "https://github.com/k35o/templates#readme",
|
|
13
|
+
"bugs": "https://github.com/k35o/templates/issues",
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"author": "k8o (https://github.com/k35o)",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "git+https://github.com/k35o/templates.git"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"bin",
|
|
22
|
+
"src",
|
|
23
|
+
"README.md",
|
|
24
|
+
"LICENSE"
|
|
25
|
+
],
|
|
26
|
+
"type": "module",
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public",
|
|
29
|
+
"provenance": true
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"bingo": "0.7.0",
|
|
33
|
+
"zod": "3.25.76"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@changesets/changelog-github": "0.7.0",
|
|
37
|
+
"@changesets/cli": "2.31.0",
|
|
38
|
+
"@k8o/oxc-config": "0.1.3",
|
|
39
|
+
"@types/node": "24.12.4",
|
|
40
|
+
"typescript": "6.0.3",
|
|
41
|
+
"vite-plus": "0.1.23"
|
|
42
|
+
},
|
|
43
|
+
"engines": {
|
|
44
|
+
"node": ">=24.13.0"
|
|
45
|
+
},
|
|
46
|
+
"scripts": {
|
|
47
|
+
"test": "vp test",
|
|
48
|
+
"dev": "node bin/index.ts",
|
|
49
|
+
"check": "vp check",
|
|
50
|
+
"check:write": "vp check --fix",
|
|
51
|
+
"typecheck": "tsc --noEmit",
|
|
52
|
+
"changeset": "changeset",
|
|
53
|
+
"version": "changeset version",
|
|
54
|
+
"release": "changeset publish",
|
|
55
|
+
"ready": "vp fmt && vp lint && vp test"
|
|
56
|
+
},
|
|
57
|
+
"bin": {
|
|
58
|
+
"create": "./bin/index.ts"
|
|
59
|
+
}
|
|
60
|
+
}
|
package/src/library.ts
ADDED
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
import {
|
|
2
|
+
changesetConfig,
|
|
3
|
+
CHANGESET_README,
|
|
4
|
+
coords,
|
|
5
|
+
type GenerateOptions,
|
|
6
|
+
GITIGNORE,
|
|
7
|
+
INSTALL_ACTION,
|
|
8
|
+
LICENSE,
|
|
9
|
+
MISE_TOML,
|
|
10
|
+
NPMRC,
|
|
11
|
+
RELEASE_YML,
|
|
12
|
+
} from './shared.ts';
|
|
13
|
+
|
|
14
|
+
// Single-package repo: pnpm-workspace.yaml carries the supply-chain rules only.
|
|
15
|
+
const PNPM_WORKSPACE = `blockExoticSubdeps: true
|
|
16
|
+
|
|
17
|
+
minimumReleaseAge: 10080
|
|
18
|
+
minimumReleaseAgeExclude:
|
|
19
|
+
- '@k8o/*'
|
|
20
|
+
|
|
21
|
+
strictDepBuilds: true
|
|
22
|
+
|
|
23
|
+
allowBuilds:
|
|
24
|
+
esbuild: false
|
|
25
|
+
|
|
26
|
+
verifyDepsBeforeRun: install
|
|
27
|
+
|
|
28
|
+
saveExact: true
|
|
29
|
+
autoInstallPeers: false
|
|
30
|
+
`;
|
|
31
|
+
|
|
32
|
+
const TSCONFIG = `{
|
|
33
|
+
"compilerOptions": {
|
|
34
|
+
"target": "ESNext",
|
|
35
|
+
"module": "ESNext",
|
|
36
|
+
"moduleResolution": "bundler",
|
|
37
|
+
"moduleDetection": "force",
|
|
38
|
+
"customConditions": ["source"],
|
|
39
|
+
"lib": ["ES2023"],
|
|
40
|
+
"strict": true,
|
|
41
|
+
"noUncheckedIndexedAccess": true,
|
|
42
|
+
"exactOptionalPropertyTypes": true,
|
|
43
|
+
"noImplicitOverride": true,
|
|
44
|
+
"noImplicitReturns": true,
|
|
45
|
+
"noFallthroughCasesInSwitch": true,
|
|
46
|
+
"noUnusedLocals": true,
|
|
47
|
+
"noUnusedParameters": true,
|
|
48
|
+
"allowImportingTsExtensions": true,
|
|
49
|
+
"isolatedModules": true,
|
|
50
|
+
"verbatimModuleSyntax": true,
|
|
51
|
+
"skipLibCheck": true,
|
|
52
|
+
"declaration": true,
|
|
53
|
+
"noEmit": true,
|
|
54
|
+
"resolveJsonModule": true,
|
|
55
|
+
"esModuleInterop": true,
|
|
56
|
+
"forceConsistentCasingInFileNames": true,
|
|
57
|
+
"types": ["node", "vite-plus/test/globals"]
|
|
58
|
+
},
|
|
59
|
+
"include": ["src/**/*", "tests/**/*", "vite.config.ts"]
|
|
60
|
+
}
|
|
61
|
+
`;
|
|
62
|
+
|
|
63
|
+
const VITE_CONFIG = `import { fmt, test, typescript } from '@k8o/oxc-config';
|
|
64
|
+
import { defineConfig } from 'vite-plus';
|
|
65
|
+
|
|
66
|
+
export default defineConfig({
|
|
67
|
+
fmt: {
|
|
68
|
+
...fmt,
|
|
69
|
+
ignorePatterns: ['CHANGELOG.md'],
|
|
70
|
+
},
|
|
71
|
+
lint: {
|
|
72
|
+
extends: [typescript],
|
|
73
|
+
ignorePatterns: ['CHANGELOG.md'],
|
|
74
|
+
options: {
|
|
75
|
+
typeAware: true,
|
|
76
|
+
},
|
|
77
|
+
overrides: [
|
|
78
|
+
{
|
|
79
|
+
files: ['tests/**/*.test.ts'],
|
|
80
|
+
plugins: [...(test.plugins ?? [])],
|
|
81
|
+
rules: test.rules ?? {},
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
},
|
|
85
|
+
pack: {
|
|
86
|
+
entry: ['src/index.ts'],
|
|
87
|
+
format: 'esm',
|
|
88
|
+
dts: true,
|
|
89
|
+
outDir: 'dist',
|
|
90
|
+
unbundle: true,
|
|
91
|
+
},
|
|
92
|
+
test: {
|
|
93
|
+
globals: true,
|
|
94
|
+
include: ['tests/**/*.test.ts'],
|
|
95
|
+
},
|
|
96
|
+
staged: {
|
|
97
|
+
'*.{js,ts,cjs,mjs,jsx,tsx,json,jsonc}': 'vp check --fix',
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
`;
|
|
101
|
+
|
|
102
|
+
const CI_YML = `name: CI
|
|
103
|
+
|
|
104
|
+
on:
|
|
105
|
+
pull_request:
|
|
106
|
+
|
|
107
|
+
permissions:
|
|
108
|
+
contents: read
|
|
109
|
+
|
|
110
|
+
jobs:
|
|
111
|
+
check:
|
|
112
|
+
name: Lint / Format / Types
|
|
113
|
+
runs-on: ubuntu-latest
|
|
114
|
+
timeout-minutes: 10
|
|
115
|
+
steps:
|
|
116
|
+
- name: Checkout branch
|
|
117
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
118
|
+
with:
|
|
119
|
+
persist-credentials: false
|
|
120
|
+
|
|
121
|
+
- name: Install
|
|
122
|
+
uses: ./.github/composite-actions/install
|
|
123
|
+
|
|
124
|
+
- name: Build
|
|
125
|
+
run: pnpm build
|
|
126
|
+
|
|
127
|
+
- name: Run check (fmt + lint + typecheck)
|
|
128
|
+
run: pnpm check
|
|
129
|
+
|
|
130
|
+
tests:
|
|
131
|
+
name: Tests
|
|
132
|
+
runs-on: ubuntu-latest
|
|
133
|
+
timeout-minutes: 10
|
|
134
|
+
steps:
|
|
135
|
+
- name: Checkout branch
|
|
136
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
137
|
+
with:
|
|
138
|
+
persist-credentials: false
|
|
139
|
+
|
|
140
|
+
- name: Install
|
|
141
|
+
uses: ./.github/composite-actions/install
|
|
142
|
+
|
|
143
|
+
- name: Run tests
|
|
144
|
+
run: pnpm test
|
|
145
|
+
|
|
146
|
+
changeset:
|
|
147
|
+
name: Changeset
|
|
148
|
+
runs-on: ubuntu-latest
|
|
149
|
+
timeout-minutes: 10
|
|
150
|
+
steps:
|
|
151
|
+
- name: Checkout branch
|
|
152
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
153
|
+
with:
|
|
154
|
+
fetch-depth: 0
|
|
155
|
+
persist-credentials: false
|
|
156
|
+
|
|
157
|
+
- name: Install
|
|
158
|
+
uses: ./.github/composite-actions/install
|
|
159
|
+
|
|
160
|
+
- name: Verify a changeset is present
|
|
161
|
+
run: pnpm exec changeset status --since=origin/main
|
|
162
|
+
`;
|
|
163
|
+
|
|
164
|
+
const RENOVATE = `{
|
|
165
|
+
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
|
166
|
+
"extends": ["github>k35o/renovate-config"],
|
|
167
|
+
"mise": {
|
|
168
|
+
"enabled": true,
|
|
169
|
+
"managerFilePatterns": ["/^mise\\\\.toml$/"]
|
|
170
|
+
},
|
|
171
|
+
"prConcurrentLimit": 10,
|
|
172
|
+
"packageRules": [
|
|
173
|
+
{
|
|
174
|
+
"matchPackageNames": ["/^@changesets/"],
|
|
175
|
+
"groupName": "changesets dependencies"
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
"matchPackageNames": ["/^pnpm$/"],
|
|
179
|
+
"groupName": "pnpm dependencies"
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
"matchPackageNames": ["/^node$/"],
|
|
183
|
+
"groupName": "node dependencies"
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
"matchPackageNames": ["changesets/action"],
|
|
187
|
+
"enabled": false
|
|
188
|
+
}
|
|
189
|
+
]
|
|
190
|
+
}
|
|
191
|
+
`;
|
|
192
|
+
|
|
193
|
+
const SRC_INDEX = `export const hello = (name: string): string => \`Hello, \${name}!\`;
|
|
194
|
+
`;
|
|
195
|
+
|
|
196
|
+
const TEST_INDEX = `import { hello } from '../src/index.ts';
|
|
197
|
+
|
|
198
|
+
test('hello greets by name', () => {
|
|
199
|
+
expect(hello('world')).toBe('Hello, world!');
|
|
200
|
+
});
|
|
201
|
+
`;
|
|
202
|
+
|
|
203
|
+
export const produceLibrary = (options: GenerateOptions) => {
|
|
204
|
+
const { repo } = coords(options.name);
|
|
205
|
+
const description = options.description ?? '';
|
|
206
|
+
|
|
207
|
+
const packageJson = {
|
|
208
|
+
name: options.name,
|
|
209
|
+
version: '0.0.0',
|
|
210
|
+
description,
|
|
211
|
+
keywords: [] as string[],
|
|
212
|
+
homepage: `https://github.com/${repo}#readme`,
|
|
213
|
+
bugs: `https://github.com/${repo}/issues`,
|
|
214
|
+
license: 'MIT',
|
|
215
|
+
author: 'k8o (https://github.com/k35o)',
|
|
216
|
+
repository: {
|
|
217
|
+
type: 'git',
|
|
218
|
+
url: `git+https://github.com/${repo}.git`,
|
|
219
|
+
},
|
|
220
|
+
files: ['dist', 'README.md', 'LICENSE', 'CHANGELOG.md'],
|
|
221
|
+
type: 'module',
|
|
222
|
+
sideEffects: false,
|
|
223
|
+
exports: {
|
|
224
|
+
'.': {
|
|
225
|
+
import: {
|
|
226
|
+
types: './dist/index.d.mts',
|
|
227
|
+
default: './dist/index.mjs',
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
'./package.json': './package.json',
|
|
231
|
+
},
|
|
232
|
+
publishConfig: {
|
|
233
|
+
access: 'public',
|
|
234
|
+
provenance: true,
|
|
235
|
+
},
|
|
236
|
+
scripts: {
|
|
237
|
+
build: 'vp pack',
|
|
238
|
+
test: 'vp test',
|
|
239
|
+
lint: 'vp lint',
|
|
240
|
+
fmt: 'vp fmt',
|
|
241
|
+
check: 'vp check',
|
|
242
|
+
'check:write': 'vp check --fix',
|
|
243
|
+
typecheck: 'tsc --noEmit',
|
|
244
|
+
changeset: 'changeset',
|
|
245
|
+
version: 'changeset version',
|
|
246
|
+
release: 'pnpm build && changeset publish',
|
|
247
|
+
prepublishOnly: 'pnpm build',
|
|
248
|
+
},
|
|
249
|
+
devDependencies: {
|
|
250
|
+
'@changesets/changelog-github': '0.7.0',
|
|
251
|
+
'@changesets/cli': '2.31.0',
|
|
252
|
+
'@k8o/oxc-config': '0.1.3',
|
|
253
|
+
'@types/node': '24.12.4',
|
|
254
|
+
typescript: '6.0.3',
|
|
255
|
+
'vite-plus': '0.1.23',
|
|
256
|
+
},
|
|
257
|
+
engines: {
|
|
258
|
+
node: '>=24.13.0',
|
|
259
|
+
},
|
|
260
|
+
packageManager: 'pnpm@11.5.1',
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
const readme = `# ${options.name}
|
|
264
|
+
|
|
265
|
+
${description}
|
|
266
|
+
|
|
267
|
+
## Install
|
|
268
|
+
|
|
269
|
+
\`\`\`sh
|
|
270
|
+
pnpm add ${options.name}
|
|
271
|
+
\`\`\`
|
|
272
|
+
|
|
273
|
+
## Develop
|
|
274
|
+
|
|
275
|
+
\`\`\`sh
|
|
276
|
+
pnpm install
|
|
277
|
+
pnpm check # fmt + lint + typecheck
|
|
278
|
+
pnpm test
|
|
279
|
+
pnpm build # vp pack -> dist/
|
|
280
|
+
\`\`\`
|
|
281
|
+
|
|
282
|
+
## Release
|
|
283
|
+
|
|
284
|
+
Versioned and published with [Changesets](https://github.com/changesets/changesets).
|
|
285
|
+
|
|
286
|
+
\`\`\`sh
|
|
287
|
+
pnpm changeset # describe the change
|
|
288
|
+
\`\`\`
|
|
289
|
+
|
|
290
|
+
Merging to \`main\` lets the release workflow open a version PR and publish to npm.
|
|
291
|
+
`;
|
|
292
|
+
|
|
293
|
+
return {
|
|
294
|
+
files: {
|
|
295
|
+
'package.json': `${JSON.stringify(packageJson, null, 2)}\n`,
|
|
296
|
+
'pnpm-workspace.yaml': PNPM_WORKSPACE,
|
|
297
|
+
'.npmrc': NPMRC,
|
|
298
|
+
'.gitignore': GITIGNORE,
|
|
299
|
+
'mise.toml': MISE_TOML,
|
|
300
|
+
'tsconfig.json': TSCONFIG,
|
|
301
|
+
'vite.config.ts': VITE_CONFIG,
|
|
302
|
+
'renovate.json': RENOVATE,
|
|
303
|
+
LICENSE,
|
|
304
|
+
'README.md': readme,
|
|
305
|
+
'.changeset': {
|
|
306
|
+
'config.json': changesetConfig(repo),
|
|
307
|
+
'README.md': CHANGESET_README,
|
|
308
|
+
},
|
|
309
|
+
'.github': {
|
|
310
|
+
'composite-actions': {
|
|
311
|
+
install: {
|
|
312
|
+
'action.yml': INSTALL_ACTION,
|
|
313
|
+
},
|
|
314
|
+
},
|
|
315
|
+
workflows: {
|
|
316
|
+
'ci.yml': CI_YML,
|
|
317
|
+
'release.yml': RELEASE_YML,
|
|
318
|
+
},
|
|
319
|
+
},
|
|
320
|
+
src: {
|
|
321
|
+
'index.ts': SRC_INDEX,
|
|
322
|
+
},
|
|
323
|
+
tests: {
|
|
324
|
+
'index.test.ts': TEST_INDEX,
|
|
325
|
+
},
|
|
326
|
+
},
|
|
327
|
+
};
|
|
328
|
+
};
|
package/src/shared.ts
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Shared k8o base files — identical across the `library` and `web` templates.
|
|
3
|
+
// Template-specific files (pnpm-workspace, tsconfig, vite.config, CI, renovate)
|
|
4
|
+
// live in ./library.ts and ./web.ts.
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
|
|
7
|
+
export type GenerateOptions = {
|
|
8
|
+
name: string;
|
|
9
|
+
description?: string | undefined;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/** Derive repo coordinates from a (possibly scoped) package name. */
|
|
13
|
+
export const coords = (name: string): { bare: string; repo: string } => {
|
|
14
|
+
if (!/^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/u.test(name)) {
|
|
15
|
+
throw new Error(
|
|
16
|
+
`Invalid package name "${name}" — expected an (optionally @scoped) npm package name.`,
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
const bare = name.replace(/^@[^/]+\//u, '');
|
|
20
|
+
return { bare, repo: `k35o/${bare}` };
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export const NPMRC = `registry=https://registry.npmjs.org/
|
|
24
|
+
@k8o:registry=https://registry.npmjs.org/
|
|
25
|
+
|
|
26
|
+
engine-strict=true
|
|
27
|
+
`;
|
|
28
|
+
|
|
29
|
+
export const GITIGNORE = `node_modules
|
|
30
|
+
dist
|
|
31
|
+
coverage
|
|
32
|
+
*.log
|
|
33
|
+
.DS_Store
|
|
34
|
+
*.tsbuildinfo
|
|
35
|
+
.env*
|
|
36
|
+
*.local
|
|
37
|
+
.vscode/*
|
|
38
|
+
!.vscode/extensions.json
|
|
39
|
+
settings.local.json
|
|
40
|
+
`;
|
|
41
|
+
|
|
42
|
+
export const MISE_TOML = `[tools]
|
|
43
|
+
node = "24.16.0"
|
|
44
|
+
pnpm = "11.5.1"
|
|
45
|
+
`;
|
|
46
|
+
|
|
47
|
+
export const INSTALL_ACTION = `name: Install
|
|
48
|
+
description: Sets up mise and runs install
|
|
49
|
+
|
|
50
|
+
runs:
|
|
51
|
+
using: composite
|
|
52
|
+
steps:
|
|
53
|
+
- name: Setup mise
|
|
54
|
+
uses: jdx/mise-action@1648a7812b9aeae629881980618f079932869151 # v4.0.1
|
|
55
|
+
with:
|
|
56
|
+
cache: true
|
|
57
|
+
|
|
58
|
+
- name: Install dependencies
|
|
59
|
+
shell: bash
|
|
60
|
+
run: pnpm install --frozen-lockfile
|
|
61
|
+
`;
|
|
62
|
+
|
|
63
|
+
export const RELEASE_YML = `name: Release
|
|
64
|
+
|
|
65
|
+
on:
|
|
66
|
+
push:
|
|
67
|
+
branches:
|
|
68
|
+
- main
|
|
69
|
+
|
|
70
|
+
concurrency:
|
|
71
|
+
group: \${{ github.workflow }}-\${{ github.ref }}
|
|
72
|
+
|
|
73
|
+
permissions:
|
|
74
|
+
contents: write
|
|
75
|
+
id-token: write
|
|
76
|
+
pull-requests: write
|
|
77
|
+
|
|
78
|
+
jobs:
|
|
79
|
+
release:
|
|
80
|
+
runs-on: ubuntu-latest
|
|
81
|
+
steps:
|
|
82
|
+
- name: Generate token
|
|
83
|
+
id: generate-token
|
|
84
|
+
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1
|
|
85
|
+
with:
|
|
86
|
+
client-id: \${{ secrets.K35O_BOT_CLIENT_ID }}
|
|
87
|
+
private-key: \${{ secrets.K35O_BOT_PRIVATE_KEY }}
|
|
88
|
+
|
|
89
|
+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
90
|
+
with:
|
|
91
|
+
fetch-depth: 0
|
|
92
|
+
token: \${{ steps.generate-token.outputs.token }}
|
|
93
|
+
|
|
94
|
+
- name: Install
|
|
95
|
+
uses: ./.github/composite-actions/install
|
|
96
|
+
|
|
97
|
+
- name: Install latest npm
|
|
98
|
+
run: npm install -g npm@latest
|
|
99
|
+
|
|
100
|
+
- name: Create release Pull Request or publish to NPM
|
|
101
|
+
uses: changesets/action@6a0a831ff30acef54f2c6aa1cbbc1096b066edaf # v1.7.0
|
|
102
|
+
with:
|
|
103
|
+
publish: pnpm release
|
|
104
|
+
env:
|
|
105
|
+
GITHUB_TOKEN: \${{ steps.generate-token.outputs.token }}
|
|
106
|
+
`;
|
|
107
|
+
|
|
108
|
+
export const CHANGESET_README = `# Changesets
|
|
109
|
+
|
|
110
|
+
Hello and welcome! This folder has been automatically generated by \`@changesets/cli\`, a build tool that works with multi-package repos, or single-package repos to help you version and publish your code. You can find the full documentation for it [in our repository](https://github.com/changesets/changesets).
|
|
111
|
+
|
|
112
|
+
We have a quick list of common questions to get you started engaging with this project in [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md).
|
|
113
|
+
`;
|
|
114
|
+
|
|
115
|
+
export const LICENSE = `MIT License
|
|
116
|
+
|
|
117
|
+
Copyright (c) k8o
|
|
118
|
+
|
|
119
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
120
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
121
|
+
in the Software without restriction, including without limitation the rights
|
|
122
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
123
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
124
|
+
furnished to do so, subject to the following conditions:
|
|
125
|
+
|
|
126
|
+
The above copyright notice and this permission notice shall be included in all
|
|
127
|
+
copies or substantial portions of the Software.
|
|
128
|
+
|
|
129
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
130
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
131
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
132
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
133
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
134
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
135
|
+
SOFTWARE.
|
|
136
|
+
`;
|
|
137
|
+
|
|
138
|
+
export const changesetConfig = (repo: string): string => `{
|
|
139
|
+
"$schema": "https://unpkg.com/@changesets/config@3.1.1/schema.json",
|
|
140
|
+
"changelog": ["@changesets/changelog-github", { "repo": "${repo}" }],
|
|
141
|
+
"commit": false,
|
|
142
|
+
"fixed": [],
|
|
143
|
+
"linked": [],
|
|
144
|
+
"access": "public",
|
|
145
|
+
"baseBranch": "main",
|
|
146
|
+
"updateInternalDependencies": "patch",
|
|
147
|
+
"ignore": []
|
|
148
|
+
}
|
|
149
|
+
`;
|
package/src/template.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { createTemplate } from 'bingo';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
import pkgJson from '../package.json' with { type: 'json' };
|
|
5
|
+
import { produceLibrary } from './library.ts';
|
|
6
|
+
import { produceWeb } from './web.ts';
|
|
7
|
+
|
|
8
|
+
export default createTemplate({
|
|
9
|
+
about: {
|
|
10
|
+
name: pkgJson.name,
|
|
11
|
+
description: pkgJson.description,
|
|
12
|
+
},
|
|
13
|
+
|
|
14
|
+
options: {
|
|
15
|
+
kind: z
|
|
16
|
+
.enum(['library', 'web'])
|
|
17
|
+
.describe('What to scaffold: a library or a web (React) package'),
|
|
18
|
+
name: z
|
|
19
|
+
.string()
|
|
20
|
+
.regex(
|
|
21
|
+
/^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/u,
|
|
22
|
+
'Must be a valid (optionally @scoped) npm package name',
|
|
23
|
+
)
|
|
24
|
+
.describe('Package name, e.g. @k8o/foo'),
|
|
25
|
+
description: z.string().optional().describe('One-line package description'),
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
produce({ options }) {
|
|
29
|
+
return options.kind === 'web'
|
|
30
|
+
? produceWeb(options)
|
|
31
|
+
: produceLibrary(options);
|
|
32
|
+
},
|
|
33
|
+
});
|
package/src/web.ts
ADDED
|
@@ -0,0 +1,585 @@
|
|
|
1
|
+
import {
|
|
2
|
+
changesetConfig,
|
|
3
|
+
CHANGESET_README,
|
|
4
|
+
coords,
|
|
5
|
+
type GenerateOptions,
|
|
6
|
+
GITIGNORE,
|
|
7
|
+
INSTALL_ACTION,
|
|
8
|
+
LICENSE,
|
|
9
|
+
MISE_TOML,
|
|
10
|
+
NPMRC,
|
|
11
|
+
RELEASE_YML,
|
|
12
|
+
} from './shared.ts';
|
|
13
|
+
|
|
14
|
+
const COMMITLINT = `export default { extends: ['@commitlint/config-conventional'] };
|
|
15
|
+
`;
|
|
16
|
+
|
|
17
|
+
// Monorepo: packages/* (publishable libs) + apps/* (private apps/examples).
|
|
18
|
+
const PNPM_WORKSPACE = `packages:
|
|
19
|
+
- packages/*
|
|
20
|
+
- apps/*
|
|
21
|
+
|
|
22
|
+
blockExoticSubdeps: true
|
|
23
|
+
|
|
24
|
+
minimumReleaseAge: 10080
|
|
25
|
+
minimumReleaseAgeExclude:
|
|
26
|
+
- '@k8o/*'
|
|
27
|
+
|
|
28
|
+
strictDepBuilds: true
|
|
29
|
+
|
|
30
|
+
allowBuilds:
|
|
31
|
+
esbuild: false
|
|
32
|
+
|
|
33
|
+
verifyDepsBeforeRun: install
|
|
34
|
+
|
|
35
|
+
saveExact: true
|
|
36
|
+
autoInstallPeers: false
|
|
37
|
+
|
|
38
|
+
catalog:
|
|
39
|
+
'@changesets/changelog-github': 0.7.0
|
|
40
|
+
'@changesets/cli': 2.31.0
|
|
41
|
+
'@commitlint/cli': 21.0.2
|
|
42
|
+
'@commitlint/config-conventional': 21.0.2
|
|
43
|
+
'@k8o/arte-odyssey': 10.1.0
|
|
44
|
+
'@k8o/oxc-config': 0.1.3
|
|
45
|
+
'@tailwindcss/vite': 4.3.0
|
|
46
|
+
'@types/node': 24.12.4
|
|
47
|
+
'@types/react': 19.2.15
|
|
48
|
+
'@types/react-dom': 19.2.3
|
|
49
|
+
'@vitejs/plugin-react': 6.0.2
|
|
50
|
+
react: 19.2.6
|
|
51
|
+
react-dom: 19.2.6
|
|
52
|
+
tailwindcss: 4.3.0
|
|
53
|
+
typescript: 6.0.3
|
|
54
|
+
vite: 8.0.14
|
|
55
|
+
vite-plus: 0.1.23
|
|
56
|
+
`;
|
|
57
|
+
|
|
58
|
+
// Root tsconfig for a React monorepo (DOM + JSX). Package tsconfigs extend it.
|
|
59
|
+
const TSCONFIG = `{
|
|
60
|
+
"compilerOptions": {
|
|
61
|
+
"target": "ESNext",
|
|
62
|
+
"module": "ESNext",
|
|
63
|
+
"moduleResolution": "bundler",
|
|
64
|
+
"moduleDetection": "force",
|
|
65
|
+
"customConditions": ["source"],
|
|
66
|
+
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
|
67
|
+
"jsx": "react-jsx",
|
|
68
|
+
"strict": true,
|
|
69
|
+
"noUncheckedIndexedAccess": true,
|
|
70
|
+
"exactOptionalPropertyTypes": true,
|
|
71
|
+
"noImplicitOverride": true,
|
|
72
|
+
"noImplicitReturns": true,
|
|
73
|
+
"noFallthroughCasesInSwitch": true,
|
|
74
|
+
"noUnusedLocals": true,
|
|
75
|
+
"noUnusedParameters": true,
|
|
76
|
+
"allowImportingTsExtensions": true,
|
|
77
|
+
"isolatedModules": true,
|
|
78
|
+
"verbatimModuleSyntax": true,
|
|
79
|
+
"skipLibCheck": true,
|
|
80
|
+
"declaration": true,
|
|
81
|
+
"noEmit": true,
|
|
82
|
+
"resolveJsonModule": true,
|
|
83
|
+
"esModuleInterop": true,
|
|
84
|
+
"forceConsistentCasingInFileNames": true,
|
|
85
|
+
"types": ["node", "vite-plus/test/globals"]
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
`;
|
|
89
|
+
|
|
90
|
+
// Root config drives fmt + lint for the whole monorepo.
|
|
91
|
+
const ROOT_VITE_CONFIG = `import { fmt, react, test } from '@k8o/oxc-config';
|
|
92
|
+
import { defineConfig } from 'vite-plus';
|
|
93
|
+
|
|
94
|
+
export default defineConfig({
|
|
95
|
+
fmt: {
|
|
96
|
+
...fmt,
|
|
97
|
+
ignorePatterns: ['CHANGELOG.md', '**/CHANGELOG.md'],
|
|
98
|
+
},
|
|
99
|
+
lint: {
|
|
100
|
+
extends: [react],
|
|
101
|
+
ignorePatterns: ['CHANGELOG.md', '**/CHANGELOG.md'],
|
|
102
|
+
options: {
|
|
103
|
+
typeAware: true,
|
|
104
|
+
},
|
|
105
|
+
settings: {
|
|
106
|
+
react: { version: '19.2.6' },
|
|
107
|
+
},
|
|
108
|
+
overrides: [
|
|
109
|
+
{
|
|
110
|
+
files: ['**/*.test.{ts,tsx}'],
|
|
111
|
+
plugins: [...(test.plugins ?? [])],
|
|
112
|
+
rules: test.rules ?? {},
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
},
|
|
116
|
+
staged: {
|
|
117
|
+
'*.{js,ts,cjs,mjs,jsx,tsx,json,jsonc}': 'vp check --fix',
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
`;
|
|
121
|
+
|
|
122
|
+
const CI_YML = `name: CI
|
|
123
|
+
|
|
124
|
+
on:
|
|
125
|
+
pull_request:
|
|
126
|
+
|
|
127
|
+
permissions:
|
|
128
|
+
contents: read
|
|
129
|
+
|
|
130
|
+
jobs:
|
|
131
|
+
check:
|
|
132
|
+
name: Build / Lint / Types
|
|
133
|
+
runs-on: ubuntu-latest
|
|
134
|
+
timeout-minutes: 10
|
|
135
|
+
steps:
|
|
136
|
+
- name: Checkout branch
|
|
137
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
138
|
+
with:
|
|
139
|
+
persist-credentials: false
|
|
140
|
+
|
|
141
|
+
- name: Install
|
|
142
|
+
uses: ./.github/composite-actions/install
|
|
143
|
+
|
|
144
|
+
- name: Build
|
|
145
|
+
run: pnpm build
|
|
146
|
+
|
|
147
|
+
- name: Run check (fmt + lint)
|
|
148
|
+
run: pnpm check
|
|
149
|
+
|
|
150
|
+
- name: Typecheck
|
|
151
|
+
run: pnpm typecheck
|
|
152
|
+
|
|
153
|
+
tests:
|
|
154
|
+
name: Tests
|
|
155
|
+
runs-on: ubuntu-latest
|
|
156
|
+
timeout-minutes: 10
|
|
157
|
+
steps:
|
|
158
|
+
- name: Checkout branch
|
|
159
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
160
|
+
with:
|
|
161
|
+
persist-credentials: false
|
|
162
|
+
|
|
163
|
+
- name: Install
|
|
164
|
+
uses: ./.github/composite-actions/install
|
|
165
|
+
|
|
166
|
+
- name: Run tests
|
|
167
|
+
run: pnpm test
|
|
168
|
+
|
|
169
|
+
changeset:
|
|
170
|
+
name: Changeset
|
|
171
|
+
runs-on: ubuntu-latest
|
|
172
|
+
timeout-minutes: 10
|
|
173
|
+
steps:
|
|
174
|
+
- name: Checkout branch
|
|
175
|
+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
176
|
+
with:
|
|
177
|
+
fetch-depth: 0
|
|
178
|
+
persist-credentials: false
|
|
179
|
+
|
|
180
|
+
- name: Install
|
|
181
|
+
uses: ./.github/composite-actions/install
|
|
182
|
+
|
|
183
|
+
- name: Verify a changeset is present
|
|
184
|
+
run: pnpm exec changeset status --since=origin/main
|
|
185
|
+
`;
|
|
186
|
+
|
|
187
|
+
const RENOVATE = `{
|
|
188
|
+
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
|
189
|
+
"extends": ["github>k35o/renovate-config"],
|
|
190
|
+
"mise": {
|
|
191
|
+
"enabled": true,
|
|
192
|
+
"managerFilePatterns": ["/^mise\\\\.toml$/"]
|
|
193
|
+
},
|
|
194
|
+
"prConcurrentLimit": 10,
|
|
195
|
+
"packageRules": [
|
|
196
|
+
{
|
|
197
|
+
"matchPackageNames": ["react", "react-dom", "/^@types/react/"],
|
|
198
|
+
"groupName": "react dependencies"
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
"matchPackageNames": ["/tailwind/"],
|
|
202
|
+
"groupName": "tailwind dependencies"
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
"matchPackageNames": ["/^@changesets/"],
|
|
206
|
+
"groupName": "changesets dependencies"
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
"matchPackageNames": ["/^@commitlint/"],
|
|
210
|
+
"groupName": "commitlint dependencies"
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
"matchPackageNames": ["/^pnpm$/"],
|
|
214
|
+
"groupName": "pnpm dependencies"
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
"matchPackageNames": ["/^node$/"],
|
|
218
|
+
"groupName": "node dependencies"
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
"matchPackageNames": ["changesets/action"],
|
|
222
|
+
"enabled": false
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
"matchDepTypes": ["peerDependencies"],
|
|
226
|
+
"enabled": false
|
|
227
|
+
}
|
|
228
|
+
]
|
|
229
|
+
}
|
|
230
|
+
`;
|
|
231
|
+
|
|
232
|
+
// --- packages/<pkg> : publishable React component library --------------------
|
|
233
|
+
|
|
234
|
+
const PKG_TSCONFIG = `{
|
|
235
|
+
"extends": "../../tsconfig.json",
|
|
236
|
+
"include": ["src/**/*.ts", "src/**/*.tsx", "vite.config.ts"]
|
|
237
|
+
}
|
|
238
|
+
`;
|
|
239
|
+
|
|
240
|
+
const PKG_VITE_CONFIG = `import react from '@vitejs/plugin-react';
|
|
241
|
+
import { defineConfig } from 'vite-plus';
|
|
242
|
+
|
|
243
|
+
export default defineConfig({
|
|
244
|
+
plugins: [react()],
|
|
245
|
+
pack: {
|
|
246
|
+
entry: ['src/**/*.{ts,tsx}', '!src/**/*.test.{ts,tsx}'],
|
|
247
|
+
format: 'esm',
|
|
248
|
+
dts: true,
|
|
249
|
+
outDir: 'dist',
|
|
250
|
+
unbundle: true,
|
|
251
|
+
},
|
|
252
|
+
test: {
|
|
253
|
+
globals: true,
|
|
254
|
+
include: ['src/**/*.test.{ts,tsx}'],
|
|
255
|
+
},
|
|
256
|
+
staged: {
|
|
257
|
+
'*': 'vp check --fix',
|
|
258
|
+
},
|
|
259
|
+
});
|
|
260
|
+
`;
|
|
261
|
+
|
|
262
|
+
const PKG_CN = `export const cn = (
|
|
263
|
+
...classes: Array<string | false | null | undefined>
|
|
264
|
+
): string => classes.filter(Boolean).join(' ');
|
|
265
|
+
`;
|
|
266
|
+
|
|
267
|
+
const PKG_CN_TEST = `import { cn } from './cn.ts';
|
|
268
|
+
|
|
269
|
+
test('cn joins truthy class names', () => {
|
|
270
|
+
expect(cn('a', false, 'b', null, undefined, 'c')).toBe('a b c');
|
|
271
|
+
});
|
|
272
|
+
`;
|
|
273
|
+
|
|
274
|
+
const PKG_HELLO = `import { Button } from '@k8o/arte-odyssey';
|
|
275
|
+
import type { FC } from 'react';
|
|
276
|
+
|
|
277
|
+
export type HelloProps = {
|
|
278
|
+
/** Who to greet. */
|
|
279
|
+
name: string;
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Sample component wrapping @k8o/arte-odyssey's Button.
|
|
284
|
+
* Render it inside <ArteOdysseyProvider> (see apps/playground).
|
|
285
|
+
*/
|
|
286
|
+
export const Hello: FC<HelloProps> = ({ name }) => (
|
|
287
|
+
<Button>Hello, {name}!</Button>
|
|
288
|
+
);
|
|
289
|
+
`;
|
|
290
|
+
|
|
291
|
+
const PKG_INDEX = `export { cn } from './helpers/cn.ts';
|
|
292
|
+
export { Hello } from './components/hello.tsx';
|
|
293
|
+
export type { HelloProps } from './components/hello.tsx';
|
|
294
|
+
`;
|
|
295
|
+
|
|
296
|
+
// --- apps/playground : private Vite + React app using arte-odyssey -----------
|
|
297
|
+
|
|
298
|
+
const APP_TSCONFIG = `{
|
|
299
|
+
"extends": "../../tsconfig.json",
|
|
300
|
+
"compilerOptions": {
|
|
301
|
+
"types": ["node", "vite/client"]
|
|
302
|
+
},
|
|
303
|
+
"include": ["src/**/*.ts", "src/**/*.tsx", "vite.config.ts"]
|
|
304
|
+
}
|
|
305
|
+
`;
|
|
306
|
+
|
|
307
|
+
const APP_VITE_CONFIG = `import tailwindcss from '@tailwindcss/vite';
|
|
308
|
+
import react from '@vitejs/plugin-react';
|
|
309
|
+
import { defineConfig } from 'vite';
|
|
310
|
+
|
|
311
|
+
export default defineConfig({
|
|
312
|
+
plugins: [react(), tailwindcss()],
|
|
313
|
+
});
|
|
314
|
+
`;
|
|
315
|
+
|
|
316
|
+
const APP_INDEX_HTML = `<!doctype html>
|
|
317
|
+
<html lang="en">
|
|
318
|
+
<head>
|
|
319
|
+
<meta charset="UTF-8" />
|
|
320
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
321
|
+
<title>playground</title>
|
|
322
|
+
</head>
|
|
323
|
+
<body>
|
|
324
|
+
<div id="root"></div>
|
|
325
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
326
|
+
</body>
|
|
327
|
+
</html>
|
|
328
|
+
`;
|
|
329
|
+
|
|
330
|
+
const APP_MAIN = `import { StrictMode } from 'react';
|
|
331
|
+
import { createRoot } from 'react-dom/client';
|
|
332
|
+
|
|
333
|
+
import { App } from './app.tsx';
|
|
334
|
+
|
|
335
|
+
import './styles.css';
|
|
336
|
+
|
|
337
|
+
const root = document.querySelector('#root');
|
|
338
|
+
if (root) {
|
|
339
|
+
createRoot(root).render(
|
|
340
|
+
<StrictMode>
|
|
341
|
+
<App />
|
|
342
|
+
</StrictMode>,
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
`;
|
|
346
|
+
|
|
347
|
+
// The playground demonstrates the @k8o/arte-odyssey stack (provider + tailwind).
|
|
348
|
+
// To preview your own package here, add it as a `workspace:*` dependency and
|
|
349
|
+
// import it — its `source` export condition resolves to TS source in dev.
|
|
350
|
+
const APP_APP = `import { ArteOdysseyProvider, Button } from '@k8o/arte-odyssey';
|
|
351
|
+
|
|
352
|
+
export const App = () => (
|
|
353
|
+
<ArteOdysseyProvider>
|
|
354
|
+
<main className="grid min-h-dvh place-items-center gap-4 p-8">
|
|
355
|
+
<h1 className="text-2xl font-bold">@PKG_NAME@ playground</h1>
|
|
356
|
+
<Button>Hello, world!</Button>
|
|
357
|
+
</main>
|
|
358
|
+
</ArteOdysseyProvider>
|
|
359
|
+
);
|
|
360
|
+
`;
|
|
361
|
+
|
|
362
|
+
const APP_STYLES = `@import 'tailwindcss';
|
|
363
|
+
@import '@k8o/arte-odyssey/styles.css';
|
|
364
|
+
`;
|
|
365
|
+
|
|
366
|
+
const APP_VITE_ENV = `/// <reference types="vite/client" />
|
|
367
|
+
`;
|
|
368
|
+
|
|
369
|
+
export const produceWeb = (options: GenerateOptions) => {
|
|
370
|
+
const { bare, repo } = coords(options.name);
|
|
371
|
+
const description = options.description ?? '';
|
|
372
|
+
|
|
373
|
+
const rootPackageJson = {
|
|
374
|
+
name: bare,
|
|
375
|
+
version: '0.0.0',
|
|
376
|
+
private: true,
|
|
377
|
+
description,
|
|
378
|
+
license: 'MIT',
|
|
379
|
+
author: 'k8o (https://github.com/k35o)',
|
|
380
|
+
repository: {
|
|
381
|
+
type: 'git',
|
|
382
|
+
url: `git+https://github.com/${repo}.git`,
|
|
383
|
+
},
|
|
384
|
+
type: 'module',
|
|
385
|
+
scripts: {
|
|
386
|
+
build: "vp run --filter './packages/*' --filter './apps/*' build",
|
|
387
|
+
dev: 'vp run --filter ./apps/playground dev',
|
|
388
|
+
test: 'vp run -r test',
|
|
389
|
+
typecheck: 'vp run -r typecheck',
|
|
390
|
+
check: 'vp check',
|
|
391
|
+
'check:write': 'vp check --fix',
|
|
392
|
+
changeset: 'changeset',
|
|
393
|
+
version: 'changeset version',
|
|
394
|
+
release: 'pnpm build && changeset publish',
|
|
395
|
+
ready: 'vp fmt && vp lint && vp run -r test',
|
|
396
|
+
},
|
|
397
|
+
devDependencies: {
|
|
398
|
+
'@changesets/changelog-github': 'catalog:',
|
|
399
|
+
'@changesets/cli': 'catalog:',
|
|
400
|
+
'@commitlint/cli': 'catalog:',
|
|
401
|
+
'@commitlint/config-conventional': 'catalog:',
|
|
402
|
+
'@k8o/oxc-config': 'catalog:',
|
|
403
|
+
'@types/node': 'catalog:',
|
|
404
|
+
typescript: 'catalog:',
|
|
405
|
+
'vite-plus': 'catalog:',
|
|
406
|
+
},
|
|
407
|
+
engines: {
|
|
408
|
+
node: '>=24.13.0',
|
|
409
|
+
},
|
|
410
|
+
packageManager: 'pnpm@11.5.1',
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
const libPackageJson = {
|
|
414
|
+
name: options.name,
|
|
415
|
+
version: '0.0.0',
|
|
416
|
+
description,
|
|
417
|
+
keywords: [] as string[],
|
|
418
|
+
homepage: `https://github.com/${repo}#readme`,
|
|
419
|
+
bugs: `https://github.com/${repo}/issues`,
|
|
420
|
+
license: 'MIT',
|
|
421
|
+
author: 'k8o (https://github.com/k35o)',
|
|
422
|
+
repository: {
|
|
423
|
+
type: 'git',
|
|
424
|
+
url: `git+https://github.com/${repo}.git`,
|
|
425
|
+
directory: `packages/${bare}`,
|
|
426
|
+
},
|
|
427
|
+
files: ['dist'],
|
|
428
|
+
type: 'module',
|
|
429
|
+
sideEffects: false,
|
|
430
|
+
exports: {
|
|
431
|
+
'.': {
|
|
432
|
+
source: './src/index.ts',
|
|
433
|
+
import: {
|
|
434
|
+
types: './dist/index.d.mts',
|
|
435
|
+
default: './dist/index.mjs',
|
|
436
|
+
},
|
|
437
|
+
},
|
|
438
|
+
'./package.json': './package.json',
|
|
439
|
+
},
|
|
440
|
+
publishConfig: {
|
|
441
|
+
access: 'public',
|
|
442
|
+
provenance: true,
|
|
443
|
+
},
|
|
444
|
+
scripts: {
|
|
445
|
+
build: 'vp pack',
|
|
446
|
+
test: 'vp test',
|
|
447
|
+
typecheck: 'tsc --noEmit',
|
|
448
|
+
check: 'vp check',
|
|
449
|
+
'check:write': 'vp check --fix',
|
|
450
|
+
},
|
|
451
|
+
devDependencies: {
|
|
452
|
+
'@k8o/arte-odyssey': 'catalog:',
|
|
453
|
+
'@types/react': 'catalog:',
|
|
454
|
+
'@types/react-dom': 'catalog:',
|
|
455
|
+
'@vitejs/plugin-react': 'catalog:',
|
|
456
|
+
react: 'catalog:',
|
|
457
|
+
'react-dom': 'catalog:',
|
|
458
|
+
'vite-plus': 'catalog:',
|
|
459
|
+
},
|
|
460
|
+
peerDependencies: {
|
|
461
|
+
'@k8o/arte-odyssey': '>=10',
|
|
462
|
+
react: '>=19',
|
|
463
|
+
'react-dom': '>=19',
|
|
464
|
+
},
|
|
465
|
+
};
|
|
466
|
+
|
|
467
|
+
const appPackageJson = {
|
|
468
|
+
name: 'playground',
|
|
469
|
+
version: '0.0.0',
|
|
470
|
+
private: true,
|
|
471
|
+
type: 'module',
|
|
472
|
+
scripts: {
|
|
473
|
+
dev: 'vite',
|
|
474
|
+
build: 'vite build',
|
|
475
|
+
preview: 'vite preview',
|
|
476
|
+
typecheck: 'tsc --noEmit',
|
|
477
|
+
},
|
|
478
|
+
dependencies: {
|
|
479
|
+
'@k8o/arte-odyssey': 'catalog:',
|
|
480
|
+
react: 'catalog:',
|
|
481
|
+
'react-dom': 'catalog:',
|
|
482
|
+
},
|
|
483
|
+
devDependencies: {
|
|
484
|
+
'@tailwindcss/vite': 'catalog:',
|
|
485
|
+
'@types/react': 'catalog:',
|
|
486
|
+
'@types/react-dom': 'catalog:',
|
|
487
|
+
'@vitejs/plugin-react': 'catalog:',
|
|
488
|
+
tailwindcss: 'catalog:',
|
|
489
|
+
typescript: 'catalog:',
|
|
490
|
+
vite: 'catalog:',
|
|
491
|
+
},
|
|
492
|
+
};
|
|
493
|
+
|
|
494
|
+
const readme = `# ${bare}
|
|
495
|
+
|
|
496
|
+
${description}
|
|
497
|
+
|
|
498
|
+
A React component library built on [@k8o/arte-odyssey](https://github.com/k35o/arte-odyssey).
|
|
499
|
+
|
|
500
|
+
## Layout
|
|
501
|
+
|
|
502
|
+
\`\`\`
|
|
503
|
+
packages/${bare} # the publishable library (${options.name})
|
|
504
|
+
apps/playground # local Vite app to preview it
|
|
505
|
+
\`\`\`
|
|
506
|
+
|
|
507
|
+
## Develop
|
|
508
|
+
|
|
509
|
+
\`\`\`sh
|
|
510
|
+
pnpm install
|
|
511
|
+
pnpm dev # run the playground app
|
|
512
|
+
pnpm check # fmt + lint
|
|
513
|
+
pnpm typecheck
|
|
514
|
+
pnpm test
|
|
515
|
+
pnpm build # build every package and app
|
|
516
|
+
\`\`\`
|
|
517
|
+
|
|
518
|
+
## Release
|
|
519
|
+
|
|
520
|
+
Versioned and published with [Changesets](https://github.com/changesets/changesets).
|
|
521
|
+
Merging to \`main\` lets the release workflow open a version PR and publish to npm.
|
|
522
|
+
`;
|
|
523
|
+
|
|
524
|
+
return {
|
|
525
|
+
files: {
|
|
526
|
+
'package.json': `${JSON.stringify(rootPackageJson, null, 2)}\n`,
|
|
527
|
+
'pnpm-workspace.yaml': PNPM_WORKSPACE,
|
|
528
|
+
'.npmrc': NPMRC,
|
|
529
|
+
'.gitignore': GITIGNORE,
|
|
530
|
+
'mise.toml': MISE_TOML,
|
|
531
|
+
'tsconfig.json': TSCONFIG,
|
|
532
|
+
'vite.config.ts': ROOT_VITE_CONFIG,
|
|
533
|
+
'commitlint.config.js': COMMITLINT,
|
|
534
|
+
'renovate.json': RENOVATE,
|
|
535
|
+
LICENSE,
|
|
536
|
+
'README.md': readme,
|
|
537
|
+
'.changeset': {
|
|
538
|
+
'config.json': changesetConfig(repo),
|
|
539
|
+
'README.md': CHANGESET_README,
|
|
540
|
+
},
|
|
541
|
+
'.github': {
|
|
542
|
+
'composite-actions': {
|
|
543
|
+
install: {
|
|
544
|
+
'action.yml': INSTALL_ACTION,
|
|
545
|
+
},
|
|
546
|
+
},
|
|
547
|
+
workflows: {
|
|
548
|
+
'ci.yml': CI_YML,
|
|
549
|
+
'release.yml': RELEASE_YML,
|
|
550
|
+
},
|
|
551
|
+
},
|
|
552
|
+
packages: {
|
|
553
|
+
[bare]: {
|
|
554
|
+
'package.json': `${JSON.stringify(libPackageJson, null, 2)}\n`,
|
|
555
|
+
'tsconfig.json': PKG_TSCONFIG,
|
|
556
|
+
'vite.config.ts': PKG_VITE_CONFIG,
|
|
557
|
+
src: {
|
|
558
|
+
'index.ts': PKG_INDEX,
|
|
559
|
+
components: {
|
|
560
|
+
'hello.tsx': PKG_HELLO,
|
|
561
|
+
},
|
|
562
|
+
helpers: {
|
|
563
|
+
'cn.ts': PKG_CN,
|
|
564
|
+
'cn.test.ts': PKG_CN_TEST,
|
|
565
|
+
},
|
|
566
|
+
},
|
|
567
|
+
},
|
|
568
|
+
},
|
|
569
|
+
apps: {
|
|
570
|
+
playground: {
|
|
571
|
+
'package.json': `${JSON.stringify(appPackageJson, null, 2)}\n`,
|
|
572
|
+
'tsconfig.json': APP_TSCONFIG,
|
|
573
|
+
'vite.config.ts': APP_VITE_CONFIG,
|
|
574
|
+
'index.html': APP_INDEX_HTML,
|
|
575
|
+
src: {
|
|
576
|
+
'main.tsx': APP_MAIN,
|
|
577
|
+
'app.tsx': APP_APP.replaceAll('@PKG_NAME@', options.name),
|
|
578
|
+
'styles.css': APP_STYLES,
|
|
579
|
+
'vite-env.d.ts': APP_VITE_ENV,
|
|
580
|
+
},
|
|
581
|
+
},
|
|
582
|
+
},
|
|
583
|
+
},
|
|
584
|
+
};
|
|
585
|
+
};
|