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/lib/oss.js ADDED
@@ -0,0 +1,102 @@
1
+ const axios = require('axios');
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+ const FormData = require('form-data');
5
+ const chalk = require('chalk');
6
+ const loading = require('loading-cli');
7
+
8
+ /**
9
+ * 获取OSS上传功能,适用于上传文件到店铺的OSS服务器
10
+ * @param {string} accessToken - 用于认证的token
11
+ * @param {string} storeDomain - 店铺域名
12
+ * @param {string} storeDomainTips - 店铺域名为空时的提示信息
13
+ * @returns {{ uploadOss: (filePath: string) => Promise<string> }} 返回一个对象,包含上传文件到OSS的函数
14
+ * @example
15
+ * const { uploadOss } = useOss('your-access-token', 'your-store-domain', 'Store domain is required');
16
+ * const ossUrl = await uploadOss('/path/to/file.txt');
17
+ * console.log(ossUrl); // 输出文件的 OSS 地址
18
+ */
19
+ function useOss(accessToken, storeDomain, storeDomainTips) {
20
+ const ins = axios.create({
21
+ headers: { 'Access-Token': accessToken }
22
+ });
23
+
24
+ ins.interceptors.request.use(
25
+ (config) => {
26
+ if (!storeDomain) {
27
+ console.error(
28
+ // chalk.red(`\n\n`)
29
+ chalk.red(`\n${storeDomainTips}\n`)
30
+ );
31
+ process.exit(-1);
32
+ }
33
+ if (config.url.startsWith('/checkout_extensions')) {
34
+ config.baseURL = `https://${storeDomain}/openapi`;
35
+ }
36
+ return config;
37
+ },
38
+ (error) => {
39
+ console.error(chalk.red(`[REQUEST ERROR] ${error.message}`));
40
+ return Promise.reject(error);
41
+ }
42
+ );
43
+
44
+ ins.interceptors.response.use(
45
+ (response) => {
46
+ return response.data;
47
+ },
48
+ (error) => {
49
+ console.log(error);
50
+
51
+ console.error(
52
+ chalk.red(
53
+ `[RESPONSE ERROR] ${error.response.status} ${error.response.config.baseURL}${error.response.config.url}`
54
+ )
55
+ );
56
+ return Promise.reject(error);
57
+ }
58
+ );
59
+
60
+ /**
61
+ * 上传文件到OSS
62
+ * @param {string} filePath - 要上传的文件路径
63
+ * @returns {Promise<string>} 返回文件的 OSS 地址
64
+ * @throws {Error} 如果上传失败或发生未知错误,抛出错误
65
+ * @example
66
+ * const ossUrl = await uploadOss('/path/to/file.txt');
67
+ * console.log(ossUrl); // 输出文件的 OSS 地址
68
+ */
69
+ async function uploadOss(filePath) {
70
+ console.log(chalk.green('正在上传文件到OSS...',filePath));
71
+ let fileName = path.basename(filePath);
72
+ const key = 'chick-extension/' + fileName;
73
+ const form = new FormData();
74
+ const url = await ins.get(`/checkout_extensions/file/sign?key=${key}`).then(async (data) => {
75
+ const url = data.write_host;
76
+ form.append('policy', data.policy);
77
+ form.append('OSSAccessKeyId', data.access_id);
78
+ form.append('success_action_status', 200);
79
+ form.append('signature', data.sign);
80
+ form.append('x-oss-forbid-overwrite', 'true');
81
+ form.append('key', key);
82
+ form.append('file', fs.createReadStream(filePath));
83
+ await ins.post(`https:${url}`, form, { headers: form.getHeaders() }).catch((error) => {
84
+ if (error.response?.data.includes('<Code>FileAlreadyExists</Code>')) {
85
+ console.log(chalk.yellow('[WARN] The current file already exists, not need to upload.'));
86
+ return;
87
+ } else {
88
+ return Promise.reject(error);
89
+ }
90
+ });
91
+ return `${data.read_host}${data.read_host.endsWith('/') ? '' : '/'}${key}`;
92
+ });
93
+ loading('succeed upload').succeed();
94
+ return url;
95
+ }
96
+
97
+ return { uploadOss };
98
+ }
99
+
100
+ module.exports = {
101
+ useOss
102
+ };
package/lib/utils.js CHANGED
@@ -2,6 +2,8 @@ const path = require('path');
2
2
  const fs = require('fs-extra');
3
3
  const AdmZip = require('adm-zip');
4
4
  const chalk = require('chalk');
5
+ const chokidar = require('chokidar');
6
+ const fsExtra = require('fs-extra');
5
7
 
6
8
  const {
7
9
  SSO_AUTH_URL,
@@ -111,3 +113,53 @@ const SSO_AUTH_URL_MAP = {
111
113
  exports.getSSOAuthUrl = (store) => {
112
114
  return SSO_AUTH_URL_MAP[getEnv(store)];
113
115
  };
116
+
117
+ /**
118
+ * 监听文件夹变化
119
+ * @param {string} workspace - 需要监听的文件夹路径
120
+ * @param {Object} options - 配置项
121
+ * @param {Function} [options.onAdd] - 文件添加时的回调函数,接收文件路径作为参数
122
+ * @param {Function} [options.onChange] - 文件更改时的回调函数,接收文件路径作为参数
123
+ * @param {Function} [options.onDelete] - 文件删除时的回调函数,接收文件路径作为参数
124
+ * @param {any} [options.xxx] - 其他chokidar.watch配置项
125
+ * @returns {Function} - 取消监听的函数
126
+ */
127
+ exports.initWatcher = (workspace, options = {}) => {
128
+ if (!fsExtra.pathExistsSync(workspace)) {
129
+ console.error(chalk.red(`[ERROR] "${workspace}" does not exist.`));
130
+ return;
131
+ }
132
+ const defaultOptions = {
133
+ persistent: true,
134
+ ignoreInitial: true
135
+ };
136
+ const { onAdd = () => {}, onChange = () => {}, onDelete = () => {}, ...restOptions } = options;
137
+ const watcherOptions = { ...defaultOptions, ...restOptions };
138
+ const watcher = chokidar.watch(workspace, watcherOptions);
139
+
140
+ watcher.on('add', (filePath) => {
141
+ console.log(chalk.cyan(`[ADD] ${filePath}`));
142
+ onAdd(filePath);
143
+ });
144
+
145
+ watcher.on('change', (filePath) => {
146
+ console.log(chalk.yellow(`[CHANGE] ${filePath}`));
147
+ onChange(filePath);
148
+ });
149
+
150
+ watcher.on('unlink', (filePath) => {
151
+ console.log(chalk.red(`[DELETE] ${filePath}`));
152
+ onDelete(filePath);
153
+ });
154
+
155
+ watcher.on('error', (error) => {
156
+ console.error(chalk.red(`[ERROR] ${error}`));
157
+ });
158
+
159
+ const stopWatcher = () => {
160
+ console.log(chalk.green('[INFO] Stopping watcher...'));
161
+ watcher.close();
162
+ };
163
+
164
+ return stopWatcher;
165
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shoplazza-cli",
3
- "version": "1.0.5",
3
+ "version": "1.0.6",
4
4
  "description": "",
5
5
  "main": "bin/shoplazza",
6
6
  "engines": {
@@ -26,7 +26,6 @@
26
26
  "@sentry/tracing": "^6.19.7",
27
27
  "adm-zip": "^0.5.9",
28
28
  "ali-oss": "^6.17.1",
29
- "archiver": "^5.3.1",
30
29
  "axios": "^1.6.8",
31
30
  "better-sqlite3": "^11.5.0",
32
31
  "chalk": "^4.1.2",
@@ -64,6 +63,7 @@
64
63
  "semver": "~7.3.5",
65
64
  "serve-static": "^1.15.0",
66
65
  "spark-md5": "^3.0.2",
66
+ "table": "^6.9.0",
67
67
  "uglify-js": "^3.17.4",
68
68
  "update-notifier": "^5.1.0",
69
69
  "vite": "~4.3.1",
@@ -1,50 +0,0 @@
1
- const inquirer = require('inquirer');
2
- const { set, PARTNER_KEYS } = require('../db/partner');
3
- const { themeAppHandler, checkoutUIHandler } = require('../extensions');
4
- const { line } = require('../log');
5
-
6
- const EXTENSION_TYPES = {
7
- CHECKOUT_UI: 'checkout_ui',
8
- THEME_APP: 'theme_app'
9
- };
10
-
11
- const generate = async () => {
12
- line();
13
- const answer = await inquirer.prompt([
14
- {
15
- type: 'list',
16
- name: 'extensionType',
17
- message: 'Choose your extension type ↓',
18
- default: EXTENSION_TYPES.THEME_APP,
19
- prefix: '*',
20
- choices: [
21
- {
22
- name: 'Theme App Extension',
23
- value: EXTENSION_TYPES.THEME_APP
24
- }
25
- ]
26
- },
27
- {
28
- type: 'input',
29
- name: 'extensionName',
30
- message: 'Input your extesion name: ',
31
- default: 'my-extension',
32
- prefix: '*'
33
- }
34
- ]);
35
- line();
36
-
37
- set({ [PARTNER_KEYS.EXTENSION_TYPE]: answer.extensionType });
38
-
39
- if (answer.extensionType === EXTENSION_TYPES.THEME_APP) {
40
- await themeAppHandler(answer.extensionName);
41
- } else if (answer.extensionType === EXTENSION_TYPES.CHECKOUT_UI) {
42
- await checkoutUIHandler(answer.extensionName);
43
- }
44
-
45
- line();
46
- };
47
-
48
- module.exports = {
49
- generate
50
- };
@@ -1,52 +0,0 @@
1
- const { default: axios } = require('axios');
2
- const { selectVersion } = require('../inquirers/version');
3
- const { PARNTER_URL } = require('../constants');
4
- const { get, getValue, PARTNER_KEYS } = require('../db/partner');
5
- const ora = require('ora');
6
- const chalk = require('chalk');
7
- const { getZipName } = require('./build');
8
- const { done, line } = require('../log');
9
-
10
- const publishVersion = async ({ version, appId, versionId, extensionId }) => {
11
- const spinner = ora(chalk.cyan('Waiting publish ...'));
12
-
13
- try {
14
- const partnerId = getValue(PARTNER_KEYS.PARTNER_ID);
15
- if (!partnerId) {
16
- spinner.fail(chalk.red('Please choose a partner first!'));
17
- return;
18
- }
19
-
20
- const url = `${PARNTER_URL}/api/partner/apps/${appId}/theme_extensions/${extensionId}/publications`;
21
- await axios.post(
22
- url,
23
- {
24
- name: getZipName(),
25
- version_id: versionId,
26
- type: 'enable'
27
- },
28
- {
29
- headers: {
30
- cookie: `awesomev2=${getValue(PARTNER_KEYS.SESSION_ID)};`,
31
- 'x-shoplazza-partner-id': partnerId
32
- }
33
- }
34
- );
35
-
36
- spinner.succeed();
37
- return true;
38
- } catch (e) {
39
- spinner.fail(e.message || e);
40
- }
41
- };
42
-
43
- const publish = async () => {
44
- const versionData = await selectVersion();
45
- await publishVersion(versionData);
46
- done();
47
- line();
48
- };
49
-
50
- module.exports = {
51
- publish
52
- };
@@ -1,13 +0,0 @@
1
- const { generateThemeDirectory } = require('./theme-app');
2
- const { done } = require('../log');
3
-
4
- const themeAppHandler = async (dirname = './') => {
5
- (await generateThemeDirectory(dirname)) && done();
6
- };
7
-
8
- const checkoutUIHandler = async () => {};
9
-
10
- module.exports = {
11
- themeAppHandler,
12
- checkoutUIHandler
13
- };
@@ -1,103 +0,0 @@
1
- const chalk = require('chalk');
2
- const ora = require('ora');
3
- const fs = require('fs-extra');
4
- const glob = require('glob');
5
- const repo = require('download-git-repo');
6
- const CleanCSS = require('clean-css');
7
- const UglifyJS = require('uglify-js');
8
-
9
- const crypto = require('crypto');
10
- const path = require('path');
11
- const { line } = require('../log');
12
-
13
- const rev = (input) => crypto.createHash('md5').update(input).digest('hex').slice(0, 8);
14
- const cleanCSS = new CleanCSS({
15
- advanced: false,
16
- aggressiveMerging: false
17
- });
18
-
19
- const generateThemeDirectory = async (dirname = './') => {
20
- const spinner = ora(chalk.cyan(`Waiting generate your theme app project...`)).start();
21
- try {
22
- await new Promise((resolve, reject) => {
23
- repo('Shoplazza/theme-extension-template', dirname, {}, (err) => {
24
- if (err) {
25
- reject(JSON.stringify(err));
26
- } else {
27
- resolve(true);
28
- }
29
- });
30
- });
31
-
32
- spinner.succeed(chalk.cyan(`Generate app successfully`));
33
- return true;
34
- } catch (e) {
35
- spinner.fail();
36
- console.log(chalk.red(e));
37
- }
38
- };
39
-
40
- const buildThemeAssets = async (outputDir = './dist', pathPrefix = '.') => {
41
- line();
42
- ora(chalk.cyan(`Prepare Build assets ...`)).succeed();
43
-
44
- const manifest = {};
45
-
46
- // build assets css
47
- const spinner = ora(chalk.cyan(`Build assets ...`)).start();
48
- try {
49
- const cssPathnames = glob.sync('./assets/*.css');
50
- const jsPathnames = glob.sync('./assets/*.js');
51
-
52
- const pathnames = [...cssPathnames, ...jsPathnames];
53
- for (const pathname of pathnames) {
54
- const filename = path.basename(pathname);
55
- const extname = path.extname(pathname);
56
- const fileContent = fs.readFileSync(pathname, 'utf8');
57
- const hash = rev(fileContent);
58
-
59
- let minified = '';
60
- let filenameWithHash = '';
61
- if (extname.includes('css')) {
62
- minified = cleanCSS.minify(fileContent);
63
- if (minified.errors.length) {
64
- spinner.fail(minified.errors);
65
- process.exit(-1);
66
- }
67
- filenameWithHash = `${filename.split('.')[0]}-${hash}.css`;
68
- const assetsPath = `${outputDir}/${filenameWithHash}`;
69
- fs.ensureFileSync(assetsPath);
70
- fs.writeFileSync(assetsPath, minified.styles);
71
- } else if (extname.includes('js')) {
72
- minified = UglifyJS.minify(fileContent);
73
- if (minified.error) {
74
- log.error(minified.error);
75
- process.exit(-1);
76
- }
77
- filenameWithHash = `${filename.split('.')[0]}-${hash}.js`;
78
- const assetsPath = `${outputDir}/${filenameWithHash}`;
79
- fs.ensureFileSync(assetsPath);
80
- fs.writeFileSync(assetsPath, minified.code);
81
- }
82
-
83
- manifest[filename] = pathPrefix + '/assets/' + filenameWithHash;
84
- }
85
-
86
- fs.writeFileSync(`./assets-manifest.json`, JSON.stringify(manifest));
87
- spinner.succeed(chalk.cyan(`Build assets done`));
88
- return true;
89
- } catch (e) {
90
- spinner.fail(chalk.red(e.message));
91
- }
92
-
93
- // build assets js
94
- const jsSpinner = ora(chalk.cyan(`Build js ...`)).start();
95
- try {
96
- jsSpinner.succeed(chalk.cyan(`Build js done`));
97
- } catch (e) {}
98
- };
99
-
100
- module.exports = {
101
- generateThemeDirectory,
102
- buildThemeAssets
103
- };
@@ -1,131 +0,0 @@
1
- const axios = require('axios');
2
- const chalk = require('chalk');
3
- const inquirer = require('inquirer');
4
-
5
- const { PARNTER_URL } = require('../constants');
6
- const { getApp, getValue, PARTNER_KEYS } = require('../db/partner');
7
- const { line } = require('../log');
8
-
9
- const requestLastVersion = async () => {
10
- try {
11
- const app = getApp();
12
- if (!app) {
13
- return;
14
- }
15
- const extensionId = getValue(PARTNER_KEYS.EXTENSION_ID);
16
- const partnerId = getValue(PARTNER_KEYS.PARTNER_ID);
17
-
18
- const url = `${PARNTER_URL}/api/partner/apps/${app.uid}/theme_extensions/${extensionId}/last_version`;
19
- const res = await axios.get(url, {
20
- headers: {
21
- cookie: `awesomev2=${getValue(PARTNER_KEYS.SESSION_ID)};`,
22
- 'x-shoplazza-partner-id': partnerId
23
- },
24
- validateStatus(status) {
25
- if (status === 404) {
26
- return true;
27
- }
28
- return status >= 200 && status < 299;
29
- }
30
- });
31
-
32
- if (res.data.errors) {
33
- return;
34
- }
35
-
36
- return res.data.version;
37
- } catch (e) {
38
- console.log(chalk.red(e.message || e));
39
- console.log(chalk.red(JSON.stringify(e.response?.data)));
40
- }
41
- };
42
-
43
- const requestVersionsData = async (params = {}) => {
44
- try {
45
- const app = getApp();
46
- if (!app) {
47
- return;
48
- }
49
- const extensionId = getValue(PARTNER_KEYS.EXTENSION_ID);
50
- const partnerId = getValue(PARTNER_KEYS.PARTNER_ID);
51
-
52
- const url = `${PARNTER_URL}/api/partner/apps/${app.uid}/theme_extensions/${extensionId}/versions`;
53
- const res = await axios.get(url, {
54
- params,
55
- headers: {
56
- cookie: `awesomev2=${getValue(PARTNER_KEYS.SESSION_ID)};`,
57
- 'x-shoplazza-partner-id': partnerId
58
- },
59
- validateStatus(status) {
60
- if (status === 404) {
61
- return true;
62
- }
63
- return status >= 200 && status < 299;
64
- }
65
- });
66
-
67
- if (res.data.errors) {
68
- throw new Error(JSON.stringify(res.data.errors));
69
- }
70
-
71
- return res.data.data;
72
- } catch (e) {
73
- console.log(chalk.red(e.message || e));
74
- console.log(chalk.red(JSON.stringify(e.response?.data)));
75
- }
76
- };
77
-
78
- const inputVersion = async () => {
79
- line();
80
- const lastVersionData = await requestLastVersion({ limit: 1 });
81
- const hint = lastVersionData ? chalk.gray(`(last version: ${lastVersionData.version})`) : '';
82
-
83
- const answer = await inquirer.prompt([
84
- {
85
- type: 'input',
86
- name: 'extensionVersion',
87
- message: `${chalk.blue('Input your extension version')} ${hint}:`,
88
- prefix: `${chalk.green('⭐️')}`
89
- }
90
- ]);
91
-
92
- return answer['extensionVersion'];
93
- };
94
-
95
- const selectVersion = async () => {
96
- line();
97
-
98
- const versionDataList = await requestVersionsData();
99
- if (!versionDataList) {
100
- console.log(chalk.red('Please deploy a version first!'));
101
- line();
102
- }
103
-
104
- const answer = await inquirer.prompt([
105
- {
106
- type: 'list',
107
- name: 'versionData',
108
- message: `${chalk.blue('Choose a version to publish:')}`,
109
- default: versionDataList[0].version,
110
- prefix: `${chalk.blue('🐱')}`,
111
- choices: versionDataList.map((v) => {
112
- return {
113
- name: `${v.version} ${v.published ? chalk.green('published') : ''}`,
114
- value: {
115
- version: v.version,
116
- appId: v.app_id,
117
- versionId: v.version_id,
118
- extensionId: v.extension_id
119
- }
120
- };
121
- })
122
- }
123
- ]);
124
-
125
- return answer['versionData'];
126
- };
127
-
128
- module.exports = {
129
- inputVersion,
130
- selectVersion
131
- };
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes