@vendure/create 3.5.2-master-202512180239 → 3.5.2

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.
@@ -38,8 +38,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.createVendureApp = createVendureApp;
40
40
  const prompts_1 = require("@clack/prompts");
41
+ const shared_constants_1 = require("@vendure/common/lib/shared-constants");
41
42
  const commander_1 = require("commander");
43
+ const crypto_1 = require("crypto");
42
44
  const fs_extra_1 = __importDefault(require("fs-extra"));
45
+ const handlebars_1 = __importDefault(require("handlebars"));
43
46
  const node_child_process_1 = require("node:child_process");
44
47
  const promises_1 = require("node:timers/promises");
45
48
  const open_1 = __importDefault(require("open"));
@@ -69,16 +72,18 @@ commander_1.program
69
72
  .option('--verbose', 'Alias for --log-level verbose', false)
70
73
  .option('--use-npm', 'Uses npm rather than as the default package manager. DEPRECATED: Npm is now the default')
71
74
  .option('--ci', 'Runs without prompts for use in CI scenarios', false)
75
+ .option('--with-storefront', 'Include Next.js storefront (only used with --ci)', false)
72
76
  .parse(process.argv);
73
77
  const options = commander_1.program.opts();
74
- void createVendureApp(projectName, options.useNpm, options.verbose ? 'verbose' : options.logLevel || 'info', options.ci).catch(err => {
78
+ void createVendureApp(projectName, options.useNpm, options.verbose ? 'verbose' : options.logLevel || 'info', options.ci, options.withStorefront).catch(err => {
75
79
  (0, logger_1.log)(err);
76
80
  process.exit(1);
77
81
  });
78
- async function createVendureApp(name, useNpm, logLevel, isCi = false) {
79
- var _a, _b;
82
+ async function createVendureApp(name, _useNpm, // Deprecated: npm is now the default package manager
83
+ logLevel, isCi = false, withStorefront = false) {
84
+ var _a, _b, _c;
80
85
  (0, logger_1.setLogLevel)(logLevel);
81
- if (!runPreChecks(name, useNpm)) {
86
+ if (!runPreChecks(name)) {
82
87
  return;
83
88
  }
84
89
  (0, prompts_1.intro)(`Let's create a ${picocolors_1.default.blue(picocolors_1.default.bold('Vendure App'))} ✨ ${picocolors_1.default.dim(`v${packageJson.version}`)}`);
@@ -98,20 +103,18 @@ async function createVendureApp(name, useNpm, logLevel, isCi = false) {
98
103
  }));
99
104
  (0, helpers_1.checkCancel)(mode);
100
105
  const portSpinner = (0, prompts_1.spinner)();
101
- let port = constants_1.SERVER_PORT;
102
- const attemptedPortRange = 20;
106
+ let port;
107
+ let storefrontPort = constants_1.STOREFRONT_PORT;
103
108
  portSpinner.start(`Establishing port...`);
104
- while (await (0, helpers_1.isServerPortInUse)(port)) {
105
- const nextPort = port + 1;
106
- portSpinner.message(picocolors_1.default.yellow(`Port ${port} is in use. Attempting to use ${nextPort}`));
107
- port = nextPort;
108
- if (port > constants_1.SERVER_PORT + attemptedPortRange) {
109
- portSpinner.stop(picocolors_1.default.red('Could not find an available port'));
110
- (0, prompts_1.outro)(`Please ensure there is a port available between ${constants_1.SERVER_PORT} and ${constants_1.SERVER_PORT + attemptedPortRange}`);
111
- process.exit(1);
112
- }
109
+ try {
110
+ port = await (0, helpers_1.findAvailablePort)(constants_1.SERVER_PORT, constants_1.PORT_SCAN_RANGE);
111
+ portSpinner.stop(`Using port ${port}`);
112
+ }
113
+ catch (e) {
114
+ portSpinner.stop(picocolors_1.default.red('Could not find an available port'));
115
+ (0, prompts_1.outro)(e.message);
116
+ process.exit(1);
113
117
  }
114
- portSpinner.stop(`Using port ${port}`);
115
118
  process.env.PORT = port.toString();
116
119
  const root = path_1.default.resolve(name);
117
120
  const appName = path_1.default.basename(root);
@@ -120,83 +123,179 @@ async function createVendureApp(name, useNpm, logLevel, isCi = false) {
120
123
  if (scaffoldExists) {
121
124
  (0, logger_1.log)(picocolors_1.default.yellow('It appears that a new Vendure project scaffold already exists. Re-using the existing files...'), { newline: 'after' });
122
125
  }
123
- const { dbType, configSource, envSource, envDtsSource, indexSource, indexWorkerSource, readmeSource, dockerfileSource, dockerComposeSource, populateProducts, } = mode === 'ci'
124
- ? await (0, gather_user_responses_1.getCiConfiguration)(root, packageManager)
126
+ const { dbType, configSource, envSource, envDtsSource, indexSource, indexWorkerSource, readmeSource, dockerfileSource, dockerComposeSource, tsconfigDashboardSource, viteConfigSource, populateProducts, includeStorefront, } = mode === 'ci'
127
+ ? await (0, gather_user_responses_1.getCiConfiguration)(root, packageManager, port, withStorefront)
125
128
  : mode === 'manual'
126
- ? await (0, gather_user_responses_1.getManualConfiguration)(root, packageManager)
127
- : await (0, gather_user_responses_1.getQuickStartConfiguration)(root, packageManager);
129
+ ? await (0, gather_user_responses_1.getManualConfiguration)(root, packageManager, port)
130
+ : await (0, gather_user_responses_1.getQuickStartConfiguration)(root, packageManager, port);
131
+ // Determine the server root directory (either root or apps/server for monorepo)
132
+ const serverRoot = includeStorefront ? path_1.default.join(root, 'apps', 'server') : root;
133
+ const storefrontRoot = path_1.default.join(root, 'apps', 'storefront');
134
+ // Find an available storefront port if including storefront
135
+ if (includeStorefront) {
136
+ const storefrontPortSpinner = (0, prompts_1.spinner)();
137
+ storefrontPortSpinner.start(`Establishing storefront port...`);
138
+ try {
139
+ // Start scanning from the higher of STOREFRONT_PORT or serverPort + 1
140
+ // to avoid conflicts with the server port
141
+ const storefrontStartPort = Math.max(constants_1.STOREFRONT_PORT, port + 1);
142
+ storefrontPort = await (0, helpers_1.findAvailablePort)(storefrontStartPort, constants_1.PORT_SCAN_RANGE);
143
+ storefrontPortSpinner.stop(`Using storefront port ${storefrontPort}`);
144
+ }
145
+ catch (e) {
146
+ storefrontPortSpinner.stop(picocolors_1.default.red('Could not find an available storefront port'));
147
+ (0, prompts_1.outro)(e.message);
148
+ process.exit(1);
149
+ }
150
+ }
128
151
  process.chdir(root);
129
152
  if (packageManager !== 'npm' && !(0, helpers_1.checkThatNpmCanReadCwd)()) {
130
153
  process.exit(1);
131
154
  }
132
- const packageJsonContents = {
133
- name: appName,
134
- version: '0.1.0',
135
- private: true,
136
- scripts: {
137
- 'dev:server': 'ts-node ./src/index.ts',
138
- 'dev:worker': 'ts-node ./src/index-worker.ts',
139
- dev: 'concurrently npm:dev:*',
140
- build: 'tsc',
141
- 'start:server': 'node ./dist/index.js',
142
- 'start:worker': 'node ./dist/index-worker.js',
143
- start: 'concurrently npm:start:*',
144
- },
145
- };
146
155
  const setupSpinner = (0, prompts_1.spinner)();
147
- setupSpinner.start(`Setting up your new Vendure project in ${picocolors_1.default.green(root)}\nThis may take a few minutes...`);
148
- const srcPathScript = (fileName) => path_1.default.join(root, 'src', `${fileName}.ts`);
149
- fs_extra_1.default.writeFileSync(path_1.default.join(root, 'package.json'), JSON.stringify(packageJsonContents, null, 2) + os_1.default.EOL);
150
- const { dependencies, devDependencies } = (0, helpers_1.getDependencies)(dbType, `@${packageJson.version}`);
151
- setupSpinner.stop(`Created ${picocolors_1.default.green('package.json')}`);
152
- const installSpinner = (0, prompts_1.spinner)();
153
- installSpinner.start(`Installing ${dependencies[0]} + ${dependencies.length - 1} more dependencies`);
154
- try {
155
- await (0, helpers_1.installPackages)({ dependencies, logLevel });
156
+ const projectType = includeStorefront ? 'monorepo' : 'project';
157
+ setupSpinner.start(`Setting up your new Vendure ${projectType} in ${picocolors_1.default.green(root)}\nThis may take a few minutes...`);
158
+ const assetPath = (fileName) => path_1.default.join(__dirname, '../assets', fileName);
159
+ const templatePath = (fileName) => path_1.default.join(__dirname, '../assets/monorepo', fileName);
160
+ if (includeStorefront) {
161
+ // Create monorepo structure
162
+ await fs_extra_1.default.ensureDir(path_1.default.join(root, 'apps'));
163
+ await fs_extra_1.default.ensureDir(serverRoot);
164
+ await fs_extra_1.default.ensureDir(path_1.default.join(serverRoot, 'src'));
165
+ // Generate root package.json from template
166
+ const rootPackageTemplate = await fs_extra_1.default.readFile(templatePath('root-package.json.hbs'), 'utf-8');
167
+ const rootPackageContent = handlebars_1.default.compile(rootPackageTemplate)({ name: appName });
168
+ fs_extra_1.default.writeFileSync(path_1.default.join(root, 'package.json'), rootPackageContent + os_1.default.EOL);
169
+ // Generate root README from template
170
+ const rootReadmeTemplate = await fs_extra_1.default.readFile(templatePath('root-readme.hbs'), 'utf-8');
171
+ const rootReadmeContent = handlebars_1.default.compile(rootReadmeTemplate)({
172
+ name: appName,
173
+ serverPort: port,
174
+ storefrontPort,
175
+ superadminIdentifier: shared_constants_1.SUPER_ADMIN_USER_IDENTIFIER,
176
+ superadminPassword: shared_constants_1.SUPER_ADMIN_USER_PASSWORD,
177
+ });
178
+ fs_extra_1.default.writeFileSync(path_1.default.join(root, 'README.md'), rootReadmeContent);
179
+ // Copy root .gitignore
180
+ await fs_extra_1.default.copyFile(templatePath('root-gitignore.template'), path_1.default.join(root, '.gitignore'));
181
+ // Create server package.json
182
+ const serverPackageJsonContents = {
183
+ name: 'server',
184
+ version: constants_1.DEFAULT_PROJECT_VERSION,
185
+ private: true,
186
+ scripts: getServerPackageScripts(),
187
+ };
188
+ fs_extra_1.default.writeFileSync(path_1.default.join(serverRoot, 'package.json'), JSON.stringify(serverPackageJsonContents, null, 2) + os_1.default.EOL);
156
189
  }
157
- catch (e) {
158
- (0, prompts_1.outro)(picocolors_1.default.red(`Failed to inst all dependencies. Please try again.`));
159
- process.exit(1);
190
+ else {
191
+ // Single project structure (original behavior)
192
+ const packageJsonContents = {
193
+ name: appName,
194
+ version: constants_1.DEFAULT_PROJECT_VERSION,
195
+ private: true,
196
+ scripts: getServerPackageScripts(),
197
+ };
198
+ fs_extra_1.default.writeFileSync(path_1.default.join(root, 'package.json'), JSON.stringify(packageJsonContents, null, 2) + os_1.default.EOL);
199
+ fs_extra_1.default.ensureDirSync(path_1.default.join(root, 'src'));
160
200
  }
161
- installSpinner.stop(`Successfully installed ${dependencies.length} dependencies`);
162
- if (devDependencies.length) {
163
- const installDevSpinner = (0, prompts_1.spinner)();
164
- installDevSpinner.start(`Installing ${devDependencies[0]} + ${devDependencies.length - 1} more dev dependencies`);
201
+ setupSpinner.stop(`Created ${picocolors_1.default.green('package.json')}`);
202
+ // Download storefront if needed
203
+ if (includeStorefront) {
204
+ const storefrontSpinner = (0, prompts_1.spinner)();
205
+ storefrontSpinner.start(`Downloading Next.js storefront...`);
165
206
  try {
166
- await (0, helpers_1.installPackages)({ dependencies: devDependencies, isDevDependencies: true, logLevel });
207
+ await (0, helpers_1.downloadAndExtractStorefront)(storefrontRoot);
208
+ // Update storefront package.json name and dev script port
209
+ const storefrontPackageJsonPath = path_1.default.join(storefrontRoot, 'package.json');
210
+ const storefrontPackageJson = await fs_extra_1.default.readJson(storefrontPackageJsonPath);
211
+ storefrontPackageJson.name = 'storefront';
212
+ if ((_a = storefrontPackageJson.scripts) === null || _a === void 0 ? void 0 : _a.dev) {
213
+ storefrontPackageJson.scripts.dev = `next dev --port ${storefrontPort}`;
214
+ }
215
+ await fs_extra_1.default.writeJson(storefrontPackageJsonPath, storefrontPackageJson, { spaces: 2 });
216
+ // Generate storefront .env.local from template
217
+ const storefrontEnvTemplate = await fs_extra_1.default.readFile(templatePath('storefront-env.hbs'), 'utf-8');
218
+ const storefrontEnvContent = handlebars_1.default.compile(storefrontEnvTemplate)({
219
+ serverPort: port,
220
+ storefrontPort,
221
+ name: appName,
222
+ revalidationSecret: (0, crypto_1.randomBytes)(32).toString('base64'),
223
+ });
224
+ fs_extra_1.default.writeFileSync(path_1.default.join(storefrontRoot, '.env.local'), storefrontEnvContent);
225
+ storefrontSpinner.stop(`Downloaded Next.js storefront`);
167
226
  }
168
227
  catch (e) {
169
- (0, prompts_1.outro)(picocolors_1.default.red(`Failed to install dev dependencies. Please try again.`));
228
+ storefrontSpinner.stop(picocolors_1.default.red(`Failed to download storefront`));
229
+ (0, logger_1.log)(e.message, { level: 'verbose' });
230
+ (0, prompts_1.outro)(picocolors_1.default.red(`Failed to download storefront: ${e.message}`));
170
231
  process.exit(1);
171
232
  }
172
- installDevSpinner.stop(`Successfully installed ${devDependencies.length} dev dependencies`);
233
+ }
234
+ // Install dependencies
235
+ const { dependencies, devDependencies } = (0, helpers_1.getDependencies)(dbType, `@${packageJson.version}`);
236
+ // Install server dependencies
237
+ await installDependenciesWithSpinner({
238
+ dependencies,
239
+ logLevel,
240
+ cwd: serverRoot,
241
+ spinnerMessage: `Installing ${dependencies[0]} + ${dependencies.length - 1} more dependencies`,
242
+ successMessage: `Successfully installed ${dependencies.length} dependencies`,
243
+ failureMessage: 'Failed to install dependencies. Please try again.',
244
+ });
245
+ if (devDependencies.length) {
246
+ await installDependenciesWithSpinner({
247
+ dependencies: devDependencies,
248
+ isDevDependencies: true,
249
+ logLevel,
250
+ cwd: serverRoot,
251
+ spinnerMessage: `Installing ${devDependencies[0]} + ${devDependencies.length - 1} more dev dependencies`,
252
+ successMessage: `Successfully installed ${devDependencies.length} dev dependencies`,
253
+ failureMessage: 'Failed to install dev dependencies. Please try again.',
254
+ });
255
+ }
256
+ if (includeStorefront) {
257
+ // Install storefront dependencies
258
+ const storefrontInstalled = await installDependenciesWithSpinner({
259
+ dependencies: [],
260
+ logLevel,
261
+ cwd: storefrontRoot,
262
+ spinnerMessage: 'Installing storefront dependencies...',
263
+ successMessage: 'Installed storefront dependencies',
264
+ failureMessage: 'Failed to install storefront dependencies',
265
+ warnOnFailure: true,
266
+ });
267
+ if (!storefrontInstalled) {
268
+ (0, logger_1.log)('You may need to run npm install in the storefront directory manually.', { level: 'info' });
269
+ }
173
270
  }
174
271
  const scaffoldSpinner = (0, prompts_1.spinner)();
175
272
  scaffoldSpinner.start(`Generating app scaffold`);
176
273
  // We add this pause so that the above output is displayed before the
177
274
  // potentially lengthy file operations begin, which can prevent that
178
275
  // from displaying and thus make the user think that the process has hung.
179
- await (0, promises_1.setTimeout)(500);
180
- fs_extra_1.default.ensureDirSync(path_1.default.join(root, 'src'));
181
- const assetPath = (fileName) => path_1.default.join(__dirname, '../assets', fileName);
276
+ await (0, promises_1.setTimeout)(constants_1.SCAFFOLD_DELAY_MS);
277
+ const srcPathScript = (fileName) => path_1.default.join(serverRoot, 'src', `${fileName}.ts`);
278
+ if (!includeStorefront) {
279
+ fs_extra_1.default.ensureDirSync(path_1.default.join(serverRoot, 'src'));
280
+ }
182
281
  const configFile = srcPathScript('vendure-config');
183
282
  try {
184
283
  await fs_extra_1.default
185
284
  .writeFile(configFile, configSource)
186
- .then(() => fs_extra_1.default.writeFile(path_1.default.join(root, '.env'), envSource))
285
+ .then(() => fs_extra_1.default.writeFile(path_1.default.join(serverRoot, '.env'), envSource))
187
286
  .then(() => fs_extra_1.default.writeFile(srcPathScript('environment.d'), envDtsSource))
188
287
  .then(() => fs_extra_1.default.writeFile(srcPathScript('index'), indexSource))
189
288
  .then(() => fs_extra_1.default.writeFile(srcPathScript('index-worker'), indexWorkerSource))
190
- .then(() => fs_extra_1.default.writeFile(path_1.default.join(root, 'README.md'), readmeSource))
191
- .then(() => fs_extra_1.default.writeFile(path_1.default.join(root, 'Dockerfile'), dockerfileSource))
192
- .then(() => fs_extra_1.default.writeFile(path_1.default.join(root, 'docker-compose.yml'), dockerComposeSource))
193
- .then(() => fs_extra_1.default.ensureDir(path_1.default.join(root, 'src/plugins')))
194
- .then(() => fs_extra_1.default.copyFile(assetPath('gitignore.template'), path_1.default.join(root, '.gitignore')))
195
- .then(() => fs_extra_1.default.copyFile(assetPath('tsconfig.template.json'), path_1.default.join(root, 'tsconfig.json')))
196
- .then(() => fs_extra_1.default.copyFile(assetPath('tsconfig.dashboard.template.json'), path_1.default.join(root, 'tsconfig.dashboard.json')))
197
- .then(() => fs_extra_1.default.copyFile(assetPath('vite.config.template.mts'), path_1.default.join(root, 'vite.config.mts')))
198
- .then(() => createDirectoryStructure(root))
199
- .then(() => copyEmailTemplates(root));
289
+ .then(() => fs_extra_1.default.writeFile(path_1.default.join(serverRoot, 'README.md'), readmeSource))
290
+ .then(() => fs_extra_1.default.writeFile(path_1.default.join(serverRoot, 'Dockerfile'), dockerfileSource))
291
+ .then(() => fs_extra_1.default.writeFile(path_1.default.join(serverRoot, 'docker-compose.yml'), dockerComposeSource))
292
+ .then(() => fs_extra_1.default.ensureDir(path_1.default.join(serverRoot, 'src/plugins')))
293
+ .then(() => fs_extra_1.default.copyFile(assetPath('gitignore.template'), path_1.default.join(serverRoot, '.gitignore')))
294
+ .then(() => fs_extra_1.default.copyFile(assetPath('tsconfig.template.json'), path_1.default.join(serverRoot, 'tsconfig.json')))
295
+ .then(() => fs_extra_1.default.writeFile(path_1.default.join(serverRoot, 'tsconfig.dashboard.json'), tsconfigDashboardSource))
296
+ .then(() => fs_extra_1.default.writeFile(path_1.default.join(serverRoot, 'vite.config.mts'), viteConfigSource))
297
+ .then(() => createDirectoryStructure(serverRoot))
298
+ .then(() => copyEmailTemplates(serverRoot));
200
299
  }
201
300
  catch (e) {
202
301
  (0, prompts_1.outro)(picocolors_1.default.red(`Failed to create app scaffold: ${e.message}`));
@@ -205,7 +304,7 @@ async function createVendureApp(name, useNpm, logLevel, isCi = false) {
205
304
  scaffoldSpinner.stop(`Generated app scaffold`);
206
305
  if (mode === 'quick' && dbType === 'postgres') {
207
306
  (0, helpers_1.cleanUpDockerResources)(name);
208
- await (0, helpers_1.startPostgresDatabase)(root);
307
+ await (0, helpers_1.startPostgresDatabase)(serverRoot);
209
308
  }
210
309
  const populateSpinner = (0, prompts_1.spinner)();
211
310
  populateSpinner.start(`Initializing your new Vendure server`);
@@ -216,31 +315,10 @@ async function createVendureApp(name, useNpm, logLevel, isCi = false) {
216
315
  populateProducts
217
316
  ? 'We are populating sample data so that you can start testing right away'
218
317
  : 'We are setting up your Vendure server',
219
- '☕ This can take a minute or two, so grab a coffee',
220
- `✨ We'd love it if you drop us a star on GitHub: https://github.com/vendure-ecommerce/vendure`,
221
- `📖 Check out the Vendure documentation at https://docs.vendure.io`,
222
- `💬 Join our Discord community to chat with other Vendure developers: https://vendure.io/community`,
223
- '💡 In the mean time, here are some tips to get you started',
224
- `Vendure provides dedicated GraphQL APIs for both the Admin and Shop`,
225
- `Almost every aspect of Vendure is customizable via plugins`,
226
- `You can run 'vendure add' from the command line to add new plugins & features`,
227
- `Use the EventBus in your plugins to react to events in the system`,
228
- `Vendure supports multiple languages & currencies out of the box`,
229
- `☕ Did we mention this can take a while?`,
230
- `Our custom fields feature allows you to add any kind of data to your entities`,
231
- `Vendure is built with TypeScript, so you get full type safety`,
232
- `Combined with GraphQL's static schema, your type safety is end-to-end`,
233
- `☕ Almost there now... thanks for your patience!`,
234
- `Collections allow you to group products together`,
235
- `Our AssetServerPlugin allows you to dynamically resize & optimize images`,
236
- `Order flows are fully customizable to suit your business requirements`,
237
- `Role-based permissions allow you to control access to every part of the system`,
238
- `Customers can be grouped for targeted promotions & custom pricing`,
239
- `You can find integrations in the Vendure Hub: https://vendure.io/hub`,
318
+ ...constants_1.TIPS_WHILE_WAITING,
240
319
  ];
241
320
  let tipIndex = 0;
242
321
  let timer;
243
- const tipInterval = 10000;
244
322
  function displayTip() {
245
323
  populateSpinner.message(tips[tipIndex]);
246
324
  tipIndex++;
@@ -248,16 +326,28 @@ async function createVendureApp(name, useNpm, logLevel, isCi = false) {
248
326
  // skip the intro tips if looping
249
327
  tipIndex = 3;
250
328
  }
251
- timer = setTimeout(displayTip, tipInterval);
329
+ timer = setTimeout(displayTip, constants_1.TIP_INTERVAL_MS);
252
330
  }
253
- timer = setTimeout(displayTip, tipInterval);
331
+ timer = setTimeout(displayTip, constants_1.TIP_INTERVAL_MS);
332
+ // Change to serverRoot so that ts-node can correctly resolve modules.
333
+ // In monorepo mode, dependencies are hoisted to the root node_modules,
334
+ // but ts-node needs to be anchored in the server directory for proper
335
+ // module resolution and to find the tsconfig.json.
336
+ process.chdir(serverRoot);
254
337
  // register ts-node so that the config file can be loaded
338
+ // We use transpileOnly to skip type checking during bootstrap, as the
339
+ // complex module resolution with npm workspaces and ESM packages can
340
+ // cause false TypeScript errors. Type checking happens when users run
341
+ // their own build/dev commands.
255
342
  // eslint-disable-next-line @typescript-eslint/no-var-requires
256
- require((0, helpers_1.resolvePackageRootDir)('ts-node', root)).register();
343
+ require((0, helpers_1.resolvePackageRootDir)('ts-node', serverRoot)).register({
344
+ project: path_1.default.join(serverRoot, 'tsconfig.json'),
345
+ transpileOnly: true,
346
+ });
257
347
  let superAdminCredentials;
258
348
  try {
259
- const { populate } = await Promise.resolve(`${path_1.default.join((0, helpers_1.resolvePackageRootDir)('@vendure/core', root), 'cli', 'populate')}`).then(s => __importStar(require(s)));
260
- const { bootstrap, DefaultLogger, LogLevel, JobQueueService } = await Promise.resolve(`${path_1.default.join((0, helpers_1.resolvePackageRootDir)('@vendure/core', root), 'dist', 'index')}`).then(s => __importStar(require(s)));
349
+ const { populate } = await Promise.resolve(`${path_1.default.join((0, helpers_1.resolvePackageRootDir)('@vendure/core', serverRoot), 'cli', 'populate')}`).then(s => __importStar(require(s)));
350
+ const { bootstrap, DefaultLogger, LogLevel, JobQueueService } = await Promise.resolve(`${path_1.default.join((0, helpers_1.resolvePackageRootDir)('@vendure/core', serverRoot), 'dist', 'index')}`).then(s => __importStar(require(s)));
261
351
  const { config } = await Promise.resolve(`${configFile}`).then(s => __importStar(require(s)));
262
352
  const assetsDir = path_1.default.join(__dirname, '../assets');
263
353
  superAdminCredentials = config.authOptions.superadminCredentials;
@@ -269,7 +359,7 @@ async function createVendureApp(name, useNpm, logLevel, isCi = false) {
269
359
  : LogLevel.Info;
270
360
  const bootstrapFn = async () => {
271
361
  var _a;
272
- await (0, helpers_1.checkDbConnection)(config.dbConnectionOptions, root);
362
+ await (0, helpers_1.checkDbConnection)(config.dbConnectionOptions, serverRoot);
273
363
  const _app = await bootstrap(Object.assign(Object.assign({}, config), { apiOptions: Object.assign(Object.assign({}, ((_a = config.apiOptions) !== null && _a !== void 0 ? _a : {})), { port }), dbConnectionOptions: Object.assign(Object.assign({}, config.dbConnectionOptions), { synchronize: true }), logger: new DefaultLogger({ level: vendureLogLevel }), importExportOptions: {
274
364
  importAssetsDir: path_1.default.join(assetsDir, 'images'),
275
365
  } }));
@@ -281,11 +371,11 @@ async function createVendureApp(name, useNpm, logLevel, isCi = false) {
281
371
  if (isCi) {
282
372
  (0, logger_1.log)('[CI] Pausing before close...');
283
373
  }
284
- await (0, promises_1.setTimeout)(isCi ? 30000 : 2000);
374
+ await (0, promises_1.setTimeout)(isCi ? constants_1.CI_PAUSE_BEFORE_CLOSE_MS : constants_1.NORMAL_PAUSE_BEFORE_CLOSE_MS);
285
375
  await app.close();
286
376
  if (isCi) {
287
377
  (0, logger_1.log)('[CI] Pausing after close...');
288
- await (0, promises_1.setTimeout)(10000);
378
+ await (0, promises_1.setTimeout)(constants_1.CI_PAUSE_AFTER_CLOSE_MS);
289
379
  }
290
380
  populateSpinner.stop(`Server successfully initialized${populateProducts ? ' and populated' : ''}`);
291
381
  clearTimeout(timer);
@@ -306,8 +396,8 @@ async function createVendureApp(name, useNpm, logLevel, isCi = false) {
306
396
  const dashboardUrl = `http://localhost:${port}/dashboard`;
307
397
  const quickStartInstructions = [
308
398
  'Use the following credentials to log in to the Dashboard:',
309
- `Username: ${picocolors_1.default.green((_a = config.authOptions.superadminCredentials) === null || _a === void 0 ? void 0 : _a.identifier)}`,
310
- `Password: ${picocolors_1.default.green((_b = config.authOptions.superadminCredentials) === null || _b === void 0 ? void 0 : _b.password)}`,
399
+ `Username: ${picocolors_1.default.green((_b = config.authOptions.superadminCredentials) === null || _b === void 0 ? void 0 : _b.identifier)}`,
400
+ `Password: ${picocolors_1.default.green((_c = config.authOptions.superadminCredentials) === null || _c === void 0 ? void 0 : _c.password)}`,
311
401
  `Open your browser and navigate to: ${picocolors_1.default.green(dashboardUrl)}`,
312
402
  '',
313
403
  ];
@@ -325,13 +415,20 @@ async function createVendureApp(name, useNpm, logLevel, isCi = false) {
325
415
  }
326
416
  // process.stdin.resume();
327
417
  process.on('SIGINT', function () {
328
- displayOutro(root, name, superAdminCredentials);
418
+ displayOutro({
419
+ root,
420
+ name,
421
+ superAdminCredentials,
422
+ includeStorefront,
423
+ serverPort: port,
424
+ storefrontPort,
425
+ });
329
426
  quickStartProcess === null || quickStartProcess === void 0 ? void 0 : quickStartProcess.kill('SIGINT');
330
427
  process.exit(0);
331
428
  });
332
429
  // Give enough time for the server to get up and running
333
430
  // before opening the window.
334
- await (0, promises_1.setTimeout)(10000);
431
+ await (0, promises_1.setTimeout)(constants_1.AUTO_RUN_DELAY_MS);
335
432
  try {
336
433
  await (0, open_1.default)(dashboardUrl, {
337
434
  newInstance: true,
@@ -350,7 +447,14 @@ async function createVendureApp(name, useNpm, logLevel, isCi = false) {
350
447
  }
351
448
  else {
352
449
  clearTimeout(timer);
353
- displayOutro(root, name, superAdminCredentials);
450
+ displayOutro({
451
+ root,
452
+ name,
453
+ superAdminCredentials,
454
+ includeStorefront,
455
+ serverPort: port,
456
+ storefrontPort,
457
+ });
354
458
  process.exit(0);
355
459
  }
356
460
  }
@@ -360,34 +464,106 @@ async function createVendureApp(name, useNpm, logLevel, isCi = false) {
360
464
  process.exit(1);
361
465
  }
362
466
  }
363
- function displayOutro(root, name, superAdminCredentials) {
467
+ /**
468
+ * Returns the standard npm scripts for the server package.json.
469
+ */
470
+ function getServerPackageScripts() {
471
+ return {
472
+ 'dev:server': 'ts-node ./src/index.ts',
473
+ 'dev:worker': 'ts-node ./src/index-worker.ts',
474
+ 'dev:dashboard': 'vite --clearScreen false',
475
+ dev: 'concurrently --kill-others npm:dev:*',
476
+ build: 'tsc',
477
+ 'build:dashboard': 'vite build',
478
+ 'start:server': 'node ./dist/index.js',
479
+ 'start:worker': 'node ./dist/index-worker.js',
480
+ start: 'concurrently npm:start:*',
481
+ };
482
+ }
483
+ /**
484
+ * Installs dependencies with a spinner, handling success/failure messaging.
485
+ * Returns true if installation succeeded, false otherwise.
486
+ */
487
+ async function installDependenciesWithSpinner(installOptions) {
488
+ const { dependencies, isDevDependencies = false, logLevel, cwd, spinnerMessage, successMessage, failureMessage, warnOnFailure = false, } = installOptions;
489
+ const installSpinner = (0, prompts_1.spinner)();
490
+ installSpinner.start(spinnerMessage);
491
+ try {
492
+ await (0, helpers_1.installPackages)({ dependencies, isDevDependencies, logLevel, cwd });
493
+ installSpinner.stop(successMessage);
494
+ return true;
495
+ }
496
+ catch (e) {
497
+ if (warnOnFailure) {
498
+ installSpinner.stop(picocolors_1.default.yellow(`Warning: ${failureMessage}`));
499
+ return false;
500
+ }
501
+ else {
502
+ (0, prompts_1.outro)(picocolors_1.default.red(failureMessage));
503
+ process.exit(1);
504
+ }
505
+ }
506
+ }
507
+ // eslint-disable-next-line @typescript-eslint/no-shadow
508
+ function displayOutro(outroOptions) {
364
509
  var _a, _b;
510
+ const { root, name, superAdminCredentials, includeStorefront, serverPort = constants_1.SERVER_PORT, storefrontPort = constants_1.STOREFRONT_PORT, } = outroOptions;
365
511
  const startCommand = 'npm run dev';
366
- const nextSteps = [
367
- `Your new Vendure server was created!`,
368
- picocolors_1.default.gray(root),
369
- `\n`,
370
- `Next, run:`,
371
- picocolors_1.default.gray('$ ') + picocolors_1.default.blue(picocolors_1.default.bold(`cd ${name}`)),
372
- picocolors_1.default.gray('$ ') + picocolors_1.default.blue(picocolors_1.default.bold(`${startCommand}`)),
373
- `\n`,
374
- `This will start the server in development mode.`,
375
- `\n`,
376
- `To run the Dashboard, in a new terminal navigate to your project directory and run:`,
377
- picocolors_1.default.gray('$ ') + picocolors_1.default.blue(picocolors_1.default.bold(`npx vite`)),
378
- `\n`,
379
- `To access the Dashboard, open your browser and navigate to:`,
380
- picocolors_1.default.green(`http://localhost:3000/dashboard`),
512
+ const identifier = (_a = superAdminCredentials === null || superAdminCredentials === void 0 ? void 0 : superAdminCredentials.identifier) !== null && _a !== void 0 ? _a : shared_constants_1.SUPER_ADMIN_USER_IDENTIFIER;
513
+ const password = (_b = superAdminCredentials === null || superAdminCredentials === void 0 ? void 0 : superAdminCredentials.password) !== null && _b !== void 0 ? _b : shared_constants_1.SUPER_ADMIN_USER_PASSWORD;
514
+ // Common footer for both modes
515
+ const commonFooter = [
381
516
  `\n`,
382
517
  `Use the following credentials to log in:`,
383
- `Username: ${picocolors_1.default.green((_a = superAdminCredentials === null || superAdminCredentials === void 0 ? void 0 : superAdminCredentials.identifier) !== null && _a !== void 0 ? _a : 'superadmin')}`,
384
- `Password: ${picocolors_1.default.green((_b = superAdminCredentials === null || superAdminCredentials === void 0 ? void 0 : superAdminCredentials.password) !== null && _b !== void 0 ? _b : 'superadmin')}`,
518
+ `Username: ${picocolors_1.default.green(identifier)}`,
519
+ `Password: ${picocolors_1.default.green(password)}`,
385
520
  '\n',
386
521
  '➡️ Docs: https://docs.vendure.io',
387
522
  '➡️ Discord community: https://vendure.io/community',
388
523
  '➡️ Star us on GitHub:',
389
524
  ' https://github.com/vendure-ecommerce/vendure',
390
525
  ];
526
+ let nextSteps;
527
+ if (includeStorefront) {
528
+ nextSteps = [
529
+ `Your new Vendure project was created!`,
530
+ picocolors_1.default.gray(root),
531
+ `\n`,
532
+ `This is a monorepo with the following apps:`,
533
+ ` ${picocolors_1.default.cyan('apps/server')} - Vendure backend`,
534
+ ` ${picocolors_1.default.cyan('apps/storefront')} - Next.js frontend`,
535
+ `\n`,
536
+ `Next, run:`,
537
+ picocolors_1.default.gray('$ ') + picocolors_1.default.blue(picocolors_1.default.bold(`cd ${name}`)),
538
+ picocolors_1.default.gray('$ ') + picocolors_1.default.blue(picocolors_1.default.bold(`${startCommand}`)),
539
+ `\n`,
540
+ `This will start both the server and storefront.`,
541
+ `\n`,
542
+ `Access points:`,
543
+ ` Dashboard: ${picocolors_1.default.green(`http://localhost:${serverPort}/dashboard`)}`,
544
+ ` Storefront: ${picocolors_1.default.green(`http://localhost:${storefrontPort}`)}`,
545
+ ...commonFooter,
546
+ ];
547
+ }
548
+ else {
549
+ nextSteps = [
550
+ `Your new Vendure server was created!`,
551
+ picocolors_1.default.gray(root),
552
+ `\n`,
553
+ `Next, run:`,
554
+ picocolors_1.default.gray('$ ') + picocolors_1.default.blue(picocolors_1.default.bold(`cd ${name}`)),
555
+ picocolors_1.default.gray('$ ') + picocolors_1.default.blue(picocolors_1.default.bold(`${startCommand}`)),
556
+ `\n`,
557
+ `This will start the server in development mode.`,
558
+ `\n`,
559
+ `To run the Dashboard, in a new terminal navigate to your project directory and run:`,
560
+ picocolors_1.default.gray('$ ') + picocolors_1.default.blue(picocolors_1.default.bold(`npx vite`)),
561
+ `\n`,
562
+ `To access the Dashboard, open your browser and navigate to:`,
563
+ picocolors_1.default.green(`http://localhost:${serverPort}/dashboard`),
564
+ ...commonFooter,
565
+ ];
566
+ }
391
567
  (0, prompts_1.note)(nextSteps.join('\n'), picocolors_1.default.green('Setup complete!'));
392
568
  (0, prompts_1.outro)(`Happy hacking!`);
393
569
  }
@@ -395,7 +571,7 @@ function displayOutro(root, name, superAdminCredentials) {
395
571
  * Run some initial checks to ensure that it is okay to proceed with creating
396
572
  * a new Vendure project in the given location.
397
573
  */
398
- function runPreChecks(name, useNpm) {
574
+ function runPreChecks(name) {
399
575
  if (typeof name === 'undefined') {
400
576
  (0, logger_1.log)(picocolors_1.default.red(`Please specify the project directory:`));
401
577
  (0, logger_1.log)(` ${picocolors_1.default.cyan(commander_1.program.name())} ${picocolors_1.default.green('<project-directory>')}`, { newline: 'after' });