create-shallot 0.5.0 → 0.5.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.
Files changed (2) hide show
  1. package/index.ts +115 -118
  2. package/package.json +1 -1
package/index.ts CHANGED
@@ -1,27 +1,94 @@
1
1
  #!/usr/bin/env bun
2
2
 
3
3
  import { existsSync, mkdirSync, writeFileSync } from "fs";
4
- import { join, resolve } from "path";
5
-
6
- const name = process.argv[2];
7
- if (!name) {
8
- console.error("Usage: bun create shallot <project-name>");
9
- process.exit(1);
4
+ import { dirname, join, resolve } from "path";
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/starter in-repo.
10
+ */
11
+ shallot?: string;
10
12
  }
11
13
 
12
- const dir = resolve(name);
13
- if (existsSync(dir)) {
14
- console.error(`Directory "${name}" already exists`);
15
- process.exit(1);
14
+ /**
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`).
18
+ */
19
+ export function template(name: string, opts: TemplateOptions = {}): Record<string, string> {
20
+ const shallot = opts.shallot ?? "latest";
21
+ return {
22
+ "public/icon.svg": ICON,
23
+ ".gitignore": "node_modules/\ndist/\nbuild/\n",
24
+ "package.json":
25
+ JSON.stringify(
26
+ {
27
+ name,
28
+ version: "0.0.0",
29
+ private: true,
30
+ type: "module",
31
+ scripts: { dev: "vite", build: "vite build", preview: "vite preview" },
32
+ dependencies: { "@dylanebert/shallot": shallot },
33
+ devDependencies: { typescript: "^5.9.3", vite: "^8.0.0" },
34
+ },
35
+ null,
36
+ 2,
37
+ ) + "\n",
38
+ "tsconfig.json":
39
+ JSON.stringify(
40
+ {
41
+ compilerOptions: {
42
+ target: "ESNext",
43
+ module: "ESNext",
44
+ moduleResolution: "bundler",
45
+ lib: ["ESNext", "DOM", "DOM.Iterable"],
46
+ types: ["@webgpu/types", "vite/client"],
47
+ strict: true,
48
+ noEmit: true,
49
+ skipLibCheck: true,
50
+ },
51
+ include: ["src"],
52
+ },
53
+ null,
54
+ 2,
55
+ ) + "\n",
56
+ "index.html": `<!doctype html>
57
+ <html lang="en">
58
+ <head>
59
+ <meta charset="UTF-8" />
60
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
61
+ <link rel="icon" type="image/svg+xml" href="./icon.svg" />
62
+ <title>${name}</title>
63
+ <style>
64
+ * { box-sizing: border-box; margin: 0; padding: 0; }
65
+ body { background: #0c0a09; overflow: hidden; }
66
+ canvas { display: block; width: 100vw; height: 100vh; }
67
+ </style>
68
+ </head>
69
+ <body>
70
+ <canvas id="canvas"></canvas>
71
+ <script type="module" src="./src/main.ts"></script>
72
+ </body>
73
+ </html>
74
+ `,
75
+ "vite.config.ts": VITE,
76
+ "src/lib.ts": LIB,
77
+ "src/main.ts": MAIN,
78
+ "public/scenes/scene.scene": SCENE,
79
+ };
16
80
  }
17
81
 
18
- mkdirSync(dir, { recursive: true });
19
- mkdirSync(join(dir, "src"), { recursive: true });
20
- mkdirSync(join(dir, "public/scenes"), { recursive: true });
82
+ /** write a template file map under dir, creating parent directories as needed. */
83
+ export function scaffold(dir: string, files: Record<string, string>): void {
84
+ for (const [rel, content] of Object.entries(files)) {
85
+ const path = join(dir, rel);
86
+ mkdirSync(dirname(path), { recursive: true });
87
+ writeFileSync(path, content);
88
+ }
89
+ }
21
90
 
22
- writeFileSync(
23
- join(dir, "public/icon.svg"),
24
- `<svg id="Shallot" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 80 80">
91
+ const ICON = `<svg id="Shallot" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 80 80">
25
92
  <title>shallot icon</title>
26
93
  <defs>
27
94
  <radialGradient id="baseGradient" cx="35%" cy="30%" r="70%" fx="25%" fy="20%">
@@ -39,85 +106,9 @@ writeFileSync(
39
106
  <path d="M40,2 C44,10 66,28 66,46 C66,60 48,70 40,78 C32,70 14,60 14,46 C14,28 36,10 40,2 Z" fill="none" stroke="#6B4230" stroke-width="2"/>
40
107
  </g>
41
108
  </svg>
42
- `,
43
- );
44
-
45
- writeFileSync(
46
- join(dir, ".gitignore"),
47
- `node_modules/
48
- dist/
49
- build/
50
- `,
51
- );
52
-
53
- writeFileSync(
54
- join(dir, "package.json"),
55
- JSON.stringify(
56
- {
57
- name,
58
- version: "0.0.0",
59
- private: true,
60
- type: "module",
61
- scripts: {
62
- dev: "vite",
63
- build: "vite build",
64
- preview: "vite preview",
65
- },
66
- dependencies: { "@dylanebert/shallot": "latest" },
67
- devDependencies: { typescript: "^5.9.3", vite: "^8.0.0" },
68
- },
69
- null,
70
- 2,
71
- ) + "\n",
72
- );
73
-
74
- writeFileSync(
75
- join(dir, "tsconfig.json"),
76
- JSON.stringify(
77
- {
78
- compilerOptions: {
79
- target: "ESNext",
80
- module: "ESNext",
81
- moduleResolution: "bundler",
82
- lib: ["ESNext", "DOM", "DOM.Iterable"],
83
- types: ["@webgpu/types", "vite/client"],
84
- strict: true,
85
- noEmit: true,
86
- skipLibCheck: true,
87
- },
88
- include: ["src"],
89
- },
90
- null,
91
- 2,
92
- ) + "\n",
93
- );
94
-
95
- writeFileSync(
96
- join(dir, "index.html"),
97
- `<!doctype html>
98
- <html lang="en">
99
- <head>
100
- <meta charset="UTF-8" />
101
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
102
- <link rel="icon" type="image/svg+xml" href="./icon.svg" />
103
- <title>${name}</title>
104
- <style>
105
- * { box-sizing: border-box; margin: 0; padding: 0; }
106
- body { background: #0c0a09; overflow: hidden; }
107
- canvas { display: block; width: 100vw; height: 100vh; }
108
- </style>
109
- </head>
110
- <body>
111
- <canvas id="canvas"></canvas>
112
- <script type="module" src="./src/main.ts"></script>
113
- </body>
114
- </html>
115
- `,
116
- );
109
+ `;
117
110
 
118
- writeFileSync(
119
- join(dir, "vite.config.ts"),
120
- `import { defineConfig } from "vite";
111
+ const VITE = `import { defineConfig } from "vite";
121
112
 
122
113
  export default defineConfig({
123
114
  base: "./",
@@ -128,12 +119,9 @@ export default defineConfig({
128
119
  emptyOutDir: true,
129
120
  },
130
121
  });
131
- `,
132
- );
122
+ `;
133
123
 
134
- writeFileSync(
135
- join(dir, "src/lib.ts"),
136
- `import { type Config, Part, quat, type State, type System, Transform } from "@dylanebert/shallot";
124
+ const LIB = `import { type Config, Part, quat, type State, type System, Transform } from "@dylanebert/shallot";
137
125
 
138
126
  // A system is plain data: an \`update\` the engine runs each frame. This one spins
139
127
  // every Part around Y. It runs in the standalone app but not the editor — \`shallot .\`
@@ -159,12 +147,9 @@ export const config: Config = {
159
147
  plugins: [{ name: "spin", systems: [SpinSystem] }],
160
148
  scene: "scenes/scene.scene",
161
149
  };
162
- `,
163
- );
150
+ `;
164
151
 
165
- writeFileSync(
166
- join(dir, "src/main.ts"),
167
- `import { run } from "@dylanebert/shallot";
152
+ const MAIN = `import { run } from "@dylanebert/shallot";
168
153
  import { config } from "./lib";
169
154
 
170
155
  // One config source (src/lib.ts) drives both this standalone build and the editor. The camera
@@ -175,12 +160,9 @@ const { dispose } = await run(config);
175
160
  if (import.meta.hot) {
176
161
  import.meta.hot.dispose(() => dispose());
177
162
  }
178
- `,
179
- );
163
+ `;
180
164
 
181
- writeFileSync(
182
- join(dir, "public/scenes/scene.scene"),
183
- `<scene>
165
+ const SCENE = `<scene>
184
166
  <a ambient-light="color: 0xd0dcec; intensity: 0.5" />
185
167
  <a directional-light="direction: -0.4 -1 -0.55; color: 0xfff4e0; intensity: 1.1" />
186
168
 
@@ -190,12 +172,27 @@ writeFileSync(
190
172
  <!-- a Part is the engine's drop-in renderable: a mesh (default "cube") wearing a surface (default "default", lit) -->
191
173
  <a part transform="pos: 0 0 0" color="rgba: 0.85 0.55 0.35 1" />
192
174
  </scene>
193
- `,
194
- );
195
-
196
- console.log(`Created ${name}/`);
197
- console.log();
198
- console.log("Next steps:");
199
- console.log(` cd ${name}`);
200
- console.log(" bun install");
201
- console.log(" bun dev");
175
+ `;
176
+
177
+ if (import.meta.main) {
178
+ const name = process.argv[2];
179
+ if (!name) {
180
+ console.error("Usage: bun create shallot <project-name>");
181
+ process.exit(1);
182
+ }
183
+
184
+ const dir = resolve(name);
185
+ if (existsSync(dir)) {
186
+ console.error(`Directory "${name}" already exists`);
187
+ process.exit(1);
188
+ }
189
+
190
+ scaffold(dir, template(name));
191
+
192
+ console.log(`Created ${name}/`);
193
+ console.log();
194
+ console.log("Next steps:");
195
+ console.log(` cd ${name}`);
196
+ console.log(" bun install");
197
+ console.log(" bun dev");
198
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-shallot",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "type": "module",
5
5
  "bin": "./index.ts",
6
6
  "files": ["index.ts"],