@nexical/cli 0.11.0 → 0.11.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/.github/workflows/deploy.yml +1 -1
- package/.husky/pre-commit +1 -0
- package/.prettierignore +8 -0
- package/.prettierrc +7 -0
- package/GEMINI.md +36 -30
- package/README.md +85 -56
- package/dist/chunk-AC4B3HPJ.js +93 -0
- package/dist/chunk-AC4B3HPJ.js.map +1 -0
- package/dist/{chunk-JYASTIIW.js → chunk-PJIOCW2A.js} +1 -1
- package/dist/chunk-PJIOCW2A.js.map +1 -0
- package/dist/{chunk-WKERTCM6.js → chunk-Q7YLW5HJ.js} +5 -2
- package/dist/chunk-Q7YLW5HJ.js.map +1 -0
- package/dist/index.js +41 -12
- package/dist/index.js.map +1 -1
- package/dist/src/commands/init.d.ts +4 -1
- package/dist/src/commands/init.js +8 -4
- package/dist/src/commands/init.js.map +1 -1
- package/dist/src/commands/module/add.d.ts +3 -1
- package/dist/src/commands/module/add.js +24 -13
- package/dist/src/commands/module/add.js.map +1 -1
- package/dist/src/commands/module/list.js +9 -5
- package/dist/src/commands/module/list.js.map +1 -1
- package/dist/src/commands/module/remove.d.ts +3 -1
- package/dist/src/commands/module/remove.js +13 -7
- package/dist/src/commands/module/remove.js.map +1 -1
- package/dist/src/commands/module/update.d.ts +3 -1
- package/dist/src/commands/module/update.js +7 -5
- package/dist/src/commands/module/update.js.map +1 -1
- package/dist/src/commands/run.d.ts +4 -1
- package/dist/src/commands/run.js +10 -2
- package/dist/src/commands/run.js.map +1 -1
- package/dist/src/commands/setup.js +17 -4
- package/dist/src/commands/setup.js.map +1 -1
- package/dist/src/utils/discovery.js +1 -1
- package/dist/src/utils/git.js +1 -1
- package/dist/src/utils/url-resolver.js +1 -1
- package/eslint.config.mjs +67 -0
- package/index.ts +34 -20
- package/package.json +56 -32
- package/src/commands/init.ts +79 -76
- package/src/commands/module/add.ts +158 -148
- package/src/commands/module/list.ts +61 -50
- package/src/commands/module/remove.ts +59 -54
- package/src/commands/module/update.ts +44 -42
- package/src/commands/run.ts +89 -81
- package/src/commands/setup.ts +78 -60
- package/src/utils/discovery.ts +98 -113
- package/src/utils/git.ts +35 -28
- package/src/utils/url-resolver.ts +50 -45
- package/test/e2e/lifecycle.e2e.test.ts +139 -131
- package/test/integration/commands/init.integration.test.ts +64 -64
- package/test/integration/commands/module.integration.test.ts +122 -122
- package/test/integration/commands/run.integration.test.ts +70 -63
- package/test/integration/utils/command-loading.integration.test.ts +40 -53
- package/test/unit/commands/init.test.ts +163 -128
- package/test/unit/commands/module/add.test.ts +312 -245
- package/test/unit/commands/module/list.test.ts +108 -91
- package/test/unit/commands/module/remove.test.ts +74 -67
- package/test/unit/commands/module/update.test.ts +74 -70
- package/test/unit/commands/run.test.ts +253 -201
- package/test/unit/commands/setup.test.ts +146 -128
- package/test/unit/utils/command-discovery.test.ts +138 -125
- package/test/unit/utils/git.test.ts +135 -117
- package/test/unit/utils/integration-helpers.test.ts +59 -49
- package/test/unit/utils/url-resolver.test.ts +46 -34
- package/test/utils/integration-helpers.ts +36 -29
- package/tsconfig.json +15 -25
- package/tsup.config.ts +14 -14
- package/vitest.config.ts +10 -10
- package/vitest.e2e.config.ts +6 -6
- package/vitest.integration.config.ts +17 -17
- package/dist/chunk-JYASTIIW.js.map +0 -1
- package/dist/chunk-OKXOCNXP.js +0 -105
- package/dist/chunk-OKXOCNXP.js.map +0 -1
- package/dist/chunk-WKERTCM6.js.map +0 -1
|
@@ -11,134 +11,134 @@ import fs from 'fs-extra';
|
|
|
11
11
|
|
|
12
12
|
// Mock picocolors to return strings as-is for easy matching
|
|
13
13
|
vi.mock('picocolors', () => ({
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
14
|
+
default: {
|
|
15
|
+
bold: (s: string) => s,
|
|
16
|
+
cyan: (s: string) => s,
|
|
17
|
+
yellow: (s: string) => s,
|
|
18
|
+
dim: (s: string) => s,
|
|
19
|
+
red: (s: string) => s,
|
|
20
|
+
green: (s: string) => s,
|
|
21
|
+
blue: (s: string) => s,
|
|
22
|
+
magenta: (s: string) => s,
|
|
23
|
+
},
|
|
24
24
|
}));
|
|
25
25
|
|
|
26
26
|
describe('Module Commands Integration', () => {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
'nexical.yaml': 'site: test\nmodules: []'
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
// Allow file protocol for submodules in this repo
|
|
41
|
-
// await execa('git', ['config', 'protocol.file.allow', 'always'], { cwd: projectDir }); // Config approach failed
|
|
42
|
-
process.env.GIT_ALLOW_PROTOCOL = 'file';
|
|
43
|
-
|
|
44
|
-
// 2. Create a "Module" that is a SEPARATE git repo
|
|
45
|
-
const modTemp = await createTempDir('module-source-');
|
|
46
|
-
moduleRepo = await createMockRepo(modTemp, {
|
|
47
|
-
'package.json': '{"name": "my-module", "version": "1.0.0", "description": "Awesome module"}',
|
|
48
|
-
'module.yaml': 'name: my-module\nversion: 1.0.0',
|
|
49
|
-
'index.ts': 'export const hello = "world";'
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
consoleTableSpy = vi.spyOn(console, 'table').mockImplementation(() => { });
|
|
53
|
-
consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
54
|
-
|
|
55
|
-
// Switch CWD to project for commands to find root
|
|
56
|
-
// Note: process.chdir behavior might persist, so we rely on mocking or careful cleanup
|
|
57
|
-
// But integration tests run sequentially in same thread usually with vitest unless configured otherwise.
|
|
58
|
-
// We will pass specific CWD to commands if possible, OR chdir and restore.
|
|
27
|
+
let projectDir: string;
|
|
28
|
+
let moduleRepo: string;
|
|
29
|
+
let consoleTableSpy: unknown;
|
|
30
|
+
|
|
31
|
+
beforeEach(async () => {
|
|
32
|
+
// 1. Create a "Project" that is a git repo
|
|
33
|
+
const temp = await createTempDir('module-project-');
|
|
34
|
+
projectDir = await createMockRepo(temp, {
|
|
35
|
+
'package.json': '{"name": "test-project", "version": "1.0.0"}',
|
|
36
|
+
'nexical.yaml': 'site: test\nmodules: []',
|
|
59
37
|
});
|
|
60
38
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
39
|
+
// Allow file protocol for submodules in this repo
|
|
40
|
+
// await execa('git', ['config', 'protocol.file.allow', 'always'], { cwd: projectDir }); // Config approach failed
|
|
41
|
+
process.env.GIT_ALLOW_PROTOCOL = 'file';
|
|
64
42
|
|
|
65
|
-
|
|
66
|
-
|
|
43
|
+
// 2. Create a "Module" that is a SEPARATE git repo
|
|
44
|
+
const modTemp = await createTempDir('module-source-');
|
|
45
|
+
moduleRepo = await createMockRepo(modTemp, {
|
|
46
|
+
'package.json': '{"name": "my-module", "version": "1.0.0", "description": "Awesome module"}',
|
|
47
|
+
'module.yaml': 'name: my-module\nversion: 1.0.0',
|
|
48
|
+
'index.ts': 'export const hello = "world";',
|
|
67
49
|
});
|
|
68
50
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
51
|
+
consoleTableSpy = vi.spyOn(console, 'table').mockImplementation(() => {});
|
|
52
|
+
vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
53
|
+
|
|
54
|
+
// Switch CWD to project for commands to find root
|
|
55
|
+
// Note: process.chdir behavior might persist, so we rely on mocking or careful cleanup
|
|
56
|
+
// But integration tests run sequentially in same thread usually with vitest unless configured otherwise.
|
|
57
|
+
// We will pass specific CWD to commands if possible, OR chdir and restore.
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
afterEach(() => {
|
|
61
|
+
vi.restoreAllMocks();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
afterAll(async () => {
|
|
65
|
+
await cleanupTestRoot();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should add, list, update and remove a module', async () => {
|
|
69
|
+
const originalCwd = process.cwd();
|
|
70
|
+
const cli = new CLI({ commandName: 'nexical' });
|
|
71
|
+
try {
|
|
72
|
+
process.chdir(projectDir);
|
|
73
|
+
|
|
74
|
+
// 1. ADD MODULE
|
|
75
|
+
const addCmd = new ModuleAddCommand(cli);
|
|
76
|
+
|
|
77
|
+
await addCmd.init();
|
|
78
|
+
await addCmd.run({ url: moduleRepo });
|
|
79
|
+
|
|
80
|
+
const modulePath = path.join(projectDir, 'modules/my-module');
|
|
81
|
+
expect(fs.existsSync(modulePath)).toBe(true);
|
|
82
|
+
expect(fs.existsSync(path.join(modulePath, 'package.json'))).toBe(true);
|
|
83
|
+
|
|
84
|
+
// Verify nexical.yaml updated
|
|
85
|
+
const config = await fs.readFile(path.join(projectDir, 'nexical.yaml'), 'utf8');
|
|
86
|
+
expect(config).toContain('modules:');
|
|
87
|
+
expect(config).toContain('- my-module');
|
|
88
|
+
|
|
89
|
+
// Check it is a submodule
|
|
90
|
+
// .git file in module dir pointing to gitdir
|
|
91
|
+
expect(fs.existsSync(path.join(modulePath, '.git'))).toBe(true);
|
|
92
|
+
const gitModules = await fs.readFile(path.join(projectDir, '.gitmodules'), 'utf-8');
|
|
93
|
+
expect(gitModules).toContain('path = modules/my-module');
|
|
94
|
+
|
|
95
|
+
// 2. LIST MODULES with valid module
|
|
96
|
+
const listCmd = new ModuleListCommand(cli);
|
|
97
|
+
await listCmd.init();
|
|
98
|
+
await listCmd.run();
|
|
99
|
+
|
|
100
|
+
// Check console.table called with module info
|
|
101
|
+
expect(consoleTableSpy).toHaveBeenCalledWith(
|
|
102
|
+
expect.arrayContaining([
|
|
103
|
+
expect.objectContaining({
|
|
104
|
+
name: 'my-module',
|
|
105
|
+
version: '1.0.0',
|
|
106
|
+
description: 'Awesome module',
|
|
107
|
+
}),
|
|
108
|
+
]),
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
// 3. UPDATE MODULE
|
|
112
|
+
const updateCmd = new ModuleUpdateCommand(cli);
|
|
113
|
+
await updateCmd.init();
|
|
114
|
+
await updateCmd.run({ name: 'my-module' });
|
|
115
|
+
// Hard to check "update" without changing the remote first.
|
|
116
|
+
// But we verify it ran without throwing.
|
|
117
|
+
|
|
118
|
+
// 4. REMOVE MODULE
|
|
119
|
+
const removeCmd = new ModuleRemoveCommand(cli);
|
|
120
|
+
await removeCmd.init();
|
|
121
|
+
await removeCmd.run({ name: 'my-module' });
|
|
122
|
+
|
|
123
|
+
expect(fs.existsSync(modulePath)).toBe(false);
|
|
124
|
+
|
|
125
|
+
// Verify git cleanup
|
|
126
|
+
// .git/modules/modules/my-module should be gone
|
|
127
|
+
const gitInternalModuleDir = path.join(projectDir, '.git/modules/modules/my-module');
|
|
128
|
+
expect(fs.existsSync(gitInternalModuleDir)).toBe(false);
|
|
129
|
+
|
|
130
|
+
// .gitmodules entry gone? `git rm` usually handles this.
|
|
131
|
+
// Check if .gitmodules file exists (if empty it might remain or be deleted depending on git version, usually implicitly updated)
|
|
132
|
+
if (fs.existsSync(path.join(projectDir, '.gitmodules'))) {
|
|
133
|
+
const updatedGitModules = await fs.readFile(path.join(projectDir, '.gitmodules'), 'utf-8');
|
|
134
|
+
expect(updatedGitModules).not.toContain('modules/my-module');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Verify nexical.yaml updated
|
|
138
|
+
const configRemoved = await fs.readFile(path.join(projectDir, 'nexical.yaml'), 'utf8');
|
|
139
|
+
expect(configRemoved).not.toContain('- my-module');
|
|
140
|
+
} finally {
|
|
141
|
+
process.chdir(originalCwd);
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
144
|
});
|
|
@@ -8,83 +8,90 @@ import { spawn } from 'child_process';
|
|
|
8
8
|
import EventEmitter from 'events';
|
|
9
9
|
|
|
10
10
|
vi.mock('child_process', () => ({
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
spawn: vi.fn(),
|
|
12
|
+
exec: vi.fn(),
|
|
13
13
|
}));
|
|
14
14
|
|
|
15
15
|
describe('RunCommand Integration', () => {
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
let projectDir: string;
|
|
17
|
+
let spawnMock: ReturnType<typeof vi.mocked>;
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
beforeEach(async () => {
|
|
20
|
+
projectDir = await createTempDir('run-project-');
|
|
21
|
+
vi.mocked(spawn).mockClear();
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
23
|
+
// Setup minimal env (New Architecture: no site/ directory)
|
|
24
|
+
await fs.ensureDir(projectDir);
|
|
25
|
+
await fs.outputFile(
|
|
26
|
+
path.join(projectDir, 'package.json'),
|
|
27
|
+
JSON.stringify({
|
|
28
|
+
name: 'nexical-core',
|
|
29
|
+
scripts: {
|
|
30
|
+
'test-script': 'echo test',
|
|
31
|
+
},
|
|
32
|
+
}),
|
|
33
|
+
);
|
|
31
34
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
await fs.ensureDir(path.join(projectDir, 'modules', 'my-auth'));
|
|
36
|
+
await fs.outputFile(
|
|
37
|
+
path.join(projectDir, 'modules', 'my-auth', 'package.json'),
|
|
38
|
+
JSON.stringify({
|
|
39
|
+
scripts: {
|
|
40
|
+
seed: 'node seed.js',
|
|
41
|
+
},
|
|
42
|
+
}),
|
|
43
|
+
);
|
|
38
44
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
spawnMock = vi.mocked(spawn).mockImplementation(() => {
|
|
46
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
47
|
+
const child = new EventEmitter() as any;
|
|
48
|
+
child.stdout = new EventEmitter();
|
|
49
|
+
child.stderr = new EventEmitter();
|
|
50
|
+
setTimeout(() => child.emit('close', 0), 10);
|
|
51
|
+
child.kill = vi.fn();
|
|
52
|
+
return child;
|
|
47
53
|
});
|
|
54
|
+
});
|
|
48
55
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
56
|
+
afterEach(() => {
|
|
57
|
+
vi.clearAllMocks();
|
|
58
|
+
vi.restoreAllMocks();
|
|
59
|
+
});
|
|
53
60
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
61
|
+
afterAll(async () => {
|
|
62
|
+
if (projectDir) await fs.remove(projectDir);
|
|
63
|
+
});
|
|
57
64
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
65
|
+
it('should run standard npm scripts', async () => {
|
|
66
|
+
const cli = new CLI({ commandName: 'nexical' });
|
|
67
|
+
const command = new RunCommand(cli);
|
|
68
|
+
Object.assign(command, { projectRoot: projectDir });
|
|
62
69
|
|
|
63
|
-
|
|
70
|
+
await command.run({ script: 'test-script', args: ['--flag'] });
|
|
64
71
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
72
|
+
expect(spawnMock).toHaveBeenCalledWith(
|
|
73
|
+
'npm',
|
|
74
|
+
['run', 'test-script', '--', '--flag'],
|
|
75
|
+
expect.objectContaining({
|
|
76
|
+
cwd: projectDir,
|
|
77
|
+
}),
|
|
78
|
+
);
|
|
79
|
+
});
|
|
73
80
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
81
|
+
it('should run module specific scripts', async () => {
|
|
82
|
+
const cli = new CLI({ commandName: 'nexical' });
|
|
83
|
+
const command = new RunCommand(cli);
|
|
84
|
+
Object.assign(command, { projectRoot: projectDir });
|
|
78
85
|
|
|
79
|
-
|
|
86
|
+
await command.run({ script: 'my-auth:seed', args: ['--force'] });
|
|
80
87
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
// Module scripts run via npm run scriptName inside module dir
|
|
89
|
+
expect(spawnMock).toHaveBeenCalledWith(
|
|
90
|
+
'npm',
|
|
91
|
+
['run', 'seed', '--', '--force'],
|
|
92
|
+
expect.objectContaining({
|
|
93
|
+
cwd: path.resolve(projectDir, 'modules', 'my-auth'),
|
|
94
|
+
}),
|
|
95
|
+
);
|
|
96
|
+
});
|
|
90
97
|
});
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
3
2
|
import path from 'node:path';
|
|
4
3
|
import fs from 'fs-extra';
|
|
@@ -14,67 +13,55 @@ const tsxPackagePath = require.resolve('tsx/package.json');
|
|
|
14
13
|
const tsxDir = path.dirname(tsxPackagePath);
|
|
15
14
|
const TSX_BIN = path.resolve(tsxDir, 'dist/cli.mjs');
|
|
16
15
|
|
|
16
|
+
// eslint-disable-next-line no-console
|
|
17
17
|
console.log('Using TSX binary at:', TSX_BIN);
|
|
18
18
|
|
|
19
19
|
describe('Command Loading Integration', () => {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
beforeAll(async () => {
|
|
21
|
+
await fs.ensureDir(TEST_TMP_DIR);
|
|
22
|
+
});
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
afterAll(async () => {
|
|
25
|
+
await fs.remove(TEST_TMP_DIR);
|
|
26
|
+
});
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
it('should load commands from modules', async () => {
|
|
29
|
+
// Create nexical.yaml to mock project root
|
|
30
|
+
await fs.writeFile(path.join(TEST_TMP_DIR, 'nexical.yaml'), 'name: test-project');
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
// Setup module structure
|
|
33
|
+
const moduleDir = path.join(TEST_TMP_DIR, 'src/modules/test-mod/src/commands');
|
|
34
|
+
await fs.ensureDir(moduleDir);
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
console.log('Hello from test module!');
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
`;
|
|
36
|
+
const jsCommandContent = `
|
|
37
|
+
export default class HelloCommand {
|
|
38
|
+
constructor(cli) { this.cli = cli; }
|
|
39
|
+
async init() { }
|
|
40
|
+
async runInit(options) { return this.run(options); }
|
|
41
|
+
async run() { console.log('Hello from test module!'); }
|
|
42
|
+
}
|
|
43
|
+
HelloCommand.description = 'Test hello command';
|
|
44
|
+
HelloCommand.args = {};
|
|
45
|
+
`;
|
|
50
46
|
|
|
51
|
-
|
|
52
|
-
export default class HelloCommand {
|
|
53
|
-
constructor(cli) { this.cli = cli; }
|
|
54
|
-
async init() {}
|
|
55
|
-
async runInit(options) { return this.run(options); }
|
|
56
|
-
async run() { console.log('Hello from test module!'); }
|
|
57
|
-
}
|
|
58
|
-
HelloCommand.description = 'Test hello command';
|
|
59
|
-
HelloCommand.args = {};
|
|
60
|
-
`;
|
|
47
|
+
await fs.writeFile(path.join(moduleDir, 'hello.js'), jsCommandContent);
|
|
61
48
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
});
|
|
49
|
+
const { stdout, stderr } = await execa('node', [TSX_BIN, CLI_ENTRY, 'hello', '--debug'], {
|
|
50
|
+
cwd: TEST_TMP_DIR,
|
|
51
|
+
reject: false,
|
|
52
|
+
env: {
|
|
53
|
+
...process.env,
|
|
54
|
+
FORCE_COLOR: '0',
|
|
55
|
+
},
|
|
56
|
+
});
|
|
72
57
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
58
|
+
if (!stdout.includes('Hello from test module!')) {
|
|
59
|
+
// eslint-disable-next-line no-console
|
|
60
|
+
console.log('STDOUT:', stdout);
|
|
61
|
+
// eslint-disable-next-line no-console
|
|
62
|
+
console.log('STDERR:', stderr);
|
|
63
|
+
}
|
|
77
64
|
|
|
78
|
-
|
|
79
|
-
|
|
65
|
+
expect(stdout).toContain('Hello from test module!');
|
|
66
|
+
}, 20000);
|
|
80
67
|
});
|