ez-saga 18.0.5 → 18.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/README.md CHANGED
@@ -142,4 +142,55 @@ function stateMapProps(state, props) {
142
142
  export default connect(stateMapProps)(View);
143
143
  ```
144
144
 
145
- We can invoke reducers and effects using dispatch, and we can obtain the results returned by effects.
145
+ We can invoke reducers and effects using dispatch, and we can obtain the results returned by effects.
146
+
147
+ # Vite Plugin for HMR
148
+
149
+ `ez-saga` provides a built-in Vite plugin to support Hot Module Replacement (HMR) for models. This allows you to modify effects and reducers during development without reloading the page.
150
+
151
+ ## Usage
152
+
153
+ 1. Import the plugin in your `vite.config.ts`:
154
+
155
+ ```typescript
156
+ import { defineConfig } from 'vite';
157
+ import ezSagaHmr from 'ez-saga/vite';
158
+
159
+ export default defineConfig({
160
+ plugins: [
161
+ ezSagaHmr(), // Add ez-saga HMR plugin
162
+ // other plugins...
163
+ ]
164
+ });
165
+ ```
166
+
167
+ 2. That's it!
168
+
169
+ The plugin automatically detects your model files and injects hot update logic. When you modify a model file, `ez-saga` will:
170
+ - Cancel old saga tasks.
171
+ - Re-register the model reducers (hot-swap logic).
172
+ - Restart the effects.
173
+
174
+ **Note**: This requires your model files to be exported using `export default` and contain a `name` property.
175
+
176
+ # Webpack Plugin for HMR
177
+
178
+ For Webpack 5 users, `ez-saga` also provides a compatible HMR plugin.
179
+
180
+ ## Usage
181
+
182
+ 1. Import the plugin in your `webpack.config.js`:
183
+
184
+ ```javascript
185
+ const EzSagaWebpackPlugin = require('ez-saga/webpack').default; // Notice the .default for CJS
186
+
187
+ module.exports = {
188
+ // ...
189
+ plugins: [
190
+ new EzSagaWebpackPlugin(),
191
+ // other plugins...
192
+ ]
193
+ };
194
+ ```
195
+
196
+ This plugin automatically configures the HMR loader for your model files.
@@ -1,3 +1,9 @@
1
+ /**
2
+ * 判断是否为标准Action
3
+ */
4
+ function isAction(action) {
5
+ return typeof action === 'object' && action !== null && 'type' in action;
6
+ }
1
7
  /**
2
8
  * 创建中间层
3
9
  * @param registedModel 已注册model
@@ -6,22 +12,18 @@ function createPromiseMiddleware(registedModel) {
6
12
  function isEffect(type) {
7
13
  if (!type || typeof type !== 'string')
8
14
  return false;
15
+ // 性能优化:快速排除不包含 '/' 的普通 Action
16
+ if (type.indexOf('/') === -1)
17
+ return false;
9
18
  const [modelName, effect] = type.split('/');
10
19
  const model = registedModel[modelName];
11
- if (model) {
12
- if (model.effects && model.effects[effect]) {
13
- return true;
14
- }
20
+ if (model && model.effects && model.effects[effect]) {
21
+ return true;
15
22
  }
16
23
  return false;
17
24
  }
18
25
  return (api) => (next) => (action) => {
19
- let exeEffect = false;
20
- if (action?.type !== undefined) {
21
- const { type } = action;
22
- exeEffect = isEffect(type);
23
- }
24
- if (exeEffect) {
26
+ if (isAction(action) && isEffect(action.type)) {
25
27
  return new Promise((resolve, reject) => {
26
28
  next({
27
29
  _dy_resolve: resolve,
@@ -30,9 +32,7 @@ function createPromiseMiddleware(registedModel) {
30
32
  });
31
33
  });
32
34
  }
33
- else {
34
- return next(action);
35
- }
35
+ return next(action);
36
36
  };
37
37
  }
38
38
  export default createPromiseMiddleware;
@@ -1 +1 @@
1
- {"version":3,"file":"PromiseMiddleware.js","sourceRoot":"","sources":["../../src/redux/PromiseMiddleware.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,SAAS,uBAAuB,CAAqB,aAA4B;IAC/E,SAAS,QAAQ,CAAC,IAAY;QAC5B,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,OAAO,KAAK,CAAC;QACpD,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC5C,MAAM,KAAK,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC;QACvC,IAAI,KAAK,EAAE,CAAC;YACV,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC3C,OAAO,IAAI,CAAC;YACd,CAAC;QACH,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,CAAC,GAA0B,EAAE,EAAE,CAAC,CAAC,IAA8B,EAAE,EAAE,CAAC,CAAC,MAAe,EAAE,EAAE;QAC7F,IAAI,SAAS,GAAG,KAAK,CAAC;QACtB,IAAK,MAAiB,EAAE,IAAI,KAAK,SAAS,EAAE,CAAC;YAC3C,MAAM,EAAE,IAAI,EAAE,GAAG,MAAgB,CAAC;YAClC,SAAS,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;QAC7B,CAAC;QACD,IAAI,SAAS,EAAE,CAAC;YACd,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBACrC,IAAI,CAAC;oBACH,WAAW,EAAE,OAAO;oBACpB,UAAU,EAAE,MAAM;oBAClB,GAAG,MAAgB;iBACpB,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;QACL,CAAC;aAAM,CAAC;YACN,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC;QACtB,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED,eAAe,uBAAuB,CAAC"}
1
+ {"version":3,"file":"PromiseMiddleware.js","sourceRoot":"","sources":["../../src/redux/PromiseMiddleware.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,SAAS,QAAQ,CAAC,MAAe;IAC/B,OAAO,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,IAAI,MAAM,CAAC;AAC3E,CAAC;AAED;;;GAGG;AACH,SAAS,uBAAuB,CAAqB,aAA4B;IAC/E,SAAS,QAAQ,CAAC,IAAY;QAC5B,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,OAAO,KAAK,CAAC;QAEpD,8BAA8B;QAC9B,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YAAE,OAAO,KAAK,CAAC;QAE3C,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC5C,MAAM,KAAK,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC;QACvC,IAAI,KAAK,IAAI,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YACpD,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,CAAC,GAA0B,EAAE,EAAE,CAAC,CAAC,IAA8B,EAAE,EAAE,CAAC,CAAC,MAAe,EAAE,EAAE;QAC7F,IAAI,QAAQ,CAAC,MAAM,CAAC,IAAI,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBACrC,IAAI,CAAC;oBACH,WAAW,EAAE,OAAO;oBACpB,UAAU,EAAE,MAAM;oBAClB,GAAG,MAAM;iBACV,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;QACL,CAAC;QACD,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC;IACtB,CAAC,CAAC;AACJ,CAAC;AAED,eAAe,uBAAuB,CAAC"}
@@ -1,28 +1,69 @@
1
- import { legacy_createStore as createStore, applyMiddleware, compose } from 'redux';
2
- import { createSlice, combineReducers } from '@reduxjs/toolkit';
1
+ import { configureStore, createSlice, combineReducers } from '@reduxjs/toolkit';
3
2
  import createSagaMiddleware from 'redux-saga';
4
3
  import { call, put, select, takeEvery, putResolve } from 'redux-saga/effects';
5
- import win from 'global/window';
6
4
  import saveState from './defaultReducer';
7
5
  import createPromiseMiddleware from './PromiseMiddleware';
6
+ // 提取公共的 Effect 工具对象,避免在循环中重复创建
7
+ const opFun = { call, put, putResolve, select };
8
+ /**
9
+ * 统一处理 Effect 的 Generator 函数
10
+ * 提取到外部以减少闭包创建,提升性能
11
+ */
12
+ function* handleEffect(modelName, effectFunc, tools, action) {
13
+ // 开始异步任务设置loading状态
14
+ yield putResolve({ type: `${modelName}/saveState`, payload: { loading: true } });
15
+ // 执行 Effect (支持 Generator 或 Promise)
16
+ const ret = yield call(effectFunc, action, tools);
17
+ // 结束异步任务关闭loading状态
18
+ yield putResolve({ type: `${modelName}/saveState`, payload: { loading: false } });
19
+ // 处理 PromiseMiddleware 的回调
20
+ if (action._dy_resolve) {
21
+ action._dy_resolve(ret);
22
+ }
23
+ }
8
24
  /**
9
25
  * 获取注册model函数
10
- * @param store redux store
11
- * @param registedModel 已注册model, 对象, 属性为model名称, value为model
12
- * @param allReducers 所有的reducers
13
- * @param sagaMiddleware saga中间件
14
- * @returns 返回function regist(model)
15
26
  */
16
- function getRegistModelFunc(store, registedModel, allReducers, sagaMiddleware) {
17
- /** model函数注册函数
18
- * @param model 模块, 其格式为
19
- * {
20
- * name: 'name',
21
- * state: {},
22
- * reducers: {},
23
- * effects: {}
24
- * }
25
- */
27
+ function getRegistModelFunc(store, registedModel, allReducers, sagaMiddleware, runningEffects) {
28
+ // 提取的公共模型安装逻辑
29
+ function installModel(model) {
30
+ // 初始化模型属性
31
+ if (!model.state)
32
+ model.state = {};
33
+ model.initialState = model.state;
34
+ if (!model.reducers)
35
+ model.reducers = {};
36
+ // 注入默认的 saveState reducer
37
+ if (!model.reducers.saveState)
38
+ model.reducers.saveState = saveState;
39
+ if (!model.effects)
40
+ model.effects = {};
41
+ // 使用 Redux Toolkit 创建 Slice
42
+ const modelSlice = createSlice({
43
+ name: model.name,
44
+ initialState: model.initialState,
45
+ reducers: model.reducers
46
+ });
47
+ // 注册 Reducer
48
+ allReducers[model.name] = modelSlice.reducer;
49
+ registedModel[model.name] = model;
50
+ // 动态更新 Store 的 Reducers
51
+ const newReducer = combineReducers(allReducers);
52
+ store.replaceReducer(newReducer);
53
+ // 注册 Effects
54
+ runningEffects[model.name] = [];
55
+ for (const effectKey in model.effects) {
56
+ const type = `${model.name}/${effectKey}`;
57
+ console.log('[ez-saga] regist effect:', type);
58
+ const effectFunc = model.effects[effectKey];
59
+ const task = sagaMiddleware.run(function* () {
60
+ // 使用 takeEvery 的参数传递功能,将上下文传入 handleEffect
61
+ // handleEffect(modelName, effectFunc, tools, action)
62
+ yield takeEvery(type, handleEffect, model.name, effectFunc, opFun);
63
+ });
64
+ runningEffects[model.name].push(task);
65
+ }
66
+ }
26
67
  return function regist(reduxModel) {
27
68
  const model = {
28
69
  ...reduxModel,
@@ -31,76 +72,52 @@ function getRegistModelFunc(store, registedModel, allReducers, sagaMiddleware) {
31
72
  if (registedModel[model.name]) {
32
73
  return;
33
74
  }
34
- if (!model.state) {
35
- model.state = {};
36
- }
37
- model.initialState = model.state;
38
- if (!model.reducers) {
39
- model.reducers = {};
40
- }
41
- if (!model.reducers.saveState) {
42
- model.reducers.saveState = saveState;
43
- }
44
- if (!model.effects) {
45
- model.effects = {};
46
- }
47
- const modelSlice = createSlice(model);
48
- const reducer = modelSlice.reducer;
49
- allReducers[model.name] = reducer;
50
- registedModel[model.name] = model;
51
- //获得一个新的reducer, 将所有的reducer整合成一个
52
- let newReducer = combineReducers(allReducers);
53
- store.replaceReducer(newReducer);
54
- //注册effects
55
- for (let effect in model.effects) {
56
- let type = `${model.name}/${effect}`;
57
- let execFun = model.effects[effect];
58
- function* loading(opFun, action) {
59
- // 开始异步任务设置loading状态
60
- yield putResolve({ type: `${model.name}/saveState`, payload: { loading: true } });
61
- let ret = yield call(execFun, action, opFun);
62
- // 结束异步任务关闭loading状态
63
- yield putResolve({ type: `${model.name}/saveState`, payload: { loading: false } });
64
- if (action._dy_resolve) {
65
- action._dy_resolve(ret);
75
+ // 安装模型
76
+ installModel(model);
77
+ // 开发环境:注册热更新事件监听
78
+ if (process.env.NODE_ENV !== 'production' && typeof window !== 'undefined') {
79
+ const eventName = `EZ_SAGA_UPDATE_${model.name}`;
80
+ const hmrHandler = (e) => {
81
+ const newModel = e.detail;
82
+ console.log('[ez-saga] Hot updating model:', newModel.name);
83
+ // 1. 取消旧任务
84
+ const tasks = runningEffects[model.name];
85
+ if (tasks && tasks.length) {
86
+ tasks.forEach(task => task.cancel());
66
87
  }
67
- }
68
- function* runEffect() {
69
- //yield takeLatest(type, loading, { call, put, putResolve, select });
70
- yield takeEvery(type, loading, { call, put, putResolve, select });
71
- }
72
- sagaMiddleware.run(runEffect);
88
+ runningEffects[model.name] = [];
89
+ // 2. 安装新模型 (包含更新 Reducer 和重启 Effects)
90
+ installModel(newModel);
91
+ };
92
+ window.addEventListener(eventName, hmrHandler);
73
93
  }
74
94
  };
75
95
  }
76
96
  /** 创建store */
77
97
  export default function create() {
78
- //已经注册的reducer, key是名字, value是reducer
79
98
  const allReducers = {};
80
- //已注册model
81
99
  const registedModel = {};
82
100
  const sagaMiddleware = createSagaMiddleware();
83
101
  const promiseMiddleware = createPromiseMiddleware(registedModel);
84
- const middlewares = [
85
- promiseMiddleware,
86
- sagaMiddleware
87
- ];
88
- // eslint-disable-next-line no-undef
89
- const composeEnhancers = process.env.NODE_ENV !== 'production'
90
- && win.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
91
- win.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({ trace: true, maxAge: 30 }) : compose;
92
- const enhancers = [applyMiddleware(...middlewares)];
93
- //redux store
94
- const store = createStore(saveState, {}, composeEnhancers(...enhancers));
95
- const regist = getRegistModelFunc(store, registedModel, allReducers, sagaMiddleware);
96
- let app = {
97
- /** redux store */
98
- store: store,
99
- /** saga中间件 */
100
- sagaMiddleware: sagaMiddleware,
101
- /** model注册函数 */
102
- regist: regist
102
+ // 使用 configureStore 替代 createStore
103
+ // 自动集成 Redux DevTools,自动组合中间件
104
+ const store = configureStore({
105
+ reducer: saveState, // 初始 Reducer
106
+ middleware: (getDefaultMiddleware) => getDefaultMiddleware({
107
+ thunk: false, // 禁用默认的 thunk,因为我们使用 saga
108
+ serializableCheck: false, // 禁用序列化检查,因为 action 中可能包含回调函数 (_dy_resolve)
109
+ immutableCheck: false // 禁用不可变检查,提升开发环境性能
110
+ }).concat(promiseMiddleware, sagaMiddleware),
111
+ devTools: process.env.NODE_ENV !== 'production',
112
+ preloadedState: {}
113
+ });
114
+ // 存储运行中的 Effect 任务,用于热更新取消
115
+ const runningEffects = {};
116
+ const regist = getRegistModelFunc(store, registedModel, allReducers, sagaMiddleware, runningEffects);
117
+ return {
118
+ store,
119
+ sagaMiddleware,
120
+ regist
103
121
  };
104
- return app;
105
122
  }
106
123
  //# sourceMappingURL=createApp.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"createApp.js","sourceRoot":"","sources":["../../src/redux/createApp.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"createApp.js","sourceRoot":"","sources":["../../src/redux/createApp.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import { Plugin } from 'vite';
2
+ /**
3
+ * ez-saga Vite 插件
4
+ *
5
+ * 自动为 ez-saga model 文件注入 HMR 代码。
6
+ * 约定:
7
+ * 1. Model 文件必须使用 export default 导出对象
8
+ * 2. 导出的对象必须包含 'name' 属性 (作为 Model 的唯一标识)
9
+ * 3. 项目中必须将 app 实例挂载到 window.app
10
+ */
11
+ export default function ezSagaHmr(): Plugin;
@@ -0,0 +1,59 @@
1
+ import MagicString from 'magic-string';
2
+ /**
3
+ * ez-saga Vite 插件
4
+ *
5
+ * 自动为 ez-saga model 文件注入 HMR 代码。
6
+ * 约定:
7
+ * 1. Model 文件必须使用 export default 导出对象
8
+ * 2. 导出的对象必须包含 'name' 属性 (作为 Model 的唯一标识)
9
+ * 3. 项目中必须将 app 实例挂载到 window.app
10
+ */
11
+ export default function ezSagaHmr() {
12
+ return {
13
+ name: 'vite-plugin-ez-saga-hmr',
14
+ apply: 'serve', // 仅在开发环境(serve)模式下应用
15
+ transform(code, id) {
16
+ // 过滤掉 node_modules 和非 JS/TS 文件
17
+ if (id.includes('node_modules') || !/\.(js|ts|tsx|jsx)$/.test(id)) {
18
+ return;
19
+ }
20
+ // 简单的特征检测:检查是否包含 ez-saga model 的关键属性
21
+ // 必须包含 name 属性,且通常包含 effects 或 reducers 或 state
22
+ // 宽容匹配:
23
+ // 1. export default ... (可能是对象字面量,也可能是变量)
24
+ // 2. name: ... (可能是字符串字面量,也可能是变量)
25
+ const hasDefaultExport = /export\s+default/.test(code);
26
+ const hasNameProp = /name\s*:/.test(code);
27
+ const hasModelKeywords = /(effects|reducers|state|initialState)\s*:\s*\{/.test(code);
28
+ if (hasDefaultExport && hasNameProp && hasModelKeywords) {
29
+ // console.log('ez-saga HMR: Injecting HMR code into', id);
30
+ // 注入 HMR 代码
31
+ // 使用 window 事件通信,解耦 app 实例引用
32
+ const hmrCode = `
33
+ if (import.meta.hot) {
34
+ import.meta.hot.accept((newModule) => {
35
+ if (newModule && newModule.default) {
36
+ const model = newModule.default;
37
+ if (model.name) {
38
+ // console.log('[ez-saga] Hot updating model:', model.name);
39
+ if (window && window.dispatchEvent) {
40
+ window.dispatchEvent(new CustomEvent('EZ_SAGA_UPDATE_' + model.name, {
41
+ detail: model
42
+ }));
43
+ }
44
+ }
45
+ }
46
+ });
47
+ }
48
+ `;
49
+ const s = new MagicString(code);
50
+ s.append(hmrCode);
51
+ return {
52
+ code: s.toString(),
53
+ map: s.generateMap({ hires: true })
54
+ };
55
+ }
56
+ }
57
+ };
58
+ }
59
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/vite/index.ts"],"names":[],"mappings":"AACA,OAAO,WAAW,MAAM,cAAc,CAAC;AAEvC;;;;;;;;GAQG;AAEH,MAAM,CAAC,OAAO,UAAU,SAAS;IAC7B,OAAO;QACH,IAAI,EAAE,yBAAyB;QAC/B,KAAK,EAAE,OAAO,EAAE,qBAAqB;QACrC,SAAS,CAAC,IAAI,EAAE,EAAE;YACd,+BAA+B;YAC/B,IAAI,EAAE,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;gBAChE,OAAO;YACX,CAAC;YACD,qCAAqC;YACrC,gDAAgD;YAChD,QAAQ;YACR,0CAA0C;YAC1C,kCAAkC;YAClC,MAAM,gBAAgB,GAAG,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACvD,MAAM,WAAW,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC1C,MAAM,gBAAgB,GAAG,gDAAgD,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAErF,IAAI,gBAAgB,IAAI,WAAW,IAAI,gBAAgB,EAAE,CAAC;gBACtD,2DAA2D;gBAC3D,YAAY;gBACZ,6BAA6B;gBAC7B,MAAM,OAAO,GAAG;;;;;;;;;;;;;;;;CAgB/B,CAAC;gBACc,MAAM,CAAC,GAAG,IAAI,WAAW,CAAC,IAAI,CAAC,CAAC;gBAChC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;gBAElB,OAAO;oBACH,IAAI,EAAE,CAAC,CAAC,QAAQ,EAAE;oBAClB,GAAG,EAAE,CAAC,CAAC,WAAW,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;iBACtC,CAAC;YACN,CAAC;QACL,CAAC;KACJ,CAAC;AACN,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { Compiler } from 'webpack';
2
+ /**
3
+ * ez-saga Webpack Plugin (Webpack 5+)
4
+ *
5
+ * 自动配置 ez-saga-loader,实现 Model 热更新。
6
+ */
7
+ export default class EzSagaWebpackPlugin {
8
+ apply(compiler: Compiler): void;
9
+ }
@@ -0,0 +1,28 @@
1
+ import path from 'path';
2
+ import { fileURLToPath } from 'url';
3
+ // ESM 环境下模拟 __dirname
4
+ const __filename = fileURLToPath(import.meta.url);
5
+ const __dirname = path.dirname(__filename);
6
+ /**
7
+ * ez-saga Webpack Plugin (Webpack 5+)
8
+ *
9
+ * 自动配置 ez-saga-loader,实现 Model 热更新。
10
+ */
11
+ export default class EzSagaWebpackPlugin {
12
+ apply(compiler) {
13
+ // 获取 loader 的绝对路径
14
+ const loaderPath = path.resolve(__dirname, './loader.js');
15
+ compiler.hooks.afterEnvironment.tap('EzSagaWebpackPlugin', () => {
16
+ // 注入 loader规则
17
+ compiler.options.module.rules.push({
18
+ test: /\.(js|ts|tsx|jsx)$/,
19
+ exclude: /node_modules/,
20
+ enforce: 'post', // 在编译后执行,避免影响 ts-loader 输入
21
+ use: [{
22
+ loader: loaderPath
23
+ }]
24
+ });
25
+ });
26
+ }
27
+ }
28
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/webpack/index.ts"],"names":[],"mappings":"AACA,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AAEpC,sBAAsB;AACtB,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;AAE3C;;;;GAIG;AACH,MAAM,CAAC,OAAO,OAAO,mBAAmB;IACpC,KAAK,CAAC,QAAkB;QACpB,kBAAkB;QAClB,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;QAE1D,QAAQ,CAAC,KAAK,CAAC,gBAAgB,CAAC,GAAG,CAAC,qBAAqB,EAAE,GAAG,EAAE;YAC5D,cAAc;YACd,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC;gBAC/B,IAAI,EAAE,oBAAoB;gBAC1B,OAAO,EAAE,cAAc;gBACvB,OAAO,EAAE,MAAM,EAAE,2BAA2B;gBAC5C,GAAG,EAAE,CAAC;wBACF,MAAM,EAAE,UAAU;qBACrB,CAAC;aACL,CAAC,CAAC;QACP,CAAC,CAAC,CAAC;IACP,CAAC;CACJ"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * ez-saga Webpack Loader
3
+ * 对应 src/vite/index.ts 的逻辑,用于 Webpack 场景
4
+ */
5
+ export default function (this: any, source: string): void;
@@ -0,0 +1,68 @@
1
+ import MagicString from 'magic-string';
2
+ /**
3
+ * ez-saga Webpack Loader
4
+ * 对应 src/vite/index.ts 的逻辑,用于 Webpack 场景
5
+ */
6
+ export default function (source) {
7
+ // 简单的特征检测
8
+ const hasDefaultExport = /export\s+default/.test(source) || /exports\.default\s*=/.test(source) || /module\.exports\s*=/.test(source);
9
+ // name属性检查 (宽松匹配: name: "foo" 或 name = "foo")
10
+ const hasNameProp = /(name\s*[:=])/.test(source);
11
+ // 关键字检查 (Loose due to compilation)
12
+ const hasModelKeywords = /(effects|reducers|state|initialState)\s*[:=]\s*\{/.test(source);
13
+ if (hasDefaultExport && hasNameProp && hasModelKeywords) {
14
+ const callback = this.async();
15
+ const s = new MagicString(source);
16
+ // Regular expressions to detect export patterns
17
+ const esmRegex = /export\s+default/;
18
+ const cjsDefaultRegex = /exports\.default\s*=/;
19
+ const cjsModuleRegex = /module\.exports\s*=/;
20
+ let match;
21
+ const modelVarName = '__ez_saga_model_def';
22
+ if ((match = esmRegex.exec(source)) !== null) {
23
+ // ESM: export default { ... }
24
+ const start = match.index;
25
+ const end = start + match[0].length;
26
+ s.overwrite(start, end, `const ${modelVarName} =`);
27
+ s.append(`\nexport default ${modelVarName};\n`);
28
+ }
29
+ else if ((match = cjsDefaultRegex.exec(source)) !== null) {
30
+ // CJS: exports.default = { ... }
31
+ const start = match.index;
32
+ const end = start + match[0].length;
33
+ s.overwrite(start, end, `const ${modelVarName} =`);
34
+ s.append(`\nexports.default = ${modelVarName};\n`);
35
+ }
36
+ else if ((match = cjsModuleRegex.exec(source)) !== null) {
37
+ // CJS: module.exports = { ... }
38
+ const start = match.index;
39
+ const end = start + match[0].length;
40
+ s.overwrite(start, end, `const ${modelVarName} =`);
41
+ s.append(`\nmodule.exports = ${modelVarName};\n`);
42
+ }
43
+ else {
44
+ // 匹配失败
45
+ this.callback(null, source);
46
+ return;
47
+ }
48
+ // 注入 HMR 逻辑
49
+ const hmrCode = `
50
+ if (module.hot) {
51
+ module.hot.accept();
52
+ if (${modelVarName} && ${modelVarName}.name) {
53
+ if (typeof window !== 'undefined' && window.dispatchEvent) {
54
+ window.dispatchEvent(new CustomEvent('EZ_SAGA_UPDATE_' + ${modelVarName}.name, {
55
+ detail: ${modelVarName}
56
+ }));
57
+ }
58
+ }
59
+ }
60
+ `;
61
+ s.append(hmrCode);
62
+ // 禁用 SourceMap
63
+ callback(null, s.toString());
64
+ return;
65
+ }
66
+ this.callback(null, source);
67
+ }
68
+ //# sourceMappingURL=loader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/webpack/loader.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,9 +1,25 @@
1
1
  {
2
2
  "name": "ez-saga",
3
- "version": "18.0.5",
3
+ "version": "18.0.7",
4
4
  "description": "The ez-saga project is a project that imitates dva-js",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
+ "exports": {
8
+ ".": "./lib/index.js",
9
+ "./vite": "./lib/vite/index.js",
10
+ "./webpack": "./lib/webpack/index.js",
11
+ "./package.json": "./package.json"
12
+ },
13
+ "typesVersions": {
14
+ "*": {
15
+ "vite": [
16
+ "lib/vite/index.d.ts"
17
+ ],
18
+ "webpack": [
19
+ "lib/webpack/index.d.ts"
20
+ ]
21
+ }
22
+ },
7
23
  "sideEffects": false,
8
24
  "repository": {
9
25
  "type": "git",
@@ -29,34 +45,22 @@
29
45
  },
30
46
  "dependencies": {
31
47
  "@reduxjs/toolkit": "^2.11.2",
48
+ "magic-string": "^0.30.21",
32
49
  "redux": "^5.0.1",
33
50
  "redux-saga": "^1.4.2",
34
- "global": "^4.4.0"
51
+ "tslib": "^2.8.1"
35
52
  },
36
- "peerDependencies": {},
37
53
  "devDependencies": {
38
- "@babel/core": "^7.23.7",
39
- "@babel/eslint-parser": "^7.23.3",
40
- "@babel/plugin-proposal-decorators": "^7.23.7",
41
- "@babel/plugin-syntax-flow": "^7.23.3",
42
- "@babel/plugin-transform-runtime": "^7.23.7",
43
- "@babel/preset-env": "^7.23.8",
44
- "@babel/preset-flow": "^7.23.3",
45
- "@babel/preset-typescript": "^7.23.3",
46
- "@babel/runtime-corejs3": "^7.23.8",
47
- "@babel/plugin-transform-class-properties": "^7.23.3",
48
- "@babel/plugin-transform-private-methods": "^7.23.3",
49
- "@babel/plugin-transform-private-property-in-object": "^7.23.4",
50
- "cross-env": "^7.0.3",
51
- "eslint": "^8.56.0",
52
- "eslint-plugin-flowtype": "^8.0.3",
53
- "eslint-plugin-import": "^2.29.1",
54
+ "@types/node": "^25.0.3",
54
55
  "eslint-plugin-node": "^11.1.0",
55
56
  "eslint-plugin-standard": "^4.1.0",
56
- "typescript": "^5.3.3"
57
+ "rimraf": "^6.1.2",
58
+ "typescript": "^5.3.3",
59
+ "vite": "^7.3.0",
60
+ "webpack": "^5.104.1"
57
61
  },
58
62
  "engines": {
59
- "node": ">= 14.0.0",
63
+ "node": ">= 18.0.0",
60
64
  "npm": ">= 6.0.0"
61
65
  },
62
66
  "browserslist": [
@@ -68,7 +72,6 @@
68
72
  "files": [
69
73
  "lib",
70
74
  "src",
71
- "index.js",
72
75
  "tsconfig.json",
73
76
  "typing.d.ts"
74
77
  ]
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- /// <reference path="./global-shim.d.ts" />
1
+
2
2
  export { PayloadAction, EffectTool, Effect, ModelReducer, ModelEffect, ReduxModel, ReduxApp } from './redux/typeDeclare';
3
3
  export { Dispatch, Action } from 'redux';
4
4
  import createApp from './redux/createApp';
@@ -1,6 +1,13 @@
1
- import { Dispatch, Middleware, MiddlewareAPI, Action } from 'redux';
1
+ import { Dispatch, Middleware, MiddlewareAPI, Action, UnknownAction } from 'redux';
2
2
  import { RegistedModel } from './typeDeclare';
3
3
 
4
+ /**
5
+ * 判断是否为标准Action
6
+ */
7
+ function isAction(action: unknown): action is Action<string> {
8
+ return typeof action === 'object' && action !== null && 'type' in action;
9
+ }
10
+
4
11
  /**
5
12
  * 创建中间层
6
13
  * @param registedModel 已注册model
@@ -8,33 +15,29 @@ import { RegistedModel } from './typeDeclare';
8
15
  function createPromiseMiddleware<D extends Dispatch>(registedModel: RegistedModel): Middleware<any, any, any> {
9
16
  function isEffect(type: string) {
10
17
  if (!type || typeof type !== 'string') return false;
18
+
19
+ // 性能优化:快速排除不包含 '/' 的普通 Action
20
+ if (type.indexOf('/') === -1) return false;
21
+
11
22
  const [modelName, effect] = type.split('/');
12
23
  const model = registedModel[modelName];
13
- if (model) {
14
- if (model.effects && model.effects[effect]) {
15
- return true;
16
- }
24
+ if (model && model.effects && model.effects[effect]) {
25
+ return true;
17
26
  }
18
27
  return false;
19
28
  }
20
29
 
21
30
  return (api: MiddlewareAPI<D, any>) => (next: (action: unknown) => any) => (action: unknown) => {
22
- let exeEffect = false;
23
- if ((action as Action)?.type !== undefined) {
24
- const { type } = action as Action;
25
- exeEffect = isEffect(type);
26
- }
27
- if (exeEffect) {
31
+ if (isAction(action) && isEffect(action.type)) {
28
32
  return new Promise((resolve, reject) => {
29
33
  next({
30
34
  _dy_resolve: resolve,
31
35
  _dy_reject: reject,
32
- ...action as Action,
36
+ ...action,
33
37
  });
34
38
  });
35
- } else {
36
- return next(action);
37
39
  }
40
+ return next(action);
38
41
  };
39
42
  }
40
43
 
@@ -1,126 +1,152 @@
1
- import { legacy_createStore as createStore, applyMiddleware, compose, Action as ReduxAction, Store } from 'redux';
2
- import { createSlice, combineReducers } from '@reduxjs/toolkit';
3
- import createSagaMiddleware, { SagaMiddleware } from 'redux-saga';
1
+ import { configureStore, createSlice, combineReducers } from '@reduxjs/toolkit';
2
+ import createSagaMiddleware, { SagaMiddleware, Task } from 'redux-saga';
4
3
  import { call, put, select, takeEvery, putResolve } from 'redux-saga/effects';
5
- import win from 'global/window';
6
- import { Reducer, Action } from 'redux';
7
- import { ReduxModel, ReduxApp, RegistedModel, ReduxSagaModel, EffectTool, PayloadAction } from './typeDeclare';
4
+ import { Reducer, Action, Store } from 'redux';
5
+ import { ReduxModel, ReduxApp, RegistedModel, ReduxSagaModel, EffectTool, PayloadAction, Effect } from './typeDeclare';
8
6
  import saveState from './defaultReducer';
9
7
  import createPromiseMiddleware from './PromiseMiddleware';
10
8
 
9
+ // 提取公共的 Effect 工具对象,避免在循环中重复创建
10
+ const opFun: EffectTool = { call, put, putResolve, select };
11
+
12
+ /**
13
+ * 统一处理 Effect 的 Generator 函数
14
+ * 提取到外部以减少闭包创建,提升性能
15
+ */
16
+ function* handleEffect(
17
+ modelName: string,
18
+ effectFunc: Effect,
19
+ tools: EffectTool,
20
+ action: PayloadAction
21
+ ) {
22
+ // 开始异步任务设置loading状态
23
+ yield putResolve({ type: `${modelName}/saveState`, payload: { loading: true } });
24
+
25
+ // 执行 Effect (支持 Generator 或 Promise)
26
+ const ret: any = yield call(effectFunc, action, tools);
27
+
28
+ // 结束异步任务关闭loading状态
29
+ yield putResolve({ type: `${modelName}/saveState`, payload: { loading: false } });
30
+
31
+ // 处理 PromiseMiddleware 的回调
32
+ if (action._dy_resolve) {
33
+ action._dy_resolve(ret);
34
+ }
35
+ }
36
+
11
37
  /**
12
38
  * 获取注册model函数
13
- * @param store redux store
14
- * @param registedModel 已注册model, 对象, 属性为model名称, value为model
15
- * @param allReducers 所有的reducers
16
- * @param sagaMiddleware saga中间件
17
- * @returns 返回function regist(model)
18
39
  */
19
- function getRegistModelFunc(store: Store<any, ReduxAction>, registedModel: RegistedModel,
20
- allReducers: { [x: string]: Reducer<any, Action>; },
21
- sagaMiddleware: SagaMiddleware<object>): (model: ReduxModel) => void {
22
- /** model函数注册函数
23
- * @param model 模块, 其格式为
24
- * {
25
- * name: 'name',
26
- * state: {},
27
- * reducers: {},
28
- * effects: {}
29
- * }
30
- */
40
+ function getRegistModelFunc(
41
+ store: Store<any, Action<string>>,
42
+ registedModel: RegistedModel,
43
+ allReducers: { [x: string]: Reducer<any, any>; },
44
+ sagaMiddleware: SagaMiddleware<object>,
45
+ runningEffects: Record<string, Task[]>
46
+ ): (model: ReduxModel) => void {
47
+ // 提取的公共模型安装逻辑
48
+ function installModel(model: ReduxSagaModel) {
49
+ // 初始化模型属性
50
+ if (!model.state) model.state = {};
51
+ model.initialState = model.state;
52
+ if (!model.reducers) model.reducers = {};
53
+ // 注入默认的 saveState reducer
54
+ if (!model.reducers.saveState) model.reducers.saveState = saveState;
55
+ if (!model.effects) model.effects = {};
56
+
57
+ // 使用 Redux Toolkit 创建 Slice
58
+ const modelSlice = createSlice({
59
+ name: model.name,
60
+ initialState: model.initialState,
61
+ reducers: model.reducers as any
62
+ });
63
+
64
+ // 注册 Reducer
65
+ allReducers[model.name] = modelSlice.reducer;
66
+ registedModel[model.name] = model;
67
+
68
+ // 动态更新 Store 的 Reducers
69
+ const newReducer = combineReducers(allReducers);
70
+ store.replaceReducer(newReducer);
71
+
72
+ // 注册 Effects
73
+ runningEffects[model.name] = [];
74
+ for (const effectKey in model.effects) {
75
+ const type = `${model.name}/${effectKey}`;
76
+ console.log('[ez-saga] regist effect:', type);
77
+ const effectFunc = model.effects[effectKey];
78
+ const task = sagaMiddleware.run(function* () {
79
+ // 使用 takeEvery 的参数传递功能,将上下文传入 handleEffect
80
+ // handleEffect(modelName, effectFunc, tools, action)
81
+ yield takeEvery(type, handleEffect, model.name, effectFunc, opFun);
82
+ });
83
+ runningEffects[model.name].push(task);
84
+ }
85
+ }
86
+
31
87
  return function regist(reduxModel: ReduxModel): void {
32
88
  const model = {
33
89
  ...reduxModel,
34
90
  initialState: {}
35
91
  } as ReduxSagaModel;
92
+
36
93
  if (registedModel[model.name]) {
37
94
  return;
38
95
  }
39
- if (!model.state) {
40
- model.state = {};
41
- }
42
- model.initialState = model.state;
43
- if (!model.reducers) {
44
- model.reducers = {};
45
- }
46
- if (!model.reducers.saveState) {
47
- model.reducers.saveState = saveState;
48
- }
49
- if (!model.effects) {
50
- model.effects = {};
51
- }
52
- const modelSlice = createSlice(model);
53
- const reducer = modelSlice.reducer;
54
- allReducers[model.name] = reducer;
55
- registedModel[model.name] = model;
56
- //获得一个新的reducer, 将所有的reducer整合成一个
57
- let newReducer = combineReducers(allReducers);
58
- store.replaceReducer(newReducer);
59
- //注册effects
60
- for (let effect in model.effects) {
61
- let type: string = `${model.name}/${effect}`;
62
- let execFun = model.effects[effect];
63
- function* loading(opFun: EffectTool, action: PayloadAction) {
64
- // 开始异步任务设置loading状态
65
- yield putResolve({ type: `${model.name}/saveState`, payload: { loading: true } });
66
- let ret = yield call(execFun, action, opFun);
67
- // 结束异步任务关闭loading状态
68
- yield putResolve({ type: `${model.name}/saveState`, payload: { loading: false } });
69
- if (action._dy_resolve) {
70
- action._dy_resolve(ret);
71
- }
72
- }
73
96
 
74
- function* runEffect() {
75
- //yield takeLatest(type, loading, { call, put, putResolve, select });
76
- yield takeEvery(type, loading, { call, put, putResolve, select });
77
- }
97
+ // 安装模型
98
+ installModel(model);
78
99
 
79
- sagaMiddleware.run(runEffect);
100
+ // 开发环境:注册热更新事件监听
101
+ if (process.env.NODE_ENV !== 'production' && typeof window !== 'undefined') {
102
+ const eventName = `EZ_SAGA_UPDATE_${model.name}`;
103
+ const hmrHandler = (e: any) => {
104
+ const newModel = e.detail as ReduxSagaModel;
105
+ console.log('[ez-saga] Hot updating model:', newModel.name);
106
+ // 1. 取消旧任务
107
+ const tasks = runningEffects[model.name];
108
+ if (tasks && tasks.length) {
109
+ tasks.forEach(task => task.cancel());
110
+ }
111
+ runningEffects[model.name] = [];
112
+ // 2. 安装新模型 (包含更新 Reducer 和重启 Effects)
113
+ installModel(newModel);
114
+ };
115
+ window.addEventListener(eventName, hmrHandler);
80
116
  }
81
117
  };
82
118
  }
83
119
 
84
-
85
120
  /** 创建store */
86
121
  export default function create(): ReduxApp {
87
- //已经注册的reducer, key是名字, value是reducer
88
122
  const allReducers = {};
89
- //已注册model
90
123
  const registedModel: RegistedModel = {};
91
124
 
92
125
  const sagaMiddleware = createSagaMiddleware();
93
-
94
126
  const promiseMiddleware = createPromiseMiddleware(registedModel);
95
127
 
96
- const middlewares = [
97
- promiseMiddleware,
98
- sagaMiddleware
99
- ];
100
-
101
- // eslint-disable-next-line no-undef
102
- const composeEnhancers = process.env.NODE_ENV !== 'production'
103
- && win.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
104
- win.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({ trace: true, maxAge: 30 }) : compose;
105
-
106
- const enhancers = [applyMiddleware(...middlewares)];
107
-
108
- //redux store
109
- const store = createStore(
110
- saveState,
111
- {},
112
- composeEnhancers(...enhancers)
113
- );
114
-
115
- const regist = getRegistModelFunc(store, registedModel, allReducers, sagaMiddleware);
116
-
117
- let app: ReduxApp = {
118
- /** redux store */
119
- store: store,
120
- /** saga中间件 */
121
- sagaMiddleware: sagaMiddleware,
122
- /** model注册函数 */
123
- regist: regist
128
+ // 使用 configureStore 替代 createStore
129
+ // 自动集成 Redux DevTools,自动组合中间件
130
+ const store = configureStore({
131
+ reducer: saveState, // 初始 Reducer
132
+ middleware: (getDefaultMiddleware) =>
133
+ getDefaultMiddleware({
134
+ thunk: false, // 禁用默认的 thunk,因为我们使用 saga
135
+ serializableCheck: false, // 禁用序列化检查,因为 action 中可能包含回调函数 (_dy_resolve)
136
+ immutableCheck: false // 禁用不可变检查,提升开发环境性能
137
+ }).concat(promiseMiddleware, sagaMiddleware),
138
+ devTools: process.env.NODE_ENV !== 'production',
139
+ preloadedState: {}
140
+ });
141
+
142
+ // 存储运行中的 Effect 任务,用于热更新取消
143
+ const runningEffects: Record<string, Task[]> = {};
144
+
145
+ const regist = getRegistModelFunc(store, registedModel, allReducers, sagaMiddleware, runningEffects);
146
+
147
+ return {
148
+ store,
149
+ sagaMiddleware,
150
+ regist
124
151
  };
125
- return app;
126
152
  }
@@ -20,11 +20,6 @@ declare module 'redux' {
20
20
  }
21
21
  }
22
22
 
23
- // /** 定义AsyncDispatch, 使其兼容redux promiseMiddleware中间件修改返回结果的情况 */
24
- // export interface AsyncDispatch {
25
- // <R = any>(action: PayloadAction, ...extraArgs: any[]): Promise<R>;
26
- // }
27
-
28
23
  /** 工具 */
29
24
  export interface EffectTool {
30
25
  /** 调用异步函数, 并获得该异步函数的结果 */
@@ -74,7 +69,7 @@ export interface RegistedModel {
74
69
  export interface ReduxApp {
75
70
  store: Store<any, ReduxAction>,
76
71
  sagaMiddleware: SagaMiddleware<object>;
77
- regist: (model: ReduxModel) => void
72
+ regist: (model: ReduxModel) => void;
78
73
  }
79
74
 
80
75
 
@@ -0,0 +1,63 @@
1
+ import { Plugin } from 'vite';
2
+ import MagicString from 'magic-string';
3
+
4
+ /**
5
+ * ez-saga Vite 插件
6
+ *
7
+ * 自动为 ez-saga model 文件注入 HMR 代码。
8
+ * 约定:
9
+ * 1. Model 文件必须使用 export default 导出对象
10
+ * 2. 导出的对象必须包含 'name' 属性 (作为 Model 的唯一标识)
11
+ * 3. 项目中必须将 app 实例挂载到 window.app
12
+ */
13
+
14
+ export default function ezSagaHmr(): Plugin {
15
+ return {
16
+ name: 'vite-plugin-ez-saga-hmr',
17
+ apply: 'serve', // 仅在开发环境(serve)模式下应用
18
+ transform(code, id) {
19
+ // 过滤掉 node_modules 和非 JS/TS 文件
20
+ if (id.includes('node_modules') || !/\.(js|ts|tsx|jsx)$/.test(id)) {
21
+ return;
22
+ }
23
+ // 简单的特征检测:检查是否包含 ez-saga model 的关键属性
24
+ // 必须包含 name 属性,且通常包含 effects 或 reducers 或 state
25
+ // 宽容匹配:
26
+ // 1. export default ... (可能是对象字面量,也可能是变量)
27
+ // 2. name: ... (可能是字符串字面量,也可能是变量)
28
+ const hasDefaultExport = /export\s+default/.test(code);
29
+ const hasNameProp = /name\s*:/.test(code);
30
+ const hasModelKeywords = /(effects|reducers|state|initialState)\s*:\s*\{/.test(code);
31
+
32
+ if (hasDefaultExport && hasNameProp && hasModelKeywords) {
33
+ // console.log('ez-saga HMR: Injecting HMR code into', id);
34
+ // 注入 HMR 代码
35
+ // 使用 window 事件通信,解耦 app 实例引用
36
+ const hmrCode = `
37
+ if (import.meta.hot) {
38
+ import.meta.hot.accept((newModule) => {
39
+ if (newModule && newModule.default) {
40
+ const model = newModule.default;
41
+ if (model.name) {
42
+ // console.log('[ez-saga] Hot updating model:', model.name);
43
+ if (window && window.dispatchEvent) {
44
+ window.dispatchEvent(new CustomEvent('EZ_SAGA_UPDATE_' + model.name, {
45
+ detail: model
46
+ }));
47
+ }
48
+ }
49
+ }
50
+ });
51
+ }
52
+ `;
53
+ const s = new MagicString(code);
54
+ s.append(hmrCode);
55
+
56
+ return {
57
+ code: s.toString(),
58
+ map: s.generateMap({ hires: true })
59
+ };
60
+ }
61
+ }
62
+ };
63
+ }
@@ -0,0 +1,31 @@
1
+ import { Compiler } from 'webpack';
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+
5
+ // ESM 环境下模拟 __dirname
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = path.dirname(__filename);
8
+
9
+ /**
10
+ * ez-saga Webpack Plugin (Webpack 5+)
11
+ *
12
+ * 自动配置 ez-saga-loader,实现 Model 热更新。
13
+ */
14
+ export default class EzSagaWebpackPlugin {
15
+ apply(compiler: Compiler) {
16
+ // 获取 loader 的绝对路径
17
+ const loaderPath = path.resolve(__dirname, './loader.js');
18
+
19
+ compiler.hooks.afterEnvironment.tap('EzSagaWebpackPlugin', () => {
20
+ // 注入 loader规则
21
+ compiler.options.module.rules.push({
22
+ test: /\.(js|ts|tsx|jsx)$/,
23
+ exclude: /node_modules/,
24
+ enforce: 'post', // 在编译后执行,避免影响 ts-loader 输入
25
+ use: [{
26
+ loader: loaderPath
27
+ }]
28
+ });
29
+ });
30
+ }
31
+ }
@@ -0,0 +1,72 @@
1
+ import MagicString from 'magic-string';
2
+
3
+ /**
4
+ * ez-saga Webpack Loader
5
+ * 对应 src/vite/index.ts 的逻辑,用于 Webpack 场景
6
+ */
7
+ export default function (this: any, source: string) {
8
+ // 简单的特征检测
9
+ const hasDefaultExport = /export\s+default/.test(source) || /exports\.default\s*=/.test(source) || /module\.exports\s*=/.test(source);
10
+ // name属性检查 (宽松匹配: name: "foo" 或 name = "foo")
11
+ const hasNameProp = /(name\s*[:=])/.test(source);
12
+ // 关键字检查 (Loose due to compilation)
13
+ const hasModelKeywords = /(effects|reducers|state|initialState)\s*[:=]\s*\{/.test(source);
14
+
15
+ if (hasDefaultExport && hasNameProp && hasModelKeywords) {
16
+ const callback = this.async();
17
+ const s = new MagicString(source);
18
+
19
+ // Regular expressions to detect export patterns
20
+ const esmRegex = /export\s+default/;
21
+ const cjsDefaultRegex = /exports\.default\s*=/;
22
+ const cjsModuleRegex = /module\.exports\s*=/;
23
+
24
+ let match;
25
+ const modelVarName = '__ez_saga_model_def';
26
+
27
+ if ((match = esmRegex.exec(source)) !== null) {
28
+ // ESM: export default { ... }
29
+ const start = match.index;
30
+ const end = start + match[0].length;
31
+ s.overwrite(start, end, `const ${modelVarName} =`);
32
+ s.append(`\nexport default ${modelVarName};\n`);
33
+ } else if ((match = cjsDefaultRegex.exec(source)) !== null) {
34
+ // CJS: exports.default = { ... }
35
+ const start = match.index;
36
+ const end = start + match[0].length;
37
+ s.overwrite(start, end, `const ${modelVarName} =`);
38
+ s.append(`\nexports.default = ${modelVarName};\n`);
39
+ } else if ((match = cjsModuleRegex.exec(source)) !== null) {
40
+ // CJS: module.exports = { ... }
41
+ const start = match.index;
42
+ const end = start + match[0].length;
43
+ s.overwrite(start, end, `const ${modelVarName} =`);
44
+ s.append(`\nmodule.exports = ${modelVarName};\n`);
45
+ } else {
46
+ // 匹配失败
47
+ this.callback(null, source);
48
+ return;
49
+ }
50
+
51
+ // 注入 HMR 逻辑
52
+ const hmrCode = `
53
+ if (module.hot) {
54
+ module.hot.accept();
55
+ if (${modelVarName} && ${modelVarName}.name) {
56
+ if (typeof window !== 'undefined' && window.dispatchEvent) {
57
+ window.dispatchEvent(new CustomEvent('EZ_SAGA_UPDATE_' + ${modelVarName}.name, {
58
+ detail: ${modelVarName}
59
+ }));
60
+ }
61
+ }
62
+ }
63
+ `;
64
+ s.append(hmrCode);
65
+
66
+ // 禁用 SourceMap
67
+ callback(null, s.toString());
68
+ return;
69
+ }
70
+
71
+ this.callback(null, source);
72
+ }
package/tsconfig.json CHANGED
@@ -18,7 +18,8 @@
18
18
  },
19
19
  "declaration": true,
20
20
  "outDir": "lib",
21
- "allowSyntheticDefaultImports": true
21
+ "allowSyntheticDefaultImports": true,
22
+ "skipLibCheck": true
22
23
  },
23
24
  "include": [
24
25
  "src/**/*",
@@ -1,10 +0,0 @@
1
- declare module 'global/window' {
2
- const window: Window & typeof globalThis;
3
- export default window;
4
- }
5
-
6
- interface Window {
7
- app: any;
8
- terminalType: string;
9
- __REDUX_DEVTOOLS_EXTENSION_COMPOSE__?: any;
10
- }
package/src/index.js DELETED
@@ -1,4 +0,0 @@
1
- import createApp from './redux/createApp';
2
- export default {
3
- createApp: createApp
4
- };