create-shallot 0.6.0 → 0.8.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.
Files changed (2) hide show
  1. package/index.ts +68 -36
  2. package/package.json +1 -1
package/index.ts CHANGED
@@ -3,26 +3,16 @@
3
3
  import { existsSync, mkdirSync, writeFileSync } from "fs";
4
4
  import { dirname, join, resolve } from "path";
5
5
 
6
- export interface TemplateOptions {
7
- /**
8
- * dependency spec for `@dylanebert/shallot`. `"latest"` for the published `bun create shallot`
9
- * scaffold; `"workspace:*"` when `bun run build` regenerates examples/templates/* in-repo.
10
- */
11
- shallot?: string;
12
- }
13
-
14
6
  /**
15
- * the project files keyed by relative path, with the project name interpolated. single source of
16
- * truth for both `bun create shallot <name>` and the in-repo examples/templates/starter example
17
- * (regenerated by `bun run build`).
7
+ * the project files keyed by relative path, with the project name interpolated. the single source of
8
+ * truth for `bun create shallot <name>`.
18
9
  *
19
10
  * the project is pure data — a `shallot.json` manifest + plugin modules + `public/`, no vite
20
- * boilerplate. the CLI provides every harness over it: `shallot dev` runs it standalone (`bun dev`),
21
- * `shallot .` opens it in the editor, `shallot build` ships it (web + native targets).
11
+ * boilerplate. the CLI provides every harness over it: `shallot dev` runs it standalone with hot
12
+ * reload, `shallot build` ships it (web + native targets). the emitted CLAUDE.md / AGENTS.md point an
13
+ * agent at the installed engine's contract.
22
14
  */
23
- export function template(name: string, opts: TemplateOptions = {}): Record<string, string> {
24
- const shallot = opts.shallot ?? "latest";
25
-
15
+ export function template(name: string): Record<string, string> {
26
16
  return {
27
17
  "public/icon.svg": ICON,
28
18
  ".gitignore": "node_modules/\ndist/\nbuild/\n",
@@ -33,8 +23,8 @@ export function template(name: string, opts: TemplateOptions = {}): Record<strin
33
23
  version: "0.0.0",
34
24
  private: true,
35
25
  type: "module",
36
- dependencies: { "@dylanebert/shallot": shallot },
37
- devDependencies: { typescript: "^5.9.3" },
26
+ dependencies: { "@dylanebert/shallot": "latest" },
27
+ devDependencies: { typescript: "^7.0.2" },
38
28
  },
39
29
  null,
40
30
  2,
@@ -58,18 +48,15 @@ export function template(name: string, opts: TemplateOptions = {}): Record<strin
58
48
  2,
59
49
  ) + "\n",
60
50
  "shallot.json": MANIFEST,
51
+ "src/env.d.ts": ENV,
61
52
  "src/spin.ts": SPIN,
62
53
  "public/scenes/scene.scene": SCENE,
63
54
  "README.md": readme(name),
55
+ "AGENTS.md": agents(name),
56
+ "CLAUDE.md": agents(name),
64
57
  };
65
58
  }
66
59
 
67
- /**
68
- * the committed in-repo template under examples/templates/<dir>. the source of truth for what
69
- * `bun run build` regenerates and `bun check` gates for staleness.
70
- */
71
- export const TEMPLATES: { dir: string }[] = [{ dir: "starter" }];
72
-
73
60
  /** write a template file map under dir, creating parent directories as needed. */
74
61
  export function scaffold(dir: string, files: Record<string, string>): void {
75
62
  for (const [rel, content] of Object.entries(files)) {
@@ -111,8 +98,8 @@ bunx shallot dev
111
98
  \`\`\`
112
99
 
113
100
  \`bun install\` fetches the engine. \`bunx shallot dev\` runs the project with hot
114
- reload; \`bunx shallot\` opens it in the editor. Edit \`src/spin.ts\` (a plugin) and
115
- \`shallot.json\` (the manifest: scene + plugin enablement) in your IDE.
101
+ reload. Edit \`src/spin.ts\` (a plugin) and \`shallot.json\` (the manifest: scene +
102
+ plugin enablement) in your IDE.
116
103
 
117
104
  ## Ship
118
105
 
@@ -124,11 +111,52 @@ Builds a web bundle to \`dist/\`. For native targets:
124
111
  \`bunx shallot build --target windows|mac|linux\` (add \`--release\` for an optimized build).
125
112
  `;
126
113
 
127
- // The project manifest: the editor (\`shallot .\`) and a shipped build both read it. \`scene\`
128
- // is the scene to open; \`plugins\` is enablement — "Orbit": true turns on the orbit camera the
129
- // scene uses, and "Spin": "./src/spin" declares our own plugin by its module path. The default
130
- // plugins (render, lit surface) are on unless you set one false. Toggle these in the editor's
131
- // plugin menu and it rewrites this file, so the editor and a build always agree on what loads.
114
+ const agents = (name: string) => `# ${name}
115
+
116
+ A WebGPU game built on \`@dylanebert/shallot\`.
117
+
118
+ ## Layout
119
+
120
+ - \`shallot.json\` — the manifest: which scene to open + which plugins to enable
121
+ - \`public/scenes/*.scene\` — the world as declarative XML (each \`<a>\` is an entity, each attribute a component)
122
+ - \`src/*.ts\` — your plugins (a plugin is data: components + systems)
123
+
124
+ ## Build, run, verify
125
+
126
+ \`\`\`bash
127
+ bunx tsc --noEmit # typecheck — run after every change
128
+ bunx shallot dev # run with hot reload while you work
129
+ bunx shallot build [--target windows|mac|linux] # ship it (web bundle, or a native app)
130
+ bunx shallot verify # prove it: boot headless, check it renders, exit 0/nonzero
131
+ \`\`\`
132
+
133
+ \`shallot verify\` is the verification step — a self-terminating gate that boots the project in a real
134
+ headless browser, waits for a settled frame, and exits nonzero on failure. Nothing left running. \`--json\`
135
+ emits the full result; \`--screenshot <path>\` saves a frame. It drives Playwright, an optional one-time
136
+ install: \`bun add -d playwright && bunx playwright install chromium\` (exit code 3 names this command if
137
+ it's missing). By default it checks the scene rendered; to assert your own pass/fail (entity poses,
138
+ physics state) install \`window.__harness\` via \`installHarness\` from \`@dylanebert/shallot/harness\` — in a
139
+ manifest project like this one, from a plugin's \`initialize(state)\` hook (the engine's AGENTS.md has the
140
+ worked example).
141
+
142
+ ## Engine reference
143
+
144
+ The engine is the documentation. Read \`node_modules/@dylanebert/shallot/AGENTS.md\` for the full
145
+ contract (ECS, plugins, scenes, GPU, UI, and the \`shallot verify\` harness), and every public export
146
+ carries JSDoc. The examples index lives at \`node_modules/@dylanebert/shallot/examples/AGENTS.md\` — grep
147
+ it for the problem you have, then read that recipe's source, before writing a pattern from scratch.
148
+
149
+ ## Conventions
150
+
151
+ Data-oriented, ECS, declarative. Add components and systems, not methods — a \`Jump\` marker plus a
152
+ system, never \`player.jump()\`. Scenes declare; code transforms. One source of truth: every value has
153
+ one authoritative home; derive, don't duplicate.
154
+ `;
155
+
156
+ // The project manifest: `shallot dev` and a shipped `shallot build` both read it. `scene` is the scene
157
+ // to open; `plugins` is enablement — "Orbit": true turns on the orbit camera the scene uses, and
158
+ // "Spin": "./src/spin" declares our own plugin by its module path. The default plugins (render, lit
159
+ // surface) are on unless you set one false.
132
160
  const MANIFEST = `{
133
161
  "$schema": "./node_modules/@dylanebert/shallot/shallot.schema.json",
134
162
  "scene": "scenes/scene.scene",
@@ -139,12 +167,16 @@ const MANIFEST = `{
139
167
  }
140
168
  `;
141
169
 
170
+ // Ambient types + the tsconfig anchor. `include: ["src"]` needs at least one matching file, so this
171
+ // keeps `bunx tsc --noEmit` green (no TS18003) even when spin.ts is deleted for a static scene. It's
172
+ // infrastructure, not demo content — don't delete it.
173
+ const ENV = `/// <reference types="@webgpu/types" />
174
+ `;
175
+
142
176
  const SPIN = `import { type Plugin, type State, type System, Part, quat, Transform } from "@dylanebert/shallot";
143
177
 
144
- // A plugin is plain data: components + systems the engine runs. This system spins every Part
145
- // around Y. It runs when the project plays (the editor's preview, or a shipped build) but not
146
- // while you edit — the editor (\`bunx shallot\`) builds in edit mode, where unannotated systems
147
- // are skipped, so the cube stays put and editable there. Delete this file (and its
178
+ // A plugin is plain data: components + systems the engine runs. This system spins every Part around Y.
179
+ // It runs in the "simulation" group, which plays when the project runs. Delete this file (and its
148
180
  // \`shallot.json\` entry) for a static scene.
149
181
  const SpinSystem: System = {
150
182
  group: "simulation",
@@ -159,7 +191,7 @@ const SpinSystem: System = {
159
191
  };
160
192
 
161
193
  // The default export is the plugin — \`shallot.json\` references this file by path and imports
162
- // its default. The name ("Spin") is how the plugin menu lists it.
194
+ // its default. The name ("Spin") is how the manifest lists it.
163
195
  const SpinPlugin: Plugin = { name: "Spin", systems: [SpinSystem] };
164
196
  export default SpinPlugin;
165
197
  `;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-shallot",
3
- "version": "0.6.0",
3
+ "version": "0.8.0",
4
4
  "type": "module",
5
5
  "bin": "./index.ts",
6
6
  "files": ["index.ts"],