@dazhicheng/common 1.0.4 → 1.0.7

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/.env ADDED
@@ -0,0 +1,23 @@
1
+ # 【通用】环境变量
2
+
3
+ # 版本号
4
+ VITE_VERSION = 1.0.0
5
+
6
+ <<<VITE_PORT>>>
7
+
8
+ # 应用部署基础路径(如部署在子目录 /admin,则设置为 /admin/)
9
+ VITE_BASE_URL = /
10
+
11
+ # 权限模式【 frontend 前端模式 / backend 后端模式 】
12
+ VITE_ACCESS_MODE = backend
13
+
14
+ <<<VITE_WITH_CREDENTIALS>>>
15
+
16
+ # 是否打开路由信息
17
+ VITE_OPEN_ROUTE_INFO = false
18
+
19
+ # 锁屏加密密钥
20
+ VITE_LOCK_ENCRYPT_KEY = s3cur3k3y4adpro
21
+
22
+ # 飞书扫码登录回调地址
23
+ VITE_FEISHU_REDIRECT_URI = http://120.76.61.203:13000/feishuOAuth
package/.env.dev ADDED
@@ -0,0 +1,16 @@
1
+ # 【开发】环境变量
2
+
3
+ # 应用部署基础路径(如部署在子目录 /admin,则设置为 /admin/)
4
+ VITE_BASE_URL = /
5
+
6
+ # API 地址前缀
7
+ VITE_API_URL = dev-api
8
+
9
+ # Delete console
10
+ VITE_DROP_CONSOLE = true
11
+
12
+ # 飞书扫码登录回调地址
13
+ # VITE_FEISHU_REDIRECT_URI = http://120.76.61.203:13006/feishuOAuth
14
+
15
+ # webSocket地址
16
+ VITE_WEBSOCKET_URL = ws://192.168.128.215:11000
@@ -0,0 +1,29 @@
1
+ # 【本地开发】环境变量
2
+
3
+ # 应用部署基础路径(如部署在子目录 /admin,则设置为 /admin/)
4
+ VITE_BASE_URL = /
5
+
6
+ # API 请求基础路径(开发环境设置为 / 使用代理,生产环境设置为完整后端地址)
7
+ VITE_API_URL = dev
8
+ VITE_MOCK_ENABLED = true
9
+
10
+ # 代理目标地址(开发环境通过 Vite 代理转发请求到此地址,解决跨域问题)
11
+ VITE_API_PROXY_URL = https://m1.apifoxmock.com/m1/6400575-6097373-default
12
+ VITE_API_PROXY_URL_dev = http://192.168.128.215:11000
13
+ VITE_API_MOCK = http://192.168.129.158:9998/mock/
14
+ VITE_API_PROXY_URL_test = http://192.168.127.72:11000/
15
+
16
+
17
+ VITE_API_PROXY_URL_dev_yun = http://116.6.49.180:11000/
18
+ VITE_API_PROXY_URL_test_yun = http://116.6.49.180:11001/
19
+
20
+ <<<VITE_API_PROXY_URL>>>
21
+
22
+ # Delete console
23
+ VITE_DROP_CONSOLE = false
24
+
25
+ # 飞书扫码登录回调地址
26
+ # VITE_FEISHU_REDIRECT_URI = http://120.76.61.203:13006/feishuOAuth
27
+
28
+ # webSocket地址
29
+ VITE_WEBSOCKET_URL = ws://192.168.128.215:11000
@@ -0,0 +1,10 @@
1
+ # 【生产】环境变量
2
+
3
+ # 应用部署基础路径(如部署在子目录 /admin,则设置为 /admin/)
4
+ VITE_BASE_URL = /
5
+
6
+ # API 地址前缀
7
+ VITE_API_URL = https://m1.apifoxmock.com/m1/6400575-6097373-default
8
+
9
+ # Delete console
10
+ VITE_DROP_CONSOLE = true
package/.env.sit ADDED
@@ -0,0 +1,10 @@
1
+ # 【预发布】环境变量
2
+
3
+ # 应用部署基础路径(如部署在子目录 /admin,则设置为 /admin/)
4
+ VITE_BASE_URL = /
5
+
6
+ # API 地址前缀
7
+ VITE_API_URL = sit-api
8
+
9
+ # Delete console
10
+ VITE_DROP_CONSOLE = true
package/.env.test ADDED
@@ -0,0 +1,16 @@
1
+ # 【测试】环境变量
2
+
3
+ # 应用部署基础路径(如部署在子目录 /admin,则设置为 /admin/)
4
+ VITE_BASE_URL = /
5
+
6
+ # API 地址前缀
7
+ VITE_API_URL = test-api
8
+
9
+ # Delete console
10
+ VITE_DROP_CONSOLE = true
11
+
12
+ # 飞书扫码登录回调地址
13
+ VITE_FEISHU_REDIRECT_URI = http://120.76.61.203:23000/feishuOAuth
14
+
15
+ # webSocket地址
16
+ VITE_WEBSOCKET_URL = ws://116.6.49.180:11001
package/.gitignore CHANGED
@@ -1,4 +1,3 @@
1
- # base
2
1
  node_modules
3
2
  .DS_Store
4
3
  dist
package/.gitlab-ci.yml ADDED
@@ -0,0 +1,74 @@
1
+ stages:
2
+ - MR_TEST
3
+ - develop
4
+ - test
5
+ - sit
6
+
7
+ variables:
8
+ DOCKER_BUILDKIT: 1
9
+
10
+ MR 构建测试:
11
+ stage: MR_TEST
12
+ image: docker:20.10.20
13
+ tags:
14
+ - MR_TEST
15
+ script:
16
+ # 构建镜像(不部署)
17
+ - docker build --build-arg NODE_ENV=dev --no-cache -t a-fulfillment-mr-test:mr-$CI_MERGE_REQUEST_IID .
18
+ # 验证镜像构建成功
19
+ - docker images | grep a-fulfillment-mr-test
20
+ rules:
21
+ - if: $CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop"
22
+
23
+ 开发环境发版:
24
+ stage: develop
25
+ image: docker:20.10.20
26
+ tags:
27
+ - develop
28
+ resource_group: deploy-develop # 重点
29
+ script:
30
+ <<<script_开发环境发版>>>
31
+ # 清理悬空镜像
32
+ - docker image prune -f || true
33
+ only:
34
+ - develop
35
+
36
+ 开发环境发版_107:
37
+ stage: develop
38
+ image: docker:20.10.20
39
+ tags:
40
+ - develop_107
41
+ resource_group: deploy-develop_107 # 重点
42
+ script:
43
+ <<<script_开发环境发版_107>>>
44
+ # 清理悬空镜像
45
+ - docker image prune -f || true
46
+ only:
47
+ - develop
48
+
49
+ # 测试环境 -----------------------------------------------------------------------------------------------
50
+ 测试环境发版:
51
+ stage: test
52
+ image: docker:20.10.20
53
+ tags:
54
+ - test
55
+ resource_group: deploy-test # 重点
56
+ script:
57
+ <<<script_测试环境发版>>>
58
+ # 清理悬空镜像
59
+ - docker image prune -f || true
60
+ only:
61
+ - test
62
+
63
+ 测试环境发版_107:
64
+ stage: test
65
+ image: docker:20.10.20
66
+ tags:
67
+ - test_107
68
+ resource_group: deploy-test_107 # 重点
69
+ script:
70
+ <<<script_测试环境发版_107>>>
71
+ # 清理悬空镜像
72
+ - docker image prune -f || true
73
+ only:
74
+ - test
@@ -0,0 +1,2 @@
1
+ /node_modules/*
2
+ /dist/*
@@ -16,5 +16,6 @@
16
16
  "proseWrap": "never",
17
17
  "htmlWhitespaceSensitivity": "strict",
18
18
  "endOfLine": "auto",
19
- "rangeStart": 0
19
+ "rangeStart": 0,
20
+ <<<自定义>>>
20
21
  }
package/.stylelintignore CHANGED
@@ -3,3 +3,6 @@ node_modules
3
3
  public
4
4
  .husky
5
5
  .vscode
6
+ src/components/Layout/MenuLeft/index.vue
7
+ src/assets
8
+ stats.html
@@ -1,76 +1,82 @@
1
- module.exports = {
2
- extends: [
3
- 'stylelint-config-standard',
4
- 'stylelint-config-recommended-scss',
5
- 'stylelint-config-recommended-vue/scss',
6
- 'stylelint-config-html/vue',
7
- 'stylelint-config-recess-order',
8
- ],
9
- overrides: [
10
- {
11
- files: ['**/*.{vue,html}'],
12
- customSyntax: 'postcss-html',
13
- },
14
- {
15
- files: ['**/*.{css,scss}'],
16
- customSyntax: 'postcss-scss',
17
- },
18
- ],
19
- rules: {
20
- 'import-notation': 'string',
21
- 'selector-class-pattern': null,
22
- 'custom-property-pattern': null,
23
- 'keyframes-name-pattern': null,
24
- 'no-descending-specificity': null,
25
- 'no-empty-source': null,
26
- 'property-no-vendor-prefix': null,
27
- 'selector-pseudo-class-no-unknown': [
28
- true,
29
- {
30
- ignorePseudoClasses: ['global', 'export', 'deep'],
31
- },
32
- ],
33
- 'property-no-unknown': [
34
- true,
35
- {
36
- ignoreProperties: [],
37
- },
38
- ],
39
- 'at-rule-no-unknown': [
40
- true,
41
- {
42
- ignoreAtRules: [
43
- 'apply',
44
- 'use',
45
- 'mixin',
46
- 'include',
47
- 'extend',
48
- 'each',
49
- 'if',
50
- 'else',
51
- 'for',
52
- 'while',
53
- 'reference',
54
- ],
55
- },
56
- ],
57
- 'scss/at-rule-no-unknown': [
58
- true,
59
- {
60
- ignoreAtRules: [
61
- 'apply',
62
- 'use',
63
- 'mixin',
64
- 'include',
65
- 'extend',
66
- 'each',
67
- 'if',
68
- 'else',
69
- 'for',
70
- 'while',
71
- 'reference',
72
- ],
73
- },
74
- ],
75
- },
76
- };
1
+ module.exports = {
2
+ // 继承推荐规范配置
3
+ extends: [
4
+ 'stylelint-config-standard',
5
+ 'stylelint-config-recommended-scss',
6
+ 'stylelint-config-recommended-vue/scss',
7
+ 'stylelint-config-html/vue',
8
+ 'stylelint-config-recess-order',
9
+ ],
10
+ // 指定不同文件对应的解析器
11
+ overrides: [
12
+ {
13
+ files: ['**/*.{vue,html}'],
14
+ customSyntax: 'postcss-html',
15
+ },
16
+ {
17
+ files: ['**/*.{css,scss}'],
18
+ customSyntax: 'postcss-scss',
19
+ },
20
+ ],
21
+ // 自定义规则
22
+ rules: {
23
+ 'import-notation': 'string', // 指定导入CSS文件的方式("string"|"url")
24
+ 'selector-class-pattern': null, // 选择器类名命名规则
25
+ 'custom-property-pattern': null, // 自定义属性命名规则
26
+ 'keyframes-name-pattern': null, // 动画帧节点样式命名规则
27
+ 'no-descending-specificity': null, // 允许无降序特异性
28
+ 'no-empty-source': null, // 允许空样式
29
+ 'property-no-vendor-prefix': null, // 允许属性前缀
30
+ // 允许 global export deep伪类
31
+ 'selector-pseudo-class-no-unknown': [
32
+ true,
33
+ {
34
+ ignorePseudoClasses: ['global', 'export', 'deep'],
35
+ },
36
+ ],
37
+ // 允许未知属性
38
+ 'property-no-unknown': [
39
+ true,
40
+ {
41
+ ignoreProperties: [],
42
+ },
43
+ ],
44
+ // 允许未知规则
45
+ 'at-rule-no-unknown': [
46
+ true,
47
+ {
48
+ ignoreAtRules: [
49
+ 'apply',
50
+ 'use',
51
+ 'mixin',
52
+ 'include',
53
+ 'extend',
54
+ 'each',
55
+ 'if',
56
+ 'else',
57
+ 'for',
58
+ 'while',
59
+ 'reference',
60
+ ],
61
+ },
62
+ ],
63
+ 'scss/at-rule-no-unknown': [
64
+ true,
65
+ {
66
+ ignoreAtRules: [
67
+ 'apply',
68
+ 'use',
69
+ 'mixin',
70
+ 'include',
71
+ 'extend',
72
+ 'each',
73
+ 'if',
74
+ 'else',
75
+ 'for',
76
+ 'while',
77
+ 'reference',
78
+ ],
79
+ },
80
+ ],
81
+ },
82
+ };
package/Dockerfile CHANGED
@@ -30,4 +30,4 @@ COPY "nginx_${NODE_ENV}.conf" /etc/nginx/conf.d/default.conf
30
30
  COPY --from=builder /app/dist /usr/share/nginx/html
31
31
 
32
32
  # 启动 Nginx
33
- CMD ["nginx", "-g", "daemon off;"]
33
+ CMD ["nginx", "-g", "daemon off;"]
@@ -16,7 +16,7 @@ const taskIdPlugin = {
16
16
  },
17
17
  },
18
18
  };
19
-
19
+ <<<自定义>>>
20
20
  module.exports = {
21
21
  // 继承的规则
22
22
  extends: ["@commitlint/config-conventional"],
@@ -1,9 +1,24 @@
1
+ // 从 URL 和路径模块中导入必要的功能
2
+ import fs from 'fs';
3
+ import path, { dirname } from 'path';
4
+ import { fileURLToPath } from 'url';
5
+
6
+ // 从 ESLint 插件中导入推荐配置
1
7
  import pluginJs from '@eslint/js';
2
8
  import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
3
9
  import pluginVue from 'eslint-plugin-vue';
4
10
  import globals from 'globals';
5
11
  import tseslint from 'typescript-eslint';
6
12
 
13
+ // 使用 import.meta.url 获取当前模块的路径
14
+ const __filename = fileURLToPath(import.meta.url);
15
+ const __dirname = dirname(__filename);
16
+
17
+ // 读取 .auto-import.json 文件的内容,并将其解析为 JSON 对象
18
+ const autoImportConfig = JSON.parse(
19
+ fs.readFileSync(path.resolve(__dirname, '.auto-import.json'), 'utf-8'),
20
+ );
21
+
7
22
  export default [
8
23
  // 指定文件匹配规则
9
24
  {
@@ -46,17 +61,6 @@ export default [
46
61
  '@typescript-eslint/no-unused-vars': 'off', // 禁用 TypeScript 未使用的变量检查
47
62
  },
48
63
  },
49
- {
50
- files: ['**/*.vue'],
51
- languageOptions: {
52
- parserOptions: {
53
- parser: tseslint.parser,
54
- ecmaFeatures: {
55
- jsx: true,
56
- },
57
- },
58
- },
59
- },
60
64
  // vue 规则
61
65
  {
62
66
  files: ['**/*.vue'],
package/nginx_dev.conf ADDED
@@ -0,0 +1,26 @@
1
+ # HTTP 服务器 - 保持原有配置
2
+ server {
3
+ <<<listen>>>
4
+ server_name localhost;
5
+ charset utf-8;
6
+
7
+ root /usr/share/nginx/html;
8
+ index index.html;
9
+
10
+ location / {
11
+ try_files $uri $uri/ /index.html;
12
+ <<<CORS配置>>>
13
+ }
14
+
15
+ location /dev-api/ {
16
+ proxy_set_header Host $host;
17
+ proxy_set_header X-Real-Ip $remote_addr;
18
+ proxy_set_header X-Forwarded-For $remote_addr;
19
+ proxy_pass http://116.6.49.180:11000/;
20
+ }
21
+
22
+ error_page 500 502 503 504 /50x.html;
23
+ location = /50x.html {
24
+ root html;
25
+ }
26
+ }
@@ -0,0 +1,26 @@
1
+ # HTTP 服务器 - 保持原有配置
2
+ server {
3
+ <<<listen>>>
4
+ server_name localhost;
5
+ charset utf-8;
6
+
7
+ root /usr/share/nginx/html;
8
+ index index.html;
9
+
10
+ location / {
11
+ try_files $uri $uri/ /index.html;
12
+ <<<CORS配置>>>
13
+ }
14
+
15
+ location /test-api/ {
16
+ proxy_set_header Host $host;
17
+ proxy_set_header X-Real-Ip $remote_addr;
18
+ proxy_set_header X-Forwarded-For $remote_addr;
19
+ proxy_pass http://116.6.49.180:11001/;
20
+ }
21
+
22
+ error_page 500 502 503 504 /50x.html;
23
+ location = /50x.html {
24
+ root html;
25
+ }
26
+ }
package/package.json CHANGED
@@ -1,30 +1,36 @@
1
1
  {
2
2
  "name": "@dazhicheng/common",
3
- "version": "1.0.4",
3
+ "version": "1.0.7",
4
4
  "description": "共享配置文件",
5
5
  "type": "module",
6
+ "types": "./src/types/index.d.ts",
6
7
  "files": [
7
- "commitlint.config.cjs",
8
+ "commitlint.config.cjs.txt",
8
9
  ".husky",
9
- ".prettierrc",
10
- ".stylelintrc.cjs",
10
+ ".prettierignore",
11
+ ".prettierrc.txt",
12
+ ".stylelintrc.cjs.txt",
11
13
  ".stylelintignore",
12
14
  ".gitignore",
13
15
  ".gitattributes",
14
- "eslint.config.mjs",
16
+ ".env",
17
+ ".env.dev",
18
+ ".env.development",
19
+ ".env.production",
20
+ ".env.sit",
21
+ ".env.test",
22
+ ".gitlab-ci.yml",
23
+ "pnpm-workspace.yaml",
24
+ "eslint.config.mjs.txt",
15
25
  "tsconfig.json",
16
- "tsconfig.web.json",
17
- "Dockerfile"
26
+ "Dockerfile",
27
+ "nginx_dev.conf",
28
+ "nginx_test.conf",
29
+ "sync-common.mjs",
30
+ "src/types/index.d.ts"
18
31
  ],
19
32
  "exports": {
20
- "./commitlint.config.cjs": "./commitlint.config.cjs",
21
- "./.prettierrc": "./.prettierrc",
22
- "./.stylelintrc.cjs": "./.stylelintrc.cjs",
23
- "./.stylelintignore": "./.stylelintignore",
24
- "./.gitignore": "./.gitignore",
25
- "./.gitattributes": "./.gitattributes",
26
- "./eslint.config.mjs": "./eslint.config.mjs",
27
- "./tsconfig.json": "./tsconfig.json"
33
+ "./sync-common.mjs": "./sync-common.mjs"
28
34
  },
29
35
  "publishConfig": {
30
36
  "access": "public"
@@ -0,0 +1,9 @@
1
+ minimumReleaseAge: 0
2
+
3
+ allowBuilds:
4
+ '@parcel/watcher': true
5
+ core-js: true
6
+ es5-ext: true
7
+ esbuild: true
8
+ vue-demi: true
9
+ '@tailwindcss/oxide': true
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,305 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { fileURLToPath } from "url";
4
+ import prettier from "prettier";
5
+
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = path.dirname(__filename);
8
+ const commonDir = __dirname;
9
+
10
+ const localMarkerPattern = /^\s*<<<([^>]+)>>>\s*$/gm;
11
+ const commonMarkerPattern = /<<<([^>]+)>>>/g;
12
+ const defaultManagedFiles = [
13
+ ".gitignore",
14
+ ".stylelintignore",
15
+ ".gitattributes",
16
+ ".prettierignore",
17
+ { name: ".prettierrc.txt" },
18
+ ".env",
19
+ ".env.dev",
20
+ ".env.development",
21
+ ".env.production",
22
+ ".env.sit",
23
+ ".env.test",
24
+ { name: ".stylelintrc.cjs.txt" },
25
+ { name: "commitlint.config.cjs.txt" },
26
+ ".gitlab-ci.yml",
27
+ "pnpm-workspace.yaml",
28
+ "Dockerfile",
29
+ { name: "eslint.config.mjs.txt" },
30
+ "tsconfig.json",
31
+ { name: "nginx_dev.conf" },
32
+ { name: "nginx_test.conf" },
33
+ { name: "commit-msg", dir: ".husky" },
34
+ { name: "pre-commit", dir: ".husky" },
35
+ ];
36
+
37
+ const getFileRelativePath = file => (file.dir ? path.join(file.dir, file.name) : file.name);
38
+ const getTargetFileName = file => {
39
+ if (file.targetName) {
40
+ return file.targetName;
41
+ }
42
+
43
+ return file.name.endsWith(".txt") ? file.name.slice(0, -4) : file.name;
44
+ };
45
+ const getTargetRelativePath = file =>
46
+ file.dir ? path.join(file.dir, getTargetFileName(file)) : getTargetFileName(file);
47
+ const getFileKey = file => `${file.dir ?? ""}::${file.name}`;
48
+
49
+ const ensureCommonFile = file => {
50
+ const filePath = path.join(commonDir, getFileRelativePath(file));
51
+ if (!fs.existsSync(filePath)) {
52
+ console.error(`\n[sync:common] {${getFileKey(file)}} missing common file: ${filePath}`);
53
+ return "";
54
+ }
55
+ return filePath;
56
+ };
57
+
58
+ const normalizeLineEndings = content => content.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
59
+
60
+ const parseLocalSections = filePaths => {
61
+ const candidates = [filePaths].flat().filter(Boolean);
62
+ const existingPath = candidates.find(filePath => fs.existsSync(filePath));
63
+
64
+ if (!existingPath) {
65
+ return { before: "", after: "", blocks: {}, hasMarkers: false, sourcePath: "" };
66
+ }
67
+
68
+ const rawContent = normalizeLineEndings(fs.readFileSync(existingPath, "utf8"));
69
+ if (!rawContent.trim()) {
70
+ return { before: "", after: "", blocks: {}, hasMarkers: false, sourcePath: existingPath };
71
+ }
72
+
73
+ const markers = [...rawContent.matchAll(localMarkerPattern)];
74
+ if (markers.length === 0) {
75
+ return { before: "", after: rawContent, blocks: {}, hasMarkers: false, sourcePath: existingPath };
76
+ }
77
+
78
+ const blocks = {};
79
+
80
+ for (let index = 0; index < markers.length; index += 1) {
81
+ const current = markers[index];
82
+ const next = markers[index + 1];
83
+ const blockName = current[1].trim();
84
+ const start = current.index + current[0].length;
85
+ const end = next ? next.index : rawContent.length;
86
+ const blockContent = rawContent.slice(start, end).replace(/^\n/, "");
87
+ blocks[blockName] = blockContent;
88
+ }
89
+
90
+ return {
91
+ before: blocks.before ?? "",
92
+ after: blocks.after ?? "",
93
+ blocks,
94
+ hasMarkers: true,
95
+ sourcePath: existingPath,
96
+ };
97
+ };
98
+
99
+ const replaceNamedBlocks = (content, blocks = {}) =>
100
+ content.replace(commonMarkerPattern, (match, blockName, offset, source) => {
101
+ const replacement = blocks[blockName.trim()];
102
+ if (!replacement) {
103
+ return "";
104
+ }
105
+
106
+ const lineStart = source.lastIndexOf("\n", offset - 1) + 1;
107
+ const linePrefix = source.slice(lineStart, offset);
108
+ const indentation = (linePrefix.match(/^\s*/) ?? [""])[0];
109
+
110
+ return replacement.split("\n").map(line => `${indentation}${line}`).join("\n");
111
+ });
112
+
113
+ const getDefaultHeader = () => "";
114
+
115
+ const getDefaultLocalFiles = file => {
116
+ const sourceRelativePath = getFileRelativePath(file);
117
+ const targetRelativePath = getTargetRelativePath(file);
118
+
119
+ return {
120
+ merged: path.join("local", sourceRelativePath),
121
+ targetMerged: targetRelativePath !== sourceRelativePath ? path.join("local", targetRelativePath) : "",
122
+ };
123
+ };
124
+
125
+ const getDefaultBlockHeaders = () => ({
126
+ before: "",
127
+ common: "",
128
+ after: "",
129
+ });
130
+
131
+ const getDefaultFileConfig = file => ({
132
+ name: file.name,
133
+ dir: file.dir,
134
+ targetName: file.targetName,
135
+ header: getDefaultHeader(),
136
+ localFiles: getDefaultLocalFiles(file),
137
+ blockHeaders: getDefaultBlockHeaders(),
138
+ });
139
+
140
+ const mergeFileConfig = (defaultFile, projectFile = {}) => ({
141
+ ...defaultFile,
142
+ ...projectFile,
143
+ localFiles: {
144
+ ...(defaultFile.localFiles ?? {}),
145
+ ...(projectFile.localFiles ?? {}),
146
+ },
147
+ blockHeaders: {
148
+ ...(defaultFile.blockHeaders ?? {}),
149
+ ...(projectFile.blockHeaders ?? {}),
150
+ },
151
+ });
152
+
153
+ const normalizeFileEntry = file => {
154
+ if (typeof file === "string") {
155
+ return { name: file };
156
+ }
157
+
158
+ return file;
159
+ };
160
+
161
+ const normalizeFiles = (config = {}) => {
162
+ if (Array.isArray(config)) {
163
+ return config.map(normalizeFileEntry);
164
+ }
165
+
166
+ if (Array.isArray(config.files)) {
167
+ return config.files.map(normalizeFileEntry);
168
+ }
169
+
170
+ return [
171
+ ...(config.textFiles ?? []).map(file => ({ type: "textFiles", ...normalizeFileEntry(file) })),
172
+ ...(config.directFiles ?? []).map(file => ({
173
+ type: "directFiles",
174
+ ...normalizeFileEntry(file),
175
+ })),
176
+ ];
177
+ };
178
+
179
+ const mergeManagedFiles = (projectConfig = {}, runtimeConfig = {}) => {
180
+ const defaultFiles = normalizeFiles(defaultManagedFiles).map(file =>
181
+ mergeFileConfig(getDefaultFileConfig(file), file),
182
+ );
183
+ const projectFiles = normalizeFiles(projectConfig);
184
+ const runtimeFiles = normalizeFiles(runtimeConfig);
185
+ const mergedMap = new Map(defaultFiles.map(file => [getFileKey(file), { ...file }]));
186
+
187
+ for (const projectFile of projectFiles) {
188
+ const key = getFileKey(projectFile);
189
+ const current = mergedMap.get(key) ?? getDefaultFileConfig(projectFile);
190
+ mergedMap.set(key, mergeFileConfig(current, projectFile));
191
+ }
192
+
193
+ for (const runtimeFile of runtimeFiles) {
194
+ const key = getFileKey(runtimeFile);
195
+ const current = mergedMap.get(key) ?? getDefaultFileConfig(runtimeFile);
196
+ mergedMap.set(key, mergeFileConfig(current, runtimeFile));
197
+ }
198
+
199
+ return [...mergedMap.values()].filter(file => file.enabled !== false);
200
+ };
201
+
202
+ const logGenerated = ({ rootDir, targetPath, commonPath, localPaths, usedLocalPaths }) => {
203
+ const targetName = path.relative(rootDir, targetPath).replaceAll("\\", "/");
204
+ const commonName = `@dazhicheng/common/${path.relative(commonDir, commonPath).replaceAll("\\", "/")}`;
205
+ const sourceText = [commonName, ...usedLocalPaths].join(" + ");
206
+
207
+ console.log(`\n[sync:common] generated ${targetName} <- ${sourceText}`);
208
+ if (localPaths.length > 0) {
209
+ console.log(`[sync:common] 如需项目增量,请修改 ${localPaths.join(" / ")}`);
210
+ console.log("[sync:common] local 文件支持 <<<name>>> 任意命名块,占位将替换 common 中同名标记");
211
+ }
212
+ };
213
+
214
+ const renderFile = ({ file, commonContent, localSections, usedLocalPaths }) => {
215
+ let output = file.header ?? "";
216
+ const renderedCommonContent = replaceNamedBlocks(commonContent, localSections.blocks);
217
+
218
+ if (localSections.before) {
219
+ if (localSections.sourcePath) {
220
+ usedLocalPaths.push(path.relative(process.cwd(), localSections.sourcePath).replaceAll("\\", "/"));
221
+ }
222
+ output += `${file.blockHeaders.before ?? ""}${localSections.before}\n\n`;
223
+ }
224
+
225
+ output += `${file.blockHeaders.common ?? ""}${renderedCommonContent}`;
226
+
227
+ if (localSections.after) {
228
+ if (localSections.sourcePath) {
229
+ const localPath = path.relative(process.cwd(), localSections.sourcePath).replaceAll("\\", "/");
230
+ if (!usedLocalPaths.includes(localPath)) {
231
+ usedLocalPaths.push(localPath);
232
+ }
233
+ }
234
+ output += `${file.blockHeaders.after ?? ""}${localSections.after}`;
235
+ }
236
+
237
+ return output.endsWith("\n") ? output : `${output}\n`;
238
+ };
239
+
240
+ const formatWithPrettier = async ({ targetPath, output }) => {
241
+ try {
242
+ const fileInfo = await prettier.getFileInfo(targetPath);
243
+ if (fileInfo.ignored || !fileInfo.inferredParser) {
244
+ console.log(`[sync:common] skipped prettier for ${targetPath}: parser unavailable`);
245
+ return output;
246
+ }
247
+
248
+ const config = (await prettier.resolveConfig(targetPath)) ?? {};
249
+ return normalizeLineEndings(await prettier.format(output, {
250
+ ...config,
251
+ filepath: targetPath,
252
+ }));
253
+ } catch (error) {
254
+ console.log(`[sync:common] skipped prettier for ${targetPath}: ${error.message}`);
255
+ return output;
256
+ }
257
+ };
258
+
259
+ const syncFiles = async ({ files, rootDir }) => {
260
+ for (const file of files) {
261
+ const commonPath = ensureCommonFile(file);
262
+ if (!commonPath) {
263
+ continue;
264
+ }
265
+ const targetPath = path.join(rootDir, getTargetRelativePath(file));
266
+ const targetDir = path.dirname(targetPath);
267
+ if (!fs.existsSync(targetDir)) {
268
+ fs.mkdirSync(targetDir, { recursive: true });
269
+ }
270
+ const localPaths = Object.values(file.localFiles ?? {}).filter(Boolean);
271
+ const resolvedLocalPaths = localPaths.map(localPath => path.join(rootDir, localPath));
272
+ const localSections = parseLocalSections(resolvedLocalPaths);
273
+ const commonContent = normalizeLineEndings(fs.readFileSync(commonPath, "utf8")).trimEnd();
274
+ const usedLocalPaths = [];
275
+ const output = renderFile({ file, commonContent, localSections, usedLocalPaths });
276
+
277
+ const formattedOutput = await formatWithPrettier({ targetPath, output });
278
+
279
+ fs.writeFileSync(targetPath, formattedOutput);
280
+ logGenerated({
281
+ rootDir,
282
+ targetPath,
283
+ commonPath,
284
+ localPaths: Object.values(file.localFiles ?? {}).filter(Boolean),
285
+ usedLocalPaths,
286
+ });
287
+ }
288
+ };
289
+
290
+ export const runSyncCommon = async ({ rootDir, files = [], config = {} }) => {
291
+ const runtimeConfig = {
292
+ ...(config ?? {}),
293
+ files: [...normalizeFiles(config ?? {}), ...normalizeFiles(files)],
294
+ };
295
+ const managedFiles = mergeManagedFiles({}, runtimeConfig);
296
+
297
+ await syncFiles({ files: managedFiles, rootDir });
298
+ };
299
+
300
+ const isDirectRun = process.argv[1] && path.resolve(process.argv[1]) === __filename;
301
+
302
+ if (isDirectRun) {
303
+ const cwd = process.cwd();
304
+ await runSyncCommon({ rootDir: cwd });
305
+ }
package/tsconfig.json CHANGED
@@ -1,29 +1,29 @@
1
- {
2
- "compilerOptions": {
3
- "target": "esnext",
4
- "module": "esnext",
5
- "moduleResolution": "node",
6
- "strict": true,
7
- "jsx": "preserve",
8
- "jsxImportSource": "vue",
9
- "sourceMap": true,
10
- "resolveJsonModule": true,
11
- "esModuleInterop": true,
12
- "lib": ["esnext", "dom"],
13
- "types": ["vite/client", "node", "element-plus/global"],
14
- "skipLibCheck": true,
15
- "baseUrl": ".",
16
- "paths": {
17
- "@/*": ["src/*"],
18
- "@views/*": ["src/views/*"],
19
- "@imgs/*": ["src/assets/images/*"],
20
- "@icons/*": ["src/assets/icons/*"],
21
- "@utils/*": ["src/utils/*"],
22
- "@stores/*": ["src/store/*"],
23
- "@plugins/*": ["src/plugins/*"],
24
- "@styles/*": ["src/assets/styles/*"]
25
- }
26
- },
27
- "include": ["src/**/*", "src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
28
- "exclude": ["node_modules", "dist", "**/*.js"]
29
- }
1
+ {
2
+ "compilerOptions": {
3
+ "target": "esnext",
4
+ "module": "esnext",
5
+ "moduleResolution": "node",
6
+ "strict": true,
7
+ "jsx": "preserve",
8
+ "jsxImportSource": "vue",
9
+ "sourceMap": true,
10
+ "resolveJsonModule": true,
11
+ "esModuleInterop": true,
12
+ "lib": ["esnext", "dom"],
13
+ "types": ["vite/client", "node", "element-plus/global"],
14
+ "skipLibCheck": true,
15
+ "baseUrl": ".",
16
+ "paths": {
17
+ "@/*": ["src/*"],
18
+ "@views/*": ["src/views/*"],
19
+ "@imgs/*": ["src/assets/images/*"],
20
+ "@icons/*": ["src/assets/icons/*"],
21
+ "@utils/*": ["src/utils/*"],
22
+ "@stores/*": ["src/store/*"],
23
+ "@plugins/*": ["src/plugins/*"],
24
+ "@styles/*": ["src/assets/styles/*"]
25
+ }
26
+ },
27
+ "include": ["src/**/*", "src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
28
+ "exclude": ["node_modules", "dist", "**/*.js"]
29
+ }