shoplazza-cli 1.0.6 → 1.0.7-beta.1
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/lib/app/api/index.js +96 -0
- package/lib/app/commands/build.js +62 -35
- package/lib/app/commands/create.js +15 -14
- package/lib/app/commands/deploy.js +19 -12
- package/lib/app/commands/list.js +15 -11
- package/lib/app/commands/serve.js +103 -50
- package/lib/app/commands/versions.js +31 -23
- package/lib/app/index.js +8 -7
- package/lib/app/template/basic-app/README.md +19 -5
- package/lib/app/template/basic-app/package.json +6 -5
- package/lib/app/template/basic-app/theme-app/assets/index.css +2 -2
- package/lib/app/template/basic-app/theme-app/assets-manifest.json +1 -0
- package/lib/app/template/basic-app/theme-app/blocks/index.liquid +6 -4
- package/lib/app/template/basic-app/theme-app/snippets/index.liquid +1 -1
- package/lib/app/template/basic-app/theme-extension.config.json +4 -0
- package/lib/app/template/embed-app/README.md +19 -6
- package/lib/app/template/embed-app/package.json +6 -5
- package/lib/app/template/embed-app/theme-app/assets-manifest.json +1 -0
- package/lib/app/template/embed-app/theme-app/blocks/index.liquid +7 -4
- package/lib/app/template/embed-app/theme-app/snippets/index.liquid +1 -1
- package/lib/app/template/embed-app/theme-app/snippets/index_css.liquid +6 -0
- package/lib/app/template/embed-app/theme-extension.config.json +4 -0
- package/lib/app/utils/config.js +7 -4
- package/lib/app/utils/index.js +10 -17
- package/lib/oss.js +0 -3
- package/lib/utils.js +1 -1
- package/package.json +1 -1
- package/lib/app/api/api.js +0 -93
- package/lib/app/api/request.js +0 -72
- package/lib/app/template/basic-app/.ci/k8s.yaml +0 -4
- package/lib/app/template/basic-app/theme-app.config.json +0 -4
- package/lib/app/template/embed-app/.ci/k8s.yaml +0 -4
- package/lib/app/template/embed-app/theme-app/assets/index.css +0 -4
- package/lib/app/template/embed-app/theme-app.config.json +0 -4
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
const instance = require('../../openAPI/index');
|
|
2
|
+
|
|
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,47 +1,69 @@
|
|
|
1
1
|
const chalk = require('chalk');
|
|
2
2
|
const inquirer = require('inquirer');
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
const {
|
|
3
|
+
const ora = require('ora');
|
|
4
|
+
const fsExtra = require('fs-extra');
|
|
5
|
+
const { getThemeAppConfig, 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');
|
|
6
9
|
|
|
7
|
-
async function createNewVersion(
|
|
8
|
-
const
|
|
9
|
-
const { zipPath, zipName } = await compress(
|
|
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
|
+
);
|
|
10
18
|
const zipOssUrl = await uploadOss(zipPath, zipName);
|
|
11
|
-
|
|
12
|
-
|
|
19
|
+
// 上传完成删除压缩包
|
|
20
|
+
fsExtra.removeSync(zipPath);
|
|
21
|
+
const res = await toCreateThemeAppVersion({
|
|
22
|
+
extension_id: extensionId,
|
|
13
23
|
version,
|
|
14
|
-
description,
|
|
24
|
+
exts: description,
|
|
15
25
|
resource_url: zipOssUrl
|
|
16
26
|
});
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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));
|
|
28
|
-
}
|
|
27
|
+
const taskId = res.data?.task_id;
|
|
28
|
+
|
|
29
|
+
if (!taskId) {
|
|
30
|
+
spinner.fail(chalk.red(`Failed to build a new version!`));
|
|
31
|
+
throw new Error('taskId is required');
|
|
29
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 built 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
|
+
});
|
|
30
49
|
}
|
|
31
50
|
|
|
32
51
|
/**
|
|
33
52
|
* 交互式命令行
|
|
34
53
|
*/
|
|
35
|
-
async function usePrompt() {
|
|
36
|
-
const
|
|
37
|
-
const latestVersion = versionList[versionList.length - 1];
|
|
54
|
+
async function usePrompt(extensionId) {
|
|
55
|
+
const res = await getThemeAppVersionList(extensionId);
|
|
38
56
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
chalk.
|
|
44
|
-
|
|
57
|
+
const latestVersion = res.data?.data?.[0];
|
|
58
|
+
|
|
59
|
+
if (latestVersion) {
|
|
60
|
+
console.log(
|
|
61
|
+
chalk.cyanBright(`\n🔍 Latest version: `) +
|
|
62
|
+
chalk.yellow(`${latestVersion.version} `) +
|
|
63
|
+
chalk.white(`(${latestVersion.description})`) +
|
|
64
|
+
chalk.gray(` [Released on: ${latestVersion.created_at}]`)
|
|
65
|
+
);
|
|
66
|
+
}
|
|
45
67
|
|
|
46
68
|
return inquirer.prompt([
|
|
47
69
|
{
|
|
@@ -52,8 +74,10 @@ async function usePrompt() {
|
|
|
52
74
|
if (!/^[0-9]+\.[0-9]+\.[0-9]+$/.test(newVersion)) {
|
|
53
75
|
return chalk.red('❌ Version must follow the format X.Y.Z (e.g., 1.0.0).');
|
|
54
76
|
}
|
|
55
|
-
if (
|
|
56
|
-
|
|
77
|
+
if (latestVersion) {
|
|
78
|
+
if (compareVersions(newVersion, latestVersion.version) !== 1) {
|
|
79
|
+
return chalk.red('❌ Version must be greater than the latest version.');
|
|
80
|
+
}
|
|
57
81
|
}
|
|
58
82
|
return true;
|
|
59
83
|
}
|
|
@@ -74,9 +98,12 @@ async function usePrompt() {
|
|
|
74
98
|
|
|
75
99
|
async function build() {
|
|
76
100
|
try {
|
|
77
|
-
const {
|
|
78
|
-
|
|
79
|
-
|
|
101
|
+
const { extensionId } = await getThemeAppConfig();
|
|
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);
|
|
80
107
|
} catch (error) {
|
|
81
108
|
console.error(chalk.red(`[ERROR IN BUILD] ${error.message}`));
|
|
82
109
|
}
|
|
@@ -13,14 +13,14 @@ async function usePrompt() {
|
|
|
13
13
|
{
|
|
14
14
|
type: 'list',
|
|
15
15
|
name: 'themeAppType',
|
|
16
|
-
message: 'Select theme
|
|
16
|
+
message: 'Select theme extension type:',
|
|
17
17
|
choices: [THEME_APP_TYPE.BASIC_APP.description, THEME_APP_TYPE.EMBEDS_APP.description],
|
|
18
18
|
prefix: '*'
|
|
19
19
|
},
|
|
20
20
|
{
|
|
21
21
|
type: 'input',
|
|
22
22
|
name: 'projectName',
|
|
23
|
-
message: 'Please enter the theme
|
|
23
|
+
message: 'Please enter the theme extension project name:',
|
|
24
24
|
prefix: '*',
|
|
25
25
|
validate: (projectName) => {
|
|
26
26
|
if (!projectName.trim()) {
|
|
@@ -35,12 +35,12 @@ async function usePrompt() {
|
|
|
35
35
|
/**
|
|
36
36
|
* 初始化项目
|
|
37
37
|
*/
|
|
38
|
-
async function initProj(projectPath, projectName) {
|
|
38
|
+
async function initProj(projectPath, projectName, isBasicApp = false) {
|
|
39
39
|
// 替换占位符 key文件相对路径 value替换的内容
|
|
40
40
|
const replacementConfig = {
|
|
41
41
|
'package.json': { projectName },
|
|
42
42
|
'theme-app/blocks/index.liquid': { projectName },
|
|
43
|
-
'theme-
|
|
43
|
+
'theme-extension.config.json': { projectName }
|
|
44
44
|
};
|
|
45
45
|
for (const [filePath, replacements] of Object.entries(replacementConfig)) {
|
|
46
46
|
const fullPath = path.resolve(projectPath, filePath);
|
|
@@ -48,10 +48,14 @@ async function initProj(projectPath, projectName) {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
// 重命名文件
|
|
51
|
-
const filesToRename = [
|
|
51
|
+
const filesToRename = [
|
|
52
|
+
isBasicApp ? 'assets/index.css' : 'snippets/index_css.liquid',
|
|
53
|
+
'blocks/index.liquid',
|
|
54
|
+
'snippets/index.liquid'
|
|
55
|
+
];
|
|
52
56
|
for (const file of filesToRename) {
|
|
53
57
|
const filePath = path.resolve(projectPath, 'theme-app', file);
|
|
54
|
-
await renameFile(filePath, `${projectName}${
|
|
58
|
+
await renameFile(filePath, `${projectName}${filePath.includes('_css') ? '_css' : ''}`);
|
|
55
59
|
}
|
|
56
60
|
}
|
|
57
61
|
|
|
@@ -59,7 +63,7 @@ async function initProj(projectPath, projectName) {
|
|
|
59
63
|
* 控制台提示信息
|
|
60
64
|
*/
|
|
61
65
|
function consoleTips(projectName) {
|
|
62
|
-
console.log(chalk.green(`Theme
|
|
66
|
+
console.log(chalk.green(`Theme extension project "${projectName}" has been successfully created.`));
|
|
63
67
|
console.log(chalk.bold(`To get started:\n`));
|
|
64
68
|
console.log(` ${chalk.cyan(`cd ${projectName}`)}`);
|
|
65
69
|
console.log(` ${chalk.cyan(`npm start`)}\n`);
|
|
@@ -71,18 +75,15 @@ function consoleTips(projectName) {
|
|
|
71
75
|
*/
|
|
72
76
|
async function _create(themeAppDescription, projectName) {
|
|
73
77
|
const projectPath = path.resolve(WORKSPACE_PATH, projectName);
|
|
74
|
-
const projectExists = await fsExtra.pathExists(projectPath);
|
|
75
78
|
|
|
76
|
-
if (
|
|
79
|
+
if (fsExtra.pathExistsSync(projectPath)) {
|
|
77
80
|
throw new Error(`Project directory "${projectName}" already exists.`);
|
|
78
81
|
}
|
|
79
82
|
await fsExtra.ensureDir(projectPath);
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
? THEME_APP_TYPE.BASIC_APP.templatePath
|
|
83
|
-
: THEME_APP_TYPE.EMBEDS_APP.templatePath;
|
|
83
|
+
const isBasicApp = themeAppDescription === THEME_APP_TYPE.BASIC_APP.description;
|
|
84
|
+
const templatePath = isBasicApp ? THEME_APP_TYPE.BASIC_APP.templatePath : THEME_APP_TYPE.EMBEDS_APP.templatePath;
|
|
84
85
|
await fsExtra.copy(templatePath, projectPath);
|
|
85
|
-
await initProj(projectPath, projectName);
|
|
86
|
+
await initProj(projectPath, projectName, isBasicApp);
|
|
86
87
|
consoleTips(projectName);
|
|
87
88
|
}
|
|
88
89
|
|
|
@@ -2,23 +2,21 @@ const inquirer = require('inquirer');
|
|
|
2
2
|
const chalk = require('chalk');
|
|
3
3
|
const ora = require('ora');
|
|
4
4
|
const { getThemeAppConfig } = require('../utils');
|
|
5
|
-
const { getThemeAppVersionList, toDeployThemeApp } = require('../api
|
|
5
|
+
const { getThemeAppVersionList, toDeployThemeApp } = require('../api');
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
async function deployVersion(appId, version) {
|
|
9
|
-
// await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
7
|
+
async function deployVersion(extensionId, versionInfo) {
|
|
10
8
|
await toDeployThemeApp({
|
|
11
|
-
extension_id:
|
|
12
|
-
|
|
9
|
+
extension_id: extensionId,
|
|
10
|
+
version_id: versionInfo.version_id,
|
|
13
11
|
type: 'enable'
|
|
14
12
|
});
|
|
15
|
-
console.log(chalk.green(`Version ${version} has been deployed successfully.`));
|
|
13
|
+
console.log(chalk.green(`Version ${versionInfo.version} has been deployed successfully.`));
|
|
16
14
|
}
|
|
17
15
|
|
|
18
16
|
async function usePrompt(versionList) {
|
|
19
17
|
const choices = versionList.map((v) => ({
|
|
20
|
-
name: `${v.version} (${v.
|
|
21
|
-
value: v.
|
|
18
|
+
name: `${v.version} (${v.exts}) - ${v.created_at}`,
|
|
19
|
+
value: v.version_id
|
|
22
20
|
}));
|
|
23
21
|
return inquirer.prompt([
|
|
24
22
|
{
|
|
@@ -32,12 +30,21 @@ async function usePrompt(versionList) {
|
|
|
32
30
|
|
|
33
31
|
async function deploy() {
|
|
34
32
|
try {
|
|
35
|
-
const {
|
|
33
|
+
const { extensionId } = await getThemeAppConfig();
|
|
34
|
+
if (!extensionId) {
|
|
35
|
+
throw new Error('ExtensionId is empty, please use `serve` command first.');
|
|
36
|
+
}
|
|
36
37
|
const spinner = ora('Fetching version list...').start();
|
|
37
|
-
const
|
|
38
|
+
const res = await getThemeAppVersionList(extensionId);
|
|
39
|
+
const versionList = res.data?.data || [];
|
|
40
|
+
if (!versionList.length) {
|
|
41
|
+
spinner.succeed('Version list loaded.');
|
|
42
|
+
throw new Error('No version found, please use `build` command to upload a version first.');
|
|
43
|
+
}
|
|
38
44
|
spinner.succeed('Version list loaded.');
|
|
39
45
|
const { selectedVersion } = await usePrompt(versionList);
|
|
40
|
-
|
|
46
|
+
const versionInfo = versionList.find((v) => v.version_id === selectedVersion);
|
|
47
|
+
await deployVersion(extensionId, versionInfo);
|
|
41
48
|
} catch (error) {
|
|
42
49
|
console.error(chalk.red(`[ERROR IN DEPLOY] ${error.message}`));
|
|
43
50
|
}
|
package/lib/app/commands/list.js
CHANGED
|
@@ -1,25 +1,29 @@
|
|
|
1
1
|
const chalk = require('chalk');
|
|
2
2
|
const ora = require('ora');
|
|
3
|
-
const {
|
|
4
|
-
const { getThemeAppList } = require('../api
|
|
3
|
+
const { renderTable } = require('../utils');
|
|
4
|
+
const { getThemeAppList } = require('../api');
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* 控制台提示
|
|
8
8
|
*/
|
|
9
9
|
function consoleTips(versionList) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
10
|
+
if (versionList.length) {
|
|
11
|
+
console.log(chalk.green(`\n📃 Your store has ${versionList.length} available theme extensions:`));
|
|
12
|
+
console.log(
|
|
13
|
+
renderTable(versionList, [
|
|
14
|
+
{ label: 'App Name', filed: 'title' },
|
|
15
|
+
{ label: 'CreateTime', filed: 'created_at', color: 'yellowBright' },
|
|
16
|
+
])
|
|
17
|
+
);
|
|
18
|
+
} else {
|
|
19
|
+
console.log(chalk.green('Your store does not have any available theme extensions.'));
|
|
20
|
+
}
|
|
18
21
|
}
|
|
19
22
|
|
|
20
23
|
async function list() {
|
|
21
24
|
try {
|
|
22
|
-
const
|
|
25
|
+
const res = await getThemeAppList();
|
|
26
|
+
const themeAppList = res.data?.data || [];
|
|
23
27
|
consoleTips(themeAppList);
|
|
24
28
|
} catch (error) {
|
|
25
29
|
console.error(chalk.red(`[ERROR IN LIST] ${error.message}`));
|
|
@@ -1,36 +1,35 @@
|
|
|
1
|
-
const path = require('path');
|
|
2
1
|
const chalk = require('chalk');
|
|
3
2
|
const inquirer = require('inquirer');
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const {
|
|
7
|
-
const {
|
|
3
|
+
const fsExtra = require('fs-extra');
|
|
4
|
+
const ora = require('ora');
|
|
5
|
+
const { STORE_DOMAIN, THEME_APP_DIR_PATH, EXCHANGE_TOKEN } = require('../utils/config');
|
|
6
|
+
const { compress, getThemeAppConfig, setThemeAppConfig, getFileInfo } = require('../utils');
|
|
7
|
+
const {
|
|
8
|
+
toCreateThemeApp,
|
|
9
|
+
toUploadThemeApp,
|
|
10
|
+
getTaskStatus,
|
|
11
|
+
getThemeList,
|
|
12
|
+
toCreateFile,
|
|
13
|
+
toUpdateFile,
|
|
14
|
+
toDeleteFile
|
|
15
|
+
} = require('../api');
|
|
16
|
+
const { watchWorkspace } = require('../../utils');
|
|
8
17
|
const { useOss } = require('../../oss');
|
|
9
18
|
|
|
10
|
-
/**
|
|
11
|
-
* 模拟上传文件到服务器
|
|
12
|
-
*/
|
|
13
|
-
async function uploadFileToServer(filePath) {
|
|
14
|
-
console.log(chalk.blue(`[UPLOAD] ${filePath} - Uploading to server...`));
|
|
15
|
-
// TODO: 替换为实际的上传逻辑(例如调用后端 API 或 WebSocket 通信)
|
|
16
|
-
await new Promise((resolve) => setTimeout(resolve, 1000)); // 模拟网络延迟
|
|
17
|
-
console.log(chalk.green(`[SUCCESS] ${filePath} - Uploaded successfully.`));
|
|
18
|
-
}
|
|
19
|
-
|
|
20
19
|
/**
|
|
21
20
|
* 控制台提示信息
|
|
22
21
|
*/
|
|
23
|
-
function consoleTips(selectedThemeId,
|
|
22
|
+
function consoleTips(selectedThemeId, extensionId) {
|
|
24
23
|
console.log(`\n======================================PREVIEW URLS======================================`);
|
|
25
24
|
console.log(chalk.cyan.bold('🔗 Admin Preview URL:'));
|
|
26
25
|
console.log(
|
|
27
|
-
chalk.blueBright(` https://${STORE_DOMAIN}/admin/card?theme_id=${selectedThemeId}&ext_debug=${
|
|
26
|
+
chalk.blueBright(` https://${STORE_DOMAIN}/admin/card?theme_id=${selectedThemeId}&ext_debug=${extensionId}\n`)
|
|
28
27
|
);
|
|
29
28
|
console.log(chalk.cyan.bold('🔗 Storefront Preview URL:'));
|
|
30
|
-
console.log(chalk.blueBright(` https://${STORE_DOMAIN}?preview_theme_id=${selectedThemeId}&ext_debug
|
|
29
|
+
console.log(chalk.blueBright(` https://${STORE_DOMAIN}?preview_theme_id=${selectedThemeId}&ext_debug=${extensionId}\n`));
|
|
31
30
|
console.log(`=====================================WATCHER RUNNING=====================================`);
|
|
32
31
|
console.log(chalk.green(`🎉 File watcher is now running!`));
|
|
33
|
-
console.log(chalk.green(`📂 Watching directory:`), chalk.blue(
|
|
32
|
+
console.log(chalk.green(`📂 Watching directory:`), chalk.blue(THEME_APP_DIR_PATH));
|
|
34
33
|
console.log(chalk.green(`✨ You can make changes to your files, and they will be processed automatically.`));
|
|
35
34
|
console.log(chalk.green(`🚀 Press Ctrl+C to stop.\n`));
|
|
36
35
|
}
|
|
@@ -38,33 +37,42 @@ function consoleTips(selectedThemeId, appId) {
|
|
|
38
37
|
/**
|
|
39
38
|
* 同步本地代码到远程服务器
|
|
40
39
|
*/
|
|
41
|
-
async function syncLocalFiles(
|
|
42
|
-
const
|
|
40
|
+
async function syncLocalFiles(extensionId) {
|
|
41
|
+
const spinner = ora('Start synchronizing local files to remote...').start();
|
|
42
|
+
const { zipPath, zipName } = await compress(THEME_APP_DIR_PATH);
|
|
43
43
|
const { uploadOss } = useOss(
|
|
44
44
|
EXCHANGE_TOKEN,
|
|
45
45
|
STORE_DOMAIN,
|
|
46
46
|
`✗ No store found. Please run ${chalk.cyan('shoplazza login')} to login to a specific store.`
|
|
47
47
|
);
|
|
48
48
|
const zipOssUrl = await uploadOss(zipPath, zipName);
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
// 上传完成删除压缩包
|
|
50
|
+
fsExtra.removeSync(zipPath);
|
|
51
|
+
const res = await toUploadThemeApp(extensionId, {
|
|
51
52
|
resource_url: zipOssUrl
|
|
52
53
|
});
|
|
53
54
|
const taskId = res.data?.task_id;
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (res.data?.task?.state === 1) {
|
|
58
|
-
console.log(chalk.green(`[SUCCESS] Theme app pushed successfully.`));
|
|
59
|
-
break;
|
|
60
|
-
} else if (res.data?.task?.state === 2) {
|
|
61
|
-
console.error(chalk.red(`[ERROR] Theme app push failed: ${res.data?.task?.error_message}`));
|
|
62
|
-
break;
|
|
63
|
-
} else {
|
|
64
|
-
console.log(chalk.yellow(`[WAITING] Theme app push in progress...`));
|
|
65
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
66
|
-
}
|
|
55
|
+
if (!taskId) {
|
|
56
|
+
spinner.fail(chalk.red(`Failed to synchronize local files to remote location!`));
|
|
57
|
+
throw new Error('taskId is required');
|
|
67
58
|
}
|
|
59
|
+
// 轮询查看任务是否完成
|
|
60
|
+
return new Promise((resolve, reject) => {
|
|
61
|
+
const timer = setInterval(async () => {
|
|
62
|
+
const res = await getTaskStatus({ taskId });
|
|
63
|
+
if (res.data?.state === 1) {
|
|
64
|
+
clearInterval(timer);
|
|
65
|
+
spinner.succeed(chalk.green(`Successfully synchronized local files to remote location!`));
|
|
66
|
+
resolve();
|
|
67
|
+
} else if (res.data?.state === 2) {
|
|
68
|
+
clearInterval(timer);
|
|
69
|
+
spinner.fail(chalk.red(`Failed to synchronize local files to remote location!`));
|
|
70
|
+
reject(new Error(res.data?.message));
|
|
71
|
+
} else {
|
|
72
|
+
spinner.text = chalk.blue(`[WAITING] Synchronizing local files to remote...`);
|
|
73
|
+
}
|
|
74
|
+
}, 1000);
|
|
75
|
+
});
|
|
68
76
|
}
|
|
69
77
|
|
|
70
78
|
/**
|
|
@@ -90,26 +98,71 @@ async function usePrompt() {
|
|
|
90
98
|
/**
|
|
91
99
|
* 创建主题插件
|
|
92
100
|
*/
|
|
93
|
-
async function createThemeApp(
|
|
94
|
-
const
|
|
95
|
-
title:
|
|
101
|
+
async function createThemeApp(themeAppConfig) {
|
|
102
|
+
const data = {
|
|
103
|
+
title: themeAppConfig.extensionName || '' // 插件显示名称
|
|
104
|
+
};
|
|
105
|
+
if (themeAppConfig.extensionId) {
|
|
106
|
+
data.extension_id = themeAppConfig.extensionId;
|
|
107
|
+
}
|
|
108
|
+
const res = await toCreateThemeApp(data);
|
|
109
|
+
const extensionId = res.data?.extension_id;
|
|
110
|
+
await setThemeAppConfig({ extensionId });
|
|
111
|
+
return extensionId;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* 启动监听
|
|
116
|
+
*/
|
|
117
|
+
function startWatcher(extensionId) {
|
|
118
|
+
const ACTION_MAP = {
|
|
119
|
+
ADD: 'add',
|
|
120
|
+
CHANGE: 'change',
|
|
121
|
+
DELETE: 'delete'
|
|
122
|
+
};
|
|
123
|
+
const actionToApiMap = {
|
|
124
|
+
[ACTION_MAP.ADD]: toCreateFile,
|
|
125
|
+
[ACTION_MAP.CHANGE]: toUpdateFile,
|
|
126
|
+
[ACTION_MAP.DELETE]: toDeleteFile
|
|
127
|
+
};
|
|
128
|
+
const funcCache = {};
|
|
129
|
+
const handleFileChange = (action) => {
|
|
130
|
+
if (funcCache[action]) {
|
|
131
|
+
return funcCache[action];
|
|
132
|
+
}
|
|
133
|
+
const func = (filePath) => {
|
|
134
|
+
const { fileName, parentDirName } = getFileInfo(filePath);
|
|
135
|
+
const data = {
|
|
136
|
+
type: parentDirName,
|
|
137
|
+
location: fileName
|
|
138
|
+
};
|
|
139
|
+
if ([ACTION_MAP.CHANGE, ACTION_MAP.ADD].includes(action)) {
|
|
140
|
+
data.content = fsExtra.readFileSync(filePath, 'utf-8');
|
|
141
|
+
}
|
|
142
|
+
actionToApiMap[action](extensionId, data);
|
|
143
|
+
};
|
|
144
|
+
funcCache[action] = func;
|
|
145
|
+
return func;
|
|
146
|
+
};
|
|
147
|
+
watchWorkspace(THEME_APP_DIR_PATH, {
|
|
148
|
+
onAdd: handleFileChange(ACTION_MAP.ADD),
|
|
149
|
+
onChange: handleFileChange(ACTION_MAP.CHANGE),
|
|
150
|
+
onDelete: handleFileChange(ACTION_MAP.DELETE),
|
|
151
|
+
ignored: '**/assets-manifest.json',
|
|
96
152
|
});
|
|
97
|
-
const appId = res.data?.extension_id;
|
|
98
|
-
// QQQ1
|
|
99
|
-
await setThemeAppConfig({ appId });
|
|
100
|
-
return appId;
|
|
101
153
|
}
|
|
102
154
|
|
|
103
155
|
async function serve() {
|
|
104
156
|
try {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
157
|
+
const themeAppConfig = await getThemeAppConfig();
|
|
158
|
+
let extensionId = themeAppConfig.extensionId;
|
|
159
|
+
if (!extensionId) {
|
|
160
|
+
extensionId = await createThemeApp(themeAppConfig);
|
|
161
|
+
}
|
|
162
|
+
await syncLocalFiles(extensionId);
|
|
110
163
|
const selectedThemeId = await usePrompt();
|
|
111
|
-
|
|
112
|
-
consoleTips(selectedThemeId,
|
|
164
|
+
startWatcher(extensionId);
|
|
165
|
+
consoleTips(selectedThemeId, extensionId);
|
|
113
166
|
} catch (error) {
|
|
114
167
|
console.error(chalk.red(`[ERROR IN SERVE] ${error.message}`));
|
|
115
168
|
}
|
|
@@ -1,38 +1,46 @@
|
|
|
1
1
|
const chalk = require('chalk');
|
|
2
2
|
const ora = require('ora');
|
|
3
3
|
const { getThemeAppConfig, renderTable } = require('../utils');
|
|
4
|
-
const { getThemeAppVersionList } = require('../api
|
|
4
|
+
const { getThemeAppVersionList } = require('../api');
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* 控制台提示
|
|
8
8
|
*/
|
|
9
9
|
function consoleTips(versionList) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
10
|
+
if (versionList.length) {
|
|
11
|
+
console.log(chalk.green('\n📜 Available Versions:'));
|
|
12
|
+
console.log(
|
|
13
|
+
renderTable(versionList, [
|
|
14
|
+
{
|
|
15
|
+
label: 'Version',
|
|
16
|
+
filed: 'version',
|
|
17
|
+
color: 'yellowBright'
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
label: 'CreateTime',
|
|
21
|
+
filed: 'created_at',
|
|
22
|
+
color: 'blackBright'
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
label: 'Description',
|
|
26
|
+
filed: 'exts',
|
|
27
|
+
color: 'whiteBright'
|
|
28
|
+
}
|
|
29
|
+
])
|
|
30
|
+
);
|
|
31
|
+
} else {
|
|
32
|
+
console.log(chalk.green('Your current theme extension does not have an available version.'));
|
|
33
|
+
}
|
|
30
34
|
}
|
|
31
35
|
|
|
32
36
|
async function versions() {
|
|
33
37
|
try {
|
|
34
|
-
const {
|
|
35
|
-
|
|
38
|
+
const { extensionId } = await getThemeAppConfig();
|
|
39
|
+
if(!extensionId) {
|
|
40
|
+
throw new Error('ExtensionId is empty, please use `serve` command first.')
|
|
41
|
+
}
|
|
42
|
+
const res = await getThemeAppVersionList(extensionId);
|
|
43
|
+
const versionList = res.data?.data || [];
|
|
36
44
|
consoleTips(versionList);
|
|
37
45
|
} catch (error) {
|
|
38
46
|
console.error(chalk.red(`[ERROR IN VERSIONS] ${error.message}`));
|