@tinycld/bootstrap 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +297 -0
  2. package/dist/args.js +130 -0
  3. package/dist/args.js.map +1 -0
  4. package/dist/bootstrap-tooling.js +66 -0
  5. package/dist/bootstrap-tooling.js.map +1 -0
  6. package/dist/copy-template.js +96 -0
  7. package/dist/copy-template.js.map +1 -0
  8. package/dist/index.js +134 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/layout.js +40 -0
  11. package/dist/layout.js.map +1 -0
  12. package/dist/link-core.js +99 -0
  13. package/dist/link-core.js.map +1 -0
  14. package/dist/link-package.js +85 -0
  15. package/dist/link-package.js.map +1 -0
  16. package/dist/prompts.js +187 -0
  17. package/dist/prompts.js.map +1 -0
  18. package/dist/substitute.js +35 -0
  19. package/dist/substitute.js.map +1 -0
  20. package/dist/validate.js +65 -0
  21. package/dist/validate.js.map +1 -0
  22. package/package.json +46 -0
  23. package/templates/full/manifest.ts +21 -0
  24. package/templates/full/package.json +30 -0
  25. package/templates/full/pb-migrations/1800000000_create_{{PKG_SLUG}}.js +67 -0
  26. package/templates/full/server/go.mod +10 -0
  27. package/templates/full/server/register.go +20 -0
  28. package/templates/full/tinycld/{{PKG_SLUG}}/collections.ts +27 -0
  29. package/templates/full/tinycld/{{PKG_SLUG}}/provider.tsx +16 -0
  30. package/templates/full/tinycld/{{PKG_SLUG}}/screens/[id].tsx +23 -0
  31. package/templates/full/tinycld/{{PKG_SLUG}}/screens/_layout.tsx +5 -0
  32. package/templates/full/tinycld/{{PKG_SLUG}}/screens/index.tsx +28 -0
  33. package/templates/full/tinycld/{{PKG_SLUG}}/seed.ts +22 -0
  34. package/templates/full/tinycld/{{PKG_SLUG}}/sidebar.tsx +20 -0
  35. package/templates/full/tinycld/{{PKG_SLUG}}/types.ts +18 -0
  36. package/templates/settings-only/manifest.ts +15 -0
  37. package/templates/settings-only/package.json +23 -0
  38. package/templates/settings-only/tinycld/{{PKG_SLUG}}/settings/main.tsx +20 -0
  39. package/templates/settings-only/tinycld/{{PKG_SLUG}}/types.ts +5 -0
  40. package/templates/shared/.github/workflows/ci.yml +59 -0
  41. package/templates/shared/README.md +54 -0
  42. package/templates/shared/tests/manifest.test.ts +14 -0
  43. package/templates/shared/tsconfig.json +22 -0
package/README.md ADDED
@@ -0,0 +1,297 @@
1
+ # @tinycld/bootstrap
2
+
3
+ Interactive scaffolder for `@tinycld` feature packages. One command produces a repo starter — manifest, CI workflow, lint/typecheck, sample screens, seed, migrations, and (optionally) a Go server — already wired to link into the [`tinycld`](https://github.com/tinycld/tinycld) app shell (which bundles `@tinycld/core`), use the `@tinycld/core/**` import convention, and pass its own CI.
4
+
5
+ Modeled after [`create-vite`](https://github.com/vitejs/vite/tree/main/packages/create-vite): tiny CLI, templates embedded in the published npm package, no runtime network fetch.
6
+
7
+ ## Requirements
8
+
9
+ - **[npm](https://docs.npmjs.com/)** (recommended) on Node ≥ 24 (the package's `engines.node` is `>=24`; older Node may work but is unsupported).
10
+ - A local `tinycld/tinycld` (app shell) checkout as a sibling of the package you're scaffolding. The app shell bundles `@tinycld/core` at `packages/@tinycld/core/` — the scaffolded package's `tsconfig` extends `../tinycld/packages/@tinycld/core/tsconfig.json` and aliases `@tinycld/core/*` to `../tinycld/packages/@tinycld/core/*`. Linking is done from the app shell via `npm run packages:link`. **The scaffolder will offer to clone and link the app shell for you** — the linking step is interactive (or scripted via `--link` / `--no-link`).
11
+ - `git` + `gh` if you intend to use the suggested "initial push" next-step — both are optional.
12
+
13
+ ## Usage
14
+
15
+ ```sh
16
+ npx @tinycld/bootstrap my-feature
17
+ ```
18
+
19
+ You'll be walked through an interactive prompt. The positional argument (`my-feature`) is the **slug** — kebab-case, 3–40 chars, becomes `@tinycld/my-feature`, the URL segment `/a/<orgSlug>/my-feature/`, and the Go module `tinycld.org/packages/my-feature`. Leave it off to be asked for it.
20
+
21
+ ### Prompts
22
+
23
+ | Prompt | Example | Notes |
24
+ |---|---|---|
25
+ | **Package slug** | `my-feature` | Skipped if given as argv. Validates kebab-case, minimum 3 chars. |
26
+ | **Human-readable name** | `My Feature` | Defaults to title-cased slug; used in the manifest's `name` + nav label. |
27
+ | **One-sentence description** | `Does a thing well.` | Used in manifest `description`, package.json, and README. |
28
+ | **Preset** | `full` / `settings-only` | See below. |
29
+ | **Lucide icon name** (full only) | `box` | Any [lucide-react-native](https://lucide.dev/icons) name. Default `box`. |
30
+ | **Nav order** (full only) | `20` | Integer 0–99, controls sidebar position. |
31
+ | **Keyboard shortcut** (full only) | `f` | Single lowercase letter, or blank. |
32
+ | **Include a Go server?** (full only) | `y` / `n` | If no, `server/` and the manifest's `server` field are omitted. |
33
+ | **Target directory** | `./my-feature` | Default creates the new repo as a child of the current directory. Must not exist or must be empty. |
34
+ | **Link into the app shell?** | `y` / `n` | After scaffolding, the CLI offers to clone (or use existing) `tinycld` app shell, run `npm install` in it, and link this package. Suppress with `--no-link`. |
35
+
36
+ ### Flags (non-interactive use)
37
+
38
+ Every prompt has a corresponding flag. Pass `--yes` (or `-y`) to accept all defaults and skip everything that wasn't given a flag — useful for scripted scaffolding.
39
+
40
+ | Flag | Maps to prompt | Notes |
41
+ |---|---|---|
42
+ | *(positional)* | Package slug | First non-flag argument. |
43
+ | `--name <s>` | Human-readable name | Defaults to title-cased slug. |
44
+ | `--description <s>` | Description | |
45
+ | `--preset <full\|settings-only>` | Preset | |
46
+ | `--icon <name>` | Lucide icon | Full preset only. |
47
+ | `--nav-order <n>` | Nav order | Integer 0–99. |
48
+ | `--shortcut <c>` | Keyboard shortcut | Single lowercase letter. |
49
+ | `--server` / `--no-server` | Include a Go server | Full preset only. |
50
+ | `--target <dir>` | Target directory | Default `./<slug>`. |
51
+ | `--link` / `--no-link` | Link into the app shell | Forces the post-scaffold link step on or off, skipping the prompt. |
52
+ | `--yes`, `-y` | — | Accept all defaults; with `--no-link`, fully non-interactive. |
53
+
54
+ Example, fully non-interactive:
55
+
56
+ ```sh
57
+ npx @tinycld/bootstrap my-feature \
58
+ --yes --no-link \
59
+ --description "Tracks widgets across the org" \
60
+ --preset full --icon box --nav-order 25 --shortcut w
61
+ ```
62
+
63
+ `--help` is not wired (yet) — run `npx @tinycld/bootstrap` with no argv to discover prompts interactively.
64
+
65
+ ## Presets
66
+
67
+ The scaffolder offers two starting points, corresponding to the two shapes already present in the tinycld ecosystem.
68
+
69
+ ### `full` — data package
70
+
71
+ Matches the shape of `@tinycld/contacts`, `@tinycld/mail`, `@tinycld/calendar`, `@tinycld/drive`. You get routes, a sidebar, an optional provider, pbtsdb collections, PocketBase migrations, seed data, and a Go server stub.
72
+
73
+ <details>
74
+ <summary>Generated tree (with <code>slug=my-feature</code>)</summary>
75
+
76
+ ```
77
+ my-feature/
78
+ ├── .github/workflows/ci.yml # lint + typecheck + unit tests; clones the tinycld app shell + links self
79
+ ├── .gitignore # node_modules, *.tsbuildinfo, lockfiles, .DS_Store
80
+ ├── README.md # developer-facing onboarding for this package
81
+ ├── manifest.ts # name, slug, routes, nav, collections, seed, server, ...
82
+ ├── package.json # @tinycld/my-feature, peer deps, scripts, exports map
83
+ ├── tsconfig.json # extends ../tinycld/packages/@tinycld/core/tsconfig.json
84
+ ├── pb-migrations/
85
+ │ └── 1800000000_create_my-feature.js # creates my_feature_items collection
86
+ ├── server/
87
+ │ ├── go.mod # module tinycld.org/packages/my-feature; replaces tinycld.org/core → ../../tinycld/packages/@tinycld/core/server
88
+ │ └── register.go # func Register(app) hook for server-side wiring
89
+ ├── tests/
90
+ │ └── manifest.test.ts # vitest smoke test of manifest shape
91
+ └── tinycld/my-feature/ # all package TypeScript lives under this prefix
92
+ ├── collections.ts # registerCollections() for pbtsdb
93
+ ├── provider.tsx # optional context provider
94
+ ├── seed.ts # default-export async seed(pb, ctx)
95
+ ├── sidebar.tsx # sidebar rendered for this package's routes
96
+ ├── types.ts # MyFeatureSchema + record interfaces
97
+ └── screens/
98
+ ├── _layout.tsx # Stack layout for /a/[orgSlug]/my-feature/**
99
+ ├── [id].tsx # detail route
100
+ └── index.tsx # list route
101
+ ```
102
+
103
+ </details>
104
+
105
+ The `tinycld/my-feature/` nesting gives the package a stable public API surface accessible via the `package.json` `exports` map: `@tinycld/my-feature/screens/*`, `/sidebar`, `/collections`, etc.
106
+
107
+ ### `settings-only` — service package
108
+
109
+ Matches `@tinycld/google-takeout-import`. The package contributes only a settings panel — no routes, no nav entry, no collections, no server. Use this for integrations or admin-style tools that live under `/a/<orgSlug>/settings/**`.
110
+
111
+ <details>
112
+ <summary>Generated tree</summary>
113
+
114
+ ```
115
+ my-service/
116
+ ├── .github/workflows/ci.yml
117
+ ├── .gitignore
118
+ ├── README.md
119
+ ├── manifest.ts # name, slug, description, settings[] only
120
+ ├── package.json
121
+ ├── tsconfig.json
122
+ ├── tests/
123
+ │ └── manifest.test.ts
124
+ └── tinycld/my-service/
125
+ ├── types.ts # public type exports (empty by default)
126
+ └── settings/
127
+ └── main.tsx # the settings panel component
128
+ ```
129
+
130
+ </details>
131
+
132
+ ## Manifest fields
133
+
134
+ The scaffolded `manifest.ts` is the single source of truth for what a package contributes. Templates only fill in the fields appropriate for the chosen preset; the full reference (every field, when to use it) lives in [`tinycld/tinycld`'s docs](https://github.com/tinycld/tinycld). Quick summary:
135
+
136
+ | Field | Meaning |
137
+ |---|---|
138
+ | `name`, `slug`, `version`, `description` | Identity. `slug` is the URL segment and the npm name's last segment. |
139
+ | `routes.directory` | Subpath (resolved through `package.json` exports) where org-scoped screens live. Generator re-exports each screen file under `app/a/[orgSlug]/<slug>/`. |
140
+ | `nav` | `{ label, icon, order, shortcut }` — sidebar entry for the org workspace. |
141
+ | `sidebar.component` | Subpath to the package's sidebar component, rendered when on its routes. |
142
+ | `provider.component` | Optional context provider mounted around the package's routes. |
143
+ | `migrations.directory` | Folder of PocketBase JS migrations; symlinked into the app shell. |
144
+ | `collections.register`, `collections.types` | Subpaths to the pbtsdb registration function and the schema types. |
145
+ | `seed.script` | Default-export async function called by the dev seeder for this package. |
146
+ | `server` | `{ package, module }` — relative dir + Go module path for the optional server extension. |
147
+ | `settings[]` | One entry per panel contributed under `/a/<orgSlug>/settings/<slug>`. |
148
+
149
+ All path-shaped fields use **short subpaths** (`'screens'`, `'sidebar'`, `'collections'`) that match the keys in `package.json`'s `exports` map — the generator follows the exports map to find the actual files under `tinycld/<slug>/...`.
150
+
151
+ ## After scaffolding
152
+
153
+ If you accepted the link-into-app-shell prompt, the package is already linked in `tinycld/`. Otherwise the CLI prints next-steps you can copy verbatim:
154
+
155
+ ```sh
156
+ # 1. Initialize git and push to GitHub
157
+ cd my-feature
158
+ git init
159
+ git add .
160
+ git commit -m 'chore: initial scaffold'
161
+ gh repo create tinycld/my-feature --public --source=. --push
162
+
163
+ # 2. Link into the tinycld app shell so you can develop against it
164
+ cd ../tinycld
165
+ npm run packages:link ../my-feature
166
+
167
+ # 3. Verify (still inside tinycld/)
168
+ npm run checks
169
+ ```
170
+
171
+ Once linked, the app shell's generator wires your manifest in automatically: routes appear at `/a/<orgSlug>/my-feature/**`, the sidebar renders, the settings panel shows up, migrations get picked up by PocketBase, etc. No further changes to `tinycld` or `core` are needed.
172
+
173
+ > ⚠️ **`tinycld/metro.config.cjs` scans `packages/` once at boot.** If you link a new package while `npm run start` is already running, restart it (Ctrl-C, then `npm run start`) so Metro's resolver picks it up. CI is fine — it always starts fresh.
174
+
175
+ ### Day-to-day development
176
+
177
+ Most work happens **inside `tinycld/`** with your package linked. Run the app, make changes, see them live:
178
+
179
+ ```sh
180
+ cd ../tinycld
181
+ npm run start # expo + pocketbase, fronted by a single-port dev proxy
182
+ npm run checks # biome + tsc — covers all linked packages including yours
183
+ npm run test:unit # vitest, includes your tests/ via the packages glob
184
+ ```
185
+
186
+ Hot reload picks up changes in your package the same way as core code, since it's symlinked.
187
+
188
+ ### Running the scaffolded package's own CI locally
189
+
190
+ The package's `.github/workflows/ci.yml` reproduces the link-and-check dance locally. The shape:
191
+
192
+ 1. Clone `tinycld/tinycld` as a sibling. (Core ships inside the app shell; no separate core clone needed.)
193
+ 2. `npm install` inside `tinycld/` (this also runs the package generator via the postinstall hook).
194
+ 3. `npm run packages:link ../<your-pkg>` from `tinycld/`.
195
+ 4. **Lint from inside `tinycld/`** (`npm run lint`). Biome lives in the app shell and its config (`tinycld/biome.json`) is the single source of truth for every linked package. There is no `biome.json` in the package repo.
196
+ 5. **Typecheck from inside `tinycld/`** (`npm run typecheck`) — the app shell's tsconfig pulls in `expo`'s base, `uniwind` global type augments (which add `className` to RN components), and the live `~/types/pbSchema` generated from PocketBase. A standalone `tsc` invocation inside the package can't see those.
197
+ 6. Run unit tests from the package (vitest discovers them via the `packages/@*/*/tests/**` glob in `tinycld/vitest.config.ts`).
198
+
199
+ When you push, GitHub Actions runs that exact flow.
200
+
201
+ ## Import conventions the templates assume
202
+
203
+ Sibling packages should import core utilities via the **scoped** path:
204
+
205
+ ```ts
206
+ // ✓ right
207
+ import { useOrgLiveQuery } from '@tinycld/core/lib/use-org-live-query'
208
+ import { useThemeColor } from '@tinycld/core/lib/use-app-theme'
209
+ import { Modal } from '@tinycld/core/ui/modal'
210
+
211
+ // ✗ wrong (legacy; resolved in earlier core layouts but not the current one)
212
+ import { useOrgLiveQuery } from '~/lib/use-org-live-query'
213
+ ```
214
+
215
+ Within the package, intra-package imports use relative paths:
216
+
217
+ ```ts
218
+ import { registerCollections } from './collections'
219
+ import { ContactForm } from '../components/ContactForm'
220
+ ```
221
+
222
+ `~/tinycld/<slug>/*` is also aliased to your own nested source, for cases where you want an absolute import within the package — but relative paths are usually clearer.
223
+
224
+ Cross-package dependencies are **not** supported at compile time. If your package needs data from another package (e.g. mail wanting to read contacts), use the runtime `usePackages()` helper from `@tinycld/core/lib/packages/use-packages` and do the lookup at runtime. See `mail/tinycld/mail/components/ContactSuggestionsList.tsx` or `drive/tinycld/drive/components/ContactSuggestionsSource.tsx` for the canonical pattern.
225
+
226
+ ## Contributing to the templates
227
+
228
+ Templates live under `templates/`:
229
+
230
+ ```
231
+ templates/
232
+ ├── shared/ # files identical across presets (tsconfig, CI workflow, README, .gitignore, tests/manifest.test.ts)
233
+ ├── full/ # data-package preset (manifest, package.json, screens, sidebar, provider, collections, types, seed, pb-migrations, server)
234
+ └── settings-only/ # settings-only preset (manifest, package.json, types, settings/main.tsx)
235
+ ```
236
+
237
+ `shared/` is copied first; the selected preset is copied on top. A preset can override a shared file just by naming it at the same relative path. After copying, if `--no-server` was chosen, `server/` is removed and the `server: { … }` field is stripped from `manifest.ts`.
238
+
239
+ Files contain `{{PLACEHOLDER}}` tokens that get substituted at scaffold time:
240
+
241
+ | Placeholder | Derivation |
242
+ |---|---|
243
+ | `{{PKG_SLUG}}` | user input, kebab-case |
244
+ | `{{PKG_NAME}}` | user input, human-readable |
245
+ | `{{PKG_SCOPED}}` | kept as alias for `{{PKG_SLUG}}` (back-compat; prefer `{{PKG_SLUG}}` in new templates) |
246
+ | `{{PKG_PASCAL}}` | PascalCase of slug |
247
+ | `{{PKG_CAMEL}}` | camelCase of slug |
248
+ | `{{PKG_SNAKE}}` | snake_case of slug (for DB table names) |
249
+ | `{{PKG_DESCRIPTION}}` | user input |
250
+ | `{{PKG_ICON}}` | user input, lucide icon name |
251
+ | `{{PKG_NAV_ORDER}}` | user input, integer |
252
+ | `{{PKG_NAV_SHORTCUT}}` | user input, single letter or empty |
253
+ | `{{GO_MODULE}}` | `tinycld.org/packages/` + slug |
254
+
255
+ Substitution runs on both file **content** and **file/directory names** — that's how `tinycld/{{PKG_SLUG}}/**` becomes `tinycld/my-feature/**`. Binary files (`.png`, `.jpg`, `.woff`, etc) are copied byte-for-byte and skipped during substitution; the full list lives in `BINARY_EXTENSIONS` in `src/copy-template.ts`.
256
+
257
+ Adding a new placeholder requires one line in `src/substitute.ts`'s `buildPlaceholders()` plus whatever tokens you scatter through the templates.
258
+
259
+ ### Local development of the scaffolder itself
260
+
261
+ ```sh
262
+ npm install
263
+ npm run dev my-feature --target /tmp/scratch # tsx live-runs src/index.ts
264
+ npm run lint # biome
265
+ npm run typecheck # tsc --noEmit
266
+ npm run checks # both of the above
267
+ npm run test # vitest: substitute + validate + end-to-end scaffold into tmpdir
268
+ npm run build # compile src/ → dist/ (what gets published)
269
+ ```
270
+
271
+ The scaffolder tests invoke `copyTemplate` into a tmp directory and assert the expected tree, file contents, and placeholder substitutions for both presets. The end-to-end flow (link into a real `tinycld/` checkout and boot the dev server) is covered manually — see git history for the validation playbook.
272
+
273
+ ## Publishing
274
+
275
+ The repo's `.github/workflows/ci.yml` has a `publish` job that runs on tag pushes (`v*`). Tag a release and it ships to npm under the `@tinycld` scope:
276
+
277
+ ```sh
278
+ # Bump version in package.json, commit, then
279
+ git tag v0.1.1
280
+ git push --tags
281
+ ```
282
+
283
+ Publishing needs an `NPM_TOKEN` repo secret (npm "automation" token scoped to `@tinycld`). Add it once at Settings → Secrets → Actions.
284
+
285
+ `prepublishOnly` runs `npm run checks && npm run test && npm run build` before any publish, so a broken tree never reaches npm.
286
+
287
+ ## Design notes
288
+
289
+ - **Templates are embedded in the npm package**, not fetched from a separate repo. `npx` grabs them once; scaffolding is offline thereafter.
290
+ - **Direct string replacement**, no handlebars / EJS. Simpler, fewer moving parts, no runtime template compiler.
291
+ - **No destructive actions**: the CLI refuses to overwrite a non-empty target directory and never touches git, gh, or your local repos without consent. The clone-and-link step is opt-in (`--link` / interactive prompt).
292
+ - **Two presets, not N flags**. We have exactly two shapes of feature package in the tinycld ecosystem today (data package, settings-only); offering a fine-grained matrix of "routes y/n, server y/n, …" adds prompt-fatigue with no real benefit.
293
+ - **`@clack/prompts`** for the interactive UX: smaller and nicer than inquirer. **`picocolors`** for output, no `chalk` weight.
294
+
295
+ ## License
296
+
297
+ MIT.
package/dist/args.js ADDED
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Minimal argv parser for bootstrap. Every prompt has a
3
+ * corresponding --flag; when a flag is set, the associated prompt is
4
+ * skipped. --yes accepts all defaults.
5
+ */
6
+ export class ArgParseError extends Error {
7
+ issues;
8
+ constructor(issues) {
9
+ super(issues.map((i) => `--${i.flag}: ${i.reason}`).join('; '));
10
+ this.issues = issues;
11
+ this.name = 'ArgParseError';
12
+ }
13
+ }
14
+ const STRING_FLAGS = new Set(['name', 'description', 'preset', 'icon', 'shortcut', 'target']);
15
+ const NUMBER_FLAGS = new Set(['nav-order']);
16
+ const BOOL_FLAGS = new Set(['server', 'link', 'yes', 'tooling']);
17
+ const BOOL_ALIASES = { y: 'yes' };
18
+ export function parseArgs(argv) {
19
+ const out = {};
20
+ const positionals = [];
21
+ const issues = [];
22
+ for (let i = 0; i < argv.length; i++) {
23
+ const arg = argv[i] ?? '';
24
+ if (!arg.startsWith('-')) {
25
+ positionals.push(arg);
26
+ continue;
27
+ }
28
+ const short = arg.startsWith('--') ? arg.slice(2) : arg.slice(1);
29
+ const eqIdx = short.indexOf('=');
30
+ const rawName = eqIdx === -1 ? short : short.slice(0, eqIdx);
31
+ const inlineValue = eqIdx === -1 ? undefined : short.slice(eqIdx + 1);
32
+ const isNegated = rawName.startsWith('no-');
33
+ const baseName = isNegated ? rawName.slice(3) : rawName;
34
+ const canonical = BOOL_ALIASES[baseName] ?? baseName;
35
+ if (BOOL_FLAGS.has(canonical)) {
36
+ if (inlineValue !== undefined) {
37
+ issues.push({ flag: canonical, reason: 'boolean flag takes no value' });
38
+ continue;
39
+ }
40
+ setBool(out, canonical, !isNegated);
41
+ continue;
42
+ }
43
+ if (isNegated) {
44
+ issues.push({ flag: rawName, reason: 'not a boolean flag' });
45
+ continue;
46
+ }
47
+ if (canonical === 'with') {
48
+ const v = inlineValue ?? argv[++i];
49
+ if (v === undefined || v.startsWith('-')) {
50
+ issues.push({ flag: 'with', reason: 'requires a feature slug' });
51
+ continue;
52
+ }
53
+ out.with = out.with ?? [];
54
+ out.with.push(v);
55
+ continue;
56
+ }
57
+ const value = inlineValue ?? argv[++i];
58
+ if (value === undefined) {
59
+ issues.push({ flag: canonical, reason: 'missing value' });
60
+ continue;
61
+ }
62
+ if (STRING_FLAGS.has(canonical)) {
63
+ setString(out, canonical, value);
64
+ }
65
+ else if (NUMBER_FLAGS.has(canonical)) {
66
+ const n = Number(value);
67
+ if (!Number.isFinite(n)) {
68
+ issues.push({ flag: canonical, reason: `"${value}" is not a number` });
69
+ continue;
70
+ }
71
+ setNumber(out, canonical, n);
72
+ }
73
+ else {
74
+ issues.push({ flag: rawName, reason: 'unknown flag' });
75
+ }
76
+ }
77
+ if (positionals[0] !== undefined)
78
+ out.slug = positionals[0];
79
+ if (issues.length > 0)
80
+ throw new ArgParseError(issues);
81
+ return out;
82
+ }
83
+ function setString(out, name, value) {
84
+ switch (name) {
85
+ case 'name':
86
+ out.name = value;
87
+ break;
88
+ case 'description':
89
+ out.description = value;
90
+ break;
91
+ case 'preset':
92
+ if (value !== 'full' && value !== 'settings-only') {
93
+ throw new ArgParseError([
94
+ { flag: 'preset', reason: `expected "full" or "settings-only", got "${value}"` },
95
+ ]);
96
+ }
97
+ out.preset = value;
98
+ break;
99
+ case 'icon':
100
+ out.icon = value;
101
+ break;
102
+ case 'shortcut':
103
+ out.shortcut = value;
104
+ break;
105
+ case 'target':
106
+ out.target = value;
107
+ break;
108
+ }
109
+ }
110
+ function setNumber(out, name, value) {
111
+ if (name === 'nav-order')
112
+ out.navOrder = value;
113
+ }
114
+ function setBool(out, name, value) {
115
+ switch (name) {
116
+ case 'server':
117
+ out.server = value;
118
+ break;
119
+ case 'link':
120
+ out.link = value;
121
+ break;
122
+ case 'yes':
123
+ out.yes = value;
124
+ break;
125
+ case 'tooling':
126
+ out.tooling = value;
127
+ break;
128
+ }
129
+ }
130
+ //# sourceMappingURL=args.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"args.js","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { existsSync, writeFileSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ // All feature members that exist in the ecosystem. The workspace manifest lists
5
+ // ALL of them (npm tolerates absent dirs), so a partial checkout still installs.
6
+ // But --tooling CLONES only app+core+requested — we do NOT force-clone all.
7
+ const ALL_FEATURES = ['contacts', 'mail', 'calendar', 'drive', 'calc', 'text', 'google-takeout-import'];
8
+ // Always cloned by tooling mode (the minimum viable workspace). package-scripts
9
+ // ships inside the workspace meta-repo itself, so it is never cloned.
10
+ const ALWAYS_CLONE = ['app', 'core'];
11
+ // Listed in the manifest workspaces array (everything that could exist).
12
+ const ALL_MEMBERS = ['app', 'core', 'package-scripts', ...ALL_FEATURES];
13
+ /**
14
+ * Write a workspace-root package.json + .npmrc into `dir`. The workspaces array
15
+ * ALWAYS lists every possible member — npm ignores entries whose dirs are
16
+ * absent, so a partial checkout (app+core+one feature) installs fine, and
17
+ * cloning more features later needs no manifest edit.
18
+ */
19
+ export function writeWorkspaceManifest(dir) {
20
+ const pkg = {
21
+ name: '@tinycld/workspace',
22
+ version: '0.0.0',
23
+ private: true,
24
+ type: 'module',
25
+ workspaces: [...ALL_MEMBERS],
26
+ scripts: {
27
+ postinstall: 'cd app && npm run packages:generate && npm run assets:copy-pdfjs',
28
+ },
29
+ };
30
+ writeFileSync(join(dir, 'package.json'), `${JSON.stringify(pkg, null, 4)}\n`);
31
+ writeFileSync(join(dir, '.npmrc'), 'legacy-peer-deps=true\n');
32
+ }
33
+ function realClone(url, dest) {
34
+ const r = spawnSync('git', ['clone', '--depth', '1', url, dest], { stdio: 'inherit' });
35
+ return r.status === 0;
36
+ }
37
+ /**
38
+ * Assemble a workspace skeleton at opts.root: write the (full) manifest, then
39
+ * clone ONLY app + core + the explicitly-requested feature members (skipping any
40
+ * already present — e.g. a CI-checked-out member). Unknown member names throw.
41
+ * Returns the members that ended up present. Does NOT run npm install (the
42
+ * caller / CI controls that, since it may want a clean `npm ci`).
43
+ */
44
+ export function bootstrapTooling(opts) {
45
+ const requested = opts.members ?? [];
46
+ const unknown = requested.filter((m) => !ALL_FEATURES.includes(m));
47
+ if (unknown.length > 0) {
48
+ throw new Error(`Unknown feature member(s): ${unknown.join(', ')}. Known: ${ALL_FEATURES.join(', ')}`);
49
+ }
50
+ const repoBase = opts.repoBase ?? 'git@github.com:tinycld';
51
+ const clone = opts.clone ?? realClone;
52
+ writeWorkspaceManifest(opts.root);
53
+ const toClone = Array.from(new Set([...ALWAYS_CLONE, ...requested]));
54
+ const present = [];
55
+ for (const m of toClone) {
56
+ const dest = join(opts.root, m);
57
+ if (existsSync(join(dest, '.git')) || existsSync(join(dest, 'package.json'))) {
58
+ present.push(m);
59
+ continue;
60
+ }
61
+ if (clone(`${repoBase}/${m}.git`, dest))
62
+ present.push(m);
63
+ }
64
+ return present;
65
+ }
66
+ //# sourceMappingURL=bootstrap-tooling.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bootstrap-tooling.js","sourceRoot":"","sources":["../src/bootstrap-tooling.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,96 @@
1
+ import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs';
2
+ import { dirname, join } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { buildPlaceholders, substitute } from "./substitute.js";
5
+ // Files copied byte-for-byte. Binary formats and anything where {{...}}
6
+ // tokens would be a false match. All other files are UTF-8 and run through
7
+ // substitute.
8
+ const BINARY_EXTENSIONS = new Set(['.png', '.jpg', '.jpeg', '.gif', '.ico', '.zip', '.woff', '.woff2']);
9
+ export function copyTemplate(templatesRoot, answers) {
10
+ const placeholders = buildPlaceholders(answers);
11
+ const target = answers.targetDir;
12
+ if (!existsSync(target))
13
+ mkdirSync(target, { recursive: true });
14
+ // Walk shared first, then the preset directory on top. Preset files with
15
+ // the same relative path overwrite shared ones — lets presets customize a
16
+ // single file (e.g. a richer tests/manifest.test.ts) without cloning the
17
+ // whole shared tree.
18
+ const sharedDir = join(templatesRoot, 'shared');
19
+ const presetDir = join(templatesRoot, answers.preset);
20
+ if (existsSync(sharedDir))
21
+ copyTree(sharedDir, target, placeholders);
22
+ if (existsSync(presetDir))
23
+ copyTree(presetDir, target, placeholders);
24
+ if (answers.preset === 'full' && answers.includeServer === false) {
25
+ // User declined the Go server. Strip the `server: { ... }` manifest
26
+ // field and drop the server/ directory. Cheaper than a separate
27
+ // sub-template that would duplicate everything else in full/.
28
+ stripServerFromManifest(join(target, 'manifest.ts'));
29
+ const serverDir = join(target, 'server');
30
+ if (existsSync(serverDir) && statSync(serverDir).isDirectory()) {
31
+ rmSync(serverDir, { recursive: true, force: true });
32
+ }
33
+ }
34
+ }
35
+ function copyTree(srcRoot, dstRoot, placeholders) {
36
+ walk(srcRoot, (isDir, relPath) => {
37
+ const rendered = substitute(relPath, placeholders);
38
+ const dst = join(dstRoot, rendered);
39
+ if (isDir) {
40
+ if (!existsSync(dst))
41
+ mkdirSync(dst, { recursive: true });
42
+ return;
43
+ }
44
+ const src = join(srcRoot, relPath);
45
+ mkdirSync(dirname(dst), { recursive: true });
46
+ if (isBinary(src)) {
47
+ cpSync(src, dst);
48
+ return;
49
+ }
50
+ const content = readFileSync(src, 'utf8');
51
+ writeFileSync(dst, substitute(content, placeholders), 'utf8');
52
+ });
53
+ }
54
+ function walk(root, visit) {
55
+ const stack = [''];
56
+ while (stack.length > 0) {
57
+ const rel = stack.pop();
58
+ if (rel === undefined)
59
+ break;
60
+ const abs = join(root, rel);
61
+ const entries = readdirSync(abs, { withFileTypes: true });
62
+ for (const entry of entries) {
63
+ const childRel = rel ? join(rel, entry.name) : entry.name;
64
+ if (entry.isDirectory()) {
65
+ visit(true, childRel);
66
+ stack.push(childRel);
67
+ }
68
+ else if (entry.isFile()) {
69
+ visit(false, childRel);
70
+ }
71
+ }
72
+ }
73
+ }
74
+ function isBinary(path) {
75
+ const idx = path.lastIndexOf('.');
76
+ if (idx < 0)
77
+ return false;
78
+ return BINARY_EXTENSIONS.has(path.slice(idx).toLowerCase());
79
+ }
80
+ function stripServerFromManifest(manifestPath) {
81
+ if (!existsSync(manifestPath))
82
+ return;
83
+ const src = readFileSync(manifestPath, 'utf8');
84
+ const stripped = src.replace(/^\s*server:\s*\{[^}]*\},?\s*$\n?/m, '');
85
+ writeFileSync(manifestPath, stripped, 'utf8');
86
+ }
87
+ /**
88
+ * Resolve templates/ relative to the compiled module. In published builds
89
+ * `dist/copy-template.js` sits next to `templates/`; in dev `src/copy-template.ts`
90
+ * sits under the same parent as `templates/`. Both resolve to `../templates`.
91
+ */
92
+ export function resolveTemplatesRoot(metaUrl) {
93
+ const here = dirname(fileURLToPath(metaUrl));
94
+ return join(here, '..', 'templates');
95
+ }
96
+ //# sourceMappingURL=copy-template.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copy-template.js","sourceRoot":"","sources":["../src/copy-template.ts"],"names":[],"mappings":"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"}