@madebywild/wvk 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -0
- package/bin/wvk.mjs +134 -8
- package/dist/index.cjs +3 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/styles.css +0 -4
- package/package.json +6 -2
- package/templates/next-app/README.md +55 -0
- package/templates/next-app/eslint.config.mjs +11 -0
- package/templates/next-app/gitignore +41 -0
- package/templates/next-app/next.config.ts +5 -0
- package/templates/next-app/package.json +28 -0
- package/templates/next-app/postcss.config.mjs +7 -0
- package/templates/next-app/src/app/globals.css +8 -0
- package/templates/next-app/src/app/layout.tsx +48 -0
- package/templates/next-app/src/app/page.tsx +48 -0
- package/templates/next-app/src/components/header.tsx +17 -0
- package/templates/next-app/tsconfig.json +29 -0
package/README.md
CHANGED
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
Wireframe kit — accessible React components, 183 icons, and design tokens that map 1:1 to the wild Wireframe Kit Figma library.
|
|
4
4
|
|
|
5
|
+
## Quick start (new project)
|
|
6
|
+
|
|
7
|
+
Scaffold a fresh Next.js 16 / React 19 / Tailwind v4 app that ships with `@madebywild/wvk` already wired up — `ThemeProvider`, design tokens, the package stylesheet, and a small `Header` composition:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx @madebywild/wvk create-next-app my-wvk-app
|
|
11
|
+
cd my-wvk-app
|
|
12
|
+
npm install
|
|
13
|
+
npm run dev
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Then open [http://localhost:3000](http://localhost:3000). The scaffold also writes an `AGENTS.md` so your agent harness (Claude Code, Cursor, Copilot, etc.) picks up the wvk authoring rules — pass `--no-agents` to skip that, or `--force` to scaffold into a non-empty directory.
|
|
17
|
+
|
|
5
18
|
## Install
|
|
6
19
|
|
|
7
20
|
```bash
|
package/bin/wvk.mjs
CHANGED
|
@@ -1,19 +1,24 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
3
|
import {
|
|
4
|
+
cpSync,
|
|
4
5
|
existsSync,
|
|
5
6
|
mkdirSync,
|
|
6
7
|
readFileSync,
|
|
8
|
+
readdirSync,
|
|
9
|
+
renameSync,
|
|
7
10
|
statSync,
|
|
11
|
+
unlinkSync,
|
|
8
12
|
writeFileSync,
|
|
9
13
|
} from "node:fs";
|
|
10
|
-
import { dirname, relative, resolve } from "node:path";
|
|
14
|
+
import { basename, dirname, join, relative, resolve } from "node:path";
|
|
11
15
|
import { fileURLToPath } from "node:url";
|
|
12
16
|
|
|
13
17
|
const PKG_NAME = "@madebywild/wvk";
|
|
14
18
|
const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url));
|
|
15
19
|
const PKG_ROOT = resolve(SCRIPT_DIR, "..");
|
|
16
20
|
const SOURCE = resolve(PKG_ROOT, "AGENTS.md");
|
|
21
|
+
const TEMPLATES_ROOT = resolve(PKG_ROOT, "templates");
|
|
17
22
|
const PKG_JSON = JSON.parse(
|
|
18
23
|
readFileSync(resolve(PKG_ROOT, "package.json"), "utf8"),
|
|
19
24
|
);
|
|
@@ -25,6 +30,9 @@ const BEGIN_RE = new RegExp(
|
|
|
25
30
|
);
|
|
26
31
|
const END_MARK = `<!-- END ${PKG_NAME} AGENTS.md -->`;
|
|
27
32
|
|
|
33
|
+
// Valid npm package name per https://github.com/npm/validate-npm-package-name
|
|
34
|
+
const NAME_RE = /^(?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/;
|
|
35
|
+
|
|
28
36
|
function escapeRegex(s) {
|
|
29
37
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
30
38
|
}
|
|
@@ -33,19 +41,28 @@ function usage() {
|
|
|
33
41
|
return `wvk — ${PKG_NAME} CLI
|
|
34
42
|
|
|
35
43
|
Usage:
|
|
44
|
+
wvk create-next-app <name> [--force] [--no-agents]
|
|
36
45
|
wvk copy-harness [--force] [--append] [--out <path>]
|
|
37
46
|
wvk --help
|
|
38
47
|
wvk --version
|
|
39
48
|
|
|
40
49
|
Commands:
|
|
41
|
-
|
|
42
|
-
|
|
50
|
+
create-next-app Scaffold a new Next.js 16 / React 19 app preconfigured with
|
|
51
|
+
${PKG_NAME}, Tailwind v4, ThemeProvider, and a Header. Drops
|
|
52
|
+
the package's AGENTS.md into the new project so a consumer's
|
|
53
|
+
agent harness picks up the wvk authoring rules.
|
|
54
|
+
copy-harness Copy this package's AGENTS.md into your project so your
|
|
55
|
+
agent harness picks up the wvk authoring rules.
|
|
43
56
|
|
|
44
57
|
Options:
|
|
45
|
-
--out <path>
|
|
46
|
-
--force
|
|
47
|
-
|
|
48
|
-
|
|
58
|
+
--out <path> (copy-harness) Destination path (default: ./AGENTS.md). Parent dirs are created.
|
|
59
|
+
--force Overwrite an existing destination. For create-next-app, allow
|
|
60
|
+
scaffolding into an existing non-empty directory.
|
|
61
|
+
Mutually exclusive with --append on copy-harness.
|
|
62
|
+
--append (copy-harness) Merge the rules into the destination as a
|
|
63
|
+
delimited, idempotent block. Re-running is safe: the block is
|
|
64
|
+
replaced in place, never duplicated.
|
|
65
|
+
--no-agents (create-next-app) Skip writing AGENTS.md into the new project.
|
|
49
66
|
`;
|
|
50
67
|
}
|
|
51
68
|
|
|
@@ -55,11 +72,12 @@ function fail(msg, code = 1) {
|
|
|
55
72
|
}
|
|
56
73
|
|
|
57
74
|
function parseArgs(argv) {
|
|
58
|
-
const args = { _: [], force: false, append: false, out: null };
|
|
75
|
+
const args = { _: [], force: false, append: false, out: null, noAgents: false };
|
|
59
76
|
for (let i = 0; i < argv.length; i++) {
|
|
60
77
|
const a = argv[i];
|
|
61
78
|
if (a === "--force") args.force = true;
|
|
62
79
|
else if (a === "--append") args.append = true;
|
|
80
|
+
else if (a === "--no-agents") args.noAgents = true;
|
|
63
81
|
else if (a === "--out") {
|
|
64
82
|
const v = argv[++i];
|
|
65
83
|
if (!v) fail("--out requires a path");
|
|
@@ -172,6 +190,110 @@ function report(destRel, mode) {
|
|
|
172
190
|
}
|
|
173
191
|
}
|
|
174
192
|
|
|
193
|
+
function isDirEmpty(dir) {
|
|
194
|
+
try {
|
|
195
|
+
return readdirSync(dir).length === 0;
|
|
196
|
+
} catch {
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function createNextApp(args) {
|
|
202
|
+
if (args.append) {
|
|
203
|
+
fail("--append is not supported for create-next-app");
|
|
204
|
+
}
|
|
205
|
+
const name = args._[1];
|
|
206
|
+
if (!name) {
|
|
207
|
+
fail("create-next-app requires a project name. Example: wvk create-next-app my-app");
|
|
208
|
+
}
|
|
209
|
+
if (!NAME_RE.test(name) && name !== ".") {
|
|
210
|
+
fail(
|
|
211
|
+
`invalid project name: ${name}\n` +
|
|
212
|
+
`wvk: must be a valid npm package name (lowercase, no spaces, may start with @scope/).`,
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
const templateRoot = resolve(TEMPLATES_ROOT, "next-app");
|
|
216
|
+
if (!existsSync(templateRoot)) {
|
|
217
|
+
fail(`scaffolding template missing in package at ${templateRoot}`);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const targetDir =
|
|
221
|
+
name === "." ? process.cwd() : resolve(process.cwd(), name);
|
|
222
|
+
const targetRel = relative(process.cwd(), targetDir) || ".";
|
|
223
|
+
const projectName = name === "." ? basename(targetDir) : name;
|
|
224
|
+
if (!NAME_RE.test(projectName)) {
|
|
225
|
+
fail(
|
|
226
|
+
`derived project name "${projectName}" is not a valid npm package name. ` +
|
|
227
|
+
`Pass an explicit name: wvk create-next-app <name>`,
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (existsSync(targetDir)) {
|
|
232
|
+
if (!statSync(targetDir).isDirectory()) {
|
|
233
|
+
fail(`${targetRel} exists and is not a directory`);
|
|
234
|
+
}
|
|
235
|
+
if (!isDirEmpty(targetDir) && !args.force) {
|
|
236
|
+
fail(
|
|
237
|
+
`${targetRel} is not empty. Re-run with --force to scaffold into it anyway ` +
|
|
238
|
+
`(existing files with the same name will be overwritten).`,
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
} else {
|
|
242
|
+
mkdirSync(targetDir, { recursive: true });
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Copy every file from the template tree. cpSync with recursive copies
|
|
246
|
+
// directories without descending into anything that doesn't exist.
|
|
247
|
+
cpSync(templateRoot, targetDir, { recursive: true, force: true });
|
|
248
|
+
|
|
249
|
+
// npm strips a literal `.gitignore` from packed tarballs (it's renamed to
|
|
250
|
+
// `.npmignore`). The template ships it as `gitignore`; rename on copy. If
|
|
251
|
+
// the caller already had a `.gitignore` (only possible with --force), keep
|
|
252
|
+
// theirs and drop ours so we don't leave an orphan `gitignore` lying around.
|
|
253
|
+
const giSrc = join(targetDir, "gitignore");
|
|
254
|
+
const giDst = join(targetDir, ".gitignore");
|
|
255
|
+
if (existsSync(giSrc)) {
|
|
256
|
+
if (existsSync(giDst)) {
|
|
257
|
+
unlinkSync(giSrc);
|
|
258
|
+
} else {
|
|
259
|
+
renameSync(giSrc, giDst);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Set the `name` field in the new app's package.json to the requested name.
|
|
264
|
+
const targetPkgPath = join(targetDir, "package.json");
|
|
265
|
+
if (existsSync(targetPkgPath)) {
|
|
266
|
+
const pkg = JSON.parse(readFileSync(targetPkgPath, "utf8"));
|
|
267
|
+
pkg.name = projectName;
|
|
268
|
+
writeFileSync(targetPkgPath, `${JSON.stringify(pkg, null, 2)}\n`);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Drop the package's AGENTS.md into the project so the consumer's agent
|
|
272
|
+
// harness picks up the wvk authoring rules. Sourced from PKG_ROOT (same
|
|
273
|
+
// file `wvk copy-harness` writes), not from the template, so the two
|
|
274
|
+
// never drift. Opt out with --no-agents.
|
|
275
|
+
if (!args.noAgents) {
|
|
276
|
+
if (!existsSync(SOURCE)) {
|
|
277
|
+
fail(`source AGENTS.md missing in package at ${SOURCE}`);
|
|
278
|
+
}
|
|
279
|
+
const agentsBody = readFileSync(SOURCE, "utf8");
|
|
280
|
+
writeFileSync(join(targetDir, "AGENTS.md"), agentsBody);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
process.stdout.write(`wvk: scaffolded ${PKG_NAME} Next.js app in ${targetRel}\n`);
|
|
284
|
+
if (!args.noAgents) {
|
|
285
|
+
process.stdout.write(
|
|
286
|
+
`wvk: wrote AGENTS.md so your agent harness picks up the wvk authoring rules\n`,
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
process.stdout.write(`wvk: next steps:\n`);
|
|
290
|
+
if (targetRel !== ".") {
|
|
291
|
+
process.stdout.write(` cd ${targetRel}\n`);
|
|
292
|
+
}
|
|
293
|
+
process.stdout.write(` npm install # or pnpm install\n`);
|
|
294
|
+
process.stdout.write(` npm run dev # then open http://localhost:3000\n`);
|
|
295
|
+
}
|
|
296
|
+
|
|
175
297
|
function main() {
|
|
176
298
|
const args = parseArgs(process.argv.slice(2));
|
|
177
299
|
if (args.help) {
|
|
@@ -191,6 +313,10 @@ function main() {
|
|
|
191
313
|
copyHarness(args);
|
|
192
314
|
return;
|
|
193
315
|
}
|
|
316
|
+
if (cmd === "create-next-app") {
|
|
317
|
+
createNextApp(args);
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
194
320
|
process.stderr.write(`wvk: unknown command: ${cmd}\n\n${usage()}`);
|
|
195
321
|
process.exit(1);
|
|
196
322
|
}
|
package/dist/index.cjs
CHANGED
|
@@ -7311,6 +7311,9 @@ var TextInput = React24.forwardRef(
|
|
|
7311
7311
|
"data-wvk-input": "shell",
|
|
7312
7312
|
style: { cursor: disabled ? "not-allowed" : "var(--wvk-cursor-text)" },
|
|
7313
7313
|
className: cn(textInputShellVariants({ inputStyle, size }), className),
|
|
7314
|
+
onClick: () => {
|
|
7315
|
+
if (!disabled) innerRef.current?.focus();
|
|
7316
|
+
},
|
|
7314
7317
|
children: [
|
|
7315
7318
|
leadingIcon ? /* @__PURE__ */ (0, import_jsx_runtime208.jsx)(
|
|
7316
7319
|
"span",
|