akfun 5.2.12 → 5.2.13

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # AKFun 前端脚手架
2
2
 
3
- AKFun 是一个基于 Webpack 与 Rollup 的多场景前端打包工具,支持 Vue、React、React+TS 技术栈,致力于提供"零配置、开箱即用"的工程能力,让开发者专注业务。
3
+ AKFun 是一个基于 Webpack 与 Rollup 的多场景前端打包工具,支持 Vue、React、React+TS 技术栈,致力于提供"零配置、开箱即用"的前端工程能力,让开发者专注于业务开发。
4
4
 
5
5
  ## 目录
6
6
 
@@ -20,10 +20,10 @@ AKFun 是一个基于 Webpack 与 Rollup 的多场景前端打包工具,支持
20
20
 
21
21
  ## 主要特性
22
22
 
23
- - **零配置**: 内置默认配置,开箱即用
24
- - **多技术栈**: 支持 Vue、React、React+TS 的调试与构建
25
- - **多构建场景**: 本地开发(含热更新/代理)、生产构建、库构建(UMD/ESM)、Node 模块构建
26
- - **灵活可配**: 支持入口、别名、代理、SASS 注入、ESLint/StyleLint、Babel/Loader/Plugin 扩展等配置
23
+ - **零配置**: 内置默认配置(前端工程最佳实践),开箱可用
24
+ - **多技术体系支持**: 支持 Vue、React、React+TS 的调试与构建
25
+ - **多种构建类型**: 本地开发(含热更新/代理)、生产构建、库构建(UMD/ESM)、Node 模块构建
26
+ - **灵活可配**: 支持入口、别名、代理、公共样式注入、ESLint/StyleLint、Babel/Loader/Plugin 扩展等配置
27
27
  - **样式与规范**: 集成 Autoprefixer、Sass、PostCSS、ESLint、StyleLint
28
28
  - **参数替换**: 支持基于 [params-replace-loader](https://www.npmjs.com/package/params-replace-loader) 的环境变量批量替换
29
29
  - **模板支持**: 提供完整的 Vue/React 项目模板
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "akfun",
3
- "version": "5.2.12",
3
+ "version": "5.2.13",
4
4
  "description": "前端脚手架:支持Vue技术栈和react技术栈",
5
5
  "keywords": [
6
6
  "前端工程",
@@ -5,8 +5,26 @@
5
5
  * - 在构建 React 组件时,自动为组件的根元素添加 data-scope 属性
6
6
  * - 支持函数组件和类组件
7
7
  * - 如果组件返回 Fragment 或数组,会在 Fragment 的第一个子元素或数组的第一个元素上添加
8
+ * - data-scope 的值默认为当前组件所在文件目录的名称
8
9
  */
10
+ const path = require('path');
11
+
9
12
  module.exports = function ({ types: t }) {
13
+ /**
14
+ * 获取文件所在目录的名称
15
+ */
16
+ function getDirectoryName(filePath) {
17
+ if (!filePath) {
18
+ return '';
19
+ }
20
+ try {
21
+ const dirPath = path.dirname(filePath);
22
+ const dirName = path.basename(dirPath);
23
+ return dirName || '';
24
+ } catch (e) {
25
+ return '';
26
+ }
27
+ }
10
28
  /**
11
29
  * 检查函数是否返回 JSX(用于判断是否是 React 组件)
12
30
  */
@@ -39,8 +57,10 @@ module.exports = function ({ types: t }) {
39
57
 
40
58
  /**
41
59
  * 在 JSX 元素上添加 data-scope 属性
60
+ * @param {Object} jsxElement - JSX 元素节点
61
+ * @param {string} scopeValue - data-scope 的值,默认为空字符串
42
62
  */
43
- function addDataScopeAttribute(jsxElement) {
63
+ function addDataScopeAttribute(jsxElement, scopeValue = '') {
44
64
  const openingElement = jsxElement.openingElement;
45
65
  const attributes = openingElement.attributes;
46
66
 
@@ -50,8 +70,11 @@ module.exports = function ({ types: t }) {
50
70
  );
51
71
 
52
72
  if (!hasDataScope) {
53
- // 创建 data-scope 属性
54
- const dataScopeAttr = t.jsxAttribute(t.jsxIdentifier('data-scope'), t.stringLiteral(''));
73
+ // 创建 data-scope 属性,值为目录名
74
+ const dataScopeAttr = t.jsxAttribute(
75
+ t.jsxIdentifier('data-scope'),
76
+ t.stringLiteral(scopeValue)
77
+ );
55
78
  // 添加到属性列表的开头
56
79
  attributes.unshift(dataScopeAttr);
57
80
  }
@@ -59,8 +82,10 @@ module.exports = function ({ types: t }) {
59
82
 
60
83
  /**
61
84
  * 处理返回语句中的 JSX
85
+ * @param {Object} path - Babel path 对象
86
+ * @param {string} scopeValue - data-scope 的值
62
87
  */
63
- function processReturnStatement(path) {
88
+ function processReturnStatement(path, scopeValue) {
64
89
  const argument = path.get('argument');
65
90
 
66
91
  if (!argument.node) {
@@ -69,7 +94,7 @@ module.exports = function ({ types: t }) {
69
94
 
70
95
  // 处理 JSX 元素
71
96
  if (argument.isJSXElement()) {
72
- addDataScopeAttribute(argument.node);
97
+ addDataScopeAttribute(argument.node, scopeValue);
73
98
  return;
74
99
  }
75
100
 
@@ -79,12 +104,12 @@ module.exports = function ({ types: t }) {
79
104
  // 找到第一个 JSX 元素子节点
80
105
  for (const child of children) {
81
106
  if (child.isJSXElement()) {
82
- addDataScopeAttribute(child.node);
107
+ addDataScopeAttribute(child.node, scopeValue);
83
108
  return;
84
109
  }
85
110
  // 如果子节点是 Fragment,递归处理
86
111
  if (child.isJSXFragment()) {
87
- processReturnStatement(child);
112
+ processReturnStatement(child, scopeValue);
88
113
  return;
89
114
  }
90
115
  }
@@ -96,7 +121,12 @@ module.exports = function ({ types: t }) {
96
121
  // 找到第一个 JSX 元素
97
122
  for (const element of elements) {
98
123
  if (element.isJSXElement()) {
99
- addDataScopeAttribute(element.node);
124
+ addDataScopeAttribute(element.node, scopeValue);
125
+ return;
126
+ }
127
+ // 如果元素是 Fragment,递归处理
128
+ if (element.isJSXFragment()) {
129
+ processReturnStatement(element, scopeValue);
100
130
  return;
101
131
  }
102
132
  }
@@ -107,27 +137,37 @@ module.exports = function ({ types: t }) {
107
137
  // 处理 true 分支
108
138
  const consequent = argument.get('consequent');
109
139
  if (consequent.isJSXElement()) {
110
- addDataScopeAttribute(consequent.node);
140
+ addDataScopeAttribute(consequent.node, scopeValue);
111
141
  } else if (consequent.isJSXFragment() || consequent.isArrayExpression()) {
112
- processReturnStatement(consequent);
142
+ processReturnStatement(consequent, scopeValue);
113
143
  }
114
144
 
115
145
  // 处理 false 分支
116
146
  const alternate = argument.get('alternate');
117
147
  if (alternate.isJSXElement()) {
118
- addDataScopeAttribute(alternate.node);
148
+ addDataScopeAttribute(alternate.node, scopeValue);
119
149
  } else if (alternate.isJSXFragment() || alternate.isArrayExpression()) {
120
- processReturnStatement(alternate);
150
+ processReturnStatement(alternate, scopeValue);
121
151
  }
122
152
  }
123
153
 
124
154
  // 处理逻辑表达式(&& 或 ||)
125
155
  if (argument.isLogicalExpression()) {
156
+ const left = argument.get('left');
126
157
  const right = argument.get('right');
158
+
159
+ // 处理左侧(可能是 JSX)
160
+ if (left.isJSXElement()) {
161
+ addDataScopeAttribute(left.node, scopeValue);
162
+ } else if (left.isJSXFragment() || left.isArrayExpression()) {
163
+ processReturnStatement(left, scopeValue);
164
+ }
165
+
166
+ // 处理右侧(可能是 JSX)
127
167
  if (right.isJSXElement()) {
128
- addDataScopeAttribute(right.node);
168
+ addDataScopeAttribute(right.node, scopeValue);
129
169
  } else if (right.isJSXFragment() || right.isArrayExpression()) {
130
- processReturnStatement(right);
170
+ processReturnStatement(right, scopeValue);
131
171
  }
132
172
  }
133
173
  }
@@ -135,30 +175,39 @@ module.exports = function ({ types: t }) {
135
175
  return {
136
176
  visitor: {
137
177
  // 处理函数组件和箭头函数组件
138
- Function(path) {
178
+ Function(functionPath) {
139
179
  // 只处理返回 JSX 的函数(即 React 组件)
140
- if (!returnsJSX(path)) {
180
+ if (!returnsJSX(functionPath)) {
141
181
  return;
142
182
  }
143
183
 
144
- const body = path.get('body');
184
+ // 获取当前文件路径并提取目录名
185
+ const filePath = functionPath.hub?.file?.opts?.filename || '';
186
+ const scopeValue = getDirectoryName(filePath);
187
+
188
+ const body = functionPath.get('body');
145
189
 
146
190
  // 处理函数体中的返回语句
147
191
  if (body.isBlockStatement()) {
148
192
  body.traverse({
149
193
  ReturnStatement(returnPath) {
150
- processReturnStatement(returnPath);
194
+ processReturnStatement(returnPath, scopeValue);
151
195
  }
152
196
  });
153
197
  } else if (body.isJSXElement()) {
154
198
  // 箭头函数直接返回 JSX
155
- addDataScopeAttribute(body.node);
199
+ addDataScopeAttribute(body.node, scopeValue);
156
200
  } else if (body.isJSXFragment()) {
157
201
  // 箭头函数直接返回 Fragment
158
202
  const children = body.get('children');
159
203
  for (const child of children) {
160
204
  if (child.isJSXElement()) {
161
- addDataScopeAttribute(child.node);
205
+ addDataScopeAttribute(child.node, scopeValue);
206
+ return;
207
+ }
208
+ // 如果子节点是 Fragment,递归处理
209
+ if (child.isJSXFragment()) {
210
+ processReturnStatement(child, scopeValue);
162
211
  return;
163
212
  }
164
213
  }
@@ -166,13 +215,17 @@ module.exports = function ({ types: t }) {
166
215
  },
167
216
 
168
217
  // 处理类组件的 render 方法
169
- ClassMethod(path) {
170
- if (path.node.kind === 'method' && path.node.key.name === 'render') {
171
- const body = path.get('body');
218
+ ClassMethod(classMethodPath) {
219
+ if (classMethodPath.node.kind === 'method' && classMethodPath.node.key.name === 'render') {
220
+ // 获取当前文件路径并提取目录名
221
+ const filePath = classMethodPath.hub?.file?.opts?.filename || '';
222
+ const scopeValue = getDirectoryName(filePath);
223
+
224
+ const body = classMethodPath.get('body');
172
225
  if (body.isBlockStatement()) {
173
226
  body.traverse({
174
227
  ReturnStatement(returnPath) {
175
- processReturnStatement(returnPath);
228
+ processReturnStatement(returnPath, scopeValue);
176
229
  }
177
230
  });
178
231
  }
@@ -108,7 +108,7 @@ module.exports = function (curConfig, curEnvConfig) {
108
108
  : undefined,
109
109
  babel({
110
110
  extensions: ['.js', '.jsx', '.ts', '.tsx'],
111
- ...babelConfig,
111
+ ...babelConfig
112
112
  }), // 备注,需要先babel()再commjs()
113
113
  buildType === 'ts' ? undefined : jsx({ factory: 'React.createElement' }),
114
114
  vue(),