@bleedingdev/modern-js-server 3.9.0-ultramodern.9 → 3.9.3-ultramodern.2

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 (34) hide show
  1. package/dist/cjs/createDevServer.js +1 -0
  2. package/dist/cjs/dev-tools/reloadManager.js +27 -3
  3. package/dist/cjs/dev-tools/watcher/dependencyTree.js +9 -15
  4. package/dist/cjs/dev-tools/watcher/index.js +1 -1
  5. package/dist/cjs/dev.js +38 -8
  6. package/dist/cjs/helpers/index.js +13 -5
  7. package/dist/cjs/helpers/mock.js +1 -1
  8. package/dist/cjs/helpers/repack.js +11 -8
  9. package/dist/cjs/types/dev.js +18 -0
  10. package/dist/esm/createDevServer.mjs +1 -0
  11. package/dist/esm/dev-tools/reloadManager.mjs +27 -3
  12. package/dist/esm/dev-tools/watcher/dependencyTree.mjs +9 -15
  13. package/dist/esm/dev-tools/watcher/index.mjs +1 -1
  14. package/dist/esm/dev.mjs +26 -9
  15. package/dist/esm/helpers/index.mjs +13 -5
  16. package/dist/esm/helpers/mock.mjs +1 -1
  17. package/dist/esm/helpers/repack.mjs +11 -8
  18. package/dist/esm/types/dev.mjs +0 -0
  19. package/dist/esm-node/createDevServer.mjs +1 -0
  20. package/dist/esm-node/dev-tools/reloadManager.mjs +27 -3
  21. package/dist/esm-node/dev-tools/watcher/dependencyTree.mjs +9 -15
  22. package/dist/esm-node/dev-tools/watcher/index.mjs +1 -1
  23. package/dist/esm-node/dev.mjs +26 -9
  24. package/dist/esm-node/helpers/index.mjs +13 -5
  25. package/dist/esm-node/helpers/mock.mjs +1 -1
  26. package/dist/esm-node/helpers/repack.mjs +11 -8
  27. package/dist/esm-node/types/dev.mjs +1 -0
  28. package/dist/types/dev-tools/reloadManager.d.ts +9 -0
  29. package/dist/types/dev.d.ts +9 -1
  30. package/dist/types/helpers/repack.d.ts +16 -1
  31. package/dist/types/index.d.ts +1 -2
  32. package/dist/types/types/dev.d.ts +44 -0
  33. package/dist/types/types.d.ts +2 -43
  34. package/package.json +24 -15
@@ -149,6 +149,7 @@ async function createDevServer(options, applyPlugins) {
149
149
  compiler,
150
150
  nodeServer,
151
151
  getRuntimeServer: ()=>currentRuntimeServer,
152
+ holdRequests: (task)=>reloadManager.hold(task),
152
153
  onFileChange: (filepath)=>{
153
154
  pendingReloadFiles.add(external_node_path_default().relative(pwd, filepath));
154
155
  reloadManager.schedule();
@@ -108,13 +108,21 @@ const notReadyHandle = ()=>new Response('Dev server is starting…', {
108
108
  'content-type': 'text/plain; charset=utf-8'
109
109
  }
110
110
  });
111
- var _current = /*#__PURE__*/ new WeakMap(), _build = /*#__PURE__*/ new WeakMap(), _debounceMs = /*#__PURE__*/ new WeakMap(), _onReload = /*#__PURE__*/ new WeakMap(), _onError = /*#__PURE__*/ new WeakMap(), _onReloadError = /*#__PURE__*/ new WeakMap(), _debounceTimer = /*#__PURE__*/ new WeakMap(), _running = /*#__PURE__*/ new WeakMap(), _pending = /*#__PURE__*/ new WeakMap(), _runningPromise = /*#__PURE__*/ new WeakMap(), _closed = /*#__PURE__*/ new WeakMap(), _runLoop = /*#__PURE__*/ new WeakSet(), _reportBuildError = /*#__PURE__*/ new WeakSet(), _reportReloadCallbackError = /*#__PURE__*/ new WeakSet(), _dispose = /*#__PURE__*/ new WeakSet();
111
+ var _current = /*#__PURE__*/ new WeakMap(), _build = /*#__PURE__*/ new WeakMap(), _debounceMs = /*#__PURE__*/ new WeakMap(), _onReload = /*#__PURE__*/ new WeakMap(), _onError = /*#__PURE__*/ new WeakMap(), _onReloadError = /*#__PURE__*/ new WeakMap(), _debounceTimer = /*#__PURE__*/ new WeakMap(), _running = /*#__PURE__*/ new WeakMap(), _pending = /*#__PURE__*/ new WeakMap(), _runningPromise = /*#__PURE__*/ new WeakMap(), _closed = /*#__PURE__*/ new WeakMap(), _held = /*#__PURE__*/ new WeakMap(), _holding = /*#__PURE__*/ new WeakMap(), _released = /*#__PURE__*/ new WeakSet(), _runLoop = /*#__PURE__*/ new WeakSet(), _reportBuildError = /*#__PURE__*/ new WeakSet(), _reportReloadCallbackError = /*#__PURE__*/ new WeakSet(), _dispose = /*#__PURE__*/ new WeakSet();
112
112
  class ReloadManager {
113
113
  setHandle(handle) {
114
114
  _class_private_field_set(this, _current, handle);
115
115
  }
116
116
  get handle() {
117
- return (request, ...args)=>_class_private_field_get(this, _current).call(this, request, ...args);
117
+ return (request, ...args)=>0 === _class_private_field_get(this, _holding) ? _class_private_field_get(this, _current).call(this, request, ...args) : _class_private_method_get(this, _released, released).call(this).then(()=>_class_private_field_get(this, _current).call(this, request, ...args));
118
+ }
119
+ hold(task) {
120
+ _class_private_field_set(this, _holding, _class_private_field_get(this, _holding) + 1);
121
+ const run = _class_private_field_get(this, _held).then(task).finally(()=>{
122
+ _class_private_field_set(this, _holding, _class_private_field_get(this, _holding) - 1);
123
+ });
124
+ _class_private_field_set(this, _held, run.catch(()=>{}));
125
+ return run;
118
126
  }
119
127
  get currentHandle() {
120
128
  return _class_private_field_get(this, _current);
@@ -153,9 +161,11 @@ class ReloadManager {
153
161
  clearTimeout(_class_private_field_get(this, _debounceTimer));
154
162
  _class_private_field_set(this, _debounceTimer, null);
155
163
  }
156
- _class_private_method_get(this, _dispose, reloadManager_dispose).call(this, _class_private_field_get(this, _current));
164
+ const current = _class_private_field_get(this, _current);
165
+ _class_private_method_get(this, _released, released).call(this).then(()=>_class_private_method_get(this, _dispose, reloadManager_dispose).call(this, current));
157
166
  }
158
167
  constructor(options){
168
+ _class_private_method_init(this, _released);
159
169
  _class_private_method_init(this, _runLoop);
160
170
  _class_private_method_init(this, _reportBuildError);
161
171
  _class_private_method_init(this, _reportReloadCallbackError);
@@ -204,11 +214,21 @@ class ReloadManager {
204
214
  writable: true,
205
215
  value: void 0
206
216
  });
217
+ _class_private_field_init(this, _held, {
218
+ writable: true,
219
+ value: void 0
220
+ });
221
+ _class_private_field_init(this, _holding, {
222
+ writable: true,
223
+ value: void 0
224
+ });
207
225
  _class_private_field_set(this, _debounceTimer, null);
208
226
  _class_private_field_set(this, _running, false);
209
227
  _class_private_field_set(this, _pending, false);
210
228
  _class_private_field_set(this, _runningPromise, null);
211
229
  _class_private_field_set(this, _closed, false);
230
+ _class_private_field_set(this, _held, Promise.resolve());
231
+ _class_private_field_set(this, _holding, 0);
212
232
  _class_private_field_set(this, _current, options.initialHandle ?? notReadyHandle);
213
233
  _class_private_field_set(this, _build, options.build);
214
234
  _class_private_field_set(this, _debounceMs, options.debounceMs ?? DEFAULT_DEBOUNCE_MS);
@@ -217,6 +237,9 @@ class ReloadManager {
217
237
  _class_private_field_set(this, _onReloadError, options.onReloadError);
218
238
  }
219
239
  }
240
+ async function released() {
241
+ while(_class_private_field_get(this, _holding) > 0)await _class_private_field_get(this, _held);
242
+ }
220
243
  async function runLoop() {
221
244
  var _this, _this1;
222
245
  do {
@@ -228,6 +251,7 @@ async function runLoop() {
228
251
  _class_private_method_get(this, _reportBuildError, reportBuildError).call(this, error);
229
252
  continue;
230
253
  }
254
+ await _class_private_method_get(this, _released, released).call(this);
231
255
  if (_class_private_field_get(this, _closed)) return void await _class_private_method_get(this, _dispose, reloadManager_dispose).call(this, next);
232
256
  const previous = _class_private_field_get(this, _current);
233
257
  _class_private_field_set(this, _current, next);
@@ -69,34 +69,28 @@ class DependencyTree {
69
69
  for (const treeNode of this.tree.values()){
70
70
  const { parent } = treeNode.module;
71
71
  const { children } = treeNode.module;
72
- if (parent && !this.shouldIgnore(parent.filename)) {
72
+ if (parent) {
73
73
  const parentTreeNode = this.tree.get(parent.filename);
74
74
  if (parentTreeNode) treeNode.parent.add(parentTreeNode);
75
75
  }
76
76
  children?.forEach((child)=>{
77
- if (!this.shouldIgnore(child.filename)) {
78
- const childTreeNode = this.tree.get(child.filename);
79
- if (childTreeNode) {
80
- treeNode.children.add(childTreeNode);
81
- childTreeNode.parent.add(treeNode);
82
- }
77
+ const childTreeNode = this.tree.get(child.filename);
78
+ if (childTreeNode) {
79
+ treeNode.children.add(childTreeNode);
80
+ childTreeNode.parent.add(treeNode);
83
81
  }
84
82
  });
85
83
  }
86
84
  }
87
85
  shouldIgnore(path) {
88
86
  const matchPath = path && external_node_path_default().isAbsolute(path) ? external_node_path_default().relative(process.cwd(), path) : path;
89
- return !matchPath || Boolean(this.ignore.find((rule)=>external_minimatch_namespaceObject.match([
90
- matchPath
91
- ], rule, {
92
- dot: true
93
- }).length > 0));
87
+ return !matchPath || this.ignore.some((rule)=>rule.match(matchPath));
94
88
  }
95
89
  constructor(){
96
90
  this.tree = new Map();
97
- this.ignore = [
98
- ...defaultIgnores
99
- ];
91
+ this.ignore = defaultIgnores.map((rule)=>new external_minimatch_namespaceObject.Minimatch(rule, {
92
+ dot: true
93
+ }));
100
94
  }
101
95
  }
102
96
  exports.DependencyTree = __webpack_exports__.DependencyTree;
@@ -49,7 +49,7 @@ const external_dependencyTree_js_namespaceObject = require("./dependencyTree.js"
49
49
  const external_statsCache_js_namespaceObject = require("./statsCache.js");
50
50
  const defaultWatchOptions = {
51
51
  ignoreInitial: true,
52
- ignored: /api\/typings\/.*/
52
+ ignored: /api\/typings\/.*|(?:^|[/\\])node_modules(?:[/\\]|$)/
53
53
  };
54
54
  const getWatchedFiles = (watcher)=>{
55
55
  const watched = watcher.getWatched();
package/dist/cjs/dev.js CHANGED
@@ -1,5 +1,14 @@
1
1
  "use strict";
2
2
  var __webpack_require__ = {};
3
+ (()=>{
4
+ __webpack_require__.n = (module)=>{
5
+ var getter = module && module.__esModule ? ()=>module['default'] : ()=>module;
6
+ __webpack_require__.d(getter, {
7
+ a: getter
8
+ });
9
+ return getter;
10
+ };
11
+ })();
3
12
  (()=>{
4
13
  __webpack_require__.d = (exports1, getters, values)=>{
5
14
  var define = (defs, kind)=>{
@@ -29,11 +38,26 @@ var __webpack_exports__ = {};
29
38
  __webpack_require__.r(__webpack_exports__);
30
39
  __webpack_require__.d(__webpack_exports__, {
31
40
  devRuntimeMiddlewarePlugin: ()=>devRuntimeMiddlewarePlugin,
41
+ isServerTemplateRequest: ()=>isServerTemplateRequest,
32
42
  setupDevInfra: ()=>setupDevInfra
33
43
  });
44
+ const external_node_path_namespaceObject = require("node:path");
45
+ var external_node_path_default = /*#__PURE__*/ __webpack_require__.n(external_node_path_namespaceObject);
34
46
  const node_namespaceObject = require("@modern-js/server-core/node");
35
47
  const utils_namespaceObject = require("@modern-js/utils");
36
48
  const index_js_namespaceObject = require("./helpers/index.js");
49
+ function isServerTemplateRequest(pathname, routes) {
50
+ const templates = new Set((routes ?? []).flatMap((route)=>route.entryName && route.entryPath?.endsWith('.html') ? [
51
+ `/${route.entryPath.replace(/\\/g, '/').replace(/^\.?\//, '')}`
52
+ ] : []));
53
+ if (!templates.size) return false;
54
+ const candidates = [
55
+ pathname
56
+ ];
57
+ if (pathname.endsWith('/')) candidates.push(`${pathname}index.html`);
58
+ else if (!external_node_path_default().posix.extname(pathname)) candidates.push(`${pathname}.html`, `${pathname}/index.html`);
59
+ return candidates.some((candidate)=>templates.has(candidate));
60
+ }
37
61
  const devRuntimeMiddlewarePlugin = (options, compiler)=>({
38
62
  name: '@modern-js/plugin-dev',
39
63
  setup (api) {
@@ -64,10 +88,14 @@ const devRuntimeMiddlewarePlugin = (options, compiler)=>({
64
88
  name: 'mock-dev',
65
89
  handler: mockMiddleware
66
90
  });
67
- builderMiddlewares && middlewares.push({
68
- name: 'rsbuild-dev',
69
- handler: (0, node_namespaceObject.connectMid2HonoMid)(builderMiddlewares)
70
- });
91
+ if (builderMiddlewares) {
92
+ const builderHandler = (0, node_namespaceObject.connectMid2HonoMid)(builderMiddlewares);
93
+ const handler = (c, next)=>isServerTemplateRequest(c.req.path, api.getServerContext().routes) ? next() : builderHandler(c, next);
94
+ middlewares.push({
95
+ name: 'rsbuild-dev',
96
+ handler
97
+ });
98
+ }
71
99
  after.forEach((middleware, index)=>{
72
100
  middlewares.push({
73
101
  name: `after-dev-server-${index}`,
@@ -81,7 +109,7 @@ const devRuntimeMiddlewarePlugin = (options, compiler)=>({
81
109
  });
82
110
  }
83
111
  });
84
- function setupDevInfra({ config, pwd, distDir, apiDir, sharedDir, builder, builderDevServer, getRuntimeServer, onFileChange, onClose, nodeServer }) {
112
+ function setupDevInfra({ config, pwd, distDir, apiDir, sharedDir, builder, builderDevServer, getRuntimeServer, holdRequests, onFileChange, onClose, nodeServer }) {
85
113
  const { close, connectWebSocket } = builderDevServer || {};
86
114
  const closeCb = [];
87
115
  close && closeCb.push(close);
@@ -91,10 +119,10 @@ function setupDevInfra({ config, pwd, distDir, apiDir, sharedDir, builder, build
91
119
  builder?.onDevCompileDone(({ stats })=>{
92
120
  if ('server' !== stats.toJson({
93
121
  all: false
94
- }).name) {
122
+ }).name) holdRequests(async ()=>{
95
123
  const runtimeServer = getRuntimeServer();
96
- if (runtimeServer) (0, index_js_namespaceObject.onRepack)(distDir, runtimeServer.hooks);
97
- }
124
+ if (runtimeServer) await (0, index_js_namespaceObject.onRepack)(distDir, runtimeServer.hooks);
125
+ }).catch((error)=>utils_namespaceObject.logger.error(error));
98
126
  });
99
127
  const { watchOptions } = config.server;
100
128
  const mockDir = config.dev?.mockDir;
@@ -138,9 +166,11 @@ function setupDevInfra({ config, pwd, distDir, apiDir, sharedDir, builder, build
138
166
  };
139
167
  }
140
168
  exports.devRuntimeMiddlewarePlugin = __webpack_exports__.devRuntimeMiddlewarePlugin;
169
+ exports.isServerTemplateRequest = __webpack_exports__.isServerTemplateRequest;
141
170
  exports.setupDevInfra = __webpack_exports__.setupDevInfra;
142
171
  for(var __rspack_i in __webpack_exports__)if (-1 === [
143
172
  "devRuntimeMiddlewarePlugin",
173
+ "isServerTemplateRequest",
144
174
  "setupDevInfra"
145
175
  ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
146
176
  Object.defineProperty(exports, '__esModule', {
@@ -103,14 +103,22 @@ var __webpack_exports__ = {};
103
103
  ].indexOf(__rspack_import_key) < 0) __rspack_reexport[__rspack_import_key] = ()=>_repack__rspack_import_6[__rspack_import_key];
104
104
  __webpack_require__.d(__webpack_exports__, __rspack_reexport);
105
105
  function startWatcher({ pwd, distDir, apiDir, sharedDir, mockDir, watchOptions, onChange }) {
106
+ const loaderDirectory = path__rspack_import_1_default().resolve(pwd, distDir, _modern_js_utils__rspack_import_0.SERVER_BUNDLE_DIRECTORY);
106
107
  const defaultWatched = [
107
- path__rspack_import_1_default().join((0, _mock__rspack_import_3.resolveMockDirectory)(pwd, mockDir), '**/*'),
108
- `${_modern_js_utils__rspack_import_0.SERVER_DIR}/**/*`,
109
- `${apiDir}/**`,
110
- `${sharedDir}/**/*`,
111
- `${distDir}/${_modern_js_utils__rspack_import_0.SERVER_BUNDLE_DIRECTORY}/*-server-loaders.js`
108
+ (0, _mock__rspack_import_3.resolveMockDirectory)(pwd, mockDir),
109
+ _modern_js_utils__rspack_import_0.SERVER_DIR,
110
+ apiDir,
111
+ sharedDir,
112
+ loaderDirectory
112
113
  ];
113
114
  const mergedWatchOptions = (0, _dev_tools_watcher__rspack_import_2.mergeWatchOptions)(watchOptions);
115
+ const ignored = mergedWatchOptions.ignored;
116
+ mergedWatchOptions.ignored = [
117
+ ...Array.isArray(ignored) ? ignored : ignored ? [
118
+ ignored
119
+ ] : [],
120
+ (file)=>file.startsWith(`${loaderDirectory}${path__rspack_import_1_default().sep}`) && !file.endsWith('-server-loaders.js')
121
+ ];
114
122
  const defaultWatchedPaths = defaultWatched.map((p)=>{
115
123
  const finalPath = path__rspack_import_1_default().isAbsolute(p) ? p : path__rspack_import_1_default().join(pwd, p);
116
124
  return path__rspack_import_1_default().normalize(finalPath);
@@ -129,7 +129,7 @@ async function getMockMiddleware(pwd, mockDir) {
129
129
  if (matchedMockAPI) {
130
130
  const { handler } = matchedMockAPI;
131
131
  if ('function' == typeof handler) return await (0, node_namespaceObject.connectMockMid2HonoMid)(handler)(c, next);
132
- return c.json(handler);
132
+ else return c.json(handler);
133
133
  }
134
134
  return next();
135
135
  };
@@ -36,14 +36,17 @@ const cleanSSRCache = (distDir)=>{
36
36
  if (key.startsWith(distDir)) delete require.cache[key];
37
37
  });
38
38
  };
39
- const onRepack = (distDir, hooks)=>{
40
- cleanSSRCache(distDir);
41
- fileReader_namespaceObject.fileReader.reset();
42
- hooks.onReset.call({
43
- event: {
44
- type: 'repack'
45
- }
46
- });
39
+ const onRepack = async (distDir, hooks)=>{
40
+ try {
41
+ await hooks.onReset.call({
42
+ event: {
43
+ type: 'repack'
44
+ }
45
+ });
46
+ } finally{
47
+ cleanSSRCache(distDir);
48
+ fileReader_namespaceObject.fileReader.reset();
49
+ }
47
50
  };
48
51
  exports.onRepack = __webpack_exports__.onRepack;
49
52
  for(var __rspack_i in __webpack_exports__)if (-1 === [
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __webpack_require__ = {};
3
+ (()=>{
4
+ __webpack_require__.r = (exports1)=>{
5
+ if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
6
+ value: 'Module'
7
+ });
8
+ Object.defineProperty(exports1, '__esModule', {
9
+ value: true
10
+ });
11
+ };
12
+ })();
13
+ var __webpack_exports__ = {};
14
+ __webpack_require__.r(__webpack_exports__);
15
+ for(var __rspack_i in __webpack_exports__)exports[__rspack_i] = __webpack_exports__[__rspack_i];
16
+ Object.defineProperty(exports, '__esModule', {
17
+ value: true
18
+ });
@@ -107,6 +107,7 @@ async function createDevServer(options, applyPlugins) {
107
107
  compiler,
108
108
  nodeServer,
109
109
  getRuntimeServer: ()=>currentRuntimeServer,
110
+ holdRequests: (task)=>reloadManager.hold(task),
110
111
  onFileChange: (filepath)=>{
111
112
  pendingReloadFiles.add(node_path.relative(pwd, filepath));
112
113
  reloadManager.schedule();
@@ -74,13 +74,21 @@ const notReadyHandle = ()=>new Response('Dev server is starting…', {
74
74
  'content-type': 'text/plain; charset=utf-8'
75
75
  }
76
76
  });
77
- var _current = /*#__PURE__*/ new WeakMap(), _build = /*#__PURE__*/ new WeakMap(), _debounceMs = /*#__PURE__*/ new WeakMap(), _onReload = /*#__PURE__*/ new WeakMap(), _onError = /*#__PURE__*/ new WeakMap(), _onReloadError = /*#__PURE__*/ new WeakMap(), _debounceTimer = /*#__PURE__*/ new WeakMap(), _running = /*#__PURE__*/ new WeakMap(), _pending = /*#__PURE__*/ new WeakMap(), _runningPromise = /*#__PURE__*/ new WeakMap(), _closed = /*#__PURE__*/ new WeakMap(), _runLoop = /*#__PURE__*/ new WeakSet(), _reportBuildError = /*#__PURE__*/ new WeakSet(), _reportReloadCallbackError = /*#__PURE__*/ new WeakSet(), _dispose = /*#__PURE__*/ new WeakSet();
77
+ var _current = /*#__PURE__*/ new WeakMap(), _build = /*#__PURE__*/ new WeakMap(), _debounceMs = /*#__PURE__*/ new WeakMap(), _onReload = /*#__PURE__*/ new WeakMap(), _onError = /*#__PURE__*/ new WeakMap(), _onReloadError = /*#__PURE__*/ new WeakMap(), _debounceTimer = /*#__PURE__*/ new WeakMap(), _running = /*#__PURE__*/ new WeakMap(), _pending = /*#__PURE__*/ new WeakMap(), _runningPromise = /*#__PURE__*/ new WeakMap(), _closed = /*#__PURE__*/ new WeakMap(), _held = /*#__PURE__*/ new WeakMap(), _holding = /*#__PURE__*/ new WeakMap(), _released = /*#__PURE__*/ new WeakSet(), _runLoop = /*#__PURE__*/ new WeakSet(), _reportBuildError = /*#__PURE__*/ new WeakSet(), _reportReloadCallbackError = /*#__PURE__*/ new WeakSet(), _dispose = /*#__PURE__*/ new WeakSet();
78
78
  class ReloadManager {
79
79
  setHandle(handle) {
80
80
  _class_private_field_set(this, _current, handle);
81
81
  }
82
82
  get handle() {
83
- return (request, ...args)=>_class_private_field_get(this, _current).call(this, request, ...args);
83
+ return (request, ...args)=>0 === _class_private_field_get(this, _holding) ? _class_private_field_get(this, _current).call(this, request, ...args) : _class_private_method_get(this, _released, released).call(this).then(()=>_class_private_field_get(this, _current).call(this, request, ...args));
84
+ }
85
+ hold(task) {
86
+ _class_private_field_set(this, _holding, _class_private_field_get(this, _holding) + 1);
87
+ const run = _class_private_field_get(this, _held).then(task).finally(()=>{
88
+ _class_private_field_set(this, _holding, _class_private_field_get(this, _holding) - 1);
89
+ });
90
+ _class_private_field_set(this, _held, run.catch(()=>{}));
91
+ return run;
84
92
  }
85
93
  get currentHandle() {
86
94
  return _class_private_field_get(this, _current);
@@ -119,9 +127,11 @@ class ReloadManager {
119
127
  clearTimeout(_class_private_field_get(this, _debounceTimer));
120
128
  _class_private_field_set(this, _debounceTimer, null);
121
129
  }
122
- _class_private_method_get(this, _dispose, reloadManager_dispose).call(this, _class_private_field_get(this, _current));
130
+ const current = _class_private_field_get(this, _current);
131
+ _class_private_method_get(this, _released, released).call(this).then(()=>_class_private_method_get(this, _dispose, reloadManager_dispose).call(this, current));
123
132
  }
124
133
  constructor(options){
134
+ _class_private_method_init(this, _released);
125
135
  _class_private_method_init(this, _runLoop);
126
136
  _class_private_method_init(this, _reportBuildError);
127
137
  _class_private_method_init(this, _reportReloadCallbackError);
@@ -170,11 +180,21 @@ class ReloadManager {
170
180
  writable: true,
171
181
  value: void 0
172
182
  });
183
+ _class_private_field_init(this, _held, {
184
+ writable: true,
185
+ value: void 0
186
+ });
187
+ _class_private_field_init(this, _holding, {
188
+ writable: true,
189
+ value: void 0
190
+ });
173
191
  _class_private_field_set(this, _debounceTimer, null);
174
192
  _class_private_field_set(this, _running, false);
175
193
  _class_private_field_set(this, _pending, false);
176
194
  _class_private_field_set(this, _runningPromise, null);
177
195
  _class_private_field_set(this, _closed, false);
196
+ _class_private_field_set(this, _held, Promise.resolve());
197
+ _class_private_field_set(this, _holding, 0);
178
198
  _class_private_field_set(this, _current, options.initialHandle ?? notReadyHandle);
179
199
  _class_private_field_set(this, _build, options.build);
180
200
  _class_private_field_set(this, _debounceMs, options.debounceMs ?? DEFAULT_DEBOUNCE_MS);
@@ -183,6 +203,9 @@ class ReloadManager {
183
203
  _class_private_field_set(this, _onReloadError, options.onReloadError);
184
204
  }
185
205
  }
206
+ async function released() {
207
+ while(_class_private_field_get(this, _holding) > 0)await _class_private_field_get(this, _held);
208
+ }
186
209
  async function runLoop() {
187
210
  var _this, _this1;
188
211
  do {
@@ -194,6 +217,7 @@ async function runLoop() {
194
217
  _class_private_method_get(this, _reportBuildError, reportBuildError).call(this, error);
195
218
  continue;
196
219
  }
220
+ await _class_private_method_get(this, _released, released).call(this);
197
221
  if (_class_private_field_get(this, _closed)) return void await _class_private_method_get(this, _dispose, reloadManager_dispose).call(this, next);
198
222
  const previous = _class_private_field_get(this, _current);
199
223
  _class_private_field_set(this, _current, next);
@@ -26,34 +26,28 @@ class DependencyTree {
26
26
  for (const treeNode of this.tree.values()){
27
27
  const { parent } = treeNode.module;
28
28
  const { children } = treeNode.module;
29
- if (parent && !this.shouldIgnore(parent.filename)) {
29
+ if (parent) {
30
30
  const parentTreeNode = this.tree.get(parent.filename);
31
31
  if (parentTreeNode) treeNode.parent.add(parentTreeNode);
32
32
  }
33
33
  children?.forEach((child)=>{
34
- if (!this.shouldIgnore(child.filename)) {
35
- const childTreeNode = this.tree.get(child.filename);
36
- if (childTreeNode) {
37
- treeNode.children.add(childTreeNode);
38
- childTreeNode.parent.add(treeNode);
39
- }
34
+ const childTreeNode = this.tree.get(child.filename);
35
+ if (childTreeNode) {
36
+ treeNode.children.add(childTreeNode);
37
+ childTreeNode.parent.add(treeNode);
40
38
  }
41
39
  });
42
40
  }
43
41
  }
44
42
  shouldIgnore(path) {
45
43
  const matchPath = path && node_path.isAbsolute(path) ? node_path.relative(process.cwd(), path) : path;
46
- return !matchPath || Boolean(this.ignore.find((rule)=>__rspack_external_minimatch.match([
47
- matchPath
48
- ], rule, {
49
- dot: true
50
- }).length > 0));
44
+ return !matchPath || this.ignore.some((rule)=>rule.match(matchPath));
51
45
  }
52
46
  constructor(){
53
47
  this.tree = new Map();
54
- this.ignore = [
55
- ...defaultIgnores
56
- ];
48
+ this.ignore = defaultIgnores.map((rule)=>new __rspack_external_minimatch.Minimatch(rule, {
49
+ dot: true
50
+ }));
57
51
  }
58
52
  }
59
53
  export { DependencyTree, defaultIgnores };
@@ -4,7 +4,7 @@ import { DependencyTree } from "./dependencyTree.mjs";
4
4
  import { StatsCache } from "./statsCache.mjs";
5
5
  const defaultWatchOptions = {
6
6
  ignoreInitial: true,
7
- ignored: /api\/typings\/.*/
7
+ ignored: /api\/typings\/.*|(?:^|[/\\])node_modules(?:[/\\]|$)/
8
8
  };
9
9
  const getWatchedFiles = (watcher)=>{
10
10
  const watched = watcher.getWatched();
package/dist/esm/dev.mjs CHANGED
@@ -1,6 +1,19 @@
1
+ import node_path from "node:path";
1
2
  import { connectMid2HonoMid } from "@modern-js/server-core/node";
2
3
  import { API_DIR, SHARED_DIR, logger } from "@modern-js/utils";
3
4
  import { getDevOptions, getMockMiddleware, initFileReader, isPathInsideDirectory, onRepack, resolveMockDirectory, startWatcher } from "./helpers/index.mjs";
5
+ function isServerTemplateRequest(pathname, routes) {
6
+ const templates = new Set((routes ?? []).flatMap((route)=>route.entryName && route.entryPath?.endsWith('.html') ? [
7
+ `/${route.entryPath.replace(/\\/g, '/').replace(/^\.?\//, '')}`
8
+ ] : []));
9
+ if (!templates.size) return false;
10
+ const candidates = [
11
+ pathname
12
+ ];
13
+ if (pathname.endsWith('/')) candidates.push(`${pathname}index.html`);
14
+ else if (!node_path.posix.extname(pathname)) candidates.push(`${pathname}.html`, `${pathname}/index.html`);
15
+ return candidates.some((candidate)=>templates.has(candidate));
16
+ }
4
17
  const devRuntimeMiddlewarePlugin = (options, compiler)=>({
5
18
  name: '@modern-js/plugin-dev',
6
19
  setup (api) {
@@ -31,10 +44,14 @@ const devRuntimeMiddlewarePlugin = (options, compiler)=>({
31
44
  name: 'mock-dev',
32
45
  handler: mockMiddleware
33
46
  });
34
- builderMiddlewares && middlewares.push({
35
- name: 'rsbuild-dev',
36
- handler: connectMid2HonoMid(builderMiddlewares)
37
- });
47
+ if (builderMiddlewares) {
48
+ const builderHandler = connectMid2HonoMid(builderMiddlewares);
49
+ const handler = (c, next)=>isServerTemplateRequest(c.req.path, api.getServerContext().routes) ? next() : builderHandler(c, next);
50
+ middlewares.push({
51
+ name: 'rsbuild-dev',
52
+ handler
53
+ });
54
+ }
38
55
  after.forEach((middleware, index)=>{
39
56
  middlewares.push({
40
57
  name: `after-dev-server-${index}`,
@@ -48,7 +65,7 @@ const devRuntimeMiddlewarePlugin = (options, compiler)=>({
48
65
  });
49
66
  }
50
67
  });
51
- function setupDevInfra({ config, pwd, distDir, apiDir, sharedDir, builder, builderDevServer, getRuntimeServer, onFileChange, onClose, nodeServer }) {
68
+ function setupDevInfra({ config, pwd, distDir, apiDir, sharedDir, builder, builderDevServer, getRuntimeServer, holdRequests, onFileChange, onClose, nodeServer }) {
52
69
  const { close, connectWebSocket } = builderDevServer || {};
53
70
  const closeCb = [];
54
71
  close && closeCb.push(close);
@@ -58,10 +75,10 @@ function setupDevInfra({ config, pwd, distDir, apiDir, sharedDir, builder, build
58
75
  builder?.onDevCompileDone(({ stats })=>{
59
76
  if ('server' !== stats.toJson({
60
77
  all: false
61
- }).name) {
78
+ }).name) holdRequests(async ()=>{
62
79
  const runtimeServer = getRuntimeServer();
63
- if (runtimeServer) onRepack(distDir, runtimeServer.hooks);
64
- }
80
+ if (runtimeServer) await onRepack(distDir, runtimeServer.hooks);
81
+ }).catch((error)=>logger.error(error));
65
82
  });
66
83
  const { watchOptions } = config.server;
67
84
  const mockDir = config.dev?.mockDir;
@@ -104,4 +121,4 @@ function setupDevInfra({ config, pwd, distDir, apiDir, sharedDir, builder, build
104
121
  close: close$
105
122
  };
106
123
  }
107
- export { devRuntimeMiddlewarePlugin, setupDevInfra };
124
+ export { devRuntimeMiddlewarePlugin, isServerTemplateRequest, setupDevInfra };
@@ -7,14 +7,22 @@ export * from "./fileReader.mjs";
7
7
  export * from "./mock.mjs";
8
8
  export * from "./repack.mjs";
9
9
  function startWatcher({ pwd, distDir, apiDir, sharedDir, mockDir, watchOptions, onChange }) {
10
+ const loaderDirectory = path.resolve(pwd, distDir, SERVER_BUNDLE_DIRECTORY);
10
11
  const defaultWatched = [
11
- path.join(resolveMockDirectory(pwd, mockDir), '**/*'),
12
- `${SERVER_DIR}/**/*`,
13
- `${apiDir}/**`,
14
- `${sharedDir}/**/*`,
15
- `${distDir}/${SERVER_BUNDLE_DIRECTORY}/*-server-loaders.js`
12
+ resolveMockDirectory(pwd, mockDir),
13
+ SERVER_DIR,
14
+ apiDir,
15
+ sharedDir,
16
+ loaderDirectory
16
17
  ];
17
18
  const mergedWatchOptions = mergeWatchOptions(watchOptions);
19
+ const ignored = mergedWatchOptions.ignored;
20
+ mergedWatchOptions.ignored = [
21
+ ...Array.isArray(ignored) ? ignored : ignored ? [
22
+ ignored
23
+ ] : [],
24
+ (file)=>file.startsWith(`${loaderDirectory}${path.sep}`) && !file.endsWith('-server-loaders.js')
25
+ ];
18
26
  const defaultWatchedPaths = defaultWatched.map((p)=>{
19
27
  const finalPath = path.isAbsolute(p) ? p : path.join(pwd, p);
20
28
  return path.normalize(finalPath);
@@ -83,7 +83,7 @@ async function getMockMiddleware(pwd, mockDir) {
83
83
  if (matchedMockAPI) {
84
84
  const { handler } = matchedMockAPI;
85
85
  if ('function' == typeof handler) return await connectMockMid2HonoMid(handler)(c, next);
86
- return c.json(handler);
86
+ else return c.json(handler);
87
87
  }
88
88
  return next();
89
89
  };
@@ -4,13 +4,16 @@ const cleanSSRCache = (distDir)=>{
4
4
  if (key.startsWith(distDir)) delete require.cache[key];
5
5
  });
6
6
  };
7
- const onRepack = (distDir, hooks)=>{
8
- cleanSSRCache(distDir);
9
- fileReader.reset();
10
- hooks.onReset.call({
11
- event: {
12
- type: 'repack'
13
- }
14
- });
7
+ const onRepack = async (distDir, hooks)=>{
8
+ try {
9
+ await hooks.onReset.call({
10
+ event: {
11
+ type: 'repack'
12
+ }
13
+ });
14
+ } finally{
15
+ cleanSSRCache(distDir);
16
+ fileReader.reset();
17
+ }
15
18
  };
16
19
  export { onRepack };
File without changes
@@ -108,6 +108,7 @@ async function createDevServer(options, applyPlugins) {
108
108
  compiler,
109
109
  nodeServer,
110
110
  getRuntimeServer: ()=>currentRuntimeServer,
111
+ holdRequests: (task)=>reloadManager.hold(task),
111
112
  onFileChange: (filepath)=>{
112
113
  pendingReloadFiles.add(node_path.relative(pwd, filepath));
113
114
  reloadManager.schedule();
@@ -75,13 +75,21 @@ const notReadyHandle = ()=>new Response('Dev server is starting…', {
75
75
  'content-type': 'text/plain; charset=utf-8'
76
76
  }
77
77
  });
78
- var _current = /*#__PURE__*/ new WeakMap(), _build = /*#__PURE__*/ new WeakMap(), _debounceMs = /*#__PURE__*/ new WeakMap(), _onReload = /*#__PURE__*/ new WeakMap(), _onError = /*#__PURE__*/ new WeakMap(), _onReloadError = /*#__PURE__*/ new WeakMap(), _debounceTimer = /*#__PURE__*/ new WeakMap(), _running = /*#__PURE__*/ new WeakMap(), _pending = /*#__PURE__*/ new WeakMap(), _runningPromise = /*#__PURE__*/ new WeakMap(), _closed = /*#__PURE__*/ new WeakMap(), _runLoop = /*#__PURE__*/ new WeakSet(), _reportBuildError = /*#__PURE__*/ new WeakSet(), _reportReloadCallbackError = /*#__PURE__*/ new WeakSet(), _dispose = /*#__PURE__*/ new WeakSet();
78
+ var _current = /*#__PURE__*/ new WeakMap(), _build = /*#__PURE__*/ new WeakMap(), _debounceMs = /*#__PURE__*/ new WeakMap(), _onReload = /*#__PURE__*/ new WeakMap(), _onError = /*#__PURE__*/ new WeakMap(), _onReloadError = /*#__PURE__*/ new WeakMap(), _debounceTimer = /*#__PURE__*/ new WeakMap(), _running = /*#__PURE__*/ new WeakMap(), _pending = /*#__PURE__*/ new WeakMap(), _runningPromise = /*#__PURE__*/ new WeakMap(), _closed = /*#__PURE__*/ new WeakMap(), _held = /*#__PURE__*/ new WeakMap(), _holding = /*#__PURE__*/ new WeakMap(), _released = /*#__PURE__*/ new WeakSet(), _runLoop = /*#__PURE__*/ new WeakSet(), _reportBuildError = /*#__PURE__*/ new WeakSet(), _reportReloadCallbackError = /*#__PURE__*/ new WeakSet(), _dispose = /*#__PURE__*/ new WeakSet();
79
79
  class ReloadManager {
80
80
  setHandle(handle) {
81
81
  _class_private_field_set(this, _current, handle);
82
82
  }
83
83
  get handle() {
84
- return (request, ...args)=>_class_private_field_get(this, _current).call(this, request, ...args);
84
+ return (request, ...args)=>0 === _class_private_field_get(this, _holding) ? _class_private_field_get(this, _current).call(this, request, ...args) : _class_private_method_get(this, _released, released).call(this).then(()=>_class_private_field_get(this, _current).call(this, request, ...args));
85
+ }
86
+ hold(task) {
87
+ _class_private_field_set(this, _holding, _class_private_field_get(this, _holding) + 1);
88
+ const run = _class_private_field_get(this, _held).then(task).finally(()=>{
89
+ _class_private_field_set(this, _holding, _class_private_field_get(this, _holding) - 1);
90
+ });
91
+ _class_private_field_set(this, _held, run.catch(()=>{}));
92
+ return run;
85
93
  }
86
94
  get currentHandle() {
87
95
  return _class_private_field_get(this, _current);
@@ -120,9 +128,11 @@ class ReloadManager {
120
128
  clearTimeout(_class_private_field_get(this, _debounceTimer));
121
129
  _class_private_field_set(this, _debounceTimer, null);
122
130
  }
123
- _class_private_method_get(this, _dispose, reloadManager_dispose).call(this, _class_private_field_get(this, _current));
131
+ const current = _class_private_field_get(this, _current);
132
+ _class_private_method_get(this, _released, released).call(this).then(()=>_class_private_method_get(this, _dispose, reloadManager_dispose).call(this, current));
124
133
  }
125
134
  constructor(options){
135
+ _class_private_method_init(this, _released);
126
136
  _class_private_method_init(this, _runLoop);
127
137
  _class_private_method_init(this, _reportBuildError);
128
138
  _class_private_method_init(this, _reportReloadCallbackError);
@@ -171,11 +181,21 @@ class ReloadManager {
171
181
  writable: true,
172
182
  value: void 0
173
183
  });
184
+ _class_private_field_init(this, _held, {
185
+ writable: true,
186
+ value: void 0
187
+ });
188
+ _class_private_field_init(this, _holding, {
189
+ writable: true,
190
+ value: void 0
191
+ });
174
192
  _class_private_field_set(this, _debounceTimer, null);
175
193
  _class_private_field_set(this, _running, false);
176
194
  _class_private_field_set(this, _pending, false);
177
195
  _class_private_field_set(this, _runningPromise, null);
178
196
  _class_private_field_set(this, _closed, false);
197
+ _class_private_field_set(this, _held, Promise.resolve());
198
+ _class_private_field_set(this, _holding, 0);
179
199
  _class_private_field_set(this, _current, options.initialHandle ?? notReadyHandle);
180
200
  _class_private_field_set(this, _build, options.build);
181
201
  _class_private_field_set(this, _debounceMs, options.debounceMs ?? DEFAULT_DEBOUNCE_MS);
@@ -184,6 +204,9 @@ class ReloadManager {
184
204
  _class_private_field_set(this, _onReloadError, options.onReloadError);
185
205
  }
186
206
  }
207
+ async function released() {
208
+ while(_class_private_field_get(this, _holding) > 0)await _class_private_field_get(this, _held);
209
+ }
187
210
  async function runLoop() {
188
211
  var _this, _this1;
189
212
  do {
@@ -195,6 +218,7 @@ async function runLoop() {
195
218
  _class_private_method_get(this, _reportBuildError, reportBuildError).call(this, error);
196
219
  continue;
197
220
  }
221
+ await _class_private_method_get(this, _released, released).call(this);
198
222
  if (_class_private_field_get(this, _closed)) return void await _class_private_method_get(this, _dispose, reloadManager_dispose).call(this, next);
199
223
  const previous = _class_private_field_get(this, _current);
200
224
  _class_private_field_set(this, _current, next);
@@ -27,34 +27,28 @@ class DependencyTree {
27
27
  for (const treeNode of this.tree.values()){
28
28
  const { parent } = treeNode.module;
29
29
  const { children } = treeNode.module;
30
- if (parent && !this.shouldIgnore(parent.filename)) {
30
+ if (parent) {
31
31
  const parentTreeNode = this.tree.get(parent.filename);
32
32
  if (parentTreeNode) treeNode.parent.add(parentTreeNode);
33
33
  }
34
34
  children?.forEach((child)=>{
35
- if (!this.shouldIgnore(child.filename)) {
36
- const childTreeNode = this.tree.get(child.filename);
37
- if (childTreeNode) {
38
- treeNode.children.add(childTreeNode);
39
- childTreeNode.parent.add(treeNode);
40
- }
35
+ const childTreeNode = this.tree.get(child.filename);
36
+ if (childTreeNode) {
37
+ treeNode.children.add(childTreeNode);
38
+ childTreeNode.parent.add(treeNode);
41
39
  }
42
40
  });
43
41
  }
44
42
  }
45
43
  shouldIgnore(path) {
46
44
  const matchPath = path && node_path.isAbsolute(path) ? node_path.relative(process.cwd(), path) : path;
47
- return !matchPath || Boolean(this.ignore.find((rule)=>__rspack_external_minimatch.match([
48
- matchPath
49
- ], rule, {
50
- dot: true
51
- }).length > 0));
45
+ return !matchPath || this.ignore.some((rule)=>rule.match(matchPath));
52
46
  }
53
47
  constructor(){
54
48
  this.tree = new Map();
55
- this.ignore = [
56
- ...defaultIgnores
57
- ];
49
+ this.ignore = defaultIgnores.map((rule)=>new __rspack_external_minimatch.Minimatch(rule, {
50
+ dot: true
51
+ }));
58
52
  }
59
53
  }
60
54
  export { DependencyTree, defaultIgnores };
@@ -6,7 +6,7 @@ import { DependencyTree } from "./dependencyTree.mjs";
6
6
  import { StatsCache } from "./statsCache.mjs";
7
7
  const defaultWatchOptions = {
8
8
  ignoreInitial: true,
9
- ignored: /api\/typings\/.*/
9
+ ignored: /api\/typings\/.*|(?:^|[/\\])node_modules(?:[/\\]|$)/
10
10
  };
11
11
  const getWatchedFiles = (watcher)=>{
12
12
  const watched = watcher.getWatched();
@@ -1,7 +1,20 @@
1
1
  import "node:module";
2
+ import node_path from "node:path";
2
3
  import { connectMid2HonoMid } from "@modern-js/server-core/node";
3
4
  import { API_DIR, SHARED_DIR, logger } from "@modern-js/utils";
4
5
  import { getDevOptions, getMockMiddleware, initFileReader, isPathInsideDirectory, onRepack, resolveMockDirectory, startWatcher } from "./helpers/index.mjs";
6
+ function isServerTemplateRequest(pathname, routes) {
7
+ const templates = new Set((routes ?? []).flatMap((route)=>route.entryName && route.entryPath?.endsWith('.html') ? [
8
+ `/${route.entryPath.replace(/\\/g, '/').replace(/^\.?\//, '')}`
9
+ ] : []));
10
+ if (!templates.size) return false;
11
+ const candidates = [
12
+ pathname
13
+ ];
14
+ if (pathname.endsWith('/')) candidates.push(`${pathname}index.html`);
15
+ else if (!node_path.posix.extname(pathname)) candidates.push(`${pathname}.html`, `${pathname}/index.html`);
16
+ return candidates.some((candidate)=>templates.has(candidate));
17
+ }
5
18
  const devRuntimeMiddlewarePlugin = (options, compiler)=>({
6
19
  name: '@modern-js/plugin-dev',
7
20
  setup (api) {
@@ -32,10 +45,14 @@ const devRuntimeMiddlewarePlugin = (options, compiler)=>({
32
45
  name: 'mock-dev',
33
46
  handler: mockMiddleware
34
47
  });
35
- builderMiddlewares && middlewares.push({
36
- name: 'rsbuild-dev',
37
- handler: connectMid2HonoMid(builderMiddlewares)
38
- });
48
+ if (builderMiddlewares) {
49
+ const builderHandler = connectMid2HonoMid(builderMiddlewares);
50
+ const handler = (c, next)=>isServerTemplateRequest(c.req.path, api.getServerContext().routes) ? next() : builderHandler(c, next);
51
+ middlewares.push({
52
+ name: 'rsbuild-dev',
53
+ handler
54
+ });
55
+ }
39
56
  after.forEach((middleware, index)=>{
40
57
  middlewares.push({
41
58
  name: `after-dev-server-${index}`,
@@ -49,7 +66,7 @@ const devRuntimeMiddlewarePlugin = (options, compiler)=>({
49
66
  });
50
67
  }
51
68
  });
52
- function setupDevInfra({ config, pwd, distDir, apiDir, sharedDir, builder, builderDevServer, getRuntimeServer, onFileChange, onClose, nodeServer }) {
69
+ function setupDevInfra({ config, pwd, distDir, apiDir, sharedDir, builder, builderDevServer, getRuntimeServer, holdRequests, onFileChange, onClose, nodeServer }) {
53
70
  const { close, connectWebSocket } = builderDevServer || {};
54
71
  const closeCb = [];
55
72
  close && closeCb.push(close);
@@ -59,10 +76,10 @@ function setupDevInfra({ config, pwd, distDir, apiDir, sharedDir, builder, build
59
76
  builder?.onDevCompileDone(({ stats })=>{
60
77
  if ('server' !== stats.toJson({
61
78
  all: false
62
- }).name) {
79
+ }).name) holdRequests(async ()=>{
63
80
  const runtimeServer = getRuntimeServer();
64
- if (runtimeServer) onRepack(distDir, runtimeServer.hooks);
65
- }
81
+ if (runtimeServer) await onRepack(distDir, runtimeServer.hooks);
82
+ }).catch((error)=>logger.error(error));
66
83
  });
67
84
  const { watchOptions } = config.server;
68
85
  const mockDir = config.dev?.mockDir;
@@ -105,4 +122,4 @@ function setupDevInfra({ config, pwd, distDir, apiDir, sharedDir, builder, build
105
122
  close: close$
106
123
  };
107
124
  }
108
- export { devRuntimeMiddlewarePlugin, setupDevInfra };
125
+ export { devRuntimeMiddlewarePlugin, isServerTemplateRequest, setupDevInfra };
@@ -9,14 +9,22 @@ export * from "./fileReader.mjs";
9
9
  export * from "./mock.mjs";
10
10
  export * from "./repack.mjs";
11
11
  function startWatcher({ pwd, distDir, apiDir, sharedDir, mockDir, watchOptions, onChange }) {
12
+ const loaderDirectory = path.resolve(pwd, distDir, SERVER_BUNDLE_DIRECTORY);
12
13
  const defaultWatched = [
13
- path.join(resolveMockDirectory(pwd, mockDir), '**/*'),
14
- `${SERVER_DIR}/**/*`,
15
- `${apiDir}/**`,
16
- `${sharedDir}/**/*`,
17
- `${distDir}/${SERVER_BUNDLE_DIRECTORY}/*-server-loaders.js`
14
+ resolveMockDirectory(pwd, mockDir),
15
+ SERVER_DIR,
16
+ apiDir,
17
+ sharedDir,
18
+ loaderDirectory
18
19
  ];
19
20
  const mergedWatchOptions = mergeWatchOptions(watchOptions);
21
+ const ignored = mergedWatchOptions.ignored;
22
+ mergedWatchOptions.ignored = [
23
+ ...Array.isArray(ignored) ? ignored : ignored ? [
24
+ ignored
25
+ ] : [],
26
+ (file)=>file.startsWith(`${loaderDirectory}${path.sep}`) && !file.endsWith('-server-loaders.js')
27
+ ];
20
28
  const defaultWatchedPaths = defaultWatched.map((p)=>{
21
29
  const finalPath = path.isAbsolute(p) ? p : path.join(pwd, p);
22
30
  return path.normalize(finalPath);
@@ -84,7 +84,7 @@ async function getMockMiddleware(pwd, mockDir) {
84
84
  if (matchedMockAPI) {
85
85
  const { handler } = matchedMockAPI;
86
86
  if ('function' == typeof handler) return await connectMockMid2HonoMid(handler)(c, next);
87
- return c.json(handler);
87
+ else return c.json(handler);
88
88
  }
89
89
  return next();
90
90
  };
@@ -6,13 +6,16 @@ const cleanSSRCache = (distDir)=>{
6
6
  if (key.startsWith(distDir)) delete require.cache[key];
7
7
  });
8
8
  };
9
- const onRepack = (distDir, hooks)=>{
10
- cleanSSRCache(distDir);
11
- fileReader.reset();
12
- hooks.onReset.call({
13
- event: {
14
- type: 'repack'
15
- }
16
- });
9
+ const onRepack = async (distDir, hooks)=>{
10
+ try {
11
+ await hooks.onReset.call({
12
+ event: {
13
+ type: 'repack'
14
+ }
15
+ });
16
+ } finally{
17
+ cleanSSRCache(distDir);
18
+ fileReader.reset();
19
+ }
17
20
  };
18
21
  export { onRepack };
@@ -0,0 +1 @@
1
+ import "node:module";
@@ -71,6 +71,15 @@ export declare class ReloadManager {
71
71
  * be recreated on reload.
72
72
  */
73
73
  get handle(): ReloadableHandle;
74
+ /**
75
+ * Run `task` after the previously held tasks, and make requests arriving
76
+ * through `handle` wait until it settles. A reload waits for it before it
77
+ * swaps the runtime. Requests already dispatched are not waited for: an SSR
78
+ * loader fetching its own dev server would deadlock. The task itself must
79
+ * not await a request to this handle, which waits for the task.
80
+ * Returns the task's own result, rejection included.
81
+ */
82
+ hold(task: () => Promise<void>): Promise<void>;
74
83
  /** The currently active resolved handle (introspection / tests). */
75
84
  get currentHandle(): ReloadableHandle;
76
85
  /** Whether a reload is currently running (introspection / tests). */
@@ -3,9 +3,12 @@ import type { Http2SecureServer } from 'node:http2';
3
3
  import type { Server as NodeHttpsServer } from 'node:https';
4
4
  import type { BuilderInstance, Rspack } from '@modern-js/builder';
5
5
  import type { ServerBase, ServerBaseOptions, ServerPlugin } from '@modern-js/server-core';
6
+ import type { ServerRoute } from '@modern-js/types';
6
7
  import type { WatchEvent } from './dev-tools/watcher/index.js';
7
8
  import type { ModernDevServerOptions } from './types.js';
8
9
  type BuilderDevServer = Awaited<ReturnType<BuilderInstance['createDevServer']>>;
10
+ /** Whether Rsbuild's asset lookup would answer `pathname` with a page template. */
11
+ export declare function isServerTemplateRequest(pathname: string, routes: readonly ServerRoute[] | undefined): boolean;
9
12
  export type DevPluginOptions = ModernDevServerOptions<ServerBaseOptions> & {
10
13
  builderDevServer?: BuilderDevServer;
11
14
  };
@@ -35,6 +38,11 @@ export interface DevInfraOptions {
35
38
  nodeServer?: NodeServer | NodeHttpsServer | Http2SecureServer;
36
39
  /** Accessor for the currently-active runtime ServerBase (a mutable ref). */
37
40
  getRuntimeServer: () => ServerBase | undefined;
41
+ /**
42
+ * Run a repack task while new requests wait for it (`ReloadManager.hold`),
43
+ * so no request re-requires the server bundle mid-reset.
44
+ */
45
+ holdRequests: (task: () => Promise<void>) => Promise<void>;
38
46
  /**
39
47
  * Triggered when a watched user server file changes (require cache already
40
48
  * busted). Wired to the runtime reload scheduler.
@@ -64,5 +72,5 @@ export interface DevInfra {
64
72
  * runtime, so a later phase can swap the trigger to the reload scheduler
65
73
  * without leaking a stale closure to a dead runtime.
66
74
  */
67
- export declare function setupDevInfra({ config, pwd, distDir, apiDir, sharedDir, builder, builderDevServer, getRuntimeServer, onFileChange, onClose, nodeServer, }: DevInfraOptions): DevInfra;
75
+ export declare function setupDevInfra({ config, pwd, distDir, apiDir, sharedDir, builder, builderDevServer, getRuntimeServer, holdRequests, onFileChange, onClose, nodeServer, }: DevInfraOptions): DevInfra;
68
76
  export {};
@@ -1,2 +1,17 @@
1
1
  import type { ServerPluginHooks } from '@modern-js/server-core';
2
- export declare const onRepack: (distDir: string, hooks: ServerPluginHooks) => void;
2
+ /**
3
+ * Reset the dev server after the server bundle is rebuilt.
4
+ *
5
+ * Runtime state a rebuilt bundle keeps outside the require cache belongs to
6
+ * the plugin that owns it and is reset by that plugin's `onReset` handler:
7
+ * `@module-federation/modern-js-v3/server` resets the Module Federation
8
+ * runtime on `globalThis` there (module-federation/core#5152).
9
+ *
10
+ * Contract: the `onReset({ event: { type: 'repack' } })` handlers run in
11
+ * registration order and are awaited before the previous generation is purged.
12
+ * The caller holds new requests until this promise settles, so no request
13
+ * re-requires the bundle while a handler is still running. As with every async
14
+ * server hook, a rejecting handler skips the handlers after it; the generation
15
+ * is still purged and the promise rejects with that error.
16
+ */
17
+ export declare const onRepack: (distDir: string, hooks: ServerPluginHooks) => Promise<void>;
@@ -1,4 +1,3 @@
1
- import type { DevServerOptions } from './types.js';
2
1
  export { createDevServer } from './createDevServer.js';
3
2
  export type { ApplyPlugins, CorsOptions, ModernDevServerOptions, } from './types.js';
4
- export type SetupMiddlewares = DevServerOptions['setupMiddlewares'];
3
+ export type { SetupMiddlewares } from './types/dev.js';
@@ -0,0 +1,44 @@
1
+ import type { DevServerHttpsOptions, ExposeServerApis, RequestHandler } from '@modern-js/types/server';
2
+ export type { DevServerHttpsOptions };
3
+ type StaticOrigin = boolean | string | RegExp | Array<boolean | string | RegExp>;
4
+ type CustomOrigin = (requestOrigin: string | undefined, callback: (err: Error | null, origin?: StaticOrigin) => void) => void;
5
+ export interface CorsOptions {
6
+ /**
7
+ * @default '*''
8
+ */
9
+ origin?: StaticOrigin | CustomOrigin | undefined;
10
+ }
11
+ export interface DevServerConfig {
12
+ /**
13
+ * Configure CORS for the dev server.
14
+ * - object: enable CORS with the specified options.
15
+ * - true: enable CORS with default options (allow all origins, not recommended).
16
+ * - false: disable CORS.
17
+ * @default
18
+ * ```js
19
+ * { origin: defaultAllowedOrigins }
20
+ * ```
21
+ * where `defaultAllowedOrigins` includes:
22
+ * - `localhost`
23
+ * - `127.0.0.1`
24
+ *
25
+ * @link https://github.com/expressjs/cors
26
+ */
27
+ cors?: boolean | CorsOptions;
28
+ }
29
+ export type DevServerOptions = {
30
+ /** Provides the ability to execute a custom function and apply custom middlewares */
31
+ setupMiddlewares?: Array<(
32
+ /** Order: `devServer.before` => `unshift` => internal middlewares => `push` => `devServer.after` */
33
+ middlewares: {
34
+ /** Use the `unshift` method if you want to run a middleware before all other middlewares */
35
+ unshift: (...handlers: RequestHandler[]) => void;
36
+ /** Use the `push` method if you want to run a middleware after all other middlewares */
37
+ push: (...handlers: RequestHandler[]) => void;
38
+ }, server: ExposeServerApis) => void>;
39
+ /** Whether to enable hot reload. */
40
+ https?: DevServerHttpsOptions;
41
+ /** Dev server specific options. */
42
+ server?: DevServerConfig;
43
+ };
44
+ export type SetupMiddlewares = DevServerOptions['setupMiddlewares'];
@@ -2,49 +2,8 @@ import type { Server as NodeServer } from 'node:http';
2
2
  import type { Http2SecureServer } from 'node:http2';
3
3
  import type { BuilderInstance } from '@modern-js/builder';
4
4
  import type { ServerBase, ServerBaseOptions, ServerPlugin } from '@modern-js/server-core';
5
- import type { DevServerHttpsOptions, ExposeServerApis, RequestHandler } from '@modern-js/types';
6
- export type { DevServerHttpsOptions };
7
- type StaticOrigin = boolean | string | RegExp | Array<boolean | string | RegExp>;
8
- type CustomOrigin = (requestOrigin: string | undefined, callback: (err: Error | null, origin?: StaticOrigin) => void) => void;
9
- export interface CorsOptions {
10
- /**
11
- * @default '*''
12
- */
13
- origin?: StaticOrigin | CustomOrigin | undefined;
14
- }
15
- export interface DevServerConfig {
16
- /**
17
- * Configure CORS for the dev server.
18
- * - object: enable CORS with the specified options.
19
- * - true: enable CORS with default options (allow all origins, not recommended).
20
- * - false: disable CORS.
21
- * @default
22
- * ```js
23
- * { origin: defaultAllowedOrigins }
24
- * ```
25
- * where `defaultAllowedOrigins` includes:
26
- * - `localhost`
27
- * - `127.0.0.1`
28
- *
29
- * @link https://github.com/expressjs/cors
30
- */
31
- cors?: boolean | CorsOptions;
32
- }
33
- export type DevServerOptions = {
34
- /** Provides the ability to execute a custom function and apply custom middlewares */
35
- setupMiddlewares?: Array<(
36
- /** Order: `devServer.before` => `unshift` => internal middlewares => `push` => `devServer.after` */
37
- middlewares: {
38
- /** Use the `unshift` method if you want to run a middleware before all other middlewares */
39
- unshift: (...handlers: RequestHandler[]) => void;
40
- /** Use the `push` method if you want to run a middleware after all other middlewares */
41
- push: (...handlers: RequestHandler[]) => void;
42
- }, server: ExposeServerApis) => void>;
43
- /** Whether to enable hot reload. */
44
- https?: DevServerHttpsOptions;
45
- /** Dev server specific options. */
46
- server?: DevServerConfig;
47
- };
5
+ import type { DevServerOptions } from './types/dev.js';
6
+ export type { CorsOptions, DevServerConfig, DevServerHttpsOptions, DevServerOptions, } from './types/dev.js';
48
7
  export type ExtraOptions = {
49
8
  dev: DevServerOptions;
50
9
  runCompile?: boolean;
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@bleedingdev/modern-js-server",
3
3
  "description": "A Progressive React Framework for modern web development.",
4
- "homepage": "https://github.com/BleedingDev/ultramodern.js#readme",
4
+ "homepage": "https://github.com/ultramodernjs/ultramodern.js#readme",
5
5
  "bugs": {
6
- "url": "https://github.com/BleedingDev/ultramodern.js/issues"
6
+ "url": "https://github.com/ultramodernjs/ultramodern.js/issues"
7
7
  },
8
8
  "repository": {
9
9
  "type": "git",
10
- "url": "git+https://github.com/BleedingDev/ultramodern.js.git",
10
+ "url": "git+https://github.com/ultramodernjs/ultramodern.js.git",
11
11
  "directory": "packages/server/server"
12
12
  },
13
13
  "license": "MIT",
@@ -17,7 +17,7 @@
17
17
  "modern",
18
18
  "modern.js"
19
19
  ],
20
- "version": "3.9.0-ultramodern.9",
20
+ "version": "3.9.3-ultramodern.2",
21
21
  "types": "./dist/types/index.d.ts",
22
22
  "main": "./dist/cjs/index.js",
23
23
  "exports": {
@@ -28,36 +28,42 @@
28
28
  "require": "./dist/cjs/index.js"
29
29
  },
30
30
  "default": "./dist/cjs/index.js"
31
+ },
32
+ "./config-types": {
33
+ "types": "./dist/types/types/dev.d.ts"
31
34
  }
32
35
  },
33
36
  "typesVersions": {
34
37
  "*": {
35
38
  ".": [
36
39
  "./dist/types/index.d.ts"
40
+ ],
41
+ "config-types": [
42
+ "./dist/types/types/dev.d.ts"
37
43
  ]
38
44
  }
39
45
  },
40
46
  "dependencies": {
41
- "@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.9.0-ultramodern.9",
42
- "@modern-js/server-core": "npm:@bleedingdev/modern-js-server-core@3.9.0-ultramodern.9",
43
- "@modern-js/server-utils": "npm:@bleedingdev/modern-js-server-utils@3.9.0-ultramodern.9",
44
- "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.9.0-ultramodern.9",
45
- "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.9.0-ultramodern.9",
47
+ "@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.9.3-ultramodern.2",
48
+ "@modern-js/server-core": "npm:@bleedingdev/modern-js-server-core@3.9.3-ultramodern.2",
49
+ "@modern-js/server-utils": "npm:@bleedingdev/modern-js-server-utils@3.9.3-ultramodern.2",
50
+ "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.9.3-ultramodern.2",
51
+ "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.9.3-ultramodern.2",
46
52
  "@swc/helpers": "^0.5.23",
47
53
  "connect-history-api-fallback": "^2.0.0",
48
54
  "http-compression": "1.1.3",
49
55
  "minimatch": "^10.2.6",
50
56
  "path-to-regexp": "^8.4.2",
51
- "ws": "^8.21.3"
57
+ "ws": "^8.22.0"
52
58
  },
53
59
  "devDependencies": {
54
- "@modern-js/builder": "npm:@bleedingdev/modern-js-builder@3.9.0-ultramodern.9",
55
- "@rslib/core": "1.0.0",
60
+ "@modern-js/builder": "npm:@bleedingdev/modern-js-builder@3.9.3-ultramodern.2",
61
+ "@module-federation/runtime": "2.9.2",
62
+ "@rslib/core": "1.0.3",
56
63
  "@scripts/rstest-config": "2.66.0",
57
64
  "@types/connect-history-api-fallback": "^1.5.4",
58
- "@types/node": "^26.4.1",
59
- "@types/ws": "^8.18.1",
60
- "@typescript/native-preview": "7.0.0-dev.20260707.2",
65
+ "@types/node": "^26.6.3",
66
+ "@types/ws": "^8.18.2",
61
67
  "node-mocks-http": "^1.18.1",
62
68
  "tsconfig-paths": "4.2.0",
63
69
  "websocket": "^1.0.35"
@@ -82,5 +88,8 @@
82
88
  "build": "rslib build",
83
89
  "dev": "rslib build --watch",
84
90
  "test": "rstest"
91
+ },
92
+ "ultramodern": {
93
+ "sourceName": "@modern-js/server"
85
94
  }
86
95
  }