@tinycld/bootstrap 1.1.0 → 1.1.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.
@@ -1,17 +1,19 @@
1
1
  import { spawnSync } from 'node:child_process';
2
- import { existsSync, mkdtempSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
3
- import { tmpdir } from 'node:os';
4
- import { join } from 'node:path';
5
- import { looksLikeWorkspaceRoot } from "./layout.js";
2
+ import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
3
+ import { dirname, join, relative } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
6
5
  // All feature members that exist in the ecosystem. The workspace manifest lists
7
6
  // ALL of them (npm tolerates absent dirs), so a partial checkout still installs.
8
7
  // But --tooling CLONES only app+core+requested — we do NOT force-clone all.
9
8
  const ALL_FEATURES = ['contacts', 'mail', 'calendar', 'drive', 'calc', 'text', 'google-takeout-import'];
10
- // Always cloned by tooling mode (the minimum viable workspace). package-scripts
11
- // ships inside the workspace meta-repo itself, so it is never cloned.
12
- const ALWAYS_CLONE = ['app', 'core'];
13
- // Listed in the manifest workspaces array (everything that could exist).
14
- const ALL_MEMBERS = ['app', 'core', 'package-scripts', ...ALL_FEATURES];
9
+ // app + core are always cloned by tooling mode (the minimum viable workspace);
10
+ // the clone set in bootstrapTooling seeds them directly (pinnable via
11
+ // appRef/coreRef). package-scripts (the tinycld-pkg CLI) now lives INSIDE the
12
+ // app member at app/package-scripts, so it arrives with the app clone and is
13
+ // never cloned separately.
14
+ // Listed in the manifest workspaces array (everything that could exist). The
15
+ // nested member path app/package-scripts is a valid npm workspace entry.
16
+ const ALL_MEMBERS = ['app', 'app/package-scripts', 'core', ...ALL_FEATURES];
15
17
  /**
16
18
  * Write (or merge into) a workspace-root package.json + .npmrc in `dir`.
17
19
  *
@@ -62,87 +64,111 @@ export function writeWorkspaceManifest(dir) {
62
64
  writeFileSync(npmrcPath, 'legacy-peer-deps=true\n');
63
65
  }
64
66
  }
65
- function realClone(url, dest) {
66
- const r = spawnSync('git', ['clone', '--depth', '1', url, dest], { stdio: 'inherit' });
67
- return r.status === 0;
67
+ /**
68
+ * Resolve the workspace-template dir relative to this module. Published builds
69
+ * have `dist/bootstrap-tooling.js` next to `templates/`; dev has
70
+ * `src/bootstrap-tooling.ts` under the same parent. Both → `../templates/workspace`.
71
+ */
72
+ function resolveWorkspaceTemplateDir() {
73
+ const here = dirname(fileURLToPath(import.meta.url));
74
+ return join(here, '..', 'templates', 'workspace');
68
75
  }
69
76
  /**
70
- * Clone the workspace meta-repo into `root`, tolerating a non-empty target.
71
- *
72
- * A plain `git clone <url> <root>` fails when `root` already contains files
73
- * (e.g. the link-package flow pre-creates a `<slug>/` subdir there). We
74
- * clone into a fresh sibling temp dir, then move each top-level entry into
75
- * `root`, skipping any that already exist. The `.git` dir is moved too unless
76
- * `root/.git` already exists.
77
+ * Copy the workspace-root scaffolding (tinycld.packages.ts, vitest.config.ts,
78
+ * tests/ stubs) from bootstrap's templates/workspace/ into `dir`. These files
79
+ * are pure scaffolding the workspace ROOT needs but that no longer lives in a
80
+ * committed workspace repo — bootstrap is their source of truth.
77
81
  *
78
- * The `cloneFn` primitive is the same injected-or-real clone used for app/core —
79
- * tests inject a stub that records URLs but does not create files, so the
80
- * move logic will simply move nothing from an empty temp dir (which is fine
81
- * for unit tests). Real runs move the full repo contents.
82
+ * Never overwrites an existing file: a real workspace checkout, or a CI lane
83
+ * that supplied its own copy, keeps what it has (same non-destructive ethos as
84
+ * the member clones). Returns the relative paths actually written.
82
85
  */
83
- function cloneWorkspaceIntoRoot(url, root, cloneFn) {
84
- const tempDir = mkdtempSync(join(tmpdir(), 'tinycld-workspace-'));
85
- try {
86
- if (!cloneFn(url, tempDir))
87
- return false;
88
- const rootHasGit = existsSync(join(root, '.git'));
89
- for (const entry of readdirSync(tempDir)) {
90
- // Skip .git if root already has one
91
- if (entry === '.git' && rootHasGit)
86
+ export function copyWorkspaceTemplate(dir, templateDir = resolveWorkspaceTemplateDir()) {
87
+ if (!existsSync(templateDir))
88
+ return [];
89
+ const written = [];
90
+ const walk = (src) => {
91
+ for (const entry of readdirSync(src)) {
92
+ const srcPath = join(src, entry);
93
+ const rel = relative(templateDir, srcPath);
94
+ const dstPath = join(dir, rel);
95
+ if (statSync(srcPath).isDirectory()) {
96
+ walk(srcPath);
92
97
  continue;
93
- const src = join(tempDir, entry);
94
- const dest = join(root, entry);
95
- // Never overwrite an entry that already exists in root
96
- if (existsSync(dest))
97
- continue;
98
- renameSync(src, dest);
98
+ }
99
+ if (existsSync(dstPath))
100
+ continue; // never overwrite
101
+ mkdirSync(dirname(dstPath), { recursive: true });
102
+ cpSync(srcPath, dstPath);
103
+ written.push(rel);
99
104
  }
100
- return true;
101
- }
102
- finally {
103
- rmSync(tempDir, { recursive: true, force: true });
104
- }
105
+ };
106
+ walk(templateDir);
107
+ return written;
108
+ }
109
+ /** Split a member spec `name@ref` into its parts. No `@` → no ref (clone HEAD). */
110
+ function splitRef(spec) {
111
+ const at = spec.indexOf('@');
112
+ if (at === -1)
113
+ return { name: spec };
114
+ return { name: spec.slice(0, at), ref: spec.slice(at + 1) || undefined };
115
+ }
116
+ function realClone(url, dest, ref) {
117
+ const args = ['clone', '--depth', '1'];
118
+ if (ref)
119
+ args.push('--branch', ref);
120
+ args.push(url, dest);
121
+ const r = spawnSync('git', args, { stdio: 'inherit' });
122
+ return r.status === 0;
105
123
  }
106
124
  /**
107
- * Assemble a workspace skeleton at opts.root: if root is not yet a workspace,
108
- * clone the workspace meta-repo into it first (self-init), then write the full
109
- * manifest, then clone ONLY app + core + the explicitly-requested feature
110
- * members (skipping any already present — e.g. a CI-checked-out member).
125
+ * Assemble a workspace skeleton at opts.root: write the canonical root manifest
126
+ * (writeWorkspaceManifest) + lay down the root scaffolding (copyWorkspaceTemplate:
127
+ * tinycld.packages.ts, vitest.config.ts, tests/ stubs, version files), then clone
128
+ * ONLY app + core + the explicitly-requested feature members (skipping any
129
+ * already present — e.g. a CI-checked-out member). There is NO workspace
130
+ * meta-repo clone: bootstrap is the source of all root scaffolding.
111
131
  * Unknown member names throw. Returns the members that ended up present. Does
112
132
  * NOT run npm install (the caller / CI controls that, since it may want a
113
133
  * clean `npm ci`).
114
134
  */
115
135
  export function bootstrapTooling(opts) {
116
- const requested = opts.members ?? [];
117
- const unknown = requested.filter((m) => !ALL_FEATURES.includes(m));
136
+ // Each requested member may be `name` or `name@ref`. Validate the NAME part.
137
+ const requested = (opts.members ?? []).map(splitRef);
138
+ const unknown = requested.filter((m) => !ALL_FEATURES.includes(m.name));
118
139
  if (unknown.length > 0) {
119
- throw new Error(`Unknown feature member(s): ${unknown.join(', ')}. Known: ${ALL_FEATURES.join(', ')}`);
140
+ throw new Error(`Unknown feature member(s): ${unknown.map((m) => m.name).join(', ')}. Known: ${ALL_FEATURES.join(', ')}`);
120
141
  }
121
142
  const repoBase = opts.repoBase ?? 'git@github.com:tinycld';
122
143
  const clone = opts.clone ?? realClone;
123
144
  const present = [];
124
- // Self-init: clone the workspace meta-repo into root if it isn't already
125
- // a workspace root. This provides package-scripts/, tinycld.packages.ts,
126
- // tests/, .node-version, .go-version, .npmrc, and the canonical root
127
- // package.json before anything else runs.
128
- if (!looksLikeWorkspaceRoot(opts.root)) {
129
- if (cloneWorkspaceIntoRoot(`${repoBase}/workspace.git`, opts.root, clone)) {
130
- present.push('workspace');
131
- }
132
- }
133
- // Merge the canonical workspaces list + postinstall into any existing
134
- // package.json (provided by the workspace clone), or write defaults when
135
- // no file exists yet (workspace clone was skipped or failed).
145
+ // Write the canonical root manifest (workspaces list + postinstall), then
146
+ // lay down the root scaffolding bootstrap owns (tinycld.packages.ts,
147
+ // vitest.config.ts, tests/ stubs, .node-version, .go-version). Both are
148
+ // non-destructive: anything already present (a real workspace checkout, or
149
+ // a CI lane that supplied its own) is left as-is.
136
150
  writeWorkspaceManifest(opts.root);
137
- const toClone = Array.from(new Set([...ALWAYS_CLONE, ...requested]));
138
- for (const m of toClone) {
139
- const dest = join(opts.root, m);
151
+ copyWorkspaceTemplate(opts.root);
152
+ // Build the clone set keyed by member NAME so a member passed both bare and
153
+ // with an @ref dedupes to one clone (the ref-bearing entry wins). app+core
154
+ // are always cloned and pinnable via appRef/coreRef.
155
+ const refByName = new Map();
156
+ refByName.set('app', opts.appRef);
157
+ refByName.set('core', opts.coreRef);
158
+ for (const { name, ref } of requested) {
159
+ // A later ref overrides an earlier bare entry; a bare entry never clears
160
+ // an existing ref.
161
+ if (ref !== undefined || !refByName.has(name))
162
+ refByName.set(name, ref);
163
+ }
164
+ for (const [name, ref] of refByName) {
165
+ const dest = join(opts.root, name);
140
166
  if (existsSync(join(dest, '.git')) || existsSync(join(dest, 'package.json'))) {
141
- present.push(m);
167
+ present.push(name);
142
168
  continue;
143
169
  }
144
- if (clone(`${repoBase}/${m}.git`, dest))
145
- present.push(m);
170
+ if (clone(`${repoBase}/${name}.git`, dest, ref))
171
+ present.push(name);
146
172
  }
147
173
  return present;
148
174
  }
@@ -1 +1 @@
1
- {"version":3,"file":"bootstrap-tooling.js","sourceRoot":"","sources":["../src/bootstrap-tooling.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"bootstrap-tooling.js","sourceRoot":"","sources":["../src/bootstrap-tooling.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -12,12 +12,34 @@ import { offerLinkPackage } from "./link-package.js";
12
12
  import { runPrompts } from "./prompts.js";
13
13
  export function runToolingMode(opts) {
14
14
  const root = opts.root ?? process.cwd();
15
+ // app + core are always-cloned members, not features, so they can't ride the
16
+ // `members` list through bootstrapTooling's feature validation. Accept them
17
+ // in the SAME `--with name@ref` surface for a uniform pinned invocation
18
+ // (`--with app@v1 --with core@v2 --with mail@v3`) by peeling app@ref/core@ref
19
+ // out of `members` into appRef/coreRef. An explicit opts.appRef/coreRef wins.
20
+ let appRef = opts.appRef;
21
+ let coreRef = opts.coreRef;
22
+ const features = [];
23
+ for (const spec of opts.members ?? []) {
24
+ const at = spec.indexOf('@');
25
+ const name = at === -1 ? spec : spec.slice(0, at);
26
+ const ref = at === -1 ? undefined : spec.slice(at + 1) || undefined;
27
+ if (name === 'app')
28
+ appRef = appRef ?? ref;
29
+ else if (name === 'core')
30
+ coreRef = coreRef ?? ref;
31
+ else
32
+ features.push(spec);
33
+ }
15
34
  // Honor TINYCLD_REPO_BASE (CI sets it to an HTTPS base since runners have no
16
35
  // SSH key). Listed first so an explicit opts.repoBase still wins via spread;
17
36
  // when neither is set, bootstrapTooling falls back to its SSH default.
18
37
  const present = bootstrapTooling({
19
38
  repoBase: process.env.TINYCLD_REPO_BASE,
20
39
  ...opts,
40
+ members: features,
41
+ appRef,
42
+ coreRef,
21
43
  root,
22
44
  });
23
45
  // Mandatory members must clone — a missing app/core means a broken workspace.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -29,14 +29,15 @@ export function ensureMember(workspaceDir, slug) {
29
29
  * symlink and the root `postinstall` runs the generator.
30
30
  *
31
31
  * Two shapes, distinguished by whether `workspaceDir` is already a workspace root:
32
- * - attach: `workspaceDir` already holds the workspace meta-repo (its
32
+ * - attach: `workspaceDir` is already an assembled workspace root (its
33
33
  * package.json name is `@tinycld/workspace`). No assembly — just ensure the
34
34
  * member and install. The existing root package.json is left untouched.
35
35
  * - bootstrap: `workspaceDir` is a fresh wrapper that only contains the
36
- * scaffolded `<slug>/` so far. Assemble the workspace *around* it — write the
37
- * workspace manifest and clone app + core via `bootstrapTooling` — then
38
- * ensure the new member and install. The `postinstall` (`cd app && …`) only
39
- * succeeds once `app/` exists, which is why we assemble before installing.
36
+ * scaffolded `<slug>/` so far. Assemble the workspace *around* it —
37
+ * `bootstrapTooling` generates the root scaffolding and clones app + core
38
+ * (no workspace meta-repo) — then ensure the new member and install. The
39
+ * `postinstall` (`cd app && …`) only succeeds once `app/` exists, which is
40
+ * why we assemble before installing.
40
41
  *
41
42
  * Returns true if the user chose to link (even if a subprocess failed — the
42
43
  * error is already logged, and we don't want to follow up with manual steps
@@ -50,7 +51,7 @@ export async function offerLinkPackage({ slug, workspaceDir, mode, assemble = re
50
51
  const needsClone = !looksLikeWorkspaceRoot(workspaceDir);
51
52
  if (mode === 'prompt') {
52
53
  const message = needsClone
53
- ? `Clone the tinycld workspace (app + core) and link ${pc.bold(slug)} now?`
54
+ ? `Assemble the tinycld workspace (app + core) and link ${pc.bold(slug)} now?`
54
55
  : `Link ${pc.bold(slug)} into the workspace now?`;
55
56
  const answer = await confirm({ message, initialValue: true });
56
57
  if (isCancel(answer) || answer !== true)
@@ -1 +1 @@
1
- {"version":3,"file":"link-package.js","sourceRoot":"","sources":["../src/link-package.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"link-package.js","sourceRoot":"","sources":["../src/link-package.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tinycld/bootstrap",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "Scaffolder for @tinycld feature packages",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -0,0 +1 @@
1
+ 1.25.0
@@ -0,0 +1 @@
1
+ 25
@@ -0,0 +1,20 @@
1
+ // Test-only stub for expo-clipboard. The real package transitively
2
+ // imports expo-modules-core, which has too many side effects to load
3
+ // under a Node test environment (it touches several globals expected
4
+ // to be set up by Metro/RN). Tests get a minimal in-memory shim that
5
+ // records the last-written text and returns it on read.
6
+
7
+ let text = ''
8
+
9
+ export async function setStringAsync(s: string): Promise<void> {
10
+ text = s
11
+ }
12
+
13
+ export async function getStringAsync(): Promise<string> {
14
+ return text
15
+ }
16
+
17
+ // Test-only escape hatch: reset the in-memory clipboard between tests.
18
+ export function __resetClipboardTextForTest(): void {
19
+ text = ''
20
+ }
@@ -0,0 +1,48 @@
1
+ // Test-only stub for expo-router. The real package's CJS entry
2
+ // (build/index.js) does `require("./global")` at module top, which
3
+ // Node's CJS resolver can't satisfy when the calling file is reached
4
+ // through a sibling-symlink path (Vite SSR's preserveSymlinks=false
5
+ // means imports under packages/@tinycld/<sibling>/... resolve via the
6
+ // sibling's real filesystem location, where node_modules don't reach
7
+ // expo-router).
8
+ //
9
+ // Tests that need to load a real screen, hook, or side-effect module
10
+ // from a sibling package import this stub instead. It surfaces the
11
+ // minimal API actually used at module-top by sibling source — the
12
+ // `router` object plus a few component shells. Tests that care about
13
+ // hook behavior should still mock those hooks individually with
14
+ // vi.mock; this stub only exists so the module graph can finish
15
+ // loading.
16
+
17
+ import { vi } from 'vitest'
18
+
19
+ const noop = () => {}
20
+ const ComponentStub = () => null
21
+
22
+ export const router = {
23
+ push: vi.fn(noop),
24
+ replace: vi.fn(noop),
25
+ back: vi.fn(noop),
26
+ canGoBack: vi.fn(() => true),
27
+ setParams: vi.fn(noop),
28
+ navigate: vi.fn(noop),
29
+ dismissAll: vi.fn(noop),
30
+ dismiss: vi.fn(noop),
31
+ }
32
+
33
+ export const Link = ComponentStub
34
+ export const Stack = Object.assign(ComponentStub, { Screen: ComponentStub })
35
+ export const Tabs = Object.assign(ComponentStub, { Screen: ComponentStub })
36
+ export const Slot = ComponentStub
37
+ export const Redirect = ComponentStub
38
+ export const Drawer = Object.assign(ComponentStub, { Screen: ComponentStub })
39
+
40
+ export const useRouter = () => router
41
+ export const usePathname = () => '/'
42
+ export const useSegments = () => []
43
+ export const useLocalSearchParams = () => ({})
44
+ export const useGlobalSearchParams = () => ({})
45
+ export const useFocusEffect = (_cb: () => void) => {}
46
+ export const useNavigation = () => ({})
47
+ export const useRootNavigationState = () => ({ key: 'test' })
48
+ export const Href = undefined
@@ -0,0 +1,73 @@
1
+ // Test-only stub for `lucide-react-native`. The real bundle pulls
2
+ // in `react-native-svg`, whose source uses TypeScript syntax that
3
+ // Vitest's transformer doesn't apply to node_modules. We don't
4
+ // render icons in unit tests anyway — assertions about icon
5
+ // rendering belong in Playwright.
6
+ //
7
+ // Implemented as CJS (.cjs) so we can use a Proxy on module.exports.
8
+ // Any named-icon import (ExternalLink, AlertTriangle, …) yields the
9
+ // same harmless component stub. Tests that genuinely need a real
10
+ // icon should reach for vi.mock locally; this stub only exists so
11
+ // the module graph can finish loading from sibling source under
12
+ // packages/@tinycld/<sibling>/... where the real bundle would crash.
13
+ //
14
+ // Vitest's ES↔CJS interop reads named exports from Object.keys() on
15
+ // the CJS module.exports, so the Proxy's `get` trap alone isn't
16
+ // enough — ESM `import { Heading1 } from '...'` requires Heading1 to
17
+ // appear as an own property. We pre-populate the dictionary with
18
+ // every Lucide identifier referenced from the codebase so static
19
+ // imports resolve to the Stub function; the Proxy handles any
20
+ // additional dynamic lookups (a defensive fallback).
21
+
22
+ const Stub = () => null
23
+
24
+ // Lucide icon names referenced by import statements anywhere in the
25
+ // codebase. Add new names here when a new icon is imported. The list
26
+ // is overinclusive on purpose: a missing entry would surface as an
27
+ // `undefined` named import in tests that try to render the icon,
28
+ // which is harder to diagnose than an unused name.
29
+ const NAMES = [
30
+ 'AlertCircle', 'AlertTriangle', 'AlignCenter', 'AlignJustify', 'AlignLeft', 'AlignRight',
31
+ 'Archive', 'ArrowDown', 'ArrowDownToLine', 'ArrowLeft', 'ArrowRight', 'ArrowUp',
32
+ 'AtSign', 'Bell', 'BellOff', 'Bold', 'Book', 'BookOpen', 'Bot', 'Box', 'Calendar',
33
+ 'Camera', 'Check', 'CheckCircle', 'CheckSquare', 'ChevronDown', 'ChevronLeft',
34
+ 'ChevronRight', 'ChevronUp', 'Circle', 'Clipboard', 'Clock', 'Cloud', 'CloudOff',
35
+ 'Code', 'Code2', 'Columns', 'Copy', 'CornerDownLeft', 'Cpu', 'CreditCard', 'Crop',
36
+ 'Database', 'Download', 'Edit', 'Edit2', 'Edit3', 'ExternalLink', 'Eye', 'EyeOff',
37
+ 'File', 'FileText', 'Files', 'Filter', 'Flag', 'Folder', 'FolderOpen', 'Forward',
38
+ 'Globe', 'GraduationCap', 'Grid', 'HardDrive', 'Hash', 'Headphones', 'Heading1',
39
+ 'Heading2', 'Heading3', 'Heading4', 'Heading5', 'Heading6', 'HelpCircle', 'Highlighter',
40
+ 'Home', 'Image', 'Inbox', 'Info', 'Italic', 'Key', 'Keyboard', 'Languages', 'Layout',
41
+ 'Link', 'Link2', 'List', 'ListChecks', 'ListOrdered', 'Loader', 'Lock', 'LogIn',
42
+ 'LogOut', 'Mail', 'MailOpen', 'Map', 'Maximize', 'Maximize2', 'MenuIcon', 'Menu',
43
+ 'MessageCircle', 'MessageSquare', 'Mic', 'MicOff', 'Minimize', 'Minimize2', 'Minus',
44
+ 'MonitorSmartphone', 'MoreHorizontal', 'MoreVertical', 'Move', 'Music', 'Package',
45
+ 'Palette', 'Paperclip', 'Pause', 'Pencil', 'Phone', 'PieChart', 'Pin', 'PinOff',
46
+ 'Play', 'Plus', 'PlusCircle', 'Pointer', 'Power', 'Printer', 'Quote', 'RefreshCcw',
47
+ 'RefreshCw', 'Replace', 'ReplaceAll', 'Reply', 'ReplyAll', 'RotateCcw', 'Save', 'Search', 'Send', 'Server',
48
+ 'Settings', 'Settings2', 'Share', 'Share2', 'Shield', 'ShieldCheck', 'Shuffle',
49
+ 'Sidebar', 'Slack', 'SlidersHorizontal', 'Smartphone', 'Smile', 'Sparkles',
50
+ 'Speaker', 'Square', 'Star', 'Strikethrough', 'Subscript', 'Sun', 'Superscript',
51
+ 'Table', 'Tag', 'Terminal', 'TestTube', 'Trash', 'Trash2', 'TrendingDown',
52
+ 'TrendingUp', 'Triangle', 'Type', 'Underline', 'Undo', 'Unlock', 'Upload',
53
+ 'User', 'UserCheck', 'UserPlus', 'Users', 'Video', 'VideoOff', 'Volume', 'Volume2',
54
+ 'VolumeX', 'Wifi', 'WifiOff', 'WrapText', 'X', 'XCircle', 'XSquare', 'Zap',
55
+ 'ZoomIn', 'ZoomOut',
56
+ ]
57
+
58
+ const exports = { default: Stub }
59
+ for (const name of NAMES) {
60
+ exports[name] = Stub
61
+ }
62
+ exports.__esModule = true
63
+
64
+ module.exports = new Proxy(exports, {
65
+ get(target, prop) {
66
+ if (typeof prop === 'symbol') return Reflect.get(target, prop)
67
+ if (prop in target) return target[prop]
68
+ // Unknown icon name: return the Stub anyway so dynamic
69
+ // lookups don't crash. Static `import { Foo }` will already
70
+ // have been resolved against `target` above.
71
+ return Stub
72
+ },
73
+ })
@@ -0,0 +1,54 @@
1
+ import * as fs from 'node:fs'
2
+ import * as os from 'node:os'
3
+ import * as path from 'node:path'
4
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest'
5
+ import { getPackages, resetPackagesCache } from '../tinycld.packages'
6
+
7
+ // Build a fake workspace tree and point getPackages at it via TINYCLD_WS_ROOT.
8
+ function makeWorkspace(): string {
9
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'tcld-ws-'))
10
+ // core member: package.json name @tinycld/core, NO manifest.ts
11
+ fs.mkdirSync(path.join(root, 'core'), { recursive: true })
12
+ fs.writeFileSync(
13
+ path.join(root, 'core', 'package.json'),
14
+ JSON.stringify({ name: '@tinycld/core' })
15
+ )
16
+ // feature member: contacts with manifest.ts + named package.json
17
+ fs.mkdirSync(path.join(root, 'contacts'), { recursive: true })
18
+ fs.writeFileSync(path.join(root, 'contacts', 'manifest.ts'), 'export default {}')
19
+ fs.writeFileSync(
20
+ path.join(root, 'contacts', 'package.json'),
21
+ JSON.stringify({ name: '@tinycld/contacts' })
22
+ )
23
+ // app shell: package.json name "app", NO manifest.ts
24
+ fs.mkdirSync(path.join(root, 'app'), { recursive: true })
25
+ fs.writeFileSync(path.join(root, 'app', 'package.json'), JSON.stringify({ name: 'app' }))
26
+ return root
27
+ }
28
+
29
+ describe('getPackages (new layout)', () => {
30
+ let root: string
31
+ beforeEach(() => {
32
+ root = makeWorkspace()
33
+ process.env.TINYCLD_WS_ROOT = root
34
+ resetPackagesCache()
35
+ })
36
+ afterEach(() => {
37
+ delete process.env.TINYCLD_WS_ROOT
38
+ fs.rmSync(root, { recursive: true, force: true })
39
+ })
40
+
41
+ it('includes core (member named @tinycld/core, no manifest) plus feature members with a manifest.ts', () => {
42
+ const pkgs = getPackages()
43
+ expect(pkgs).toContain('@tinycld/core')
44
+ expect(pkgs).toContain('@tinycld/contacts')
45
+ })
46
+
47
+ it('excludes the app shell (named "app", no manifest.ts)', () => {
48
+ expect(getPackages()).not.toContain('app')
49
+ })
50
+
51
+ it('lists core first (stable order: core, then features sorted)', () => {
52
+ expect(getPackages()[0]).toBe('@tinycld/core')
53
+ })
54
+ })
@@ -0,0 +1,55 @@
1
+ import { vi } from 'vitest'
2
+
3
+ process.env.EXPO_PUBLIC_ENV ??= 'test'
4
+
5
+ // tinycld.config.ts is the runtime source of truth, but loading the real one
6
+ // pulls every linked package's collections/screens (and their un-mockable RN
7
+ // surface) into the test graph. Shim it to an empty config so unit tests stay
8
+ // light; tests that need specific packages can override per-file.
9
+ vi.mock('@tinycld/app-generated/tinycld-config', () => ({
10
+ tinycldConfig: [],
11
+ }))
12
+
13
+ vi.mock('@sentry/react-native', () => ({
14
+ init: vi.fn(),
15
+ captureException: vi.fn(),
16
+ withScope: vi.fn(),
17
+ }))
18
+
19
+ // React Native's entry uses Flow syntax Vite/Rollup can't parse. Substitute
20
+ // the minimal surface our tests touch.
21
+ vi.mock('react-native', () => ({
22
+ Platform: { OS: 'web' },
23
+ Dimensions: {
24
+ get: () => ({ width: 1024, height: 768 }),
25
+ addEventListener: () => ({ remove: () => {} }),
26
+ },
27
+ }))
28
+
29
+ // @react-native-async-storage/async-storage requires the RN module bridge and
30
+ // is pulled in transitively by `~/lib/store`. Substitute a minimal in-memory
31
+ // shim so tests can exercise the Zustand registry without RN.
32
+ vi.mock('@react-native-async-storage/async-storage', () => {
33
+ const store = new Map<string, string>()
34
+ const api = {
35
+ getItem: async (key: string) => store.get(key) ?? null,
36
+ setItem: async (key: string, value: string) => {
37
+ store.set(key, value)
38
+ },
39
+ removeItem: async (key: string) => {
40
+ store.delete(key)
41
+ },
42
+ clear: async () => {
43
+ store.clear()
44
+ },
45
+ getAllKeys: async () => Array.from(store.keys()),
46
+ multiGet: async (keys: string[]) => keys.map(k => [k, store.get(k) ?? null]),
47
+ multiSet: async (pairs: [string, string][]) => {
48
+ for (const [k, v] of pairs) store.set(k, v)
49
+ },
50
+ multiRemove: async (keys: string[]) => {
51
+ for (const k of keys) store.delete(k)
52
+ },
53
+ }
54
+ return { default: api, ...api }
55
+ })
@@ -0,0 +1,75 @@
1
+ import * as fs from 'node:fs'
2
+ import * as path from 'node:path'
3
+
4
+ // New flat layout: every package (core, app, features) is a direct member dir
5
+ // under the workspace root. core is the member whose package.json name is
6
+ // "@tinycld/core" (it has no manifest.ts). Feature members are dirs with a
7
+ // manifest.ts. Members without a manifest.ts are excluded (this includes the app shell).
8
+ //
9
+ // TINYCLD_WS_ROOT overrides the workspace root for tests that scan a fake tree.
10
+ // It is resolved at call time (not module load) so that tests can set it in
11
+ // beforeEach after the module is already imported.
12
+ const CORE_NAME = '@tinycld/core'
13
+
14
+ let cached: string[] | null = null
15
+
16
+ /**
17
+ * Returns the package names linked into the app, core first then features
18
+ * sorted by name. Core is the member named "@tinycld/core" (no manifest.ts);
19
+ * feature members are dirs containing a manifest.ts with a named package.json.
20
+ * The app shell is excluded. Cached per process; reset with resetPackagesCache().
21
+ */
22
+ export function getPackages(): string[] {
23
+ if (cached) return cached
24
+
25
+ const wsRoot = process.env.TINYCLD_WS_ROOT
26
+ ? path.resolve(process.env.TINYCLD_WS_ROOT)
27
+ : path.resolve(import.meta.dirname)
28
+
29
+ let core: string | null = null
30
+ const features: string[] = []
31
+
32
+ if (fs.existsSync(wsRoot)) {
33
+ for (const entry of fs.readdirSync(wsRoot)) {
34
+ const dir = path.join(wsRoot, entry)
35
+ if (!isDir(dir)) continue
36
+ const pkgJsonPath = path.join(dir, 'package.json')
37
+ if (!fs.existsSync(pkgJsonPath)) continue
38
+ let name: unknown
39
+ try {
40
+ name = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8')).name
41
+ } catch {
42
+ continue // unreadable package.json
43
+ }
44
+ if (typeof name !== 'string' || name.length === 0) continue
45
+ if (name === CORE_NAME) {
46
+ core = name
47
+ continue
48
+ }
49
+ if (hasManifest(dir)) features.push(name)
50
+ }
51
+ }
52
+
53
+ features.sort()
54
+ cached = core ? [core, ...features] : features
55
+ return cached
56
+ }
57
+
58
+ // Test-only: clear the per-process cache so a fresh TINYCLD_WS_ROOT is read.
59
+ export function resetPackagesCache(): void {
60
+ cached = null
61
+ }
62
+
63
+ function hasManifest(dir: string): boolean {
64
+ return (
65
+ fs.existsSync(path.join(dir, 'manifest.ts')) || fs.existsSync(path.join(dir, 'manifest.js'))
66
+ )
67
+ }
68
+
69
+ function isDir(p: string): boolean {
70
+ try {
71
+ return fs.statSync(p).isDirectory()
72
+ } catch {
73
+ return false
74
+ }
75
+ }
@@ -0,0 +1,104 @@
1
+ import fs from 'node:fs'
2
+ import path from 'node:path'
3
+ import { defineConfig } from 'vitest/config'
4
+
5
+ // Workspace-root vitest config. Supplies dedup pins, stub aliases, and the
6
+ // react-native/expo/sentry mocks that let the broader core suite run under
7
+ // Node without the Metro/RN runtime.
8
+ const ROOT = import.meta.dirname
9
+ const CORE_DIR = path.join(ROOT, 'core')
10
+ const APP_GENERATED = path.join(ROOT, 'app', 'lib', 'generated')
11
+
12
+ // Locate a hoisted dependency's dir. In this workspace layout deps hoist to
13
+ // the workspace-root node_modules (all members declare framework deps as
14
+ // peerDependencies only, so the app shell's direct deps live in
15
+ // node_modules/ here at the root). Falls back one level up (CI layouts where
16
+ // the workspace root is nested under another root).
17
+ const pkgDir = (pkg: string): string => {
18
+ for (const root of [path.join(ROOT, 'node_modules'), path.join(ROOT, '..', 'node_modules')]) {
19
+ const candidate = path.join(root, pkg)
20
+ if (fs.existsSync(candidate)) return candidate
21
+ }
22
+ return path.join(ROOT, 'node_modules', pkg)
23
+ }
24
+
25
+ export default defineConfig({
26
+ resolve: {
27
+ alias: [
28
+ // --- dedup pins (Vite SSR resolver differs from Metro; keep until
29
+ // a test proves the workspace dedupes these on its own) ---
30
+ { find: /^react$/, replacement: path.join(pkgDir('react'), 'index.js') },
31
+ {
32
+ find: /^react\/jsx-runtime$/,
33
+ replacement: path.join(pkgDir('react'), 'jsx-runtime.js'),
34
+ },
35
+ {
36
+ find: /^react\/jsx-dev-runtime$/,
37
+ replacement: path.join(pkgDir('react'), 'jsx-dev-runtime.js'),
38
+ },
39
+ // yjs/y-protocols use instanceof checks; a duplicate copy reached
40
+ // through a member symlink breaks nested Y.Map.set. Pin to the
41
+ // single install.
42
+ { find: /^yjs$/, replacement: pkgDir('yjs') },
43
+ { find: /^y-protocols\/(.+)$/, replacement: `${pkgDir('y-protocols')}/$1` },
44
+ // hyperformula's ESM build has broken relative imports under Vite
45
+ // SSR; pin to the self-consistent commonjs entry.
46
+ {
47
+ find: /^hyperformula$/,
48
+ replacement: path.join(pkgDir('hyperformula'), 'commonjs/index.js'),
49
+ },
50
+ // --- @tinycld/core path remaps. Unlike Metro, Vite's exports-map
51
+ // resolution does NOT do directory-index fallback, so
52
+ // `@tinycld/core/lib/notify` (a dir → lib/notify/index.ts) fails
53
+ // to load through the exports wildcard. Remap straight to the
54
+ // core source path, which Vite resolves with its own extension
55
+ // + index probing.
56
+ {
57
+ find: /^@tinycld\/core$/,
58
+ replacement: path.join(CORE_DIR, 'index.ts'),
59
+ },
60
+ {
61
+ find: /^@tinycld\/core\/(.+)$/,
62
+ replacement: `${CORE_DIR}/$1`,
63
+ },
64
+ // --- @tinycld/app-generated/* build-time contract (written to
65
+ // app/lib/generated/ by the generator; Vitest doesn't read
66
+ // tsconfig paths) ---
67
+ {
68
+ find: /^@tinycld\/app-generated\/(.+)$/,
69
+ replacement: `${APP_GENERATED}/$1`,
70
+ },
71
+ // --- test doubles ---
72
+ // expo-clipboard transitively pulls in expo-modules-core, whose
73
+ // module-load-time side effects don't survive a bare Node test env.
74
+ {
75
+ find: /^expo-clipboard$/,
76
+ replacement: path.join(ROOT, 'tests/expo-clipboard-stub.ts'),
77
+ },
78
+ // expo-router's CJS entry does `require("./global")` at module top,
79
+ // which Node can't resolve when reached through a member symlink.
80
+ {
81
+ find: /^expo-router$/,
82
+ replacement: path.join(ROOT, 'tests/expo-router-stub.ts'),
83
+ },
84
+ // lucide-react-native pulls in react-native-svg (TS source, not
85
+ // transformed in node_modules under Vitest); a CJS Proxy stub
86
+ // yields a harmless component for any named icon import.
87
+ {
88
+ find: /^lucide-react-native$/,
89
+ replacement: path.join(ROOT, 'tests/lucide-react-native-stub.cjs'),
90
+ },
91
+ ],
92
+ },
93
+ test: {
94
+ environment: 'node',
95
+ include: [
96
+ 'tests/**/*.test.{ts,tsx}',
97
+ 'core/**/__tests__/**/*.test.{ts,tsx}',
98
+ 'core/**/*.test.{ts,tsx}',
99
+ 'app/scripts/**/__tests__/**/*.test.{ts,tsx}',
100
+ 'package-scripts/tests/**/*.test.{ts,tsx}',
101
+ ],
102
+ setupFiles: ['tests/unit-setup.ts'],
103
+ },
104
+ })