@swell/cli 2.3.4 → 2.4.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/dist/commands/api/delete.d.ts +1 -0
- package/dist/commands/api/delete.js +8 -1
- package/dist/commands/api/get.d.ts +1 -0
- package/dist/commands/api/get.js +9 -2
- package/dist/commands/api/index.js +4 -2
- package/dist/commands/api/post.d.ts +1 -0
- package/dist/commands/api/post.js +9 -2
- package/dist/commands/api/put.d.ts +1 -0
- package/dist/commands/api/put.js +9 -2
- package/dist/commands/app/dev.js +6 -2
- package/dist/commands/app/frontend/dev.js +6 -3
- package/dist/commands/create/app.js +2 -2
- package/dist/commands/create/frontend.js +2 -2
- package/dist/commands/create/tests.js +5 -2
- package/dist/commands/inspect/models.d.ts +0 -1
- package/dist/commands/inspect/models.js +2 -15
- package/dist/commands/theme/dev.js +10 -7
- package/dist/create-app-command.d.ts +8 -2
- package/dist/create-app-command.js +137 -79
- package/dist/env/local.d.ts +1 -0
- package/dist/env/local.js +1 -0
- package/dist/env/production.d.ts +1 -0
- package/dist/env/production.js +1 -0
- package/dist/env/review.d.ts +1 -0
- package/dist/env/review.js +1 -0
- package/dist/env/staging.d.ts +1 -0
- package/dist/env/staging.js +1 -0
- package/dist/lib/api.d.ts +5 -2
- package/dist/lib/api.js +60 -9
- package/dist/lib/apps/index.d.ts +11 -0
- package/dist/lib/apps/index.js +45 -46
- package/dist/lib/constants.d.ts +1 -0
- package/dist/lib/constants.js +5 -0
- package/dist/lib/create/tests.js +1 -1
- package/dist/lib/package-manager.d.ts +86 -0
- package/dist/lib/package-manager.js +186 -0
- package/dist/push-app-command.js +13 -6
- package/dist/swell-api-command.js +8 -2
- package/oclif.manifest.json +76 -15
- package/package.json +1 -1
package/dist/lib/apps/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import { pluralize, titleize } from 'inflection';
|
|
|
3
3
|
import * as fs from 'node:fs';
|
|
4
4
|
import * as path from 'node:path';
|
|
5
5
|
import { toAppId } from '../create/index.js';
|
|
6
|
+
import { detectPackageManager, transformCommand } from '../package-manager.js';
|
|
6
7
|
import { AppConfig } from './app-config.js';
|
|
7
8
|
export { AppConfig, FunctionProcessingError, IgnoringFileError, } from './app-config.js';
|
|
8
9
|
export { allBaseFilesInDir, allConfigDirsPaths, allConfigFilesInDir, allConfigFilesPaths, allConfigFilesPathsByType, getAllConfigPaths, globAllFilesByPath, isPathDirectory, } from './paths.js';
|
|
@@ -113,54 +114,42 @@ export var ConfigInputFields;
|
|
|
113
114
|
ConfigInputFields["VALUES"] = "values";
|
|
114
115
|
ConfigInputFields["VERSION"] = "version";
|
|
115
116
|
})(ConfigInputFields || (ConfigInputFields = {}));
|
|
116
|
-
//
|
|
117
|
-
|
|
118
|
-
const LegacyFrontendProjectTypes = [
|
|
117
|
+
// Frontend project types - all use direct commands run from frontend/ directory
|
|
118
|
+
export const FrontendProjectTypes = [
|
|
119
119
|
{
|
|
120
120
|
buildCommand: 'npx astro build',
|
|
121
121
|
devCommand: 'npx astro dev --port ${PORT}',
|
|
122
|
-
mainPackage: 'astro',
|
|
123
|
-
name: 'Astro',
|
|
124
|
-
slug: 'astro',
|
|
125
|
-
},
|
|
126
|
-
];
|
|
127
|
-
// Modern apps (workspace-based) - All frameworks
|
|
128
|
-
export const FrontendProjectTypes = [
|
|
129
|
-
{
|
|
130
|
-
buildCommand: 'npm exec --workspace=frontend -- astro build',
|
|
131
|
-
devCommand: 'npm exec --workspace=frontend -- astro dev --port ${PORT}',
|
|
132
122
|
installCommand: 'npm create cloudflare@latest -- frontend --framework=astro --deploy=false --git=false -- --no-git --yes --skip-houston --typescript strict',
|
|
133
123
|
mainPackage: 'astro',
|
|
134
124
|
name: 'Astro',
|
|
135
125
|
slug: 'astro',
|
|
136
126
|
},
|
|
137
127
|
{
|
|
138
|
-
buildCommand: '
|
|
139
|
-
devCommand: '
|
|
128
|
+
buildCommand: 'npx ng build',
|
|
129
|
+
devCommand: 'npx ng serve --port ${PORT}',
|
|
140
130
|
installCommand: 'npm create cloudflare@latest -- frontend --framework=angular --deploy=false --git=false -- --style=sass --zoneless --ai-config=none',
|
|
141
131
|
mainPackage: '@angular/core',
|
|
142
132
|
name: 'Angular',
|
|
143
133
|
slug: 'angular',
|
|
144
134
|
},
|
|
145
135
|
{
|
|
146
|
-
devCommand: '
|
|
136
|
+
devCommand: 'npx wrangler dev --port ${PORT}',
|
|
147
137
|
installCommand: 'npm create cloudflare@latest -- frontend --framework=hono --deploy=false --git=false',
|
|
148
138
|
mainPackage: 'hono',
|
|
149
139
|
name: 'Hono',
|
|
150
140
|
slug: 'hono',
|
|
151
141
|
},
|
|
152
142
|
{
|
|
153
|
-
buildCommand: '
|
|
154
|
-
devCommand: '
|
|
143
|
+
buildCommand: 'npx nuxt build',
|
|
144
|
+
devCommand: 'npx nuxt dev --port ${PORT}',
|
|
155
145
|
installCommand: 'npm create cloudflare@latest -- frontend --framework=nuxt --deploy=false --git=false -- --no-modules -f',
|
|
156
146
|
mainPackage: 'nuxt',
|
|
157
147
|
name: 'Nuxt',
|
|
158
148
|
slug: 'nuxt',
|
|
159
149
|
},
|
|
160
150
|
{
|
|
161
|
-
buildCommand: '
|
|
162
|
-
|
|
163
|
-
devCommand: 'npm exec --workspace=frontend -- next dev --turbopack --port ${PORT}',
|
|
151
|
+
buildCommand: 'npx opennextjs-cloudflare build',
|
|
152
|
+
devCommand: 'npx next dev --turbopack --port ${PORT}',
|
|
164
153
|
installCommand: 'npm create cloudflare@latest -- frontend --framework=next --deploy=false --git=false -- --typescript --use-npm --src-dir --app --eslint --import-alias "@/*" --tailwind --turbopack',
|
|
165
154
|
mainPackage: 'next',
|
|
166
155
|
name: 'Next.js',
|
|
@@ -183,28 +172,8 @@ export function getFrontendProjectValidValues(withNone = true, withLegacy = true
|
|
|
183
172
|
export function getAppSlugId(app) {
|
|
184
173
|
return toAppId(app.private_id) || app.public_id || app.id;
|
|
185
174
|
}
|
|
186
|
-
function hasWorkspaceStructure(appPath) {
|
|
187
|
-
// Check if root package.json has workspaces field including 'frontend'
|
|
188
|
-
const rootPkgPath = path.join(appPath, 'package.json');
|
|
189
|
-
if (!filePathExists(rootPkgPath)) {
|
|
190
|
-
return false;
|
|
191
|
-
}
|
|
192
|
-
try {
|
|
193
|
-
const content = fs.readFileSync(rootPkgPath, 'utf8');
|
|
194
|
-
const pkg = JSON.parse(content);
|
|
195
|
-
return Array.isArray(pkg.workspaces) && pkg.workspaces.includes('frontend');
|
|
196
|
-
}
|
|
197
|
-
catch {
|
|
198
|
-
return false;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
175
|
export function getFrontendProjectType(appPath) {
|
|
202
|
-
//
|
|
203
|
-
const hasWorkspace = hasWorkspaceStructure(appPath);
|
|
204
|
-
const projectTypes = hasWorkspace
|
|
205
|
-
? FrontendProjectTypes
|
|
206
|
-
: LegacyFrontendProjectTypes;
|
|
207
|
-
// Try frontend/package.json first (new workspace structure), then root package.json (old structure)
|
|
176
|
+
// Try frontend/package.json first (workspace structure), then root package.json (legacy)
|
|
208
177
|
const pkgPaths = [
|
|
209
178
|
path.join(appPath, 'frontend', 'package.json'),
|
|
210
179
|
path.join(appPath, 'package.json'),
|
|
@@ -216,15 +185,16 @@ export function getFrontendProjectType(appPath) {
|
|
|
216
185
|
try {
|
|
217
186
|
const content = fs.readFileSync(pkgPath, 'utf8');
|
|
218
187
|
const pkg = JSON.parse(content);
|
|
219
|
-
for (const projectType of
|
|
188
|
+
for (const projectType of FrontendProjectTypes) {
|
|
220
189
|
if (pkg.dependencies?.[projectType.mainPackage] ||
|
|
221
190
|
pkg.devDependencies?.[projectType.mainPackage]) {
|
|
222
191
|
// Create a copy to avoid mutating the original
|
|
223
192
|
const detectedType = { ...projectType };
|
|
224
193
|
// If buildCommand not explicitly set in framework definition, detect it
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
194
|
+
// Check if package.json has a "build" script, if not set to empty string (no build needed)
|
|
195
|
+
detectedType.buildCommand ||= pkg.scripts?.build
|
|
196
|
+
? 'npm run build'
|
|
197
|
+
: '';
|
|
228
198
|
return detectedType;
|
|
229
199
|
}
|
|
230
200
|
}
|
|
@@ -236,6 +206,20 @@ export function getFrontendProjectType(appPath) {
|
|
|
236
206
|
}
|
|
237
207
|
return undefined;
|
|
238
208
|
}
|
|
209
|
+
/**
|
|
210
|
+
* Get project commands transformed for the detected package manager.
|
|
211
|
+
* Detects the package manager from lock files in appPath and transforms
|
|
212
|
+
* npm-style commands to the equivalent for that package manager.
|
|
213
|
+
*/
|
|
214
|
+
export function getProjectCommands(appPath, projectType) {
|
|
215
|
+
const pm = detectPackageManager(appPath);
|
|
216
|
+
return {
|
|
217
|
+
buildCommand: projectType.buildCommand
|
|
218
|
+
? transformCommand(projectType.buildCommand, pm)
|
|
219
|
+
: undefined,
|
|
220
|
+
devCommand: transformCommand(projectType.devCommand, pm),
|
|
221
|
+
};
|
|
222
|
+
}
|
|
239
223
|
export function getConfigTypeFromPath(path) {
|
|
240
224
|
for (const type in AllConfigPaths) {
|
|
241
225
|
if (AllConfigPaths[type] === path) {
|
|
@@ -377,3 +361,18 @@ export function batchAppFilesByType(configs) {
|
|
|
377
361
|
// Add base files if config types are not specified
|
|
378
362
|
return batches;
|
|
379
363
|
}
|
|
364
|
+
export function hasAppContext() {
|
|
365
|
+
return fs.existsSync('swell.json');
|
|
366
|
+
}
|
|
367
|
+
export async function getCurrentAppSlugId() {
|
|
368
|
+
if (!hasAppContext()) {
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
try {
|
|
372
|
+
const appConfig = JSON.parse(await fs.promises.readFile('swell.json', 'utf8'));
|
|
373
|
+
return appConfig.id;
|
|
374
|
+
}
|
|
375
|
+
catch {
|
|
376
|
+
// noop
|
|
377
|
+
}
|
|
378
|
+
}
|
package/dist/lib/constants.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export declare const API_BASE_URL: any;
|
|
|
2
2
|
export declare const LOCAL_PROXY_PROVIDER: any;
|
|
3
3
|
export declare const SCHEMAS_BASE_URL: any;
|
|
4
4
|
export declare function getAdminApiBaseUrl(storeId: string): any;
|
|
5
|
+
export declare function getFrontendApiBaseUrl(storeId: string): any;
|
|
5
6
|
export declare function getLoginHost(storeId?: string): any;
|
|
6
7
|
export declare function getAppFrontendHost(storeId: string, installedAppId: string): any;
|
|
7
8
|
export declare function getStorefrontFrontendHost(storefrontSlug: string, branchId?: string): any;
|
package/dist/lib/constants.js
CHANGED
|
@@ -5,6 +5,8 @@ import { env } from './config.js';
|
|
|
5
5
|
export const API_BASE_URL = env.get('API_BASE_URL') || defaultEnv.API_BASE_URL;
|
|
6
6
|
// url for admin api calls
|
|
7
7
|
const ADMIN_API_BASE_URL = env.get('ADMIN_API_BASE_URL') || defaultEnv.ADMIN_API_BASE_URL;
|
|
8
|
+
// url for store api calls
|
|
9
|
+
const FRONTEND_API_BASE_URL = env.get('FRONTEND_API_BASE_URL') || defaultEnv.FRONTEND_API_BASE_URL;
|
|
8
10
|
// url host for admin login
|
|
9
11
|
const LOGIN_HOST = env.get('LOGIN_HOST') || defaultEnv.LOGIN_HOST;
|
|
10
12
|
// url host for app frontend
|
|
@@ -19,6 +21,9 @@ export const SCHEMAS_BASE_URL = env.get('SCHEMAS_BASE_URL') || defaultEnv.SCHEMA
|
|
|
19
21
|
export function getAdminApiBaseUrl(storeId) {
|
|
20
22
|
return ADMIN_API_BASE_URL.replace('${STORE_ID}', storeId);
|
|
21
23
|
}
|
|
24
|
+
export function getFrontendApiBaseUrl(storeId) {
|
|
25
|
+
return FRONTEND_API_BASE_URL.replace('${STORE_ID}', storeId);
|
|
26
|
+
}
|
|
22
27
|
export function getLoginHost(storeId) {
|
|
23
28
|
return LOGIN_HOST.replace('${STORE_ID}', storeId || 'login');
|
|
24
29
|
}
|
package/dist/lib/create/tests.js
CHANGED
|
@@ -4,7 +4,7 @@ import { filePathExists, writeFile } from '../apps/index.js';
|
|
|
4
4
|
import { envDtsTemplate, integrationTestTemplate, mockRequestTemplate, setupGlobalsTemplate, swellClientTemplate, tsconfigTemplate, unitTestTemplate, vitestConfigTemplate, } from './tests/templates/index.js';
|
|
5
5
|
const TEST_DEV_DEPENDENCIES = {
|
|
6
6
|
'@cloudflare/vitest-pool-workers': 'latest',
|
|
7
|
-
vitest: '
|
|
7
|
+
vitest: '3.2.x',
|
|
8
8
|
'@swell/app-types': 'latest',
|
|
9
9
|
};
|
|
10
10
|
async function writeTextFile(filePath, contents, overwrite) {
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
export type PackageManager = 'npm' | 'yarn' | 'pnpm' | 'bun';
|
|
2
|
+
export interface PackageManagerCommands {
|
|
3
|
+
/** Install command (e.g., 'npm install') */
|
|
4
|
+
install: string;
|
|
5
|
+
/** Run a script (e.g., 'npm run build' for script 'build') */
|
|
6
|
+
run: (script: string) => string;
|
|
7
|
+
/** Execute command in workspace (e.g., 'npm exec --workspace=frontend -- astro build') */
|
|
8
|
+
workspaceExec: (workspace: string, command: string) => string;
|
|
9
|
+
/** Run a script in a workspace (e.g., 'npm run build --workspace=frontend') */
|
|
10
|
+
workspaceRun: (workspace: string, script: string) => string;
|
|
11
|
+
/** Execute a binary (e.g., 'npx wrangler') */
|
|
12
|
+
exec: (command: string) => string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Find lock file in directory or parent directories
|
|
16
|
+
* @returns Lock file name and path, or null if not found
|
|
17
|
+
*/
|
|
18
|
+
export declare function findLockFile(directory: string): {
|
|
19
|
+
name: string;
|
|
20
|
+
packageManager: PackageManager;
|
|
21
|
+
path: string;
|
|
22
|
+
} | null;
|
|
23
|
+
/**
|
|
24
|
+
* Detect package manager from lock file in directory tree
|
|
25
|
+
* @returns Detected package manager, defaults to 'npm' if no lock file found
|
|
26
|
+
*/
|
|
27
|
+
export declare function detectPackageManager(directory: string): PackageManager;
|
|
28
|
+
/**
|
|
29
|
+
* Get command templates for a package manager
|
|
30
|
+
*/
|
|
31
|
+
export declare function getPackageManagerCommands(pm: PackageManager): PackageManagerCommands;
|
|
32
|
+
/**
|
|
33
|
+
* Detect package manager and get its commands
|
|
34
|
+
*/
|
|
35
|
+
export declare function getPackageManager(directory: string): {
|
|
36
|
+
commands: PackageManagerCommands;
|
|
37
|
+
lockFile: {
|
|
38
|
+
name: string;
|
|
39
|
+
path: string;
|
|
40
|
+
} | null;
|
|
41
|
+
name: PackageManager;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Transform an npm command to the equivalent for the detected package manager
|
|
45
|
+
* Handles common patterns:
|
|
46
|
+
* - 'npm run X' -> 'yarn run X' / 'pnpm run X' / 'bun run X'
|
|
47
|
+
* - 'npm run X --workspace=Y' -> 'yarn workspace Y run X' / 'pnpm --filter Y run X' / 'bun run --filter Y X'
|
|
48
|
+
* - 'npm exec --workspace=X -- Y' -> equivalent workspace command
|
|
49
|
+
* - 'npx X' -> 'yarn dlx X' / 'pnpm dlx X' / 'bunx X'
|
|
50
|
+
*/
|
|
51
|
+
export declare function transformCommand(command: string, pm: PackageManager): string;
|
|
52
|
+
/**
|
|
53
|
+
* Transform npm create commands to the equivalent for another package manager.
|
|
54
|
+
* Handles 'npm create X@version -- args' -> 'pm create X@version args'
|
|
55
|
+
* Also transforms --use-npm to --use-{pm} for CLIs that support it (e.g., Next.js)
|
|
56
|
+
* For yarn, removes @latest suffix since yarn classic (v1) may not handle it properly.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* transformCreateCommand('npm create cloudflare@latest -- frontend --framework=hono', 'bun')
|
|
60
|
+
* // Returns: 'bun create cloudflare@latest frontend --framework=hono'
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* transformCreateCommand('npm create cloudflare@latest -- frontend --framework=next --use-npm', 'pnpm')
|
|
64
|
+
* // Returns: 'pnpm create cloudflare@latest frontend --framework=next --use-pnpm'
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* transformCreateCommand('npm create cloudflare@latest -- frontend --framework=astro', 'yarn')
|
|
68
|
+
* // Returns: 'yarn create cloudflare -- frontend --framework=astro'
|
|
69
|
+
*/
|
|
70
|
+
export declare function transformCreateCommand(command: string, pm: PackageManager): string;
|
|
71
|
+
/**
|
|
72
|
+
* Get spawn arguments for executing a package binary.
|
|
73
|
+
* Returns the program and any prefix args needed before the command args.
|
|
74
|
+
* Useful for node's spawn() which needs program and args separately.
|
|
75
|
+
*
|
|
76
|
+
* Uses npx for most package managers (universally available via Node.js).
|
|
77
|
+
* Uses bunx for bun projects (performance benefit from Bun runtime).
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* const { program, prefixArgs } = getExecSpawnArgs('npm');
|
|
81
|
+
* spawn(program, [...prefixArgs, 'wrangler', 'dev', '--port=3000']);
|
|
82
|
+
*/
|
|
83
|
+
export declare function getExecSpawnArgs(pm: PackageManager): {
|
|
84
|
+
prefixArgs: string[];
|
|
85
|
+
program: string;
|
|
86
|
+
};
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
/** Lock file to package manager mapping (priority order: bun > pnpm > yarn > npm) */
|
|
4
|
+
const LOCK_FILES = [
|
|
5
|
+
['bun.lockb', 'bun'],
|
|
6
|
+
['bun.lock', 'bun'],
|
|
7
|
+
['pnpm-lock.yaml', 'pnpm'],
|
|
8
|
+
['yarn.lock', 'yarn'],
|
|
9
|
+
['package-lock.json', 'npm'],
|
|
10
|
+
];
|
|
11
|
+
/** Command templates for each package manager */
|
|
12
|
+
const COMMANDS = {
|
|
13
|
+
npm: {
|
|
14
|
+
install: 'npm install',
|
|
15
|
+
run: (script) => `npm run ${script}`,
|
|
16
|
+
workspaceExec: (workspace, command) => `npm exec --workspace=${workspace} -- ${command}`,
|
|
17
|
+
workspaceRun: (workspace, script) => `npm run ${script} --workspace=${workspace}`,
|
|
18
|
+
exec: (command) => `npx ${command}`,
|
|
19
|
+
},
|
|
20
|
+
yarn: {
|
|
21
|
+
install: 'yarn install',
|
|
22
|
+
run: (script) => `yarn run ${script}`,
|
|
23
|
+
workspaceExec: (workspace, command) => `yarn workspace ${workspace} ${command}`,
|
|
24
|
+
workspaceRun: (workspace, script) => `yarn workspace ${workspace} run ${script}`,
|
|
25
|
+
exec: (command) => `yarn dlx ${command}`,
|
|
26
|
+
},
|
|
27
|
+
pnpm: {
|
|
28
|
+
install: 'pnpm install',
|
|
29
|
+
run: (script) => `pnpm run ${script}`,
|
|
30
|
+
workspaceExec: (workspace, command) => `pnpm --filter ${workspace} exec ${command}`,
|
|
31
|
+
workspaceRun: (workspace, script) => `pnpm --filter ${workspace} run ${script}`,
|
|
32
|
+
exec: (command) => `pnpm dlx ${command}`,
|
|
33
|
+
},
|
|
34
|
+
bun: {
|
|
35
|
+
install: 'bun install',
|
|
36
|
+
run: (script) => `bun run ${script}`,
|
|
37
|
+
// Use --cwd for executing binaries in a workspace (--filter is for scripts only)
|
|
38
|
+
workspaceExec: (workspace, command) => `bun --cwd ${workspace} x ${command}`,
|
|
39
|
+
workspaceRun: (workspace, script) => `bun run --filter ${workspace} ${script}`,
|
|
40
|
+
exec: (command) => `bunx ${command}`,
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Find lock file in directory or parent directories
|
|
45
|
+
* @returns Lock file name and path, or null if not found
|
|
46
|
+
*/
|
|
47
|
+
export function findLockFile(directory) {
|
|
48
|
+
let currentDir = path.resolve(directory);
|
|
49
|
+
const { root } = path.parse(currentDir);
|
|
50
|
+
while (currentDir !== root) {
|
|
51
|
+
for (const [lockFile, pm] of LOCK_FILES) {
|
|
52
|
+
const lockPath = path.join(currentDir, lockFile);
|
|
53
|
+
if (fs.existsSync(lockPath)) {
|
|
54
|
+
return { name: lockFile, packageManager: pm, path: lockPath };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
currentDir = path.dirname(currentDir);
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Detect package manager from lock file in directory tree
|
|
63
|
+
* @returns Detected package manager, defaults to 'npm' if no lock file found
|
|
64
|
+
*/
|
|
65
|
+
export function detectPackageManager(directory) {
|
|
66
|
+
const lockFile = findLockFile(directory);
|
|
67
|
+
return lockFile?.packageManager ?? 'npm';
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Get command templates for a package manager
|
|
71
|
+
*/
|
|
72
|
+
export function getPackageManagerCommands(pm) {
|
|
73
|
+
return COMMANDS[pm];
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Detect package manager and get its commands
|
|
77
|
+
*/
|
|
78
|
+
export function getPackageManager(directory) {
|
|
79
|
+
const lockFile = findLockFile(directory);
|
|
80
|
+
const name = lockFile?.packageManager ?? 'npm';
|
|
81
|
+
return {
|
|
82
|
+
commands: COMMANDS[name],
|
|
83
|
+
lockFile: lockFile ? { name: lockFile.name, path: lockFile.path } : null,
|
|
84
|
+
name,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Transform an npm command to the equivalent for the detected package manager
|
|
89
|
+
* Handles common patterns:
|
|
90
|
+
* - 'npm run X' -> 'yarn run X' / 'pnpm run X' / 'bun run X'
|
|
91
|
+
* - 'npm run X --workspace=Y' -> 'yarn workspace Y run X' / 'pnpm --filter Y run X' / 'bun run --filter Y X'
|
|
92
|
+
* - 'npm exec --workspace=X -- Y' -> equivalent workspace command
|
|
93
|
+
* - 'npx X' -> 'yarn dlx X' / 'pnpm dlx X' / 'bunx X'
|
|
94
|
+
*/
|
|
95
|
+
export function transformCommand(command, pm) {
|
|
96
|
+
if (pm === 'npm') {
|
|
97
|
+
return command;
|
|
98
|
+
}
|
|
99
|
+
const commands = COMMANDS[pm];
|
|
100
|
+
// Transform 'npm exec --workspace=X -- Y'
|
|
101
|
+
const workspaceMatch = command.match(/^npm exec --workspace=(\S+) -- (.+)$/);
|
|
102
|
+
if (workspaceMatch) {
|
|
103
|
+
return commands.workspaceExec(workspaceMatch[1], workspaceMatch[2]);
|
|
104
|
+
}
|
|
105
|
+
// Transform 'npm run X --workspace=Y'
|
|
106
|
+
const workspaceRunMatch = command.match(/^npm run (\S+) --workspace=(\S+)$/);
|
|
107
|
+
if (workspaceRunMatch) {
|
|
108
|
+
return commands.workspaceRun(workspaceRunMatch[2], workspaceRunMatch[1]);
|
|
109
|
+
}
|
|
110
|
+
// Transform 'npm run X' (with optional flags)
|
|
111
|
+
const runMatch = command.match(/^npm run (\S.*)$/);
|
|
112
|
+
if (runMatch) {
|
|
113
|
+
return commands.run(runMatch[1]);
|
|
114
|
+
}
|
|
115
|
+
// Transform 'npx X' to 'bunx X' for bun (performance benefit from Bun runtime)
|
|
116
|
+
// For other package managers, keep npx as-is (universally available via Node.js)
|
|
117
|
+
if (pm === 'bun') {
|
|
118
|
+
const npxMatch = command.match(/^npx (.+)$/);
|
|
119
|
+
if (npxMatch) {
|
|
120
|
+
return `bunx ${npxMatch[1]}`;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// Warn if command looks like npm but wasn't transformed
|
|
124
|
+
if (command.startsWith('npm ')) {
|
|
125
|
+
console.warn(`Warning: Command "${command}" was not transformed for ${pm}.`);
|
|
126
|
+
}
|
|
127
|
+
return command;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Transform npm create commands to the equivalent for another package manager.
|
|
131
|
+
* Handles 'npm create X@version -- args' -> 'pm create X@version args'
|
|
132
|
+
* Also transforms --use-npm to --use-{pm} for CLIs that support it (e.g., Next.js)
|
|
133
|
+
* For yarn, removes @latest suffix since yarn classic (v1) may not handle it properly.
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* transformCreateCommand('npm create cloudflare@latest -- frontend --framework=hono', 'bun')
|
|
137
|
+
* // Returns: 'bun create cloudflare@latest frontend --framework=hono'
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* transformCreateCommand('npm create cloudflare@latest -- frontend --framework=next --use-npm', 'pnpm')
|
|
141
|
+
* // Returns: 'pnpm create cloudflare@latest frontend --framework=next --use-pnpm'
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* transformCreateCommand('npm create cloudflare@latest -- frontend --framework=astro', 'yarn')
|
|
145
|
+
* // Returns: 'yarn create cloudflare -- frontend --framework=astro'
|
|
146
|
+
*/
|
|
147
|
+
export function transformCreateCommand(command, pm) {
|
|
148
|
+
if (pm === 'npm') {
|
|
149
|
+
return command;
|
|
150
|
+
}
|
|
151
|
+
// Match 'npm create X@version -- args' or 'npm create X -- args'
|
|
152
|
+
const createMatch = command.match(/^npm create (\S+) -- (.+)$/);
|
|
153
|
+
if (createMatch) {
|
|
154
|
+
let [, packageWithVersion, args] = createMatch;
|
|
155
|
+
// Replace --use-npm with --use-{pm} for Next.js and similar CLIs
|
|
156
|
+
const transformedArgs = args.replace(/--use-npm\b/, `--use-${pm}`);
|
|
157
|
+
// For yarn classic (v1), remove @latest since yarn create fetches latest by default
|
|
158
|
+
// and may not properly handle version specifiers. Also add -- separator for args.
|
|
159
|
+
if (pm === 'yarn') {
|
|
160
|
+
packageWithVersion = packageWithVersion.replace(/@latest$/, '');
|
|
161
|
+
return `yarn create ${packageWithVersion} -- ${transformedArgs}`;
|
|
162
|
+
}
|
|
163
|
+
return `${pm} create ${packageWithVersion} ${transformedArgs}`;
|
|
164
|
+
}
|
|
165
|
+
return command;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Get spawn arguments for executing a package binary.
|
|
169
|
+
* Returns the program and any prefix args needed before the command args.
|
|
170
|
+
* Useful for node's spawn() which needs program and args separately.
|
|
171
|
+
*
|
|
172
|
+
* Uses npx for most package managers (universally available via Node.js).
|
|
173
|
+
* Uses bunx for bun projects (performance benefit from Bun runtime).
|
|
174
|
+
*
|
|
175
|
+
* @example
|
|
176
|
+
* const { program, prefixArgs } = getExecSpawnArgs('npm');
|
|
177
|
+
* spawn(program, [...prefixArgs, 'wrangler', 'dev', '--port=3000']);
|
|
178
|
+
*/
|
|
179
|
+
export function getExecSpawnArgs(pm) {
|
|
180
|
+
// Use bunx for bun (performance benefit), npx for everything else
|
|
181
|
+
// npx is universally available via Node.js and avoids yarn dlx v1/v2 issues
|
|
182
|
+
if (pm === 'bun') {
|
|
183
|
+
return { prefixArgs: [], program: 'bunx' };
|
|
184
|
+
}
|
|
185
|
+
return { prefixArgs: [], program: 'npx' };
|
|
186
|
+
}
|
package/dist/push-app-command.js
CHANGED
|
@@ -6,10 +6,11 @@ import * as path from 'node:path';
|
|
|
6
6
|
import Stream from 'node:stream';
|
|
7
7
|
import ora from 'ora';
|
|
8
8
|
import { default as swellConfig } from './lib/app-config.js';
|
|
9
|
-
import { ConfigType, getFrontendProjectValidValues, allConfigFilesInDir, appConfigFromFile, filePathExists, filePathExistsAsync, findAppConfig, getAppSlugId, getConfigTypeFromPath, getConfigTypeKeyFromValue, getFrontendProjectType, globAllFilesByPath, hashString, isPathDirectory, } from './lib/apps/index.js';
|
|
9
|
+
import { ConfigType, getFrontendProjectValidValues, allConfigFilesInDir, appConfigFromFile, filePathExists, filePathExistsAsync, findAppConfig, getAppSlugId, getConfigTypeFromPath, getConfigTypeKeyFromValue, getFrontendProjectType, getProjectCommands, globAllFilesByPath, hashString, isPathDirectory, } from './lib/apps/index.js';
|
|
10
10
|
import { getGlobIgnorePathsChecker } from './lib/apps/paths.js';
|
|
11
11
|
import { default as localConfig } from './lib/config.js';
|
|
12
12
|
import { toAppId } from './lib/create/index.js';
|
|
13
|
+
import { detectPackageManager, transformCommand, } from './lib/package-manager.js';
|
|
13
14
|
import { getProxyUrl } from './lib/proxy.js';
|
|
14
15
|
import { selectEnvironmentId } from './lib/stores.js';
|
|
15
16
|
import style from './lib/style.js';
|
|
@@ -109,12 +110,14 @@ export class PushAppCommand extends RemoteAppCommand {
|
|
|
109
110
|
this.showFrontendMigrationError();
|
|
110
111
|
return; // unreachable but helps TypeScript narrow the type
|
|
111
112
|
}
|
|
112
|
-
|
|
113
|
+
// Get commands transformed for the detected package manager
|
|
114
|
+
const { buildCommand } = getProjectCommands(this.appPath, projectType);
|
|
115
|
+
if (!buildCommand) {
|
|
113
116
|
// No build command needed for this framework
|
|
114
117
|
return;
|
|
115
118
|
}
|
|
116
119
|
this.log(`Building ${projectType.name} frontend...\n`);
|
|
117
|
-
await this.execFrontend(
|
|
120
|
+
await this.execFrontend(buildCommand);
|
|
118
121
|
}
|
|
119
122
|
async chooseAppToPull(query) {
|
|
120
123
|
const typeLabelPlural = this.appType === 'theme' ? 'themes' : 'apps';
|
|
@@ -332,7 +335,8 @@ export class PushAppCommand extends RemoteAppCommand {
|
|
|
332
335
|
}
|
|
333
336
|
}
|
|
334
337
|
async execFrontend(command, onOutput) {
|
|
335
|
-
|
|
338
|
+
// Run from frontend directory where wrangler.toml and framework configs live
|
|
339
|
+
return this.exec(command, this.frontendPath || path.join(this.appPath, 'frontend'), onOutput);
|
|
336
340
|
}
|
|
337
341
|
async getAllAppStorefronts(params) {
|
|
338
342
|
const query = Object.entries(params || {})
|
|
@@ -794,12 +798,15 @@ export class PushAppCommand extends RemoteAppCommand {
|
|
|
794
798
|
let pagesProjectError = false;
|
|
795
799
|
let outputBuffer = '';
|
|
796
800
|
this.log(`\nDeploying to Cloudflare...\n`);
|
|
801
|
+
// Get the wrangler deploy command for the detected package manager
|
|
802
|
+
const pm = detectPackageManager(this.appPath);
|
|
803
|
+
const deployCommand = transformCommand('npx wrangler deploy', pm);
|
|
797
804
|
try {
|
|
798
|
-
await this.execFrontend(
|
|
805
|
+
await this.execFrontend(deployCommand, (string) => {
|
|
799
806
|
// Accumulate output for URL parsing after command completes
|
|
800
807
|
outputBuffer += string;
|
|
801
808
|
// Suppress workers.dev URL from output (users should use swell domain)
|
|
802
|
-
if (
|
|
809
|
+
if (/^\s*https:\/\/\S+\.workers\.dev\s*$/m.test(string)) {
|
|
803
810
|
return false;
|
|
804
811
|
}
|
|
805
812
|
// Check for Pages project error
|
|
@@ -29,10 +29,14 @@ export class SwellApiCommand extends SwellCommand {
|
|
|
29
29
|
const parsedInput = await super.parse(options, argv);
|
|
30
30
|
const { args, flags } = parsedInput;
|
|
31
31
|
const { path: requestPath } = args;
|
|
32
|
-
const { live, body } = flags;
|
|
32
|
+
const { live, api, body } = flags;
|
|
33
|
+
const isFrontendAPI = api === 'frontend';
|
|
33
34
|
if (!live) {
|
|
34
35
|
await this.api.setEnv('test');
|
|
35
36
|
}
|
|
37
|
+
if (isFrontendAPI) {
|
|
38
|
+
await this.api.setPublicKey();
|
|
39
|
+
}
|
|
36
40
|
if (!requestPath.startsWith('/')) {
|
|
37
41
|
throw new Error('Path must start with a forward slash (/)');
|
|
38
42
|
}
|
|
@@ -53,7 +57,9 @@ export class SwellApiCommand extends SwellCommand {
|
|
|
53
57
|
const queryParams = this.parseQueryString(queryString);
|
|
54
58
|
return this.buildFunctionCallRequest(parsedInput, functionId, processedBody, queryParams);
|
|
55
59
|
}
|
|
56
|
-
const paths =
|
|
60
|
+
const paths = isFrontendAPI
|
|
61
|
+
? { frontendPath: requestPath }
|
|
62
|
+
: { adminPath: `/data${requestPath}` };
|
|
57
63
|
return {
|
|
58
64
|
...parsedInput,
|
|
59
65
|
paths,
|