shoplazza-cli 1.0.5 → 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.
Files changed (62) hide show
  1. package/bin/shoplazza +3 -14
  2. package/lib/app/api/index.js +96 -0
  3. package/lib/app/commands/build.js +101 -53
  4. package/lib/app/commands/create.js +101 -0
  5. package/lib/app/commands/deploy.js +44 -208
  6. package/lib/app/commands/list.js +35 -0
  7. package/lib/app/commands/serve.js +171 -0
  8. package/lib/app/commands/versions.js +52 -0
  9. package/lib/app/index.js +19 -30
  10. package/lib/app/template/basic-app/README.md +98 -0
  11. package/lib/app/template/basic-app/package.json +16 -0
  12. package/lib/app/template/basic-app/theme-app/assets/index.css +4 -0
  13. package/lib/app/template/basic-app/theme-app/assets-manifest.json +1 -0
  14. package/lib/app/template/basic-app/theme-app/blocks/index.liquid +16 -0
  15. package/lib/app/template/basic-app/theme-app/snippets/index.liquid +8 -0
  16. package/lib/app/template/basic-app/theme-extension.config.json +4 -0
  17. package/lib/app/template/embed-app/README.md +97 -0
  18. package/lib/app/template/embed-app/package.json +16 -0
  19. package/lib/app/template/embed-app/theme-app/assets-manifest.json +1 -0
  20. package/lib/app/template/embed-app/theme-app/blocks/index.liquid +18 -0
  21. package/lib/app/template/embed-app/theme-app/locales/ar-SA.json +3 -0
  22. package/lib/app/template/embed-app/theme-app/locales/de-DE.json +3 -0
  23. package/lib/app/template/embed-app/theme-app/locales/en-US.json +3 -0
  24. package/lib/app/template/embed-app/theme-app/locales/es-ES.json +3 -0
  25. package/lib/app/template/embed-app/theme-app/locales/fr-FR.json +3 -0
  26. package/lib/app/template/embed-app/theme-app/locales/id-ID.json +3 -0
  27. package/lib/app/template/embed-app/theme-app/locales/it-IT.json +3 -0
  28. package/lib/app/template/embed-app/theme-app/locales/ja-JP.json +3 -0
  29. package/lib/app/template/embed-app/theme-app/locales/ko-KR.json +3 -0
  30. package/lib/app/template/embed-app/theme-app/locales/nl-NL.json +3 -0
  31. package/lib/app/template/embed-app/theme-app/locales/pl-PL.json +3 -0
  32. package/lib/app/template/embed-app/theme-app/locales/pt-PT.json +3 -0
  33. package/lib/app/template/embed-app/theme-app/locales/ru-RU.json +3 -0
  34. package/lib/app/template/embed-app/theme-app/locales/th-TH.json +3 -0
  35. package/lib/app/template/embed-app/theme-app/locales/zh-CN.json +3 -0
  36. package/lib/app/template/embed-app/theme-app/locales/zh-TW.json +3 -0
  37. package/lib/app/template/embed-app/theme-app/snippets/index.liquid +8 -0
  38. package/lib/app/template/embed-app/theme-app/snippets/index_css.liquid +6 -0
  39. package/lib/app/template/embed-app/theme-extension.config.json +4 -0
  40. package/lib/app/utils/config.js +32 -0
  41. package/lib/app/utils/index.js +213 -0
  42. package/lib/checkout/api.js +11 -34
  43. package/lib/checkout/build/plugin/vite-plugin-transform-extension-html.js +1 -1
  44. package/lib/checkout/dev/index.js +2 -1
  45. package/lib/commands/login.js +1 -1
  46. package/lib/commands/logout.js +1 -1
  47. package/lib/openAPI/index.js +21 -10
  48. package/lib/oss.js +99 -0
  49. package/lib/utils.js +52 -0
  50. package/package.json +2 -2
  51. package/lib/app/commands/generate.js +0 -50
  52. package/lib/app/commands/publish.js +0 -52
  53. package/lib/app/extensions/index.js +0 -13
  54. package/lib/app/extensions/theme-app.js +0 -103
  55. package/lib/app/inquirers/version.js +0 -131
  56. /package/lib/{app → common}/constants.js +0 -0
  57. /package/lib/{app → common}/db/partner.js +0 -0
  58. /package/lib/{app → common}/inquirers/choose-app.js +0 -0
  59. /package/lib/{app → common}/inquirers/choose-partner.js +0 -0
  60. /package/lib/{app → common}/log.js +0 -0
  61. /package/lib/{app → common}/login.js +0 -0
  62. /package/lib/{app → common}/logout.js +0 -0
@@ -0,0 +1,35 @@
1
+ const chalk = require('chalk');
2
+ const ora = require('ora');
3
+ const { renderTable } = require('../utils');
4
+ const { getThemeAppList } = require('../api');
5
+
6
+ /**
7
+ * 控制台提示
8
+ */
9
+ function consoleTips(versionList) {
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
+ }
21
+ }
22
+
23
+ async function list() {
24
+ try {
25
+ const res = await getThemeAppList();
26
+ const themeAppList = res.data?.data || [];
27
+ consoleTips(themeAppList);
28
+ } catch (error) {
29
+ console.error(chalk.red(`[ERROR IN LIST] ${error.message}`));
30
+ }
31
+ }
32
+
33
+ module.exports = {
34
+ list
35
+ };
@@ -0,0 +1,171 @@
1
+ const chalk = require('chalk');
2
+ const inquirer = require('inquirer');
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');
17
+ const { useOss } = require('../../oss');
18
+
19
+ /**
20
+ * 控制台提示信息
21
+ */
22
+ function consoleTips(selectedThemeId, extensionId) {
23
+ console.log(`\n======================================PREVIEW URLS======================================`);
24
+ console.log(chalk.cyan.bold('🔗 Admin Preview URL:'));
25
+ console.log(
26
+ chalk.blueBright(` https://${STORE_DOMAIN}/admin/card?theme_id=${selectedThemeId}&ext_debug=${extensionId}\n`)
27
+ );
28
+ console.log(chalk.cyan.bold('🔗 Storefront Preview URL:'));
29
+ console.log(chalk.blueBright(` https://${STORE_DOMAIN}?preview_theme_id=${selectedThemeId}&ext_debug=${extensionId}\n`));
30
+ console.log(`=====================================WATCHER RUNNING=====================================`);
31
+ console.log(chalk.green(`🎉 File watcher is now running!`));
32
+ console.log(chalk.green(`📂 Watching directory:`), chalk.blue(THEME_APP_DIR_PATH));
33
+ console.log(chalk.green(`✨ You can make changes to your files, and they will be processed automatically.`));
34
+ console.log(chalk.green(`🚀 Press Ctrl+C to stop.\n`));
35
+ }
36
+
37
+ /**
38
+ * 同步本地代码到远程服务器
39
+ */
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
+ const { uploadOss } = useOss(
44
+ EXCHANGE_TOKEN,
45
+ STORE_DOMAIN,
46
+ `✗ No store found. Please run ${chalk.cyan('shoplazza login')} to login to a specific store.`
47
+ );
48
+ const zipOssUrl = await uploadOss(zipPath, zipName);
49
+ // 上传完成删除压缩包
50
+ fsExtra.removeSync(zipPath);
51
+ const res = await toUploadThemeApp(extensionId, {
52
+ resource_url: zipOssUrl
53
+ });
54
+ const taskId = res.data?.task_id;
55
+ if (!taskId) {
56
+ spinner.fail(chalk.red(`Failed to synchronize local files to remote location!`));
57
+ throw new Error('taskId is required');
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
+ });
76
+ }
77
+
78
+ /**
79
+ * 交互选择预览主题
80
+ */
81
+ async function usePrompt() {
82
+ const res = await getThemeList();
83
+ const themeList = res.data?.data?.themes || [];
84
+ const { selectedThemeId } = await inquirer.prompt([
85
+ {
86
+ type: 'list',
87
+ name: 'selectedThemeId',
88
+ message: 'Please select a theme to preview:',
89
+ choices: themeList.map((theme) => ({
90
+ name: theme.name,
91
+ value: theme.id
92
+ }))
93
+ }
94
+ ]);
95
+ return selectedThemeId;
96
+ }
97
+
98
+ /**
99
+ * 创建主题插件
100
+ */
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',
152
+ });
153
+ }
154
+
155
+ async function serve() {
156
+ try {
157
+ const themeAppConfig = await getThemeAppConfig();
158
+ let extensionId = themeAppConfig.extensionId;
159
+ if (!extensionId) {
160
+ extensionId = await createThemeApp(themeAppConfig);
161
+ }
162
+ await syncLocalFiles(extensionId);
163
+ const selectedThemeId = await usePrompt();
164
+ startWatcher(extensionId);
165
+ consoleTips(selectedThemeId, extensionId);
166
+ } catch (error) {
167
+ console.error(chalk.red(`[ERROR IN SERVE] ${error.message}`));
168
+ }
169
+ }
170
+
171
+ module.exports = { serve };
@@ -0,0 +1,52 @@
1
+ const chalk = require('chalk');
2
+ const ora = require('ora');
3
+ const { getThemeAppConfig, renderTable } = require('../utils');
4
+ const { getThemeAppVersionList } = require('../api');
5
+
6
+ /**
7
+ * 控制台提示
8
+ */
9
+ function consoleTips(versionList) {
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
+ }
34
+ }
35
+
36
+ async function versions() {
37
+ try {
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 || [];
44
+ consoleTips(versionList);
45
+ } catch (error) {
46
+ console.error(chalk.red(`[ERROR IN VERSIONS] ${error.message}`));
47
+ }
48
+ }
49
+
50
+ module.exports = {
51
+ versions
52
+ };
package/lib/app/index.js CHANGED
@@ -1,37 +1,26 @@
1
- const { checkAndLogin } = require('./login');
2
- const { build } = require('./commands/build');
3
- const { deploy } = require('./commands/deploy');
4
- const { generate } = require('./commands/generate');
5
- const { publish } = require('./commands/publish');
6
- const { choosePartner } = require('./inquirers/choose-partner');
7
- const { chooseApp } = require('./inquirers/choose-app');
8
-
9
- const generateExtension = async () => {
10
- (await checkAndLogin()) && (await generate());
11
- };
1
+ function initThemeAppCommand(_program) {
2
+ const program = _program.command('te').alias('theme_extension').description('Shoplazza theme extension CLI');
12
3
 
13
- const deployExtension = async () => {
14
- (await checkAndLogin()) && (await deploy());
15
- };
4
+ program.command('create').description('Create a new theme extension').action(require('./commands/create').create);
16
5
 
17
- const publishExtension = async () => {
18
- (await checkAndLogin()) && (await publish());
19
- };
6
+ program
7
+ .command('serve')
8
+ .description('Start a local development server for the theme extension')
9
+ .action(require('./commands/serve').serve);
20
10
 
21
- const retry = async (options) => {
22
- if (options.partner) {
23
- (await checkAndLogin()) && (await choosePartner()) && (await chooseApp());
24
- }
11
+ program.command('build').description('Build the theme extension for production').action(require('./commands/build').build);
25
12
 
26
- if (options.app) {
27
- (await checkAndLogin()) && (await chooseApp());
28
- }
29
- };
13
+ program
14
+ .command('versions')
15
+ .description('List all versions of the theme extension')
16
+ .action(require('./commands/versions').versions);
17
+
18
+ program.command('deploy').description('Deploy the theme extension').action(require('./commands/deploy').deploy);
19
+
20
+ program.command('list').description('List all theme extensions').action(require('./commands/list').list);
21
+
22
+ }
30
23
 
31
24
  module.exports = {
32
- generateExtension,
33
- deployExtension,
34
- publishExtension,
35
- buildExtension: build,
36
- retry
25
+ initThemeAppCommand
37
26
  };
@@ -0,0 +1,98 @@
1
+ # Theme Basic App
2
+
3
+ ## 概览
4
+
5
+ 一个基础的主题插件项目
6
+
7
+ ## 安装
8
+
9
+ 首先,确保您安装了“Shoplazza CLI”。如果没有,您可以使用以下命令全局安装它:
10
+
11
+ ```bash
12
+ npm install -g shoplazza-cli
13
+ ```
14
+
15
+ ## 可用的脚本
16
+
17
+ ### `create`
18
+
19
+ 创建主题插件项目模版
20
+
21
+ ```bash
22
+ shoplazza te create
23
+ ```
24
+
25
+ ---
26
+
27
+ 进入项目目录后,即可使用以下命令:
28
+
29
+ ### `start`
30
+
31
+ 启动开发模式
32
+
33
+ ```bash
34
+ npm start
35
+ ```
36
+
37
+ 或者
38
+
39
+ ```bash
40
+ shoplazza te serve
41
+ ```
42
+
43
+ ### `build`
44
+
45
+ 基于当前的草稿生成对应正式版本的主题插件
46
+
47
+ ```bash
48
+ npm run build
49
+ ```
50
+
51
+ 或者
52
+
53
+ ```bash
54
+ shoplazza te build
55
+ ```
56
+
57
+ ### `deploy`
58
+
59
+ 部署正式版本的主题插件(部署后插件在店铺的主题编辑器的app列表中可见)
60
+
61
+ ```bash
62
+ npm run deploy
63
+ ```
64
+
65
+ 或者
66
+
67
+ ```bash
68
+ shoplazza te deploy
69
+ ```
70
+
71
+ ### `versions`
72
+
73
+ 查询主题插件的历史正式版本列表
74
+
75
+ ```bash
76
+ npm run versions
77
+ ```
78
+
79
+ 或者
80
+
81
+ ```bash
82
+ shoplazza te versions
83
+ ```
84
+
85
+ ### `list`
86
+
87
+ 查询店铺的私有主题插件列表
88
+
89
+ ```bash
90
+ npm run list
91
+ ```
92
+
93
+ 或者
94
+
95
+ ```bash
96
+ shoplazza te list
97
+ ```
98
+
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "{{projectName}}",
3
+ "version": "1.0.0",
4
+ "description": "theme basic extension",
5
+ "main": "null",
6
+ "scripts": {
7
+ "start": "shoplazza te serve",
8
+ "build": "shoplazza te build",
9
+ "deploy": "shoplazza te deploy",
10
+ "versions": "shoplazza te versions",
11
+ "list": "shoplazza te list"
12
+ },
13
+ "keywords": [],
14
+ "author": "",
15
+ "license": "ISC"
16
+ }
@@ -0,0 +1,4 @@
1
+ .container {
2
+ color: #da534d;
3
+ text-align: center;
4
+ }
@@ -0,0 +1,16 @@
1
+ {% use "{{projectName}}.css" %}
2
+
3
+ <div id="{{projectName}}" class="container">
4
+ <h1>SHOPLAZZA THEME EXTENSION</h1>
5
+ {% include "{{projectName}}" %}
6
+ </div>
7
+
8
+ {% schema %}
9
+ {
10
+ "name": {
11
+ "en-US": "{{projectName}}-US",
12
+ "zh-CN": "{{projectName}}-CN"
13
+ },
14
+ "settings": []
15
+ }
16
+ {% endschema %}
@@ -0,0 +1,8 @@
1
+ <div>
2
+ <h3>BASIC TYPE</h3>
3
+ </div>
4
+
5
+
6
+
7
+
8
+
@@ -0,0 +1,4 @@
1
+ {
2
+ "extensionId": "",
3
+ "extensionName": "{{projectName}}"
4
+ }
@@ -0,0 +1,97 @@
1
+ # Theme Embed App
2
+
3
+ ## 概览
4
+
5
+ 一个 embed 的主题插件项目
6
+
7
+ ## 安装
8
+
9
+ 首先,确保您安装了“Shoplazza CLI”。如果没有,您可以使用以下命令全局安装它:
10
+
11
+ ```bash
12
+ npm install -g shoplazza-cli
13
+ ```
14
+
15
+ ## 可用的脚本
16
+
17
+ ### `create`
18
+
19
+ 创建主题插件项目模版
20
+
21
+ ```bash
22
+ shoplazza te create
23
+ ```
24
+
25
+ ---
26
+
27
+ 进入项目目录后,即可使用以下命令:
28
+
29
+ ### `start`
30
+
31
+ 启动开发模式
32
+
33
+ ```bash
34
+ npm start
35
+ ```
36
+
37
+ 或者
38
+
39
+ ```bash
40
+ shoplazza te serve
41
+ ```
42
+
43
+ ### `build`
44
+
45
+ 基于当前的草稿生成对应正式版本的主题插件
46
+
47
+ ```bash
48
+ npm run build
49
+ ```
50
+
51
+ 或者
52
+
53
+ ```bash
54
+ shoplazza te build
55
+ ```
56
+
57
+ ### `deploy`
58
+
59
+ 部署正式版本的主题插件
60
+
61
+ ```bash
62
+ npm run deploy
63
+ ```
64
+
65
+ 或者
66
+
67
+ ```bash
68
+ shoplazza te deploy
69
+ ```
70
+
71
+ ### `versions`
72
+
73
+ 查询主题插件的历史正式版本列表
74
+
75
+ ```bash
76
+ npm run versions
77
+ ```
78
+
79
+ 或者
80
+
81
+ ```bash
82
+ shoplazza te versions
83
+ ```
84
+
85
+ ### `list`
86
+
87
+ 查询店铺的私有主题插件列表
88
+
89
+ ```bash
90
+ npm run list
91
+ ```
92
+
93
+ 或者
94
+
95
+ ```bash
96
+ shoplazza te list
97
+ ```
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "{{projectName}}",
3
+ "version": "1.0.0",
4
+ "description": "theme embed extension",
5
+ "main": "null",
6
+ "scripts": {
7
+ "start": "shoplazza te serve",
8
+ "build": "shoplazza te build",
9
+ "deploy": "shoplazza te deploy",
10
+ "versions": "shoplazza te versions",
11
+ "list": "shoplazza te list"
12
+ },
13
+ "keywords": [],
14
+ "author": "",
15
+ "license": "ISC"
16
+ }
@@ -0,0 +1,18 @@
1
+ {% include "{{projectName}}_css" %}
2
+
3
+ <div id="{{projectName}}" class="container">
4
+ <h1>SHOPLAZZA THEME EXTENSION</h1>
5
+ {% include "{{projectName}}" %}
6
+ </div>
7
+
8
+ {% schema %}
9
+ {
10
+ "name": {
11
+ "en-US": "{{projectName}}-US",
12
+ "zh-CN": "{{projectName}}-CN"
13
+ },
14
+ "target":"body",
15
+ "icon":"https://cdn.shoplazza.com/oss/operation/0aee0d0cff21440e95a08ec4c560c691.svg",
16
+ "settings": []
17
+ }
18
+ {% endschema %}
@@ -0,0 +1,3 @@
1
+ {
2
+ "general": {}
3
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "general": {}
3
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "general": {}
3
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "general": {}
3
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "general": {}
3
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "general": {}
3
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "general": {}
3
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "general": {}
3
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "general": {}
3
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "general": {}
3
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "general": {}
3
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "general": {}
3
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "general": {}
3
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "general": {}
3
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "general": {}
3
+ }