shoplazza-cli 1.0.5 → 1.0.6

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 (63) hide show
  1. package/bin/shoplazza +3 -14
  2. package/lib/app/api/api.js +93 -0
  3. package/lib/app/api/request.js +72 -0
  4. package/lib/app/commands/build.js +74 -53
  5. package/lib/app/commands/create.js +100 -0
  6. package/lib/app/commands/deploy.js +39 -210
  7. package/lib/app/commands/list.js +31 -0
  8. package/lib/app/commands/serve.js +118 -0
  9. package/lib/app/commands/versions.js +44 -0
  10. package/lib/app/index.js +17 -29
  11. package/lib/app/template/basic-app/.ci/k8s.yaml +4 -0
  12. package/lib/app/template/basic-app/README.md +84 -0
  13. package/lib/app/template/basic-app/package.json +15 -0
  14. package/lib/app/template/basic-app/theme-app/assets/index.css +4 -0
  15. package/lib/app/template/basic-app/theme-app/blocks/index.liquid +14 -0
  16. package/lib/app/template/basic-app/theme-app/snippets/index.liquid +8 -0
  17. package/lib/app/template/basic-app/theme-app.config.json +4 -0
  18. package/lib/app/template/embed-app/.ci/k8s.yaml +4 -0
  19. package/lib/app/template/embed-app/README.md +84 -0
  20. package/lib/app/template/embed-app/package.json +15 -0
  21. package/lib/app/template/embed-app/theme-app/assets/index.css +4 -0
  22. package/lib/app/template/embed-app/theme-app/blocks/index.liquid +15 -0
  23. package/lib/app/template/embed-app/theme-app/locales/ar-SA.json +3 -0
  24. package/lib/app/template/embed-app/theme-app/locales/de-DE.json +3 -0
  25. package/lib/app/template/embed-app/theme-app/locales/en-US.json +3 -0
  26. package/lib/app/template/embed-app/theme-app/locales/es-ES.json +3 -0
  27. package/lib/app/template/embed-app/theme-app/locales/fr-FR.json +3 -0
  28. package/lib/app/template/embed-app/theme-app/locales/id-ID.json +3 -0
  29. package/lib/app/template/embed-app/theme-app/locales/it-IT.json +3 -0
  30. package/lib/app/template/embed-app/theme-app/locales/ja-JP.json +3 -0
  31. package/lib/app/template/embed-app/theme-app/locales/ko-KR.json +3 -0
  32. package/lib/app/template/embed-app/theme-app/locales/nl-NL.json +3 -0
  33. package/lib/app/template/embed-app/theme-app/locales/pl-PL.json +3 -0
  34. package/lib/app/template/embed-app/theme-app/locales/pt-PT.json +3 -0
  35. package/lib/app/template/embed-app/theme-app/locales/ru-RU.json +3 -0
  36. package/lib/app/template/embed-app/theme-app/locales/th-TH.json +3 -0
  37. package/lib/app/template/embed-app/theme-app/locales/zh-CN.json +3 -0
  38. package/lib/app/template/embed-app/theme-app/locales/zh-TW.json +3 -0
  39. package/lib/app/template/embed-app/theme-app/snippets/index.liquid +8 -0
  40. package/lib/app/template/embed-app/theme-app.config.json +4 -0
  41. package/lib/app/utils/config.js +29 -0
  42. package/lib/app/utils/index.js +220 -0
  43. package/lib/checkout/api.js +11 -34
  44. package/lib/checkout/build/plugin/vite-plugin-transform-extension-html.js +1 -1
  45. package/lib/checkout/dev/index.js +2 -1
  46. package/lib/commands/login.js +1 -1
  47. package/lib/commands/logout.js +1 -1
  48. package/lib/openAPI/index.js +21 -10
  49. package/lib/oss.js +102 -0
  50. package/lib/utils.js +52 -0
  51. package/package.json +2 -2
  52. package/lib/app/commands/generate.js +0 -50
  53. package/lib/app/commands/publish.js +0 -52
  54. package/lib/app/extensions/index.js +0 -13
  55. package/lib/app/extensions/theme-app.js +0 -103
  56. package/lib/app/inquirers/version.js +0 -131
  57. /package/lib/{app → common}/constants.js +0 -0
  58. /package/lib/{app → common}/db/partner.js +0 -0
  59. /package/lib/{app → common}/inquirers/choose-app.js +0 -0
  60. /package/lib/{app → common}/inquirers/choose-partner.js +0 -0
  61. /package/lib/{app → common}/log.js +0 -0
  62. /package/lib/{app → common}/login.js +0 -0
  63. /package/lib/{app → common}/logout.js +0 -0
package/bin/shoplazza CHANGED
@@ -10,8 +10,7 @@ const report = require('../lib/report');
10
10
  require('../lib/tracing');
11
11
 
12
12
  const { makeCheckoutCommand } = require('../lib/checkout');
13
-
14
- const { generateExtension, deployExtension, publishExtension, buildExtension, retry } = require('../lib/app');
13
+ const { initThemeAppCommand } = require('../lib/app');
15
14
 
16
15
  Sentry.init({
17
16
  dsn: 'https://89964605acaf4db8839f2d5237396d6c@sentry.shoplazza.com/730',
@@ -113,21 +112,11 @@ theme
113
112
  .option('-t, --theme <theme>', 'The ID of the theme that you want to delete.')
114
113
  .action(require('../lib/commands/theme/delete'));
115
114
 
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
115
 
129
116
  // checkout cli
130
117
  makeCheckoutCommand(program);
118
+ // theme app cli
119
+ initThemeAppCommand(program);
131
120
 
132
121
  program.parse(process.argv);
133
122
  !program.args.length && program.help();
@@ -0,0 +1,93 @@
1
+ const { get, post, put, del, patch } = require('./request');
2
+
3
+ /**
4
+ * 获取主题列表
5
+ */
6
+ async function getThemeList() {
7
+ return get('/themes');
8
+ }
9
+
10
+ /**
11
+ * 获取店铺私有主题插件列表
12
+ * @returns
13
+ */
14
+ async function getThemeAppList() {
15
+ return [
16
+ {
17
+ name: '主题插件1',
18
+ version: '1.0.0',
19
+ description: '主题插件1的描述'
20
+ },
21
+ {
22
+ name: '主题插件2',
23
+ version: '2.0.0',
24
+ description: '主题插件2的描述'
25
+ },
26
+ {
27
+ name: '主题插件3',
28
+ version: '3.0.0',
29
+ description: '主题插件3的描述'
30
+ }
31
+ ];
32
+ // return get('/theme-extensions');
33
+ }
34
+
35
+ /**
36
+ * 创建主题插件
37
+ */
38
+ async function toCreateThemeApp(data) {
39
+ return put('/theme-extensions', data);
40
+ }
41
+
42
+ /**
43
+ * 上传主题插件
44
+ */
45
+ async function toUploadThemeApp(data) {
46
+ return patch(`theme-extensions/${data.appId}/dev-doctree`, data);
47
+ }
48
+
49
+ /**
50
+ * 获取任务状态
51
+ */
52
+ async function getTaskStatus(data) {
53
+ return get(`/theme-extensions/version-tasks/${data.taskId}`);
54
+ }
55
+
56
+ /**
57
+ * 获取主题插件版本列表
58
+ */
59
+ async function getThemeAppVersionList(data) {
60
+ return [
61
+ { version: '1.0.0', date: '2023-01-01', description: 'Initial release' },
62
+ { version: '1.1.0', date: '2023-02-01', description: 'Added new features and bug fixes' },
63
+ { version: '1.2.0', date: '2023-03-01', description: 'Fixed bugs and improved performance' },
64
+ { version: '1.3.0', date: '2023-04-01', description: 'Optimized performance and added new features' },
65
+ { version: '1.4.0', date: '2023-05-01', description: 'Fixed bugs and improved user experience' }
66
+ ];
67
+ // return get(`/theme-extensions/:${data.appId}/versions`);
68
+ }
69
+
70
+ /**
71
+ * 创建主题插件版本
72
+ */
73
+ async function toCreateThemeAppVersion(data) {
74
+ return post(`/theme-extensions/version-tasks`, data);
75
+ }
76
+
77
+ /**
78
+ * 部署主题插件
79
+ */
80
+ async function toDeployThemeApp(data) {
81
+ return post(`theme-extensions/publications`, data);
82
+ }
83
+
84
+ module.exports = {
85
+ getThemeList,
86
+ getThemeAppList,
87
+ toCreateThemeApp,
88
+ toUploadThemeApp,
89
+ getThemeAppVersionList,
90
+ toCreateThemeAppVersion,
91
+ toDeployThemeApp,
92
+ getTaskStatus
93
+ };
@@ -0,0 +1,72 @@
1
+ const instance = require('../../openAPI/index');
2
+
3
+ /**
4
+ * 基础的 API 请求方法
5
+ * @param {string} method 请求方法 (GET, POST, PUT, DELETE)
6
+ * @param {string} url 请求的路径
7
+ * @param {Object} data 请求的数据(适用于 POST、PUT)
8
+ * @param {Object} params 请求的 URL 参数(适用于 GET)
9
+ */
10
+ async function request({ method, url, data, params }) {
11
+ const response = await instance({
12
+ method,
13
+ url,
14
+ data,
15
+ params
16
+ });
17
+ return response; // 返回请求结果
18
+ }
19
+
20
+ /**
21
+ * GET 请求封装
22
+ * @param {string} url 请求的路径
23
+ * @param {Object} params 请求的 URL 参数
24
+ */
25
+ async function get(url, params) {
26
+ return request({ method: 'get', url, params });
27
+ }
28
+
29
+ /**
30
+ * POST 请求封装
31
+ * @param {string} url 请求的路径
32
+ * @param {Object} data 请求的数据
33
+ */
34
+ async function post(url, data) {
35
+ return request({ method: 'post', url, data });
36
+ }
37
+
38
+ /**
39
+ * PUT 请求封装
40
+ * @param {string} url 请求的路径
41
+ * @param {Object} data 请求的数据
42
+ */
43
+ async function put(url, data) {
44
+ return request({ method: 'put', url, data });
45
+ }
46
+
47
+ /**
48
+ * DELETE 请求封装
49
+ * @param {string} url 请求的路径
50
+ * @param {Object} params 请求的 URL 参数
51
+ */
52
+ async function del(url, params) {
53
+ return request({ method: 'delete', url, params });
54
+ }
55
+
56
+ /**
57
+ * PATCH 请求封装
58
+ * @param {string} url 请求的路径
59
+ * @param {Object} data 请求的数据
60
+ */
61
+ async function patch(url, data) {
62
+ return request({ method: 'patch', url, data });
63
+ }
64
+
65
+
66
+ module.exports = {
67
+ get,
68
+ post,
69
+ put,
70
+ del,
71
+ patch
72
+ };
@@ -1,66 +1,87 @@
1
1
  const chalk = require('chalk');
2
- const ora = require('ora');
3
- const fs = require('fs-extra');
2
+ const inquirer = require('inquirer');
4
3
  const path = require('path');
4
+ const { getThemeAppConfig, compareVersions } = require('../utils');
5
+ const { getThemeAppVersionList, toCreateThemeAppVersion } = require('../api/api');
5
6
 
6
- const { zipTheme } = require('../../utils');
7
- const { line, done } = require('../log');
8
- const { buildThemeAssets } = require('../extensions/theme-app');
9
-
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');
7
+ async function createNewVersion(appId, version, description) {
8
+ const themeAppPath = path.resolve(WORKSPACE_PATH, 'theme-app');
9
+ const { zipPath, zipName } = await compress(themeAppPath);
10
+ const zipOssUrl = await uploadOss(zipPath, zipName);
11
+ const taskId = await toCreateThemeAppVersion({
12
+ extension_id: appId,
13
+ version,
14
+ description,
15
+ resource_url: zipOssUrl
16
+ });
17
+ while (true) {
18
+ const res = await getTaskStatus({ taskId });
19
+ if (res.data?.task?.state === 1) {
20
+ console.log(chalk.green(`[SUCCESS] Theme app pushed successfully.`));
21
+ break;
22
+ } else if (res.data?.task?.state === 2) {
23
+ console.error(chalk.red(`[ERROR] Theme app push failed: ${res.data?.task?.error_message}`));
24
+ break;
25
+ } else {
26
+ console.log(chalk.yellow(`[WAITING] Theme app push in progress...`));
27
+ await new Promise((resolve) => setTimeout(resolve, 1000));
27
28
  }
28
- return true;
29
- } catch (e) {
30
- console.log(e);
31
29
  }
32
- };
30
+ }
33
31
 
34
- const getZipName = () => {
35
- return path.basename(process.cwd());
36
- };
32
+ /**
33
+ * 交互式命令行
34
+ */
35
+ async function usePrompt() {
36
+ const versionList = await getThemeAppVersionList();
37
+ const latestVersion = versionList[versionList.length - 1];
37
38
 
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
- };
39
+ console.log(
40
+ chalk.cyanBright(`\n🔍 Latest version: `) +
41
+ chalk.yellow(`${latestVersion.version} `) +
42
+ chalk.white(`(${latestVersion.description})`) +
43
+ chalk.gray(` [Released on: ${latestVersion.date}]`)
44
+ );
45
+
46
+ return inquirer.prompt([
47
+ {
48
+ type: 'input',
49
+ name: 'newVersion',
50
+ message: 'Enter the new version number:',
51
+ validate: (newVersion) => {
52
+ if (!/^[0-9]+\.[0-9]+\.[0-9]+$/.test(newVersion)) {
53
+ return chalk.red('❌ Version must follow the format X.Y.Z (e.g., 1.0.0).');
54
+ }
55
+ if (compareVersions(newVersion, latestVersion.version) !== 1) {
56
+ return chalk.red('❌ Version must be greater than the latest version.');
57
+ }
58
+ return true;
59
+ }
60
+ },
61
+ {
62
+ type: 'input',
63
+ name: 'description',
64
+ message: 'Enter the description for this version:',
65
+ validate: (description) => {
66
+ if (!description.trim()) {
67
+ return chalk.red('❌ Description cannot be empty.');
68
+ }
69
+ return true;
70
+ }
71
+ }
72
+ ]);
73
+ }
43
74
 
44
- const zipApp = async () => {
45
- const spinner = ora(chalk.cyan('Building your app and generate zip package ...')).start();
75
+ async function build() {
46
76
  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);
77
+ const { appId } = await getThemeAppConfig();
78
+ const { newVersion, description } = await usePrompt();
79
+ await createNewVersion(appId, newVersion, description);
80
+ } catch (error) {
81
+ console.error(chalk.red(`[ERROR IN BUILD] ${error.message}`));
53
82
  }
54
- };
55
-
56
- const build = async () => {
57
- line();
58
- (await clearManifest()) && (await buildThemeAssets('./assets')) && (await zipApp());
59
- done();
60
- line();
61
- };
83
+ }
62
84
 
63
85
  module.exports = {
64
- build,
65
- getZipName
86
+ build
66
87
  };
@@ -0,0 +1,100 @@
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 app 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 app project name:',
24
+ prefix: '*',
25
+ validate: (projectName) => {
26
+ if (!projectName.trim()) {
27
+ return 'Project name cannot be empty';
28
+ }
29
+ return true;
30
+ }
31
+ }
32
+ ]);
33
+ }
34
+
35
+ /**
36
+ * 初始化项目
37
+ */
38
+ async function initProj(projectPath, projectName) {
39
+ // 替换占位符 key文件相对路径 value替换的内容
40
+ const replacementConfig = {
41
+ 'package.json': { projectName },
42
+ 'theme-app/blocks/index.liquid': { projectName },
43
+ 'theme-app.config.json': { projectName }
44
+ };
45
+ for (const [filePath, replacements] of Object.entries(replacementConfig)) {
46
+ const fullPath = path.resolve(projectPath, filePath);
47
+ await replacePlaceholders(fullPath, replacements);
48
+ }
49
+
50
+ // 重命名文件
51
+ const filesToRename = ['assets/index.css', 'blocks/index.liquid', 'snippets/index.liquid'];
52
+ for (const file of filesToRename) {
53
+ const filePath = path.resolve(projectPath, 'theme-app', file);
54
+ await renameFile(filePath, `${projectName}${file.includes('snippets') ? '_snippet' : ''}`);
55
+ }
56
+ }
57
+
58
+ /**
59
+ * 控制台提示信息
60
+ */
61
+ function consoleTips(projectName) {
62
+ console.log(chalk.green(`Theme app project "${projectName}" has been successfully created.`));
63
+ console.log(chalk.bold(`To get started:\n`));
64
+ console.log(` ${chalk.cyan(`cd ${projectName}`)}`);
65
+ console.log(` ${chalk.cyan(`npm start`)}\n`);
66
+ console.log(chalk.greenBright(`Happy coding! 🎉🎉🎉`));
67
+ }
68
+
69
+ /**
70
+ * 创建项目
71
+ */
72
+ async function _create(themeAppDescription, projectName) {
73
+ const projectPath = path.resolve(WORKSPACE_PATH, projectName);
74
+ const projectExists = await fsExtra.pathExists(projectPath);
75
+
76
+ if (projectExists) {
77
+ throw new Error(`Project directory "${projectName}" already exists.`);
78
+ }
79
+ await fsExtra.ensureDir(projectPath);
80
+ const templatePath =
81
+ THEME_APP_TYPE.BASIC_APP.description === themeAppDescription
82
+ ? THEME_APP_TYPE.BASIC_APP.templatePath
83
+ : THEME_APP_TYPE.EMBEDS_APP.templatePath;
84
+ await fsExtra.copy(templatePath, projectPath);
85
+ await initProj(projectPath, projectName);
86
+ consoleTips(projectName);
87
+ }
88
+
89
+ async function create() {
90
+ try {
91
+ const { themeAppType, projectName } = await usePrompt();
92
+ await _create(themeAppType, projectName);
93
+ } catch (error) {
94
+ console.error(chalk.red(`[ERROR IN CREATE] ${error.message}`));
95
+ }
96
+ }
97
+
98
+ module.exports = {
99
+ create
100
+ };