@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.
- package/dist/commands/__tests__/create-workspace.test.d.ts +2 -0
- package/dist/commands/__tests__/create-workspace.test.d.ts.map +1 -0
- package/dist/commands/__tests__/create.test.d.ts +2 -0
- package/dist/commands/__tests__/create.test.d.ts.map +1 -0
- package/dist/commands/create-workspace.d.ts +5 -0
- package/dist/commands/create-workspace.d.ts.map +1 -0
- package/dist/commands/create-workspace.js +80 -0
- package/dist/commands/create.d.ts +7 -0
- package/dist/commands/create.d.ts.map +1 -0
- package/dist/commands/create.js +97 -0
- package/dist/constants.d.ts +1 -14
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +11 -34
- package/dist/esm/commands/__tests__/create-workspace.test.d.ts +2 -0
- package/dist/esm/commands/__tests__/create-workspace.test.d.ts.map +1 -0
- package/dist/esm/commands/__tests__/create.test.d.ts +2 -0
- package/dist/esm/commands/__tests__/create.test.d.ts.map +1 -0
- package/dist/esm/commands/create-workspace.d.ts +5 -0
- package/dist/esm/commands/create-workspace.d.ts.map +1 -0
- package/dist/esm/commands/create-workspace.mjs +74 -0
- package/dist/esm/commands/create.d.ts +7 -0
- package/dist/esm/commands/create.d.ts.map +1 -0
- package/dist/esm/commands/create.mjs +91 -0
- package/dist/esm/constants.d.ts +1 -14
- package/dist/esm/constants.d.ts.map +1 -1
- package/dist/esm/constants.mjs +11 -32
- package/dist/esm/index.mjs +28 -0
- package/dist/esm/shadow/__tests__/consumer-pkg.test.d.ts +2 -0
- package/dist/esm/shadow/__tests__/consumer-pkg.test.d.ts.map +1 -0
- package/dist/esm/shadow/__tests__/merge-package-json.test.d.ts +2 -0
- package/dist/esm/shadow/__tests__/merge-package-json.test.d.ts.map +1 -0
- package/dist/esm/shadow/consumer-pkg.d.ts +5 -15
- package/dist/esm/shadow/consumer-pkg.d.ts.map +1 -1
- package/dist/esm/shadow/consumer-pkg.mjs +11 -72
- package/dist/esm/shadow/merge-package-json.d.ts +10 -4
- package/dist/esm/shadow/merge-package-json.d.ts.map +1 -1
- package/dist/esm/shadow/merge-package-json.mjs +12 -11
- package/dist/esm/shadow/sync.mjs +2 -2
- package/dist/esm/templates/app/claude-settings.mjs +10 -0
- package/dist/esm/types.d.ts +0 -1
- package/dist/esm/types.d.ts.map +1 -1
- package/dist/esm/util/__tests__/templates.test.d.ts +2 -0
- package/dist/esm/util/__tests__/templates.test.d.ts.map +1 -0
- package/dist/esm/util/__tests__/workspace-templates-shipped.test.d.ts +2 -0
- package/dist/esm/util/__tests__/workspace-templates-shipped.test.d.ts.map +1 -0
- package/dist/esm/util/__tests__/workspace.test.d.ts +2 -0
- package/dist/esm/util/__tests__/workspace.test.d.ts.map +1 -0
- package/dist/esm/util/s3/upload.mjs +1 -1
- package/dist/esm/util/templates.d.ts +22 -0
- package/dist/esm/util/templates.d.ts.map +1 -0
- package/dist/esm/util/templates.mjs +36 -0
- package/dist/esm/util/workspace.d.ts +20 -0
- package/dist/esm/util/workspace.d.ts.map +1 -0
- package/dist/esm/util/workspace.mjs +46 -0
- package/dist/index.js +28 -0
- package/dist/shadow/__tests__/consumer-pkg.test.d.ts +2 -0
- package/dist/shadow/__tests__/consumer-pkg.test.d.ts.map +1 -0
- package/dist/shadow/__tests__/merge-package-json.test.d.ts +2 -0
- package/dist/shadow/__tests__/merge-package-json.test.d.ts.map +1 -0
- package/dist/shadow/consumer-pkg.d.ts +5 -15
- package/dist/shadow/consumer-pkg.d.ts.map +1 -1
- package/dist/shadow/consumer-pkg.js +10 -72
- package/dist/shadow/merge-package-json.d.ts +10 -4
- package/dist/shadow/merge-package-json.d.ts.map +1 -1
- package/dist/shadow/merge-package-json.js +11 -10
- package/dist/shadow/sync.js +2 -2
- package/dist/templates/app/claude-settings.js +13 -0
- package/dist/types.d.ts +0 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/util/__tests__/templates.test.d.ts +2 -0
- package/dist/util/__tests__/templates.test.d.ts.map +1 -0
- package/dist/util/__tests__/workspace-templates-shipped.test.d.ts +2 -0
- package/dist/util/__tests__/workspace-templates-shipped.test.d.ts.map +1 -0
- package/dist/util/__tests__/workspace.test.d.ts +2 -0
- package/dist/util/__tests__/workspace.test.d.ts.map +1 -0
- package/dist/util/s3/upload.js +1 -1
- package/dist/util/templates.d.ts +22 -0
- package/dist/util/templates.d.ts.map +1 -0
- package/dist/util/templates.js +45 -0
- package/dist/util/workspace.d.ts +20 -0
- package/dist/util/workspace.d.ts.map +1 -0
- package/dist/util/workspace.js +57 -0
- package/package.json +5 -3
- package/src/commands/__tests__/create-workspace.test.ts +78 -0
- package/src/commands/__tests__/create.test.ts +111 -0
- package/src/commands/create-workspace.ts +85 -0
- package/src/commands/create.ts +113 -0
- package/src/constants.ts +11 -34
- package/src/index.ts +28 -0
- package/src/shadow/__tests__/consumer-pkg.test.ts +56 -0
- package/src/shadow/__tests__/merge-package-json.test.ts +44 -0
- package/src/shadow/consumer-pkg.ts +10 -88
- package/src/shadow/merge-package-json.ts +12 -11
- package/src/shadow/sync.ts +2 -2
- package/src/templates/app/CLAUDE.md +72 -0
- package/src/templates/app/claude-settings.json +7 -0
- package/src/templates/app/default-index.tsx.tmpl +21 -0
- package/src/templates/app/gitignore +4 -0
- package/src/templates/app/html-index.tsx.tmpl +21 -0
- package/src/templates/app/package.json.tmpl +13 -0
- package/src/templates/workspace/README.md +39 -0
- package/src/templates/workspace/gitignore +23 -0
- package/src/templates/workspace/npmrc +3 -0
- package/src/templates/workspace/nvmrc +1 -0
- package/src/templates/workspace/package.json.tmpl +23 -0
- package/src/templates/workspace/yarn-releases/yarn-4.5.1.cjs +934 -0
- package/src/templates/workspace/yarnrc.yml +7 -0
- package/src/types.ts +4 -1
- package/src/util/__tests__/templates.test.ts +27 -0
- package/src/util/__tests__/workspace-templates-shipped.test.ts +35 -0
- package/src/util/__tests__/workspace.test.ts +65 -0
- package/src/util/s3/upload.ts +1 -1
- package/src/util/templates.ts +37 -0
- 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.
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
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
|
-
|
|
29
|
-
devDependencies: constants.WORKSPACE_DEV_DEPS,
|
|
30
|
+
devDependencies: { ...constants.SHADOW_DEV_DEPS },
|
|
30
31
|
};
|
|
31
32
|
}
|
|
32
33
|
/**
|
package/dist/shadow/sync.js
CHANGED
|
@@ -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
|
-
//
|
|
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
package/dist/types.d.ts.map
CHANGED
|
@@ -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;
|
|
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 @@
|
|
|
1
|
+
{"version":3,"file":"templates.test.d.ts","sourceRoot":"","sources":["../../../src/util/__tests__/templates.test.ts"],"names":[],"mappings":""}
|
|
@@ -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 @@
|
|
|
1
|
+
{"version":3,"file":"workspace.test.d.ts","sourceRoot":"","sources":["../../../src/util/__tests__/workspace.test.ts"],"names":[],"mappings":""}
|
package/dist/util/s3/upload.js
CHANGED
|
@@ -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.
|
|
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": "
|
|
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
|
+
}
|