@swell/cli 2.0.19 → 2.1.0
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/app/dev.d.ts +16 -2
- package/dist/commands/app/dev.js +359 -3
- package/dist/commands/app/frontend/dev.d.ts +7 -6
- package/dist/commands/app/frontend/dev.js +123 -60
- package/dist/commands/app/install.js +15 -4
- package/dist/create-app-command.d.ts +4 -0
- package/dist/create-app-command.js +161 -1
- 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/apps/app-config.d.ts +49 -0
- package/dist/lib/apps/app-config.js +21 -14
- package/dist/lib/apps/index.d.ts +3 -4
- package/dist/lib/apps/index.js +102 -19
- package/dist/lib/bundle.js +2 -1
- package/dist/lib/constants.d.ts +1 -4
- package/dist/lib/constants.js +5 -4
- package/dist/lib/proxy.d.ts +1 -0
- package/dist/lib/proxy.js +29 -1
- package/dist/lib/swell-function-wrapper.d.ts +23 -4
- package/dist/lib/swell-function-wrapper.js +11 -4
- package/dist/push-app-command.d.ts +8 -4
- package/dist/push-app-command.js +100 -23
- package/dist/remote-app-command.js +24 -4
- package/oclif.manifest.json +52 -11
- package/package.json +3 -1
|
@@ -1,26 +1,21 @@
|
|
|
1
1
|
import { Flags } from '@oclif/core';
|
|
2
|
-
import getPort, { portNumbers } from 'get-port';
|
|
3
|
-
import ngrok from 'ngrok';
|
|
4
|
-
import ora from 'ora';
|
|
5
2
|
import { default as localConfig } from '../../../lib/config.js';
|
|
6
3
|
import style from '../../../lib/style.js';
|
|
7
4
|
import { PushAppCommand } from '../../../push-app-command.js';
|
|
8
|
-
|
|
5
|
+
import AppDev from '../dev.js';
|
|
9
6
|
export default class AppFrontendDev extends PushAppCommand {
|
|
10
7
|
static examples = [
|
|
11
8
|
'swell app frontend dev',
|
|
12
9
|
'swell app frontend dev --no-push',
|
|
13
|
-
'swell app frontend dev --port 3000',
|
|
10
|
+
'swell app frontend dev --proxy-port 3000',
|
|
14
11
|
'swell app frontend dev --storefront-select',
|
|
15
12
|
'swell app frontend dev --storefront-id <id>',
|
|
16
13
|
];
|
|
17
14
|
static flags = {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
char: 'p',
|
|
23
|
-
description: 'override the default port to run the app locally',
|
|
15
|
+
...AppDev.flags,
|
|
16
|
+
'proxy-port': Flags.integer({
|
|
17
|
+
description: 'specify the port for an existing frontend proxy',
|
|
18
|
+
default: 3001,
|
|
24
19
|
}),
|
|
25
20
|
'storefront-id': Flags.string({
|
|
26
21
|
description: 'identify a storefront to preview with and push theme files to',
|
|
@@ -29,25 +24,23 @@ export default class AppFrontendDev extends PushAppCommand {
|
|
|
29
24
|
default: false,
|
|
30
25
|
description: 'prompt to select a storefront to preview with',
|
|
31
26
|
}),
|
|
27
|
+
'app-dev': Flags.boolean({
|
|
28
|
+
description: 'indicates frontend app is running in app dev mode',
|
|
29
|
+
default: true,
|
|
30
|
+
}),
|
|
32
31
|
};
|
|
33
32
|
static orientation = {
|
|
34
33
|
env: 'test',
|
|
35
34
|
};
|
|
36
35
|
static summary = `Run a frontend app in dev mode from your local machine.`;
|
|
37
|
-
logAppLocalUrl(storeId, sessionId) {
|
|
38
|
-
const frontendAppRoute = this.app.type === 'storefront' && this.app.storefront?.external
|
|
39
|
-
? `/${this.app.private_id}`
|
|
40
|
-
: '';
|
|
41
|
-
this.log(`View it at ${style.link(`${this.localFrontendUrl(storeId, sessionId)}${frontendAppRoute}`)}\n`);
|
|
42
|
-
}
|
|
43
36
|
async run() {
|
|
44
37
|
const { flags } = await this.parse(AppFrontendDev);
|
|
45
|
-
const { port } = flags;
|
|
38
|
+
const { port, 'proxy-port': proxyPort, 'app-dev': isAppDev } = flags;
|
|
46
39
|
const noPush = flags['no-push'];
|
|
47
40
|
if (!(await this.ensureAppExists(undefined, false))) {
|
|
48
41
|
return;
|
|
49
42
|
}
|
|
50
|
-
if (!noPush) {
|
|
43
|
+
if (!noPush && !isAppDev) {
|
|
51
44
|
await this.pushAppConfigs();
|
|
52
45
|
}
|
|
53
46
|
if (this.app.type === 'storefront') {
|
|
@@ -55,54 +48,124 @@ export default class AppFrontendDev extends PushAppCommand {
|
|
|
55
48
|
this.logStorefrontConnected();
|
|
56
49
|
}
|
|
57
50
|
this.saveCurrentStorefront();
|
|
58
|
-
|
|
59
|
-
|
|
51
|
+
if (!isAppDev) {
|
|
52
|
+
this.log(`Starting app dev server...\n`);
|
|
53
|
+
}
|
|
54
|
+
const serverPort = await this.startProxyServer(proxyPort || port);
|
|
55
|
+
await this.execFrontendProject(serverPort, isAppDev);
|
|
60
56
|
}
|
|
61
|
-
async
|
|
57
|
+
async execFrontendProject(serverPort, isAppDev) {
|
|
58
|
+
const projectType = this.getFrontendProjectType(!isAppDev);
|
|
59
|
+
if (!projectType) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
62
|
const currentStore = localConfig.getDefaultStore();
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
63
|
+
let serverReadyDetected = false;
|
|
64
|
+
await this.execFrontend(projectType.devCommand.replace('${PORT}', String(serverPort)), (string) => {
|
|
65
|
+
// Framework-specific output handling and ready detection
|
|
66
|
+
if (projectType.slug === 'nextjs') {
|
|
67
|
+
// 1. NOISE FILTER: Hide unwanted output
|
|
68
|
+
if (string.includes('Using vars defined in .dev.vars')) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
// 2. URL FILTER: Hide localhost/network URLs (confusing - users should use Swell tunnel)
|
|
72
|
+
if (/- Local:\s+http:\/\//i.test(string) ||
|
|
73
|
+
/- Network:\s+http:\/\//i.test(string) ||
|
|
74
|
+
/Ready on http:\/\/localhost/i.test(string)) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
// 3. READY DETECTION: Detect server ready, show our message
|
|
78
|
+
if (!serverReadyDetected) {
|
|
79
|
+
if (/✓ Ready in \d+/i.test(string) ||
|
|
80
|
+
/✓ Starting/i.test(string)) {
|
|
81
|
+
serverReadyDetected = true;
|
|
82
|
+
this.log(`Started ${projectType.name} dev server on port ${serverPort}.\n`);
|
|
83
|
+
const sessionId = localConfig.getSessionId(currentStore);
|
|
84
|
+
setTimeout(() => this.logAppLocalUrl(currentStore, sessionId), 100);
|
|
85
|
+
if (!isAppDev) {
|
|
86
|
+
this.watchForChanges();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
79
90
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
91
|
+
else if (projectType.slug === 'astro') {
|
|
92
|
+
// 2. URL FILTER: Hide localhost/network URLs (confusing - users should use Swell tunnel)
|
|
93
|
+
if (string.includes('┃ Local') ||
|
|
94
|
+
string.includes('┃ Network')) {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
// 3. READY DETECTION: Detect server ready, show our message
|
|
98
|
+
if (!serverReadyDetected && /watching for file changes/i.test(string)) {
|
|
99
|
+
serverReadyDetected = true;
|
|
100
|
+
this.log(`Started ${projectType.name} dev server on port ${serverPort}.\n`);
|
|
101
|
+
const sessionId = localConfig.getSessionId(currentStore);
|
|
102
|
+
setTimeout(() => this.logAppLocalUrl(currentStore, sessionId), 100);
|
|
103
|
+
if (!isAppDev) {
|
|
104
|
+
this.watchForChanges();
|
|
105
|
+
}
|
|
106
|
+
}
|
|
83
107
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
108
|
+
else if (projectType.slug === 'angular') {
|
|
109
|
+
// 1. NOISE FILTER: Hide unwanted output
|
|
110
|
+
if (string.includes('press h + enter to show help')) {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
// 2. URL FILTER: Hide localhost/network URLs (confusing - users should use Swell tunnel)
|
|
114
|
+
if (/➜\s+Local:\s+http:\/\//i.test(string)) {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
// 3. READY DETECTION: Detect server ready, show our message
|
|
118
|
+
if (!serverReadyDetected) {
|
|
119
|
+
if (/Application bundle generation complete/i.test(string) ||
|
|
120
|
+
/Watch mode enabled/i.test(string)) {
|
|
121
|
+
serverReadyDetected = true;
|
|
122
|
+
this.log(`Started ${projectType.name} dev server on port ${serverPort}.\n`);
|
|
123
|
+
const sessionId = localConfig.getSessionId(currentStore);
|
|
124
|
+
setTimeout(() => this.logAppLocalUrl(currentStore, sessionId), 100);
|
|
125
|
+
if (!isAppDev) {
|
|
126
|
+
this.watchForChanges();
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
87
130
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
131
|
+
else if (projectType.slug === 'hono') {
|
|
132
|
+
// 2. URL FILTER: Hide localhost URLs (confusing - users should use Swell tunnel)
|
|
133
|
+
// Note: For Hono, the ready signal IS the URL line, so we handle both in section 3
|
|
134
|
+
// 3. READY DETECTION: Detect server ready, show our message, hide the URL
|
|
135
|
+
if (!serverReadyDetected && /Ready on http:\/\/localhost:\d+/i.test(string)) {
|
|
136
|
+
serverReadyDetected = true;
|
|
137
|
+
this.log(`Started ${projectType.name} dev server on port ${serverPort}.\n`);
|
|
138
|
+
const sessionId = localConfig.getSessionId(currentStore);
|
|
139
|
+
setTimeout(() => this.logAppLocalUrl(currentStore, sessionId), 100);
|
|
140
|
+
if (!isAppDev) {
|
|
141
|
+
this.watchForChanges();
|
|
142
|
+
}
|
|
143
|
+
return false; // Hide the "Ready on localhost" line
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
else if (projectType.slug === 'nuxt') {
|
|
147
|
+
// 2. URL FILTER: Hide localhost/network URLs (confusing - users should use Swell tunnel)
|
|
148
|
+
if (/➜ Local:\s+http:\/\//i.test(string) ||
|
|
149
|
+
/➜ Network:/i.test(string)) {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
// 3. READY DETECTION: Detect server ready, show our message
|
|
153
|
+
if (!serverReadyDetected && /Nuxt \d+\.\d+\.\d+/i.test(string)) {
|
|
154
|
+
serverReadyDetected = true;
|
|
155
|
+
this.log(`Started ${projectType.name} dev server on port ${serverPort}.\n`);
|
|
156
|
+
const sessionId = localConfig.getSessionId(currentStore);
|
|
157
|
+
setTimeout(() => this.logAppLocalUrl(currentStore, sessionId), 100);
|
|
158
|
+
if (!isAppDev) {
|
|
159
|
+
this.watchForChanges();
|
|
160
|
+
}
|
|
161
|
+
}
|
|
94
162
|
}
|
|
95
163
|
});
|
|
96
164
|
}
|
|
97
|
-
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
proxy_url: proxyUrl,
|
|
103
|
-
storefront_id: storefront?.id || null,
|
|
104
|
-
storefront_slug: storefront?.slug || null,
|
|
105
|
-
},
|
|
106
|
-
}), () => spinner.fail('Error starting local proxy'));
|
|
165
|
+
logAppLocalUrl(storeId, sessionId) {
|
|
166
|
+
const frontendAppRoute = this.app.type === 'storefront' && this.app.storefront?.external
|
|
167
|
+
? `/${this.app.private_id}`
|
|
168
|
+
: '';
|
|
169
|
+
this.log(`View it at ${style.link(`${this.localFrontendUrl(storeId, sessionId)}${frontendAppRoute}`)}\n`);
|
|
107
170
|
}
|
|
108
171
|
}
|
|
@@ -3,6 +3,7 @@ import { Flags } from '@oclif/core';
|
|
|
3
3
|
import ora from 'ora';
|
|
4
4
|
import * as semver from 'semver';
|
|
5
5
|
import config from '../../lib/config.js';
|
|
6
|
+
import { readRcFile } from '../../lib/app-config.js';
|
|
6
7
|
import { selectEnvironmentId, selectLoggedInStoreId, } from '../../lib/stores.js';
|
|
7
8
|
import style from '../../lib/style.js';
|
|
8
9
|
import { RemoteAppCommand } from '../../remote-app-command.js';
|
|
@@ -123,10 +124,20 @@ Without a store specified, install the app in the Live environment of the curren
|
|
|
123
124
|
}
|
|
124
125
|
}
|
|
125
126
|
async confirmRemoveDevelopmentApp(storeToInstall) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
127
|
+
let sourceAppId;
|
|
128
|
+
try {
|
|
129
|
+
const sourceApp = await readRcFile(this.swellRcPath());
|
|
130
|
+
sourceAppId = sourceApp?.id;
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
// noop
|
|
134
|
+
}
|
|
135
|
+
const existingSourcedApps = sourceAppId
|
|
136
|
+
? await this.api.get({ adminPath: `/client/apps` }, {
|
|
137
|
+
query: { source_id: sourceAppId },
|
|
138
|
+
})
|
|
139
|
+
: null;
|
|
140
|
+
if (existingSourcedApps?.results[0]?.id) {
|
|
130
141
|
const continueInstall = await confirm({
|
|
131
142
|
default: false,
|
|
132
143
|
message: `${style.storeId(storeToInstall)} has a development version of ${style.appConfigValue(this.app.name)}. Do you want to replace it with an installed instance?`,
|
|
@@ -7,6 +7,10 @@ export declare abstract class CreateAppCommand extends SwellCommand {
|
|
|
7
7
|
};
|
|
8
8
|
private devApi;
|
|
9
9
|
constructor(argv: string[], config: any);
|
|
10
|
+
addAllowedHostsToAstro(configPath: string): Promise<void>;
|
|
11
|
+
addAllowedHostsToAngular(configPath: string): Promise<void>;
|
|
12
|
+
addAllowedHostsToNuxt(configPath: string): Promise<void>;
|
|
13
|
+
private addFrontendAllowedHosts;
|
|
10
14
|
createAppConfigFolders(swellConfig: any): Promise<void>;
|
|
11
15
|
createFrontendApp(swellConfig: any, flags: any, directCreate?: boolean, kind?: string): Promise<boolean>;
|
|
12
16
|
createStorefrontApp(swellConfig: any, flags: any, directCreate?: boolean): Promise<boolean>;
|
|
@@ -10,6 +10,7 @@ import Api from './lib/api.js';
|
|
|
10
10
|
import { FrontendProjectTypes, getAllConfigPaths, writeFile, writeJsonFile, } from './lib/apps/index.js';
|
|
11
11
|
import style from './lib/style.js';
|
|
12
12
|
import { SwellCommand } from './swell-command.js';
|
|
13
|
+
import fs from 'node:fs/promises';
|
|
13
14
|
const execAsync = promisify(exec);
|
|
14
15
|
export class CreateAppCommand extends SwellCommand {
|
|
15
16
|
static baseFlags = {
|
|
@@ -30,6 +31,134 @@ export class CreateAppCommand extends SwellCommand {
|
|
|
30
31
|
super(argv, config);
|
|
31
32
|
this.devApi = new Api(undefined, 'test');
|
|
32
33
|
}
|
|
34
|
+
async addAllowedHostsToAstro(configPath) {
|
|
35
|
+
const astroConfigPath = path.join(configPath, 'frontend', 'astro.config.mjs');
|
|
36
|
+
let content;
|
|
37
|
+
try {
|
|
38
|
+
content = await fs.readFile(astroConfigPath, 'utf-8');
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (content.includes('allowedHosts')) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const lines = content.split('\n');
|
|
47
|
+
let insertIndex = -1;
|
|
48
|
+
for (let i = 0; i < lines.length; i++) {
|
|
49
|
+
if (lines[i].includes('defineConfig({')) {
|
|
50
|
+
insertIndex = i + 1;
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (insertIndex === -1) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
// Detect indentation from next line or use default
|
|
58
|
+
const nextLine = lines[insertIndex];
|
|
59
|
+
const indentMatch = nextLine?.match(/^(\s+)/);
|
|
60
|
+
const baseIndent = indentMatch ? indentMatch[1] : ' ';
|
|
61
|
+
const viteConfig = [
|
|
62
|
+
`${baseIndent}vite: {`,
|
|
63
|
+
`${baseIndent} server: {`,
|
|
64
|
+
`${baseIndent} allowedHosts: ['.ngrok.app', '.loca.lt'],`,
|
|
65
|
+
`${baseIndent} },`,
|
|
66
|
+
`${baseIndent}},`,
|
|
67
|
+
];
|
|
68
|
+
lines.splice(insertIndex, 0, ...viteConfig);
|
|
69
|
+
await fs.writeFile(astroConfigPath, lines.join('\n'), 'utf-8');
|
|
70
|
+
}
|
|
71
|
+
async addAllowedHostsToAngular(configPath) {
|
|
72
|
+
const angularJsonPath = path.join(configPath, 'frontend', 'angular.json');
|
|
73
|
+
let content;
|
|
74
|
+
try {
|
|
75
|
+
content = await fs.readFile(angularJsonPath, 'utf-8');
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
let config;
|
|
81
|
+
try {
|
|
82
|
+
config = JSON.parse(content);
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const projects = config.projects;
|
|
88
|
+
if (!projects || typeof projects !== 'object') {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
const projectNames = Object.keys(projects);
|
|
92
|
+
if (projectNames.length === 0) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
// Use first project (typically only one exists in new apps)
|
|
96
|
+
const projectName = projectNames[0];
|
|
97
|
+
const project = projects[projectName];
|
|
98
|
+
if (!project?.architect?.serve) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (!project.architect.serve.options) {
|
|
102
|
+
project.architect.serve.options = {};
|
|
103
|
+
}
|
|
104
|
+
if (project.architect.serve.options.allowedHosts) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
project.architect.serve.options.allowedHosts = ['.ngrok.app', '.loca.lt'];
|
|
108
|
+
await fs.writeFile(angularJsonPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
109
|
+
}
|
|
110
|
+
async addAllowedHostsToNuxt(configPath) {
|
|
111
|
+
const nuxtConfigPath = path.join(configPath, 'frontend', 'nuxt.config.ts');
|
|
112
|
+
let content;
|
|
113
|
+
try {
|
|
114
|
+
content = await fs.readFile(nuxtConfigPath, 'utf-8');
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
if (content.includes('allowedHosts')) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const lines = content.split('\n');
|
|
123
|
+
let insertIndex = -1;
|
|
124
|
+
for (let i = 0; i < lines.length; i++) {
|
|
125
|
+
if (lines[i].includes('defineNuxtConfig({')) {
|
|
126
|
+
insertIndex = i + 1;
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (insertIndex === -1) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
const nextLine = lines[insertIndex];
|
|
134
|
+
const indentMatch = nextLine?.match(/^(\s+)/);
|
|
135
|
+
const baseIndent = indentMatch ? indentMatch[1] : ' ';
|
|
136
|
+
const viteConfig = [
|
|
137
|
+
`${baseIndent}vite: {`,
|
|
138
|
+
`${baseIndent} server: {`,
|
|
139
|
+
`${baseIndent} allowedHosts: ['.ngrok.app', '.loca.lt'],`,
|
|
140
|
+
`${baseIndent} },`,
|
|
141
|
+
`${baseIndent}},`,
|
|
142
|
+
];
|
|
143
|
+
lines.splice(insertIndex, 0, ...viteConfig);
|
|
144
|
+
await fs.writeFile(nuxtConfigPath, lines.join('\n'), 'utf-8');
|
|
145
|
+
}
|
|
146
|
+
async addFrontendAllowedHosts(projectType, configPath) {
|
|
147
|
+
// Configure allowedHosts for frameworks using Vite dev server
|
|
148
|
+
// This allows tunneling services (ngrok, localtunnel) to proxy requests to the local dev environment
|
|
149
|
+
switch (projectType.slug) {
|
|
150
|
+
case 'astro':
|
|
151
|
+
await this.addAllowedHostsToAstro(configPath);
|
|
152
|
+
break;
|
|
153
|
+
case 'angular':
|
|
154
|
+
await this.addAllowedHostsToAngular(configPath);
|
|
155
|
+
break;
|
|
156
|
+
case 'nuxt':
|
|
157
|
+
await this.addAllowedHostsToNuxt(configPath);
|
|
158
|
+
break;
|
|
159
|
+
// Other frameworks don't require allowedHosts configuration
|
|
160
|
+
}
|
|
161
|
+
}
|
|
33
162
|
async createAppConfigFolders(swellConfig) {
|
|
34
163
|
for (const type of getAllConfigPaths(swellConfig.get('type'))) {
|
|
35
164
|
// Create a config folder
|
|
@@ -67,6 +196,9 @@ export class CreateAppCommand extends SwellCommand {
|
|
|
67
196
|
if (!projectType) {
|
|
68
197
|
this.error(`Could not find project type: ${frameworkType}`);
|
|
69
198
|
}
|
|
199
|
+
if (!projectType.installCommand) {
|
|
200
|
+
this.error(`Project type ${projectType.name} cannot be installed (legacy type)`);
|
|
201
|
+
}
|
|
70
202
|
this.log();
|
|
71
203
|
spinner.start(`Creating ${projectType?.name} frontend app (this may take a while)...`);
|
|
72
204
|
try {
|
|
@@ -88,6 +220,31 @@ export class CreateAppCommand extends SwellCommand {
|
|
|
88
220
|
this.log();
|
|
89
221
|
return false;
|
|
90
222
|
}
|
|
223
|
+
// Ensure frontend package.json has correct name for workspace
|
|
224
|
+
const frontendPkgPath = path.join(configPath, 'frontend', 'package.json');
|
|
225
|
+
try {
|
|
226
|
+
const pkgContent = await fs.readFile(frontendPkgPath, 'utf-8');
|
|
227
|
+
const pkg = JSON.parse(pkgContent);
|
|
228
|
+
if (pkg.name !== 'frontend') {
|
|
229
|
+
pkg.name = 'frontend';
|
|
230
|
+
await fs.writeFile(frontendPkgPath, JSON.stringify(pkg, null, 2), 'utf-8');
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
catch {
|
|
234
|
+
// Ignore if package.json doesn't exist or can't be read
|
|
235
|
+
}
|
|
236
|
+
await this.addFrontendAllowedHosts(projectType, configPath);
|
|
237
|
+
// Re-run npm install at root to properly initialize workspace structure
|
|
238
|
+
// (removes frontend/package-lock.json, hoists dependencies, creates root lock file)
|
|
239
|
+
spinner.start('Initializing workspace...');
|
|
240
|
+
try {
|
|
241
|
+
const execAsync = promisify(exec);
|
|
242
|
+
await execAsync('npm install', { cwd: configPath });
|
|
243
|
+
spinner.succeed('Workspace initialized');
|
|
244
|
+
}
|
|
245
|
+
catch (error) {
|
|
246
|
+
spinner.warn('Failed to initialize workspace');
|
|
247
|
+
}
|
|
91
248
|
spinner.succeed(`${projectType?.name} initialized app in ${configPath}/frontend/`);
|
|
92
249
|
if (directCreate) {
|
|
93
250
|
this.log();
|
|
@@ -212,6 +369,9 @@ export class CreateAppCommand extends SwellCommand {
|
|
|
212
369
|
const configPath = path.dirname(config.path);
|
|
213
370
|
const packageJson = {
|
|
214
371
|
description: config.get('description'),
|
|
372
|
+
// Include workspace even if frontend doesn't exist yet
|
|
373
|
+
// npm tolerates missing workspace directories without errors
|
|
374
|
+
workspaces: ['frontend'],
|
|
215
375
|
devDependencies: {
|
|
216
376
|
'@swell/app-types': '^1.0.5',
|
|
217
377
|
},
|
|
@@ -226,7 +386,7 @@ export class CreateAppCommand extends SwellCommand {
|
|
|
226
386
|
target: 'esnext',
|
|
227
387
|
types: ['@swell/app-types'],
|
|
228
388
|
},
|
|
229
|
-
exclude: ['node_modules'],
|
|
389
|
+
exclude: ['node_modules', 'frontend'],
|
|
230
390
|
include: ['**/*.ts'],
|
|
231
391
|
};
|
|
232
392
|
await writeJsonFile(path.join(configPath, 'package.json'), packageJson);
|
package/dist/env/local.d.ts
CHANGED
package/dist/env/local.js
CHANGED
package/dist/env/production.d.ts
CHANGED
package/dist/env/production.js
CHANGED
package/dist/env/review.d.ts
CHANGED
package/dist/env/review.js
CHANGED
|
@@ -4,4 +4,5 @@ export default {
|
|
|
4
4
|
LOGIN_HOST: 'https://${REVIEW_DOMAIN}.rav2.swell.store',
|
|
5
5
|
APP_PAGES_HOST: 'https://${STORE_ID}--${APP_ID}--${ENV}.${REVIEW_DOMAIN}.rav2.swell.store',
|
|
6
6
|
STOREFRONT_FRONTEND_HOST: 'https://${BRANCH_PREFIX}${STOREFRONT_SLUG}.${REVIEW_DOMAIN}.rav2.swell.store',
|
|
7
|
+
LOCAL_PROXY_PROVIDER: '',
|
|
7
8
|
};
|
package/dist/env/staging.d.ts
CHANGED
package/dist/env/staging.js
CHANGED
|
@@ -53,4 +53,53 @@ export declare abstract class AppConfig {
|
|
|
53
53
|
protected abstract preparePostData(postData: any): Promise<any>;
|
|
54
54
|
abstract type: ConfigType;
|
|
55
55
|
}
|
|
56
|
+
export declare class AppConfigDefault extends AppConfig {
|
|
57
|
+
hasValues: boolean;
|
|
58
|
+
type: ConfigType;
|
|
59
|
+
preparePostData(postData: any): Promise<any>;
|
|
60
|
+
}
|
|
61
|
+
export declare class AppConfigModel extends AppConfig {
|
|
62
|
+
hasValues: boolean;
|
|
63
|
+
type: ConfigType;
|
|
64
|
+
preparePostData(postData: any): Promise<any>;
|
|
65
|
+
}
|
|
66
|
+
export declare class AppConfigContent extends AppConfig {
|
|
67
|
+
hasValues: boolean;
|
|
68
|
+
type: ConfigType;
|
|
69
|
+
preparePostData(postData: any): Promise<any>;
|
|
70
|
+
}
|
|
71
|
+
export declare class AppConfigNotification extends AppConfig {
|
|
72
|
+
hasValues: boolean;
|
|
73
|
+
type: ConfigType;
|
|
74
|
+
preparePostData(postData: any): Promise<any>;
|
|
75
|
+
}
|
|
76
|
+
export declare class AppConfigSetting extends AppConfig {
|
|
77
|
+
hasValues: boolean;
|
|
78
|
+
type: ConfigType;
|
|
79
|
+
preparePostData(postData: any): Promise<any>;
|
|
80
|
+
}
|
|
81
|
+
export declare class AppConfigWebhook extends AppConfig {
|
|
82
|
+
hasValues: boolean;
|
|
83
|
+
type: ConfigType;
|
|
84
|
+
preparePostData(postData: any): Promise<any>;
|
|
85
|
+
}
|
|
86
|
+
export declare class AppConfigFunction extends AppConfig {
|
|
87
|
+
hasValues: boolean;
|
|
88
|
+
type: ConfigType;
|
|
89
|
+
isRootFunction(): boolean;
|
|
90
|
+
preparePostData(postData: any): Promise<any>;
|
|
91
|
+
}
|
|
92
|
+
export declare class AppConfigAsset extends AppConfigDefault {
|
|
93
|
+
hasValues: boolean;
|
|
94
|
+
type: ConfigType;
|
|
95
|
+
}
|
|
96
|
+
export declare class AppConfigFrontend extends AppConfigDefault {
|
|
97
|
+
hasValues: boolean;
|
|
98
|
+
type: ConfigType;
|
|
99
|
+
}
|
|
100
|
+
export declare class AppConfigTheme extends AppConfigDefault {
|
|
101
|
+
hasValues: boolean;
|
|
102
|
+
type: ConfigType;
|
|
103
|
+
preparePostData(postData: any): Promise<any>;
|
|
104
|
+
}
|
|
56
105
|
export {};
|