@automattic/vip 3.12.2 → 3.13.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/assets/dev-env.lando.template.yml.ejs +2 -0
- package/dist/bin/vip-config-envvar-delete.js +1 -1
- package/dist/bin/vip-config-envvar-set.js +1 -1
- package/dist/bin/vip-config-envvar.js +4 -4
- package/dist/bin/vip-dev-env-create.js +4 -1
- package/dist/bin/vip-dev-env-start.js +11 -3
- package/dist/bin/vip-dev-env.js +4 -0
- package/dist/bin/vip-sync.js +1 -1
- package/dist/commands/dev-env-sync-sql.js +3 -2
- package/dist/lib/cli/progress.js +1 -1
- package/dist/lib/dev-environment/dev-environment-cli.js +175 -68
- package/dist/lib/dev-environment/dev-environment-core.js +144 -18
- package/dist/lib/dev-environment/dev-environment-lando.js +2 -8
- package/dist/lib/media-import/progress.js +1 -1
- package/dist/lib/validations/sql.js +3 -3
- package/docs/CHANGELOG.md +51 -0
- package/npm-shrinkwrap.json +462 -944
- package/package.json +6 -11
|
@@ -81,4 +81,4 @@ async function deleteEnvVarCommand(arg, opt) {
|
|
|
81
81
|
envContext: true,
|
|
82
82
|
requiredArgs: 1,
|
|
83
83
|
usage: `${baseUsage} <VARIABLE_NAME>`
|
|
84
|
-
}).examples(examples).option('skip-confirmation', 'Skip
|
|
84
|
+
}).examples(examples).option('skip-confirmation', 'Skip the confirmation prompt (USE WITH CAUTION).', false).argv(process.argv, deleteEnvVarCommand);
|
|
@@ -100,4 +100,4 @@ async function setEnvVarCommand(arg, opt) {
|
|
|
100
100
|
envContext: true,
|
|
101
101
|
requiredArgs: 1,
|
|
102
102
|
usage: `${baseUsage} <VARIABLE_NAME>`
|
|
103
|
-
}).option('from-file', 'Read environment variable value from file (useful for multiline input)').option('skip-confirmation', 'Skip
|
|
103
|
+
}).option('from-file', 'Read environment variable value from a UTF-8-encoded text file (useful for multiline input). Accepts a relative or absolute path.').option('skip-confirmation', 'Skip the confirmation prompt (USE WITH CAUTION).', false).examples(examples).argv(process.argv, setEnvVarCommand);
|
|
@@ -8,8 +8,8 @@ const exampleUsage = 'vip @example-app.develop config envvar';
|
|
|
8
8
|
|
|
9
9
|
// Command examples
|
|
10
10
|
const examples = [{
|
|
11
|
-
usage: `${exampleUsage}
|
|
12
|
-
description: '
|
|
11
|
+
usage: `${exampleUsage} set MY_VARIABLE`,
|
|
12
|
+
description: 'Add or update the environment variable "MY_VARIABLE" and assign its value at the prompt.'
|
|
13
13
|
}, {
|
|
14
14
|
usage: `${exampleUsage} get MY_VARIABLE`,
|
|
15
15
|
description: 'Retrieve the value of the environment variable "MY_VARIABLE".'
|
|
@@ -20,8 +20,8 @@ const examples = [{
|
|
|
20
20
|
usage: `${exampleUsage} list`,
|
|
21
21
|
description: 'List the names of all environment variables.'
|
|
22
22
|
}, {
|
|
23
|
-
usage: `${exampleUsage}
|
|
24
|
-
description: '
|
|
23
|
+
usage: `${exampleUsage} delete MY_VARIABLE`,
|
|
24
|
+
description: 'Delete the environment variable "MY_VARIABLE" from the environment.'
|
|
25
25
|
}];
|
|
26
26
|
(0, _command.default)({
|
|
27
27
|
requiredArgs: 0,
|
|
@@ -69,10 +69,13 @@ cmd.argv(process.argv, async (arg, opt) => {
|
|
|
69
69
|
let defaultOptions = {};
|
|
70
70
|
/** @type {Record<string,import('../lib/dev-environment/types').IntegrationConfig>} */
|
|
71
71
|
let integrationsConfig = {};
|
|
72
|
+
/** @type {Record<string,string>} */
|
|
73
|
+
let envVars = {};
|
|
72
74
|
try {
|
|
73
75
|
if (opt.app) {
|
|
74
76
|
const appInfo = await (0, _devEnvironmentCore.getApplicationInformation)(opt.app, opt.env);
|
|
75
77
|
integrationsConfig = appInfo.environment?.integrations ?? {};
|
|
78
|
+
envVars = appInfo.environment?.envVars ?? {};
|
|
76
79
|
defaultOptions = (0, _devEnvironmentCli.getOptionsFromAppInfo)(appInfo);
|
|
77
80
|
}
|
|
78
81
|
} catch (error) {
|
|
@@ -90,7 +93,7 @@ cmd.argv(process.argv, async (arg, opt) => {
|
|
|
90
93
|
const instanceData = await (0, _devEnvironmentCli.promptForArguments)(preselectedOptions, defaultOptions, suppressPrompts, true);
|
|
91
94
|
instanceData.siteSlug = slug;
|
|
92
95
|
try {
|
|
93
|
-
await (0, _devEnvironmentCore.createEnvironment)(lando, instanceData, integrationsConfig);
|
|
96
|
+
await (0, _devEnvironmentCore.createEnvironment)(lando, instanceData, integrationsConfig, envVars);
|
|
94
97
|
await (0, _devEnvironmentCore.printEnvironmentInfo)(lando, slug, {
|
|
95
98
|
extended: false,
|
|
96
99
|
suppressWarnings: true
|
|
@@ -21,17 +21,24 @@ const examples = [{
|
|
|
21
21
|
usage: `${exampleUsage} --skip-rebuild --slug=example-site`,
|
|
22
22
|
description: 'Start only the services of a local environment that are not currently in a running state.'
|
|
23
23
|
}, {
|
|
24
|
-
usage: `${exampleUsage} --vscode --slug=example-site`,
|
|
24
|
+
usage: `${exampleUsage} --editor=vscode --slug=example-site`,
|
|
25
25
|
description: 'Start a local environment and generate a Workspace file for developing in Visual Studio Code.'
|
|
26
|
+
}, {
|
|
27
|
+
usage: `${exampleUsage} --editor=cursor --slug=example-site`,
|
|
28
|
+
description: 'Start a local environment and generate a Workspace file for developing in Cursor Editor.'
|
|
29
|
+
}, {
|
|
30
|
+
usage: `${exampleUsage} --editor=phpstorm --slug=example-site`,
|
|
31
|
+
description: 'Start a local environment and generate a Workspace file for developing in PhpStorm.'
|
|
26
32
|
}];
|
|
27
33
|
(0, _command.default)({
|
|
28
34
|
usage
|
|
29
|
-
}).option('slug', 'A unique name for a local environment. Default is "vip-local".', undefined, _devEnvironmentCli.processSlug).option('skip-rebuild', 'Only start services that are not in a running state.').option(['w', 'skip-wp-versions-check'], 'Skip the prompt to update WordPress; occurs if the last major release version is not configured.').option('vscode', 'Generate a Visual Studio Code Workspace file.').examples(examples).argv(process.argv, async (arg, opt) => {
|
|
35
|
+
}).option('slug', 'A unique name for a local environment. Default is "vip-local".', undefined, _devEnvironmentCli.processSlug).option('skip-rebuild', 'Only start services that are not in a running state.').option(['w', 'skip-wp-versions-check'], 'Skip the prompt to update WordPress; occurs if the last major release version is not configured.').option('vscode', 'Generate a Visual Studio Code Workspace file (deprecated, use --editor=vscode instead).').option('editor', 'Generate a workspace file for the specified editor (supports: vscode, cursor, windsurf, phpstorm).').examples(examples).argv(process.argv, async (arg, opt) => {
|
|
30
36
|
const slug = await (0, _devEnvironmentCli.getEnvironmentName)(opt);
|
|
31
37
|
const lando = await (0, _devEnvironmentLando.bootstrapLando)();
|
|
32
38
|
(0, _devEnvironmentCli.validateDependencies)(lando);
|
|
33
39
|
const startProcessing = new Date();
|
|
34
40
|
const trackingInfo = (0, _devEnvironmentCli.getEnvTrackingInfo)(slug);
|
|
41
|
+
trackingInfo.editor = opt.editor || (opt.vscode ? 'vscode' : undefined);
|
|
35
42
|
trackingInfo.vscode = Boolean(opt.vscode);
|
|
36
43
|
trackingInfo.docker = lando.config.versions.engine;
|
|
37
44
|
trackingInfo.docker_compose = lando.config.versions.compose;
|
|
@@ -55,6 +62,7 @@ const examples = [{
|
|
|
55
62
|
process.exitCode = 1;
|
|
56
63
|
}
|
|
57
64
|
(0, _devEnvironmentCli.postStart)(slug, {
|
|
58
|
-
|
|
65
|
+
editor: opt.editor,
|
|
66
|
+
vscode: Boolean(opt.vscode)
|
|
59
67
|
});
|
|
60
68
|
});
|
package/dist/bin/vip-dev-env.js
CHANGED
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
|
|
4
4
|
var _command = _interopRequireDefault(require("../lib/cli/command"));
|
|
5
5
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
6
|
+
if (process.getuid?.() === 0) {
|
|
7
|
+
console.error('This script should not be run as root. Exiting.');
|
|
8
|
+
process.exit(1);
|
|
9
|
+
}
|
|
6
10
|
(0, _command.default)({
|
|
7
11
|
requiredArgs: 0
|
|
8
12
|
}).command('create', 'Create a new local environment.').command('update', 'Update the settings of a local environment.').command('start', 'Start a local environment.').command('stop', 'Stop a local environment.').command('destroy', 'Remove a local environment.').command('info', 'Retrieve information about a local environment.').command('list', 'Retrieve information about all local environments.').command('exec', 'Run a WP-CLI command against a local environment.').command('import', 'Import media or database files to a local environment.').command('shell', 'Create a shell and run commands against a local environment.').command('logs', 'Retrieve logs for a local environment.').command('sync', 'Sync the database of a VIP Platform environment to a local environment.').command('purge', 'Remove all local environments.').argv(process.argv);
|
package/dist/bin/vip-sync.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
3
|
|
|
4
|
+
var _singleLineLog = require("@wwa/single-line-log");
|
|
4
5
|
var _chalk = _interopRequireDefault(require("chalk"));
|
|
5
6
|
var _graphqlTag = _interopRequireDefault(require("graphql-tag"));
|
|
6
|
-
var _singleLineLog = require("single-line-log");
|
|
7
7
|
var _api = _interopRequireDefault(require("../lib/api"));
|
|
8
8
|
var _app2 = _interopRequireDefault(require("../lib/api/app"));
|
|
9
9
|
var _command = _interopRequireDefault(require("../lib/cli/command"));
|
|
@@ -67,8 +67,9 @@ async function extractSiteUrls(sqlFile) {
|
|
|
67
67
|
return new Promise((resolve, reject) => {
|
|
68
68
|
const urls = new Set();
|
|
69
69
|
readInterface.on('line', line => {
|
|
70
|
-
|
|
70
|
+
let url = findSiteHomeUrl(line);
|
|
71
71
|
if (url) {
|
|
72
|
+
url = url.replace(/\/$/, '');
|
|
72
73
|
urls.add(url);
|
|
73
74
|
}
|
|
74
75
|
});
|
|
@@ -178,7 +179,7 @@ class DevEnvSyncSQLCommand {
|
|
|
178
179
|
if (!site?.blogId || site.blogId === 1) continue;
|
|
179
180
|
const url = site?.homeUrl;
|
|
180
181
|
if (!url) continue;
|
|
181
|
-
const strippedUrl = stripProtocol(url);
|
|
182
|
+
const strippedUrl = stripProtocol(url).replace(/\/$/, '');
|
|
182
183
|
if (!this.searchReplaceMap[strippedUrl]) continue;
|
|
183
184
|
const domain = new URL(url).hostname;
|
|
184
185
|
const newDomain = primaryDomain === domain ? this.landoDomain : `${this.slugifyDomain(domain)}.${this.landoDomain}`;
|
package/dist/lib/cli/progress.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
exports.__esModule = true;
|
|
4
4
|
exports.StepStatus = exports.ProgressTracker = void 0;
|
|
5
|
+
var _singleLineLog = require("@wwa/single-line-log");
|
|
5
6
|
var _nodeOs = require("node:os");
|
|
6
|
-
var _singleLineLog = require("single-line-log");
|
|
7
7
|
var _format = require("../../lib/cli/format");
|
|
8
8
|
const PRINT_INTERVAL = process.env.DEBUG ? 5000 : 200; // How often the report is printed. Mainly affects the "spinner" animation.
|
|
9
9
|
let StepStatus = exports.StepStatus = /*#__PURE__*/function (StepStatus) {
|
|
@@ -23,6 +23,7 @@ exports.promptForComponent = promptForComponent;
|
|
|
23
23
|
exports.promptForMultisite = promptForMultisite;
|
|
24
24
|
exports.promptForPhpVersion = promptForPhpVersion;
|
|
25
25
|
exports.promptForText = promptForText;
|
|
26
|
+
exports.promptForURL = promptForURL;
|
|
26
27
|
exports.promptForWordPress = promptForWordPress;
|
|
27
28
|
exports.resolvePath = resolvePath;
|
|
28
29
|
exports.resolvePhpVersion = resolvePhpVersion;
|
|
@@ -161,7 +162,7 @@ function processComponentOptionInput(passedParam, allowLocal) {
|
|
|
161
162
|
}
|
|
162
163
|
return {
|
|
163
164
|
mode: 'image',
|
|
164
|
-
tag: param === 'demo' ? undefined : param
|
|
165
|
+
tag: param === 'demo' || param === 'image' ? undefined : param
|
|
165
166
|
};
|
|
166
167
|
}
|
|
167
168
|
function getOptionsFromAppInfo(appInfo) {
|
|
@@ -190,6 +191,13 @@ function getOptionsFromAppInfo(appInfo) {
|
|
|
190
191
|
// eslint-disable-next-line complexity
|
|
191
192
|
async function promptForArguments(preselectedOptions, defaultOptions, suppressPrompts, create) {
|
|
192
193
|
debug('Provided preselected', preselectedOptions, 'and default', defaultOptions);
|
|
194
|
+
let isVIPUser;
|
|
195
|
+
try {
|
|
196
|
+
const currentUser = await (0, _user.getCurrentUserInfo)(true);
|
|
197
|
+
isVIPUser = currentUser?.isVIP ?? false;
|
|
198
|
+
} catch (err) {
|
|
199
|
+
isVIPUser = false;
|
|
200
|
+
}
|
|
193
201
|
if (suppressPrompts) {
|
|
194
202
|
preselectedOptions = {
|
|
195
203
|
...defaultOptions,
|
|
@@ -208,7 +216,7 @@ async function promptForArguments(preselectedOptions, defaultOptions, suppressPr
|
|
|
208
216
|
elasticsearch: false,
|
|
209
217
|
php: preselectedOptions.php ? resolvePhpVersion(preselectedOptions.php) : await promptForPhpVersion(resolvePhpVersion(defaultOptions.php ?? _devEnvironment.DEV_ENVIRONMENT_DEFAULTS.phpVersion)),
|
|
210
218
|
mariadb: preselectedOptions.mariadb ?? defaultOptions.mariadb,
|
|
211
|
-
mediaRedirectDomain: preselectedOptions.mediaRedirectDomain ?? '',
|
|
219
|
+
mediaRedirectDomain: preselectedOptions.mediaRedirectDomain ?? defaultOptions.mediaRedirectDomain ?? '',
|
|
212
220
|
wordpress: {
|
|
213
221
|
mode: 'image',
|
|
214
222
|
tag: ''
|
|
@@ -241,16 +249,19 @@ async function promptForArguments(preselectedOptions, defaultOptions, suppressPr
|
|
|
241
249
|
if (setMediaRedirectDomain) {
|
|
242
250
|
instanceData.mediaRedirectDomain = defaultOptions.mediaRedirectDomain;
|
|
243
251
|
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
252
|
+
} else if (!create && defaultOptions.mediaRedirectDomain) {
|
|
253
|
+
const mediaRedirectPromptText = 'URL to redirect for missing media files ("n" to disable)?';
|
|
254
|
+
const mediaRedirectDomain = await promptForURL(mediaRedirectPromptText, defaultOptions.mediaRedirectDomain);
|
|
255
|
+
instanceData.mediaRedirectDomain = mediaRedirectDomain;
|
|
256
|
+
}
|
|
257
|
+
instanceData.wordpress = await processWordPress(preselectedOptions.wordpress ?? '', defaultOptions.wordpress ?? '');
|
|
258
|
+
instanceData.appCode = await processComponent('appCode', preselectedOptions.appCode ?? '', defaultOptions.appCode ?? '', suppressPrompts, validateAppCodeLocalPath);
|
|
259
|
+
let preselectedMU = preselectedOptions.muPlugins;
|
|
260
|
+
const defaultMU = defaultOptions.muPlugins;
|
|
261
|
+
if (!preselectedMU && !isVIPUser && (!defaultMU || ['demo', 'image'].includes(defaultMU))) {
|
|
262
|
+
preselectedMU = 'image';
|
|
263
|
+
}
|
|
264
|
+
instanceData.muPlugins = await processComponent('muPlugins', preselectedMU ?? '', defaultMU ?? '', suppressPrompts, validateMuPluginsLocalPath);
|
|
254
265
|
debug(`Processing elasticsearch with preselected "%s"`, preselectedOptions.elasticsearch);
|
|
255
266
|
if ('elasticsearch' in preselectedOptions) {
|
|
256
267
|
instanceData.elasticsearch = Boolean(preselectedOptions.elasticsearch);
|
|
@@ -295,48 +306,41 @@ async function processWordPress(preselectedValue, defaultValue) {
|
|
|
295
306
|
debug(result);
|
|
296
307
|
return result;
|
|
297
308
|
}
|
|
298
|
-
async function processComponent(
|
|
299
|
-
debug(`
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
try {
|
|
304
|
-
const currentUser = await (0, _user.getCurrentUserInfo)(true);
|
|
305
|
-
allowLocal = currentUser?.isVIP ?? false;
|
|
306
|
-
} catch (err) {
|
|
307
|
-
allowLocal = false;
|
|
308
|
-
}
|
|
309
|
+
async function processComponent(componentType, preselectedValue, defaultValue, suppressPrompts, localPathValidator) {
|
|
310
|
+
debug(`Processing the '${componentType}' component, with preselected = %s, default = %s`, preselectedValue, defaultValue);
|
|
311
|
+
const defaultObject = defaultValue ? processComponentOptionInput(defaultValue, true) : null;
|
|
312
|
+
if (preselectedValue && !suppressPrompts) {
|
|
313
|
+
console.log('%s Path to your local %s: %s', _chalk.default.green('✓'), componentDisplayNames[componentType], preselectedValue);
|
|
309
314
|
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
console.log(`${_chalk.default.green('✓')} Path to your local ${componentDisplayNames[component]}: ${preselectedValue}`);
|
|
315
|
-
}
|
|
316
|
-
} else {
|
|
317
|
-
result = await promptForComponent(component, allowLocal, defaultObject);
|
|
315
|
+
let result = preselectedValue ? processComponentOptionInput(preselectedValue, true) : await promptForComponent(componentType, true, defaultObject);
|
|
316
|
+
debug(result);
|
|
317
|
+
if (result.mode === 'local') {
|
|
318
|
+
result = await validateLocalPath(result, localPathValidator, componentType, defaultObject);
|
|
318
319
|
}
|
|
319
320
|
debug(result);
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
321
|
+
return result;
|
|
322
|
+
}
|
|
323
|
+
async function validateLocalPath(config, validator, componentName, defaultObject) {
|
|
324
|
+
while (config.mode === 'local') {
|
|
325
|
+
const resolvedPath = resolvePath(config.dir ?? '');
|
|
326
|
+
config.dir = resolvedPath;
|
|
323
327
|
const {
|
|
324
328
|
result: isPathValid,
|
|
325
329
|
message
|
|
326
|
-
} =
|
|
330
|
+
} = validator(resolvedPath);
|
|
327
331
|
if (isPathValid) {
|
|
328
332
|
break;
|
|
329
333
|
} else if (isStdinTTY) {
|
|
330
334
|
console.log(_chalk.default.yellow('Warning:'), message);
|
|
331
335
|
// eslint-disable-next-line no-await-in-loop
|
|
332
|
-
|
|
336
|
+
config = await promptForComponent(componentName, true, defaultObject);
|
|
333
337
|
} else {
|
|
334
338
|
throw new Error(message);
|
|
335
339
|
}
|
|
336
340
|
}
|
|
337
|
-
return
|
|
341
|
+
return config;
|
|
338
342
|
}
|
|
339
|
-
function
|
|
343
|
+
function validateMuPluginsLocalPath(providedPath) {
|
|
340
344
|
if (!isNonEmptyDirectory(providedPath)) {
|
|
341
345
|
const message = `Provided path "${providedPath}" does not point to a valid or existing directory.`;
|
|
342
346
|
return {
|
|
@@ -344,24 +348,35 @@ function validateLocalPath(component, providedPath) {
|
|
|
344
348
|
message
|
|
345
349
|
};
|
|
346
350
|
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
351
|
+
return {
|
|
352
|
+
result: true,
|
|
353
|
+
message: ''
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
function validateAppCodeLocalPath(providedPath) {
|
|
357
|
+
if (!isNonEmptyDirectory(providedPath)) {
|
|
358
|
+
const message = `Provided path "${providedPath}" does not point to a valid or existing directory.`;
|
|
359
|
+
return {
|
|
360
|
+
result: false,
|
|
361
|
+
message
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
const files = ['languages', 'plugins', 'themes', 'private', 'images', 'client-mu-plugins', 'vip-config'];
|
|
365
|
+
const missingFiles = [];
|
|
366
|
+
for (const file of files) {
|
|
367
|
+
const filePath = _path.default.resolve(providedPath, file);
|
|
368
|
+
if (!(0, _nodeFs.existsSync)(filePath)) {
|
|
369
|
+
missingFiles.push(file);
|
|
363
370
|
}
|
|
364
371
|
}
|
|
372
|
+
if (missingFiles.length > 0) {
|
|
373
|
+
// eslint-disable-next-line max-len
|
|
374
|
+
const message = `Provided path "${providedPath}" is missing following files/folders: ${missingFiles.join(', ')}. Learn more: https://docs.wpvip.com/wordpress-skeleton/`;
|
|
375
|
+
return {
|
|
376
|
+
result: false,
|
|
377
|
+
message
|
|
378
|
+
};
|
|
379
|
+
}
|
|
365
380
|
return {
|
|
366
381
|
result: true,
|
|
367
382
|
message: ''
|
|
@@ -399,6 +414,36 @@ async function promptForText(message, initial) {
|
|
|
399
414
|
}
|
|
400
415
|
return (result.input || '').trim();
|
|
401
416
|
}
|
|
417
|
+
async function promptForURL(message, initial) {
|
|
418
|
+
let result = {
|
|
419
|
+
input: initial
|
|
420
|
+
};
|
|
421
|
+
if (isStdinTTY) {
|
|
422
|
+
const URLValidator = value => {
|
|
423
|
+
if (!value.trim() || value === 'n') {
|
|
424
|
+
return true;
|
|
425
|
+
}
|
|
426
|
+
try {
|
|
427
|
+
const url = new URL(value);
|
|
428
|
+
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
|
|
429
|
+
return 'value must be a http:// or https:// URL';
|
|
430
|
+
}
|
|
431
|
+
return true;
|
|
432
|
+
} catch {
|
|
433
|
+
return 'value needs to be a valid URL or an empty string';
|
|
434
|
+
}
|
|
435
|
+
};
|
|
436
|
+
result = await (0, _enquirer.prompt)({
|
|
437
|
+
type: 'input',
|
|
438
|
+
name: 'input',
|
|
439
|
+
message,
|
|
440
|
+
initial,
|
|
441
|
+
validate: URLValidator
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
const retval = result.input.trim();
|
|
445
|
+
return retval === 'n' ? '' : retval;
|
|
446
|
+
}
|
|
402
447
|
const multisiteOptions = ['subdomain', 'subdirectory'];
|
|
403
448
|
async function promptForMultisite(message, initial) {
|
|
404
449
|
// `undefined` is used here only because our tests need overhauling
|
|
@@ -676,30 +721,92 @@ function getEnvTrackingInfo(slug) {
|
|
|
676
721
|
};
|
|
677
722
|
}
|
|
678
723
|
}
|
|
724
|
+
// Map of supported editors and their configurations
|
|
725
|
+
const SUPPORTED_EDITORS = {
|
|
726
|
+
vscode: {
|
|
727
|
+
displayName: 'VS Code',
|
|
728
|
+
candidates: ['code', 'code-insiders', 'codium'],
|
|
729
|
+
workspace: {
|
|
730
|
+
extension: 'code-workspace',
|
|
731
|
+
generator: _devEnvironmentCore.generateVSCodeWorkspace
|
|
732
|
+
}
|
|
733
|
+
},
|
|
734
|
+
cursor: {
|
|
735
|
+
displayName: 'Cursor',
|
|
736
|
+
candidates: ['cursor'],
|
|
737
|
+
workspace: {
|
|
738
|
+
extension: 'code-workspace',
|
|
739
|
+
generator: _devEnvironmentCore.generateVSCodeWorkspace
|
|
740
|
+
}
|
|
741
|
+
},
|
|
742
|
+
phpstorm: {
|
|
743
|
+
displayName: 'PHPStorm',
|
|
744
|
+
candidates: ['phpstorm', 'phpstorm64.exe'],
|
|
745
|
+
errors: {
|
|
746
|
+
notFound: `PHPStorm launcher was not detected in the expected path.\nPlease follow the setup instructions: https://www.jetbrains.com/help/phpstorm/working-with-the-ide-features-from-command-line.html#standalone`
|
|
747
|
+
},
|
|
748
|
+
workspace: {
|
|
749
|
+
extension: 'iml',
|
|
750
|
+
generator: _devEnvironmentCore.generatePHPStormWorkspace
|
|
751
|
+
}
|
|
752
|
+
},
|
|
753
|
+
windsurf: {
|
|
754
|
+
displayName: 'Windsurf',
|
|
755
|
+
candidates: ['windsurf'],
|
|
756
|
+
workspace: {
|
|
757
|
+
extension: 'code-workspace',
|
|
758
|
+
generator: _devEnvironmentCore.generateVSCodeWorkspace
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
};
|
|
762
|
+
|
|
763
|
+
// Helper to get workspace path for any editor
|
|
764
|
+
function getWorkspacePath(slug, editor) {
|
|
765
|
+
const {
|
|
766
|
+
workspace
|
|
767
|
+
} = SUPPORTED_EDITORS[editor];
|
|
768
|
+
if (editor === 'phpstorm') {
|
|
769
|
+
return _path.default.join((0, _devEnvironmentCore.getEnvironmentPath)(slug), '.idea', `${slug}.${workspace.extension}`);
|
|
770
|
+
}
|
|
771
|
+
return _path.default.join((0, _devEnvironmentCore.getEnvironmentPath)(slug), `${slug}.${workspace.extension}`);
|
|
772
|
+
}
|
|
679
773
|
function postStart(slug, options) {
|
|
680
|
-
|
|
681
|
-
|
|
774
|
+
let editorType;
|
|
775
|
+
if (options.editor) {
|
|
776
|
+
const editor = options.editor.toLowerCase();
|
|
777
|
+
if (!Object.keys(SUPPORTED_EDITORS).includes(editor)) {
|
|
778
|
+
throw new Error(`Invalid editor specified. Supported editors are: ${Object.keys(SUPPORTED_EDITORS).join(', ')}`);
|
|
779
|
+
}
|
|
780
|
+
editorType = editor;
|
|
781
|
+
} else if (options.vscode) {
|
|
782
|
+
editorType = 'vscode';
|
|
783
|
+
}
|
|
784
|
+
if (editorType) {
|
|
785
|
+
launchEditor(slug, editorType);
|
|
682
786
|
}
|
|
683
787
|
}
|
|
684
|
-
const
|
|
685
|
-
const
|
|
788
|
+
const launchEditor = (slug, type) => {
|
|
789
|
+
const editorConfig = SUPPORTED_EDITORS[type];
|
|
790
|
+
const workspacePath = getWorkspacePath(slug, type);
|
|
686
791
|
if ((0, _nodeFs.existsSync)(workspacePath)) {
|
|
687
|
-
console.log('
|
|
792
|
+
console.log('Project already exists, skipping creation.');
|
|
688
793
|
} else {
|
|
689
|
-
|
|
690
|
-
console.log(
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
794
|
+
editorConfig.workspace.generator(slug);
|
|
795
|
+
console.log(`${editorConfig.displayName} project generated`);
|
|
796
|
+
}
|
|
797
|
+
console.log(`Project file location:\n${workspacePath}\n`);
|
|
798
|
+
const executable = findExecutable(editorConfig.candidates);
|
|
799
|
+
if (executable) {
|
|
800
|
+
// For PHPStorm, pass the environment home folder instead of the workspace path
|
|
801
|
+
const launchPath = type === 'phpstorm' ? (0, _devEnvironmentCore.getEnvironmentPath)(slug) : workspacePath;
|
|
802
|
+
(0, _child_process.spawn)(executable, [launchPath], {
|
|
695
803
|
shell: process.platform === 'win32'
|
|
696
804
|
});
|
|
697
805
|
} else {
|
|
698
|
-
console.log(
|
|
806
|
+
console.log(editorConfig.errors?.notFound || `${editorConfig.displayName} was not detected in the expected path.`);
|
|
699
807
|
}
|
|
700
808
|
};
|
|
701
|
-
const
|
|
702
|
-
const candidates = ['code', 'code-insiders', 'codium'];
|
|
809
|
+
const findExecutable = candidates => {
|
|
703
810
|
for (const candidate of candidates) {
|
|
704
811
|
const result = (0, _shelljs.which)(candidate);
|
|
705
812
|
if (result) {
|