@bigcommerce/stencil-cli 3.8.0 → 3.8.4

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/CHANGELOG.md CHANGED
@@ -1,3 +1,20 @@
1
+ ### 3.8.4 (2022-01-13)
2
+
3
+ - fix: `stencil init` command including `apiHost` option is now recognized ([830](https://github.com/bigcommerce/stencil-cli/pull/830))
4
+ - feat: `stencil push` command allows to push a theme to multiple channels ([825](https://github.com/bigcommerce/stencil-cli/pull/825))
5
+
6
+ ### 3.8.3 (2022-01-12)
7
+
8
+ - fix: add activate sass engine name logic ([837](https://github.com/bigcommerce/stencil-cli/pull/837))
9
+
10
+ ### 3.8.2 (2022-01-11)
11
+
12
+ - fix: strf-9600 bump stencil styles version ([832](https://github.com/bigcommerce/stencil-cli/pull/832))
13
+
14
+ ### 3.8.1 (2022-01-10)
15
+
16
+ - fix: Updated package.json to use colors 1.4.0 ([827](https://github.com/bigcommerce/stencil-cli/pull/827))
17
+
1
18
  ### 3.8.0 (2021-12-30)
2
19
 
3
20
  - build(deps): bump axios from 0.21.4 to 0.24.0 ([821](https://github.com/bigcommerce/stencil-cli/pull/821))
package/README.md CHANGED
@@ -56,7 +56,9 @@ This is useful for tracking your changes in your Theme, and is the tool we use t
56
56
 
57
57
  Run `stencil push` to bundle the local theme and upload it to your store, so it will be available in My Themes.
58
58
  To push the theme and also activate it, use `stencil push -a`. To automatically delete the oldest theme if you are at
59
- your theme limit, use `stencil push -d`. These can be used together, as `stencil push -a -d`.
59
+ your theme limit, use `stencil push -d`. These can be used together, as `stencil push -a -d`. You can apply the theme to
60
+ multiple storefronts, just specify ids of desired storefronts/channels after `-c` option `stencil push -a -c 123 456 789`.
61
+ If you want to apply theme to all available storefronts, just use `-allc` option: `stencil push -a -allc`.
60
62
 
61
63
  Run `stencil pull` to sync changes to your theme configuration from your live store. For example, if Page Builder has
62
64
  been used to change certain theme settings, this will update those settings in config.json in your theme files so you
@@ -11,6 +11,7 @@ program
11
11
  .option('-u, --url [url]', 'Store URL')
12
12
  .option('-t, --token [token]', 'Access Token')
13
13
  .option('-p, --port [port]', 'Port')
14
+ .option('-h, --apiHost [host]', 'API Host')
14
15
  .parse(process.argv);
15
16
 
16
17
  checkNodeVersion();
@@ -22,5 +23,6 @@ new StencilInit()
22
23
  normalStoreUrl: cliOptions.url,
23
24
  accessToken: cliOptions.token,
24
25
  port: cliOptions.port,
26
+ apiHost: cliOptions.apiHost,
25
27
  })
26
28
  .catch(printCliResultErrorAndExit);
@@ -15,10 +15,10 @@ program
15
15
  .option('-a, --activate [variationname]', 'specify the variation of the theme to activate')
16
16
  .option('-d, --delete', 'delete oldest private theme if upload limit reached')
17
17
  .option(
18
- '-c, --channel_id [channelId]',
19
- 'specify the channel ID of the storefront to push the theme to',
20
- parseInt,
18
+ '-c, --channel_ids <channelIds...>',
19
+ 'specify the channel IDs of the storefront to push the theme to',
21
20
  )
21
+ .option('-allc, --all_channels', 'push a theme to all available channels')
22
22
  .parse(process.argv);
23
23
 
24
24
  checkNodeVersion();
@@ -26,11 +26,12 @@ checkNodeVersion();
26
26
  const cliOptions = program.opts();
27
27
  const options = {
28
28
  apiHost: cliOptions.host,
29
- channelId: cliOptions.channel_id,
29
+ channelIds: cliOptions.channel_ids,
30
30
  bundleZipPath: cliOptions.file,
31
31
  activate: cliOptions.activate,
32
32
  saveBundleName: cliOptions.save,
33
33
  deleteOldest: cliOptions.delete,
34
+ allChannels: cliOptions.all_channels,
34
35
  };
35
36
  stencilPush(options, (err, result) => {
36
37
  if (err) {
package/constants.js CHANGED
@@ -15,17 +15,11 @@ const DEFAULT_CUSTOM_LAYOUTS_CONFIG = {
15
15
 
16
16
  /// ///////////////////////////////////////// Other //////////////////////////////////////// ///
17
17
 
18
- const DEV_API_HOST = 'https://api.service.bcdev';
19
- const INTG_API_HOST = 'https://api.integration.zone';
20
- const STG_API_HOST = 'https://api.staging.zone';
21
18
  const API_HOST = 'https://api.bigcommerce.com';
22
19
 
23
20
  module.exports = {
24
21
  PACKAGE_INFO,
25
22
  THEME_PATH,
26
23
  DEFAULT_CUSTOM_LAYOUTS_CONFIG,
27
- DEV_API_HOST,
28
- INTG_API_HOST,
29
- STG_API_HOST,
30
24
  API_HOST,
31
25
  };
@@ -4,13 +4,7 @@ const inquirerModule = require('inquirer');
4
4
 
5
5
  const serverConfigModule = require('../server/config');
6
6
  const StencilConfigManager = require('./StencilConfigManager');
7
- const {
8
- DEFAULT_CUSTOM_LAYOUTS_CONFIG,
9
- API_HOST,
10
- INTG_API_HOST,
11
- STG_API_HOST,
12
- DEV_API_HOST,
13
- } = require('../constants');
7
+ const { DEFAULT_CUSTOM_LAYOUTS_CONFIG, API_HOST } = require('../constants');
14
8
 
15
9
  class StencilInit {
16
10
  /**
@@ -71,20 +65,21 @@ class StencilInit {
71
65
  }
72
66
 
73
67
  /**
74
- * @param {{port: (number), normalStoreUrl: (string), accessToken: (string)}} stencilConfig
75
- * @returns {{port: (number), normalStoreUrl: (string), accessToken: (string)}}
68
+ * @param {{port: (number), normalStoreUrl: (string), accessToken: (string), apiHost: (string)}} stencilConfig
69
+ * @returns {{port: (number), normalStoreUrl: (string), accessToken: (string), apiHost: (string)}}
76
70
  */
77
71
  getDefaultAnswers(stencilConfig) {
78
72
  return {
79
73
  normalStoreUrl: stencilConfig.normalStoreUrl,
80
74
  accessToken: stencilConfig.accessToken,
81
75
  port: stencilConfig.port || this._serverConfig.get('/server/port'),
76
+ apiHost: API_HOST,
82
77
  };
83
78
  }
84
79
 
85
80
  /**
86
- * @param {{port: (number), normalStoreUrl: (string), accessToken: (string)}} defaultAnswers
87
- * @param {{port: (number), normalStoreUrl: (string), accessToken: (string)}} cliOptions
81
+ * @param {{port: (number), normalStoreUrl: (string), accessToken: (string), apiHost: (string)}} defaultAnswers
82
+ * @param {{port: (number), normalStoreUrl: (string), accessToken: (string), apiHost: (string)}} cliOptions
88
83
  * @returns {{object[]}}
89
84
  */
90
85
  getQuestions(defaultAnswers, cliOptions) {
@@ -110,6 +105,15 @@ class StencilInit {
110
105
  });
111
106
  }
112
107
 
108
+ if (!cliOptions.apiHost) {
109
+ prompts.push({
110
+ type: 'input',
111
+ name: 'apiHost',
112
+ message: 'What API Host would you like to use?',
113
+ default: defaultAnswers.apiHost,
114
+ });
115
+ }
116
+
113
117
  if (!cliOptions.port) {
114
118
  prompts.push({
115
119
  type: 'input',
@@ -139,27 +143,12 @@ class StencilInit {
139
143
  return questions.length ? this._inquirer.prompt(questions) : {};
140
144
  }
141
145
 
142
- apiHostFromStoreUrl(storeUrl) {
143
- let host = null;
144
- if (storeUrl !== undefined) {
145
- if (storeUrl.includes('service.bcdev')) {
146
- host = DEV_API_HOST;
147
- }
148
- if (storeUrl.includes('my-integration.zone')) {
149
- host = INTG_API_HOST;
150
- }
151
- if (storeUrl.includes('my-staging.zone')) {
152
- host = STG_API_HOST;
153
- }
154
- }
155
-
156
- if (host === null) {
157
- host = API_HOST;
158
- }
146
+ updateApiHost(stencilConfig, cliOptions) {
147
+ const host = cliOptions.apiHost || API_HOST;
159
148
 
160
149
  console.log('Set API host to: ' + host);
161
150
 
162
- return host;
151
+ return { ...stencilConfig, apiHost: host };
163
152
  }
164
153
 
165
154
  /**
@@ -169,13 +158,10 @@ class StencilInit {
169
158
  * @returns {object}
170
159
  */
171
160
  applyAnswers(stencilConfig, answers, cliOptions) {
172
- const storeUrl =
173
- answers.normalStoreUrl || cliOptions.normalStoreUrl || stencilConfig.normalStoreUrl;
174
-
161
+ const config = this.updateApiHost(stencilConfig, cliOptions);
175
162
  return {
176
163
  customLayouts: DEFAULT_CUSTOM_LAYOUTS_CONFIG,
177
- apiHost: this.apiHostFromStoreUrl(storeUrl),
178
- ...stencilConfig,
164
+ ...config,
179
165
  ...cliOptions,
180
166
  ...answers,
181
167
  };
@@ -274,14 +274,14 @@ utils.promptUserWhetherToApplyTheme = async (options) => {
274
274
  utils.getChannels = async (options) => {
275
275
  const {
276
276
  config: { accessToken },
277
- channelId,
277
+ channelIds,
278
278
  storeHash,
279
279
  applyTheme,
280
280
  } = options;
281
281
 
282
282
  const apiHost = options.apiHost || options.config.apiHost;
283
283
 
284
- if (!applyTheme || channelId) {
284
+ if (!applyTheme || channelIds) {
285
285
  return options;
286
286
  }
287
287
 
@@ -339,14 +339,19 @@ utils.getVariations = async (options) => {
339
339
  };
340
340
 
341
341
  utils.promptUserForChannel = async (options) => {
342
- const { applyTheme, channelId, channels } = options;
342
+ const { applyTheme, channelIds, channels, allChannels } = options;
343
343
 
344
- if (!applyTheme || channelId) {
344
+ if (!applyTheme || channelIds) {
345
345
  return options;
346
346
  }
347
347
 
348
- const selectedChannelId = await utils.promptUserToSelectChannel(channels);
349
- return { ...options, channelId: selectedChannelId };
348
+ if (allChannels) {
349
+ const allIds = channels.map((chanel) => chanel.channel_id);
350
+ return { ...options, channelIds: allIds };
351
+ }
352
+
353
+ const selectedChannelIds = await utils.promptUserToSelectChannel(channels);
354
+ return { ...options, channelIds: selectedChannelIds };
350
355
  };
351
356
 
352
357
  utils.promptUserToSelectChannel = async (channels) => {
@@ -356,9 +361,9 @@ utils.promptUserToSelectChannel = async (channels) => {
356
361
 
357
362
  const questions = [
358
363
  {
359
- type: 'list',
360
- name: 'channelId',
361
- message: 'Which channel would you like to use?',
364
+ type: 'checkbox',
365
+ name: 'channelIds',
366
+ message: 'Which channel(s) would you like to use?',
362
367
  choices: channels.map((channel) => ({
363
368
  name: channel.url,
364
369
  value: channel.channel_id,
@@ -367,7 +372,7 @@ utils.promptUserToSelectChannel = async (channels) => {
367
372
  ];
368
373
 
369
374
  const answer = await Inquirer.prompt(questions);
370
- return answer.channelId;
375
+ return answer.channelIds;
371
376
  };
372
377
 
373
378
  utils.promptUserForVariation = async (options) => {
@@ -414,18 +419,18 @@ utils.requestToApplyVariation = async (options) => {
414
419
  config: { accessToken },
415
420
  storeHash,
416
421
  variationId,
417
- channelId,
422
+ channelIds,
418
423
  } = options;
419
424
 
420
425
  const apiHost = options.apiHost || options.config.apiHost;
421
426
 
422
427
  if (options.applyTheme) {
423
428
  await themeApiClient.activateThemeByVariationId({
424
- accessToken,
429
+ variationId,
430
+ channelIds,
425
431
  apiHost,
426
432
  storeHash,
427
- variationId,
428
- channelId,
433
+ accessToken,
429
434
  });
430
435
  }
431
436
 
@@ -65,33 +65,36 @@ async function checkCliVersion({ storeUrl, currentCliVersion = PACKAGE_INFO.vers
65
65
  /**
66
66
  * @param {object} options
67
67
  * @param {string} options.variationId
68
- * @param {string} options.channelId
68
+ * @param {array} options.channelIds
69
69
  * @param {string} options.apiHost
70
70
  * @param {string} options.storeHash
71
71
  * @param {string} options.accessToken
72
- * @returns {Promise<any>}
72
+ * @returns {Promise<Object[]>}
73
73
  */
74
74
  async function activateThemeByVariationId({
75
75
  variationId,
76
- channelId,
76
+ channelIds,
77
77
  apiHost,
78
78
  storeHash,
79
79
  accessToken,
80
80
  }) {
81
81
  try {
82
- return await networkUtils.sendApiRequest({
83
- url: `${apiHost}/stores/${storeHash}/v3/themes/actions/activate`,
84
- headers: {
85
- 'content-type': 'application/json',
86
- },
87
- method: 'POST',
88
- accessToken,
89
- data: {
90
- variation_id: variationId,
91
- channel_id: channelId,
92
- which: 'original',
93
- },
94
- });
82
+ const promises = channelIds.map((id) =>
83
+ networkUtils.sendApiRequest({
84
+ url: `${apiHost}/stores/${storeHash}/v3/themes/actions/activate`,
85
+ headers: {
86
+ 'content-type': 'application/json',
87
+ },
88
+ method: 'POST',
89
+ accessToken,
90
+ data: {
91
+ variation_id: variationId,
92
+ channel_id: Number(id),
93
+ which: 'original',
94
+ },
95
+ }),
96
+ );
97
+ return Promise.all(promises);
95
98
  } catch (err) {
96
99
  err.name =
97
100
  err.response && err.response.status === 504