@multiplatform.one/cli 6.4.2 → 6.6.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 (100) hide show
  1. package/README.md +23 -1
  2. package/lib/bin/multiplatformOne.mjs +6 -4
  3. package/lib/commands/init.mjs +1 -1
  4. package/lib/commands/initApp.mjs +209 -8
  5. package/lib/commands/updateApp.mjs +170 -16
  6. package/package.json +2 -2
  7. package/scripts/clone.sh +1 -1
  8. package/scripts/update.sh +1 -1
  9. package/src/bin/multiplatformOne.ts +127 -108
  10. package/src/commands/init.ts +1 -1
  11. package/src/commands/initApp.spec.ts +369 -3
  12. package/src/commands/initApp.ts +325 -7
  13. package/src/commands/updateApp.spec.ts +81 -0
  14. package/src/commands/updateApp.ts +213 -12
  15. package/templates/app/package.json +19 -1
  16. package/templates/pieces/frappe/universal/README.md.partial +32 -0
  17. package/templates/pieces/frappe/universal/docker/compose.frappe.yaml +52 -0
  18. package/templates/pieces/frappe/universal/env.example.partial +8 -0
  19. package/templates/pieces/frappe/universal/packages/config/config.json.partial +3 -0
  20. package/templates/pieces/gnome/universal/README.md.partial +68 -0
  21. package/templates/pieces/gnome/universal/apps/__NAME__/gnome/main.tsx +165 -0
  22. package/templates/pieces/gnome/universal/apps/__NAME__/gnome/polyfills.ts +10 -0
  23. package/templates/pieces/gnome/universal/apps/__NAME__/gnome/shims/components.ts +36 -0
  24. package/templates/pieces/gnome/universal/apps/__NAME__/gnome/shims/empty-module.ts +28 -0
  25. package/templates/pieces/gnome/universal/apps/__NAME__/gnome/shims/forms.ts +18 -0
  26. package/templates/pieces/gnome/universal/apps/__NAME__/gnome/shims/one.ts +89 -0
  27. package/templates/pieces/gnome/universal/apps/__NAME__/gnome/shims/theme.ts +25 -0
  28. package/templates/pieces/gnome/universal/apps/__NAME__/gnome/tamagui-barrel.ts +77 -0
  29. package/templates/pieces/gnome/universal/apps/__NAME__/package.json.partial +18 -0
  30. package/templates/pieces/gnome/universal/apps/__NAME__/vite.config.gnome.ts +124 -0
  31. package/templates/pieces/gnome/universal/gitignore.partial +3 -0
  32. package/templates/pieces/gnome/universal/package.json.partial +6 -0
  33. package/templates/pieces/keycloak/universal/README.md.partial +19 -0
  34. package/templates/pieces/keycloak/universal/docker/compose.keycloak.yaml +27 -0
  35. package/templates/pieces/keycloak/universal/docker/keycloak/realm.json +43 -0
  36. package/templates/pieces/keycloak/universal/env.example.partial +9 -0
  37. package/templates/pieces/keycloak/universal/packages/config/config.json.partial +9 -0
  38. package/templates/pieces/tauri/universal/README.md.partial +18 -0
  39. package/templates/pieces/tauri/universal/apps/__NAME__/package.json.partial +10 -0
  40. package/templates/pieces/tauri/universal/apps/__NAME__/src-tauri/Cargo.toml +26 -0
  41. package/templates/pieces/tauri/universal/apps/__NAME__/src-tauri/build.rs +3 -0
  42. package/templates/pieces/tauri/universal/apps/__NAME__/src-tauri/capabilities/default.json +7 -0
  43. package/templates/pieces/tauri/universal/apps/__NAME__/src-tauri/icons/128x128.png +0 -0
  44. package/templates/pieces/tauri/universal/apps/__NAME__/src-tauri/icons/128x128@2x.png +0 -0
  45. package/templates/pieces/tauri/universal/apps/__NAME__/src-tauri/icons/32x32.png +0 -0
  46. package/templates/pieces/tauri/universal/apps/__NAME__/src-tauri/icons/icon.icns +0 -0
  47. package/templates/pieces/tauri/universal/apps/__NAME__/src-tauri/icons/icon.ico +0 -0
  48. package/templates/pieces/tauri/universal/apps/__NAME__/src-tauri/src/lib.rs +14 -0
  49. package/templates/pieces/tauri/universal/apps/__NAME__/src-tauri/src/main.rs +9 -0
  50. package/templates/pieces/tauri/universal/apps/__NAME__/src-tauri/tauri.conf.json +43 -0
  51. package/templates/pieces/tauri/universal/gitignore.partial +3 -0
  52. package/templates/pieces/tauri/universal/package.json.partial +6 -0
  53. package/templates/pieces/vscode/universal/README.md.partial +17 -0
  54. package/templates/pieces/vscode/universal/apps/__NAME__/package.json.partial +19 -0
  55. package/templates/pieces/vscode/universal/apps/__NAME__/vite.config.vscode.ts +17 -0
  56. package/templates/pieces/vscode/universal/apps/__NAME__/vscode/extension/env.d.ts +1 -0
  57. package/templates/pieces/vscode/universal/apps/__NAME__/vscode/extension/index.ts +13 -0
  58. package/templates/pieces/vscode/universal/apps/__NAME__/vscode/extension/views/helper.ts +14 -0
  59. package/templates/pieces/vscode/universal/apps/__NAME__/vscode/extension/views/panel.ts +38 -0
  60. package/templates/pieces/vscode/universal/apps/__NAME__/vscode/index.html +49 -0
  61. package/templates/pieces/vscode/universal/apps/__NAME__/vscode/manifest.ts +33 -0
  62. package/templates/pieces/vscode/universal/apps/__NAME__/vscode/package.json +7 -0
  63. package/templates/pieces/vscode/universal/apps/__NAME__/vscode/tamagui.css +3 -0
  64. package/templates/pieces/vscode/universal/apps/__NAME__/vscode/tsconfig.json +9 -0
  65. package/templates/pieces/vscode/universal/apps/__NAME__/vscode/webview/app.tsx +24 -0
  66. package/templates/pieces/vscode/universal/apps/__NAME__/vscode/webview/components/ErrorBoundary.tsx +41 -0
  67. package/templates/pieces/vscode/universal/apps/__NAME__/vscode/webview/fonts/inter-400.woff2 +0 -0
  68. package/templates/pieces/vscode/universal/apps/__NAME__/vscode/webview/fonts/inter-700.woff2 +0 -0
  69. package/templates/pieces/vscode/universal/apps/__NAME__/vscode/webview/fonts.css +21 -0
  70. package/templates/pieces/vscode/universal/apps/__NAME__/vscode/webview/main.tsx +35 -0
  71. package/templates/pieces/vscode/universal/apps/__NAME__/vscode/webview/router.tsx +58 -0
  72. package/templates/pieces/vscode/universal/apps/__NAME__/vscode/webview/useVSCodeTheme.ts +56 -0
  73. package/templates/pieces/vscode/universal/apps/__NAME__/vscode/webview/utils/vscode.ts +16 -0
  74. package/templates/pieces/vscode/universal/gitignore.partial +2 -0
  75. package/templates/pieces/vscode/universal/package.json.partial +6 -0
  76. package/templates/pieces/webext/universal/README.md.partial +16 -0
  77. package/templates/pieces/webext/universal/apps/__NAME__/package.json.partial +22 -0
  78. package/templates/pieces/webext/universal/apps/__NAME__/tsconfig.json.partial +5 -0
  79. package/templates/pieces/webext/universal/apps/__NAME__/vite.config.webext-background.ts +19 -0
  80. package/templates/pieces/webext/universal/apps/__NAME__/vite.config.webext.ts +35 -0
  81. package/templates/pieces/webext/universal/apps/__NAME__/webext/assets/fonts/inter-400.woff2 +0 -0
  82. package/templates/pieces/webext/universal/apps/__NAME__/webext/assets/fonts/inter-700.woff2 +0 -0
  83. package/templates/pieces/webext/universal/apps/__NAME__/webext/background/main.ts +10 -0
  84. package/templates/pieces/webext/universal/apps/__NAME__/webext/components/ErrorBoundary.tsx +41 -0
  85. package/templates/pieces/webext/universal/apps/__NAME__/webext/env.ts +2 -0
  86. package/templates/pieces/webext/universal/apps/__NAME__/webext/manifest.ts +45 -0
  87. package/templates/pieces/webext/universal/apps/__NAME__/webext/prepare.ts +21 -0
  88. package/templates/pieces/webext/universal/apps/__NAME__/webext/static/assets/icon-512.png +0 -0
  89. package/templates/pieces/webext/universal/apps/__NAME__/webext/tamagui.css +3 -0
  90. package/templates/pieces/webext/universal/apps/__NAME__/webext/tsconfig.json +9 -0
  91. package/templates/pieces/webext/universal/apps/__NAME__/webext/views/popup/index.html +77 -0
  92. package/templates/pieces/webext/universal/apps/__NAME__/webext/views/popup/main.tsx +73 -0
  93. package/templates/pieces/webext/universal/gitignore.partial +2 -0
  94. package/templates/pieces/webext/universal/package.json.partial +6 -0
  95. package/templates/universal/apps/__NAME__/package.json +1 -1
  96. package/templates/universal/package.json +25 -1
  97. package/types/commands/initApp.d.ts +43 -0
  98. package/types/commands/initApp.d.ts.map +1 -1
  99. package/types/commands/updateApp.d.ts +4 -0
  100. package/types/commands/updateApp.d.ts.map +1 -1
package/scripts/clone.sh CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/bin/sh
2
2
  # Clone multiplatform.one repo into target directory.
3
3
  # Usage: clone.sh <remote_url> <branch> <target_dir>
4
- # Example: clone.sh https://gitlab.com/bitspur/multiplatform.one/multiplatform.one.git main ./my-app
4
+ # Example: clone.sh https://gitlab.com/bitspur/frappe/multiplatform.one.git main ./my-app
5
5
 
6
6
  set -e
7
7
 
package/scripts/update.sh CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/bin/sh
2
2
  # Merge from upstream. Run from project root.
3
3
  # Usage: update.sh <remote_url> [branch]
4
- # Example: update.sh https://gitlab.com/bitspur/multiplatform.one/multiplatform.one.git main
4
+ # Example: update.sh https://gitlab.com/bitspur/frappe/multiplatform.one.git main
5
5
 
6
6
  set -e
7
7
 
@@ -19,7 +19,7 @@ import YAML from "yaml";
19
19
  import yoctoSpinner from "yocto-spinner";
20
20
  import { discoverE2EApp, runE2ESession } from "../commands/e2e";
21
21
  import { init, runModifyStep } from "../commands/init";
22
- import { initApp, readProvenance } from "../commands/initApp";
22
+ import { INIT_APP_PIECES, initApp, readProvenance } from "../commands/initApp";
23
23
  import { updateApp } from "../commands/updateApp";
24
24
 
25
25
  const projectRoot = lookupProjectRoot();
@@ -221,6 +221,15 @@ program
221
221
  .option("--universal", "scaffold the universal template (web + iOS + Android; default)")
222
222
  .option("--web", "scaffold the web-only template (Vite + React)")
223
223
  .option("--app", "alias of --web (back-compat)")
224
+ .option("--frappe", "add the Frappe backend piece (env keys, provider, docker compose bench)")
225
+ .option("--keycloak", "add the Keycloak auth piece (env keys, provider, docker compose + realm)")
226
+ .option(
227
+ "--gnome",
228
+ "add the GNOME desktop target piece (GTK4/GJS via react-gnome — native widgets, no webview)",
229
+ )
230
+ .option("--tauri", "add the desktop target piece (src-tauri skeleton, webview)")
231
+ .option("--vscode", "add the VS Code extension target piece")
232
+ .option("--webext", "add the browser extension target piece (MV3 popup + background)")
224
233
  .option("--skip-install", "skip pnpm install after scaffolding")
225
234
  .option("--skip-git", "skip git init + scaffold commit")
226
235
  .option(
@@ -269,20 +278,28 @@ program
269
278
  if (webOnly && options.universal) {
270
279
  throw new Error("Pass either --web/--app or --universal, not both");
271
280
  }
281
+ // Piece flags are multi-select; passing any of them skips the interactive
282
+ // pieces prompt. No flags + --yes (or non-TTY) → no pieces.
283
+ const pieces = INIT_APP_PIECES.filter((piece) => Boolean(options[piece]));
272
284
  await initApp(name, {
273
285
  skipInstall: Boolean(options.skipInstall),
274
286
  skipGit: Boolean(options.skipGit),
275
287
  version: options.mpoVersion,
276
288
  template: webOnly ? "app" : options.universal ? "universal" : undefined,
277
289
  yes: Boolean(options.yes),
290
+ pieces: pieces.length ? pieces : undefined,
278
291
  });
279
292
  });
280
293
 
281
- const defaultUpdateRemote = "https://gitlab.com/bitspur/multiplatform.one/multiplatform.one.git";
294
+ const defaultUpdateRemote = "https://gitlab.com/bitspur/frappe/multiplatform.one.git";
282
295
 
283
296
  program
284
297
  .command("update")
285
- .option("-c, --checkout <branch>", "branch, tag or commit to merge from upstream (monorepo forks)", "main")
298
+ .option(
299
+ "-c, --checkout <branch>",
300
+ "branch, tag or commit to merge from upstream (monorepo forks)",
301
+ "main",
302
+ )
286
303
  .option("-r, --remote <url>", "upstream remote URL (monorepo forks)", defaultUpdateRemote)
287
304
  .option("--skip-install", "skip pnpm install after the update")
288
305
  .option(
@@ -292,121 +309,123 @@ program
292
309
  .description(
293
310
  "update a scaffolded project to the current template (three-way merge via .mpo.json provenance); monorepo forks fall back to the upstream merge flow",
294
311
  )
295
- .action(async (options: {
296
- checkout: string;
297
- remote: string;
298
- skipInstall?: boolean;
299
- mpoVersion?: string;
300
- }) => {
301
- // Update prechecks: must be in a git repo
302
- if (
303
- await spawn("git", ["rev-parse", "--is-inside-work-tree"]).then(
304
- () => false,
305
- () => true,
306
- )
307
- ) {
308
- throw new Error("mpo cannot be updated outside of a git repository");
309
- }
310
- // Scaffolded consumer projects carry .mpo.json provenance — use the
311
- // copier-style three-way template update. Monorepo forks (identified by
312
- // the legacy features/package.json marker) fall through to the upstream-
313
- // merge flow below; anything else gets the provenance guidance instead
314
- // of the fork flow's unrelated errors.
315
- if (readProvenance(projectRoot)) {
316
- await updateApp({ skipInstall: options.skipInstall, version: options.mpoVersion });
317
- return;
318
- }
319
- const legacyFork = await fs.stat(path.resolve(projectRoot, "features/package.json")).then(
320
- (stat) => stat.isFile(),
321
- () => false,
322
- );
323
- if (!legacyFork) {
324
- throw new Error(
325
- ".mpo.json not found — this project predates scaffold provenance. " +
326
- "Create it with { template, cliVersion, name } matching how the project " +
327
- "was generated (cliVersion = the @multiplatform.one/cli that scaffolded it), " +
328
- "then re-run `mpo update`.",
329
- );
330
- }
331
- // Require clean working tree (no staged or unstaged changes)
332
- if (
333
- await spawn("git", ["diff", "--cached", "--quiet"]).then(
334
- () => false,
335
- () => true,
336
- )
337
- ) {
338
- throw new Error("mpo cannot be updated with uncommitted changes (staged changes present)");
339
- }
340
- if (
341
- await spawn("git", ["diff", "--quiet"]).then(
312
+ .action(
313
+ async (options: {
314
+ checkout: string;
315
+ remote: string;
316
+ skipInstall?: boolean;
317
+ mpoVersion?: string;
318
+ }) => {
319
+ // Update prechecks: must be in a git repo
320
+ if (
321
+ await spawn("git", ["rev-parse", "--is-inside-work-tree"]).then(
322
+ () => false,
323
+ () => true,
324
+ )
325
+ ) {
326
+ throw new Error("mpo cannot be updated outside of a git repository");
327
+ }
328
+ // Scaffolded consumer projects carry .mpo.json provenance use the
329
+ // copier-style three-way template update. Monorepo forks (identified by
330
+ // the legacy features/package.json marker) fall through to the upstream-
331
+ // merge flow below; anything else gets the provenance guidance instead
332
+ // of the fork flow's unrelated errors.
333
+ if (readProvenance(projectRoot)) {
334
+ await updateApp({ skipInstall: options.skipInstall, version: options.mpoVersion });
335
+ return;
336
+ }
337
+ const legacyFork = await fs.stat(path.resolve(projectRoot, "features/package.json")).then(
338
+ (stat) => stat.isFile(),
342
339
  () => false,
343
- () => true,
344
- )
345
- ) {
346
- throw new Error(
347
- "multiplatform.one cannot be updated with uncommitted changes (unstaged changes present)",
348
340
  );
349
- }
350
- // Validate multiplatform.one project with clear errors per failure
351
- const rootPkgPath = path.resolve(projectRoot, "package.json");
352
- const featuresPkgPath = path.resolve(projectRoot, "features/package.json");
353
- try {
354
- const stat = await fs.stat(rootPkgPath);
355
- if (!stat.isFile()) {
356
- throw new Error("mpo update requires a valid project: root package.json not found");
341
+ if (!legacyFork) {
342
+ throw new Error(
343
+ ".mpo.json not found — this project predates scaffold provenance. " +
344
+ "Create it with { template, cliVersion, name } matching how the project " +
345
+ "was generated (cliVersion = the @multiplatform.one/cli that scaffolded it), " +
346
+ "then re-run `mpo update`.",
347
+ );
357
348
  }
358
- } catch (err) {
359
- if (err instanceof Error && err.message.includes("valid project")) {
360
- throw err;
349
+ // Require clean working tree (no staged or unstaged changes)
350
+ if (
351
+ await spawn("git", ["diff", "--cached", "--quiet"]).then(
352
+ () => false,
353
+ () => true,
354
+ )
355
+ ) {
356
+ throw new Error("mpo cannot be updated with uncommitted changes (staged changes present)");
361
357
  }
362
- throw new Error("mpo update requires a valid project: root package.json not found");
363
- }
364
- try {
365
- const stat = await fs.stat(featuresPkgPath);
366
- if (!stat.isFile()) {
358
+ if (
359
+ await spawn("git", ["diff", "--quiet"]).then(
360
+ () => false,
361
+ () => true,
362
+ )
363
+ ) {
364
+ throw new Error(
365
+ "multiplatform.one cannot be updated with uncommitted changes (unstaged changes present)",
366
+ );
367
+ }
368
+ // Validate multiplatform.one project with clear errors per failure
369
+ const rootPkgPath = path.resolve(projectRoot, "package.json");
370
+ const featuresPkgPath = path.resolve(projectRoot, "features/package.json");
371
+ try {
372
+ const stat = await fs.stat(rootPkgPath);
373
+ if (!stat.isFile()) {
374
+ throw new Error("mpo update requires a valid project: root package.json not found");
375
+ }
376
+ } catch (err) {
377
+ if (err instanceof Error && err.message.includes("valid project")) {
378
+ throw err;
379
+ }
380
+ throw new Error("mpo update requires a valid project: root package.json not found");
381
+ }
382
+ try {
383
+ const stat = await fs.stat(featuresPkgPath);
384
+ if (!stat.isFile()) {
385
+ throw new Error("mpo update requires a valid project: features/package.json not found");
386
+ }
387
+ } catch (err) {
388
+ if (err instanceof Error && err.message.includes("valid project")) {
389
+ throw err;
390
+ }
367
391
  throw new Error("mpo update requires a valid project: features/package.json not found");
368
392
  }
369
- } catch (err) {
370
- if (err instanceof Error && err.message.includes("valid project")) {
371
- throw err;
393
+ const featuresPkg = JSON.parse(await fs.readFile(featuresPkgPath, "utf8")) as {
394
+ dependencies?: Record<string, string>;
395
+ };
396
+ if (
397
+ !featuresPkg?.dependencies?.["multiplatform.one"] ||
398
+ !String(featuresPkg.dependencies["multiplatform.one"]).length
399
+ ) {
400
+ throw new Error(
401
+ "mpo update requires a valid project: features must depend on multiplatform.one",
402
+ );
372
403
  }
373
- throw new Error("mpo update requires a valid project: features/package.json not found");
374
- }
375
- const featuresPkg = JSON.parse(await fs.readFile(featuresPkgPath, "utf8")) as {
376
- dependencies?: Record<string, string>;
377
- };
378
- if (
379
- !featuresPkg?.dependencies?.["multiplatform.one"] ||
380
- !String(featuresPkg.dependencies["multiplatform.one"]).length
381
- ) {
382
- throw new Error(
383
- "mpo update requires a valid project: features must depend on multiplatform.one",
384
- );
385
- }
386
404
 
387
- // Merge from upstream (update.sh: add remote, fetch, merge, remove remote, restore .updateignore list, pnpm lint)
388
- const updateScript = path.resolve(
389
- path.dirname(fileURLToPath(import.meta.url)),
390
- "../../scripts/update.sh",
391
- );
392
- await spawn("sh", [updateScript, options.remote, options.checkout], {
393
- cwd: projectRoot,
394
- stdio: "inherit",
395
- shell: true,
396
- });
405
+ // Merge from upstream (update.sh: add remote, fetch, merge, remove remote, restore .updateignore list, pnpm lint)
406
+ const updateScript = path.resolve(
407
+ path.dirname(fileURLToPath(import.meta.url)),
408
+ "../../scripts/update.sh",
409
+ );
410
+ await spawn("sh", [updateScript, options.remote, options.checkout], {
411
+ cwd: projectRoot,
412
+ stdio: "inherit",
413
+ shell: true,
414
+ });
397
415
 
398
- // Re-apply modify step with auto-detected apps and services
399
- const presentServices = await getPresentServices(projectRoot);
400
- const presentApps = await getPresentApps(projectRoot);
401
- runModifyStep(projectRoot, presentServices, presentApps);
416
+ // Re-apply modify step with auto-detected apps and services
417
+ const presentServices = await getPresentServices(projectRoot);
418
+ const presentApps = await getPresentApps(projectRoot);
419
+ runModifyStep(projectRoot, presentServices, presentApps);
402
420
 
403
- // Post-update refresh: .vscode from current workspace
404
- try {
405
- await generateVscodeConfig(projectRoot);
406
- } catch {
407
- // may fail in older projects without apps/ layout
408
- }
409
- });
421
+ // Post-update refresh: .vscode from current workspace
422
+ try {
423
+ await generateVscodeConfig(projectRoot);
424
+ } catch {
425
+ // may fail in older projects without apps/ layout
426
+ }
427
+ },
428
+ );
410
429
 
411
430
  program
412
431
  .command("wait")
@@ -3,7 +3,7 @@ import { type Dirent, existsSync, readFileSync, readdirSync, rmSync, writeFileSy
3
3
  import { join, resolve } from "node:path";
4
4
  import inquirer from "inquirer";
5
5
 
6
- const repoUrl = "https://gitlab.com/bitspur/multiplatform.one/multiplatform.one.git";
6
+ const repoUrl = "https://gitlab.com/bitspur/frappe/multiplatform.one.git";
7
7
 
8
8
  const services = ["frappe", "solana", "ethereum", "sui"] as const;
9
9