@commercetools-frontend/create-mc-app 22.2.0 → 22.3.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.
@@ -33,7 +33,7 @@ import prettier from 'prettier';
33
33
 
34
34
  var pkgJson = {
35
35
  name: "@commercetools-frontend/create-mc-app",
36
- version: "22.2.0",
36
+ version: "22.3.0",
37
37
  description: "Create Merchant Center applications to quickly get up and running",
38
38
  bugs: "https://github.com/commercetools/merchant-center-application-kit/issues",
39
39
  repository: {
@@ -59,6 +59,8 @@ var pkgJson = {
59
59
  "@babel/core": "^7.20.12",
60
60
  "@babel/runtime": "^7.20.13",
61
61
  "@babel/runtime-corejs3": "^7.20.13",
62
+ "@types/babel__core": "^7.20.0",
63
+ "@types/semver": "^7.5.0",
62
64
  cac: "6.7.14",
63
65
  execa: "5.1.1",
64
66
  listr2: "5.0.8",
@@ -83,14 +85,10 @@ async function getLatestReleaseVersion() {
83
85
 
84
86
  function hintOutdatedVersion(currentVersion, releaseVersion) {
85
87
  var _context;
86
-
87
88
  const hasBeenReleastedInLatestTag = semver.gt(releaseVersion, currentVersion);
88
-
89
89
  const hintNewerVersions = _filterInstanceProperty(_context = [hasBeenReleastedInLatestTag && "".concat(releaseVersion)]).call(_context, Boolean).join(', ');
90
-
91
90
  if (hintNewerVersions.length > 0) {
92
91
  var _context2;
93
-
94
92
  console.log('');
95
93
  console.log(_concatInstanceProperty(_context2 = "New version available! ".concat(currentVersion, " -> ")).call(_context2, hintNewerVersions));
96
94
  console.log('');
@@ -98,7 +96,6 @@ function hintOutdatedVersion(currentVersion, releaseVersion) {
98
96
  }
99
97
 
100
98
  const isSemVer = version => /^(v?)([0-9].[0-9].[0-9])+/.test(version);
101
-
102
99
  const shouldUseYarn = () => {
103
100
  try {
104
101
  const result = execa.commandSync('yarn --version', {
@@ -109,28 +106,30 @@ const shouldUseYarn = () => {
109
106
  return false;
110
107
  }
111
108
  };
112
-
109
+ const getPreferredPackageManager = options => {
110
+ if (options.packageManager) {
111
+ return options.packageManager;
112
+ }
113
+ // Attempt to use yarn (backwards compatibility)
114
+ if (shouldUseYarn()) {
115
+ return 'yarn';
116
+ }
117
+ // Fall back to npm
118
+ return 'npm';
119
+ };
113
120
  const slugify = name => name.toLowerCase().replace(/_/gi, '-');
114
-
115
121
  const upperFirst = value => value.charAt(0).toUpperCase() + _sliceInstanceProperty(value).call(value, 1);
116
-
117
122
  const wordify = slug => {
118
123
  var _context;
119
-
120
124
  return _mapInstanceProperty(_context = slug.split('-')).call(_context, word => upperFirst(word)).join(' ');
121
125
  };
122
-
123
126
  const resolveFilePathByExtension = requestedModule => {
124
127
  var _context2, _context4;
125
-
126
128
  const fileExtension = _findInstanceProperty(_context2 = ['.js', '.ts', '.mjs', '.cjs']).call(_context2, ext => {
127
129
  var _context3;
128
-
129
130
  const filePath = _concatInstanceProperty(_context3 = "".concat(requestedModule)).call(_context3, ext);
130
-
131
131
  return fs.existsSync(filePath);
132
132
  });
133
-
134
133
  return _concatInstanceProperty(_context4 = "".concat(requestedModule)).call(_context4, fileExtension);
135
134
  };
136
135
 
@@ -138,112 +137,91 @@ const availableTemplates = {
138
137
  starter: 'starter',
139
138
  'starter-typescript': 'starter-typescript'
140
139
  };
141
-
142
140
  const throwIfTemplateIsNotSupported = templateName => {
143
141
  var _context;
144
-
145
142
  switch (templateName) {
146
143
  case availableTemplates.starter:
147
144
  case availableTemplates['starter-typescript']:
148
145
  break;
149
-
150
146
  default:
151
147
  const templateNamesList = _Object$keys(availableTemplates).toString();
152
-
153
148
  throw new Error(_concatInstanceProperty(_context = "The provided template name \"".concat(templateName, "\" does not exist. Available templates are \"")).call(_context, templateNamesList, "\". Make sure you are also using the latest version of \"@commercetools-frontend/create-mc-app\"."));
154
149
  }
155
150
  };
156
-
157
151
  const throwIfProjectDirectoryExists = (dirName, dirPath) => {
158
152
  if (fs.existsSync(dirPath)) {
159
153
  var _context2;
160
-
161
154
  throw new Error(_concatInstanceProperty(_context2 = "A directory named \"".concat(dirName, "\" already exists at this location \"")).call(_context2, dirPath, "\". Please choose a different project name or remove the directory, then try running the command again."));
162
155
  }
163
156
  };
164
-
165
157
  const throwIfTemplateVersionDoesNotExist = (templateName, templateFolderPath, versionToCheck) => {
166
158
  if (!fs.existsSync(templateFolderPath)) {
167
159
  var _context3;
168
-
169
160
  throw new Error(_concatInstanceProperty(_context3 = "The downloaded template \"".concat(templateName, "\" does not exist for the given version \"")).call(_context3, versionToCheck, "\". Check the releases page if you are looking for a specific version: https://github.com/commercetools/merchant-center-application-kit/releases"));
170
- } // In case the version is semver (usually release tags) we check that
161
+ }
162
+ // In case the version is semver (usually release tags) we check that
171
163
  // the cloned repository contains the template matching the given version
172
-
173
-
174
164
  if (isSemVer(versionToCheck)) {
175
165
  const templatePackageJson = JSON.parse(fs.readFileSync(path.join(templateFolderPath, 'package.json'), {
176
166
  encoding: 'utf8'
177
167
  }));
178
168
  const versionAsNumber = versionToCheck.replace('v', '');
179
-
180
169
  if (templatePackageJson.version !== versionAsNumber) {
181
170
  var _context4, _context5;
182
-
183
171
  throw new Error(_concatInstanceProperty(_context4 = _concatInstanceProperty(_context5 = "The downloaded template \"".concat(templateName, "\" does not match the version \"")).call(_context5, versionAsNumber, "\", instead got \"")).call(_context4, templatePackageJson.version, "\". Check the releases page if you want to provide a specific version: https://github.com/commercetools/merchant-center-application-kit/releases"));
184
172
  }
185
173
  }
186
174
  };
187
-
188
175
  const throwIfInitialProjectKeyIsMissing = initialProjectKey => {
189
176
  if (!initialProjectKey) {
190
177
  throw new Error("Provide a valid project key that you have access to.");
191
178
  }
192
179
  };
193
-
194
180
  const throwIfNodeVersionIsNotSupported = (currentNodeVersion, expectedVersionRange) => {
195
181
  const hasValidNodeVersion = semver.satisfies(currentNodeVersion, expectedVersionRange);
196
-
197
182
  if (!hasValidNodeVersion) {
198
183
  var _context6;
199
-
200
184
  throw new Error(_concatInstanceProperty(_context6 = "You are running Node ".concat(currentNodeVersion, " but create-mc-app requires Node ")).call(_context6, expectedVersionRange, ". Please update your version of Node."));
201
185
  }
202
186
  };
203
187
 
204
188
  const question = (rl, value) => new _Promise(resolve => rl.question(value, resolve));
205
-
206
189
  const getEntryPointUriPath = async (rl, options) => {
207
190
  var _context;
208
-
209
191
  if (options.entryPointUriPath) {
210
192
  return options.entryPointUriPath;
211
193
  }
212
-
213
194
  const randomEntryPointUriPath = _concatInstanceProperty(_context = "".concat(options.template, "-")).call(_context, crypto.randomBytes(3).toString('hex'));
214
-
215
195
  if (options.yes) {
216
196
  return randomEntryPointUriPath;
217
197
  }
218
-
219
198
  const answerEntryPointUriPath = await question(rl, "Provide the Custom Application entryPointUriPath (default \"".concat(randomEntryPointUriPath, "\"): "));
220
199
  return answerEntryPointUriPath || randomEntryPointUriPath;
221
200
  };
222
-
223
201
  const getInitialProjectKey = async (rl, options) => {
224
202
  if (options.initialProjectKey) {
225
203
  return options.initialProjectKey;
226
204
  }
227
-
228
205
  const initialProjectKey = await question(rl, "Provide the initial project key for local development: ");
229
206
  throwIfInitialProjectKeyIsMissing(initialProjectKey);
230
207
  return initialProjectKey;
231
208
  };
232
-
233
209
  async function processOptions(projectDirectoryName, options) {
234
210
  if (!projectDirectoryName) {
235
211
  throw new Error('Missing required argument "[project-directory]"');
236
212
  }
213
+ const projectDirectoryPath = path.resolve(projectDirectoryName);
237
214
 
238
- const projectDirectoryPath = path.resolve(projectDirectoryName); // Parse options
239
-
215
+ // Parse options
240
216
  let tagOrBranchVersion = options.templateVersion || 'main';
241
217
  tagOrBranchVersion = isSemVer(tagOrBranchVersion) && !_startsWithInstanceProperty(tagOrBranchVersion).call(tagOrBranchVersion, 'v') ? "v".concat(tagOrBranchVersion) : tagOrBranchVersion;
242
- const templateName = options.template; // Validate options
218
+ const templateName = options.template;
243
219
 
220
+ // Validate options
244
221
  throwIfProjectDirectoryExists(projectDirectoryName, projectDirectoryPath);
245
- throwIfTemplateIsNotSupported(templateName); // Read prompts
222
+ throwIfTemplateIsNotSupported(templateName);
246
223
 
224
+ // Read prompts
247
225
  const rl = readline.createInterface({
248
226
  input: process.stdin,
249
227
  output: process.stdout
@@ -257,18 +235,17 @@ async function processOptions(projectDirectoryName, options) {
257
235
  templateName,
258
236
  tagOrBranchVersion,
259
237
  entryPointUriPath,
260
- initialProjectKey
238
+ initialProjectKey,
239
+ packageManager: options.packageManager
261
240
  };
262
241
  }
263
242
 
264
243
  const filesToBeRemoved = ['CHANGELOG.md'];
265
-
266
244
  function downloadTemplate(options) {
267
245
  return {
268
246
  title: 'Downloading template',
269
247
  task: () => {
270
248
  var _context;
271
-
272
249
  const tmpDir = os.tmpdir();
273
250
  const tmpFolderNameForClonedRepository = ['merchant-center-application-kit', '--', options.tagOrBranchVersion, '--', _Date$now().toString()].join('');
274
251
  const clonedRepositoryPath = path.join(tmpDir, tmpFolderNameForClonedRepository);
@@ -281,11 +258,9 @@ function downloadTemplate(options) {
281
258
  cwd: tmpDir,
282
259
  encoding: 'utf-8'
283
260
  });
284
-
285
261
  if (result.failed) {
286
262
  throw new Error(result.stderr);
287
263
  }
288
-
289
264
  throwIfTemplateVersionDoesNotExist(options.templateName, clonedRepositoryPath, options.tagOrBranchVersion);
290
265
  return result.stdout;
291
266
  }
@@ -293,21 +268,18 @@ function downloadTemplate(options) {
293
268
  title: _concatInstanceProperty(_context = "Copying template ".concat(options.templateName, " into project directory ")).call(_context, options.projectDirectoryPath),
294
269
  task: async () => {
295
270
  const command = process.platform === 'win32' || process.platform === 'cygwin' ? 'move' : 'mv';
296
- const result = await execa(command, [templateFolderPath, // Wrap folder path in quotes to avoid issues with empty spaces in the folder names.
271
+ const result = await execa(command, [templateFolderPath,
272
+ // Wrap folder path in quotes to avoid issues with empty spaces in the folder names.
297
273
  options.projectDirectoryPath], {
298
274
  encoding: 'utf-8'
299
275
  });
300
-
301
276
  if (result.failed) {
302
277
  throw new Error(result.stderr);
303
278
  }
304
-
305
279
  const templatePackageJsonPath = path.join(options.projectDirectoryPath, 'package.json');
306
-
307
280
  if (!fs.existsSync(templatePackageJsonPath)) {
308
281
  throw new Error("Unable to verify that the template application has a package.json at \"".concat(templatePackageJsonPath, "\""));
309
282
  }
310
-
311
283
  return result.stdout;
312
284
  }
313
285
  }, {
@@ -317,11 +289,9 @@ function downloadTemplate(options) {
317
289
  const result = await execa(command, _mapInstanceProperty(filesToBeRemoved).call(filesToBeRemoved, filePath => path.join(options.projectDirectoryPath, filePath)), {
318
290
  encoding: 'utf-8'
319
291
  });
320
-
321
292
  if (result.failed) {
322
293
  throw new Error(result.stderr);
323
294
  }
324
-
325
295
  return result.stdout;
326
296
  }
327
297
  }]);
@@ -333,10 +303,10 @@ function installDependencies(options) {
333
303
  return {
334
304
  title: 'Installing dependencies (this might take a while)',
335
305
  task: () => {
336
- const useYarn = shouldUseYarn();
337
- const packageManager = useYarn ? 'yarn' : 'npm'; // TODO: we could check for min yarn/npm versions
338
- // See https://github.com/facebook/create-react-app/blob/0f4781e8507249ce29a9ac1409fece67c1a53c38/packages/create-react-app/createReactApp.js#L225-L254
306
+ const packageManager = getPreferredPackageManager(options);
339
307
 
308
+ // TODO: we could check for min yarn/npm versions
309
+ // See https://github.com/facebook/create-react-app/blob/0f4781e8507249ce29a9ac1409fece67c1a53c38/packages/create-react-app/createReactApp.js#L225-L254
340
310
  return execa(packageManager, ['install'], {
341
311
  cwd: options.projectDirectoryPath,
342
312
  encoding: 'utf-8'
@@ -346,25 +316,20 @@ function installDependencies(options) {
346
316
  }
347
317
 
348
318
  function ownKeys(object, enumerableOnly) { var keys = _Object$keys(object); if (_Object$getOwnPropertySymbols) { var symbols = _Object$getOwnPropertySymbols(object); enumerableOnly && (symbols = _filterInstanceProperty(symbols).call(symbols, function (sym) { return _Object$getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
349
-
350
319
  function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var _context2, _context3; var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? _forEachInstanceProperty(_context2 = ownKeys(Object(source), !0)).call(_context2, function (key) { _defineProperty(target, key, source[key]); }) : _Object$getOwnPropertyDescriptors ? _Object$defineProperties(target, _Object$getOwnPropertyDescriptors(source)) : _forEachInstanceProperty(_context3 = ownKeys(Object(source))).call(_context3, function (key) { _Object$defineProperty(target, key, _Object$getOwnPropertyDescriptor(source, key)); }); } return target; }
351
-
352
320
  const replaceApplicationKitPackageVersionWith = function (releaseVersion) {
353
321
  var _context;
354
-
355
322
  let dependencies = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
356
323
  return _reduceInstanceProperty(_context = _Object$entries(dependencies)).call(_context, (updatedDependencies, _ref) => {
357
324
  let _ref2 = _slicedToArray(_ref, 2),
358
- dependencyName = _ref2[0],
359
- dependencyVersion = _ref2[1];
360
-
325
+ dependencyName = _ref2[0],
326
+ dependencyVersion = _ref2[1];
361
327
  const updatedVersion = dependencyVersion === 'workspace:*' ? releaseVersion : dependencyVersion;
362
328
  return _objectSpread(_objectSpread({}, updatedDependencies), {}, {
363
329
  [dependencyName]: updatedVersion
364
330
  });
365
331
  }, {});
366
332
  };
367
-
368
333
  function updatePackageJson(options, releaseVersion) {
369
334
  return {
370
335
  title: 'Updating package.json',
@@ -373,7 +338,7 @@ function updatePackageJson(options, releaseVersion) {
373
338
  const appPackageJson = JSON.parse(fs.readFileSync(packageJsonPath, {
374
339
  encoding: 'utf8'
375
340
  }));
376
-
341
+ const packageManager = getPreferredPackageManager(options);
377
342
  const updatedAppPackageJson = _Object$assign({}, appPackageJson, {
378
343
  version: '1.0.0',
379
344
  // Given the package name is derived from the `projectDirectoryName`
@@ -384,9 +349,11 @@ function updatePackageJson(options, releaseVersion) {
384
349
  description: '',
385
350
  // Replace the package versions with the `workspace:` syntax to the real version.
386
351
  dependencies: replaceApplicationKitPackageVersionWith(releaseVersion, appPackageJson.dependencies),
387
- devDependencies: replaceApplicationKitPackageVersionWith(releaseVersion, appPackageJson.devDependencies)
352
+ devDependencies: replaceApplicationKitPackageVersionWith(releaseVersion, appPackageJson.devDependencies),
353
+ scripts: _objectSpread(_objectSpread({}, appPackageJson.scripts), {}, {
354
+ 'start:prod:local': appPackageJson.scripts['start:prod:local'].replace('yarn', packageManager)
355
+ })
388
356
  });
389
-
390
357
  fs.writeFileSync(packageJsonPath, _JSON$stringify(updatedAppPackageJson, null, 2) + os.EOL, {
391
358
  encoding: 'utf8'
392
359
  });
@@ -406,32 +373,27 @@ function replaceApplicationInfoInCustomApplicationConfig(filePath, options) {
406
373
  }) && nodePath.parent.type === 'ObjectProperty') {
407
374
  nodePath.parent.value = types.stringLiteral(appName);
408
375
  }
409
-
410
376
  if (nodePath.isIdentifier({
411
377
  name: 'initialProjectKey'
412
378
  }) && nodePath.parent.type === 'ObjectProperty') {
413
379
  nodePath.parent.value = types.stringLiteral(options.initialProjectKey);
414
380
  }
415
-
416
381
  if (nodePath.isIdentifier({
417
382
  name: 'defaultLabel'
418
383
  })) {
419
384
  const isMainMenuLinkParent = nodePath.findParent(parentPath => parentPath.isIdentifier({
420
385
  name: 'mainMenuLink'
421
386
  }));
422
-
423
387
  if (isMainMenuLinkParent && nodePath.parent.type === 'ObjectProperty') {
424
388
  nodePath.parent.value = types.stringLiteral(appName);
425
389
  }
426
390
  }
427
391
  }
428
-
429
392
  }
430
393
  };
431
394
  }],
432
395
  retainLines: true
433
396
  });
434
-
435
397
  if (result !== null && result !== void 0 && result.code) {
436
398
  const prettierConfig = prettier.resolveConfig.sync(options.projectDirectoryPath);
437
399
  const formattedData = prettier.format(result.code + os.EOL, prettierConfig !== null && prettierConfig !== void 0 ? prettierConfig : undefined);
@@ -440,7 +402,6 @@ function replaceApplicationInfoInCustomApplicationConfig(filePath, options) {
440
402
  });
441
403
  }
442
404
  }
443
-
444
405
  function updateCustomApplicationConfig(options) {
445
406
  return {
446
407
  title: 'Updating Custom Applications config',
@@ -461,13 +422,11 @@ function replaceEntryPointUriPathInConstants(filePath, options) {
461
422
  nodePath.node.init = types.stringLiteral(options.entryPointUriPath);
462
423
  }
463
424
  }
464
-
465
425
  }
466
426
  };
467
427
  }],
468
428
  retainLines: true
469
429
  });
470
-
471
430
  if (result !== null && result !== void 0 && result.code) {
472
431
  const prettierConfig = prettier.resolveConfig.sync(options.projectDirectoryPath);
473
432
  const formattedData = prettier.format(result.code + os.EOL, prettierConfig !== null && prettierConfig !== void 0 ? prettierConfig : undefined);
@@ -476,7 +435,6 @@ function replaceEntryPointUriPathInConstants(filePath, options) {
476
435
  });
477
436
  }
478
437
  }
479
-
480
438
  function updateApplicationConstants(options) {
481
439
  return {
482
440
  title: 'Updating application constants',
@@ -488,14 +446,14 @@ function updateApplicationConstants(options) {
488
446
  }
489
447
 
490
448
  throwIfNodeVersionIsNotSupported(process.versions.node, pkgJson.engines.node);
491
- const cli = cac('create-mc-app'); // Makes the script crash on unhandled rejections instead of silently
449
+ const cli = cac('create-mc-app');
450
+
451
+ // Makes the script crash on unhandled rejections instead of silently
492
452
  // ignoring them. In the future, promise rejections that are not handled will
493
453
  // terminate the Node.js process with a non-zero exit code.
494
-
495
454
  process.on('unhandledRejection', err => {
496
455
  throw err;
497
456
  });
498
-
499
457
  const run = () => {
500
458
  // Default command
501
459
  cli.command('[project-directory]').usage('[project-directory]\n\n Bootstraps a new Custom Application project using one of the predefined templates.').option('--template <name>', '(optional) The name of the template to install.', {
@@ -506,34 +464,33 @@ const run = () => {
506
464
  default: false
507
465
  }).option('--yes', '(optional) If set, the prompt options with default values will be skipped.', {
508
466
  default: false
509
- }).option('--entry-point-uri-path <value>', '(optional) The version of the template to install. (default: starter-<hash>)').option('--initial-project-key <value>', '(optional) A commercetools project key used for the initial login in development. By default, the value is prompted in the terminal.').action(async (projectDirectory, options) => {
467
+ }).option('--entry-point-uri-path <value>', '(optional) The version of the template to install. (default: starter-<hash>)').option('--initial-project-key <value>', '(optional) A commercetools project key used for the initial login in development. By default, the value is prompted in the terminal.').option('--package-manager <value>', '(optional) The preferred package manager to use: npm, yarn, pnpm.').action(async (projectDirectory, options) => {
510
468
  var _context;
511
-
512
469
  if (!projectDirectory) {
513
470
  cli.outputHelp();
514
471
  return;
515
472
  }
516
-
517
473
  const releaseVersion = await getLatestReleaseVersion();
518
474
  hintOutdatedVersion(pkgJson.version, releaseVersion);
519
475
  console.log('');
520
476
  console.log("Documentation available at https://docs.commercetools.com/custom-applications");
521
477
  console.log('');
522
478
  const taskOptions = await processOptions(projectDirectory, options);
523
- const taskList = new Listr(_filterInstanceProperty(_context = [downloadTemplate(taskOptions), updatePackageJson(taskOptions, releaseVersion), updateCustomApplicationConfig(taskOptions), updateApplicationConstants(taskOptions), !options.skipInstall && installDependencies(taskOptions)]).call(_context, Boolean));
479
+ const shouldInstallDependencies = !options.skipInstall ||
480
+ // TODO: remove once we manage to ensure the package manager is installed, for example via Corepack.
481
+ options.packageManager === 'pnpm';
482
+ const taskList = new Listr(_filterInstanceProperty(_context = [downloadTemplate(taskOptions), updatePackageJson(taskOptions, releaseVersion), updateCustomApplicationConfig(taskOptions), updateApplicationConstants(taskOptions), shouldInstallDependencies && installDependencies(taskOptions)]).call(_context, Boolean));
524
483
  await taskList.run();
525
- const useYarn = shouldUseYarn();
484
+ const packageManager = getPreferredPackageManager(taskOptions);
526
485
  console.log('');
527
486
  console.log("\uD83C\uDF89 \uD83C\uDF89 \uD83C\uDF89 The Custom Application has been created in the \"".concat(taskOptions.projectDirectoryName, "\" folder."));
528
487
  console.log('');
529
488
  console.log("To get started:");
530
489
  console.log("$ cd ".concat(taskOptions.projectDirectoryName));
531
-
532
- if (options.skipInstall) {
533
- console.log("$ ".concat(useYarn ? 'yarn' : 'npm', " install"));
490
+ if (!shouldInstallDependencies) {
491
+ console.log("$ ".concat(packageManager, " install"));
534
492
  }
535
-
536
- console.log("$ ".concat(useYarn ? 'yarn' : 'npm', " start"));
493
+ console.log("$ ".concat(packageManager, " start"));
537
494
  console.log('');
538
495
  console.log("Visit https://docs.commercetools.com/custom-applications for more info about developing Custom Applications. Enjoy \uD83D\uDE80");
539
496
  });
@@ -1,13 +1,16 @@
1
1
  export type TCliGlobalOptions = {
2
2
  '--'?: string[];
3
3
  };
4
+ export type TTemplate = 'starter' | 'starter-typescript';
5
+ export type TPackageManager = 'npm' | 'yarn' | 'pnpm';
4
6
  export type TCliCommandOptions = {
5
- template: 'starter' | 'starter-typescript';
7
+ template: TTemplate;
6
8
  templateVersion: string;
7
9
  skipInstall: boolean;
8
10
  yes: boolean;
9
11
  entryPointUriPath?: string;
10
12
  initialProjectKey?: string;
13
+ packageManager?: TPackageManager;
11
14
  };
12
15
  export type TCliTaskOptions = {
13
16
  projectDirectoryName: string;
@@ -16,4 +19,5 @@ export type TCliTaskOptions = {
16
19
  tagOrBranchVersion: string;
17
20
  entryPointUriPath: string;
18
21
  initialProjectKey: string;
22
+ packageManager: TCliCommandOptions['packageManager'];
19
23
  };
@@ -1,7 +1,9 @@
1
+ import type { TCliTaskOptions, TPackageManager } from './types';
1
2
  declare const isSemVer: (version: string) => boolean;
2
3
  declare const shouldUseYarn: () => boolean;
4
+ declare const getPreferredPackageManager: (options: TCliTaskOptions) => TPackageManager;
3
5
  declare const slugify: (name: string) => string;
4
6
  declare const upperFirst: (value: string) => string;
5
7
  declare const wordify: (slug: string) => string;
6
8
  declare const resolveFilePathByExtension: (requestedModule: string) => string;
7
- export { isSemVer, shouldUseYarn, slugify, wordify, upperFirst, resolveFilePathByExtension, };
9
+ export { isSemVer, shouldUseYarn, slugify, wordify, upperFirst, resolveFilePathByExtension, getPreferredPackageManager, };
@@ -1,5 +1,5 @@
1
- import type { TCliCommandOptions } from './types';
2
- declare const throwIfTemplateIsNotSupported: (templateName: TCliCommandOptions['template']) => void;
1
+ import type { TTemplate } from './types';
2
+ declare const throwIfTemplateIsNotSupported: (templateName: TTemplate) => void;
3
3
  declare const throwIfProjectDirectoryExists: (dirName: string, dirPath: string) => void;
4
4
  declare const throwIfTemplateVersionDoesNotExist: (templateName: string, templateFolderPath: string, versionToCheck: string) => void;
5
5
  declare const throwIfInitialProjectKeyIsMissing: (initialProjectKey?: string) => void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@commercetools-frontend/create-mc-app",
3
- "version": "22.2.0",
3
+ "version": "22.3.0",
4
4
  "description": "Create Merchant Center applications to quickly get up and running",
5
5
  "bugs": "https://github.com/commercetools/merchant-center-application-kit/issues",
6
6
  "repository": {
@@ -9,7 +9,12 @@
9
9
  "directory": "packages/create-mc-app"
10
10
  },
11
11
  "homepage": "https://docs.commercetools.com/custom-applications",
12
- "keywords": ["javascript", "frontend", "react", "toolkit"],
12
+ "keywords": [
13
+ "javascript",
14
+ "frontend",
15
+ "react",
16
+ "toolkit"
17
+ ],
13
18
  "license": "MIT",
14
19
  "publishConfig": {
15
20
  "access": "public"
@@ -21,6 +26,8 @@
21
26
  "@babel/core": "^7.20.12",
22
27
  "@babel/runtime": "^7.20.13",
23
28
  "@babel/runtime-corejs3": "^7.20.13",
29
+ "@types/babel__core": "^7.20.0",
30
+ "@types/semver": "^7.5.0",
24
31
  "cac": "6.7.14",
25
32
  "execa": "5.1.1",
26
33
  "listr2": "5.0.8",
@@ -33,4 +40,4 @@
33
40
  "engines": {
34
41
  "node": "16.x || >=18.0.0"
35
42
  }
36
- }
43
+ }
package/src/cli.ts CHANGED
@@ -6,7 +6,7 @@ import hintOutdatedVersion from './hint-outdated-version';
6
6
  import processOptions from './process-options';
7
7
  import * as tasks from './tasks';
8
8
  import type { TCliCommandOptions } from './types';
9
- import { shouldUseYarn } from './utils';
9
+ import { getPreferredPackageManager } from './utils';
10
10
  import { throwIfNodeVersionIsNotSupported } from './validations';
11
11
 
12
12
  throwIfNodeVersionIsNotSupported(process.versions.node, pkgJson.engines.node);
@@ -55,6 +55,10 @@ const run = () => {
55
55
  '--initial-project-key <value>',
56
56
  '(optional) A commercetools project key used for the initial login in development. By default, the value is prompted in the terminal.'
57
57
  )
58
+ .option(
59
+ '--package-manager <value>',
60
+ '(optional) The preferred package manager to use: npm, yarn, pnpm.'
61
+ )
58
62
  .action(async (projectDirectory, options: TCliCommandOptions) => {
59
63
  if (!projectDirectory) {
60
64
  cli.outputHelp();
@@ -73,18 +77,23 @@ const run = () => {
73
77
 
74
78
  const taskOptions = await processOptions(projectDirectory, options);
75
79
 
80
+ const shouldInstallDependencies =
81
+ !options.skipInstall ||
82
+ // TODO: remove once we manage to ensure the package manager is installed, for example via Corepack.
83
+ options.packageManager === 'pnpm';
84
+
76
85
  const taskList = new Listr(
77
86
  [
78
87
  tasks.downloadTemplate(taskOptions),
79
88
  tasks.updatePackageJson(taskOptions, releaseVersion),
80
89
  tasks.updateCustomApplicationConfig(taskOptions),
81
90
  tasks.updateApplicationConstants(taskOptions),
82
- !options.skipInstall && tasks.installDependencies(taskOptions),
91
+ shouldInstallDependencies && tasks.installDependencies(taskOptions),
83
92
  ].filter(Boolean) as ListrTask[]
84
93
  );
85
94
 
86
95
  await taskList.run();
87
- const useYarn = shouldUseYarn();
96
+ const packageManager = getPreferredPackageManager(taskOptions);
88
97
 
89
98
  console.log('');
90
99
  console.log(
@@ -93,10 +102,10 @@ const run = () => {
93
102
  console.log('');
94
103
  console.log(`To get started:`);
95
104
  console.log(`$ cd ${taskOptions.projectDirectoryName}`);
96
- if (options.skipInstall) {
97
- console.log(`$ ${useYarn ? 'yarn' : 'npm'} install`);
105
+ if (!shouldInstallDependencies) {
106
+ console.log(`$ ${packageManager} install`);
98
107
  }
99
- console.log(`$ ${useYarn ? 'yarn' : 'npm'} start`);
108
+ console.log(`$ ${packageManager} start`);
100
109
  console.log('');
101
110
  console.log(
102
111
  `Visit https://docs.commercetools.com/custom-applications for more info about developing Custom Applications. Enjoy 🚀`
@@ -91,6 +91,7 @@ async function processOptions(
91
91
  tagOrBranchVersion,
92
92
  entryPointUriPath,
93
93
  initialProjectKey,
94
+ packageManager: options.packageManager,
94
95
  };
95
96
  }
96
97
 
@@ -1,14 +1,14 @@
1
1
  import execa from 'execa';
2
2
  import type { ListrTask } from 'listr2';
3
3
  import type { TCliTaskOptions } from '../types';
4
- import { shouldUseYarn } from '../utils';
4
+ import { getPreferredPackageManager } from '../utils';
5
5
 
6
6
  function installDependencies(options: TCliTaskOptions): ListrTask {
7
7
  return {
8
8
  title: 'Installing dependencies (this might take a while)',
9
9
  task: () => {
10
- const useYarn = shouldUseYarn();
11
- const packageManager = useYarn ? 'yarn' : 'npm';
10
+ const packageManager = getPreferredPackageManager(options);
11
+
12
12
  // TODO: we could check for min yarn/npm versions
13
13
  // See https://github.com/facebook/create-react-app/blob/0f4781e8507249ce29a9ac1409fece67c1a53c38/packages/create-react-app/createReactApp.js#L225-L254
14
14
  return execa(packageManager, ['install'], {
@@ -3,7 +3,7 @@ import os from 'os';
3
3
  import path from 'path';
4
4
  import type { ListrTask } from 'listr2';
5
5
  import type { TCliTaskOptions } from '../types';
6
- import { slugify } from '../utils';
6
+ import { getPreferredPackageManager, slugify } from '../utils';
7
7
 
8
8
  const replaceApplicationKitPackageVersionWith = (
9
9
  releaseVersion: string,
@@ -37,6 +37,7 @@ function updatePackageJson(
37
37
  const appPackageJson = JSON.parse(
38
38
  fs.readFileSync(packageJsonPath, { encoding: 'utf8' })
39
39
  );
40
+ const packageManager = getPreferredPackageManager(options);
40
41
 
41
42
  const updatedAppPackageJson = Object.assign({}, appPackageJson, {
42
43
  version: '1.0.0',
@@ -55,6 +56,12 @@ function updatePackageJson(
55
56
  releaseVersion,
56
57
  appPackageJson.devDependencies
57
58
  ),
59
+ scripts: {
60
+ ...appPackageJson.scripts,
61
+ 'start:prod:local': appPackageJson.scripts[
62
+ 'start:prod:local'
63
+ ].replace('yarn', packageManager),
64
+ },
58
65
  });
59
66
 
60
67
  fs.writeFileSync(