create-gesso-app 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/CHANGELOG.md ADDED
@@ -0,0 +1,28 @@
1
+ # create-gesso-app
2
+
3
+ ## 0.1.0
4
+
5
+ First public release.
6
+
7
+ ```bash
8
+ npm create gesso-app my-app
9
+ ```
10
+
11
+ Scaffolds a Gesso application: a Vite project whose interface is built,
12
+ laid out and painted in a render worker, with the main thread holding
13
+ the canvas and forwarding input.
14
+
15
+ Two templates. `web` is a browser project with the render worker, the
16
+ Vite plugin and the development error overlay wired up. `electrobun` is
17
+ a native window with its state in the main process, where every window
18
+ is a replica of the same channels.
19
+
20
+ `--local` packs the packages out of a Gesso checkout instead of
21
+ installing them from the registry, which is how to scaffold a project
22
+ against changes that are not released yet. It is also how
23
+ `pnpm check:scaffold` runs, so that gate tests the working tree rather
24
+ than the last release.
25
+
26
+ The CLI is compiled rather than shipped as TypeScript, because `node`
27
+ only strips types on 24 and later and this is the first thing a
28
+ stranger runs.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kevin Baker
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,117 @@
1
+ # create-gesso-app
2
+
3
+ Scaffolds a Gesso application: a Vite project whose interface is built,
4
+ laid out and painted in a render worker.
5
+
6
+ ```bash
7
+ npm create gesso-app my-app
8
+ npm create gesso-app my-app -- --template electrobun # a native window
9
+ ```
10
+
11
+ The packages come from the registry. `--local` packs them out of a
12
+ Gesso checkout instead, for scaffolding against changes that are not
13
+ released yet; that is the mode `pnpm check:scaffold` uses, so the gate
14
+ tests the working tree and not the last release.
15
+
16
+ ## What the `web` template writes
17
+
18
+ ```text
19
+ index.html a host element with a size, and nothing else
20
+ tsconfig.json bundler resolution, and the two lines that buy JSX
21
+ vite.config.ts empty, because Gesso needs no plugin
22
+ src/main.ts the main thread: name the worker, mount into #app
23
+ src/worker.ts the render worker: name the root component
24
+ src/App.tsx the screen
25
+ ```
26
+
27
+ ## What the `electrobun` template writes
28
+
29
+ ```text
30
+ electrobun.config.ts the app, and where the built assets go in the bundle
31
+ hutch.config.ts what `hutch run <script>` does, and which Electrobun
32
+ vite.config.ts the window's assets, with the projected SDK aliased
33
+ tsconfig.json extends the devkit's, plus the two lines that buy JSX
34
+ src/shared/Counter.ts the channel both processes import
35
+ src/shared/rpc.ts the one message a window and the main process exchange
36
+ src/main/index.ts the main process: the state, and the windows
37
+ src/view/main.ts a window's main thread: build the bridge, mount
38
+ src/view/render.worker.ts the render worker, and the channels it attaches
39
+ src/render/App.tsx the screen
40
+ ```
41
+
42
+ Each template's `package.json` names version ranges, and an ordinary
43
+ install fetches them.
44
+
45
+ ## What `--local` does, and why it exists
46
+
47
+ `--local` packs the packages a template needs out of a Gesso checkout
48
+ with `pnpm pack`, writes the tarballs into the new project's `vendor/`
49
+ and points `dependencies` and `overrides` at them with `file:`
50
+ specifiers. It only works from inside the workspace, and it says so
51
+ rather than failing obscurely.
52
+
53
+ It exists so a scaffold can be made against changes that are not
54
+ released. `pnpm check:scaffold` runs this way for exactly that reason:
55
+ a gate that installed the last release would pass no matter what the
56
+ working tree did.
57
+
58
+ `pnpm pack` and not a workspace link, because packing is the only thing
59
+ that applies each package's `publishConfig`, which rewrites `exports`
60
+ from `src/*.ts` to `dist`. A link never goes through it, so a project
61
+ that linked would be testing a resolution no consumer ever gets. That
62
+ is the same route `scripts/check-install.ts` takes and for the same
63
+ reason.
64
+
65
+ A `web` project installs with **npm**. npm satisfies the packages'
66
+ declarations of each other from the tarballs already in its tree; pnpm 11
67
+ resolves them independently and asks a registry that has never heard of
68
+ them. An `electrobun` project installs with **`hutch install`**, because
69
+ Hutch owns that project's `node_modules` and projects the Electrobun
70
+ devkit into it, and two package managers cannot own one `node_modules`.
71
+ `overrides` is written for both, and is what keeps either installer from
72
+ going to a registry for a package it already has as a tarball.
73
+
74
+ ## The Electrobun template, and what is different about it
75
+
76
+ The application is the same shape: the same render worker, the same
77
+ components, the same channels. Three things around it are not.
78
+
79
+ **It is set up by Hutch, not by npm.** Electrobun 2.x is a toolchain a
80
+ launcher downloads, and `hutch electrobun prepare` projects the SDK into
81
+ the project's own `.hutch/devkit`. There is no `electrobun` dependency
82
+ in the generated `package.json` and adding one would not help: the npm
83
+ package of that name is a bootstrap for the launcher. Both
84
+ `vite.config.ts` and `tsconfig.json` read the SDK out of `.hutch/devkit`,
85
+ so neither can be loaded before the first prepare, which is why every
86
+ script in `hutch.config.ts` runs prepare first. The generated project's
87
+ README says how to get `hutch` if it is not on the path.
88
+
89
+ **The application layer is a process rather than a worker.** The state
90
+ lives in the main process and every window replicates it, so the
91
+ template ships a channel, a two-line RPC schema, and a main process that
92
+ serves one and opens windows over the other.
93
+
94
+ **The main process is not pre-bundled, and this is the one judgement
95
+ call in the template.** An application that imports the framework
96
+ out of this workspace has to bundle its main process with esbuild before
97
+ Electrobun sees it, because a bundler cannot resolve a package that was
98
+ never installed. A scaffolded project
99
+ installs them, so `electrobun.config.ts` points Cottontail at
100
+ `src/main/index.ts` and lets Electrobun bundle it, which is the ordinary
101
+ arrangement. The generated README records the escape hatch in case a
102
+ future toolchain disagrees.
103
+
104
+ ## The checks, and what is not checked
105
+
106
+ `pnpm check:scaffold` runs the CLI into a temporary directory, installs,
107
+ typechecks and builds the result, starts the dev server the CLI told the
108
+ person to start, and drives it in headless Chrome until the counter
109
+ counts. **It covers the `web` template only.** Run it after changing
110
+ anything under `templates/web/`.
111
+
112
+ Nothing in this repository can open a native window, and standing a Hutch
113
+ project up in a gate would mean downloading a native toolchain to check
114
+ an import, so the `electrobun` template has no automated gate at all. It
115
+ is checked by scaffolding it and running it on a machine with the
116
+ toolchain, which is how every other Electrobun claim in this repository
117
+ was checked.
@@ -0,0 +1,400 @@
1
+ #!/usr/bin/env node
2
+ import { execFileSync } from "node:child_process";
3
+ import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
4
+ import { basename, isAbsolute, join, resolve } from "node:path";
5
+ //#region bin/create-gesso-app.ts
6
+ /**
7
+ * `create-gesso-app`: scaffolds a Gesso application (the
8
+ * last item).
9
+ *
10
+ * What it writes is the configuration this framework is for. The
11
+ * application runs in a render worker; the page's own script creates
12
+ * the app, hands it a worker constructor and mounts it, and that is the
13
+ * whole of the main thread's job.
14
+ *
15
+ * The one thing this CLI does that a public scaffolder would not is
16
+ * vendor its own dependencies. No `gesso-*` package is on a registry,
17
+ * so a generated `package.json` that named a version would produce a
18
+ * project that cannot install. Instead the CLI packs the packages a
19
+ * template needs out of this workspace with `pnpm pack`, drops the
20
+ * tarballs into the new project's `vendor/` directory and writes
21
+ * `file:` specifiers at them. That is the same route
22
+ * `scripts/check-install.ts` takes, and it is the route that exercises
23
+ * each package's `publishConfig`, which is the only thing that rewrites
24
+ * `exports` from `src/*.ts` to `dist`. This ends the day the packages
25
+ * are published to a registry.
26
+ *
27
+ * node packages/create-gesso-app/bin/create-gesso-app.ts ../my-app
28
+ * node packages/create-gesso-app/bin/create-gesso-app.ts ../my-app --no-build
29
+ * node packages/create-gesso-app/bin/create-gesso-app.ts ../my-app --template electrobun
30
+ *
31
+ * The two templates differ in what runs beneath the same application.
32
+ * `web` is a Vite project a browser loads. `electrobun` is a native
33
+ * window whose application layer is a separate process, and it is set
34
+ * up by Hutch rather than by npm: the Electrobun SDK is projected into
35
+ * a project by `hutch electrobun prepare` rather than installed from a
36
+ * registry, so the generated project depends on the toolchain being
37
+ * present and says so.
38
+ */
39
+ /** Where the packed tarballs land inside the generated project. */
40
+ const VENDOR_DIR = "vendor";
41
+ /** Template files whose names cannot be checked into this repository as-is. */
42
+ const RENAMED = /* @__PURE__ */ new Map([["_gitignore", ".gitignore"]]);
43
+ /**
44
+ * The templates, and the two things that differ between them: what a
45
+ * project depends on, and how a person starts it.
46
+ *
47
+ * `devtools` and `vite-plugin` joined the web template's first three
48
+ * when the feedback loop was wired in by default:
49
+ * the plugin is what writes the worker construction and the
50
+ * hot-replacement wiring, and it loads the overlay from devtools the
51
+ * first time the worker throws. Both are development dependencies of
52
+ * the generated project and neither is reachable from a production
53
+ * build.
54
+ */
55
+ const TEMPLATES = {
56
+ web: {
57
+ vendored: [
58
+ "core",
59
+ "framework",
60
+ "components",
61
+ "devtools",
62
+ "vite-plugin"
63
+ ],
64
+ substitute: ["index.html", "README.md"],
65
+ next: (name, where, local) => [
66
+ `\nCreated ${name} in ${where}.\n`,
67
+ ` cd ${where}`,
68
+ " pnpm install # or npm install",
69
+ " pnpm dev\n",
70
+ ...local ? [
71
+ "Gesso came from this workspace rather than the registry, packed into",
72
+ "vendor/. The packed packages ask each other for version ranges, so npm",
73
+ "is pointed at the tarballs by `overrides` in package.json and pnpm by",
74
+ "`overrides` in pnpm-workspace.yaml. The project's README says how to",
75
+ "move to the published packages.\n"
76
+ ] : []
77
+ ].join("\n")
78
+ },
79
+ electrobun: {
80
+ vendored: [
81
+ "core",
82
+ "framework",
83
+ "components",
84
+ "electrobun"
85
+ ],
86
+ substitute: [
87
+ "electrobun.config.ts",
88
+ "src/main/index.ts",
89
+ "src/view/index.html",
90
+ "README.md"
91
+ ],
92
+ next: (name, where) => `
93
+ Created ${name} in ${where}.
94
+
95
+ cd ${where}
96
+ hutch install
97
+ hutch run dev
98
+
99
+ Hutch rather than npm, and this is the part to read before running it.
100
+ Electrobun 2.x is a toolchain a launcher downloads, not a package a
101
+ registry serves: \`hutch electrobun prepare\` projects the SDK into the
102
+ project's own .hutch/devkit, which is where vite.config.ts and
103
+ tsconfig.json look for it, and every script in hutch.config.ts runs that
104
+ first. \`hutch install\` runs npm underneath. If \`hutch\` is not on your
105
+ path yet, the project's README says how to get it.
106
+
107
+ \`pnpm check:scaffold:electrobun\` installs, typechecks and builds a
108
+ project like this one without opening it. The window itself is checked
109
+ by running it; the README records the last time that was done.
110
+ `
111
+ }
112
+ };
113
+ const packageRoot = join(import.meta.dirname, "..");
114
+ const workspaceRoot = join(packageRoot, "..", "..");
115
+ const USAGE = `Usage: create-gesso-app <directory> [options]
116
+
117
+ Creates a Gesso application in <directory>: a project whose interface is
118
+ built, laid out and painted in a render worker.
119
+
120
+ Options:
121
+ --name <name> Package name for the new project. Defaults to the
122
+ directory's own name.
123
+ --template <name> "web" for a Vite project a browser loads, or
124
+ "electrobun" for a native window with its state in a
125
+ main process. Defaults to "web".
126
+ --force Write into a directory that already has files in it.
127
+ --local Install the Gesso packages from this workspace, packed
128
+ into the project, instead of from the registry. Only
129
+ works inside a Gesso checkout.
130
+ --no-build With --local, pack without rebuilding first. Only safe
131
+ when dist/ is already current.
132
+ -h, --help Print this.
133
+ `;
134
+ function fail(message) {
135
+ console.error(message);
136
+ process.exit(1);
137
+ }
138
+ function parseArgs(argv) {
139
+ let target;
140
+ let name;
141
+ let template = "web";
142
+ let force = false;
143
+ let build = true;
144
+ let local = false;
145
+ for (let i = 0; i < argv.length; i++) {
146
+ const arg = argv[i];
147
+ switch (arg) {
148
+ case "-h":
149
+ case "--help":
150
+ console.log(USAGE);
151
+ process.exit(0);
152
+ break;
153
+ case "--force":
154
+ force = true;
155
+ break;
156
+ case "--local":
157
+ local = true;
158
+ break;
159
+ case "--no-build":
160
+ build = false;
161
+ break;
162
+ case "--name":
163
+ name = argv[++i];
164
+ break;
165
+ case "--template":
166
+ template = argv[++i] ?? "";
167
+ break;
168
+ default:
169
+ if (arg.startsWith("-")) fail(`Unknown option ${arg}.\n\n${USAGE}`);
170
+ if (target !== void 0) fail(`Two directories were given, ${target} and ${arg}.\n\n${USAGE}`);
171
+ target = arg;
172
+ }
173
+ }
174
+ if (target === void 0) fail(`A directory to create is required.\n\n${USAGE}`);
175
+ if (name !== void 0 && !/^[a-z0-9][a-z0-9._-]*$/.test(name)) fail(`"${name}" is not a usable package name: use lowercase letters, digits, dots, dashes and underscores.`);
176
+ const absolute = isAbsolute(target) ? target : resolve(process.cwd(), target);
177
+ return {
178
+ target: absolute,
179
+ name: name ?? basename(absolute),
180
+ template,
181
+ force,
182
+ build,
183
+ local
184
+ };
185
+ }
186
+ /** Looks a template up, and lists the ones that exist when it is not one. */
187
+ function templateOf(name) {
188
+ const template = TEMPLATES[name];
189
+ if (template === void 0 || !existsSync(join(packageRoot, "templates", name))) fail(`There is no "${name}" template. Available: ${Object.keys(TEMPLATES).join(", ")}.`);
190
+ return template;
191
+ }
192
+ function prepareTarget(options) {
193
+ if (!existsSync(options.target)) {
194
+ mkdirSync(options.target, { recursive: true });
195
+ return;
196
+ }
197
+ const existing = readdirSync(options.target);
198
+ if (existing.length > 0 && !options.force) fail(`${options.target} is not empty (${existing.length} entries). Pass --force to write into it anyway.`);
199
+ }
200
+ /**
201
+ * Whether a package's `dist` is older than anything in its `src`.
202
+ *
203
+ * A newest-mtime comparison rather than a build system: it is wrong
204
+ * only in the direction of building something that did not need it,
205
+ * and a missing `dist` always builds. `--no-build` skips the question
206
+ * entirely for the case where the caller knows.
207
+ */
208
+ function needsBuild(pkg) {
209
+ const dir = join(workspaceRoot, "packages", pkg);
210
+ const dist = join(dir, "dist");
211
+ if (!existsSync(dist)) return true;
212
+ return newestChange(join(dir, "src")) > newestChange(dist);
213
+ }
214
+ /** The most recent modification time anywhere under a directory. */
215
+ function newestChange(dir) {
216
+ let newest = 0;
217
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
218
+ const path = join(dir, entry.name);
219
+ newest = Math.max(newest, entry.isDirectory() ? newestChange(path) : statSync(path).mtimeMs);
220
+ }
221
+ return newest;
222
+ }
223
+ /**
224
+ * Packs the workspace packages into the new project and returns the
225
+ * `file:` specifier for each.
226
+ *
227
+ * The specifiers are relative to the project directory, so the whole
228
+ * directory can be moved or copied and still install.
229
+ */
230
+ function vendorPackages(options, template) {
231
+ if (options.build) {
232
+ const stale = template.vendored.filter(needsBuild);
233
+ if (stale.length > 0) {
234
+ console.log(`building ${stale.join(", ")}…`);
235
+ const filters = stale.flatMap((pkg) => ["--filter", `./packages/${pkg}`]);
236
+ execFileSync("pnpm", [...filters, "build"], {
237
+ cwd: workspaceRoot,
238
+ stdio: "ignore"
239
+ });
240
+ }
241
+ }
242
+ const into = join(options.target, VENDOR_DIR);
243
+ rmSync(into, {
244
+ recursive: true,
245
+ force: true
246
+ });
247
+ mkdirSync(into, { recursive: true });
248
+ console.log("packing them into the new project…");
249
+ const specifiers = /* @__PURE__ */ new Map();
250
+ for (const pkg of template.vendored) {
251
+ const before = new Set(readdirSync(into));
252
+ execFileSync("pnpm", [
253
+ "pack",
254
+ "--pack-destination",
255
+ into
256
+ ], {
257
+ cwd: join(workspaceRoot, "packages", pkg),
258
+ stdio: "ignore"
259
+ });
260
+ const created = readdirSync(into).filter((entry) => !before.has(entry) && entry.endsWith(".tgz"));
261
+ if (created.length !== 1) fail(`pnpm pack in packages/${pkg} produced ${created.length} tarballs, expected 1.`);
262
+ specifiers.set(`gesso-${pkg}`, `file:${VENDOR_DIR}/${created[0]}`);
263
+ }
264
+ return specifiers;
265
+ }
266
+ /** Copies the template, renaming the files that had to be disguised. */
267
+ function copyTemplate(from, options) {
268
+ cpSync(from, options.target, {
269
+ recursive: true,
270
+ force: true
271
+ });
272
+ for (const [disguised, real] of RENAMED) {
273
+ const path = join(options.target, disguised);
274
+ if (existsSync(path)) renameSync(path, join(options.target, real));
275
+ }
276
+ }
277
+ /**
278
+ * Writes the project's name and its dependency specifiers.
279
+ *
280
+ * `overrides` carries the same specifiers, because the packages declare
281
+ * each other by version range: `gesso-framework` and
282
+ * `gesso-components` do, and so does `gesso-electrobun`. Without it an
283
+ * installer is free to go looking for `gesso-core@^0.1.0` on a registry
284
+ * that has never heard of it, and whether it does so depends on what
285
+ * else is in the tree.
286
+ */
287
+ function writeManifest(options, specifiers) {
288
+ const path = join(options.target, "package.json");
289
+ const manifest = JSON.parse(readFileSync(path, "utf8"));
290
+ manifest.name = options.name;
291
+ for (const [pkg, specifier] of specifiers) {
292
+ const where = manifest.dependencies[pkg] !== void 0 ? manifest.dependencies : manifest.devDependencies?.[pkg] !== void 0 ? manifest.devDependencies : void 0;
293
+ if (where === void 0) fail(`The ${options.template} template does not depend on ${pkg}, so there is nowhere to vendor it.`);
294
+ where[pkg] = specifier;
295
+ }
296
+ if (specifiers.size > 0) manifest.overrides = Object.fromEntries(specifiers);
297
+ else delete manifest.overrides;
298
+ writeFileSync(path, `${JSON.stringify(manifest, null, 2)}\n`);
299
+ }
300
+ /**
301
+ * Says the same thing to pnpm that `overrides` says to npm.
302
+ *
303
+ * npm was chosen because pnpm did not work, and the reason is still
304
+ * exactly right: `pnpm pack` rewrites `workspace:^`
305
+ * into `^0.1.0`, so the packed `gesso-framework` asks for
306
+ * `gesso-core@^0.1.0` and pnpm 11 goes to a registry that has never
307
+ * heard of it. What that record then rejected was shipping a
308
+ * `pnpm-workspace.yaml` in a project that is not a workspace.
309
+ *
310
+ * That trade has moved. The file is five lines, it is the only place
311
+ * pnpm 11 reads `overrides` from, and the alternative is a scaffold
312
+ * that fails for the package manager this repository itself uses. It
313
+ * says what it is for and it is deleted along with `vendor/` the day
314
+ * the packages are published.
315
+ */
316
+ function writePnpmOverrides(options, specifiers) {
317
+ const lines = [
318
+ "# The tarballs in vendor/ again, for pnpm.",
319
+ "#",
320
+ "# Gesso is not published, so the packed gesso-* packages ask each",
321
+ "# other for version ranges no registry can answer. npm reads the",
322
+ "# `overrides` in package.json; pnpm 11 reads only this file. Both",
323
+ "# go away when the packages are on a registry: delete vendor/, this",
324
+ "# file and `overrides`, and put version ranges back.",
325
+ "overrides:",
326
+ ...[...specifiers].map(([pkg, specifier]) => ` '${pkg}': '${specifier}'`),
327
+ ""
328
+ ];
329
+ writeFileSync(join(options.target, "pnpm-workspace.yaml"), lines.join("\n"));
330
+ }
331
+ /**
332
+ * Substitutes the template's placeholders in the files that carry them.
333
+ *
334
+ * The list is the template's rather than a walk of the tree, so that a
335
+ * placeholder added to a file nobody listed fails visibly in the
336
+ * generated project instead of being quietly left as `{{name}}`
337
+ * somewhere it is never read.
338
+ */
339
+ function substitute(options, template) {
340
+ for (const relative of template.substitute) {
341
+ const path = join(options.target, relative);
342
+ if (!existsSync(path)) fail(`The ${options.template} template says ${relative} carries {{name}}, and it was not written.`);
343
+ writeFileSync(path, readFileSync(path, "utf8").replaceAll("{{name}}", options.name));
344
+ }
345
+ }
346
+ /**
347
+ * Appends the `vendor/` explanation to the generated README.
348
+ *
349
+ * It lives here rather than in the template because a project made
350
+ * without `--local` has no `vendor/`, and a README explaining a
351
+ * directory that is not there is worse than one that says nothing.
352
+ */
353
+ function explainVendoring(options) {
354
+ const path = join(options.target, "README.md");
355
+ if (!existsSync(path)) return;
356
+ const section = [
357
+ "",
358
+ "## Why `vendor/` exists, and how to remove it",
359
+ "",
360
+ "This project was scaffolded with `--local`, so it installs Gesso from",
361
+ "a checkout rather than from the registry: the packages were packed",
362
+ "into `vendor/` and the manifest points at the tarballs.",
363
+ "",
364
+ "The packed packages declare each other by version range, so without",
365
+ "help a package manager is free to go looking for `gesso-core@^0.1.0`",
366
+ "on the registry and get a different copy than the one beside it.",
367
+ "`overrides` in `package.json` is what tells npm; `overrides` in",
368
+ "`pnpm-workspace.yaml` is what tells pnpm, which reads it nowhere else.",
369
+ "",
370
+ "To pick up a further change, run `create-gesso-app --local` over this",
371
+ "directory again with `--force`.",
372
+ "",
373
+ "To move to the published packages: delete `vendor/`, delete",
374
+ "`pnpm-workspace.yaml`, delete `overrides`, and put version ranges back",
375
+ "in `dependencies`.",
376
+ ""
377
+ ].join("\n");
378
+ writeFileSync(path, readFileSync(path, "utf8").trimEnd() + "\n" + section);
379
+ }
380
+ function main() {
381
+ const options = parseArgs(process.argv.slice(2));
382
+ if (options.local && !existsSync(join(workspaceRoot, "packages", "core", "package.json"))) fail("--local packs the packages out of the Gesso workspace, and this is not one.\nRun it from inside a checkout, or drop --local to install from the registry.");
383
+ const template = templateOf(options.template);
384
+ prepareTarget(options);
385
+ copyTemplate(join(packageRoot, "templates", options.template), options);
386
+ const specifiers = options.local ? vendorPackages(options, template) : /* @__PURE__ */ new Map();
387
+ writeManifest(options, specifiers);
388
+ if (options.local) {
389
+ writePnpmOverrides(options, specifiers);
390
+ explainVendoring(options);
391
+ }
392
+ substitute(options, template);
393
+ const where = options.target.startsWith(process.cwd()) ? options.target.slice(process.cwd().length + 1) : options.target;
394
+ console.log(template.next(options.name, where, options.local));
395
+ }
396
+ main();
397
+ //#endregion
398
+ export {};
399
+
400
+ //# sourceMappingURL=create-gesso-app.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-gesso-app.mjs","names":[],"sources":["../bin/create-gesso-app.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * `create-gesso-app`: scaffolds a Gesso application (the\n * last item).\n *\n * What it writes is the configuration this framework is for. The\n * application runs in a render worker; the page's own script creates\n * the app, hands it a worker constructor and mounts it, and that is the\n * whole of the main thread's job.\n *\n * The one thing this CLI does that a public scaffolder would not is\n * vendor its own dependencies. No `gesso-*` package is on a registry,\n * so a generated `package.json` that named a version would produce a\n * project that cannot install. Instead the CLI packs the packages a\n * template needs out of this workspace with `pnpm pack`, drops the\n * tarballs into the new project's `vendor/` directory and writes\n * `file:` specifiers at them. That is the same route\n * `scripts/check-install.ts` takes, and it is the route that exercises\n * each package's `publishConfig`, which is the only thing that rewrites\n * `exports` from `src/*.ts` to `dist`. This ends the day the packages\n * are published to a registry.\n *\n * node packages/create-gesso-app/bin/create-gesso-app.ts ../my-app\n * node packages/create-gesso-app/bin/create-gesso-app.ts ../my-app --no-build\n * node packages/create-gesso-app/bin/create-gesso-app.ts ../my-app --template electrobun\n *\n * The two templates differ in what runs beneath the same application.\n * `web` is a Vite project a browser loads. `electrobun` is a native\n * window whose application layer is a separate process, and it is set\n * up by Hutch rather than by npm: the Electrobun SDK is projected into\n * a project by `hutch electrobun prepare` rather than installed from a\n * registry, so the generated project depends on the toolchain being\n * present and says so.\n */\nimport { execFileSync } from 'node:child_process';\nimport {\n cpSync,\n existsSync,\n mkdirSync,\n readFileSync,\n readdirSync,\n renameSync,\n rmSync,\n statSync,\n writeFileSync\n} from 'node:fs';\nimport { basename, isAbsolute, join, resolve } from 'node:path';\n\n/** Where the packed tarballs land inside the generated project. */\nconst VENDOR_DIR = 'vendor';\n/** Template files whose names cannot be checked into this repository as-is. */\nconst RENAMED = new Map([['_gitignore', '.gitignore']]);\n\ninterface Template {\n /** Workspace packages to pack into the project, in dependency order. */\n readonly vendored: readonly string[];\n /** Files carrying `{{name}}`, relative to the project directory. */\n readonly substitute: readonly string[];\n /** What to tell the person once the project is written. */\n readonly next: (name: string, where: string, local: boolean) => string;\n}\n\n/**\n * The templates, and the two things that differ between them: what a\n * project depends on, and how a person starts it.\n *\n * `devtools` and `vite-plugin` joined the web template's first three\n * when the feedback loop was wired in by default:\n * the plugin is what writes the worker construction and the\n * hot-replacement wiring, and it loads the overlay from devtools the\n * first time the worker throws. Both are development dependencies of\n * the generated project and neither is reachable from a production\n * build.\n */\nconst TEMPLATES: Record<string, Template> = {\n web: {\n vendored: ['core', 'framework', 'components', 'devtools', 'vite-plugin'],\n substitute: ['index.html', 'README.md'],\n next: (name, where, local) =>\n [\n `\\nCreated ${name} in ${where}.\\n`,\n ` cd ${where}`,\n ' pnpm install # or npm install',\n ' pnpm dev\\n',\n ...(local\n ? [\n 'Gesso came from this workspace rather than the registry, packed into',\n 'vendor/. The packed packages ask each other for version ranges, so npm',\n 'is pointed at the tarballs by `overrides` in package.json and pnpm by',\n \"`overrides` in pnpm-workspace.yaml. The project's README says how to\",\n 'move to the published packages.\\n'\n ]\n : [])\n ].join('\\n')\n },\n electrobun: {\n vendored: ['core', 'framework', 'components', 'electrobun'],\n substitute: ['electrobun.config.ts', 'src/main/index.ts', 'src/view/index.html', 'README.md'],\n next: (name, where) => `\nCreated ${name} in ${where}.\n\n cd ${where}\n hutch install\n hutch run dev\n\nHutch rather than npm, and this is the part to read before running it.\nElectrobun 2.x is a toolchain a launcher downloads, not a package a\nregistry serves: \\`hutch electrobun prepare\\` projects the SDK into the\nproject's own .hutch/devkit, which is where vite.config.ts and\ntsconfig.json look for it, and every script in hutch.config.ts runs that\nfirst. \\`hutch install\\` runs npm underneath. If \\`hutch\\` is not on your\npath yet, the project's README says how to get it.\n\n\\`pnpm check:scaffold:electrobun\\` installs, typechecks and builds a\nproject like this one without opening it. The window itself is checked\nby running it; the README records the last time that was done.\n`\n }\n};\n\nconst packageRoot = join(import.meta.dirname, '..');\nconst workspaceRoot = join(packageRoot, '..', '..');\n\ninterface Options {\n readonly target: string;\n readonly name: string;\n readonly template: string;\n readonly force: boolean;\n readonly build: boolean;\n /**\n * Install the packages out of this workspace instead of the registry.\n *\n * What every scaffold did before the packages were published, kept\n * because it is the only way to scaffold a project against changes\n * that are not released yet. `pnpm check:scaffold` runs this way, so\n * the gate tests the working tree rather than the last release.\n */\n readonly local: boolean;\n}\n\nconst USAGE = `Usage: create-gesso-app <directory> [options]\n\nCreates a Gesso application in <directory>: a project whose interface is\nbuilt, laid out and painted in a render worker.\n\nOptions:\n --name <name> Package name for the new project. Defaults to the\n directory's own name.\n --template <name> \"web\" for a Vite project a browser loads, or\n \"electrobun\" for a native window with its state in a\n main process. Defaults to \"web\".\n --force Write into a directory that already has files in it.\n --local Install the Gesso packages from this workspace, packed\n into the project, instead of from the registry. Only\n works inside a Gesso checkout.\n --no-build With --local, pack without rebuilding first. Only safe\n when dist/ is already current.\n -h, --help Print this.\n`;\n\nfunction fail(message: string): never {\n console.error(message);\n process.exit(1);\n}\n\nfunction parseArgs(argv: readonly string[]): Options {\n let target: string | undefined;\n let name: string | undefined;\n let template = 'web';\n let force = false;\n let build = true;\n let local = false;\n\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i];\n switch (arg) {\n case '-h':\n case '--help':\n console.log(USAGE);\n process.exit(0);\n break;\n case '--force':\n force = true;\n break;\n case '--local':\n local = true;\n break;\n case '--no-build':\n build = false;\n break;\n case '--name':\n name = argv[++i];\n break;\n case '--template':\n template = argv[++i] ?? '';\n break;\n default:\n if (arg.startsWith('-')) {\n fail(`Unknown option ${arg}.\\n\\n${USAGE}`);\n }\n if (target !== undefined) {\n fail(`Two directories were given, ${target} and ${arg}.\\n\\n${USAGE}`);\n }\n target = arg;\n }\n }\n\n if (target === undefined) {\n fail(`A directory to create is required.\\n\\n${USAGE}`);\n }\n if (name !== undefined && !/^[a-z0-9][a-z0-9._-]*$/.test(name)) {\n fail(`\"${name}\" is not a usable package name: use lowercase letters, digits, dots, dashes and underscores.`);\n }\n\n const absolute = isAbsolute(target) ? target : resolve(process.cwd(), target);\n return { target: absolute, name: name ?? basename(absolute), template, force, build, local };\n}\n\n/** Looks a template up, and lists the ones that exist when it is not one. */\nfunction templateOf(name: string): Template {\n const template = TEMPLATES[name];\n if (template === undefined || !existsSync(join(packageRoot, 'templates', name))) {\n fail(`There is no \"${name}\" template. Available: ${Object.keys(TEMPLATES).join(', ')}.`);\n }\n return template;\n}\n\nfunction prepareTarget(options: Options): void {\n if (!existsSync(options.target)) {\n mkdirSync(options.target, { recursive: true });\n return;\n }\n const existing = readdirSync(options.target);\n if (existing.length > 0 && !options.force) {\n fail(`${options.target} is not empty (${existing.length} entries). Pass --force to write into it anyway.`);\n }\n}\n\n/**\n * Whether a package's `dist` is older than anything in its `src`.\n *\n * A newest-mtime comparison rather than a build system: it is wrong\n * only in the direction of building something that did not need it,\n * and a missing `dist` always builds. `--no-build` skips the question\n * entirely for the case where the caller knows.\n */\nfunction needsBuild(pkg: string): boolean {\n const dir = join(workspaceRoot, 'packages', pkg);\n const dist = join(dir, 'dist');\n if (!existsSync(dist)) {\n return true;\n }\n return newestChange(join(dir, 'src')) > newestChange(dist);\n}\n\n/** The most recent modification time anywhere under a directory. */\nfunction newestChange(dir: string): number {\n let newest = 0;\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n const path = join(dir, entry.name);\n newest = Math.max(newest, entry.isDirectory() ? newestChange(path) : statSync(path).mtimeMs);\n }\n return newest;\n}\n\n/**\n * Packs the workspace packages into the new project and returns the\n * `file:` specifier for each.\n *\n * The specifiers are relative to the project directory, so the whole\n * directory can be moved or copied and still install.\n */\nfunction vendorPackages(options: Options, template: Template): Map<string, string> {\n if (options.build) {\n // Only the packages that go into the project, and only the ones\n // whose `dist` is older than their `src`. Building the whole\n // workspace took the better part of a minute for a scaffold that\n // does not install two of the packages it built, and every second\n // of it was spent between a person typing a command and seeing\n // anything happen.\n const stale = template.vendored.filter(needsBuild);\n if (stale.length > 0) {\n console.log(`building ${stale.join(', ')}…`);\n const filters = stale.flatMap(pkg => ['--filter', `./packages/${pkg}`]);\n execFileSync('pnpm', [...filters, 'build'], { cwd: workspaceRoot, stdio: 'ignore' });\n }\n }\n\n const into = join(options.target, VENDOR_DIR);\n rmSync(into, { recursive: true, force: true });\n mkdirSync(into, { recursive: true });\n\n console.log('packing them into the new project…');\n const specifiers = new Map<string, string>();\n for (const pkg of template.vendored) {\n const before = new Set(readdirSync(into));\n execFileSync('pnpm', ['pack', '--pack-destination', into], {\n cwd: join(workspaceRoot, 'packages', pkg),\n stdio: 'ignore'\n });\n const created = readdirSync(into).filter(entry => !before.has(entry) && entry.endsWith('.tgz'));\n if (created.length !== 1) {\n fail(`pnpm pack in packages/${pkg} produced ${created.length} tarballs, expected 1.`);\n }\n specifiers.set(`gesso-${pkg}`, `file:${VENDOR_DIR}/${created[0]}`);\n }\n return specifiers;\n}\n\n/** Copies the template, renaming the files that had to be disguised. */\nfunction copyTemplate(from: string, options: Options): void {\n // `force` here is not the option of the same name: whether writing\n // into an occupied directory is allowed was settled by `prepareTarget`,\n // and a template file always wins over whatever it lands on.\n cpSync(from, options.target, { recursive: true, force: true });\n for (const [disguised, real] of RENAMED) {\n const path = join(options.target, disguised);\n if (existsSync(path)) {\n renameSync(path, join(options.target, real));\n }\n }\n}\n\n/**\n * Writes the project's name and its dependency specifiers.\n *\n * `overrides` carries the same specifiers, because the packages declare\n * each other by version range: `gesso-framework` and\n * `gesso-components` do, and so does `gesso-electrobun`. Without it an\n * installer is free to go looking for `gesso-core@^0.1.0` on a registry\n * that has never heard of it, and whether it does so depends on what\n * else is in the tree.\n */\nfunction writeManifest(options: Options, specifiers: ReadonlyMap<string, string>): void {\n const path = join(options.target, 'package.json');\n const manifest = JSON.parse(readFileSync(path, 'utf8')) as {\n name: string;\n dependencies: Record<string, string>;\n devDependencies?: Record<string, string>;\n overrides?: Record<string, string>;\n };\n manifest.name = options.name;\n for (const [pkg, specifier] of specifiers) {\n // The plugin and the overlay are development dependencies and the\n // runtime packages are not, so the specifier goes wherever the\n // template already declared the package.\n const where =\n manifest.dependencies[pkg] !== undefined\n ? manifest.dependencies\n : manifest.devDependencies?.[pkg] !== undefined\n ? manifest.devDependencies\n : undefined;\n if (where === undefined) {\n fail(`The ${options.template} template does not depend on ${pkg}, so there is nowhere to vendor it.`);\n }\n where[pkg] = specifier;\n }\n if (specifiers.size > 0) {\n manifest.overrides = Object.fromEntries(specifiers);\n } else {\n delete manifest.overrides;\n }\n writeFileSync(path, `${JSON.stringify(manifest, null, 2)}\\n`);\n}\n\n/**\n * Says the same thing to pnpm that `overrides` says to npm.\n *\n * npm was chosen because pnpm did not work, and the reason is still\n * exactly right: `pnpm pack` rewrites `workspace:^`\n * into `^0.1.0`, so the packed `gesso-framework` asks for\n * `gesso-core@^0.1.0` and pnpm 11 goes to a registry that has never\n * heard of it. What that record then rejected was shipping a\n * `pnpm-workspace.yaml` in a project that is not a workspace.\n *\n * That trade has moved. The file is five lines, it is the only place\n * pnpm 11 reads `overrides` from, and the alternative is a scaffold\n * that fails for the package manager this repository itself uses. It\n * says what it is for and it is deleted along with `vendor/` the day\n * the packages are published.\n */\nfunction writePnpmOverrides(options: Options, specifiers: ReadonlyMap<string, string>): void {\n const lines = [\n '# The tarballs in vendor/ again, for pnpm.',\n '#',\n '# Gesso is not published, so the packed gesso-* packages ask each',\n '# other for version ranges no registry can answer. npm reads the',\n '# `overrides` in package.json; pnpm 11 reads only this file. Both',\n '# go away when the packages are on a registry: delete vendor/, this',\n '# file and `overrides`, and put version ranges back.',\n 'overrides:',\n ...[...specifiers].map(([pkg, specifier]) => ` '${pkg}': '${specifier}'`),\n ''\n ];\n writeFileSync(join(options.target, 'pnpm-workspace.yaml'), lines.join('\\n'));\n}\n\n/**\n * Substitutes the template's placeholders in the files that carry them.\n *\n * The list is the template's rather than a walk of the tree, so that a\n * placeholder added to a file nobody listed fails visibly in the\n * generated project instead of being quietly left as `{{name}}`\n * somewhere it is never read.\n */\nfunction substitute(options: Options, template: Template): void {\n for (const relative of template.substitute) {\n const path = join(options.target, relative);\n if (!existsSync(path)) {\n fail(`The ${options.template} template says ${relative} carries {{name}}, and it was not written.`);\n }\n writeFileSync(path, readFileSync(path, 'utf8').replaceAll('{{name}}', options.name));\n }\n}\n\n/**\n * Appends the `vendor/` explanation to the generated README.\n *\n * It lives here rather than in the template because a project made\n * without `--local` has no `vendor/`, and a README explaining a\n * directory that is not there is worse than one that says nothing.\n */\nfunction explainVendoring(options: Options): void {\n const path = join(options.target, 'README.md');\n if (!existsSync(path)) {\n return;\n }\n const section = [\n '',\n '## Why `vendor/` exists, and how to remove it',\n '',\n 'This project was scaffolded with `--local`, so it installs Gesso from',\n 'a checkout rather than from the registry: the packages were packed',\n 'into `vendor/` and the manifest points at the tarballs.',\n '',\n 'The packed packages declare each other by version range, so without',\n 'help a package manager is free to go looking for `gesso-core@^0.1.0`',\n 'on the registry and get a different copy than the one beside it.',\n '`overrides` in `package.json` is what tells npm; `overrides` in',\n '`pnpm-workspace.yaml` is what tells pnpm, which reads it nowhere else.',\n '',\n 'To pick up a further change, run `create-gesso-app --local` over this',\n 'directory again with `--force`.',\n '',\n 'To move to the published packages: delete `vendor/`, delete',\n '`pnpm-workspace.yaml`, delete `overrides`, and put version ranges back',\n 'in `dependencies`.',\n ''\n ].join('\\n');\n writeFileSync(path, readFileSync(path, 'utf8').trimEnd() + '\\n' + section);\n}\n\nfunction main(): void {\n const options = parseArgs(process.argv.slice(2));\n if (options.local && !existsSync(join(workspaceRoot, 'packages', 'core', 'package.json'))) {\n fail(\n '--local packs the packages out of the Gesso workspace, and this is not one.\\n' +\n 'Run it from inside a checkout, or drop --local to install from the registry.'\n );\n }\n\n const template = templateOf(options.template);\n prepareTarget(options);\n copyTemplate(join(packageRoot, 'templates', options.template), options);\n // Without --local the template's own version ranges are the answer,\n // and the manifest needs nothing but its name.\n const specifiers = options.local ? vendorPackages(options, template) : new Map<string, string>();\n writeManifest(options, specifiers);\n if (options.local) {\n writePnpmOverrides(options, specifiers);\n explainVendoring(options);\n }\n substitute(options, template);\n\n const where = options.target.startsWith(process.cwd())\n ? options.target.slice(process.cwd().length + 1)\n : options.target;\n console.log(template.next(options.name, where, options.local));\n}\n\nmain();\n"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "create-gesso-app",
3
+ "version": "0.1.0",
4
+ "description": "Scaffolds a Gesso application: a Vite project whose interface runs in a render worker.",
5
+ "license": "MIT",
6
+ "author": "Kevin Baker",
7
+ "homepage": "https://gesso-docs.vercel.app",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/kevinpbaker/gesso.git",
11
+ "directory": "packages/create-gesso-app"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/kevinpbaker/gesso/issues"
15
+ },
16
+ "keywords": [
17
+ "gesso",
18
+ "canvas",
19
+ "ui",
20
+ "typescript",
21
+ "scaffold",
22
+ "starter",
23
+ "cli",
24
+ "create-app",
25
+ "template"
26
+ ],
27
+ "type": "module",
28
+ "bin": {
29
+ "create-gesso-app": "./dist/create-gesso-app.mjs"
30
+ },
31
+ "engines": {
32
+ "node": ">=20.19.0"
33
+ },
34
+ "files": [
35
+ "README.md",
36
+ "CHANGELOG.md",
37
+ "dist",
38
+ "LICENSE",
39
+ "templates"
40
+ ],
41
+ "devDependencies": {
42
+ "tsdown": "^0.22.14"
43
+ },
44
+ "scripts": {
45
+ "build": "tsdown"
46
+ }
47
+ }