create-gesso-app 0.1.0 → 0.2.1

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 CHANGED
@@ -1,5 +1,58 @@
1
1
  # create-gesso-app
2
2
 
3
+ ## 0.2.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 53f5703: **A scaffolded sibling keeps its whole name.** `create-gesso-app
8
+ ../gessosheet`, run from inside a checkout called `gesso`, finished by
9
+ announcing `Created gessosheet in heet.`
10
+
11
+ The closing message worked out where the project had landed by testing
12
+ whether the target path started with the current directory and slicing
13
+ that many characters off the front. That is a string test standing in
14
+ for a path one, and `/work/gessosheet` starts with `/work/gesso`
15
+ without ever having been inside it, so the name lost its first four
16
+ characters and the `cd` line underneath told the reader to go
17
+ somewhere that does not exist.
18
+
19
+ It is `relative` now, which answers the question that was being asked.
20
+ Which of the two paths to print is then readability rather than
21
+ correctness: a child or a sibling is shorter said relatively, and is
22
+ what the caller typed, while a target on the far side of the tree is a
23
+ run of `..` segments the absolute path beats. The files were always
24
+ written to the right place; only the message was wrong.
25
+
26
+ - b2840a2: **A scaffold installs the framework it was released beside.** Both
27
+ templates still asked for `gesso-core@^0.1.0` and its siblings after
28
+ the packages moved to 0.2.0, so `npm create gesso-app` against the
29
+ registry resolved a scaffold onto the previous framework.
30
+
31
+ The ranges are current again, and they are no longer maintained by
32
+ remembering. `pnpm changeset:version` now runs
33
+ `scripts/sync-template-ranges.ts` after it moves the packages, which
34
+ points every `gesso-*` range in the templates at the version that
35
+ package is actually at. The templates are the one manifest a release
36
+ would otherwise miss: they are data the CLI copies rather than
37
+ workspace members, so changesets does not know they exist.
38
+
39
+ `pnpm check:scaffold` already refused a drifted template and still
40
+ does. It kept its job; it simply is not the only thing standing
41
+ between a release and a stale scaffold any more, having fired on 0.2.0
42
+ and been talked past.
43
+
44
+ ## 0.2.0
45
+
46
+ ### Patch Changes
47
+
48
+ - 6c078dd: **The Electrobun template's README no longer explains a `vendor/` that
49
+ is not there.** The CLI appends the vendoring section itself, and only
50
+ under `--local`, so the template carrying its own hardcoded copy meant a
51
+ registry-scaffolded project shipped with a section about a directory it
52
+ does not have, and a `--local` one got the section twice. Removed from
53
+ the template; the two other lines that assumed every project was
54
+ vendored now say which route they are about.
55
+
3
56
  ## 0.1.0
4
57
 
5
58
  First public release.
@@ -1,7 +1,31 @@
1
1
  #!/usr/bin/env node
2
2
  import { execFileSync } from "node:child_process";
3
3
  import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
4
- import { basename, isAbsolute, join, resolve } from "node:path";
4
+ import { basename, isAbsolute, join, relative, resolve } from "node:path";
5
+ //#region bin/displayPath.ts
6
+ /**
7
+ * The new project's directory, written the way the reader should type it.
8
+ *
9
+ * This used to slice `process.cwd()` off the front of the target when
10
+ * the target started with it, which is a string test standing in for a
11
+ * path one. A sibling whose name extends the current directory's —
12
+ * `../gessosheet` scaffolded from inside `gesso` — passes that test
13
+ * without ever having been inside it, and the message printed the last
14
+ * four characters of the name: "Created gessosheet in heet."
15
+ *
16
+ * `relative` answers the question that was being asked. Which of the
17
+ * two to print is then a readability choice rather than a correctness
18
+ * one: a child or a sibling is shorter said relatively and is what the
19
+ * caller typed, while a target on the far side of the tree is a run of
20
+ * `..` segments that the absolute path beats. Shorter wins, and the
21
+ * tie goes to the absolute path because it is unambiguous.
22
+ */
23
+ function displayPath(from, target) {
24
+ const fromHere = relative(from, target);
25
+ if (fromHere === "") return target;
26
+ return fromHere.length < target.length ? fromHere : target;
27
+ }
28
+ //#endregion
5
29
  //#region bin/create-gesso-app.ts
6
30
  /**
7
31
  * `create-gesso-app`: scaffolds a Gesso application (the
@@ -12,20 +36,24 @@ import { basename, isAbsolute, join, resolve } from "node:path";
12
36
  * the app, hands it a worker constructor and mounts it, and that is the
13
37
  * whole of the main thread's job.
14
38
  *
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.
39
+ * By default the generated `package.json` names the published
40
+ * `gesso-*` packages by version range and an install goes to the
41
+ * registry, which is what a scaffold anywhere else does.
42
+ *
43
+ * `--local` is the other route, and the one this CLI keeps that a
44
+ * public scaffolder would not: it packs the packages a template needs
45
+ * out of this workspace with `pnpm pack`, drops the tarballs into the
46
+ * new project's `vendor/` directory and writes `file:` specifiers at
47
+ * them. That is the only way to scaffold against changes that are not
48
+ * released yet, it is the same route `scripts/check-install.ts` takes,
49
+ * and it is the route that exercises each package's `publishConfig`,
50
+ * which is the only thing that rewrites `exports` from `src/*.ts` to
51
+ * `dist`. `pnpm check:scaffold` runs this way, so the gate tests the
52
+ * working tree rather than the last release.
26
53
  *
27
54
  * 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
55
+ * node packages/create-gesso-app/bin/create-gesso-app.ts ../my-app --local
56
+ * node packages/create-gesso-app/bin/create-gesso-app.ts ../my-app --local --no-build
29
57
  * node packages/create-gesso-app/bin/create-gesso-app.ts ../my-app --template electrobun
30
58
  *
31
59
  * The two templates differ in what runs beneath the same application.
@@ -280,9 +308,10 @@ function copyTemplate(from, options) {
280
308
  * `overrides` carries the same specifiers, because the packages declare
281
309
  * each other by version range: `gesso-framework` and
282
310
  * `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.
311
+ * installer scaffolded with `--local` is free to resolve
312
+ * `gesso-core@^0.1.0` off the registry rather than from the tarball
313
+ * beside it, and whether it does so depends on what else is in the
314
+ * tree. Without `--local` there are no specifiers and no `overrides`.
286
315
  */
287
316
  function writeManifest(options, specifiers) {
288
317
  const path = join(options.target, "package.json");
@@ -301,27 +330,28 @@ function writeManifest(options, specifiers) {
301
330
  * Says the same thing to pnpm that `overrides` says to npm.
302
331
  *
303
332
  * 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
333
+ * exactly right: `pnpm pack` rewrites `workspace:^` into `^0.1.0`, so
334
+ * the packed `gesso-framework` asks for `gesso-core@^0.1.0` and pnpm 11
335
+ * resolves it off the registry rather than from the tarball beside it,
336
+ * which is a different copy of the package than the one this project
337
+ * was told to use. What that record then rejected was shipping a
308
338
  * `pnpm-workspace.yaml` in a project that is not a workspace.
309
339
  *
310
340
  * That trade has moved. The file is five lines, it is the only place
311
341
  * pnpm 11 reads `overrides` from, and the alternative is a scaffold
312
342
  * 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.
343
+ * says what it is for, and it is written only under `--local`.
315
344
  */
316
345
  function writePnpmOverrides(options, specifiers) {
317
346
  const lines = [
318
347
  "# The tarballs in vendor/ again, for pnpm.",
319
348
  "#",
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.",
349
+ "# This project was scaffolded with --local, so the packed gesso-*",
350
+ "# packages ask each other for version ranges the registry would",
351
+ "# answer with a different copy. npm reads the `overrides` in",
352
+ "# package.json; pnpm 11 reads only this file. To move to the",
353
+ "# published packages: delete vendor/, this file and `overrides`,",
354
+ "# and put version ranges back.",
325
355
  "overrides:",
326
356
  ...[...specifiers].map(([pkg, specifier]) => ` '${pkg}': '${specifier}'`),
327
357
  ""
@@ -390,8 +420,7 @@ function main() {
390
420
  explainVendoring(options);
391
421
  }
392
422
  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));
423
+ console.log(template.next(options.name, displayPath(process.cwd(), options.target), options.local));
395
424
  }
396
425
  main();
397
426
  //#endregion
@@ -1 +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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"}
1
+ {"version":3,"file":"create-gesso-app.mjs","names":[],"sources":["../bin/displayPath.ts","../bin/create-gesso-app.ts"],"sourcesContent":["import { relative } from 'node:path';\n\n/**\n * The new project's directory, written the way the reader should type it.\n *\n * This used to slice `process.cwd()` off the front of the target when\n * the target started with it, which is a string test standing in for a\n * path one. A sibling whose name extends the current directory's —\n * `../gessosheet` scaffolded from inside `gesso` — passes that test\n * without ever having been inside it, and the message printed the last\n * four characters of the name: \"Created gessosheet in heet.\"\n *\n * `relative` answers the question that was being asked. Which of the\n * two to print is then a readability choice rather than a correctness\n * one: a child or a sibling is shorter said relatively and is what the\n * caller typed, while a target on the far side of the tree is a run of\n * `..` segments that the absolute path beats. Shorter wins, and the\n * tie goes to the absolute path because it is unambiguous.\n */\nexport function displayPath(from: string, target: string): string {\n const fromHere = relative(from, target);\n if (fromHere === '') {\n return target;\n }\n return fromHere.length < target.length ? fromHere : target;\n}\n","#!/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 * By default the generated `package.json` names the published\n * `gesso-*` packages by version range and an install goes to the\n * registry, which is what a scaffold anywhere else does.\n *\n * `--local` is the other route, and the one this CLI keeps that a\n * public scaffolder would not: it packs the packages a template needs\n * out of this workspace with `pnpm pack`, drops the tarballs into the\n * new project's `vendor/` directory and writes `file:` specifiers at\n * them. That is the only way to scaffold against changes that are not\n * released yet, it is the same route `scripts/check-install.ts` takes,\n * and it is the route that exercises each package's `publishConfig`,\n * which is the only thing that rewrites `exports` from `src/*.ts` to\n * `dist`. `pnpm check:scaffold` runs this way, so the gate tests the\n * working tree rather than the last release.\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 --local\n * node packages/create-gesso-app/bin/create-gesso-app.ts ../my-app --local --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\nimport { displayPath } from './displayPath.ts';\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 scaffolded with `--local` is free to resolve\n * `gesso-core@^0.1.0` off the registry rather than from the tarball\n * beside it, and whether it does so depends on what else is in the\n * tree. Without `--local` there are no specifiers and no `overrides`.\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:^` into `^0.1.0`, so\n * the packed `gesso-framework` asks for `gesso-core@^0.1.0` and pnpm 11\n * resolves it off the registry rather than from the tarball beside it,\n * which is a different copy of the package than the one this project\n * was told to use. 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 written only under `--local`.\n */\nfunction writePnpmOverrides(options: Options, specifiers: ReadonlyMap<string, string>): void {\n const lines = [\n '# The tarballs in vendor/ again, for pnpm.',\n '#',\n '# This project was scaffolded with --local, so the packed gesso-*',\n '# packages ask each other for version ranges the registry would',\n '# answer with a different copy. npm reads the `overrides` in',\n '# package.json; pnpm 11 reads only this file. To move to the',\n '# published packages: delete vendor/, this file and `overrides`,',\n '# 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 console.log(template.next(options.name, displayPath(process.cwd(), options.target), options.local));\n}\n\nmain();\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-gesso-app",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "Scaffolds a Gesso application: a Vite project whose interface runs in a render worker.",
5
5
  "license": "MIT",
6
6
  "author": "Kevin Baker",
@@ -40,16 +40,17 @@ lands under that directory instead.
40
40
  ## Running it
41
41
 
42
42
  ```sh
43
- hutch install # dependencies, including the vendored Gesso packages
43
+ hutch install # dependencies
44
44
  hutch run dev # prepare, build the window's assets, open the window
45
45
  ```
46
46
 
47
47
  `hutch install` runs npm underneath, because `hutch.config.ts` says
48
- `packageManager: 'npm'`: Hutch's own resolver reads a relative `file:`
49
- override against the package that asked for it rather than against this
50
- directory, and stops with `FileNotFound` on the second vendored package.
51
- npm reads `overrides` from here, which is what the tarballs in `vendor/`
52
- need. You will see a `package-lock.json`, and no `hutch.lock`.
48
+ `packageManager: 'npm'`. That matters if this project was scaffolded
49
+ with `--local`: Hutch's own resolver reads a relative `file:` override
50
+ against the package that asked for it rather than against this
51
+ directory, and stops with `FileNotFound` on the second packed package,
52
+ while npm reads `overrides` from here, which is what tarballs in
53
+ `vendor/` need. You will see a `package-lock.json`, and no `hutch.lock`.
53
54
 
54
55
  `hutch run build` makes a distributable build under `build/stable-*`,
55
56
  and `hutch run typecheck` checks the types without building. Every
@@ -160,41 +161,15 @@ Electrobun bundle it, which is the ordinary arrangement. Gesso's own
160
161
  Electrobun applications do not do this: they pre-bundle their main
161
162
  process with esbuild first, because they import the framework out of a
162
163
  workspace rather than out of `node_modules`, and a bundler cannot
163
- resolve a package that was never installed. Here the packages are
164
- installed, in `vendor/`, so the ordinary arrangement is the right one.
164
+ resolve a package that was never installed. Here they are installed
165
+ into `node_modules` like anything else, so the ordinary arrangement is
166
+ the right one.
165
167
 
166
168
  If a future toolchain ever fails to resolve `gesso-electrobun` from the
167
169
  main process, that is the escape hatch: bundle `src/main/index.ts` to a
168
170
  plain `.js` file with `electrobun/main` left external, and point the
169
171
  `cottontail.entrypoint` at the bundle instead.
170
172
 
171
- ## Why `vendor/` exists, and how to remove it
172
-
173
- Gesso is not published to a registry yet. A `package.json` naming a
174
- version of `gesso-core` would produce a project that cannot install, so
175
- `create-gesso-app` packed the packages out of its own workspace, put the
176
- tarballs in `vendor/` and pointed `dependencies` and `overrides` at
177
- them:
178
-
179
- ```json
180
- "gesso-core": "file:vendor/gesso-core-0.1.0.tgz"
181
- ```
182
-
183
- `overrides` is there because the packages declare each other by version
184
- range, and without it an installer is free to go looking for
185
- `gesso-core@^0.1.0` on a registry that has never heard of it.
186
-
187
- To pick up a change made in the Gesso workspace, run `create-gesso-app`
188
- again over this directory with `--force`, or repack by hand:
189
-
190
- ```sh
191
- cd path/to/gesso && pnpm --filter './packages/*' build
192
- cd packages/core && pnpm pack --pack-destination path/to/this/project/vendor
193
- ```
194
-
195
- When the packages are published this all goes away: delete `vendor/`,
196
- delete `overrides`, and put version ranges back in `dependencies`.
197
-
198
173
  ## Where to go next
199
174
 
200
175
  - `src/render/App.tsx` is commented with what each part of it is doing.
@@ -5,10 +5,10 @@
5
5
  "type": "module",
6
6
  "description": "A Gesso application in a native Electrobun window.",
7
7
  "dependencies": {
8
- "gesso-components": "^0.1.0",
9
- "gesso-core": "^0.1.0",
10
- "gesso-electrobun": "^0.1.0",
11
- "gesso-framework": "^0.1.0",
8
+ "gesso-components": "^0.2.1",
9
+ "gesso-core": "^0.2.1",
10
+ "gesso-electrobun": "^0.2.1",
11
+ "gesso-framework": "^0.2.1",
12
12
  "rxjs": "^7.8.2"
13
13
  },
14
14
  "devDependencies": {
@@ -10,14 +10,14 @@
10
10
  "typecheck": "tsc --noEmit"
11
11
  },
12
12
  "dependencies": {
13
- "gesso-components": "^0.1.0",
14
- "gesso-core": "^0.1.0",
15
- "gesso-framework": "^0.1.0",
13
+ "gesso-components": "^0.2.1",
14
+ "gesso-core": "^0.2.1",
15
+ "gesso-framework": "^0.2.1",
16
16
  "rxjs": "^7.8.2"
17
17
  },
18
18
  "devDependencies": {
19
- "gesso-devtools": "^0.1.0",
20
- "gesso-vite-plugin": "^0.1.0",
19
+ "gesso-devtools": "^0.2.1",
20
+ "gesso-vite-plugin": "^0.2.1",
21
21
  "typescript": "~7.0.2",
22
22
  "vite": "^8.2.0"
23
23
  }