Skip to content

Commit e13fec9

Browse files
committed
TRAC-137: Add native hosting option in catalyst CLI
1 parent 3ab3c67 commit e13fec9

41 files changed

Lines changed: 4243 additions & 196 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/catalyst/package.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,20 @@
2222
"node": "^20.0.0 || ^22.0.0 || ^24.0.0"
2323
},
2424
"dependencies": {
25+
"@inquirer/prompts": "^7.5.3",
2526
"@segment/analytics-node": "^2.2.1",
2627
"adm-zip": "^0.5.16",
2728
"commander": "^14.0.0",
2829
"conf": "^13.1.0",
2930
"consola": "^3.4.2",
31+
"cross-spawn": "^7.0.6",
3032
"dotenv": "^16.5.0",
3133
"execa": "^9.6.0",
34+
"fs-extra": "^11.3.0",
35+
"lodash.kebabcase": "^4.1.1",
36+
"nypm": "^0.5.4",
3237
"open": "^10.1.0",
38+
"std-env": "^3.9.0",
3339
"yocto-spinner": "^1.0.0",
3440
"zod": "^4.0.5"
3541
},
@@ -38,6 +44,9 @@
3844
"@bigcommerce/eslint-config-catalyst": "workspace:^",
3945
"@commander-js/extra-typings": "^14.0.0",
4046
"@types/adm-zip": "^0.5.7",
47+
"@types/cross-spawn": "^6.0.6",
48+
"@types/fs-extra": "^11.0.4",
49+
"@types/lodash.kebabcase": "^4.1.9",
4150
"@types/node": "^22.15.30",
4251
"@vitest/coverage-v8": "^3.2.4",
4352
"@vitest/ui": "^3.2.4",
Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,63 @@
11
import { Command } from 'commander';
2-
import { expect, test, vi } from 'vitest';
2+
import { execa } from 'execa';
3+
import { afterEach, beforeAll, beforeEach, expect, test, vi } from 'vitest';
4+
5+
import { consola } from '../lib/logger';
6+
import { getProjectState } from '../lib/project-state';
7+
import { program } from '../program';
38

49
import { build } from './build';
510

11+
vi.mock('execa', () => ({
12+
execa: vi.fn(() => Promise.resolve({})),
13+
__esModule: true,
14+
}));
15+
16+
vi.mock('../lib/project-state', () => ({
17+
getProjectState: vi.fn(),
18+
}));
19+
20+
const untransformedState = {
21+
projectUuid: undefined,
22+
hasMiddleware: false,
23+
hasProxy: true,
24+
hasOpenNextDep: false,
25+
isLinked: false,
26+
isTransformed: false,
27+
isFullySetUp: false,
28+
};
29+
630
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
731
vi.spyOn(process, 'exit').mockImplementation(() => null as never);
832

33+
beforeAll(() => {
34+
consola.wrapAll();
35+
});
36+
37+
beforeEach(() => {
38+
consola.mockTypes(() => vi.fn());
39+
});
40+
41+
afterEach(() => {
42+
vi.clearAllMocks();
43+
});
44+
945
test('properly configured Command instance', () => {
1046
expect(build).toBeInstanceOf(Command);
1147
expect(build.name()).toBe('build');
1248
expect(build.options).toEqual(
1349
expect.arrayContaining([expect.objectContaining({ long: '--project-uuid' })]),
1450
);
1551
});
52+
53+
test('falls through to `next build` when project is not transformed', async () => {
54+
vi.mocked(getProjectState).mockReturnValue(untransformedState);
55+
56+
await program.parseAsync(['node', 'catalyst', 'build']);
57+
58+
expect(execa).toHaveBeenCalledWith(
59+
'pnpm',
60+
['exec', 'next', 'build'],
61+
expect.objectContaining({ stdio: 'inherit', cwd: process.cwd() }),
62+
);
63+
});

packages/catalyst/src/cli/commands/build.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { join } from 'node:path';
66
import { getModuleCliPath } from '../lib/get-module-cli-path';
77
import { consola } from '../lib/logger';
88
import { getProjectConfig } from '../lib/project-config';
9+
import { getProjectState } from '../lib/project-state';
910
import { getWranglerConfig } from '../lib/wrangler-config';
1011

1112
const WRANGLER_VERSION = '4.24.3';
@@ -96,6 +97,23 @@ Examples:
9697
).env('CATALYST_PROJECT_UUID'),
9798
)
9899
.action(async (options) => {
100+
// Project must be transformed (middleware swapped in, OpenNext dep installed)
101+
// before the OpenNext build pipeline can run. If it isn't, fall through to
102+
// `next build` so this command works for self-hosted Catalyst projects too.
103+
const state = getProjectState();
104+
105+
if (!state.isTransformed) {
106+
consola.info('Project is not set up for Commerce Hosting — running `next build`.');
107+
consola.info('To deploy to Commerce Hosting, run `catalyst deploy`.');
108+
109+
await execa('pnpm', ['exec', 'next', 'build'], {
110+
stdio: 'inherit',
111+
cwd: process.cwd(),
112+
});
113+
114+
return;
115+
}
116+
99117
const config = getProjectConfig();
100118
const projectUuid = options.projectUuid ?? config.get('projectUuid');
101119

0 commit comments

Comments
 (0)