@mondaydotcomorg/z2h-cli 0.1.0 → 0.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.
Files changed (114) hide show
  1. package/dist/commands/__tests__/create-workspace.test.d.ts +2 -0
  2. package/dist/commands/__tests__/create-workspace.test.d.ts.map +1 -0
  3. package/dist/commands/__tests__/create.test.d.ts +2 -0
  4. package/dist/commands/__tests__/create.test.d.ts.map +1 -0
  5. package/dist/commands/create-workspace.d.ts +5 -0
  6. package/dist/commands/create-workspace.d.ts.map +1 -0
  7. package/dist/commands/create-workspace.js +80 -0
  8. package/dist/commands/create.d.ts +7 -0
  9. package/dist/commands/create.d.ts.map +1 -0
  10. package/dist/commands/create.js +97 -0
  11. package/dist/constants.d.ts +1 -14
  12. package/dist/constants.d.ts.map +1 -1
  13. package/dist/constants.js +11 -34
  14. package/dist/esm/commands/__tests__/create-workspace.test.d.ts +2 -0
  15. package/dist/esm/commands/__tests__/create-workspace.test.d.ts.map +1 -0
  16. package/dist/esm/commands/__tests__/create.test.d.ts +2 -0
  17. package/dist/esm/commands/__tests__/create.test.d.ts.map +1 -0
  18. package/dist/esm/commands/create-workspace.d.ts +5 -0
  19. package/dist/esm/commands/create-workspace.d.ts.map +1 -0
  20. package/dist/esm/commands/create-workspace.mjs +74 -0
  21. package/dist/esm/commands/create.d.ts +7 -0
  22. package/dist/esm/commands/create.d.ts.map +1 -0
  23. package/dist/esm/commands/create.mjs +91 -0
  24. package/dist/esm/constants.d.ts +1 -14
  25. package/dist/esm/constants.d.ts.map +1 -1
  26. package/dist/esm/constants.mjs +11 -32
  27. package/dist/esm/index.mjs +28 -0
  28. package/dist/esm/shadow/__tests__/consumer-pkg.test.d.ts +2 -0
  29. package/dist/esm/shadow/__tests__/consumer-pkg.test.d.ts.map +1 -0
  30. package/dist/esm/shadow/__tests__/merge-package-json.test.d.ts +2 -0
  31. package/dist/esm/shadow/__tests__/merge-package-json.test.d.ts.map +1 -0
  32. package/dist/esm/shadow/consumer-pkg.d.ts +5 -15
  33. package/dist/esm/shadow/consumer-pkg.d.ts.map +1 -1
  34. package/dist/esm/shadow/consumer-pkg.mjs +11 -72
  35. package/dist/esm/shadow/merge-package-json.d.ts +10 -4
  36. package/dist/esm/shadow/merge-package-json.d.ts.map +1 -1
  37. package/dist/esm/shadow/merge-package-json.mjs +12 -11
  38. package/dist/esm/shadow/sync.mjs +2 -2
  39. package/dist/esm/templates/app/claude-settings.mjs +10 -0
  40. package/dist/esm/types.d.ts +0 -1
  41. package/dist/esm/types.d.ts.map +1 -1
  42. package/dist/esm/util/__tests__/templates.test.d.ts +2 -0
  43. package/dist/esm/util/__tests__/templates.test.d.ts.map +1 -0
  44. package/dist/esm/util/__tests__/workspace-templates-shipped.test.d.ts +2 -0
  45. package/dist/esm/util/__tests__/workspace-templates-shipped.test.d.ts.map +1 -0
  46. package/dist/esm/util/__tests__/workspace.test.d.ts +2 -0
  47. package/dist/esm/util/__tests__/workspace.test.d.ts.map +1 -0
  48. package/dist/esm/util/s3/upload.mjs +1 -1
  49. package/dist/esm/util/templates.d.ts +22 -0
  50. package/dist/esm/util/templates.d.ts.map +1 -0
  51. package/dist/esm/util/templates.mjs +36 -0
  52. package/dist/esm/util/workspace.d.ts +20 -0
  53. package/dist/esm/util/workspace.d.ts.map +1 -0
  54. package/dist/esm/util/workspace.mjs +46 -0
  55. package/dist/index.js +28 -0
  56. package/dist/shadow/__tests__/consumer-pkg.test.d.ts +2 -0
  57. package/dist/shadow/__tests__/consumer-pkg.test.d.ts.map +1 -0
  58. package/dist/shadow/__tests__/merge-package-json.test.d.ts +2 -0
  59. package/dist/shadow/__tests__/merge-package-json.test.d.ts.map +1 -0
  60. package/dist/shadow/consumer-pkg.d.ts +5 -15
  61. package/dist/shadow/consumer-pkg.d.ts.map +1 -1
  62. package/dist/shadow/consumer-pkg.js +10 -72
  63. package/dist/shadow/merge-package-json.d.ts +10 -4
  64. package/dist/shadow/merge-package-json.d.ts.map +1 -1
  65. package/dist/shadow/merge-package-json.js +11 -10
  66. package/dist/shadow/sync.js +2 -2
  67. package/dist/templates/app/claude-settings.js +13 -0
  68. package/dist/types.d.ts +0 -1
  69. package/dist/types.d.ts.map +1 -1
  70. package/dist/util/__tests__/templates.test.d.ts +2 -0
  71. package/dist/util/__tests__/templates.test.d.ts.map +1 -0
  72. package/dist/util/__tests__/workspace-templates-shipped.test.d.ts +2 -0
  73. package/dist/util/__tests__/workspace-templates-shipped.test.d.ts.map +1 -0
  74. package/dist/util/__tests__/workspace.test.d.ts +2 -0
  75. package/dist/util/__tests__/workspace.test.d.ts.map +1 -0
  76. package/dist/util/s3/upload.js +1 -1
  77. package/dist/util/templates.d.ts +22 -0
  78. package/dist/util/templates.d.ts.map +1 -0
  79. package/dist/util/templates.js +45 -0
  80. package/dist/util/workspace.d.ts +20 -0
  81. package/dist/util/workspace.d.ts.map +1 -0
  82. package/dist/util/workspace.js +57 -0
  83. package/package.json +5 -3
  84. package/src/commands/__tests__/create-workspace.test.ts +78 -0
  85. package/src/commands/__tests__/create.test.ts +111 -0
  86. package/src/commands/create-workspace.ts +85 -0
  87. package/src/commands/create.ts +113 -0
  88. package/src/constants.ts +11 -34
  89. package/src/index.ts +28 -0
  90. package/src/shadow/__tests__/consumer-pkg.test.ts +56 -0
  91. package/src/shadow/__tests__/merge-package-json.test.ts +44 -0
  92. package/src/shadow/consumer-pkg.ts +10 -88
  93. package/src/shadow/merge-package-json.ts +12 -11
  94. package/src/shadow/sync.ts +2 -2
  95. package/src/templates/app/CLAUDE.md +72 -0
  96. package/src/templates/app/claude-settings.json +7 -0
  97. package/src/templates/app/default-index.tsx.tmpl +21 -0
  98. package/src/templates/app/gitignore +4 -0
  99. package/src/templates/app/html-index.tsx.tmpl +21 -0
  100. package/src/templates/app/package.json.tmpl +13 -0
  101. package/src/templates/workspace/README.md +39 -0
  102. package/src/templates/workspace/gitignore +23 -0
  103. package/src/templates/workspace/npmrc +3 -0
  104. package/src/templates/workspace/nvmrc +1 -0
  105. package/src/templates/workspace/package.json.tmpl +23 -0
  106. package/src/templates/workspace/yarn-releases/yarn-4.5.1.cjs +934 -0
  107. package/src/templates/workspace/yarnrc.yml +7 -0
  108. package/src/types.ts +4 -1
  109. package/src/util/__tests__/templates.test.ts +27 -0
  110. package/src/util/__tests__/workspace-templates-shipped.test.ts +35 -0
  111. package/src/util/__tests__/workspace.test.ts +65 -0
  112. package/src/util/s3/upload.ts +1 -1
  113. package/src/util/templates.ts +37 -0
  114. package/src/util/workspace.ts +52 -0
@@ -10,23 +10,24 @@ const _interopDefault = e => e && e.__esModule ? e : { default: e };
10
10
  const crypto__default = /*#__PURE__*/_interopDefault(crypto);
11
11
 
12
12
  /**
13
- * Build the shadow package.json. Declares the shared runtime deps as
14
- * peerDependencies — yarn resolves them from the workspace root (the
15
- * consumer's package.json), guaranteeing a single React instance across
16
- * the wrapper and consumer src.
13
+ * Build the shadow package.json.
14
+ *
15
+ * Runtime deps live at the workspace root (`~/zth-projects/`) and are
16
+ * hoisted in via yarn workspaces — no need to mirror them here. The shadow
17
+ * only declares the toolchain it directly executes (`trident-toolkit`) so
18
+ * yarn berry wires the `trident` bin into the shadow's script PATH.
19
+ *
20
+ * The `@trident/` scope is preserved so trident-toolkit strips it and the
21
+ * emitted bundle becomes `MF-mf-<appName>`, matching the URL the host MF
22
+ * routes to.
17
23
  */
18
24
  function buildShadowPkg(consumerPkg) {
19
- // Use the `@trident/` scope so trident-toolkit strips it and the emitted
20
- // bundle becomes `MF-mf-<appName>`, matching the URL the host MF routes to.
21
- // The scope also keeps the shadow's name distinct from the consumer's
22
- // package.json name (same workspace, different identifiers).
23
25
  return {
24
26
  name: `@trident/${util_appName.mfAppName(consumerPkg.name)}`,
25
27
  version: '0.0.1',
26
28
  private: true,
27
29
  scripts: constants.SHADOW_SCRIPTS,
28
- peerDependencies: { ...constants.PROTECTED_DEPS },
29
- devDependencies: constants.WORKSPACE_DEV_DEPS,
30
+ devDependencies: { ...constants.SHADOW_DEV_DEPS },
30
31
  };
31
32
  }
32
33
  /**
@@ -18,8 +18,8 @@ async function syncShadow(paths, consumerPkg) {
18
18
  const oldHash = (await fsExtra.pathExists(paths.shadowDepsHash)) ? await fsExtra.readFile(paths.shadowDepsHash, 'utf8') : null;
19
19
  if (oldHash !== newHash) {
20
20
  util_logger.info(oldHash ? 'consumer deps changed — yarn install' : 'first run — yarn install');
21
- // Yarn 4 is cache-first and non-interactive by default
22
- // in non-TTY contexts, so no flags are needed
21
+ // Yarn 4 is cache-first and non-interactive by default in non-TTY
22
+ // contexts, so plain `yarn install` is all we need.
23
23
  await execa.execa('yarn', ['install'], {
24
24
  cwd: paths.consumerDir,
25
25
  stdio: 'inherit',
@@ -0,0 +1,13 @@
1
+ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: 'Module' } });
2
+
3
+ var permissions = {
4
+ allow: [
5
+ "Skill(z2h:*)"
6
+ ]
7
+ };
8
+ const claudeSettings = {
9
+ permissions: permissions
10
+ };
11
+
12
+ exports.default = claudeSettings;
13
+ exports.permissions = permissions;
package/dist/types.d.ts CHANGED
@@ -20,7 +20,6 @@ export interface ShadowPkg {
20
20
  version: string;
21
21
  private: true;
22
22
  scripts: Record<string, string>;
23
- peerDependencies: Record<string, string>;
24
23
  devDependencies: Record<string, string>;
25
24
  }
26
25
  export interface ShadowMetadata {
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,EAAE,GAAG;QAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAChD,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACtB;AAGD,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,IAAI,CAAC;IACd,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACzC;AAGD,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAGD,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;CACxB;AAGD,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,aAAa,CAAC;CACtB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,EAAE,GAAG;QAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAChD,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CACtB;AAOD,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,IAAI,CAAC;IACd,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACzC;AAGD,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAGD,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;CACxB;AAGD,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,aAAa,CAAC;CACtB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=templates.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"templates.test.d.ts","sourceRoot":"","sources":["../../../src/util/__tests__/templates.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=workspace-templates-shipped.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace-templates-shipped.test.d.ts","sourceRoot":"","sources":["../../../src/util/__tests__/workspace-templates-shipped.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=workspace.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace.test.d.ts","sourceRoot":"","sources":["../../../src/util/__tests__/workspace.test.ts"],"names":[],"mappings":""}
@@ -1,8 +1,8 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
 
3
3
  const path = require('node:path');
4
- const fsExtra = require('fs-extra');
5
4
  const promises = require('node:fs/promises');
5
+ const fsExtra = require('fs-extra');
6
6
  const mimeTypes = require('mime-types');
7
7
  const pMap = require('p-map');
8
8
  const clientS3 = require('@aws-sdk/client-s3');
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Substitute `__PLACEHOLDER__` tokens in `content` with values from `vars`.
3
+ * Values are inserted literally — no regex special-char interpretation —
4
+ * which matters for content like `$1` or `&` that could otherwise be
5
+ * captured by `String.prototype.replace`.
6
+ */
7
+ export declare function renderTemplate(content: string, vars: Record<string, string>): string;
8
+ /**
9
+ * Absolute path to the bundled `templates/` directory under `src/`.
10
+ *
11
+ * The CLI's `package.json` `files` array ships `src/` (so `src/templates/`
12
+ * lands in published packages). The build emits `.ts` files to `dist/`,
13
+ * but raw template assets (`.tmpl`, `.md`, `gitignore`, `.json`) live only
14
+ * under `src/templates/`. Resolving here keeps a single source of truth
15
+ * for both:
16
+ * - dev (vitest, tsx) → `<pkg>/src/util/` + ../../src/templates → `<pkg>/src/templates`
17
+ * - published install (dist/util) → `<pkg>/dist/util/` + ../../src/templates → `<pkg>/src/templates`
18
+ */
19
+ export declare function getTemplatesDir(): string;
20
+ export type AppTemplate = 'default' | 'html-embed';
21
+ export declare const APP_TEMPLATES: AppTemplate[];
22
+ //# sourceMappingURL=templates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../../src/util/templates.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAQpF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAGxC;AAED,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,YAAY,CAAC;AACnD,eAAO,MAAM,aAAa,EAAE,WAAW,EAA8B,CAAC"}
@@ -0,0 +1,45 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+
3
+ const path = require('node:path');
4
+ const node_url = require('node:url');
5
+
6
+ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
7
+ const _interopDefault = e => e && e.__esModule ? e : { default: e };
8
+
9
+ const path__default = /*#__PURE__*/_interopDefault(path);
10
+
11
+ /**
12
+ * Substitute `__PLACEHOLDER__` tokens in `content` with values from `vars`.
13
+ * Values are inserted literally — no regex special-char interpretation —
14
+ * which matters for content like `$1` or `&` that could otherwise be
15
+ * captured by `String.prototype.replace`.
16
+ */
17
+ function renderTemplate(content, vars) {
18
+ let out = content;
19
+ for (const [key, value] of Object.entries(vars)) {
20
+ // Use split+join (literal pattern) to avoid regex interpretation of
21
+ // special chars in `value`.
22
+ out = out.split(key).join(value);
23
+ }
24
+ return out;
25
+ }
26
+ /**
27
+ * Absolute path to the bundled `templates/` directory under `src/`.
28
+ *
29
+ * The CLI's `package.json` `files` array ships `src/` (so `src/templates/`
30
+ * lands in published packages). The build emits `.ts` files to `dist/`,
31
+ * but raw template assets (`.tmpl`, `.md`, `gitignore`, `.json`) live only
32
+ * under `src/templates/`. Resolving here keeps a single source of truth
33
+ * for both:
34
+ * - dev (vitest, tsx) → `<pkg>/src/util/` + ../../src/templates → `<pkg>/src/templates`
35
+ * - published install (dist/util) → `<pkg>/dist/util/` + ../../src/templates → `<pkg>/src/templates`
36
+ */
37
+ function getTemplatesDir() {
38
+ const here = path__default.default.dirname(node_url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('util/templates.js', document.baseURI).href))));
39
+ return path__default.default.resolve(here, '..', '..', 'src', 'templates');
40
+ }
41
+ const APP_TEMPLATES = ['default', 'html-embed'];
42
+
43
+ exports.APP_TEMPLATES = APP_TEMPLATES;
44
+ exports.getTemplatesDir = getTemplatesDir;
45
+ exports.renderTemplate = renderTemplate;
@@ -0,0 +1,20 @@
1
+ export declare const WORKSPACE_DIR_NAME = "zth-projects";
2
+ export declare const WORKSPACE_MARKER_FIELD = "z2hWorkspace";
3
+ /**
4
+ * The Z2H workspace home is hardcoded to `~/zth-projects/` for users.
5
+ * Tests override via the `Z2H_WORKSPACE_HOME` env var so they can use a
6
+ * tmp dir without touching the user's real home.
7
+ */
8
+ export declare function getWorkspaceHome(): string;
9
+ /**
10
+ * Returns true iff `<dir>/package.json` exists and has `"z2hWorkspace": true`.
11
+ * Any read or parse error returns false (defensive — callers route to a
12
+ * helpful message via assertWorkspace).
13
+ */
14
+ export declare function isZ2hWorkspace(dir: string): Promise<boolean>;
15
+ /**
16
+ * Resolves to the workspace home path if the marker is present; otherwise
17
+ * throws an Error pointing the user at `z2h-cli create-workspace`.
18
+ */
19
+ export declare function assertWorkspace(): Promise<string>;
20
+ //# sourceMappingURL=workspace.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace.d.ts","sourceRoot":"","sources":["../../src/util/workspace.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,kBAAkB,iBAAiB,CAAC;AACjD,eAAO,MAAM,sBAAsB,iBAAiB,CAAC;AAErD;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAMD;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAWlE;AAED;;;GAGG;AACH,wBAAsB,eAAe,IAAI,OAAO,CAAC,MAAM,CAAC,CASvD"}
@@ -0,0 +1,57 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+
3
+ const path = require('node:path');
4
+ const os = require('node:os');
5
+ const fsExtra = require('fs-extra');
6
+
7
+ const _interopDefault = e => e && e.__esModule ? e : { default: e };
8
+
9
+ const path__default = /*#__PURE__*/_interopDefault(path);
10
+ const os__default = /*#__PURE__*/_interopDefault(os);
11
+
12
+ const WORKSPACE_DIR_NAME = 'zth-projects';
13
+ const WORKSPACE_MARKER_FIELD = 'z2hWorkspace';
14
+ /**
15
+ * The Z2H workspace home is hardcoded to `~/zth-projects/` for users.
16
+ * Tests override via the `Z2H_WORKSPACE_HOME` env var so they can use a
17
+ * tmp dir without touching the user's real home.
18
+ */
19
+ function getWorkspaceHome() {
20
+ return process.env.Z2H_WORKSPACE_HOME ?? path__default.default.join(os__default.default.homedir(), WORKSPACE_DIR_NAME);
21
+ }
22
+ /**
23
+ * Returns true iff `<dir>/package.json` exists and has `"z2hWorkspace": true`.
24
+ * Any read or parse error returns false (defensive — callers route to a
25
+ * helpful message via assertWorkspace).
26
+ */
27
+ async function isZ2hWorkspace(dir) {
28
+ const pkgPath = path__default.default.join(dir, 'package.json');
29
+ if (!(await fsExtra.pathExists(pkgPath))) {
30
+ return false;
31
+ }
32
+ try {
33
+ const pkg = (await fsExtra.readJson(pkgPath));
34
+ return pkg[WORKSPACE_MARKER_FIELD] === true;
35
+ }
36
+ catch {
37
+ return false;
38
+ }
39
+ }
40
+ /**
41
+ * Resolves to the workspace home path if the marker is present; otherwise
42
+ * throws an Error pointing the user at `z2h-cli create-workspace`.
43
+ */
44
+ async function assertWorkspace() {
45
+ const home = getWorkspaceHome();
46
+ if (await isZ2hWorkspace(home)) {
47
+ return home;
48
+ }
49
+ throw new Error(`No Z2H workspace at ${home}. Run "z2h-cli create-workspace" first ` +
50
+ `(or set Z2H_WORKSPACE_HOME if you keep the workspace elsewhere).`);
51
+ }
52
+
53
+ exports.WORKSPACE_DIR_NAME = WORKSPACE_DIR_NAME;
54
+ exports.WORKSPACE_MARKER_FIELD = WORKSPACE_MARKER_FIELD;
55
+ exports.assertWorkspace = assertWorkspace;
56
+ exports.getWorkspaceHome = getWorkspaceHome;
57
+ exports.isZ2hWorkspace = isZ2hWorkspace;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mondaydotcomorg/z2h-cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "bin": "./bin/z2h-cli.js",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -20,7 +20,8 @@
20
20
  ]
21
21
  },
22
22
  "scripts": {
23
- "test": "echo noop",
23
+ "test": "vitest run",
24
+ "test:watch": "vitest",
24
25
  "lint": "trident-library lint",
25
26
  "build": "trident-library build",
26
27
  "watch": "trident-library build -w"
@@ -46,7 +47,8 @@
46
47
  "@types/fs-extra": "^11.0.4",
47
48
  "@types/mime-types": "^2.1.4",
48
49
  "@types/node": "^22.19.0",
49
- "typescript": "^5.7.2"
50
+ "typescript": "^5.7.2",
51
+ "vitest": "^2.1.4"
50
52
  },
51
53
  "repository": {
52
54
  "type": "git",
@@ -0,0 +1,78 @@
1
+ import path from 'node:path';
2
+ import os from 'node:os';
3
+ import { mkdtemp, rm, writeJson, readJson, ensureDir, pathExists } from 'fs-extra';
4
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
5
+
6
+ import { execa } from 'execa';
7
+ import { createWorkspaceCommand } from '../create-workspace';
8
+
9
+ // Mock execa so `yarn install` doesn't actually run.
10
+ vi.mock('execa', () => ({
11
+ execa: vi.fn().mockResolvedValue({ stdout: '', stderr: '', exitCode: 0 }),
12
+ }));
13
+
14
+ describe('create-workspace', () => {
15
+ let tmp: string;
16
+
17
+ beforeEach(async () => {
18
+ tmp = await mkdtemp(path.join(os.tmpdir(), 'z2h-cw-'));
19
+ process.env.Z2H_WORKSPACE_HOME = tmp;
20
+ vi.mocked(execa).mockClear();
21
+ // After mkdtemp the dir exists and is empty; we want absent for the
22
+ // "happy path" tests, so remove it.
23
+ await rm(tmp, { recursive: true, force: true });
24
+ });
25
+
26
+ afterEach(async () => {
27
+ delete process.env.Z2H_WORKSPACE_HOME;
28
+ await rm(tmp, { recursive: true, force: true });
29
+ });
30
+
31
+ it('creates package.json with the marker, gitignore, README, then runs yarn install', async () => {
32
+ await createWorkspaceCommand({});
33
+
34
+ const pkgPath = path.join(tmp, 'package.json');
35
+ expect(await pathExists(pkgPath)).toBe(true);
36
+ const pkg = await readJson(pkgPath);
37
+ expect(pkg.z2hWorkspace).toBe(true);
38
+ expect(pkg.workspaces).toEqual(['*', '*/.zth']);
39
+ expect(pkg.private).toBe(true);
40
+ expect(pkg.dependencies).toBeDefined();
41
+ expect(pkg.devDependencies).toBeDefined();
42
+
43
+ expect(await pathExists(path.join(tmp, '.gitignore'))).toBe(true);
44
+ expect(await pathExists(path.join(tmp, '.yarnrc.yml'))).toBe(true);
45
+ expect(await pathExists(path.join(tmp, '.nvmrc'))).toBe(true);
46
+ expect(await pathExists(path.join(tmp, '.npmrc'))).toBe(true);
47
+ expect(await pathExists(path.join(tmp, 'README.md'))).toBe(true);
48
+
49
+ expect(execa).toHaveBeenCalledWith(
50
+ 'yarn',
51
+ expect.arrayContaining(['install']),
52
+ expect.objectContaining({ cwd: tmp })
53
+ );
54
+ });
55
+
56
+ it('refuses if the target dir is non-empty', async () => {
57
+ await ensureDir(tmp);
58
+ await writeJson(path.join(tmp, 'somefile.json'), { hello: 'world' });
59
+
60
+ await expect(createWorkspaceCommand({})).rejects.toThrow(/non-empty/);
61
+ expect(execa).not.toHaveBeenCalled();
62
+ });
63
+
64
+ it('substitutes the current z2h-cli version into the workspace template', async () => {
65
+ await createWorkspaceCommand({});
66
+
67
+ const pkg = await readJson(path.join(tmp, 'package.json'));
68
+ const cliDep = pkg.devDependencies['@mondaydotcomorg/z2h-cli'];
69
+ expect(cliDep).toBeTruthy();
70
+ expect(cliDep).not.toBe('__Z2H_CLI_VERSION__'); // should be substituted
71
+ });
72
+
73
+ it('ships the yarn berry release inline so any `yarn` invocation runs 4.x without corepack', async () => {
74
+ await createWorkspaceCommand({});
75
+
76
+ expect(await pathExists(path.join(tmp, '.yarn', 'releases', 'yarn-4.5.1.cjs'))).toBe(true);
77
+ });
78
+ });
@@ -0,0 +1,111 @@
1
+ import path from 'node:path';
2
+ import os from 'node:os';
3
+ import { mkdtemp, rm, writeJson, readJson, ensureDir, pathExists, writeFile } from 'fs-extra';
4
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
5
+
6
+ import { execa } from 'execa';
7
+ import { createCommand } from '../create';
8
+
9
+ vi.mock('execa', () => ({
10
+ execa: vi.fn().mockResolvedValue({ stdout: '', stderr: '', exitCode: 0 }),
11
+ }));
12
+
13
+ async function makeWorkspace(tmp: string): Promise<void> {
14
+ await ensureDir(tmp);
15
+ await writeJson(path.join(tmp, 'package.json'), {
16
+ name: '@mondaydotcomorg/zth-projects-workspace',
17
+ private: true,
18
+ z2hWorkspace: true,
19
+ workspaces: ['*', '*/.zth'],
20
+ });
21
+ }
22
+
23
+ describe('create', () => {
24
+ let tmp: string;
25
+
26
+ beforeEach(async () => {
27
+ tmp = await mkdtemp(path.join(os.tmpdir(), 'z2h-c-'));
28
+ process.env.Z2H_WORKSPACE_HOME = tmp;
29
+ vi.mocked(execa).mockClear();
30
+ });
31
+
32
+ afterEach(async () => {
33
+ delete process.env.Z2H_WORKSPACE_HOME;
34
+ await rm(tmp, { recursive: true, force: true });
35
+ });
36
+
37
+ it('refuses when there is no workspace marker', async () => {
38
+ await expect(createCommand('demo-app', {})).rejects.toThrow(/create-workspace/);
39
+ expect(execa).not.toHaveBeenCalled();
40
+ });
41
+
42
+ it('rejects invalid app names', async () => {
43
+ await makeWorkspace(tmp);
44
+ await expect(createCommand('Invalid_Name', {})).rejects.toThrow(/lowercase kebab-case/);
45
+ await expect(createCommand('1starts-with-digit', {})).rejects.toThrow(/lowercase kebab-case/);
46
+ });
47
+
48
+ it('refuses when the target app dir already exists and is non-empty', async () => {
49
+ await makeWorkspace(tmp);
50
+ const appDir = path.join(tmp, 'demo-app');
51
+ await ensureDir(appDir);
52
+ await writeFile(path.join(appDir, 'something.txt'), 'hi');
53
+ await expect(createCommand('demo-app', {})).rejects.toThrow(/non-empty/);
54
+ });
55
+
56
+ it('scaffolds an app with the default React template', async () => {
57
+ await makeWorkspace(tmp);
58
+ await createCommand('demo-app', {});
59
+
60
+ const appDir = path.join(tmp, 'demo-app');
61
+ expect(await pathExists(path.join(appDir, 'package.json'))).toBe(true);
62
+ expect(await pathExists(path.join(appDir, 'src', 'index.tsx'))).toBe(true);
63
+ expect(await pathExists(path.join(appDir, '.gitignore'))).toBe(true);
64
+ expect(await pathExists(path.join(appDir, '.claude', 'settings.json'))).toBe(true);
65
+ expect(await pathExists(path.join(appDir, 'CLAUDE.md'))).toBe(true);
66
+
67
+ const pkg = await readJson(path.join(appDir, 'package.json'));
68
+ expect(pkg.name).toBe('demo-app');
69
+ expect(pkg.private).toBe(true);
70
+ expect(pkg.scripts.start).toBe('z2h-cli dev');
71
+
72
+ const indexSrc = (await import('node:fs/promises')).readFile;
73
+ const content = await indexSrc(path.join(appDir, 'src', 'index.tsx'), 'utf8');
74
+ expect(content).toContain('export default function DemoApp()');
75
+ expect(content).not.toContain('__APP_COMPONENT__');
76
+
77
+ expect(execa).toHaveBeenCalledWith(
78
+ 'yarn',
79
+ expect.arrayContaining(['install']),
80
+ expect.objectContaining({ cwd: tmp })
81
+ );
82
+ });
83
+
84
+ it('scaffolds an html-embed app and copies the html file', async () => {
85
+ await makeWorkspace(tmp);
86
+ const htmlPath = path.join(tmp, 'page.html');
87
+ await writeFile(htmlPath, '<html><body>Hi</body></html>');
88
+
89
+ await createCommand('html-app', { template: 'html-embed', htmlFile: htmlPath });
90
+
91
+ const appDir = path.join(tmp, 'html-app');
92
+ expect(await pathExists(path.join(appDir, 'src', 'page.html'))).toBe(true);
93
+ const indexContent = await (
94
+ await import('node:fs/promises')
95
+ ).readFile(path.join(appDir, 'src', 'index.tsx'), 'utf8');
96
+ expect(indexContent).toContain("import pageUrl from './page.html?url'");
97
+ expect(indexContent).toContain('export default function HtmlApp()');
98
+ });
99
+
100
+ it('rejects unknown templates', async () => {
101
+ await makeWorkspace(tmp);
102
+ await expect(createCommand('demo', { template: 'nonexistent' })).rejects.toThrow(/unknown template/i);
103
+ });
104
+
105
+ it('rejects --html-file when the file does not exist', async () => {
106
+ await makeWorkspace(tmp);
107
+ await expect(createCommand('demo', { template: 'html-embed', htmlFile: '/no/such/file.html' })).rejects.toThrow(
108
+ /html file/i
109
+ );
110
+ });
111
+ });
@@ -0,0 +1,85 @@
1
+ import path from 'node:path';
2
+ import { readFile } from 'node:fs/promises';
3
+ import { ensureDir, readJson, writeFile, pathExists, readdir, copy } from 'fs-extra';
4
+ import { execa } from 'execa';
5
+ import { info, output, setOutputMode } from '../util/logger';
6
+ import { getWorkspaceHome } from '../util/workspace';
7
+ import { getTemplatesDir, renderTemplate } from '../util/templates';
8
+
9
+ export interface CreateWorkspaceOptions {
10
+ json?: boolean;
11
+ }
12
+
13
+ /**
14
+ * Read the current z2h-cli version from its own package.json — the version
15
+ * that gets pinned into the workspace's devDependencies. That way the
16
+ * workspace's `@mondaydotcomorg/z2h-cli` always matches the version that
17
+ * bootstrapped it.
18
+ */
19
+ async function readCurrentCliVersion(): Promise<string> {
20
+ // util/templates.ts already locates `<package-root>/src/templates`. The
21
+ // package's own package.json sits two levels above templates/.
22
+ const templatesDir = getTemplatesDir();
23
+ const pkgPath = path.resolve(templatesDir, '..', '..', 'package.json');
24
+ const pkg = (await readJson(pkgPath)) as { version: string };
25
+ return `^${pkg.version}`;
26
+ }
27
+
28
+ export async function createWorkspaceCommand(opts: CreateWorkspaceOptions): Promise<void> {
29
+ if (opts.json) {
30
+ setOutputMode('json');
31
+ }
32
+
33
+ const home = getWorkspaceHome();
34
+ const templatesDir = getTemplatesDir();
35
+ const wsTemplatesDir = path.join(templatesDir, 'workspace');
36
+
37
+ // Refuse non-empty target.
38
+ if (await pathExists(home)) {
39
+ const entries = await readdir(home);
40
+ if (entries.length > 0) {
41
+ throw new Error(`${home} is non-empty. Wipe it before running create-workspace.`);
42
+ }
43
+ } else {
44
+ await ensureDir(home);
45
+ }
46
+
47
+ const [cliVersion, pkgRaw] = await Promise.all([
48
+ readCurrentCliVersion(),
49
+ readFile(path.join(wsTemplatesDir, 'package.json.tmpl'), 'utf8'),
50
+ ]);
51
+
52
+ // Sources have plain names (gitignore, yarnrc.yml, nvmrc, npmrc) so they
53
+ // aren't interpreted as the active config files of *this* package; we
54
+ // rename to dotfiles at the destination.
55
+ //
56
+ // Yarn berry ships inline at .yarn/releases/. The workspace's .yarnrc.yml
57
+ // sets `yarnPath` to point here, so any `yarn` invocation (system classic
58
+ // or otherwise) reads the rc, delegates to this binary, and runs as 4.5.1
59
+ // — no corepack, no global pin needed. Mirrors how bigbrain-z2h ships its
60
+ // own yarn release.
61
+ await Promise.all([
62
+ writeFile(path.join(home, 'package.json'), renderTemplate(pkgRaw, { __Z2H_CLI_VERSION__: cliVersion })),
63
+ copy(path.join(wsTemplatesDir, 'gitignore'), path.join(home, '.gitignore')),
64
+ copy(path.join(wsTemplatesDir, 'yarnrc.yml'), path.join(home, '.yarnrc.yml')),
65
+ copy(path.join(wsTemplatesDir, 'nvmrc'), path.join(home, '.nvmrc')),
66
+ copy(path.join(wsTemplatesDir, 'npmrc'), path.join(home, '.npmrc')),
67
+ copy(path.join(wsTemplatesDir, 'README.md'), path.join(home, 'README.md')),
68
+ copy(
69
+ path.join(wsTemplatesDir, 'yarn-releases', 'yarn-4.5.1.cjs'),
70
+ path.join(home, '.yarn', 'releases', 'yarn-4.5.1.cjs')
71
+ ),
72
+ ]);
73
+
74
+ info(`bootstrapping workspace at ${home}`);
75
+ await execa('yarn', ['install'], {
76
+ cwd: home,
77
+ stdio: 'inherit',
78
+ });
79
+
80
+ output(`[z2h-cli] workspace ready at ${home}`, {
81
+ ok: true,
82
+ workspace: home,
83
+ z2hCliVersion: cliVersion,
84
+ });
85
+ }