@rsbuild/core 1.4.3 → 1.4.5

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/dist/index.js CHANGED
@@ -8,7 +8,7 @@ import picocolors from "../compiled/picocolors/index.js";
8
8
  import node_fs, { existsSync } from "node:fs";
9
9
  import { EventEmitter } from "events";
10
10
  import { createRequire } from "node:module";
11
- import node_os, { constants } from "node:os";
11
+ import node_os, { constants as external_node_os_constants } from "node:os";
12
12
  import node_process from "node:process";
13
13
  import rspack_chain from "../compiled/rspack-chain/index.js";
14
14
  import { isPromise, isRegExp } from "node:util/types";
@@ -777,7 +777,7 @@ let constants_filename = fileURLToPath(import.meta.url), constants_dirname = dir
777
777
  node: [
778
778
  'node >= 16'
779
779
  ]
780
- }, HTML_REGEX = /\.html$/, JS_REGEX = /\.(?:js|mjs|cjs|jsx)$/, SCRIPT_REGEX = /\.(?:js|jsx|mjs|cjs|ts|tsx|mts|cts)$/, CSS_REGEX = /\.css$/, NODE_MODULES_REGEX = /[\\/]node_modules[\\/]/, PLUGIN_SWC_NAME = 'rsbuild:swc', PLUGIN_CSS_NAME = 'rsbuild:css', FONT_EXTENSIONS = [
780
+ }, HTML_REGEX = /\.html$/, JS_REGEX = /\.(?:js|mjs|cjs|jsx)$/, SCRIPT_REGEX = /\.(?:js|jsx|mjs|cjs|ts|tsx|mts|cts)$/, CSS_REGEX = /\.css$/, RAW_QUERY_REGEX = /^\?raw$/, INLINE_QUERY_REGEX = /^\?inline$/, NODE_MODULES_REGEX = /[\\/]node_modules[\\/]/, PLUGIN_SWC_NAME = 'rsbuild:swc', PLUGIN_CSS_NAME = 'rsbuild:css', FONT_EXTENSIONS = [
781
781
  'woff',
782
782
  'woff2',
783
783
  'eot',
@@ -1096,8 +1096,10 @@ let prettyTime = (seconds)=>{
1096
1096
  return `${format(seconds.toFixed(digits))} s`;
1097
1097
  }
1098
1098
  if (seconds < 60) return `${format(seconds.toFixed(1))} s`;
1099
- let minutes = seconds / 60;
1100
- return `${format(minutes.toFixed(2))} m`;
1099
+ let minutes = Math.floor(seconds / 60), minutesLabel = `${format(minutes.toFixed(0))} m`, remainingSeconds = seconds % 60;
1100
+ if (0 === remainingSeconds) return minutesLabel;
1101
+ let secondsLabel = `${format(remainingSeconds.toFixed(+(remainingSeconds % 1 != 0)))} s`;
1102
+ return `${minutesLabel} ${secondsLabel}`;
1101
1103
  }, isTTY = (type = 'stdout')=>('stdin' === type ? process.stdin.isTTY : process.stdout.isTTY) && !process.env.CI, addCompilationError = (compilation, message)=>{
1102
1104
  compilation.errors.push(new compilation.compiler.webpack.WebpackError(message));
1103
1105
  };
@@ -2040,7 +2042,7 @@ let getEnvironmentHTMLPaths = (entry, config)=>'web' !== config.output.target ||
2040
2042
  }, {});
2041
2043
  async function updateEnvironmentContext(context, configs) {
2042
2044
  for (let [index, [name, config]] of (context.environments ||= {}, Object.entries(configs).entries())){
2043
- let browserslist = await getBrowserslistByEnvironment(context.rootPath, config), { entry = {}, tsconfigPath } = config.source, htmlPaths = getEnvironmentHTMLPaths(entry, config), environmentContext = {
2045
+ let browserslist = await getBrowserslistByEnvironment(context.rootPath, config), { entry = {}, tsconfigPath } = config.source, htmlPaths = getEnvironmentHTMLPaths(entry, config), webSocketToken = 'dev' === context.action ? await helpers_hash(context.rootPath + name) : '', environmentContext = {
2044
2046
  index,
2045
2047
  name,
2046
2048
  distPath: function(cwd, config) {
@@ -2051,7 +2053,8 @@ async function updateEnvironmentContext(context, configs) {
2051
2053
  browserslist,
2052
2054
  htmlPaths,
2053
2055
  tsconfigPath,
2054
- config
2056
+ config,
2057
+ webSocketToken
2055
2058
  };
2056
2059
  context.environments[name] = new Proxy(environmentContext, {
2057
2060
  get: (target, prop)=>target[prop],
@@ -2062,7 +2065,7 @@ async function updateEnvironmentContext(context, configs) {
2062
2065
  async function createContext(options, userConfig) {
2063
2066
  let { cwd } = options, rootPath = userConfig.root ? ensureAbsolutePath(cwd, userConfig.root) : cwd, rsbuildConfig = await withDefaultConfig(rootPath, userConfig), cachePath = join(rootPath, 'node_modules', '.cache'), specifiedEnvironments = options.environment && options.environment.length > 0 ? options.environment : void 0, bundlerType = userConfig.provider ? 'webpack' : 'rspack';
2064
2067
  return {
2065
- version: "1.4.3",
2068
+ version: "1.4.5",
2066
2069
  rootPath,
2067
2070
  distPath: '',
2068
2071
  cachePath,
@@ -2630,7 +2633,7 @@ async function createCompiler_createCompiler(options) {
2630
2633
  }
2631
2634
  logger.start('build started...');
2632
2635
  }(compiler, context), isCompiling = !0;
2633
- }), 'build' === context.action && compiler.hooks.run.tap('rsbuild:run', ()=>{
2636
+ }), 'build' === context.action && (isMultiCompiler ? compiler.compilers[0] : compiler).hooks.run.tap('rsbuild:run', ()=>{
2634
2637
  logger.info('build started...'), logRspackVersion();
2635
2638
  }), compiler.hooks.done.tap('rsbuild:done', (stats)=>{
2636
2639
  ((stats)=>{
@@ -2751,7 +2754,7 @@ let chainStaticAssetRule = ({ emit, rule, maxSize, filename, assetType })=>{
2751
2754
  let generatorOptions = {
2752
2755
  filename
2753
2756
  };
2754
- !1 === emit && (generatorOptions.emit = !1), rule.oneOf(`${assetType}-asset-url`).type('asset/resource').resourceQuery(/(__inline=false|url)/).set('generator', generatorOptions), rule.oneOf(`${assetType}-asset-inline`).type('asset/inline').resourceQuery(/inline/), rule.oneOf(`${assetType}-asset-raw`).type('asset/source').resourceQuery(/raw/), rule.oneOf(`${assetType}-asset`).type('asset').parser({
2757
+ !1 === emit && (generatorOptions.emit = !1), rule.oneOf(`${assetType}-asset-url`).type('asset/resource').resourceQuery(/^\?(__inline=false|url)$/).set('generator', generatorOptions), rule.oneOf(`${assetType}-asset-inline`).type('asset/inline').resourceQuery(INLINE_QUERY_REGEX), rule.oneOf(`${assetType}-asset-raw`).type('asset/source').resourceQuery(RAW_QUERY_REGEX), rule.oneOf(`${assetType}-asset`).type('asset').parser({
2755
2758
  dataUrlCondition: {
2756
2759
  maxSize
2757
2760
  }
@@ -3639,8 +3642,8 @@ let getPort = async ({ host, port, strictPort, tryLimits = 20 })=>{
3639
3642
  return isIPv6(host) ? '::' === host ? '[::1]' : `[${host}]` : host;
3640
3643
  }, concatUrl = ({ host, port, protocol })=>`${protocol}://${host}:${port}`, LOCAL_LABEL = 'Local: ', NETWORK_LABEL = 'Network: ', getUrlLabel = (url)=>{
3641
3644
  try {
3642
- let { host } = new URL(url);
3643
- return isLoopbackHost(host) ? LOCAL_LABEL : NETWORK_LABEL;
3645
+ let { hostname } = new URL(url);
3646
+ return isLoopbackHost(hostname) ? LOCAL_LABEL : NETWORK_LABEL;
3644
3647
  } catch {
3645
3648
  return NETWORK_LABEL;
3646
3649
  }
@@ -3685,9 +3688,6 @@ let getPort = async ({ host, port, strictPort, tryLimits = 20 })=>{
3685
3688
  })
3686
3689
  });
3687
3690
  return addressUrls;
3688
- }, COMPILATION_ID_REGEX = /[^a-zA-Z0-9_-]/g, getCompilationId = (compiler)=>{
3689
- let uniqueName = compiler.options.output.uniqueName ?? '';
3690
- return `${compiler.name ?? ''}_${uniqueName.replace(COMPILATION_ID_REGEX, '_')}`;
3691
3691
  };
3692
3692
  function getServerTerminator(server) {
3693
3693
  let listened = !1, pendingSockets = new Set(), onConnection = (socket)=>{
@@ -4155,13 +4155,15 @@ async function getResolvedClientConfig(clientConfig, serverConfig) {
4155
4155
  }
4156
4156
  let getCompilationMiddleware = async (compiler, options)=>{
4157
4157
  let { default: rsbuildDevMiddleware } = await import("../compiled/rsbuild-dev-middleware/index.js"), { clientPaths, callbacks, devConfig, serverConfig } = options, resolvedClientConfig = await getResolvedClientConfig(devConfig.client, serverConfig);
4158
- return applyToCompiler(compiler, (compiler)=>{
4159
- clientPaths && function({ compiler, clientPaths, devConfig, resolvedClientConfig }) {
4158
+ return applyToCompiler(compiler, (compiler, index)=>{
4159
+ var _Object_values_find;
4160
+ let token = null == (_Object_values_find = Object.values(options.environments).find((env)=>env.index === index)) ? void 0 : _Object_values_find.webSocketToken;
4161
+ token && (clientPaths && function({ compiler, clientPaths, devConfig, resolvedClientConfig, token }) {
4160
4162
  if (((compiler)=>{
4161
4163
  let { target } = compiler.options;
4162
4164
  return !!target && (Array.isArray(target) ? target.includes('web') : 'web' === target);
4163
4165
  })(compiler)) for (let clientPath of (new compiler.webpack.DefinePlugin({
4164
- RSBUILD_COMPILATION_NAME: JSON.stringify(getCompilationId(compiler)),
4166
+ RSBUILD_WEB_SOCKET_TOKEN: JSON.stringify(token),
4165
4167
  RSBUILD_CLIENT_CONFIG: JSON.stringify(devConfig.client),
4166
4168
  RSBUILD_RESOLVED_CLIENT_CONFIG: JSON.stringify(resolvedClientConfig),
4167
4169
  RSBUILD_DEV_LIVE_RELOAD: devConfig.liveReload
@@ -4172,19 +4174,26 @@ let getCompilationMiddleware = async (compiler, options)=>{
4172
4174
  compiler,
4173
4175
  clientPaths,
4174
4176
  devConfig,
4175
- resolvedClientConfig
4176
- }), ((compiler, { onDone, onInvalid })=>{
4177
+ resolvedClientConfig,
4178
+ token
4179
+ }), (({ compiler, token, callbacks: { onDone, onInvalid } })=>{
4177
4180
  if (((compiler)=>{
4178
4181
  let { target } = compiler.options;
4179
4182
  return !!target && (Array.isArray(target) ? target.includes('node') : 'node' === target);
4180
4183
  })(compiler)) return;
4181
4184
  let { compile, invalid, done } = compiler.hooks;
4182
4185
  compile.tap('rsbuild-dev-server', ()=>{
4183
- onInvalid(getCompilationId(compiler));
4186
+ onInvalid(token);
4184
4187
  }), invalid.tap('rsbuild-dev-server', (fileName)=>{
4185
- onInvalid(getCompilationId(compiler), fileName);
4186
- }), done.tap('rsbuild-dev-server', onDone);
4187
- })(compiler, callbacks);
4188
+ onInvalid(token, fileName);
4189
+ }), done.tap('rsbuild-dev-server', (stats)=>{
4190
+ onDone(token, stats);
4191
+ });
4192
+ })({
4193
+ compiler,
4194
+ callbacks,
4195
+ token
4196
+ }));
4188
4197
  }), rsbuildDevMiddleware(compiler, {
4189
4198
  etag: 'weak',
4190
4199
  publicPath: '/',
@@ -4230,6 +4239,10 @@ function socketServer_define_property(obj, key, value) {
4230
4239
  writable: !0
4231
4240
  }) : obj[key] = value, obj;
4232
4241
  }
4242
+ let parseQueryString = (req)=>{
4243
+ let queryStr = req.url ? req.url.split('?')[1] : '';
4244
+ return queryStr ? Object.fromEntries(new URLSearchParams(queryStr)) : {};
4245
+ };
4233
4246
  class SocketServer {
4234
4247
  clearHeartbeatTimer() {
4235
4248
  this.heartbeatTimer && (clearTimeout(this.heartbeatTimer), this.heartbeatTimer = null);
@@ -4244,51 +4257,44 @@ class SocketServer {
4244
4257
  }), this.wsServer.on('error', (err)=>{
4245
4258
  logger.error(err);
4246
4259
  }), this.heartbeatTimer = setTimeout(this.checkSockets, 30000).unref(), this.wsServer.on('connection', (socket, req)=>{
4247
- let queryStr = req.url ? req.url.split('?')[1] : '';
4248
- this.onConnect(socket, queryStr ? Object.fromEntries(new URLSearchParams(queryStr)) : {});
4260
+ let query = parseQueryString(req);
4261
+ this.onConnect(socket, query.token);
4249
4262
  });
4250
4263
  }
4251
- updateStats(stats) {
4252
- let compilationId = getCompilationId(stats.compilation);
4253
- this.stats[compilationId] = stats, this.sockets.length && this.sendStats({
4254
- compilationId
4264
+ updateStats(stats, token) {
4265
+ this.stats[token] = stats, this.sockets.size && this.sendStats({
4266
+ token
4255
4267
  });
4256
4268
  }
4257
- sockWrite({ type, compilationId, data }) {
4258
- for (let socket of this.sockets)this.send(socket, JSON.stringify({
4259
- type,
4260
- data,
4261
- compilationId
4262
- }));
4269
+ sockWrite(message, token) {
4270
+ let messageStr = JSON.stringify(message);
4271
+ if (token) {
4272
+ let socket = this.sockets.get(token);
4273
+ socket && this.send(socket, messageStr);
4274
+ } else for (let socket of this.sockets.values())this.send(socket, messageStr);
4263
4275
  }
4264
- singleWrite(socket, { type, data, compilationId }) {
4265
- this.send(socket, JSON.stringify({
4266
- type,
4267
- data,
4268
- compilationId
4269
- }));
4276
+ singleWrite(socket, message) {
4277
+ this.send(socket, JSON.stringify(message));
4270
4278
  }
4271
4279
  async close() {
4272
4280
  for (let socket of (this.clearHeartbeatTimer(), this.wsServer.removeAllListeners(), this.wsServer.clients))socket.terminate();
4273
- for (let socket of this.sockets)socket.close();
4274
- return this.stats = {}, this.initialChunks = {}, this.sockets.length = 0, new Promise((resolve, reject)=>{
4281
+ for (let socket of this.sockets.values())socket.close();
4282
+ return this.stats = {}, this.initialChunks = {}, this.sockets.clear(), new Promise((resolve, reject)=>{
4275
4283
  this.wsServer.close((err)=>{
4276
4284
  err ? reject(err) : resolve();
4277
4285
  });
4278
4286
  });
4279
4287
  }
4280
- onConnect(socket, params) {
4288
+ onConnect(socket, token) {
4281
4289
  socket.isAlive = !0, socket.on('pong', ()=>{
4282
4290
  socket.isAlive = !0;
4283
- }), this.sockets.push(socket), socket.on('close', ()=>{
4284
- let index = this.sockets.indexOf(socket);
4285
- index >= 0 && this.sockets.splice(index, 1);
4291
+ }), this.sockets.set(token, socket), socket.on('close', ()=>{
4292
+ this.sockets.delete(token);
4286
4293
  }), (this.options.hmr || this.options.liveReload) && this.singleWrite(socket, {
4287
- type: 'hot',
4288
- compilationId: params.compilationId
4294
+ type: 'hot'
4289
4295
  }), this.stats && this.sendStats({
4290
4296
  force: !0,
4291
- compilationId: params.compilationId
4297
+ token
4292
4298
  });
4293
4299
  }
4294
4300
  getStats(name) {
@@ -4313,37 +4319,33 @@ class SocketServer {
4313
4319
  root: curStats.compilation.compiler.options.context
4314
4320
  } : null;
4315
4321
  }
4316
- sendStats({ force = !1, compilationId }) {
4317
- let result = this.getStats(compilationId);
4322
+ sendStats({ force = !1, token }) {
4323
+ let result = this.getStats(token);
4318
4324
  if (!result) return null;
4319
4325
  let { statsJson, root } = result, newInitialChunks = new Set();
4320
4326
  if (statsJson.entrypoints) for (let entrypoint of Object.values(statsJson.entrypoints)){
4321
4327
  let chunks = entrypoint.chunks;
4322
4328
  if (Array.isArray(chunks)) for (let chunkName of chunks)chunkName && newInitialChunks.add(String(chunkName));
4323
4329
  }
4324
- let initialChunks = this.initialChunks[compilationId], shouldReload = !!statsJson.entrypoints && !!initialChunks && !(initialChunks.size === newInitialChunks.size && [
4330
+ let initialChunks = this.initialChunks[token], shouldReload = !!statsJson.entrypoints && !!initialChunks && !(initialChunks.size === newInitialChunks.size && [
4325
4331
  ...initialChunks
4326
4332
  ].every((value)=>newInitialChunks.has(value)));
4327
- if (this.initialChunks[compilationId] = newInitialChunks, shouldReload) return this.sockWrite({
4328
- type: 'static-changed',
4329
- compilationId
4330
- });
4333
+ if (this.initialChunks[token] = newInitialChunks, shouldReload) return this.sockWrite({
4334
+ type: 'static-changed'
4335
+ }, token);
4331
4336
  if (!force && statsJson && !statsJson.errorsCount && statsJson.assets && statsJson.assets.every((asset)=>!asset.emitted)) return this.sockWrite({
4332
- type: 'ok',
4333
- compilationId
4334
- });
4337
+ type: 'ok'
4338
+ }, token);
4335
4339
  if (this.sockWrite({
4336
4340
  type: 'hash',
4337
- compilationId,
4338
4341
  data: statsJson.hash
4339
- }), statsJson.errorsCount) {
4342
+ }, token), statsJson.errorsCount) {
4340
4343
  let { errors: formattedErrors } = formatStatsMessages({
4341
4344
  errors: getAllStatsErrors(statsJson),
4342
4345
  warnings: []
4343
4346
  });
4344
4347
  return this.sockWrite({
4345
4348
  type: 'errors',
4346
- compilationId,
4347
4349
  data: {
4348
4350
  text: formattedErrors,
4349
4351
  html: function(errors, root) {
@@ -4501,7 +4503,7 @@ class SocketServer {
4501
4503
  `;
4502
4504
  }(formattedErrors, root)
4503
4505
  }
4504
- });
4506
+ }, token);
4505
4507
  }
4506
4508
  if (statsJson.warningsCount) {
4507
4509
  let { warnings: formattedWarnings } = formatStatsMessages({
@@ -4510,29 +4512,30 @@ class SocketServer {
4510
4512
  });
4511
4513
  return this.sockWrite({
4512
4514
  type: 'warnings',
4513
- compilationId,
4514
4515
  data: {
4515
4516
  text: formattedWarnings
4516
4517
  }
4517
- });
4518
+ }, token);
4518
4519
  }
4519
4520
  return this.sockWrite({
4520
- type: 'ok',
4521
- compilationId
4522
- });
4521
+ type: 'ok'
4522
+ }, token);
4523
4523
  }
4524
4524
  send(connection, message) {
4525
4525
  1 === connection.readyState && connection.send(message);
4526
4526
  }
4527
- constructor(options){
4528
- socketServer_define_property(this, "wsServer", void 0), socketServer_define_property(this, "sockets", []), socketServer_define_property(this, "options", void 0), socketServer_define_property(this, "stats", void 0), socketServer_define_property(this, "initialChunks", void 0), socketServer_define_property(this, "heartbeatTimer", null), socketServer_define_property(this, "upgrade", (req, sock, head)=>{
4529
- this.wsServer.shouldHandle(req) && this.wsServer.handleUpgrade(req, sock, head, (connection)=>{
4527
+ constructor(options, environments){
4528
+ socketServer_define_property(this, "wsServer", void 0), socketServer_define_property(this, "sockets", new Map()), socketServer_define_property(this, "options", void 0), socketServer_define_property(this, "stats", void 0), socketServer_define_property(this, "initialChunks", void 0), socketServer_define_property(this, "heartbeatTimer", null), socketServer_define_property(this, "environments", void 0), socketServer_define_property(this, "upgrade", (req, socket, head)=>{
4529
+ if (!this.wsServer.shouldHandle(req)) return;
4530
+ let query = parseQueryString(req);
4531
+ if (!Object.values(this.environments).map((env)=>env.webSocketToken).includes(query.token)) return void socket.destroy();
4532
+ this.wsServer.handleUpgrade(req, socket, head, (connection)=>{
4530
4533
  this.wsServer.emit('connection', connection, req);
4531
4534
  });
4532
4535
  }), socketServer_define_property(this, "checkSockets", ()=>{
4533
4536
  for (let socket of this.wsServer.clients)socket.isAlive ? (socket.isAlive = !1, socket.ping(()=>{})) : socket.terminate();
4534
4537
  null !== this.heartbeatTimer && (this.heartbeatTimer = setTimeout(this.checkSockets, 30000).unref());
4535
- }), this.options = options, this.stats = {}, this.initialChunks = {};
4538
+ }), this.options = options, this.stats = {}, this.initialChunks = {}, this.environments = environments;
4536
4539
  }
4537
4540
  }
4538
4541
  function compilationManager_define_property(obj, key, value) {
@@ -4565,29 +4568,28 @@ class CompilationManager {
4565
4568
  });
4566
4569
  }
4567
4570
  async setupCompilationMiddleware() {
4568
- let { devConfig, serverConfig, publicPaths } = this, clientPaths = function(devConfig) {
4571
+ let { devConfig, serverConfig, publicPaths, environments } = this, clientPaths = function(devConfig) {
4569
4572
  var _devConfig_client;
4570
4573
  let clientPaths = [];
4571
4574
  return (devConfig.hmr || devConfig.liveReload) && (clientPaths.push(compilationManager_require.resolve('@rsbuild/core/client/hmr')), (null == (_devConfig_client = devConfig.client) ? void 0 : _devConfig_client.overlay) && clientPaths.push(`${compilationManager_require.resolve('@rsbuild/core/client/overlay')}`)), clientPaths;
4572
4575
  }(devConfig), middleware = await getCompilationMiddleware(this.compiler, {
4573
4576
  callbacks: {
4574
- onInvalid: (compilationId, fileName)=>{
4577
+ onInvalid: (token, fileName)=>{
4575
4578
  if ('string' == typeof fileName && HTML_REGEX.test(fileName)) return void this.socketServer.sockWrite({
4576
- type: 'static-changed',
4577
- compilationId
4578
- });
4579
+ type: 'static-changed'
4580
+ }, token);
4579
4581
  this.socketServer.sockWrite({
4580
- type: 'invalid',
4581
- compilationId
4582
- });
4582
+ type: 'invalid'
4583
+ }, token);
4583
4584
  },
4584
- onDone: (stats)=>{
4585
- this.socketServer.updateStats(stats);
4585
+ onDone: (token, stats)=>{
4586
+ this.socketServer.updateStats(stats, token);
4586
4587
  }
4587
4588
  },
4588
4589
  clientPaths,
4589
4590
  devConfig,
4590
- serverConfig
4591
+ serverConfig,
4592
+ environments
4591
4593
  }), { base } = serverConfig, assetPrefixes = publicPaths.map(getPathnameFromUrl).map((prefix)=>base && '/' !== base ? stripBase(prefix, base) : prefix), wrapper = async (req, res, next)=>{
4592
4594
  let { url } = req, assetPrefix = url && assetPrefixes.find((prefix)=>url.startsWith(prefix));
4593
4595
  assetPrefix && '/' !== assetPrefix ? (req.url = url.slice(assetPrefix.length - 1), middleware(req, res, (...args)=>{
@@ -4597,7 +4599,7 @@ class CompilationManager {
4597
4599
  wrapper.close = middleware.close, wrapper.watch = middleware.watch, this.middleware = wrapper;
4598
4600
  }
4599
4601
  constructor({ dev, server, compiler, publicPaths, environments }){
4600
- compilationManager_define_property(this, "middleware", void 0), compilationManager_define_property(this, "outputFileSystem", void 0), compilationManager_define_property(this, "devConfig", void 0), compilationManager_define_property(this, "serverConfig", void 0), compilationManager_define_property(this, "compiler", void 0), compilationManager_define_property(this, "publicPaths", void 0), compilationManager_define_property(this, "socketServer", void 0), compilationManager_define_property(this, "readFileSync", (fileName)=>'readFileSync' in this.outputFileSystem ? this.outputFileSystem.readFileSync(fileName, 'utf-8') : node_fs.readFileSync(fileName, 'utf-8')), this.devConfig = ((config, environments)=>{
4602
+ compilationManager_define_property(this, "middleware", void 0), compilationManager_define_property(this, "outputFileSystem", void 0), compilationManager_define_property(this, "devConfig", void 0), compilationManager_define_property(this, "serverConfig", void 0), compilationManager_define_property(this, "compiler", void 0), compilationManager_define_property(this, "environments", void 0), compilationManager_define_property(this, "publicPaths", void 0), compilationManager_define_property(this, "socketServer", void 0), compilationManager_define_property(this, "readFileSync", (fileName)=>'readFileSync' in this.outputFileSystem ? this.outputFileSystem.readFileSync(fileName, 'utf-8') : node_fs.readFileSync(fileName, 'utf-8')), this.devConfig = ((config, environments)=>{
4601
4603
  let writeToDiskValues = Object.values(environments).map((env)=>env.config.dev.writeToDisk);
4602
4604
  return 1 === new Set(writeToDiskValues).size ? {
4603
4605
  ...config,
@@ -4609,11 +4611,13 @@ class CompilationManager {
4609
4611
  return compilationName && environments[compilationName] && (writeToDisk = environments[compilationName].config.dev.writeToDisk ?? writeToDisk), 'function' == typeof writeToDisk ? writeToDisk(filePath) : writeToDisk;
4610
4612
  }
4611
4613
  };
4612
- })(dev, environments), this.serverConfig = server, this.compiler = compiler, this.publicPaths = publicPaths, this.outputFileSystem = node_fs, this.socketServer = new SocketServer(dev);
4614
+ })(dev, environments), this.serverConfig = server, this.compiler = compiler, this.environments = environments, this.publicPaths = publicPaths, this.outputFileSystem = node_fs, this.socketServer = new SocketServer(dev, environments);
4613
4615
  }
4614
4616
  }
4615
- let ENCODING_REGEX = /\bgzip\b/, CONTENT_TYPE_REGEX = /text|javascript|\/json|xml/i, gzipMiddleware = ({ level = node_zlib.constants.Z_BEST_SPEED } = {})=>(req, res, next)=>{
4616
- let gzip, writeHeadStatus, accept = req.headers['accept-encoding'], encoding = 'string' == typeof accept && ENCODING_REGEX.test(accept);
4617
+ let ENCODING_REGEX = /\bgzip\b/, CONTENT_TYPE_REGEX = /text|javascript|\/json|xml/i, gzipMiddleware = ({ filter, level = node_zlib.constants.Z_BEST_SPEED } = {})=>(req, res, next)=>{
4618
+ let gzip, writeHeadStatus;
4619
+ if (filter && !1 === filter(req, res)) return void next();
4620
+ let accept = req.headers['accept-encoding'], encoding = 'string' == typeof accept && ENCODING_REGEX.test(accept);
4617
4621
  if ('HEAD' === req.method || !encoding) return void next();
4618
4622
  let started = !1, { end, write, on, writeHead } = res, listeners = [], start = ()=>{
4619
4623
  if (!started) {
@@ -4737,7 +4741,8 @@ let ENCODING_REGEX = /\bgzip\b/, CONTENT_TYPE_REGEX = /text|javascript|\/json|xm
4737
4741
  let { middlewares: proxyMiddlewares, upgrade } = await createProxyMiddleware(server.proxy);
4738
4742
  for (let middleware of (upgradeEvents.push(upgrade), proxyMiddlewares))middlewares.push(middleware);
4739
4743
  }
4740
- if (server.compress && middlewares.push(gzipMiddleware()), 'dev' === context.action && 'rspack' === context.bundlerType && compilationManager) {
4744
+ let { compress } = server;
4745
+ if (compress && middlewares.push(gzipMiddleware('object' == typeof compress ? compress : void 0)), 'dev' === context.action && 'rspack' === context.bundlerType && compilationManager) {
4741
4746
  var _compiler_options_experiments;
4742
4747
  let { compiler } = compilationManager;
4743
4748
  (helpers_isMultiCompiler(compiler) ? compiler.compilers.some((childCompiler)=>{
@@ -5155,7 +5160,7 @@ let runner_run = async ({ bundlePath, ...runnerFactoryOptions })=>new BasicRunne
5155
5160
  }, shutdownRefCount = 0, setupGracefulShutdown = ()=>{
5156
5161
  shutdownRefCount++;
5157
5162
  let onSigterm = ()=>{
5158
- handleTermination(constants.signals.SIGTERM + 128);
5163
+ handleTermination(external_node_os_constants.signals.SIGTERM + 128);
5159
5164
  };
5160
5165
  process.once('SIGTERM', onSigterm);
5161
5166
  let isCI = 'true' === process.env.CI, onStdinEnd = ()=>{
@@ -5488,9 +5493,14 @@ class RsbuildProdServer {
5488
5493
  for (let middleware of middlewares)this.middlewares.use(middleware);
5489
5494
  this.app.on('upgrade', upgrade);
5490
5495
  }
5491
- if (compress && this.middlewares.use(gzipMiddleware({
5492
- level: 6
5493
- })), base && '/' !== base && this.middlewares.use(getBaseMiddleware({
5496
+ if (compress) {
5497
+ let { constants } = await import("node:zlib");
5498
+ this.middlewares.use(gzipMiddleware({
5499
+ level: constants.Z_DEFAULT_COMPRESSION,
5500
+ ...'object' == typeof compress ? compress : void 0
5501
+ }));
5502
+ }
5503
+ if (base && '/' !== base && this.middlewares.use(getBaseMiddleware({
5494
5504
  base
5495
5505
  })), await this.applyStaticAssetMiddleware(), historyApiFallback) {
5496
5506
  let { default: connectHistoryApiFallback } = await import("../compiled/connect-history-api-fallback/index.js"), historyApiFallbackMiddleware = connectHistoryApiFallback(!0 === historyApiFallback ? {} : historyApiFallback);
@@ -6276,8 +6286,11 @@ throw new Error('Failed to load Node.js addon: "${name}"\\n' + error);
6276
6286
  rule.test(CSS_REGEX).type("javascript/auto").dependency({
6277
6287
  not: 'url'
6278
6288
  }).resourceQuery({
6279
- not: /raw|inline/
6280
- }), inlineRule.test(CSS_REGEX).type("javascript/auto").resourceQuery(/inline/), chain.module.rule(CHAIN_ID.RULE.CSS_RAW).test(CSS_REGEX).type('asset/source').resourceQuery(/raw/);
6289
+ not: [
6290
+ RAW_QUERY_REGEX,
6291
+ INLINE_QUERY_REGEX
6292
+ ]
6293
+ }), inlineRule.test(CSS_REGEX).type("javascript/auto").resourceQuery(INLINE_QUERY_REGEX), chain.module.rule(CHAIN_ID.RULE.CSS_RAW).test(CSS_REGEX).type('asset/source').resourceQuery(RAW_QUERY_REGEX);
6281
6294
  let emitCss = config.output.emitCss ?? 'web' === target;
6282
6295
  if (emitCss) if (config.output.injectStyles) {
6283
6296
  let styleLoaderOptions = reduceConfigs({
@@ -6459,9 +6472,9 @@ throw new Error('Failed to load Node.js addon: "${name}"\\n' + error);
6459
6472
  let { config, browserslist } = environment, cacheRoot = node_path.join(api.context.cachePath, '.swc'), rule = chain.module.rule(CHAIN_ID.RULE.JS).test(SCRIPT_REGEX).type("javascript/auto").dependency({
6460
6473
  not: 'url'
6461
6474
  }).resourceQuery({
6462
- not: /raw/
6475
+ not: RAW_QUERY_REGEX
6463
6476
  });
6464
- chain.module.rule(CHAIN_ID.RULE.JS_RAW).test(SCRIPT_REGEX).type('asset/source').resourceQuery(/raw/);
6477
+ chain.module.rule(CHAIN_ID.RULE.JS_RAW).test(SCRIPT_REGEX).type('asset/source').resourceQuery(RAW_QUERY_REGEX);
6465
6478
  let dataUriRule = chain.module.rule(CHAIN_ID.RULE.JS_DATA_URI).mimetype({
6466
6479
  or: [
6467
6480
  "text/javascript",
@@ -7254,9 +7267,9 @@ async function createRsbuild(options = {}) {
7254
7267
  exitCode
7255
7268
  });
7256
7269
  }, exitHook_callbacks.add(onExit), isRegistered || (isRegistered = !0, node_process.on('SIGINT', ()=>{
7257
- exit(constants.signals.SIGINT + 128, 'SIGINT');
7270
+ exit(external_node_os_constants.signals.SIGINT + 128, 'SIGINT');
7258
7271
  }), node_process.once('SIGTERM', ()=>{
7259
- exit(constants.signals.SIGTERM + 128, 'SIGTERM');
7272
+ exit(external_node_os_constants.signals.SIGTERM + 128, 'SIGTERM');
7260
7273
  }), node_process.once('exit', (exitCode)=>{
7261
7274
  exit(exitCode, 'exit');
7262
7275
  })), onExitListened = !0;
@@ -7634,11 +7647,11 @@ async function runCLI() {
7634
7647
  ].includes(level) && (logger.level = level);
7635
7648
  }
7636
7649
  let { npm_execpath } = process.env;
7637
- (!npm_execpath || npm_execpath.includes('npx-cli.js') || npm_execpath.includes('.bun')) && logger.log(), logger.greet(` Rsbuild v1.4.3\n`);
7650
+ (!npm_execpath || npm_execpath.includes('npx-cli.js') || npm_execpath.includes('.bun')) && logger.log(), logger.greet(` Rsbuild v1.4.5\n`);
7638
7651
  try {
7639
7652
  !function() {
7640
7653
  let cli = ((name = "")=>new CAC(name))('rsbuild');
7641
- cli.help(), cli.version("1.4.3"), cli.option('--base <base>', 'specify the base path of the server').option('-c, --config <config>', 'specify the configuration file, can be a relative or absolute path').option('--config-loader <loader>', 'specify the loader to load the config file, can be `jiti` or `native`', {
7654
+ cli.help(), cli.version("1.4.5"), cli.option('--base <base>', 'specify the base path of the server').option('-c, --config <config>', 'specify the configuration file, can be a relative or absolute path').option('--config-loader <loader>', 'specify the loader to load the config file, can be `jiti` or `native`', {
7642
7655
  default: 'jiti'
7643
7656
  }).option('-r, --root <root>', 'specify the project root directory, can be an absolute path or a path relative to cwd').option('-m, --mode <mode>', 'specify the build mode, can be `development`, `production` or `none`').option('--log-level <level>', 'specify the log level, can be `info`, `warn`, `error` or `silent`').option('--env-mode <mode>', 'specify the env mode to load the `.env.[mode]` file').option('--environment <name>', 'specify the name of environment to build', {
7644
7657
  type: [
@@ -7696,5 +7709,5 @@ async function runCLI() {
7696
7709
  logger.error('Failed to start Rsbuild CLI.'), logger.error(err);
7697
7710
  }
7698
7711
  }
7699
- let src_version = "1.4.3";
7712
+ let src_version = "1.4.5";
7700
7713
  export { PLUGIN_CSS_NAME, PLUGIN_SWC_NAME, createRsbuild, defaultAllowedOrigins, defineConfig, ensureAssetPrefix, loadConfig_loadConfig as loadConfig, loadEnv, logger, mergeRsbuildConfig, rspack, runCLI, src_version as version };
@@ -28,6 +28,8 @@ export declare const HTML_REGEX: RegExp;
28
28
  export declare const JS_REGEX: RegExp;
29
29
  export declare const SCRIPT_REGEX: RegExp;
30
30
  export declare const CSS_REGEX: RegExp;
31
+ export declare const RAW_QUERY_REGEX: RegExp;
32
+ export declare const INLINE_QUERY_REGEX: RegExp;
31
33
  export declare const NODE_MODULES_REGEX: RegExp;
32
34
  export declare const PLUGIN_SWC_NAME = "rsbuild:swc";
33
35
  export declare const PLUGIN_CSS_NAME = "rsbuild:css";
@@ -19,4 +19,4 @@ export { type Logger, logger } from './logger';
19
19
  export { mergeRsbuildConfig } from './mergeConfig';
20
20
  export type { RsbuildDevServer } from './server/devServer';
21
21
  export type { StartServerResult } from './server/helper';
22
- export type { AliasStrategy, AppIcon, AppIconItem, Build, BuildOptions, BundlerPluginInstance, Charset, CleanDistPath, CleanDistPathObject, ClientConfig, CliShortcut, ConfigChain, ConfigChainWithContext, ConsoleType, CreateCompiler, CreateRsbuildOptions, CrossOrigin, CSSLoaderOptions, CSSModules, CSSModulesLocalsConvention, DataUriLimit, Decorators, DevConfig, DistPathConfig, EnvironmentConfig, EnvironmentContext, FilenameConfig, HistoryApiFallbackContext, HistoryApiFallbackOptions, HtmlBasicTag, HtmlConfig, HtmlFallback, HtmlRspackPlugin, HtmlTagContext, HtmlTagDescriptor, HtmlTagHandler, InlineChunkConfig, InlineChunkTest, InlineChunkTestFunction, InspectConfigOptions, InspectConfigResult, InternalContext, LegalComments, LogLevel, ManifestConfig, ManifestData, ManifestObjectConfig, MergedEnvironmentConfig, MetaAttrs, MetaOptions, Minify, ModifyBundlerChainFn, ModifyBundlerChainUtils, ModifyChainUtils, ModifyEnvironmentConfigFn, ModifyEnvironmentConfigUtils, ModifyHTMLContext, ModifyHTMLFn, ModifyHTMLTagsContext, ModifyHTMLTagsFn, ModifyRsbuildConfigFn, ModifyRsbuildConfigUtils, ModifyRspackConfigFn, ModifyRspackConfigUtils, ModifyWebpackChainFn, ModifyWebpackChainUtils, ModifyWebpackConfigFn, ModifyWebpackConfigUtils, ModuleFederationConfig, NormalizedConfig, NormalizedDevConfig, NormalizedEnvironmentConfig, NormalizedHtmlConfig, NormalizedModuleFederationConfig, NormalizedOutputConfig, NormalizedPerformanceConfig, NormalizedSecurityConfig, NormalizedServerConfig, NormalizedSourceConfig, NormalizedToolsConfig, OnAfterBuildFn, OnAfterCreateCompilerFn, OnAfterEnvironmentCompileFn, OnAfterStartDevServerFn, OnAfterStartProdServerFn, OnBeforeBuildFn, OnBeforeCreateCompilerFn, OnBeforeEnvironmentCompileFn, OnBeforeStartDevServerFn, OnBeforeStartProdServerFn, OnCloseBuildFn, OnCloseDevServerFn, OnDevCompileDoneFn, OnExitFn, OutputConfig, OutputStructure, PerformanceConfig, PluginManager, Polyfill, PostCSSLoaderOptions, PostCSSOptions, PostCSSPlugin, PreconnectOption, PreviewOptions, PrintUrls, ProcessAssetsDescriptor, ProcessAssetsHandler, ProcessAssetsHook, ProgressBarConfig, ProxyBypass, ProxyConfig, ProxyFilter, ProxyOptions, PublicDir, PublicDirOptions, RequestHandler, ResolvedCreateRsbuildOptions, ResolveHandler, ResolveHook, ResourceHintsIncludeType, RsbuildConfig, RsbuildContext, RsbuildEntry, RsbuildEntryDescription, RsbuildInstance, RsbuildMode, RsbuildPlugin, RsbuildPluginAPI, RsbuildPlugins, RsbuildProvider, RsbuildProviderHelpers, RsbuildTarget, RspackChain, RspackRule, ScriptInject, ScriptLoading, SecurityConfig, ServerConfig, SetupMiddlewaresContext, SetupMiddlewaresFn, SourceConfig, SourceMap, SplitChunks, SriAlgorithm, SriOptions, StartDevServerOptions, StyleLoaderOptions, ToolsConfig, TransformContext, TransformDescriptor, TransformHandler, TransformHook, TransformImport, WatchFiles, } from './types';
22
+ export type { AliasStrategy, AppIcon, AppIconItem, Build, BuildOptions, BundlerPluginInstance, Charset, CleanDistPath, CleanDistPathObject, ClientConfig, CliShortcut, CompressOptions, ConfigChain, ConfigChainWithContext, ConsoleType, CreateCompiler, CreateRsbuildOptions, CrossOrigin, CSSLoaderOptions, CSSModules, CSSModulesLocalsConvention, DataUriLimit, Decorators, DevConfig, DistPathConfig, EnvironmentConfig, EnvironmentContext, FilenameConfig, HistoryApiFallbackContext, HistoryApiFallbackOptions, HtmlBasicTag, HtmlConfig, HtmlFallback, HtmlRspackPlugin, HtmlTagContext, HtmlTagDescriptor, HtmlTagHandler, InlineChunkConfig, InlineChunkTest, InlineChunkTestFunction, InspectConfigOptions, InspectConfigResult, InternalContext, LegalComments, LogLevel, ManifestConfig, ManifestData, ManifestObjectConfig, MergedEnvironmentConfig, MetaAttrs, MetaOptions, Minify, ModifyBundlerChainFn, ModifyBundlerChainUtils, ModifyChainUtils, ModifyEnvironmentConfigFn, ModifyEnvironmentConfigUtils, ModifyHTMLContext, ModifyHTMLFn, ModifyHTMLTagsContext, ModifyHTMLTagsFn, ModifyRsbuildConfigFn, ModifyRsbuildConfigUtils, ModifyRspackConfigFn, ModifyRspackConfigUtils, ModifyWebpackChainFn, ModifyWebpackChainUtils, ModifyWebpackConfigFn, ModifyWebpackConfigUtils, ModuleFederationConfig, NormalizedConfig, NormalizedDevConfig, NormalizedEnvironmentConfig, NormalizedHtmlConfig, NormalizedModuleFederationConfig, NormalizedOutputConfig, NormalizedPerformanceConfig, NormalizedSecurityConfig, NormalizedServerConfig, NormalizedSourceConfig, NormalizedToolsConfig, OnAfterBuildFn, OnAfterCreateCompilerFn, OnAfterEnvironmentCompileFn, OnAfterStartDevServerFn, OnAfterStartProdServerFn, OnBeforeBuildFn, OnBeforeCreateCompilerFn, OnBeforeEnvironmentCompileFn, OnBeforeStartDevServerFn, OnBeforeStartProdServerFn, OnCloseBuildFn, OnCloseDevServerFn, OnDevCompileDoneFn, OnExitFn, OutputConfig, OutputStructure, PerformanceConfig, PluginManager, Polyfill, PostCSSLoaderOptions, PostCSSOptions, PostCSSPlugin, PreconnectOption, PreviewOptions, PrintUrls, ProcessAssetsDescriptor, ProcessAssetsHandler, ProcessAssetsHook, ProgressBarConfig, ProxyBypass, ProxyConfig, ProxyFilter, ProxyOptions, PublicDir, PublicDirOptions, RequestHandler, ResolvedCreateRsbuildOptions, ResolveHandler, ResolveHook, ResourceHintsIncludeType, RsbuildConfig, RsbuildContext, RsbuildEntry, RsbuildEntryDescription, RsbuildInstance, RsbuildMode, RsbuildPlugin, RsbuildPluginAPI, RsbuildPlugins, RsbuildProvider, RsbuildProviderHelpers, RsbuildTarget, RspackChain, RspackRule, ScriptInject, ScriptLoading, SecurityConfig, ServerConfig, SetupMiddlewaresContext, SetupMiddlewaresFn, SourceConfig, SourceMap, SplitChunks, SriAlgorithm, SriOptions, StartDevServerOptions, StyleLoaderOptions, ToolsConfig, TransformContext, TransformDescriptor, TransformHandler, TransformHook, TransformImport, WatchFiles, } from './types';
@@ -1,11 +1,11 @@
1
- import type { EnvironmentContext, DevConfig as OriginDevConfig, Rspack, ServerConfig } from '../types';
1
+ import type { EnvironmentContext, NormalizedDevConfig, NormalizedServerConfig, Rspack } from '../types';
2
2
  import { type CompilationMiddleware } from './compilationMiddleware';
3
3
  import { SocketServer } from './socketServer';
4
4
  type Options = {
5
5
  publicPaths: string[];
6
6
  environments: Record<string, EnvironmentContext>;
7
- dev: OriginDevConfig;
8
- server: ServerConfig;
7
+ dev: NormalizedDevConfig;
8
+ server: NormalizedServerConfig;
9
9
  compiler: Rspack.Compiler | Rspack.MultiCompiler;
10
10
  };
11
11
  /**
@@ -19,6 +19,7 @@ export declare class CompilationManager {
19
19
  private devConfig;
20
20
  private serverConfig;
21
21
  compiler: Rspack.Compiler | Rspack.MultiCompiler;
22
+ private environments;
22
23
  private publicPaths;
23
24
  socketServer: SocketServer;
24
25
  constructor({ dev, server, compiler, publicPaths, environments }: Options);
@@ -1,16 +1,20 @@
1
1
  import type { IncomingMessage, ServerResponse } from 'node:http';
2
- import type { Compiler, MultiCompiler } from '@rspack/core';
3
- import type { DevConfig, NextFunction, ServerConfig } from '../types';
2
+ import type { Compiler, MultiCompiler, Stats } from '@rspack/core';
3
+ import type { DevConfig, EnvironmentContext, NextFunction, ServerConfig } from '../types';
4
4
  export declare const isClientCompiler: (compiler: {
5
5
  options: {
6
6
  target?: Compiler["options"]["target"];
7
7
  };
8
8
  }) => boolean;
9
- type ServerCallbacks = {
10
- onInvalid: (compilationId?: string, fileName?: string | null) => void;
11
- onDone: (stats: any) => void;
9
+ export type ServerCallbacks = {
10
+ onInvalid: (token: string, fileName?: string | null) => void;
11
+ onDone: (token: string, stats: Stats) => void;
12
12
  };
13
- export declare const setupServerHooks: (compiler: Compiler, { onDone, onInvalid }: ServerCallbacks) => void;
13
+ export declare const setupServerHooks: ({ compiler, token, callbacks: { onDone, onInvalid }, }: {
14
+ compiler: Compiler;
15
+ token: string;
16
+ callbacks: ServerCallbacks;
17
+ }) => void;
14
18
  type Middleware = (req: IncomingMessage, res: ServerResponse, next: NextFunction) => Promise<void>;
15
19
  export type CompilationMiddlewareOptions = {
16
20
  /**
@@ -23,6 +27,7 @@ export type CompilationMiddlewareOptions = {
23
27
  callbacks: ServerCallbacks;
24
28
  devConfig: DevConfig;
25
29
  serverConfig: ServerConfig;
30
+ environments: Record<string, EnvironmentContext>;
26
31
  };
27
32
  export type CompilationMiddleware = Middleware & {
28
33
  close: (callback: (err: Error | null | undefined) => void) => any;
@@ -1,4 +1,2 @@
1
- import type { RequestHandler } from '../types';
2
- export declare const gzipMiddleware: ({ level, }?: {
3
- level?: number;
4
- }) => RequestHandler;
1
+ import type { CompressOptions, RequestHandler } from '../types';
2
+ export declare const gzipMiddleware: ({ filter, level, }?: CompressOptions) => RequestHandler;
@@ -1,7 +1,7 @@
1
1
  import type { IncomingMessage, Server } from 'node:http';
2
2
  import type { Http2SecureServer } from 'node:http2';
3
3
  import type { Socket } from 'node:net';
4
- import type { InternalContext, NormalizedConfig, OutputStructure, PrintUrls, Routes, RsbuildEntry, Rspack } from '../types';
4
+ import type { InternalContext, NormalizedConfig, OutputStructure, PrintUrls, Routes, RsbuildEntry } from '../types';
5
5
  /**
6
6
  * It used to subscribe http upgrade event
7
7
  */
@@ -71,7 +71,6 @@ export declare const getAddressUrls: ({ protocol, port, host, }: {
71
71
  port: number;
72
72
  host?: string;
73
73
  }) => Promise<AddressUrl[]>;
74
- export declare const getCompilationId: (compiler: Rspack.Compiler | Rspack.Compilation) => string;
75
74
  export declare function getServerTerminator(server: Server | Http2SecureServer): () => Promise<void>;
76
75
  /**
77
76
  * Escape HTML characters