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.
- package/index.ts +115 -118
- 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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
197
|
-
|
|
198
|
-
console.
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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
|
+
}
|