shoplazza-cli 1.0.7 → 1.0.8

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.
Files changed (124) hide show
  1. package/README.md +83 -2
  2. package/bin/shoplazza +8 -15
  3. package/lib/app/api/index.js +96 -0
  4. package/lib/app/commands/build.js +101 -53
  5. package/lib/app/commands/connect.js +73 -0
  6. package/lib/app/commands/create.js +105 -0
  7. package/lib/app/commands/deploy.js +45 -208
  8. package/lib/app/commands/list.js +35 -0
  9. package/lib/app/commands/release.js +59 -0
  10. package/lib/app/commands/serve.js +172 -0
  11. package/lib/app/commands/versions.js +55 -0
  12. package/lib/app/index.js +22 -30
  13. package/lib/app/template/basic-app/README.md +125 -0
  14. package/lib/app/template/basic-app/extension.config.json +4 -0
  15. package/lib/app/template/basic-app/package.json +18 -0
  16. package/lib/app/template/basic-app/theme-app/assets/index.css +4 -0
  17. package/lib/app/template/basic-app/theme-app/assets-manifest.json +1 -0
  18. package/lib/app/template/basic-app/theme-app/blocks/index.liquid +16 -0
  19. package/lib/app/template/basic-app/theme-app/locales/ar-SA.json +1 -0
  20. package/lib/app/template/basic-app/theme-app/locales/de-DE.json +1 -0
  21. package/lib/app/template/basic-app/theme-app/locales/en-US.json +6 -0
  22. package/lib/app/template/basic-app/theme-app/locales/es-ES.json +1 -0
  23. package/lib/app/template/basic-app/theme-app/locales/fr-FR.json +1 -0
  24. package/lib/app/template/basic-app/theme-app/locales/id-ID.json +1 -0
  25. package/lib/app/template/basic-app/theme-app/locales/it-IT.json +1 -0
  26. package/lib/app/template/basic-app/theme-app/locales/ja-JP.json +1 -0
  27. package/lib/app/template/basic-app/theme-app/locales/ko-KR.json +1 -0
  28. package/lib/app/template/basic-app/theme-app/locales/nl-NL.json +1 -0
  29. package/lib/app/template/basic-app/theme-app/locales/pl-PL.json +1 -0
  30. package/lib/app/template/basic-app/theme-app/locales/pt-PT.json +1 -0
  31. package/lib/app/template/basic-app/theme-app/locales/ru-RU.json +1 -0
  32. package/lib/app/template/basic-app/theme-app/locales/th-TH.json +1 -0
  33. package/lib/app/template/basic-app/theme-app/locales/zh-CN.json +6 -0
  34. package/lib/app/template/basic-app/theme-app/locales/zh-TW.json +1 -0
  35. package/lib/app/template/basic-app/theme-app/snippets/index.liquid +8 -0
  36. package/lib/app/template/embed-app/README.md +125 -0
  37. package/lib/app/template/embed-app/extension.config.json +4 -0
  38. package/lib/app/template/embed-app/package.json +18 -0
  39. package/lib/app/template/embed-app/theme-app/assets-manifest.json +1 -0
  40. package/lib/app/template/embed-app/theme-app/blocks/index.liquid +18 -0
  41. package/lib/app/template/embed-app/theme-app/locales/ar-SA.json +1 -0
  42. package/lib/app/template/embed-app/theme-app/locales/de-DE.json +1 -0
  43. package/lib/app/template/embed-app/theme-app/locales/en-US.json +6 -0
  44. package/lib/app/template/embed-app/theme-app/locales/es-ES.json +1 -0
  45. package/lib/app/template/embed-app/theme-app/locales/fr-FR.json +1 -0
  46. package/lib/app/template/embed-app/theme-app/locales/id-ID.json +1 -0
  47. package/lib/app/template/embed-app/theme-app/locales/it-IT.json +1 -0
  48. package/lib/app/template/embed-app/theme-app/locales/ja-JP.json +1 -0
  49. package/lib/app/template/embed-app/theme-app/locales/ko-KR.json +1 -0
  50. package/lib/app/template/embed-app/theme-app/locales/nl-NL.json +1 -0
  51. package/lib/app/template/embed-app/theme-app/locales/pl-PL.json +1 -0
  52. package/lib/app/template/embed-app/theme-app/locales/pt-PT.json +1 -0
  53. package/lib/app/template/embed-app/theme-app/locales/ru-RU.json +1 -0
  54. package/lib/app/template/embed-app/theme-app/locales/th-TH.json +1 -0
  55. package/lib/app/template/embed-app/theme-app/locales/zh-CN.json +6 -0
  56. package/lib/app/template/embed-app/theme-app/locales/zh-TW.json +1 -0
  57. package/lib/app/template/embed-app/theme-app/snippets/index.liquid +8 -0
  58. package/lib/app/template/embed-app/theme-app/snippets/index_css.liquid +6 -0
  59. package/lib/app/utils/config.js +32 -0
  60. package/lib/app/utils/index.js +213 -0
  61. package/lib/auth/getCode.js +2 -2
  62. package/lib/auth/index.js +2 -2
  63. package/lib/check.js +28 -0
  64. package/lib/checkout/api.js +12 -36
  65. package/lib/checkout/build.js +1 -1
  66. package/lib/checkout/create.js +1 -1
  67. package/lib/checkout/deploy.js +1 -1
  68. package/lib/checkout/dev/index.js +1 -1
  69. package/lib/checkout/fields.js +1 -1
  70. package/lib/checkout/preview.js +1 -1
  71. package/lib/checkout/pull.js +1 -0
  72. package/lib/checkout/push.js +1 -1
  73. package/lib/checkout/undeploy.js +1 -1
  74. package/lib/checkout/util.js +1 -1
  75. package/lib/checkout/verify.js +1 -1
  76. package/lib/commands/login.js +1 -1
  77. package/lib/commands/logout.js +1 -1
  78. package/lib/commands/theme/delete.js +1 -1
  79. package/lib/commands/theme/package.js +1 -1
  80. package/lib/commands/theme/publish.js +1 -1
  81. package/lib/commands/theme/pull.js +1 -1
  82. package/lib/commands/theme/push.js +1 -1
  83. package/lib/commands/theme/serve.js +2 -2
  84. package/lib/{app → common}/login.js +1 -1
  85. package/lib/function/bin/index.js +20 -0
  86. package/lib/function/bin/javy/javy-arm-linux-v5.0.1 +0 -0
  87. package/lib/function/bin/javy/javy-arm-macos-v5.0.1 +0 -0
  88. package/lib/function/bin/javy/javy-x86_64-linux-v5.0.1 +0 -0
  89. package/lib/function/bin/javy/javy-x86_64-macos-v5.0.1 +0 -0
  90. package/lib/function/bin/javy/javy-x86_64-windows-v5.0.1 +0 -0
  91. package/lib/function/commands/compile.js +42 -0
  92. package/lib/function/commands/create.js +77 -0
  93. package/lib/function/commands/list.js +18 -0
  94. package/lib/function/commands/release.js +69 -0
  95. package/lib/function/index.js +24 -0
  96. package/lib/function/template/js/README.md +37 -0
  97. package/lib/function/template/js/_gitignore +4 -0
  98. package/lib/function/template/js/extension.config.json +5 -0
  99. package/lib/function/template/js/package.json +17 -0
  100. package/lib/function/template/js/src/index.js +64 -0
  101. package/lib/function/utils.js +29 -0
  102. package/lib/openAPI/api.js +1 -1
  103. package/lib/openAPI/index.js +21 -11
  104. package/lib/oss.js +99 -0
  105. package/lib/partner-api/axios.js +67 -0
  106. package/lib/partner-api/index.js +79 -0
  107. package/lib/{checkout → utils}/console.js +3 -2
  108. package/lib/utils/env.js +17 -0
  109. package/lib/utils/file.js +48 -0
  110. package/lib/utils/platform.js +37 -0
  111. package/lib/{utils.js → utils/utils.js} +52 -0
  112. package/package.json +2 -2
  113. package/lib/app/commands/generate.js +0 -50
  114. package/lib/app/commands/publish.js +0 -52
  115. package/lib/app/extensions/index.js +0 -13
  116. package/lib/app/extensions/theme-app.js +0 -103
  117. package/lib/app/inquirers/version.js +0 -131
  118. /package/lib/{app → common}/constants.js +0 -0
  119. /package/lib/{app → common}/db/partner.js +0 -0
  120. /package/lib/{app → common}/inquirers/choose-app.js +0 -0
  121. /package/lib/{app → common}/inquirers/choose-partner.js +0 -0
  122. /package/lib/{app → common}/log.js +0 -0
  123. /package/lib/{app → common}/logout.js +0 -0
  124. /package/lib/{config.js → utils/config.js} +0 -0
package/README.md CHANGED
@@ -56,9 +56,9 @@ You can use Shoplazza CLI to develop checkout, customized checkout page as you
56
56
  #### Step 1: 创建一个项目模板
57
57
 
58
58
  创建过程会要求你输入店铺地址和token,token可以在后台中应用->管理私有应用->创建应用 获得。创建过程还要求你输入一个extension名字,一个项目下支持存在多个extension,这是为了方便管理,它们互不影响。
59
- ```
59
+ ```bash
60
60
  shoplazza checkout create
61
- // 安装依赖
61
+ # 安装依赖
62
62
  npm i
63
63
  ```
64
64
 
@@ -164,6 +164,87 @@ $ shoplazza theme list
164
164
 
165
165
  > You might want to use a theme's ID to pull, push, publish, or delete a theme using Shoplazza CLI.
166
166
 
167
+
168
+
169
+ ## Theme Extension Develop
170
+
171
+ ### Overview
172
+
173
+ Theme Extension is a method provided by Shoplazza for extending and customizing themes. Developers can use Theme Extensions to enhance and tailor theme pages.
174
+
175
+ ### Create an Extension
176
+
177
+ ```bash
178
+ shoplazza te create
179
+ ```
180
+
181
+ When creating a new Theme Extension, you will be prompted to choose between two types: **Basic Extension** and **Embed Extension**.
182
+
183
+ - **Basic Extension**: Allows modifying insertion positions via the theme editor.
184
+ - **Embed Extension**: Insertion positions are hard-coded within the Extension.
185
+
186
+ ### Start Development
187
+
188
+ ```bash
189
+ cd your-project # Navigate to the project directory
190
+ shoplazza te serve
191
+ ```
192
+
193
+ Since the Extension is displayed on theme pages, so you need to select a theme for preview. A preview link will be provided after selection.
194
+
195
+ - **First link**: Redirects to the theme editor. You can add the Extension to theme pages here. After adding, save the changes to preview on the client side.
196
+ - **Second link**: Redirects to the storefront preview page. After adding the Extension via the editor, any local code updates will take effect upon refreshing the storefront page.
197
+
198
+ ### Build for Production
199
+
200
+ ```bash
201
+ shoplazza te build
202
+ ```
203
+
204
+ Generates a deployable production build from the current project code. Multiple builds can be created, and one can be selected during deployment.
205
+
206
+ ### Deploy an Extension
207
+
208
+ To deploy an Extension to your store:
209
+
210
+ ```bash
211
+ shoplazza te deploy
212
+ ```
213
+
214
+ Once deployed, the Extension will be live in the store.
215
+
216
+ ### Connect Extension to an APP
217
+
218
+ An APP is the primary extension method of Shoplazza platform. A Theme Extension is part of an APP, and one APP can connect multiple Theme Extensions. To conect your Extension to an APP:
219
+
220
+ ```bash
221
+ shoplazza te connect
222
+ ```
223
+
224
+ You will be prompted to enter the APP's `client_id` and `client_secret`, which can be obtained from the Shoplazza Partner Dashboard.
225
+
226
+ ### Release an Extension
227
+ To update the Extension version connected to your app, run the release command:
228
+
229
+ ```bash
230
+ shoplazza te release
231
+ ```
232
+
233
+ The release will be based on the current production version of your store. Once published, the changes will take effect for **all stores that have installed your app**.
234
+
235
+ ### CLI Command Guide
236
+
237
+ | Command | Description |
238
+ | :-----------------------------------: | :--------------------------------------------: |
239
+ | `shoplazza te create` | Create a new Theme Extension project |
240
+ | `shoplazza te serve` | Start a local development server |
241
+ | `shoplazza te build` | Build a production version of the Extension |
242
+ | `shoplazza te versions` | View production version list of an Extension |
243
+ | `shoplazza te list` | List private Extensions in the store |
244
+ | `shoplazza te deploy` | Deploy a production build to the current store |
245
+ | `shoplazza te connect <extension-id>` | Bind an Extension to an APP |
246
+ | `shoplazza te release` | Publish an Extension to its bound APP |
247
+
167
248
  ## Core commands
168
249
 
169
250
  ### help
package/bin/shoplazza CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  process.noDeprecation = true; // 全局禁用废弃警告
4
+ require('../lib/check');
4
5
  const { program } = require('commander');
5
6
  const updateNotifier = require('update-notifier');
6
7
  const Sentry = require('@sentry/node');
@@ -10,8 +11,8 @@ const report = require('../lib/report');
10
11
  require('../lib/tracing');
11
12
 
12
13
  const { makeCheckoutCommand } = require('../lib/checkout');
13
-
14
- const { generateExtension, deployExtension, publishExtension, buildExtension, retry } = require('../lib/app');
14
+ const { initThemeAppCommand } = require('../lib/app');
15
+ const { makeFunctionCommand } = require('../lib/function');
15
16
 
16
17
  Sentry.init({
17
18
  dsn: 'https://89964605acaf4db8839f2d5237396d6c@sentry.shoplazza.com/730',
@@ -30,6 +31,7 @@ updateNotifier({ pkg }).notify({
30
31
  isGlobal: true
31
32
  });
32
33
  program.usage('[command] [options]');
34
+ program.option('--debug <ent>', 'debug mode')
33
35
 
34
36
  program.command('version').description('Welcome to the Shoplazza CLI').action(require('../lib/commands/version'));
35
37
  program.description('Welcome to the Shoplazza CLI').option('-v --version').action(require('../lib/commands/version'));
@@ -113,21 +115,12 @@ theme
113
115
  .option('-t, --theme <theme>', 'The ID of the theme that you want to delete.')
114
116
  .action(require('../lib/commands/theme/delete'));
115
117
 
116
- const app = program.command('app').description('Shoplazza app extensions cli');
117
-
118
- app.command('generate').command('extension').description('Generate your extension').action(generateExtension);
119
- app.command('deploy').command('extension').description('Deploy your extension to cdn').action(deployExtension);
120
- app.command('publish').command('extension').description('Publish your extension').action(publishExtension);
121
- app.command('build').description('Build your extension').action(buildExtension);
122
- app
123
- .command('retry')
124
- .description('Retry to do some tasks.')
125
- .option('-p, --partner', 'Retry get and choose your partner list.')
126
- .option('-a, --app', 'Retry get and choose your app list.')
127
- .action(retry);
128
-
129
118
  // checkout cli
130
119
  makeCheckoutCommand(program);
120
+ // theme app cli
121
+ initThemeAppCommand(program);
122
+ // function cli
123
+ makeFunctionCommand(program);
131
124
 
132
125
  program.parse(process.argv);
133
126
  !program.args.length && program.help();
@@ -0,0 +1,96 @@
1
+ const instance = require('../../openAPI/index');
2
+ const request = require('../../partner-api/axios');
3
+
4
+ /**
5
+ * 获取主题列表
6
+ */
7
+ async function getThemeList() {
8
+ return instance.get('/themes');
9
+ }
10
+
11
+ /**
12
+ * 获取店铺私有主题插件列表
13
+ * @returns
14
+ */
15
+ async function getThemeAppList() {
16
+ return instance.get('/theme-extensions');
17
+ }
18
+
19
+ /**
20
+ * 创建主题插件 or 更新主题插件信息
21
+ */
22
+ async function toCreateThemeApp(data) {
23
+ return instance.put('/theme-extensions', data);
24
+ }
25
+
26
+ /**
27
+ * 上传主题插件
28
+ */
29
+ async function toUploadThemeApp(extension_id, data) {
30
+ return instance.patch(`/theme-extensions/${extension_id}/dev-doctree`, data);
31
+ }
32
+
33
+ /**
34
+ * 新增文件
35
+ */
36
+ async function toCreateFile(extension_id, data) {
37
+ return instance.post(`/theme-extensions/${extension_id}/dev-doc`, data);
38
+ }
39
+
40
+ /**
41
+ * 更新文件
42
+ */
43
+ async function toUpdateFile(extension_id, data) {
44
+ return instance.patch(`/theme-extensions/${extension_id}/dev-doc`, data);
45
+ }
46
+
47
+ /**
48
+ * 删除文件
49
+ */
50
+ async function toDeleteFile(extension_id, params) {
51
+ return instance.delete(`/theme-extensions/${extension_id}/dev-doc`, {
52
+ params
53
+ });
54
+ }
55
+
56
+ /**
57
+ * 获取任务状态
58
+ */
59
+ async function getTaskStatus(data) {
60
+ return instance.get(`/theme-extensions/version-tasks/${data.taskId}`);
61
+ }
62
+
63
+ /**
64
+ * 获取主题插件版本列表
65
+ */
66
+ async function getThemeAppVersionList(extension_id) {
67
+ return instance.get(`/theme-extensions/${extension_id}/versions`);
68
+ }
69
+
70
+ /**
71
+ * 创建主题插件版本
72
+ */
73
+ async function toCreateThemeAppVersion(data) {
74
+ return instance.post(`/theme-extensions/version-tasks`, data);
75
+ }
76
+
77
+ /**
78
+ * 部署主题插件
79
+ */
80
+ async function toDeployThemeApp(data) {
81
+ return instance.post(`/theme-extensions/publications`, data);
82
+ }
83
+
84
+ module.exports = {
85
+ getThemeList,
86
+ getThemeAppList,
87
+ toCreateThemeApp,
88
+ toCreateFile,
89
+ toUpdateFile,
90
+ toDeleteFile,
91
+ toUploadThemeApp,
92
+ getThemeAppVersionList,
93
+ toCreateThemeAppVersion,
94
+ toDeployThemeApp,
95
+ getTaskStatus,
96
+ };
@@ -1,66 +1,114 @@
1
1
  const chalk = require('chalk');
2
+ const inquirer = require('inquirer');
2
3
  const ora = require('ora');
3
- const fs = require('fs-extra');
4
- const path = require('path');
4
+ const fsExtra = require('fs-extra');
5
+ const { getThemeExtensionConfig, compareVersions, compress } = require('../utils');
6
+ const { THEME_APP_DIR_PATH, EXCHANGE_TOKEN, STORE_DOMAIN } = require('../utils/config');
7
+ const { getThemeAppVersionList, toCreateThemeAppVersion, getTaskStatus } = require('../api');
8
+ const { useOss } = require('../../oss');
5
9
 
6
- const { zipTheme } = require('../../utils');
7
- const { line, done } = require('../log');
8
- const { buildThemeAssets } = require('../extensions/theme-app');
10
+ async function createNewVersion(extensionId, version, description) {
11
+ const spinner = ora('Start building a new version...').start();
12
+ const { zipPath, zipName } = await compress(THEME_APP_DIR_PATH);
13
+ const { uploadOss } = useOss(
14
+ EXCHANGE_TOKEN,
15
+ STORE_DOMAIN,
16
+ `✗ No store found. Please run ${chalk.cyan('shoplazza login')} to login to a specific store.`
17
+ );
18
+ const zipOssUrl = await uploadOss(zipPath, zipName);
19
+ // 上传完成删除压缩包
20
+ fsExtra.removeSync(zipPath);
21
+ const res = await toCreateThemeAppVersion({
22
+ extension_id: extensionId,
23
+ version,
24
+ exts: description,
25
+ resource_url: zipOssUrl
26
+ });
27
+ const taskId = res.data?.task_id;
9
28
 
10
- const isFileExist = (path) => {
11
- return fs.existsSync(path);
12
- };
13
-
14
- const clearManifest = async () => {
15
- try {
16
- if (isFileExist('./assets-manifest.json') && isFileExist('./assets')) {
17
- const mainfest = JSON.parse(fs.readFileSync('./assets-manifest.json', { encoding: 'utf-8' }));
18
- await Promise.all(
19
- Object.values(mainfest).map((path) => {
20
- if (isFileExist(path)) {
21
- return fs.unlink(path);
22
- }
23
- return Promise.resolve(true);
24
- })
25
- );
26
- await fs.unlink('./assets-manifest.json');
27
- }
28
- return true;
29
- } catch (e) {
30
- console.log(e);
29
+ if (!taskId) {
30
+ spinner.fail(chalk.red(`Failed to build a new version!`));
31
+ throw new Error('taskId is required');
31
32
  }
32
- };
33
+ return new Promise((resolve, reject) => {
34
+ const timer = setInterval(async () => {
35
+ const res = await getTaskStatus({ taskId });
36
+ if (res.data?.state === 1) {
37
+ clearInterval(timer);
38
+ spinner.succeed(chalk.green(`Successfully build a new version(v${version})!`));
39
+ resolve();
40
+ } else if (res.data?.state === 2) {
41
+ clearInterval(timer);
42
+ spinner.fail(chalk.red(`Failed to build a new version!`));
43
+ reject(new Error(res.data?.message));
44
+ } else {
45
+ spinner.text = chalk.blue(`[WAITING] Building a new version...`);
46
+ }
47
+ }, 1000);
48
+ });
49
+ }
33
50
 
34
- const getZipName = () => {
35
- return path.basename(process.cwd());
36
- };
51
+ /**
52
+ * 交互式命令行
53
+ */
54
+ async function usePrompt(extensionId) {
55
+ const res = await getThemeAppVersionList(extensionId);
37
56
 
38
- const generateZip = async (name) => {
39
- const zipPath = `${process.cwd()}/${name}.zip`;
40
- isFileExist(zipPath) && (await fs.unlink(zipPath));
41
- zipTheme(process.cwd(), name);
42
- };
57
+ const latestVersion = res.data?.data?.[0];
43
58
 
44
- const zipApp = async () => {
45
- const spinner = ora(chalk.cyan('Building your app and generate zip package ...')).start();
46
- try {
47
- const zipName = getZipName();
48
- await generateZip(zipName);
49
- spinner.succeed(chalk.cyan(`Success to build your app and generate zip package: ${chalk.green(`${zipName}.zip`)}`));
50
- return true;
51
- } catch (e) {
52
- spinner.fail(e.message);
59
+ if (latestVersion) {
60
+ console.log(
61
+ chalk.cyanBright(`\n🔍 Latest version: `) +
62
+ chalk.yellow(`${latestVersion.version} `) +
63
+ chalk.white(`(${latestVersion.exts})`) +
64
+ chalk.gray(` [Released on: ${latestVersion.created_at}]`)
65
+ );
53
66
  }
54
- };
55
67
 
56
- const build = async () => {
57
- line();
58
- (await clearManifest()) && (await buildThemeAssets('./assets')) && (await zipApp());
59
- done();
60
- line();
61
- };
68
+ return inquirer.prompt([
69
+ {
70
+ type: 'input',
71
+ name: 'newVersion',
72
+ message: 'Enter the new version number(e.g., 1.0.0):',
73
+ validate: (newVersion) => {
74
+ if (!/^[0-9]+\.[0-9]+\.[0-9]+$/.test(newVersion)) {
75
+ return chalk.red('❌ Version must follow the format X.Y.Z (e.g., 1.0.0).');
76
+ }
77
+ if (latestVersion) {
78
+ if (compareVersions(newVersion, latestVersion.version) !== 1) {
79
+ return chalk.red('❌ Version must be greater than the latest version.');
80
+ }
81
+ }
82
+ return true;
83
+ }
84
+ },
85
+ {
86
+ type: 'input',
87
+ name: 'description',
88
+ message: 'Enter the description for this version:',
89
+ validate: (description) => {
90
+ if (!description.trim()) {
91
+ return chalk.red('❌ Description cannot be empty.');
92
+ }
93
+ return true;
94
+ }
95
+ }
96
+ ]);
97
+ }
98
+
99
+ async function build() {
100
+ try {
101
+ const { extensionId } = await getThemeExtensionConfig();
102
+ if (!extensionId) {
103
+ throw new Error('ExtensionId is empty, please use `serve` command first.');
104
+ }
105
+ const { newVersion, description } = await usePrompt(extensionId);
106
+ await createNewVersion(extensionId, newVersion, description);
107
+ } catch (error) {
108
+ console.error(chalk.red(`[ERROR IN BUILD] ${error.message}`));
109
+ }
110
+ }
62
111
 
63
112
  module.exports = {
64
- build,
65
- getZipName
113
+ build
66
114
  };
@@ -0,0 +1,73 @@
1
+ const chalk = require('chalk');
2
+ const inquirer = require('inquirer');
3
+ const { getThemeExtensionConfig, setThemeExtensionConfig } = require('../utils');
4
+ const { toConnectApp } = require('../../partner-api/index');
5
+
6
+ /**
7
+ * 交互式命令行
8
+ */
9
+ async function usePrompt() {
10
+ return await inquirer.prompt([
11
+ {
12
+ type: 'input',
13
+ name: 'clientId',
14
+ message: 'Please enter the shoplazza app Client ID:',
15
+ prefix: '*',
16
+ validate: (clientId) => {
17
+ if (!clientId.trim()) {
18
+ return 'Client ID cannot be empty';
19
+ }
20
+ return true;
21
+ }
22
+ },
23
+ {
24
+ type: 'input',
25
+ name: 'clientSecret',
26
+ message: 'Please enter the shoplazza app Client Secret:',
27
+ prefix: '*',
28
+ validate: (clientSecret) => {
29
+ if (!clientSecret.trim()) {
30
+ return 'Client Secret cannot be empty';
31
+ }
32
+ return true;
33
+ }
34
+ }
35
+ ]);
36
+ }
37
+
38
+ async function connectToApp() {
39
+ try {
40
+ let { extensionId, appId, accessToken } = await getThemeExtensionConfig();
41
+ if (!extensionId) {
42
+ throw new Error('ExtensionId is empty, please use `serve` command first.');
43
+ }
44
+
45
+ if (!accessToken) {
46
+ const result = await usePrompt();
47
+ appId = result.clientId;
48
+ appSecret = result.clientSecret;
49
+ await setThemeExtensionConfig({
50
+ appId,
51
+ appSecret
52
+ });
53
+ }
54
+
55
+ const res = await toConnectApp({
56
+ extensionId,
57
+ appId
58
+ });
59
+
60
+ if (+res.data?.code === 200) {
61
+ console.log(chalk.green('Connected to the Shoplazza app successfully!'));
62
+ console.log(chalk.cyan('You can use the `release` command to release a specific version to the Shoplazza app.'));
63
+ } else if (+res.data?.code === 400) {
64
+ console.log(chalk.green(res.data?.message));
65
+ }
66
+ } catch (error) {
67
+ console.error(chalk.red(`[ERROR IN CONNECT] ${error.message}`));
68
+ }
69
+ }
70
+
71
+ module.exports = {
72
+ connect: connectToApp
73
+ };
@@ -0,0 +1,105 @@
1
+ const inquirer = require('inquirer');
2
+ const path = require('path');
3
+ const fsExtra = require('fs-extra');
4
+ const chalk = require('chalk');
5
+ const { WORKSPACE_PATH, THEME_APP_TYPE } = require('../utils/config');
6
+ const { replacePlaceholders, renameFile } = require('../utils');
7
+
8
+ /**
9
+ * 交互式命令行
10
+ */
11
+ async function usePrompt() {
12
+ return await inquirer.prompt([
13
+ {
14
+ type: 'list',
15
+ name: 'themeAppType',
16
+ message: 'Select theme extension type:',
17
+ choices: [THEME_APP_TYPE.BASIC_APP.description, THEME_APP_TYPE.EMBEDS_APP.description],
18
+ prefix: '*'
19
+ },
20
+ {
21
+ type: 'input',
22
+ name: 'projectName',
23
+ message: 'Please enter the theme extension project name:',
24
+ prefix: '*',
25
+ validate: (projectName) => {
26
+ if (!projectName.trim()) {
27
+ return 'Project name cannot be empty';
28
+ }
29
+ // 禁止包含空格
30
+ if (projectName.includes(' ')) {
31
+ return 'Project name cannot contain spaces';
32
+ }
33
+ return true;
34
+ }
35
+ }
36
+ ]);
37
+ }
38
+
39
+ /**
40
+ * 初始化项目
41
+ */
42
+ async function initProj(projectPath, projectName, isBasicApp = false) {
43
+ // 替换占位符 key文件相对路径 value替换的内容
44
+ const replacementConfig = {
45
+ 'package.json': { projectName },
46
+ 'theme-app/blocks/index.liquid': { projectName },
47
+ 'extension.config.json': { projectName }
48
+ };
49
+ for (const [filePath, replacements] of Object.entries(replacementConfig)) {
50
+ const fullPath = path.resolve(projectPath, filePath);
51
+ await replacePlaceholders(fullPath, replacements);
52
+ }
53
+
54
+ // 重命名文件
55
+ const filesToRename = [
56
+ isBasicApp ? 'assets/index.css' : 'snippets/index_css.liquid',
57
+ 'blocks/index.liquid',
58
+ 'snippets/index.liquid'
59
+ ];
60
+ for (const file of filesToRename) {
61
+ const filePath = path.resolve(projectPath, 'theme-app', file);
62
+ await renameFile(filePath, `${projectName}${filePath.includes('_css') ? '_css' : ''}`);
63
+ }
64
+ }
65
+
66
+ /**
67
+ * 控制台提示信息
68
+ */
69
+ function consoleTips(projectName) {
70
+ console.log(chalk.green(`Theme extension project "${projectName}" has been successfully created.`));
71
+ console.log(chalk.bold(`To get started:\n`));
72
+ console.log(` ${chalk.cyan(`cd ${projectName}`)}`);
73
+ console.log(` ${chalk.cyan(`npm start`)}\n`);
74
+ console.log(chalk.greenBright(`Happy coding! 🎉🎉🎉`));
75
+ }
76
+
77
+ /**
78
+ * 创建项目
79
+ */
80
+ async function _create(themeAppDescription, projectName) {
81
+ const projectPath = path.resolve(WORKSPACE_PATH, projectName);
82
+
83
+ if (fsExtra.pathExistsSync(projectPath)) {
84
+ throw new Error(`Project directory "${projectName}" already exists.`);
85
+ }
86
+ await fsExtra.ensureDir(projectPath);
87
+ const isBasicApp = themeAppDescription === THEME_APP_TYPE.BASIC_APP.description;
88
+ const templatePath = isBasicApp ? THEME_APP_TYPE.BASIC_APP.templatePath : THEME_APP_TYPE.EMBEDS_APP.templatePath;
89
+ await fsExtra.copy(templatePath, projectPath);
90
+ await initProj(projectPath, projectName, isBasicApp);
91
+ consoleTips(projectName);
92
+ }
93
+
94
+ async function create() {
95
+ try {
96
+ const { themeAppType, projectName } = await usePrompt();
97
+ await _create(themeAppType, projectName);
98
+ } catch (error) {
99
+ console.error(chalk.red(`[ERROR IN CREATE] ${error.message}`));
100
+ }
101
+ }
102
+
103
+ module.exports = {
104
+ create
105
+ };