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