@astryxdesign/cli 0.1.2-canary.56a62aa → 0.1.2-canary.5a012db
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@astryxdesign/cli",
|
|
3
|
-
"version": "0.1.2-canary.
|
|
3
|
+
"version": "0.1.2-canary.5a012db",
|
|
4
4
|
"displayName": "CLI",
|
|
5
5
|
"description": "Scaffold projects, browse templates, generate themes, and get agent-ready docs from the command line.",
|
|
6
6
|
"author": "Meta Open Source",
|
|
@@ -75,9 +75,9 @@
|
|
|
75
75
|
"zod": "^4.4.3"
|
|
76
76
|
},
|
|
77
77
|
"peerDependencies": {
|
|
78
|
-
"@astryxdesign/core": "0.1.2-canary.
|
|
79
|
-
"@astryxdesign/lab": "0.1.2-canary.
|
|
80
|
-
"@astryxdesign/theme-neutral": "0.1.2-canary.
|
|
78
|
+
"@astryxdesign/core": "0.1.2-canary.5a012db",
|
|
79
|
+
"@astryxdesign/lab": "0.1.2-canary.5a012db",
|
|
80
|
+
"@astryxdesign/theme-neutral": "0.1.2-canary.5a012db",
|
|
81
81
|
"gpt-tokenizer": "^2.0.0"
|
|
82
82
|
},
|
|
83
83
|
"peerDependenciesMeta": {
|
|
@@ -92,9 +92,9 @@
|
|
|
92
92
|
}
|
|
93
93
|
},
|
|
94
94
|
"devDependencies": {
|
|
95
|
-
"@astryxdesign/core": "0.1.2-canary.
|
|
96
|
-
"@astryxdesign/lab": "0.1.2-canary.
|
|
97
|
-
"@astryxdesign/theme-neutral": "0.1.2-canary.
|
|
95
|
+
"@astryxdesign/core": "0.1.2-canary.5a012db",
|
|
96
|
+
"@astryxdesign/lab": "0.1.2-canary.5a012db",
|
|
97
|
+
"@astryxdesign/theme-neutral": "0.1.2-canary.5a012db",
|
|
98
98
|
"gpt-tokenizer": "^2.0.0"
|
|
99
99
|
},
|
|
100
100
|
"scripts": {
|
|
@@ -18,14 +18,10 @@ import * as fs from 'node:fs';
|
|
|
18
18
|
import * as path from 'node:path';
|
|
19
19
|
import * as os from 'node:os';
|
|
20
20
|
import {fileURLToPath} from 'node:url';
|
|
21
|
+
import {ensureCoreBuilt} from './ensure-core-built.mjs';
|
|
21
22
|
|
|
22
23
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
23
24
|
const CLI_BIN = path.resolve(__dirname, '../../bin/astryx.mjs');
|
|
24
|
-
const REPO_ROOT = path.resolve(__dirname, '../../../..');
|
|
25
|
-
const CORE_THEME_ENTRY = path.join(
|
|
26
|
-
REPO_ROOT,
|
|
27
|
-
'packages/core/dist/theme/index.js',
|
|
28
|
-
);
|
|
29
25
|
|
|
30
26
|
function runCli(args, cwd) {
|
|
31
27
|
try {
|
|
@@ -59,13 +55,7 @@ function writeTheme(dir, name) {
|
|
|
59
55
|
// in-CLI fallback generator). Build core once if it isn't already present so
|
|
60
56
|
// the suite works in any CI job, regardless of job ordering.
|
|
61
57
|
beforeAll(() => {
|
|
62
|
-
|
|
63
|
-
execFileSync('pnpm', ['-F', '@astryxdesign/core', 'build'], {
|
|
64
|
-
cwd: REPO_ROOT,
|
|
65
|
-
stdio: 'pipe',
|
|
66
|
-
timeout: 180_000,
|
|
67
|
-
});
|
|
68
|
-
}
|
|
58
|
+
ensureCoreBuilt();
|
|
69
59
|
}, 200_000);
|
|
70
60
|
|
|
71
61
|
let tmpDir;
|
|
@@ -18,9 +18,9 @@
|
|
|
18
18
|
* - paragraphs use the body font, not the heading font.
|
|
19
19
|
*
|
|
20
20
|
* Building `astryx theme build` requires a compiled @astryxdesign/core (there is no in-CLI
|
|
21
|
-
* fallback generator), so this suite builds core once in beforeAll
|
|
22
|
-
*
|
|
23
|
-
* ordering.
|
|
21
|
+
* fallback generator), so this suite builds core once in beforeAll via the
|
|
22
|
+
* shared ensureCoreBuilt() helper — which serializes concurrent Vitest workers
|
|
23
|
+
* behind a lock — to stay self-sufficient regardless of CI job ordering.
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
26
|
import {describe, it, expect, beforeAll, beforeEach, afterEach} from 'vitest';
|
|
@@ -29,14 +29,10 @@ import * as fs from 'node:fs';
|
|
|
29
29
|
import * as path from 'node:path';
|
|
30
30
|
import * as os from 'node:os';
|
|
31
31
|
import {fileURLToPath} from 'node:url';
|
|
32
|
+
import {ensureCoreBuilt} from './ensure-core-built.mjs';
|
|
32
33
|
|
|
33
34
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
34
35
|
const CLI_BIN = path.resolve(__dirname, '../../bin/astryx.mjs');
|
|
35
|
-
const REPO_ROOT = path.resolve(__dirname, '../../../..');
|
|
36
|
-
const CORE_THEME_ENTRY = path.join(
|
|
37
|
-
REPO_ROOT,
|
|
38
|
-
'packages/core/dist/theme/index.js',
|
|
39
|
-
);
|
|
40
36
|
|
|
41
37
|
function runCli(args, cwd) {
|
|
42
38
|
try {
|
|
@@ -71,13 +67,7 @@ function writeTheme(dir, name) {
|
|
|
71
67
|
// `astryx theme build` imports the compiled @astryxdesign/core/theme entry. Build core
|
|
72
68
|
// once if it isn't already present so the suite works in any CI job.
|
|
73
69
|
beforeAll(() => {
|
|
74
|
-
|
|
75
|
-
execFileSync('pnpm', ['-F', '@astryxdesign/core', 'build'], {
|
|
76
|
-
cwd: REPO_ROOT,
|
|
77
|
-
stdio: 'pipe',
|
|
78
|
-
timeout: 180_000,
|
|
79
|
-
});
|
|
80
|
-
}
|
|
70
|
+
ensureCoreBuilt();
|
|
81
71
|
}, 200_000);
|
|
82
72
|
|
|
83
73
|
let tmpDir;
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
* the watcher keeps running.
|
|
10
10
|
*
|
|
11
11
|
* Building `astryx theme build` requires a compiled @astryxdesign/core, so this
|
|
12
|
-
* suite builds core once in beforeAll (
|
|
12
|
+
* suite builds core once in beforeAll via the shared ensureCoreBuilt() helper —
|
|
13
|
+
* which serializes concurrent Vitest workers behind a lock — to stay
|
|
14
|
+
* self-sufficient regardless of CI job ordering.
|
|
13
15
|
*/
|
|
14
16
|
|
|
15
17
|
import {describe, it, expect, beforeAll, beforeEach, afterEach} from 'vitest';
|
|
@@ -18,14 +20,10 @@ import * as fs from 'node:fs';
|
|
|
18
20
|
import * as path from 'node:path';
|
|
19
21
|
import * as os from 'node:os';
|
|
20
22
|
import {fileURLToPath} from 'node:url';
|
|
23
|
+
import {ensureCoreBuilt} from './ensure-core-built.mjs';
|
|
21
24
|
|
|
22
25
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
23
26
|
const CLI_BIN = path.resolve(__dirname, '../../bin/astryx.mjs');
|
|
24
|
-
const REPO_ROOT = path.resolve(__dirname, '../../../..');
|
|
25
|
-
const CORE_THEME_ENTRY = path.join(
|
|
26
|
-
REPO_ROOT,
|
|
27
|
-
'packages/core/dist/theme/index.js',
|
|
28
|
-
);
|
|
29
27
|
|
|
30
28
|
function runCli(args, cwd) {
|
|
31
29
|
try {
|
|
@@ -56,13 +54,7 @@ async function waitFor(predicate, {timeout = 8000, interval = 50} = {}) {
|
|
|
56
54
|
}
|
|
57
55
|
|
|
58
56
|
beforeAll(() => {
|
|
59
|
-
|
|
60
|
-
execFileSync('pnpm', ['-F', '@astryxdesign/core', 'build'], {
|
|
61
|
-
cwd: REPO_ROOT,
|
|
62
|
-
stdio: 'pipe',
|
|
63
|
-
timeout: 180_000,
|
|
64
|
-
});
|
|
65
|
-
}
|
|
57
|
+
ensureCoreBuilt();
|
|
66
58
|
}, 200_000);
|
|
67
59
|
|
|
68
60
|
let tmpDir;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @file Shared test helper: build @astryxdesign/core once, race-free.
|
|
5
|
+
*
|
|
6
|
+
* `astryx theme build` imports the compiled @astryxdesign/core/theme entry
|
|
7
|
+
* (there is no in-CLI fallback generator), so any test exercising it needs a
|
|
8
|
+
* built core. The CI `test` job runs `pnpm test` without a prior core build,
|
|
9
|
+
* and Vitest runs test files in parallel worker forks. When two build-theme
|
|
10
|
+
* suites each ran `if (!exists) pnpm -F @astryxdesign/core build` in their own
|
|
11
|
+
* beforeAll, both workers saw dist missing and launched concurrent builds that
|
|
12
|
+
* collided on the shared packages/core/dist (core's build starts with
|
|
13
|
+
* `rimraf dist`): one worker wiped dist while the other was mid-write, failing
|
|
14
|
+
* nondeterministically ("Could not resolve dist/index.js" / "ENOTEMPTY rmdir
|
|
15
|
+
* dist/hooks").
|
|
16
|
+
*
|
|
17
|
+
* This serializes the build behind a filesystem lock so exactly one worker
|
|
18
|
+
* builds and the rest wait for it to finish before reading dist.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import {execFileSync} from 'node:child_process';
|
|
22
|
+
import * as fs from 'node:fs';
|
|
23
|
+
import * as os from 'node:os';
|
|
24
|
+
import * as path from 'node:path';
|
|
25
|
+
import {fileURLToPath} from 'node:url';
|
|
26
|
+
|
|
27
|
+
const REPO_ROOT = path.resolve(
|
|
28
|
+
path.dirname(fileURLToPath(import.meta.url)),
|
|
29
|
+
'../../../..',
|
|
30
|
+
);
|
|
31
|
+
const CORE_THEME_ENTRY = path.join(
|
|
32
|
+
REPO_ROOT,
|
|
33
|
+
'packages/core/dist/theme/index.js',
|
|
34
|
+
);
|
|
35
|
+
// A lock directory (mkdir is atomic across processes) guards the build.
|
|
36
|
+
const LOCK_DIR = path.join(os.tmpdir(), 'astryx-core-build.lock');
|
|
37
|
+
|
|
38
|
+
const BUILD_TIMEOUT_MS = 180_000;
|
|
39
|
+
// A lock older than this is assumed abandoned by a crashed/killed worker.
|
|
40
|
+
const STALE_LOCK_MS = BUILD_TIMEOUT_MS + 20_000;
|
|
41
|
+
const POLL_MS = 250;
|
|
42
|
+
|
|
43
|
+
function sleepSync(ms) {
|
|
44
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function buildCore() {
|
|
48
|
+
execFileSync('pnpm', ['-F', '@astryxdesign/core', 'build'], {
|
|
49
|
+
cwd: REPO_ROOT,
|
|
50
|
+
stdio: 'pipe',
|
|
51
|
+
timeout: BUILD_TIMEOUT_MS,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function lockIsStale() {
|
|
56
|
+
try {
|
|
57
|
+
return Date.now() - fs.statSync(LOCK_DIR).mtimeMs > STALE_LOCK_MS;
|
|
58
|
+
} catch {
|
|
59
|
+
// Vanished between the exists check and stat — treat as released.
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Try to acquire the lock. Returns true if this worker now holds it. */
|
|
65
|
+
function tryAcquire() {
|
|
66
|
+
try {
|
|
67
|
+
fs.mkdirSync(LOCK_DIR);
|
|
68
|
+
return true;
|
|
69
|
+
} catch (err) {
|
|
70
|
+
if (err.code !== 'EEXIST') {
|
|
71
|
+
throw err;
|
|
72
|
+
}
|
|
73
|
+
if (lockIsStale()) {
|
|
74
|
+
fs.rmSync(LOCK_DIR, {recursive: true, force: true});
|
|
75
|
+
try {
|
|
76
|
+
fs.mkdirSync(LOCK_DIR);
|
|
77
|
+
return true;
|
|
78
|
+
} catch (retryErr) {
|
|
79
|
+
if (retryErr.code !== 'EEXIST') {
|
|
80
|
+
throw retryErr;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Ensure packages/core/dist is built exactly once, even when called
|
|
90
|
+
* concurrently from parallel Vitest workers. Safe to call from every
|
|
91
|
+
* build-theme suite's beforeAll.
|
|
92
|
+
*/
|
|
93
|
+
export function ensureCoreBuilt() {
|
|
94
|
+
if (fs.existsSync(CORE_THEME_ENTRY)) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const deadline = Date.now() + STALE_LOCK_MS;
|
|
99
|
+
while (Date.now() < deadline) {
|
|
100
|
+
if (fs.existsSync(CORE_THEME_ENTRY)) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (tryAcquire()) {
|
|
104
|
+
try {
|
|
105
|
+
if (!fs.existsSync(CORE_THEME_ENTRY)) {
|
|
106
|
+
buildCore();
|
|
107
|
+
}
|
|
108
|
+
} finally {
|
|
109
|
+
fs.rmSync(LOCK_DIR, {recursive: true, force: true});
|
|
110
|
+
}
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
// Another worker is building; wait for it to finish and release the lock.
|
|
114
|
+
sleepSync(POLL_MS);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Waited past the stale threshold without the artifact appearing — build it
|
|
118
|
+
// ourselves rather than let the suite fail on a missing entry.
|
|
119
|
+
buildCore();
|
|
120
|
+
}
|