@swell/cli 2.0.20 → 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/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/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
|
}
|
|
@@ -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 {};
|
|
@@ -174,14 +174,14 @@ export class AppConfig {
|
|
|
174
174
|
: this.fileData?.toString('utf8');
|
|
175
175
|
}
|
|
176
176
|
}
|
|
177
|
-
class AppConfigDefault extends AppConfig {
|
|
177
|
+
export class AppConfigDefault extends AppConfig {
|
|
178
178
|
hasValues = false;
|
|
179
179
|
type = ConfigType.FILE;
|
|
180
180
|
preparePostData(postData) {
|
|
181
181
|
return postData;
|
|
182
182
|
}
|
|
183
183
|
}
|
|
184
|
-
class AppConfigModel extends AppConfig {
|
|
184
|
+
export class AppConfigModel extends AppConfig {
|
|
185
185
|
hasValues = true;
|
|
186
186
|
type = ConfigType.MODEL;
|
|
187
187
|
preparePostData(postData) {
|
|
@@ -190,7 +190,7 @@ class AppConfigModel extends AppConfig {
|
|
|
190
190
|
return postData;
|
|
191
191
|
}
|
|
192
192
|
}
|
|
193
|
-
class AppConfigContent extends AppConfig {
|
|
193
|
+
export class AppConfigContent extends AppConfig {
|
|
194
194
|
hasValues = true;
|
|
195
195
|
type = ConfigType.CONTENT;
|
|
196
196
|
preparePostData(postData) {
|
|
@@ -198,7 +198,7 @@ class AppConfigContent extends AppConfig {
|
|
|
198
198
|
return postData;
|
|
199
199
|
}
|
|
200
200
|
}
|
|
201
|
-
class AppConfigNotification extends AppConfig {
|
|
201
|
+
export class AppConfigNotification extends AppConfig {
|
|
202
202
|
hasValues = true;
|
|
203
203
|
type = ConfigType.NOTIFICATION;
|
|
204
204
|
preparePostData(postData) {
|
|
@@ -225,35 +225,42 @@ class AppConfigNotification extends AppConfig {
|
|
|
225
225
|
return postData;
|
|
226
226
|
}
|
|
227
227
|
}
|
|
228
|
-
class AppConfigSetting extends AppConfig {
|
|
228
|
+
export class AppConfigSetting extends AppConfig {
|
|
229
229
|
hasValues = true;
|
|
230
230
|
type = ConfigType.SETTING;
|
|
231
231
|
preparePostData(postData) {
|
|
232
232
|
return postData;
|
|
233
233
|
}
|
|
234
234
|
}
|
|
235
|
-
class AppConfigWebhook extends AppConfig {
|
|
235
|
+
export class AppConfigWebhook extends AppConfig {
|
|
236
236
|
hasValues = true;
|
|
237
237
|
type = ConfigType.WEBHOOK;
|
|
238
238
|
preparePostData(postData) {
|
|
239
239
|
return postData;
|
|
240
240
|
}
|
|
241
241
|
}
|
|
242
|
-
class AppConfigFunction extends AppConfig {
|
|
242
|
+
export class AppConfigFunction extends AppConfig {
|
|
243
243
|
hasValues = true;
|
|
244
244
|
type = ConfigType.FUNCTION;
|
|
245
|
+
isRootFunction() {
|
|
246
|
+
return (this.isRootConfig('functions') &&
|
|
247
|
+
(this.filePath.endsWith('.js') || this.filePath.endsWith('.ts')));
|
|
248
|
+
}
|
|
245
249
|
async preparePostData(postData) {
|
|
246
|
-
|
|
247
|
-
(this.filePath.endsWith('.js') || this.filePath.endsWith('.ts'));
|
|
248
|
-
if (isFunction) {
|
|
250
|
+
if (this.isRootFunction()) {
|
|
249
251
|
try {
|
|
252
|
+
// get file contents and if it's empty ignore
|
|
253
|
+
const fileData = this.prepareFileData();
|
|
254
|
+
if (!fileData) {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
250
257
|
const { code, config } = await bundleFunction(this.filePath);
|
|
251
258
|
if (!config) {
|
|
252
259
|
throw new IgnoringFileError('Function must export a `config` object.');
|
|
253
260
|
}
|
|
254
261
|
// Save the original file and the bundled version
|
|
255
262
|
postData.file = {
|
|
256
|
-
data:
|
|
263
|
+
data: fileData,
|
|
257
264
|
};
|
|
258
265
|
postData.build_file = {
|
|
259
266
|
content_type: 'text/javascript',
|
|
@@ -269,15 +276,15 @@ class AppConfigFunction extends AppConfig {
|
|
|
269
276
|
}
|
|
270
277
|
}
|
|
271
278
|
// Assets do not get installed but saved as plain files
|
|
272
|
-
class AppConfigAsset extends AppConfigDefault {
|
|
279
|
+
export class AppConfigAsset extends AppConfigDefault {
|
|
273
280
|
hasValues = false;
|
|
274
281
|
type = ConfigType.ASSET;
|
|
275
282
|
}
|
|
276
|
-
class AppConfigFrontend extends AppConfigDefault {
|
|
283
|
+
export class AppConfigFrontend extends AppConfigDefault {
|
|
277
284
|
hasValues = false;
|
|
278
285
|
type = ConfigType.FRONTEND;
|
|
279
286
|
}
|
|
280
|
-
class AppConfigTheme extends AppConfigDefault {
|
|
287
|
+
export class AppConfigTheme extends AppConfigDefault {
|
|
281
288
|
hasValues = false;
|
|
282
289
|
type = ConfigType.THEME;
|
|
283
290
|
preparePostData(postData) {
|