@tinycld/bootstrap 2.4.8 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -7
- package/dist/assemble-workspace.js +48 -409
- package/dist/assemble-workspace.js.map +1 -1
- package/package.json +1 -1
- package/templates/full/package.json +0 -8
- package/templates/full/tinycld/{{PKG_SLUG}}/collections.ts +21 -5
- package/templates/full/tinycld/{{PKG_SLUG}}/provider.tsx +1 -1
- package/templates/full/tinycld/{{PKG_SLUG}}/screens/[id].tsx +4 -4
- package/templates/full/tinycld/{{PKG_SLUG}}/screens/index.tsx +7 -4
- package/templates/full/tinycld/{{PKG_SLUG}}/sidebar.tsx +1 -1
- package/templates/settings-only/package.json +0 -5
- package/templates/settings-only/tinycld/{{PKG_SLUG}}/settings/main.tsx +1 -1
- package/templates/workspace/scripts/link-members.ts +47 -5
- package/templates/workspace/tests/tinycld.packages.test.ts +49 -0
- package/templates/workspace/tinycld.packages.ts +36 -0
package/README.md
CHANGED
|
@@ -23,7 +23,9 @@ pnpm install # links members + runs the generator (postinstall)
|
|
|
23
23
|
cd tinycld && pnpm run dev
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
The CLI writes
|
|
26
|
+
The CLI writes a small set of workspace files from embedded templates (`tinycld.packages.ts`, `vitest.config.ts`, shared test stubs), then clones the `tinycld` repo (which carries `@tinycld/core` + `@tinycld/package-scripts` nested) as a sibling. Each `--with <slug>` adds one feature sibling. `tinycld` is always cloned; everything else is opt-in.
|
|
27
|
+
|
|
28
|
+
Bootstrap carries no vendor version pins and owns none of the root coordination-file formats. Immediately after cloning, it runs `scripts/write-workspace-root.ts` **from inside the freshly cloned `tinycld` repo** to write `package.json`, `pnpm-workspace.yaml` (incl. the pnpm `overrides:` block), a root `package-versions.json` copy, `biome.json`, `.watchmanconfig`, and `.npmrc` — all derived from `tinycld/core/package-versions.json`, the pins' source of truth. This means `--assemble-only` requires a tinycld ref that contains `scripts/write-workspace-root.ts` (the first tinycld release after 2026-08); an older ref fails assembly with a clear error naming the missing script, rather than silently producing a workspace with stale or missing root files.
|
|
27
29
|
|
|
28
30
|
`--with name@ref` pins a clone to a tag, branch, or commit:
|
|
29
31
|
|
|
@@ -47,7 +49,7 @@ TINYCLD_REPO_BASE=https://github.com/tinycld \
|
|
|
47
49
|
npx @tinycld/bootstrap --new my-feature
|
|
48
50
|
```
|
|
49
51
|
|
|
50
|
-
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
|
|
52
|
+
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/my-feature/`, and the Go module `tinycld.org/packages/my-feature`. Leave it off to be asked for it.
|
|
51
53
|
|
|
52
54
|
If `--new` runs from inside an existing workspace root (a `tinycld/` shell sibling detected), the new package is scaffolded as a sibling and the link step adds it to `pnpm-workspace.yaml`. Otherwise the CLI creates a wrapper directory `./tinycld-<slug>/` with the package at `./tinycld-<slug>/<slug>/`, assembles a workspace around it (cloning the `tinycld` repo), and links — leaving you a self-contained, runnable workspace.
|
|
53
55
|
|
|
@@ -131,7 +133,7 @@ my-feature/
|
|
|
131
133
|
├── sidebar.tsx # sidebar rendered for this package's routes
|
|
132
134
|
├── types.ts # MyFeatureSchema + record interfaces
|
|
133
135
|
└── screens/
|
|
134
|
-
├── _layout.tsx # Stack layout for /a/
|
|
136
|
+
├── _layout.tsx # Stack layout for /a/my-feature/**
|
|
135
137
|
├── [id].tsx # detail route
|
|
136
138
|
└── index.tsx # list route
|
|
137
139
|
```
|
|
@@ -142,7 +144,7 @@ The `tinycld/my-feature/` nesting gives the package a stable public API surface
|
|
|
142
144
|
|
|
143
145
|
### `settings-only` — service package
|
|
144
146
|
|
|
145
|
-
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
|
|
147
|
+
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/settings/**`.
|
|
146
148
|
|
|
147
149
|
<details>
|
|
148
150
|
<summary>Generated tree</summary>
|
|
@@ -172,7 +174,7 @@ The scaffolded `manifest.ts` is the single source of truth for what a package co
|
|
|
172
174
|
| Field | Meaning |
|
|
173
175
|
|---|---|
|
|
174
176
|
| `name`, `slug`, `version`, `description` | Identity. `slug` is the URL segment and the npm name's last segment. |
|
|
175
|
-
| `routes.directory` | Subpath (resolved through `package.json` exports) where
|
|
177
|
+
| `routes.directory` | Subpath (resolved through `package.json` exports) where the package's screens live. Generator re-exports each screen file under `app/a/(app)/<slug>/`. |
|
|
176
178
|
| `nav` | `{ label, icon, order, shortcut }` — sidebar entry for the org workspace. |
|
|
177
179
|
| `sidebar.component` | Subpath to the package's sidebar component, rendered when on its routes. |
|
|
178
180
|
| `provider.component` | Optional context provider mounted around the package's routes. |
|
|
@@ -180,7 +182,7 @@ The scaffolded `manifest.ts` is the single source of truth for what a package co
|
|
|
180
182
|
| `collections.register`, `collections.types` | Subpaths to the pbtsdb registration function and the schema types. |
|
|
181
183
|
| `seed.script` | Default-export async function called by the dev seeder for this package. |
|
|
182
184
|
| `server` | `{ package, module }` — relative dir + Go module path for the optional server extension. |
|
|
183
|
-
| `settings[]` | One entry per panel contributed under `/a
|
|
185
|
+
| `settings[]` | One entry per panel contributed under `/a/settings/<slug>`. |
|
|
184
186
|
|
|
185
187
|
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>/...`.
|
|
186
188
|
|
|
@@ -206,7 +208,7 @@ cd my-feature
|
|
|
206
208
|
pnpm exec tinycld-pkg check
|
|
207
209
|
```
|
|
208
210
|
|
|
209
|
-
Once linked, the app shell's generator wires your manifest in automatically: routes appear at `/a
|
|
211
|
+
Once linked, the app shell's generator wires your manifest in automatically: routes appear at `/a/my-feature/**`, the sidebar renders, the settings panel shows up, migrations get picked up by PocketBase. No further changes to the `tinycld` shell or `@tinycld/core` are needed.
|
|
210
212
|
|
|
211
213
|
> ⚠️ **`tinycld/metro.config.cjs` watches the workspace root**, but Expo's resolver caches package metadata at boot. If you add a new sibling while `pnpm run dev` is already running, restart it (Ctrl-C, then `pnpm run dev`) so the new member is picked up. CI is fine — it always starts fresh.
|
|
212
214
|
|
|
@@ -1,412 +1,46 @@
|
|
|
1
1
|
import { spawnSync } from 'node:child_process';
|
|
2
|
-
import { cpSync, existsSync, mkdirSync, readdirSync,
|
|
2
|
+
import { cpSync, existsSync, mkdirSync, readdirSync, statSync } from 'node:fs';
|
|
3
3
|
import { dirname, join, relative } from 'node:path';
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
5
|
// All feature members that exist in the ecosystem. The workspace manifest lists
|
|
6
6
|
// ALL of them (npm tolerates absent dirs), so a partial checkout still installs.
|
|
7
7
|
// But --assemble-only CLONES only app+core+requested — we do NOT force-clone all.
|
|
8
|
-
const ALL_FEATURES = [
|
|
8
|
+
const ALL_FEATURES = [
|
|
9
|
+
'contacts',
|
|
10
|
+
'mail',
|
|
11
|
+
'calendar',
|
|
12
|
+
'drive',
|
|
13
|
+
'calc',
|
|
14
|
+
'text',
|
|
15
|
+
'google-takeout-import',
|
|
16
|
+
'boards',
|
|
17
|
+
];
|
|
9
18
|
// The `tinycld` member is the one always-cloned repo (tinycld/tinycld): it is
|
|
10
19
|
// the merged app shell + core. assembleWorkspace seeds it directly, pinnable via
|
|
11
20
|
// tinycldRef. Both @tinycld/core (at tinycld/core) and @tinycld/package-scripts
|
|
12
21
|
// (the tinycld-pkg CLI, at tinycld/package-scripts) live NESTED inside it, so
|
|
13
22
|
// they arrive with the single clone and are never cloned separately.
|
|
14
|
-
// Listed in the manifest workspaces array (everything that could exist). The
|
|
15
|
-
// nested member paths tinycld/core and tinycld/package-scripts are valid npm
|
|
16
|
-
// workspace entries that resolve inside the one cloned `tinycld` repo.
|
|
17
|
-
const ALL_MEMBERS = ['tinycld', 'tinycld/core', 'tinycld/package-scripts', ...ALL_FEATURES];
|
|
18
|
-
// pnpm version pinned via the package.json "packageManager" field so corepack
|
|
19
|
-
// resolves the same pnpm everywhere (local, CI, EAS). Bump in lockstep with the
|
|
20
|
-
// workspace's committed packageManager.
|
|
21
|
-
const PNPM_VERSION = '11.3.0';
|
|
22
|
-
// Framework / native / styling version pins, written to package-versions.json and
|
|
23
|
-
// transcribed into the pnpm `overrides:` block. The in-app OTA rebuild installs
|
|
24
|
-
// with `pnpm install --no-frozen-lockfile` (admin package editing changes the
|
|
25
|
-
// FEATURE set, so the lockfile can't be frozen), which would otherwise re-resolve
|
|
26
|
-
// every caret/tilde range and drift these deps past the device's embedded native
|
|
27
|
-
// binary — a native-module mismatch breaks the OTA bundle, a uniwind/tailwindcss
|
|
28
|
-
// bump recompiles classNames differently (app-wide visual drift). Overrides force
|
|
29
|
-
// the embedded-binary versions regardless of --no-frozen-lockfile while leaving
|
|
30
|
-
// feature packages free. Bump in lockstep with each native (EAS) build.
|
|
31
|
-
const FRAMEWORK_OVERRIDES = {
|
|
32
|
-
expo: '55.0.26',
|
|
33
|
-
react: '19.2.0',
|
|
34
|
-
'react-dom': '19.2.0',
|
|
35
|
-
'react-native': '0.83.6',
|
|
36
|
-
'react-native-reanimated': '4.2.1',
|
|
37
|
-
'react-native-worklets': '0.7.4',
|
|
38
|
-
'react-native-gesture-handler': '3.0.1',
|
|
39
|
-
'react-native-safe-area-context': '5.6.2',
|
|
40
|
-
'react-native-screens': '4.23.0',
|
|
41
|
-
'react-native-svg': '15.15.3',
|
|
42
|
-
'react-native-web': '0.21.2',
|
|
43
|
-
'expo-router': '55.0.16',
|
|
44
|
-
'expo-image': '55.0.11',
|
|
45
|
-
'expo-image-picker': '55.0.20',
|
|
46
|
-
'@sentry/react-native': '7.11.0',
|
|
47
|
-
uniwind: '1.8.0',
|
|
48
|
-
tailwindcss: '4.3.0',
|
|
49
|
-
'tailwind-variants': '0.1.20',
|
|
50
|
-
'tailwind-merge': '3.6.0',
|
|
51
|
-
'@gluestack-ui/utils': '5.0.5-alpha.0',
|
|
52
|
-
'@shopify/flash-list': '2.0.2',
|
|
53
|
-
'lucide-react-native': '1.17.0',
|
|
54
|
-
'react-hook-form': '7.77.0',
|
|
55
|
-
'@tanstack/db': '0.8.5',
|
|
56
|
-
// Must move in lockstep with @tanstack/db above: react-db calls internals of
|
|
57
|
-
// a specific db version, so pinning one and letting the other float produces
|
|
58
|
-
// a workspace that installs clean and throws on first render
|
|
59
|
-
// ("(0, t.isCollection) is not a function" — every screen dead).
|
|
60
|
-
'@tanstack/react-db': '0.3.5',
|
|
61
|
-
// Same lockstep, and it is NOT optional just because nothing imports it
|
|
62
|
-
// directly: query-db-collection reaches into @tanstack/db's internals
|
|
63
|
-
// (getLoadSubsetDemandKey). Left unpinned it floats to a release built
|
|
64
|
-
// against a newer db than the pin above, and the workspace dies at import
|
|
65
|
-
// with "does not provide an export named 'getLoadSubsetDemandKey'" — which
|
|
66
|
-
// is exactly how it failed in CI while every developer's machine, holding
|
|
67
|
-
// an older resolved copy, stayed green.
|
|
68
|
-
'@tanstack/query-db-collection': '1.2.10',
|
|
69
|
-
'@tanstack/react-query': '5.101.0',
|
|
70
|
-
// Not a version pin: a git fork. Upstream drax has unmerged fixes the board
|
|
71
|
-
// drag depends on, so every workspace must resolve to the fork or the
|
|
72
|
-
// kanban drag regresses. The fork commits its built lib/ and declares no
|
|
73
|
-
// prepare script, so this needs no allowBuilds entry — pnpm only blocks git
|
|
74
|
-
// deps that would run a build script on install.
|
|
75
|
-
'react-native-drax': 'github:nathanstitt/react-native-drax#b863d89a70c73454d7f3495bf79e382586495fa4',
|
|
76
|
-
};
|
|
77
|
-
// The documentation key carried in package-versions.json (ignored by the YAML
|
|
78
|
-
// generators — it isn't a valid package name). Kept in sync with the Go reader's
|
|
79
|
-
// delete("//") in rebuild_assemble.go.
|
|
80
|
-
const OVERRIDES_DOC = 'Source of truth for the pnpm `overrides:` block in pnpm-workspace.yaml. Both generators (this file and the Go writePnpmWorkspaceYAML in the OTA rebuild) read these pins so the dev workspace and the OTA build pin identically. See FRAMEWORK_OVERRIDES in assemble-workspace.ts for why. Bump in lockstep with each native (EAS) build.';
|
|
81
|
-
// renderOverridesBlock formats FRAMEWORK_OVERRIDES as a pnpm `overrides:` YAML
|
|
82
|
-
// block, sorted for a stable diff. Scoped names (leading @) are single-quoted so
|
|
83
|
-
// YAML doesn't read them as anchors; plain names are bare. Mirrors the Go
|
|
84
|
-
// renderOverridesBlock so both generators emit byte-identical blocks.
|
|
85
|
-
function renderOverridesBlock() {
|
|
86
|
-
const lines = ['overrides:'];
|
|
87
|
-
for (const name of Object.keys(FRAMEWORK_OVERRIDES).sort()) {
|
|
88
|
-
const key = name.startsWith('@') ? `'${name}'` : name;
|
|
89
|
-
lines.push(` ${key}: ${FRAMEWORK_OVERRIDES[name]}`);
|
|
90
|
-
}
|
|
91
|
-
return lines.join('\n');
|
|
92
|
-
}
|
|
93
23
|
/**
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
* linked by pnpm (→ "No manifest found"). Self-registering it here closes that
|
|
102
|
-
* gap without requiring every package to be hardcoded in ALL_FEATURES.
|
|
24
|
+
* Run the workspace root-writer that ships INSIDE the cloned tinycld repo.
|
|
25
|
+
* Bootstrap deliberately knows nothing about the root files' contents — not
|
|
26
|
+
* the vendor version pins (tinycld/core/package-versions.json) and not the
|
|
27
|
+
* file formats. A vendor bump or format change is a tinycld commit, never a
|
|
28
|
+
* bootstrap release. Node >=24 (our engines floor) executes the TypeScript
|
|
29
|
+
* directly via native type stripping; the script is self-contained by
|
|
30
|
+
* contract, so no install has to have happened yet.
|
|
103
31
|
*/
|
|
104
|
-
function
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
32
|
+
function delegateWorkspaceRoot(root) {
|
|
33
|
+
const script = join(root, 'tinycld', 'scripts', 'write-workspace-root.ts');
|
|
34
|
+
const tinycldDir = join(root, 'tinycld');
|
|
35
|
+
if (!existsSync(script)) {
|
|
36
|
+
throw new Error(`${script} not found — the tinycld checkout at ${tinycldDir} predates core-owned ` +
|
|
37
|
+
'workspace roots — update it (git pull) or pass a newer tinycldRef; or use an ' +
|
|
38
|
+
'older @tinycld/bootstrap that still writes root files itself.');
|
|
108
39
|
}
|
|
109
|
-
|
|
110
|
-
|
|
40
|
+
const r = spawnSync(process.execPath, [script], { cwd: root, stdio: 'inherit' });
|
|
41
|
+
if (r.status !== 0) {
|
|
42
|
+
throw new Error(`write-workspace-root failed (exit ${r.status ?? 'signal'}) — see output above`);
|
|
111
43
|
}
|
|
112
|
-
return entries.filter((name) => {
|
|
113
|
-
if (name === 'node_modules' || name.startsWith('.'))
|
|
114
|
-
return false;
|
|
115
|
-
const dir = join(root, name);
|
|
116
|
-
try {
|
|
117
|
-
if (!statSync(dir).isDirectory())
|
|
118
|
-
return false;
|
|
119
|
-
}
|
|
120
|
-
catch {
|
|
121
|
-
return false;
|
|
122
|
-
}
|
|
123
|
-
const hasManifest = existsSync(join(dir, 'manifest.ts')) || existsSync(join(dir, 'manifest.js'));
|
|
124
|
-
return existsSync(join(dir, 'package.json')) && hasManifest;
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
/**
|
|
128
|
-
* pnpm-workspace.yaml contents: member discovery list + the settings the
|
|
129
|
-
* ecosystem depends on. node-linker=hoisted reproduces npm's flat node_modules
|
|
130
|
-
* so feature siblings (peerDeps-only, no node_modules of their own) resolve
|
|
131
|
-
* react/expo/etc. up to the root, and Metro/tsc resolution work unchanged.
|
|
132
|
-
* pnpm 10+ reads these keys from THIS file, not .npmrc.
|
|
133
|
-
*
|
|
134
|
-
* `dir` is the workspace root: any manifest-bearing member already on disk but
|
|
135
|
-
* not in ALL_MEMBERS (a CI- or custom-package checkout) is unioned into the
|
|
136
|
-
* `packages:` list so pnpm links what the generator discovers. This list is the
|
|
137
|
-
* source of truth pnpm reads — unlike the package.json `workspaces` hint.
|
|
138
|
-
*/
|
|
139
|
-
function pnpmWorkspaceYaml(dir) {
|
|
140
|
-
const allMembers = [...new Set([...ALL_MEMBERS, ...discoverPresentMembers(dir)])];
|
|
141
|
-
const members = allMembers.map((m) => ` - ${m}`).join('\n');
|
|
142
|
-
return [
|
|
143
|
-
'nodeLinker: hoisted',
|
|
144
|
-
'linkWorkspacePackages: true',
|
|
145
|
-
'strictPeerDependencies: false',
|
|
146
|
-
'enablePrePostScripts: true',
|
|
147
|
-
'',
|
|
148
|
-
'# pnpm 11 ships a default minimumReleaseAge supply-chain gate (~24h) that',
|
|
149
|
-
'# rejects very freshly-published versions. The @tinycld/* libraries are',
|
|
150
|
-
'# first-party and released in lockstep with these members, so a same-day',
|
|
151
|
-
'# pbtsdb (or other @tinycld dep) bump must install immediately rather than',
|
|
152
|
-
'# blocking install/CI for a day. Exclude them from the gate; third-party',
|
|
153
|
-
'# packages still get the freshness window.',
|
|
154
|
-
'minimumReleaseAgeExclude:',
|
|
155
|
-
' - pbtsdb',
|
|
156
|
-
" - '@tinycld/*'",
|
|
157
|
-
'',
|
|
158
|
-
'packages:',
|
|
159
|
-
members,
|
|
160
|
-
'',
|
|
161
|
-
'# Build-script approvals (pnpm blocks dependency build scripts by default).',
|
|
162
|
-
'allowBuilds:',
|
|
163
|
-
' esbuild: true',
|
|
164
|
-
" '@sentry/cli': true",
|
|
165
|
-
'',
|
|
166
|
-
'# Framework/native/styling version pins — see package-versions.json (the',
|
|
167
|
-
'# source of truth) and FRAMEWORK_OVERRIDES in @tinycld/bootstrap. These keep',
|
|
168
|
-
"# the OTA rebuild's --no-frozen-lockfile install from drifting these deps off",
|
|
169
|
-
'# the embedded native binary.',
|
|
170
|
-
renderOverridesBlock(),
|
|
171
|
-
'',
|
|
172
|
-
].join('\n');
|
|
173
|
-
}
|
|
174
|
-
/**
|
|
175
|
-
* .watchmanconfig contents: the directories watchman must NOT crawl or subscribe
|
|
176
|
-
* to. The motivating failure is native build artifacts — tinycld/ios (Pods) and
|
|
177
|
-
* tinycld/android (.gradle, .cxx, build/) are multi-GB and churn on every native
|
|
178
|
-
* build, overflowing the fsevents kernel queue ("MustScanSubDirs UserDropped")
|
|
179
|
-
* and forcing slow recrawls that stall Metro starts.
|
|
180
|
-
*
|
|
181
|
-
* CRITICAL: the root node_modules is NOT ignored. Under node-linker=hoisted it's
|
|
182
|
-
* the flat store Metro resolves EVERY dependency from (incl. expo-router/entry,
|
|
183
|
-
* the app entry point); hiding it from watchman makes Metro's file map miss those
|
|
184
|
-
* files → "Unable to resolve module ./node_modules/...". Only node_modules/.cache
|
|
185
|
-
* (Metro's transform cache, never a resolvable path) is excluded. Each MEMBER's
|
|
186
|
-
* own node_modules IS ignored — under hoisting those are near-empty (deps resolve
|
|
187
|
-
* up to the root store), so Metro never reads them and they're pure crawl cost.
|
|
188
|
-
*
|
|
189
|
-
* Ignore_dirs matches path prefixes relative to the watch root, so the bare
|
|
190
|
-
* ".git" covers only the root-level one and every member's .git is listed
|
|
191
|
-
* explicitly. Listing an ignore_dir that doesn't exist is harmless — watchman
|
|
192
|
-
* skips absent paths.
|
|
193
|
-
*
|
|
194
|
-
* NON_MEMBER_DIRS are sibling dirs that aren't workspace members (so they're
|
|
195
|
-
* absent from the member union above) but routinely exist in a dev checkout and
|
|
196
|
-
* carry their own node_modules/.git: bootstrap (cloned for development), and the
|
|
197
|
-
* utils/web ecosystem tooling. Their node_modules/.git/dist are excluded too —
|
|
198
|
-
* none are part of the Metro app graph.
|
|
199
|
-
*/
|
|
200
|
-
const NON_MEMBER_DIRS = ['bootstrap', 'utils', 'web'];
|
|
201
|
-
function watchmanConfig(dir) {
|
|
202
|
-
const members = [...new Set([...ALL_MEMBERS, ...discoverPresentMembers(dir)])];
|
|
203
|
-
const memberDirs = members.flatMap((m) => [`${m}/node_modules`, `${m}/.git`]);
|
|
204
|
-
const testResultDirs = members.filter((m) => !m.startsWith('tinycld')).map((m) => `${m}/test-results`);
|
|
205
|
-
const nonMemberDirs = NON_MEMBER_DIRS.flatMap((d) => [`${d}/node_modules`, `${d}/.git`, `${d}/dist`]);
|
|
206
|
-
const ignoreDirs = [
|
|
207
|
-
// Root-level .git only. The root node_modules is deliberately NOT ignored:
|
|
208
|
-
// under node-linker=hoisted it's the flat store Metro resolves every dep
|
|
209
|
-
// from (incl. expo-router/entry), so hiding it breaks module resolution.
|
|
210
|
-
'.git',
|
|
211
|
-
// Metro's own transform cache under the root store — churns every build,
|
|
212
|
-
// never a resolvable module path.
|
|
213
|
-
'node_modules/.cache',
|
|
214
|
-
...memberDirs,
|
|
215
|
-
...nonMemberDirs,
|
|
216
|
-
// App-shell native + generated output that churns on builds. ios/android
|
|
217
|
-
// are multi-GB and the original motivation for this file.
|
|
218
|
-
'tinycld/ios',
|
|
219
|
-
'tinycld/android',
|
|
220
|
-
'tinycld/modules/app-updater/android/build',
|
|
221
|
-
'tinycld/modules/app-updater/ios/build',
|
|
222
|
-
'tinycld/.expo',
|
|
223
|
-
'tinycld/dist',
|
|
224
|
-
'tinycld/server/pb_data',
|
|
225
|
-
'tinycld/server/pb_test_data',
|
|
226
|
-
'tinycld/test-results',
|
|
227
|
-
'tinycld/playwright-report',
|
|
228
|
-
...testResultDirs,
|
|
229
|
-
];
|
|
230
|
-
const config = {
|
|
231
|
-
enable_parallel_crawl: true,
|
|
232
|
-
// fsevents_try_resync is FALSE — watchman's own default since Dec 2021.
|
|
233
|
-
// When fsevents drops events (kFSEventStreamEventFlagUserDropped, common
|
|
234
|
-
// under the ~110k-file root store on a `pnpm install` / `git checkout`
|
|
235
|
-
// burst), resync-from-journal usually fails on macOS and falls back to a
|
|
236
|
-
// full recrawl — that's the 60s "syncToNow: timed out waiting for cookie
|
|
237
|
-
// file" stall on Metro start. Leaving it false skips the doomed resync.
|
|
238
|
-
fsevents_try_resync: false,
|
|
239
|
-
// Latency passed to FSEventStreamCreate (seconds). Higher = the OS batches
|
|
240
|
-
// more change notifications per wakeup, so a burst overflows the kernel
|
|
241
|
-
// queue less. Measured on a fast SSD here, an 8k-file burst (a `pnpm
|
|
242
|
-
// install` / `git checkout`) still drops events at 0.1/0.25, goes clean at
|
|
243
|
-
// 0.5, and survives even a 20k-file burst at 1.0. We ship 1.0 because this
|
|
244
|
-
// default lands on unknown hardware: a slow / encrypted / cloud-synced
|
|
245
|
-
// volume drains the fsevents queue more slowly, so it drops at a FAR
|
|
246
|
-
// smaller burst than an SSD — the extra latency is the margin those
|
|
247
|
-
// machines need. The cost is ~1s of detection lag before HMR fires per
|
|
248
|
-
// save; on a slow disk (where rebuilds are already slower) that's
|
|
249
|
-
// proportionally minor, and stability beats shaving it. (default is 0.01.)
|
|
250
|
-
fsevents_latency: 1.0,
|
|
251
|
-
ignore_dirs: [...new Set(ignoreDirs)],
|
|
252
|
-
};
|
|
253
|
-
return `${JSON.stringify(config, null, 4)}\n`;
|
|
254
|
-
}
|
|
255
|
-
/**
|
|
256
|
-
* Write (or merge into) the workspace-root coordination files in `dir`:
|
|
257
|
-
* package.json, pnpm-workspace.yaml, and scripts/link-members.ts.
|
|
258
|
-
*
|
|
259
|
-
* The workspace is a pnpm workspace. Member discovery + pnpm settings live in
|
|
260
|
-
* pnpm-workspace.yaml (always rewritten — bootstrap owns the member list).
|
|
261
|
-
* package.json carries the pinned packageManager, the tsx devDep the postinstall
|
|
262
|
-
* needs, and the postinstall script (link-members + generator). When a
|
|
263
|
-
* package.json already exists (real workspace checkout), its other fields are
|
|
264
|
-
* preserved and only the bootstrap-owned bits are enforced.
|
|
265
|
-
*
|
|
266
|
-
* A legacy npm `workspaces` array is also written as a monorepo-detection HINT
|
|
267
|
-
* for external tooling (EAS/expo archiver keys off it, not pnpm-workspace.yaml).
|
|
268
|
-
* pnpm ignores it when pnpm-workspace.yaml is present.
|
|
269
|
-
*/
|
|
270
|
-
export function writeWorkspaceManifest(dir) {
|
|
271
|
-
const pkgPath = join(dir, 'package.json');
|
|
272
|
-
let existing = {};
|
|
273
|
-
if (existsSync(pkgPath)) {
|
|
274
|
-
try {
|
|
275
|
-
existing = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
276
|
-
}
|
|
277
|
-
catch {
|
|
278
|
-
// Unparseable existing file — start from defaults
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
const existingScripts = typeof existing.scripts === 'object' && existing.scripts !== null
|
|
282
|
-
? existing.scripts
|
|
283
|
-
: {};
|
|
284
|
-
const existingDevDeps = typeof existing.devDependencies === 'object' && existing.devDependencies !== null
|
|
285
|
-
? existing.devDependencies
|
|
286
|
-
: {};
|
|
287
|
-
const pkg = {
|
|
288
|
-
name: '@tinycld/workspace',
|
|
289
|
-
version: '0.0.0',
|
|
290
|
-
private: true,
|
|
291
|
-
type: 'module',
|
|
292
|
-
...existing,
|
|
293
|
-
packageManager: `pnpm@${PNPM_VERSION}`,
|
|
294
|
-
// Monorepo-detection hint for external tooling (see doc comment); pnpm
|
|
295
|
-
// itself reads members from pnpm-workspace.yaml and ignores this. Union
|
|
296
|
-
// in any manifest-bearing member already on disk so the hint matches the
|
|
297
|
-
// authoritative pnpm-workspace.yaml member list.
|
|
298
|
-
workspaces: [...new Set([...ALL_MEMBERS, ...discoverPresentMembers(dir)])],
|
|
299
|
-
scripts: {
|
|
300
|
-
...existingScripts,
|
|
301
|
-
// link-members runs FIRST so the @tinycld/<member> symlinks (notably
|
|
302
|
-
// @tinycld/core) exist before the generator's package-build step
|
|
303
|
-
// (text's webview-editor build.ts does a bare `import '@tinycld/core/...'`
|
|
304
|
-
// resolved by package name, which fails on a CLEAN install if the
|
|
305
|
-
// symlink isn't there yet). It then runs AGAIN after generate to add
|
|
306
|
-
// the @tinycld/app-generated link (whose target, tinycld/lib/generated,
|
|
307
|
-
// only exists once generate has run); link-members is idempotent and
|
|
308
|
-
// skips app-generated when absent, so the first pass is harmless and
|
|
309
|
-
// the second completes the graph. All app scripts run from tinycld/.
|
|
310
|
-
postinstall: 'tsx scripts/link-members.ts && cd tinycld && pnpm run packages:generate && cd .. && tsx scripts/link-members.ts && cd tinycld && pnpm run assets:copy-pdfjs',
|
|
311
|
-
},
|
|
312
|
-
devDependencies: {
|
|
313
|
-
...existingDevDeps,
|
|
314
|
-
tsx: '^4.21.0',
|
|
315
|
-
},
|
|
316
|
-
};
|
|
317
|
-
writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 4)}\n`);
|
|
318
|
-
// pnpm-workspace.yaml is the source of truth for members + settings — always
|
|
319
|
-
// rewrite it (unlike package.json, no human-owned fields live here).
|
|
320
|
-
writeFileSync(join(dir, 'pnpm-workspace.yaml'), pnpmWorkspaceYaml(dir));
|
|
321
|
-
// package-versions.json holds the version pins transcribed into the YAML
|
|
322
|
-
// `overrides:` block above. It's a standalone file (not just inline in the
|
|
323
|
-
// YAML) because the Go OTA-rebuild generator reads it to emit the same block
|
|
324
|
-
// on the server, where bootstrap never runs — so dev and OTA pin identically.
|
|
325
|
-
// Always rewritten: the pins are bootstrap-owned, no human fields live here.
|
|
326
|
-
const overridesJson = { '//': OVERRIDES_DOC, ...FRAMEWORK_OVERRIDES };
|
|
327
|
-
writeFileSync(join(dir, 'package-versions.json'), `${JSON.stringify(overridesJson, null, 4)}\n`);
|
|
328
|
-
// .watchmanconfig is derived from the member list, like pnpm-workspace.yaml —
|
|
329
|
-
// always rewrite so it self-heals and can't drift to a stale ignore list (a
|
|
330
|
-
// hand-maintained one kept missing the hoisted root node_modules, which is
|
|
331
|
-
// what stalls Metro starts). No human-owned fields live here.
|
|
332
|
-
writeFileSync(join(dir, '.watchmanconfig'), watchmanConfig(dir));
|
|
333
|
-
// Minimal .npmrc: all pnpm settings live in pnpm-workspace.yaml (pnpm 10+
|
|
334
|
-
// reads them there, not from .npmrc). Only written when absent (don't
|
|
335
|
-
// clobber a real checkout's).
|
|
336
|
-
const npmrcPath = join(dir, '.npmrc');
|
|
337
|
-
if (!existsSync(npmrcPath)) {
|
|
338
|
-
writeFileSync(npmrcPath, '# pnpm settings live in pnpm-workspace.yaml (pnpm 10+ reads them there).\n');
|
|
339
|
-
}
|
|
340
|
-
writeRootBiomeConfig(dir);
|
|
341
|
-
}
|
|
342
|
-
// Re-root a biome glob written against the canonical's dir (<root>/tinycld/) so
|
|
343
|
-
// it means the same thing one level up at the workspace root. `**`-anchored globs
|
|
344
|
-
// already match at any depth; only bare relative segments need the tinycld/ prefix.
|
|
345
|
-
function rerootBiomeGlob(glob) {
|
|
346
|
-
const negated = glob.startsWith('!');
|
|
347
|
-
const body = negated ? glob.slice(1) : glob;
|
|
348
|
-
if (body.startsWith('**') || body.startsWith('/'))
|
|
349
|
-
return glob;
|
|
350
|
-
const rerooted = `tinycld/${body}`;
|
|
351
|
-
return negated ? `!${rerooted}` : rerooted;
|
|
352
|
-
}
|
|
353
|
-
function rerootBiomePluginPath(p) {
|
|
354
|
-
if (p.startsWith('/'))
|
|
355
|
-
return p;
|
|
356
|
-
return `./tinycld/${p.replace(/^\.\//, '')}`;
|
|
357
|
-
}
|
|
358
|
-
/**
|
|
359
|
-
* Write the workspace-root biome.json. Biome searches only UPWARD for config,
|
|
360
|
-
* and the canonical biome.json lives at <root>/tinycld/ — a SIBLING of the
|
|
361
|
-
* feature members, never an ancestor. Without a `root: true` config above the
|
|
362
|
-
* members, running biome from inside a sibling (or via the editor/LSP) finds
|
|
363
|
-
* nothing and falls back to biome's built-in defaults, flooding output with
|
|
364
|
-
* bogus reformatting.
|
|
365
|
-
*
|
|
366
|
-
* This config must NOT use `extends: ['./tinycld/biome.json']`: Biome 2.5.0
|
|
367
|
-
* silently DROPS the entire `plugins` array from any config reached through a
|
|
368
|
-
* file-path `extends` (biome #8488, only partially fixed by #8524, which covers
|
|
369
|
-
* `extends: "//"` but not a root extending a sibling file). Rules still load, so
|
|
370
|
-
* the GritQL pbtsdb guards would just never run. So the root config must INLINE
|
|
371
|
-
* the canonical's rules + plugins. The generator (tinycld/scripts/generate.ts,
|
|
372
|
-
* run by every install's postinstall) is the authoritative writer and re-derives
|
|
373
|
-
* this file from the canonical each install; this seed only has to be correct for
|
|
374
|
-
* the window between `--assemble-only` and the first install.
|
|
375
|
-
*
|
|
376
|
-
* Bootstrap can only inline when the canonical is already on disk (a re-assemble
|
|
377
|
-
* onto an existing workspace, or after the tinycld clone). On a fresh assemble the
|
|
378
|
-
* tinycld repo isn't cloned yet, so we fall back to a minimal `root: true` seed
|
|
379
|
-
* with no plugins — harmless because nothing is installed to lint yet, and the
|
|
380
|
-
* first install's generator immediately replaces it with the full inlined config.
|
|
381
|
-
*
|
|
382
|
-
* `files.includes`/plugin paths are re-rooted (the canonical writes them relative
|
|
383
|
-
* to tinycld/; inlining moves them up one dir). `vcs.root` points at tinycld/, not
|
|
384
|
-
* the workspace root: the canonical relies on tinycld/.gitignore to exclude
|
|
385
|
-
* generated/build artifacts, and every invocation under the workspace root
|
|
386
|
-
* resolves THIS config as the root and inherits its vcs settings. A fresh
|
|
387
|
-
* workspace root has no .gitignore (and isn't a git repo), so anchoring vcs there
|
|
388
|
-
* would error "couldn't find an ignore file".
|
|
389
|
-
*/
|
|
390
|
-
function writeRootBiomeConfig(root) {
|
|
391
|
-
const canonicalPath = join(root, 'tinycld', 'biome.json');
|
|
392
|
-
const config = existsSync(canonicalPath)
|
|
393
|
-
? (() => {
|
|
394
|
-
const canonical = JSON.parse(readFileSync(canonicalPath, 'utf8'));
|
|
395
|
-
const c = { ...canonical, root: true };
|
|
396
|
-
delete c.extends;
|
|
397
|
-
if (c.files?.includes) {
|
|
398
|
-
c.files = { ...c.files, includes: c.files.includes.map(rerootBiomeGlob) };
|
|
399
|
-
}
|
|
400
|
-
if (Array.isArray(c.plugins)) {
|
|
401
|
-
c.plugins = c.plugins.map((entry) => typeof entry === 'string'
|
|
402
|
-
? rerootBiomePluginPath(entry)
|
|
403
|
-
: { ...entry, path: rerootBiomePluginPath(entry.path) });
|
|
404
|
-
}
|
|
405
|
-
return c;
|
|
406
|
-
})()
|
|
407
|
-
: { $schema: 'https://biomejs.dev/schemas/2.5.0/schema.json', root: true };
|
|
408
|
-
config.vcs = { enabled: true, clientKind: 'git', useIgnoreFile: true, root: 'tinycld' };
|
|
409
|
-
writeFileSync(join(root, 'biome.json'), `${JSON.stringify(config, null, 4)}\n`);
|
|
410
44
|
}
|
|
411
45
|
/**
|
|
412
46
|
* Resolve the workspace-template dir relative to this module. Published builds
|
|
@@ -466,13 +100,17 @@ function realClone(url, dest, ref) {
|
|
|
466
100
|
return r.status === 0;
|
|
467
101
|
}
|
|
468
102
|
/**
|
|
469
|
-
* Assemble a workspace skeleton at opts.root:
|
|
470
|
-
* (
|
|
471
|
-
*
|
|
472
|
-
*
|
|
473
|
-
*
|
|
474
|
-
*
|
|
475
|
-
*
|
|
103
|
+
* Assemble a workspace skeleton at opts.root: clone ONLY the tinycld member
|
|
104
|
+
* (merged app shell + core) + the explicitly-requested feature members
|
|
105
|
+
* (skipping any already present — e.g. a CI-checked-out member, where the
|
|
106
|
+
* merged repo is checked out into the tinycld/ slot), then lay down the root
|
|
107
|
+
* scaffolding bootstrap still owns (copyWorkspaceTemplate: tinycld.packages.ts,
|
|
108
|
+
* vitest.config.ts, tests/ stubs, version files) and delegate everything
|
|
109
|
+
* content-bearing about the workspace root (package.json, pnpm-workspace.yaml,
|
|
110
|
+
* package-versions.json, .watchmanconfig, biome.json, .npmrc) to the
|
|
111
|
+
* root-writer script that ships INSIDE the cloned tinycld repo. There is NO
|
|
112
|
+
* workspace meta-repo clone: bootstrap only clones members, never writes
|
|
113
|
+
* vendor version pins or root-file formats itself.
|
|
476
114
|
* Unknown member names throw. Returns the members that ended up present. Does
|
|
477
115
|
* NOT run the install (the caller / CI controls that — e.g. `pnpm install` or a
|
|
478
116
|
* frozen-lockfile install for reproducible builds).
|
|
@@ -486,14 +124,6 @@ export function assembleWorkspace(opts) {
|
|
|
486
124
|
}
|
|
487
125
|
const repoBase = opts.repoBase ?? 'git@github.com:tinycld';
|
|
488
126
|
const clone = opts.clone ?? realClone;
|
|
489
|
-
const present = [];
|
|
490
|
-
// Write the canonical root manifest (workspaces list + postinstall), then
|
|
491
|
-
// lay down the root scaffolding bootstrap owns (tinycld.packages.ts,
|
|
492
|
-
// vitest.config.ts, tests/ stubs, .node-version, .go-version). Both are
|
|
493
|
-
// non-destructive: anything already present (a real workspace checkout, or
|
|
494
|
-
// a CI lane that supplied its own) is left as-is.
|
|
495
|
-
writeWorkspaceManifest(opts.root);
|
|
496
|
-
copyWorkspaceTemplate(opts.root);
|
|
497
127
|
// Build the clone set keyed by member NAME so a member passed both bare and
|
|
498
128
|
// with an @ref dedupes to one clone (the ref-bearing entry wins). The
|
|
499
129
|
// `tinycld` repo (merged app shell + core, with core + package-scripts
|
|
@@ -506,6 +136,7 @@ export function assembleWorkspace(opts) {
|
|
|
506
136
|
if (ref !== undefined || !refByName.has(name))
|
|
507
137
|
refByName.set(name, ref);
|
|
508
138
|
}
|
|
139
|
+
const present = [];
|
|
509
140
|
for (const [name, ref] of refByName) {
|
|
510
141
|
const dest = join(opts.root, name);
|
|
511
142
|
if (existsSync(join(dest, '.git')) || existsSync(join(dest, 'package.json'))) {
|
|
@@ -515,6 +146,14 @@ export function assembleWorkspace(opts) {
|
|
|
515
146
|
if (clone(`${repoBase}/${name}.git`, dest, ref))
|
|
516
147
|
present.push(name);
|
|
517
148
|
}
|
|
149
|
+
// Root scaffolding AFTER cloning: the static template files bootstrap
|
|
150
|
+
// still owns (never overwrites), then the cloned repo's own root-writer
|
|
151
|
+
// for everything content-bearing. Skip delegation when tinycld itself
|
|
152
|
+
// failed to clone — the caller (runAssembleOnly) raises the clearer
|
|
153
|
+
// "failed to clone required member" error.
|
|
154
|
+
copyWorkspaceTemplate(opts.root);
|
|
155
|
+
if (present.includes('tinycld'))
|
|
156
|
+
delegateWorkspaceRoot(opts.root);
|
|
518
157
|
return present;
|
|
519
158
|
}
|
|
520
159
|
//# sourceMappingURL=assemble-workspace.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assemble-workspace.js","sourceRoot":"","sources":["../src/assemble-workspace.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"assemble-workspace.js","sourceRoot":"","sources":["../src/assemble-workspace.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -24,13 +24,5 @@
|
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"@tinycld/package-scripts": "*"
|
|
27
|
-
},
|
|
28
|
-
"peerDependencies": {
|
|
29
|
-
"react": ">=19",
|
|
30
|
-
"react-native": ">=0.83",
|
|
31
|
-
"expo-router": ">=55.0.0",
|
|
32
|
-
"pbtsdb": ">=0.6.3",
|
|
33
|
-
"@tanstack/react-db": ">=0.3",
|
|
34
|
-
"@tanstack/db": ">=0.8"
|
|
35
27
|
}
|
|
36
28
|
}
|
|
@@ -4,7 +4,10 @@ import type { createCollection } from 'pbtsdb/core'
|
|
|
4
4
|
import { BasicIndex } from 'pbtsdb/core'
|
|
5
5
|
import type { {{PKG_PASCAL}}Schema } from './types'
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
// Replace (not intersect) the generated entries for this package's own
|
|
8
|
+
// collections — a plain intersection would merge each overlapping entry
|
|
9
|
+
// field-wise, letting a generated `any` absorb any typed override.
|
|
10
|
+
type MergedSchema = Omit<Schema, keyof {{PKG_PASCAL}}Schema> & {{PKG_PASCAL}}Schema
|
|
8
11
|
|
|
9
12
|
// Collections contributed by this package. Core calls this during pbtsdb
|
|
10
13
|
// bootstrap; the returned object's keys become top-level keys on the app's
|
|
@@ -13,12 +16,25 @@ export function registerCollections(
|
|
|
13
16
|
newCollection: ReturnType<typeof createCollection<MergedSchema>>,
|
|
14
17
|
_core: CoreStores
|
|
15
18
|
) {
|
|
19
|
+
// Hoisted rather than inlined: an inline `collectionOptions` object literal
|
|
20
|
+
// defeats inference of `alwaysFetchRelations` against `relations`, typing every
|
|
21
|
+
// expand path as `never`.
|
|
22
|
+
const indexing = {
|
|
23
|
+
autoIndex: 'eager' as const,
|
|
24
|
+
defaultIndexType: BasicIndex,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// `relations` declares where PocketBase's expanded records are filed. Rows
|
|
28
|
+
// never carry `expand`; read a relation from its own collection, with
|
|
29
|
+
// `materialize()` in a select, a join, or `collection.get(id)`.
|
|
30
|
+
// relations: { owner: _core.users },
|
|
31
|
+
// Add `alwaysFetchRelations: ['owner']` only when this collection is the
|
|
32
|
+
// sole path by which rows enter an ON-DEMAND target — an eager target syncs
|
|
33
|
+
// itself, so fetching its relation only adds payload. For one query,
|
|
34
|
+
// use `collection.fetchRelations('owner')` instead.
|
|
16
35
|
const {{PKG_SNAKE}}_items = newCollection('{{PKG_SNAKE}}_items', {
|
|
17
36
|
omitOnInsert: ['created', 'updated'] as const,
|
|
18
|
-
collectionOptions:
|
|
19
|
-
autoIndex: 'eager' as const,
|
|
20
|
-
defaultIndexType: BasicIndex,
|
|
21
|
-
},
|
|
37
|
+
collectionOptions: indexing,
|
|
22
38
|
})
|
|
23
39
|
|
|
24
40
|
return {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ReactNode } from 'react'
|
|
2
2
|
|
|
3
|
-
// Optional context provider mounted by core inside /a/
|
|
3
|
+
// Optional context provider mounted by core inside /a/{{PKG_SLUG}}.
|
|
4
4
|
// Wrap any package-specific context providers or run mount-time side
|
|
5
5
|
// effects (e.g. subscribing to events, prefetching queries) here.
|
|
6
6
|
//
|
|
@@ -3,11 +3,11 @@ import { useLocalSearchParams } from 'expo-router'
|
|
|
3
3
|
import { Text, View } from 'react-native'
|
|
4
4
|
|
|
5
5
|
// Dynamic detail route. Wired automatically by core's generator to
|
|
6
|
-
// /a
|
|
6
|
+
// /a/{{PKG_SLUG}}/<id>.
|
|
7
7
|
//
|
|
8
|
-
// To navigate back to the index or to another
|
|
9
|
-
//
|
|
10
|
-
// https://tinycld.org/docs/tasks/routing
|
|
8
|
+
// To navigate back to the index or to another app screen, use `useOrgHref()`
|
|
9
|
+
// from `@tinycld/core/lib/org-routes` rather than a literal path — it supplies
|
|
10
|
+
// the /a app-route prefix. See https://tinycld.org/docs/tasks/routing
|
|
11
11
|
|
|
12
12
|
export default function {{PKG_PASCAL}}Detail() {
|
|
13
13
|
const { id } = useLocalSearchParams<{ id: string }>()
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { useThemeColor } from '@tinycld/core/lib/use-app-theme'
|
|
2
2
|
import { ScrollView, Text, View } from 'react-native'
|
|
3
3
|
|
|
4
|
-
//
|
|
4
|
+
// Index route for {{PKG_NAME}}, served at /a/{{PKG_SLUG}}.
|
|
5
5
|
// Replace this placeholder with your list view (cards, table, whatever you
|
|
6
|
-
// need) and wire it to your pbtsdb collections using `
|
|
6
|
+
// need) and wire it to your pbtsdb collections using `useLiveQuery`
|
|
7
|
+
// (or `useMyLiveQuery` when the query filters the current user's own rows).
|
|
7
8
|
//
|
|
8
9
|
// For navigation, use `useOrgHref()` from `@tinycld/core/lib/org-routes` —
|
|
9
|
-
// never literal paths like `router.push('/{{PKG_SLUG}}/new')`, which
|
|
10
|
-
//
|
|
10
|
+
// never literal paths like `router.push('/{{PKG_SLUG}}/new')`, which miss the
|
|
11
|
+
// /a app-route prefix and resolve to +not-found. `/a` is a constant segment,
|
|
12
|
+
// not an org slug: single-org deployments give each org its own host, so
|
|
13
|
+
// nothing interpolates into it. See https://tinycld.org/docs/tasks/routing
|
|
11
14
|
//
|
|
12
15
|
// const orgHref = useOrgHref()
|
|
13
16
|
// router.push(orgHref('{{PKG_SLUG}}/new'))
|
|
@@ -2,7 +2,7 @@ import { useThemeColor } from '@tinycld/core/lib/use-app-theme'
|
|
|
2
2
|
import { Text, View } from 'react-native'
|
|
3
3
|
|
|
4
4
|
// Sidebar for the {{PKG_NAME}} package. Rendered in the workspace drawer
|
|
5
|
-
// when a user is on any /a
|
|
5
|
+
// when a user is on any /a/{{PKG_SLUG}}/... route.
|
|
6
6
|
//
|
|
7
7
|
// Replace with real navigation (folders, favorites, filters, etc). See
|
|
8
8
|
// @tinycld/calendar or @tinycld/mail sidebars for richer examples.
|
|
@@ -2,7 +2,7 @@ import { useThemeColor } from '@tinycld/core/lib/use-app-theme'
|
|
|
2
2
|
import { ScrollView, Text, View } from 'react-native'
|
|
3
3
|
|
|
4
4
|
// The settings panel for {{PKG_NAME}}. Mounted by core inside
|
|
5
|
-
// /a
|
|
5
|
+
// /a/settings/{{PKG_SLUG}}/{{PKG_SLUG}} via the manifest's
|
|
6
6
|
// `settings[]` entry. This is the only screen the package contributes.
|
|
7
7
|
|
|
8
8
|
export default function {{PKG_PASCAL}}Settings() {
|
|
@@ -54,6 +54,36 @@ function memberDirsByName(): Map<string, string> {
|
|
|
54
54
|
// not a dir, or no/unreadable package.json — skip
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
|
+
// A member may also be nested one level down inside a dir that is not
|
|
58
|
+
// itself a member — hosting/ui, whose parent ships Go only and stays out of
|
|
59
|
+
// the workspace. Scanned after the top level so a top-level member wins a
|
|
60
|
+
// name collision.
|
|
61
|
+
for (const entry of fs.readdirSync(WS_ROOT)) {
|
|
62
|
+
if (entry === 'node_modules' || entry.startsWith('.')) continue
|
|
63
|
+
const parent = path.join(WS_ROOT, entry)
|
|
64
|
+
let nestedEntries: string[]
|
|
65
|
+
try {
|
|
66
|
+
if (!fs.statSync(parent).isDirectory()) continue
|
|
67
|
+
nestedEntries = fs.readdirSync(parent)
|
|
68
|
+
} catch {
|
|
69
|
+
continue
|
|
70
|
+
}
|
|
71
|
+
for (const nested of nestedEntries) {
|
|
72
|
+
if (nested === 'node_modules' || nested.startsWith('.')) continue
|
|
73
|
+
const dir = path.join(parent, nested)
|
|
74
|
+
try {
|
|
75
|
+
if (!fs.statSync(dir).isDirectory()) continue
|
|
76
|
+
const name = JSON.parse(
|
|
77
|
+
fs.readFileSync(path.join(dir, 'package.json'), 'utf8')
|
|
78
|
+
).name
|
|
79
|
+
if (typeof name === 'string' && name.length > 0 && !index.has(name)) {
|
|
80
|
+
index.set(name, dir)
|
|
81
|
+
}
|
|
82
|
+
} catch {
|
|
83
|
+
// not a dir, or no/unreadable package.json — skip
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
57
87
|
// @tinycld/core lives nested in the app shell (<WS_ROOT>/<APP_DIR>/core/);
|
|
58
88
|
// the top-level scan won't find it. Register it explicitly so the
|
|
59
89
|
// node_modules/@tinycld/core symlink is still created.
|
|
@@ -81,13 +111,25 @@ function linkInto(nodeModulesDir: string, scopeName: string, targetDir: string):
|
|
|
81
111
|
|
|
82
112
|
// If a correct symlink already exists, leave it. Otherwise replace whatever
|
|
83
113
|
// is there (stale link, or pnpm's own link for a depended-on member).
|
|
114
|
+
//
|
|
115
|
+
// The removal must not sit behind lstat in the same try: a DANGLING symlink
|
|
116
|
+
// (one whose target moved) makes readlink/lstat throw, and the old shape
|
|
117
|
+
// then skipped rmSync and fell straight into symlinkSync -> EEXIST, failing
|
|
118
|
+
// the whole install.
|
|
119
|
+
let matches = false
|
|
84
120
|
try {
|
|
85
|
-
|
|
86
|
-
return
|
|
87
|
-
}
|
|
88
|
-
fs.rmSync(linkPath, { recursive: true, force: true })
|
|
121
|
+
matches = fs.lstatSync(linkPath).isSymbolicLink() && fs.readlinkSync(linkPath) === relTarget
|
|
89
122
|
} catch {
|
|
90
|
-
// nothing at linkPath
|
|
123
|
+
// nothing at linkPath, or a link we cannot read — treat as "replace it"
|
|
124
|
+
}
|
|
125
|
+
if (matches) return
|
|
126
|
+
// unlink first for the symlink case: rmSync follows a link to decide how to
|
|
127
|
+
// remove it, so a DANGLING one (target moved) is left in place and the
|
|
128
|
+
// symlinkSync below then fails EEXIST.
|
|
129
|
+
try {
|
|
130
|
+
fs.unlinkSync(linkPath)
|
|
131
|
+
} catch {
|
|
132
|
+
fs.rmSync(linkPath, { recursive: true, force: true })
|
|
91
133
|
}
|
|
92
134
|
fs.symlinkSync(relTarget, linkPath)
|
|
93
135
|
}
|
|
@@ -52,3 +52,52 @@ describe('getPackages (new layout)', () => {
|
|
|
52
52
|
expect(getPackages()[0]).toBe('@tinycld/core')
|
|
53
53
|
})
|
|
54
54
|
})
|
|
55
|
+
|
|
56
|
+
// A member may live one level down inside a dir that is NOT itself a member:
|
|
57
|
+
// hosting/ui, whose parent ships Go only and is deliberately kept out of the
|
|
58
|
+
// workspace, so its client surface stays in the hosting repo rather than
|
|
59
|
+
// needing one of its own.
|
|
60
|
+
describe('getPackages (nested members)', () => {
|
|
61
|
+
let root: string
|
|
62
|
+
beforeEach(() => {
|
|
63
|
+
root = makeWorkspace()
|
|
64
|
+
process.env.TINYCLD_WS_ROOT = root
|
|
65
|
+
resetPackagesCache()
|
|
66
|
+
})
|
|
67
|
+
afterEach(() => {
|
|
68
|
+
delete process.env.TINYCLD_WS_ROOT
|
|
69
|
+
fs.rmSync(root, { recursive: true, force: true })
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it('finds a member nested inside a non-member parent', () => {
|
|
73
|
+
const ui = path.join(root, 'hosting', 'ui')
|
|
74
|
+
fs.mkdirSync(ui, { recursive: true })
|
|
75
|
+
fs.writeFileSync(path.join(ui, 'manifest.ts'), 'export default {}')
|
|
76
|
+
fs.writeFileSync(path.join(ui, 'package.json'), JSON.stringify({ name: '@tinycld/host-ui' }))
|
|
77
|
+
// The parent itself is not a member: no manifest.ts of its own.
|
|
78
|
+
fs.writeFileSync(path.join(root, 'hosting', 'go.mod'), 'module example.test\n')
|
|
79
|
+
|
|
80
|
+
resetPackagesCache()
|
|
81
|
+
expect(getPackages()).toContain('@tinycld/host-ui')
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
it('does not descend into a dir that is itself a member', () => {
|
|
85
|
+
// A feature's own subdirectory is its source tree, not another member.
|
|
86
|
+
const inner = path.join(root, 'contacts', 'vendored')
|
|
87
|
+
fs.mkdirSync(inner, { recursive: true })
|
|
88
|
+
fs.writeFileSync(path.join(inner, 'manifest.ts'), 'export default {}')
|
|
89
|
+
fs.writeFileSync(
|
|
90
|
+
path.join(inner, 'package.json'),
|
|
91
|
+
JSON.stringify({ name: '@tinycld/should-not-appear' })
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
resetPackagesCache()
|
|
95
|
+
expect(getPackages()).not.toContain('@tinycld/should-not-appear')
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
it('lists each member once when a nested scan revisits the tree', () => {
|
|
99
|
+
resetPackagesCache()
|
|
100
|
+
const pkgs = getPackages()
|
|
101
|
+
expect(new Set(pkgs).size).toBe(pkgs.length)
|
|
102
|
+
})
|
|
103
|
+
})
|
|
@@ -49,6 +49,25 @@ export function getPackages(): string[] {
|
|
|
49
49
|
if (hasManifest(dir)) features.push(name)
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
// A member may also be nested one level down, inside a dir that is not
|
|
53
|
+
// itself a member — hosting/ui is the case: hosting/ ships Go only and
|
|
54
|
+
// stays out of the workspace, but its client surface has to be a
|
|
55
|
+
// package the generator sees, and keeping it there keeps it in the
|
|
56
|
+
// hosting repo. One level only: deep enough for this shape, shallow
|
|
57
|
+
// enough not to walk the tree.
|
|
58
|
+
for (const entry of fs.readdirSync(wsRoot)) {
|
|
59
|
+
const parent = path.join(wsRoot, entry)
|
|
60
|
+
if (entry === 'node_modules' || entry.startsWith('.')) continue
|
|
61
|
+
if (!isDir(parent) || hasManifest(parent)) continue
|
|
62
|
+
for (const nested of readdirSafe(parent)) {
|
|
63
|
+
const dir = path.join(parent, nested)
|
|
64
|
+
if (nested === 'node_modules' || nested.startsWith('.')) continue
|
|
65
|
+
if (!isDir(dir) || !hasManifest(dir)) continue
|
|
66
|
+
const name = readPackageName(dir)
|
|
67
|
+
if (name && name !== CORE_NAME) features.push(name)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
52
71
|
// @tinycld/core moved INSIDE the app shell (at <appDir>/core/); the
|
|
53
72
|
// top-level scan above won't see it. Look for it explicitly so it is
|
|
54
73
|
// still listed first. The app dir is normally <wsRoot>/tinycld, but EAS
|
|
@@ -78,6 +97,23 @@ export function resetPackagesCache(): void {
|
|
|
78
97
|
cached = null
|
|
79
98
|
}
|
|
80
99
|
|
|
100
|
+
function readdirSafe(dir: string): string[] {
|
|
101
|
+
try {
|
|
102
|
+
return fs.readdirSync(dir)
|
|
103
|
+
} catch {
|
|
104
|
+
return []
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function readPackageName(dir: string): string | null {
|
|
109
|
+
try {
|
|
110
|
+
const name = JSON.parse(fs.readFileSync(path.join(dir, 'package.json'), 'utf8')).name
|
|
111
|
+
return typeof name === 'string' && name.length > 0 ? name : null
|
|
112
|
+
} catch {
|
|
113
|
+
return null
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
81
117
|
function hasManifest(dir: string): boolean {
|
|
82
118
|
return (
|
|
83
119
|
fs.existsSync(path.join(dir, 'manifest.ts')) || fs.existsSync(path.join(dir, 'manifest.js'))
|