@farris/cli 2.3.7 → 2.3.9

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 (171) hide show
  1. package/lib/commands/add-app.js +5 -5
  2. package/lib/commands/add-app.js.map +1 -1
  3. package/lib/commands/add-lib.js +5 -5
  4. package/lib/commands/add-lib.js.map +1 -1
  5. package/lib/commands/add-micro.js +12 -0
  6. package/lib/commands/add-micro.js.map +1 -0
  7. package/lib/commands/add-project.js +196 -194
  8. package/lib/commands/add-project.js.map +1 -1
  9. package/lib/commands/build-components.js +60 -60
  10. package/lib/commands/build-components.js.map +1 -1
  11. package/lib/commands/build-css.js +38 -38
  12. package/lib/commands/build-css.js.map +1 -1
  13. package/lib/commands/build-lib.js +30 -30
  14. package/lib/commands/build-lib.js.map +1 -1
  15. package/lib/commands/build.js +19 -19
  16. package/lib/commands/build.js.map +1 -1
  17. package/lib/commands/create-app-options.js +85 -74
  18. package/lib/commands/create-app-options.js.map +1 -1
  19. package/lib/commands/create-app.js +131 -131
  20. package/lib/commands/create-app.js.map +1 -1
  21. package/lib/commands/create-options.js +58 -32
  22. package/lib/commands/create-options.js.map +1 -1
  23. package/lib/commands/create.js +341 -331
  24. package/lib/commands/create.js.map +1 -1
  25. package/lib/commands/dev-serve.js +18 -18
  26. package/lib/commands/extract-i18n.js +74 -74
  27. package/lib/commands/extract-i18n.js.map +1 -1
  28. package/lib/commands/generate/api.js +124 -124
  29. package/lib/commands/generate/api.js.map +1 -1
  30. package/lib/commands/generate/common.js +101 -101
  31. package/lib/commands/generate/common.js.map +1 -1
  32. package/lib/commands/generate/component.js +156 -156
  33. package/lib/commands/generate/component.js.map +1 -1
  34. package/lib/commands/generate/view.js +168 -168
  35. package/lib/commands/generate/view.js.map +1 -1
  36. package/lib/commands/preview-serve.js +16 -16
  37. package/lib/commands/workspace/add-app.js +15 -15
  38. package/lib/commands/workspace/add-app.js.map +1 -1
  39. package/lib/commands/workspace/create.js +62 -62
  40. package/lib/commands/workspace/create.js.map +1 -1
  41. package/lib/commands/workspace/inspect.js +30 -30
  42. package/lib/commands/workspace/inspect.js.map +1 -1
  43. package/lib/commands/workspace/verify.js +38 -38
  44. package/lib/commands/workspace/verify.js.map +1 -1
  45. package/lib/common/agents-doc.js +33 -33
  46. package/lib/common/agents-doc.js.map +1 -1
  47. package/lib/common/apply-catalog-deps.js +15 -15
  48. package/lib/common/apply-catalog-deps.js.map +1 -1
  49. package/lib/common/component-file-name.js +3 -3
  50. package/lib/common/constant.js +85 -84
  51. package/lib/common/constant.js.map +1 -1
  52. package/lib/common/create-app-target.js +46 -46
  53. package/lib/common/create-app-target.js.map +1 -1
  54. package/lib/common/generate-app.js +106 -109
  55. package/lib/common/generate-app.js.map +1 -1
  56. package/lib/common/get-dependencies.js +9 -9
  57. package/lib/common/get-farris-config.js +25 -25
  58. package/lib/common/get-farris-config.js.map +1 -1
  59. package/lib/common/get-version.js +6 -6
  60. package/lib/common/get-vite-config.js +192 -192
  61. package/lib/common/get-vite-config.js.map +1 -1
  62. package/lib/common/lib-package-exports.js +24 -24
  63. package/lib/common/lib-package-exports.js.map +1 -1
  64. package/lib/common/list-lib-components.js +23 -23
  65. package/lib/common/list-lib-components.js.map +1 -1
  66. package/lib/common/output.js +164 -164
  67. package/lib/common/output.js.map +1 -1
  68. package/lib/common/project-layout.js +155 -183
  69. package/lib/common/project-layout.js.map +1 -1
  70. package/lib/common/safety.js +172 -173
  71. package/lib/common/safety.js.map +1 -1
  72. package/lib/common/template-manifest.js +18 -14
  73. package/lib/common/template-manifest.js.map +1 -1
  74. package/lib/common/template-renderer.js +114 -114
  75. package/lib/common/template-renderer.js.map +1 -1
  76. package/lib/common/transaction.js +83 -83
  77. package/lib/common/transaction.js.map +1 -1
  78. package/lib/common/verifier.js +187 -201
  79. package/lib/common/verifier.js.map +1 -1
  80. package/lib/common/workspace-contract.js +191 -157
  81. package/lib/common/workspace-contract.js.map +1 -1
  82. package/lib/config/vite-app.js +13 -13
  83. package/lib/config/vite-common.js +20 -20
  84. package/lib/config/vite-common.js.map +1 -1
  85. package/lib/config/vite-component.js +32 -32
  86. package/lib/config/vite-component.js.map +1 -1
  87. package/lib/config/vite-lib.js +26 -26
  88. package/lib/config/vite-lib.js.map +1 -1
  89. package/lib/index.js +261 -238
  90. package/lib/index.js.map +1 -1
  91. package/lib/plugins/create-app-package-json.js +23 -23
  92. package/lib/plugins/create-component-style.js +44 -44
  93. package/lib/plugins/create-package-json.js +48 -48
  94. package/lib/plugins/create-package-json.js.map +1 -1
  95. package/lib/plugins/dts.js +8 -8
  96. package/lib/plugins/html-system.js +9 -9
  97. package/lib/plugins/mock.js +240 -292
  98. package/lib/plugins/mock.js.map +1 -1
  99. package/lib/plugins/msw-adapter.js +141 -0
  100. package/lib/plugins/msw-adapter.js.map +1 -0
  101. package/lib/plugins/replace.js +17 -17
  102. package/lib/plugins/systemjs-bundle.js +15 -15
  103. package/lib/plugins/systemjs-bundle.js.map +1 -1
  104. package/package.json +6 -1
  105. package/templates/create/.gitignore.tpl +28 -8
  106. package/templates/create/AGENTS.md +76 -45
  107. package/templates/create/README.md +44 -11
  108. package/templates/create/package.json +2 -1
  109. package/templates/lib/.gitignore.tpl +31 -1
  110. package/templates/lib/.gitlab-ci.yml +2 -2
  111. package/templates/lib/README.md +24 -1
  112. package/templates/lib/{test → components/__tests__}/index.spec.ts +1 -1
  113. package/templates/lib/{test → components/__tests__}/index.spec.tsx +2 -2
  114. package/templates/lib/farris.config.mjs +2 -2
  115. package/templates/lib/package.json +1 -1
  116. package/templates/lib/tsconfig.json +1 -2
  117. package/templates/lib/vitest.config.ts +4 -2
  118. package/templates/micro/.eslintrc.cjs +15 -0
  119. package/templates/micro/.gitignore.tpl +33 -0
  120. package/templates/micro/.gitlab-ci.yml +59 -0
  121. package/templates/micro/.prettierrc.json +8 -0
  122. package/templates/micro/farris.config.mjs +34 -0
  123. package/templates/micro/index.html +12 -0
  124. package/templates/micro/mock/README.md +53 -0
  125. package/templates/micro/mock/tsconfig.json +10 -0
  126. package/templates/micro/package.json +47 -0
  127. package/templates/micro/src/App.tsx +25 -0
  128. package/templates/micro/src/locales/index.ts +5 -0
  129. package/templates/micro/src/locales/zh-CHS.json +1 -0
  130. package/templates/micro/src/main.ts +47 -0
  131. package/templates/micro/src/router/index.ts +24 -0
  132. package/templates/micro/src/views/AboutView.tsx +12 -0
  133. package/templates/micro/src/views/HomeView.tsx +13 -0
  134. package/templates/micro/src/views/__tests__/AboutView.spec.tsx +11 -0
  135. package/templates/micro/src/views/__tests__/HomeView.spec.tsx +11 -0
  136. package/templates/micro/tsconfig.json +18 -0
  137. package/templates/micro/vitest.config.ts +12 -0
  138. package/templates/mobile/.gitignore.tpl +25 -24
  139. package/templates/mobile/farris.config.mjs +2 -2
  140. package/templates/mobile/mock/README.md +39 -20
  141. package/templates/mobile/mock/tsconfig.json +10 -0
  142. package/templates/mobile/package.json +5 -2
  143. package/templates/mobile/src/main.ts +5 -40
  144. package/templates/mobile/src/views/HomeView.tsx +2 -2
  145. package/templates/mobile/src/views/__tests__/AboutView.spec.tsx +11 -0
  146. package/templates/mobile/src/views/__tests__/HomeView.spec.tsx +11 -0
  147. package/templates/mobile/tsconfig.json +1 -2
  148. package/templates/mobile/vitest.config.ts +4 -2
  149. package/templates/web/.gitignore.tpl +25 -24
  150. package/templates/web/farris.config.mjs +2 -2
  151. package/templates/web/mock/README.md +39 -20
  152. package/templates/web/mock/tsconfig.json +10 -0
  153. package/templates/web/package.json +5 -2
  154. package/templates/web/src/App.tsx +0 -4
  155. package/templates/web/src/main.ts +5 -40
  156. package/templates/web/src/views/HomeView.tsx +2 -2
  157. package/templates/web/src/views/__tests__/AboutView.spec.tsx +11 -0
  158. package/templates/web/src/views/__tests__/HomeView.spec.tsx +11 -0
  159. package/templates/web/tsconfig.json +1 -2
  160. package/templates/web/vitest.config.ts +4 -2
  161. package/templates/mobile/src/components/TheButton.tsx +0 -9
  162. package/templates/mobile/test/src/components/TheButton.spec.ts +0 -15
  163. package/templates/mobile/test/src/components/TheButton.spec.tsx +0 -15
  164. package/templates/web/src/components/TheButton.tsx +0 -9
  165. package/templates/web/src/micro-app.d.ts +0 -20
  166. package/templates/web/src/styles/index.css +0 -0
  167. package/templates/web/test/src/components/TheButton.spec.ts +0 -15
  168. package/templates/web/test/src/components/TheButton.spec.tsx +0 -15
  169. /package/templates/{web → micro}/src/micro/context.ts +0 -0
  170. /package/templates/{mobile → micro}/src/micro-app.d.ts +0 -0
  171. /package/templates/{mobile/src/styles/index.css → micro/src/styles/index.scss} +0 -0
@@ -1,174 +1,173 @@
1
- import { existsSync, lstatSync, readdirSync } from 'node:fs';
2
- import { resolve } from 'node:path';
3
- import { WorkspaceStatus } from './constant.js';
4
- import { LAYERED_CONTAINERS, LEGACY_CONTAINERS } from './project-layout.js';
5
- // 已知 AI 配置与项目元数据文件/目录(忽略列表)
6
- export const KNOWN_AI_AND_META_ENTRIES = new Set([
7
- '.git',
8
- '.gitignore',
9
- '.agent',
10
- '.agents',
11
- '.cursor',
12
- '.vscode',
13
- '.superpowers',
14
- '.changeset',
15
- '.github',
16
- '.codebuddy',
17
- '.trae',
18
- '.gemini',
19
- '.aider.conf.yml',
20
- 'agents.md',
21
- 'claude.md',
22
- 'gemini.md',
23
- '.windsurfrules',
24
- '.cursorrules'
25
- ]);
26
- // 工程证据关键文件。容器目录名一律取自 project-layout,避免字面量漂移
27
- export const PROJECT_EVIDENCE_FILES = [
28
- 'package.json',
29
- 'pnpm-workspace.yaml',
30
- 'vite.config.ts',
31
- 'vite.config.js',
32
- 'vite.config.mjs',
33
- 'farris.config.ts',
34
- 'farris.config.js',
35
- 'farris.config.mjs',
36
- 'tsconfig.json',
37
- 'src',
38
- ...LEGACY_CONTAINERS,
39
- ...LAYERED_CONTAINERS,
40
- 'node_modules'
41
- ];
42
- /**
43
- * 校验目标路径安全性,防止目录穿越与非法路径
44
- */
45
- export function validateTargetPath(target) {
46
- if (!target || typeof target !== 'string') {
47
- return { valid: false, resolved: '', error: '目标路径不能为空' };
48
- }
49
- const resolved = resolve(process.cwd(), target);
50
- // 检查是否在非法系统根或包含非法控制字符
51
- if (resolved.includes('\0')) {
52
- return { valid: false, resolved: '', error: '目标路径包含非法字符' };
53
- }
54
- return { valid: true, resolved };
55
- }
56
- /**
57
- * 扫描目标目录状态
58
- */
59
- export function inspectTarget(targetPath) {
60
- const pathValidation = validateTargetPath(targetPath);
61
- if (!pathValidation.valid) {
62
- return {
63
- status: WorkspaceStatus.INVALID_TARGET,
64
- canCreate: false,
65
- target: targetPath,
66
- exists: false,
67
- conflicts: [],
68
- preservedInputEntries: [],
69
- message: pathValidation.error
70
- };
71
- }
72
- const resolvedTarget = pathValidation.resolved;
73
- if (!existsSync(resolvedTarget)) {
74
- return {
75
- status: WorkspaceStatus.MISSING,
76
- canCreate: true,
77
- target: resolvedTarget,
78
- exists: false,
79
- conflicts: [],
80
- preservedInputEntries: []
81
- };
82
- }
83
- const stat = lstatSync(resolvedTarget);
84
- if (!stat.isDirectory()) {
85
- return {
86
- status: WorkspaceStatus.INVALID_TARGET,
87
- canCreate: false,
88
- target: resolvedTarget,
89
- exists: true,
90
- conflicts: [resolvedTarget],
91
- preservedInputEntries: [],
92
- message: '目标路径存在但不是目录'
93
- };
94
- }
95
- const entries = readdirSync(resolvedTarget);
96
- if (entries.length === 0) {
97
- return {
98
- status: WorkspaceStatus.EMPTY,
99
- canCreate: true,
100
- target: resolvedTarget,
101
- exists: true,
102
- conflicts: [],
103
- preservedInputEntries: []
104
- };
105
- }
106
- // 检查是否是现有 workspace
107
- const hasWorkspaceYaml = entries.includes('pnpm-workspace.yaml');
108
- if (hasWorkspaceYaml) {
109
- return {
110
- status: WorkspaceStatus.EXISTING_WORKSPACE,
111
- canCreate: false,
112
- target: resolvedTarget,
113
- exists: true,
114
- conflicts: ['pnpm-workspace.yaml'],
115
- preservedInputEntries: []
116
- };
117
- }
118
- // 检查是否包含已有工程特征
119
- const projectConflicts = entries.filter((e) => PROJECT_EVIDENCE_FILES.includes(e));
120
- if (projectConflicts.length > 0) {
121
- return {
122
- status: WorkspaceStatus.EXISTING_OR_UNKNOWN_PROJECT,
123
- canCreate: false,
124
- target: resolvedTarget,
125
- exists: true,
126
- conflicts: projectConflicts,
127
- preservedInputEntries: []
128
- };
129
- }
130
- // 分析是否全是 AI 配置或受保护输入
131
- let hasAiOnly = true;
132
- const preservedInputs = [];
133
- for (const entry of entries) {
134
- const lower = entry.toLowerCase();
135
- if (KNOWN_AI_AND_META_ENTRIES.has(lower)) {
136
- continue;
137
- }
138
- hasAiOnly = false;
139
- // 属于非冲突的用户保留文件(如 docs, prd.md, design.md 等)
140
- preservedInputs.push(entry);
141
- }
142
- if (hasAiOnly) {
143
- return {
144
- status: WorkspaceStatus.AI_CONFIG_ONLY,
145
- canCreate: true,
146
- target: resolvedTarget,
147
- exists: true,
148
- conflicts: [],
149
- preservedInputEntries: []
150
- };
151
- }
152
- return {
153
- status: WorkspaceStatus.PRESERVED_INPUT_ONLY,
154
- canCreate: true,
155
- target: resolvedTarget,
156
- exists: true,
157
- conflicts: [],
158
- preservedInputEntries: preservedInputs
159
- };
160
- }
161
- /**
162
- * 预检计划写入的文件与已有文件是否存在冲突
163
- */
164
- export function checkPlannedConflicts(targetDir, plannedRelativeFiles) {
165
- const conflicts = [];
166
- for (const relFile of plannedRelativeFiles) {
167
- const fullPath = resolve(targetDir, relFile);
168
- if (existsSync(fullPath)) {
169
- conflicts.push(relFile);
170
- }
171
- }
172
- return conflicts;
173
- }
1
+ import { existsSync, lstatSync, readdirSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+ import { WorkspaceStatus } from './constant.js';
4
+ import { CONTAINERS } from './project-layout.js';
5
+ // 已知 AI 配置与项目元数据文件/目录(忽略列表)
6
+ export const KNOWN_AI_AND_META_ENTRIES = new Set([
7
+ '.git',
8
+ '.gitignore',
9
+ '.agent',
10
+ '.agents',
11
+ '.cursor',
12
+ '.vscode',
13
+ '.superpowers',
14
+ '.changeset',
15
+ '.github',
16
+ '.codebuddy',
17
+ '.trae',
18
+ '.gemini',
19
+ '.aider.conf.yml',
20
+ 'agents.md',
21
+ 'claude.md',
22
+ 'gemini.md',
23
+ '.windsurfrules',
24
+ '.cursorrules'
25
+ ]);
26
+ // 工程证据关键文件。容器目录名一律取自 project-layout,避免字面量漂移
27
+ export const PROJECT_EVIDENCE_FILES = [
28
+ 'package.json',
29
+ 'pnpm-workspace.yaml',
30
+ 'vite.config.ts',
31
+ 'vite.config.js',
32
+ 'vite.config.mjs',
33
+ 'farris.config.ts',
34
+ 'farris.config.js',
35
+ 'farris.config.mjs',
36
+ 'tsconfig.json',
37
+ 'src',
38
+ ...CONTAINERS,
39
+ 'node_modules'
40
+ ];
41
+ /**
42
+ * 校验目标路径安全性,防止目录穿越与非法路径
43
+ */
44
+ export function validateTargetPath(target) {
45
+ if (!target || typeof target !== 'string') {
46
+ return { valid: false, resolved: '', error: '目标路径不能为空' };
47
+ }
48
+ const resolved = resolve(process.cwd(), target);
49
+ // 检查是否在非法系统根或包含非法控制字符
50
+ if (resolved.includes('\0')) {
51
+ return { valid: false, resolved: '', error: '目标路径包含非法字符' };
52
+ }
53
+ return { valid: true, resolved };
54
+ }
55
+ /**
56
+ * 扫描目标目录状态
57
+ */
58
+ export function inspectTarget(targetPath) {
59
+ const pathValidation = validateTargetPath(targetPath);
60
+ if (!pathValidation.valid) {
61
+ return {
62
+ status: WorkspaceStatus.INVALID_TARGET,
63
+ canCreate: false,
64
+ target: targetPath,
65
+ exists: false,
66
+ conflicts: [],
67
+ preservedInputEntries: [],
68
+ message: pathValidation.error
69
+ };
70
+ }
71
+ const resolvedTarget = pathValidation.resolved;
72
+ if (!existsSync(resolvedTarget)) {
73
+ return {
74
+ status: WorkspaceStatus.MISSING,
75
+ canCreate: true,
76
+ target: resolvedTarget,
77
+ exists: false,
78
+ conflicts: [],
79
+ preservedInputEntries: []
80
+ };
81
+ }
82
+ const stat = lstatSync(resolvedTarget);
83
+ if (!stat.isDirectory()) {
84
+ return {
85
+ status: WorkspaceStatus.INVALID_TARGET,
86
+ canCreate: false,
87
+ target: resolvedTarget,
88
+ exists: true,
89
+ conflicts: [resolvedTarget],
90
+ preservedInputEntries: [],
91
+ message: '目标路径存在但不是目录'
92
+ };
93
+ }
94
+ const entries = readdirSync(resolvedTarget);
95
+ if (entries.length === 0) {
96
+ return {
97
+ status: WorkspaceStatus.EMPTY,
98
+ canCreate: true,
99
+ target: resolvedTarget,
100
+ exists: true,
101
+ conflicts: [],
102
+ preservedInputEntries: []
103
+ };
104
+ }
105
+ // 检查是否是现有 workspace
106
+ const hasWorkspaceYaml = entries.includes('pnpm-workspace.yaml');
107
+ if (hasWorkspaceYaml) {
108
+ return {
109
+ status: WorkspaceStatus.EXISTING_WORKSPACE,
110
+ canCreate: false,
111
+ target: resolvedTarget,
112
+ exists: true,
113
+ conflicts: ['pnpm-workspace.yaml'],
114
+ preservedInputEntries: []
115
+ };
116
+ }
117
+ // 检查是否包含已有工程特征
118
+ const projectConflicts = entries.filter((e) => PROJECT_EVIDENCE_FILES.includes(e));
119
+ if (projectConflicts.length > 0) {
120
+ return {
121
+ status: WorkspaceStatus.EXISTING_OR_UNKNOWN_PROJECT,
122
+ canCreate: false,
123
+ target: resolvedTarget,
124
+ exists: true,
125
+ conflicts: projectConflicts,
126
+ preservedInputEntries: []
127
+ };
128
+ }
129
+ // 分析是否全是 AI 配置或受保护输入
130
+ let hasAiOnly = true;
131
+ const preservedInputs = [];
132
+ for (const entry of entries) {
133
+ const lower = entry.toLowerCase();
134
+ if (KNOWN_AI_AND_META_ENTRIES.has(lower)) {
135
+ continue;
136
+ }
137
+ hasAiOnly = false;
138
+ // 属于非冲突的用户保留文件(如 docs, prd.md, design.md 等)
139
+ preservedInputs.push(entry);
140
+ }
141
+ if (hasAiOnly) {
142
+ return {
143
+ status: WorkspaceStatus.AI_CONFIG_ONLY,
144
+ canCreate: true,
145
+ target: resolvedTarget,
146
+ exists: true,
147
+ conflicts: [],
148
+ preservedInputEntries: []
149
+ };
150
+ }
151
+ return {
152
+ status: WorkspaceStatus.PRESERVED_INPUT_ONLY,
153
+ canCreate: true,
154
+ target: resolvedTarget,
155
+ exists: true,
156
+ conflicts: [],
157
+ preservedInputEntries: preservedInputs
158
+ };
159
+ }
160
+ /**
161
+ * 预检计划写入的文件与已有文件是否存在冲突
162
+ */
163
+ export function checkPlannedConflicts(targetDir, plannedRelativeFiles) {
164
+ const conflicts = [];
165
+ for (const relFile of plannedRelativeFiles) {
166
+ const fullPath = resolve(targetDir, relFile);
167
+ if (existsSync(fullPath)) {
168
+ conflicts.push(relFile);
169
+ }
170
+ }
171
+ return conflicts;
172
+ }
174
173
  //# sourceMappingURL=safety.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"safety.js","sourceRoot":"","sources":["../../src/common/safety.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"safety.js","sourceRoot":"","sources":["../../src/common/safety.ts"],"names":[],"mappings":"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"}
@@ -1,15 +1,19 @@
1
- import { resolve } from 'node:path';
2
- import { getDirname } from './constant.js';
3
- export function resolveTemplate(input) {
4
- if (input.type === 'lib') {
5
- return { id: 'lib' };
6
- }
7
- if (!input.platform) {
8
- throw new Error('platform is required when type is app');
9
- }
10
- return { id: input.platform };
11
- }
12
- export function getTemplateDir(id) {
13
- return resolve(getDirname(import.meta.url), '../../templates', id);
14
- }
1
+ import { resolve } from 'node:path';
2
+ import { getDirname } from './constant.js';
3
+ export function resolveTemplate(input) {
4
+ if (input.type === 'lib') {
5
+ return { id: 'lib' };
6
+ }
7
+ // 微应用只做 web(D3):自带微前端契约、交付 iife 单文件
8
+ if (input.type === 'micro') {
9
+ return { id: 'micro' };
10
+ }
11
+ if (!input.platform) {
12
+ throw new Error('platform is required when type is app');
13
+ }
14
+ return { id: input.platform };
15
+ }
16
+ export function getTemplateDir(id) {
17
+ return resolve(getDirname(import.meta.url), '../../templates', id);
18
+ }
15
19
  //# sourceMappingURL=template-manifest.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"template-manifest.js","sourceRoot":"","sources":["../../src/common/template-manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAQ3C,MAAM,UAAU,eAAe,CAAC,KAG/B;IACC,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK,EAAE;QACxB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC;KACtB;IACD,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE;QACnB,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;KAC1D;IACD,OAAO,EAAE,EAAE,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,EAAc;IAC3C,OAAO,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,iBAAiB,EAAE,EAAE,CAAC,CAAC;AACrE,CAAC"}
1
+ {"version":3,"file":"template-manifest.js","sourceRoot":"","sources":["../../src/common/template-manifest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAQ3C,MAAM,UAAU,eAAe,CAAC,KAG/B;IACC,IAAI,KAAK,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;QACzB,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC;IACvB,CAAC;IACD,oCAAoC;IACpC,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAC3B,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC;IACzB,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;QACpB,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;IAC3D,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,EAAc;IAC3C,OAAO,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,iBAAiB,EAAE,EAAE,CAAC,CAAC;AACrE,CAAC"}
@@ -1,115 +1,115 @@
1
- import { existsSync, readdirSync, statSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
2
- import { join, dirname, relative } from 'node:path';
3
- /**
4
- * 将变量替换为模板内容
5
- */
6
- export function renderTemplateString(content, variables) {
7
- let result = content;
8
- Object.keys(variables).forEach((key) => {
9
- const value = variables[key];
10
- const regexp = new RegExp(`<%=\\s*${key}\\s*%>`, 'g');
11
- let strValue = '';
12
- if (typeof value === 'object' && value !== null) {
13
- if (Array.isArray(value)) {
14
- strValue = JSON.stringify(value);
15
- }
16
- else {
17
- strValue = JSON.stringify(value, null, 2);
18
- }
19
- }
20
- else {
21
- strValue = String(value ?? '');
22
- }
23
- result = result.replace(regexp, () => strValue);
24
- });
25
- return result;
26
- }
27
- /**
28
- * 检测文件内容中未被替换的 <%= xxx %> 占位符
29
- */
30
- export function findUnreplacedPlaceholders(content) {
31
- const matches = content.match(/<%=\s*[\w.-]+\s*%>/g);
32
- return matches ? Array.from(new Set(matches)) : [];
33
- }
34
- /**
35
- * 仅枚举模板渲染后会写入的相对路径,不产生任何文件系统写入。
36
- */
37
- export function collectTemplateOutputPaths(sourceDir, filter) {
38
- const outputs = [];
39
- function walk(currentSrc) {
40
- if (!existsSync(currentSrc)) {
41
- return;
42
- }
43
- for (const entry of readdirSync(currentSrc)) {
44
- const srcPath = join(currentSrc, entry);
45
- const relativePath = relative(sourceDir, srcPath).replace(/\\/g, '/');
46
- if (filter && !filter(relativePath)) {
47
- continue;
48
- }
49
- if (statSync(srcPath).isDirectory()) {
50
- walk(srcPath);
51
- }
52
- else {
53
- outputs.push(relativePath.endsWith('.tpl') ? relativePath.slice(0, -4) : relativePath);
54
- }
55
- }
56
- }
57
- walk(sourceDir);
58
- return outputs;
59
- }
60
- /**
61
- * 递归渲染模板目录到目标目录
62
- */
63
- export function renderTemplateDir(options) {
64
- const { sourceDir, targetDir, variables, skipExisting = false, filter } = options;
65
- const filesCreated = [];
66
- const unreplaced = [];
67
- function walk(currentSrc, currentTarget) {
68
- if (!existsSync(currentSrc)) {
69
- return;
70
- }
71
- const entries = readdirSync(currentSrc);
72
- for (const entry of entries) {
73
- const srcPath = join(currentSrc, entry);
74
- const stat = statSync(srcPath);
75
- const relPath = relative(sourceDir, srcPath).replace(/\\/g, '/');
76
- if (filter && !filter(relPath)) {
77
- continue;
78
- }
79
- if (stat.isDirectory()) {
80
- const nextTarget = join(currentTarget, entry);
81
- walk(srcPath, nextTarget);
82
- }
83
- else {
84
- // 去掉 .tpl 后缀
85
- let targetFileName = entry;
86
- if (targetFileName.endsWith('.tpl')) {
87
- targetFileName = targetFileName.slice(0, -4);
88
- }
89
- const targetFilePath = join(currentTarget, targetFileName);
90
- if (existsSync(targetFilePath) && skipExisting) {
91
- continue;
92
- }
93
- const rawContent = readFileSync(srcPath, 'utf-8');
94
- const rendered = renderTemplateString(rawContent, variables);
95
- const leftovers = findUnreplacedPlaceholders(rendered);
96
- if (leftovers.length > 0) {
97
- leftovers.forEach((p) => {
98
- unreplaced.push({ file: targetFilePath, placeholder: p });
99
- });
100
- }
101
- if (!existsSync(dirname(targetFilePath))) {
102
- mkdirSync(dirname(targetFilePath), { recursive: true });
103
- }
104
- writeFileSync(targetFilePath, rendered, 'utf-8');
105
- filesCreated.push(targetFilePath);
106
- }
107
- }
108
- }
109
- walk(sourceDir, targetDir);
110
- return {
111
- filesCreated,
112
- unreplacedPlaceholders: unreplaced
113
- };
114
- }
1
+ import { existsSync, readdirSync, statSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
2
+ import { join, dirname, relative } from 'node:path';
3
+ /**
4
+ * 将变量替换为模板内容
5
+ */
6
+ export function renderTemplateString(content, variables) {
7
+ let result = content;
8
+ Object.keys(variables).forEach((key) => {
9
+ const value = variables[key];
10
+ const regexp = new RegExp(`<%=\\s*${key}\\s*%>`, 'g');
11
+ let strValue = '';
12
+ if (typeof value === 'object' && value !== null) {
13
+ if (Array.isArray(value)) {
14
+ strValue = JSON.stringify(value);
15
+ }
16
+ else {
17
+ strValue = JSON.stringify(value, null, 2);
18
+ }
19
+ }
20
+ else {
21
+ strValue = String(value ?? '');
22
+ }
23
+ result = result.replace(regexp, () => strValue);
24
+ });
25
+ return result;
26
+ }
27
+ /**
28
+ * 检测文件内容中未被替换的 <%= xxx %> 占位符
29
+ */
30
+ export function findUnreplacedPlaceholders(content) {
31
+ const matches = content.match(/<%=\s*[\w.-]+\s*%>/g);
32
+ return matches ? Array.from(new Set(matches)) : [];
33
+ }
34
+ /**
35
+ * 仅枚举模板渲染后会写入的相对路径,不产生任何文件系统写入。
36
+ */
37
+ export function collectTemplateOutputPaths(sourceDir, filter) {
38
+ const outputs = [];
39
+ function walk(currentSrc) {
40
+ if (!existsSync(currentSrc)) {
41
+ return;
42
+ }
43
+ for (const entry of readdirSync(currentSrc)) {
44
+ const srcPath = join(currentSrc, entry);
45
+ const relativePath = relative(sourceDir, srcPath).replace(/\\/g, '/');
46
+ if (filter && !filter(relativePath)) {
47
+ continue;
48
+ }
49
+ if (statSync(srcPath).isDirectory()) {
50
+ walk(srcPath);
51
+ }
52
+ else {
53
+ outputs.push(relativePath.endsWith('.tpl') ? relativePath.slice(0, -4) : relativePath);
54
+ }
55
+ }
56
+ }
57
+ walk(sourceDir);
58
+ return outputs;
59
+ }
60
+ /**
61
+ * 递归渲染模板目录到目标目录
62
+ */
63
+ export function renderTemplateDir(options) {
64
+ const { sourceDir, targetDir, variables, skipExisting = false, filter } = options;
65
+ const filesCreated = [];
66
+ const unreplaced = [];
67
+ function walk(currentSrc, currentTarget) {
68
+ if (!existsSync(currentSrc)) {
69
+ return;
70
+ }
71
+ const entries = readdirSync(currentSrc);
72
+ for (const entry of entries) {
73
+ const srcPath = join(currentSrc, entry);
74
+ const stat = statSync(srcPath);
75
+ const relPath = relative(sourceDir, srcPath).replace(/\\/g, '/');
76
+ if (filter && !filter(relPath)) {
77
+ continue;
78
+ }
79
+ if (stat.isDirectory()) {
80
+ const nextTarget = join(currentTarget, entry);
81
+ walk(srcPath, nextTarget);
82
+ }
83
+ else {
84
+ // 去掉 .tpl 后缀
85
+ let targetFileName = entry;
86
+ if (targetFileName.endsWith('.tpl')) {
87
+ targetFileName = targetFileName.slice(0, -4);
88
+ }
89
+ const targetFilePath = join(currentTarget, targetFileName);
90
+ if (existsSync(targetFilePath) && skipExisting) {
91
+ continue;
92
+ }
93
+ const rawContent = readFileSync(srcPath, 'utf-8');
94
+ const rendered = renderTemplateString(rawContent, variables);
95
+ const leftovers = findUnreplacedPlaceholders(rendered);
96
+ if (leftovers.length > 0) {
97
+ leftovers.forEach((p) => {
98
+ unreplaced.push({ file: targetFilePath, placeholder: p });
99
+ });
100
+ }
101
+ if (!existsSync(dirname(targetFilePath))) {
102
+ mkdirSync(dirname(targetFilePath), { recursive: true });
103
+ }
104
+ writeFileSync(targetFilePath, rendered, 'utf-8');
105
+ filesCreated.push(targetFilePath);
106
+ }
107
+ }
108
+ }
109
+ walk(sourceDir, targetDir);
110
+ return {
111
+ filesCreated,
112
+ unreplacedPlaceholders: unreplaced
113
+ };
114
+ }
115
115
  //# sourceMappingURL=template-renderer.js.map