create-gesso-app 0.2.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,46 @@
|
|
|
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
|
+
|
|
3
44
|
## 0.2.0
|
|
4
45
|
|
|
5
46
|
### Patch Changes
|
|
@@ -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
|
|
@@ -396,8 +420,7 @@ function main() {
|
|
|
396
420
|
explainVendoring(options);
|
|
397
421
|
}
|
|
398
422
|
substitute(options, template);
|
|
399
|
-
|
|
400
|
-
console.log(template.next(options.name, where, options.local));
|
|
423
|
+
console.log(template.next(options.name, displayPath(process.cwd(), options.target), options.local));
|
|
401
424
|
}
|
|
402
425
|
main();
|
|
403
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 * 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\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 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
|
@@ -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
|
|
9
|
-
"gesso-core": "^0.1
|
|
10
|
-
"gesso-electrobun": "^0.1
|
|
11
|
-
"gesso-framework": "^0.1
|
|
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
|
|
14
|
-
"gesso-core": "^0.1
|
|
15
|
-
"gesso-framework": "^0.1
|
|
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
|
|
20
|
-
"gesso-vite-plugin": "^0.1
|
|
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
|
}
|