@chem-x/starter-kit 26.9.20-1318 → 26.9.20-1340
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/cli/create.js +38 -0
- package/cli/create.spec.js +56 -2
- package/cli/index.js +2 -15
- package/package.json +1 -1
package/cli/create.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
import fs from 'node:fs';
|
|
3
4
|
import { runScaffold } from './scaffold.js';
|
|
4
5
|
import { runAudit } from './index.js';
|
|
5
6
|
import { sanitizeOutputStreams } from './terminal.js';
|
|
@@ -9,6 +10,43 @@ sanitizeOutputStreams();
|
|
|
9
10
|
installGlobalErrorCatcher();
|
|
10
11
|
|
|
11
12
|
const rawArgs = process.argv.slice(2);
|
|
13
|
+
|
|
14
|
+
if (rawArgs.includes('--help') || rawArgs.includes('-h')) {
|
|
15
|
+
process.stdout.write(`
|
|
16
|
+
Chemical X Protocol: Project Scaffolder
|
|
17
|
+
|
|
18
|
+
USAGE
|
|
19
|
+
npm create chemx [directory] [options]
|
|
20
|
+
npx create-chemx [directory] [options]
|
|
21
|
+
|
|
22
|
+
OPTIONS
|
|
23
|
+
--yes, -y Skip interactive prompts and scaffold Community Edition immediately
|
|
24
|
+
--headless Run in headless mode for CI/CD and AI agent automation
|
|
25
|
+
-h, --help Show this help message
|
|
26
|
+
-v, --version Show version number
|
|
27
|
+
|
|
28
|
+
EXAMPLES
|
|
29
|
+
npm create chemx my-molecular-app
|
|
30
|
+
npx create-chemx my-molecular-app --yes
|
|
31
|
+
pnpm create chemx my-app
|
|
32
|
+
|
|
33
|
+
DOCUMENTATION
|
|
34
|
+
https://chemicalx.xophz.com
|
|
35
|
+
\n`);
|
|
36
|
+
process.exit(0);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (rawArgs.includes('--version') || rawArgs.includes('-v')) {
|
|
40
|
+
try {
|
|
41
|
+
const pkgPath = new URL('../package.json', import.meta.url);
|
|
42
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
43
|
+
process.stdout.write(`create-chemx v${pkg.version || 'unknown'}\n`);
|
|
44
|
+
} catch {
|
|
45
|
+
process.stdout.write('create-chemx\n');
|
|
46
|
+
}
|
|
47
|
+
process.exit(0);
|
|
48
|
+
}
|
|
49
|
+
|
|
12
50
|
const nonFlagArgs = rawArgs.filter((arg) => !arg.startsWith('-'));
|
|
13
51
|
const dirArg = (nonFlagArgs[0] === 'create' || nonFlagArgs[0] === 'init' || nonFlagArgs[0] === 'scaffold')
|
|
14
52
|
? nonFlagArgs[1]
|
package/cli/create.spec.js
CHANGED
|
@@ -56,6 +56,36 @@ describe('Scaffolding Entrypoint Invocations', () => {
|
|
|
56
56
|
assert.ok(fs.existsSync(path.join(defaultTarget, 'package.json')), 'package.json should exist in default directory');
|
|
57
57
|
});
|
|
58
58
|
|
|
59
|
+
test('node cli/create.js --help prints usage and does not scaffold', () => {
|
|
60
|
+
const tmpBase = fs.mkdtempSync(path.join(os.tmpdir(), 'chemx-test-'));
|
|
61
|
+
cleanupDirs.push(tmpBase);
|
|
62
|
+
|
|
63
|
+
const result = spawnSync('node', [CREATE_BIN, '--help'], {
|
|
64
|
+
encoding: 'utf-8',
|
|
65
|
+
cwd: tmpBase,
|
|
66
|
+
timeout: 5000
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
assert.equal(result.status, 0, 'Should exit with code 0 on --help');
|
|
70
|
+
assert.match(result.stdout, /Chemical X Protocol: Project Scaffolder/, 'Should show scaffolder help');
|
|
71
|
+
assert.ok(!fs.existsSync(path.join(tmpBase, 'my-molecular-app')), 'Must not scaffold any directory on --help');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test('node cli/create.js --version prints version and does not scaffold', () => {
|
|
75
|
+
const tmpBase = fs.mkdtempSync(path.join(os.tmpdir(), 'chemx-test-'));
|
|
76
|
+
cleanupDirs.push(tmpBase);
|
|
77
|
+
|
|
78
|
+
const result = spawnSync('node', [CREATE_BIN, '--version'], {
|
|
79
|
+
encoding: 'utf-8',
|
|
80
|
+
cwd: tmpBase,
|
|
81
|
+
timeout: 5000
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
assert.equal(result.status, 0, 'Should exit with code 0 on --version');
|
|
85
|
+
assert.match(result.stdout, /create-chemx v/, 'Should print create-chemx version');
|
|
86
|
+
assert.ok(!fs.existsSync(path.join(tmpBase, 'my-molecular-app')), 'Must not scaffold any directory on --version');
|
|
87
|
+
});
|
|
88
|
+
|
|
59
89
|
test('node cli/create.js create <dir> --yes tolerates redundant create argument', () => {
|
|
60
90
|
const tmpBase = fs.mkdtempSync(path.join(os.tmpdir(), 'chemx-test-'));
|
|
61
91
|
cleanupDirs.push(tmpBase);
|
|
@@ -133,14 +163,38 @@ describe('Scaffolding Entrypoint Invocations', () => {
|
|
|
133
163
|
assert.ok(fs.existsSync(path.join(targetDir, 'package.json')), 'package.json should exist');
|
|
134
164
|
});
|
|
135
165
|
|
|
136
|
-
test('node cli/index.js config
|
|
166
|
+
test('node cli/index.js config fails fast as unknown command without creating directory', () => {
|
|
167
|
+
const tmpBase = fs.mkdtempSync(path.join(os.tmpdir(), 'chemx-test-'));
|
|
168
|
+
cleanupDirs.push(tmpBase);
|
|
169
|
+
|
|
137
170
|
const result = spawnSync('node', [INDEX_BIN, 'config'], {
|
|
138
171
|
encoding: 'utf-8',
|
|
172
|
+
cwd: tmpBase,
|
|
139
173
|
timeout: 5000
|
|
140
174
|
});
|
|
141
175
|
|
|
142
176
|
assert.equal(result.status, 1, 'Unknown command should exit with code 1');
|
|
143
177
|
assert.match(result.stderr, /Unknown command "config"/, 'Should report unknown command');
|
|
178
|
+
assert.ok(!fs.existsSync(path.join(tmpBase, 'config')), 'Must NOT create a config directory');
|
|
144
179
|
});
|
|
145
|
-
});
|
|
146
180
|
|
|
181
|
+
test('node cli/index.js under create-chemx path STILL fails fast on unknown command (regression test)', () => {
|
|
182
|
+
const tmpBase = fs.mkdtempSync(path.join(os.tmpdir(), 'chemx-test-'));
|
|
183
|
+
cleanupDirs.push(tmpBase);
|
|
184
|
+
// Simulate being installed inside a path with "create-chemx" in its name
|
|
185
|
+
const pkgDir = path.join(tmpBase, 'node_modules', '@chemx', 'create-chemx', 'cli');
|
|
186
|
+
fs.mkdirSync(pkgDir, { recursive: true });
|
|
187
|
+
const fakeIndex = path.join(pkgDir, 'index.js');
|
|
188
|
+
fs.symlinkSync(INDEX_BIN, fakeIndex);
|
|
189
|
+
|
|
190
|
+
const result = spawnSync('node', [fakeIndex, 'config'], {
|
|
191
|
+
encoding: 'utf-8',
|
|
192
|
+
cwd: tmpBase,
|
|
193
|
+
timeout: 5000
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
assert.equal(result.status, 1, 'Unknown command should exit with code 1');
|
|
197
|
+
assert.match(result.stderr, /Unknown command "config"/, 'Should report unknown command');
|
|
198
|
+
assert.ok(!fs.existsSync(path.join(tmpBase, 'config')), 'Must NOT create a config directory when installed under create-chemx path');
|
|
199
|
+
});
|
|
200
|
+
});
|
package/cli/index.js
CHANGED
|
@@ -83,11 +83,6 @@ const loadProjectConfig = () => {
|
|
|
83
83
|
return {};
|
|
84
84
|
}
|
|
85
85
|
};
|
|
86
|
-
const isCreateInvoked =
|
|
87
|
-
invokedBin.includes('create') ||
|
|
88
|
-
Boolean(process.argv[1] && process.argv[1].includes('create-chemx')) ||
|
|
89
|
-
Boolean(process.env.npm_lifecycle_event && process.env.npm_lifecycle_event.includes('create')) ||
|
|
90
|
-
Boolean(rawArgs[0] && (rawArgs[0] === 'create' || rawArgs[0] === 'init' || rawArgs[0] === 'scaffold'));
|
|
91
86
|
|
|
92
87
|
export const runAudit = async (customDir = null, isCli = false) => {
|
|
93
88
|
const projectConfig = loadProjectConfig();
|
|
@@ -296,15 +291,6 @@ export {
|
|
|
296
291
|
const main = async () => {
|
|
297
292
|
const firstArg = rawArgs[0];
|
|
298
293
|
|
|
299
|
-
if (isCreateInvoked) {
|
|
300
|
-
const nonFlagArgs = rawArgs.filter((arg) => !arg.startsWith('-'));
|
|
301
|
-
const dirArg = (nonFlagArgs[0] === 'create' || nonFlagArgs[0] === 'init' || nonFlagArgs[0] === 'scaffold')
|
|
302
|
-
? nonFlagArgs[1]
|
|
303
|
-
: nonFlagArgs[0];
|
|
304
|
-
await runScaffold(dirArg, rawArgs, runAudit);
|
|
305
|
-
return;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
294
|
switch (firstArg) {
|
|
309
295
|
case 'team':
|
|
310
296
|
case 'swarm':
|
|
@@ -346,7 +332,8 @@ const main = async () => {
|
|
|
346
332
|
case 'init':
|
|
347
333
|
await runInit(rawArgs[1] || 'src/chemical-x', rawArgs, runAudit);
|
|
348
334
|
break;
|
|
349
|
-
case 'create':
|
|
335
|
+
case 'create':
|
|
336
|
+
case 'scaffold': {
|
|
350
337
|
const nonFlagArgs = rawArgs.slice(1).filter((arg) => !arg.startsWith('-'));
|
|
351
338
|
await runScaffold(nonFlagArgs[0], rawArgs, runAudit);
|
|
352
339
|
break;
|