@meteorjs/rspack 1.1.0-beta.30 → 1.1.0-beta.32

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.
@@ -0,0 +1,10 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(chmod +x:*)",
5
+ "Bash(bash:*)",
6
+ "Bash(npm install:*)",
7
+ "Bash(node:*)"
8
+ ]
9
+ }
10
+ }
package/README.md ADDED
@@ -0,0 +1,142 @@
1
+ # @meteorjs/rspack
2
+
3
+ The default [Rspack](https://rspack.dev) configuration for Meteor applications. This package provides everything you need to bundle your Meteor app with Rspack out of the box: client and server builds, SWC transpilation, React/Blaze/Angular support, hot module replacement, asset management, and all the Meteor-specific wiring so you don't have to.
4
+
5
+ When Meteor runs with the Rspack bundler enabled, this package is what generates the underlying Rspack configuration. It detects your project setup (TypeScript, React, Blaze, Angular), sets up the right loaders and plugins, defines `Meteor.isClient`/`Meteor.isServer` and friends, configures caching, and exposes a set of helpers you can use in your own `rspack.config.js` to customize the build without breaking Meteor integration.
6
+
7
+ ## What it provides
8
+
9
+ - **Dual client/server builds** with the correct targets, externals, and output paths
10
+ - **SWC-based transpilation** for JS/TS/JSX/TSX with automatic framework detection
11
+ - **React Fast Refresh** in development when React is enabled
12
+ - **Blaze template handling** via ignore-loader when Blaze is enabled
13
+ - **Persistent filesystem caching** for fast rebuilds
14
+ - **Asset externals and HTML generation** through custom Rspack plugins
15
+ - **A `defineConfig` helper** that accepts a factory function receiving Meteor environment flags and build utilities
16
+ - **Customizable config** via `rspack.config.js` in your project root, with safe merging that warns if you try to override reserved settings
17
+ - **Automatic CSS delegation** when rspack is configured with CSS, Less, or SCSS loaders, Meteor automatically detects the handled extensions after the first compilation and stops processing those files itself in the entry folder context. No `.meteorignore` entries needed.
18
+
19
+ ## Installation
20
+
21
+ [Rspack integration](https://docs.meteor.com/about/modern-build-stack/rspack-bundler-integration.html) is automatically managed by the rspack Atmosphere package.
22
+
23
+ ```bash
24
+ meteor add rspack
25
+ ```
26
+
27
+ By doing this, your Meteor app will automatically serve `@meteorjs/rspack` and the required `@rspack/cli`, `@rspack/core`, among others.
28
+
29
+ ## Usage
30
+
31
+ In your project's `rspack.config.js`, use the `defineConfig` helper to customize the build. The factory function receives a `env` object with Meteor environment flags and helper utilities:
32
+
33
+ ```js
34
+ const { defineConfig } = require('@meteorjs/rspack');
35
+
36
+ module.exports = defineConfig((env, argv) => {
37
+ // env.isClient, env.isServer, env.isDevelopment, env.isProduction
38
+ // env.isReactEnabled, env.isBlazeEnabled, etc.
39
+
40
+ return {
41
+ // Your custom Rspack configuration here.
42
+ // It gets safely merged with the Meteor defaults.
43
+ };
44
+ });
45
+ ```
46
+
47
+ More information is available in the official docs: [Rspack Bundler Integration](https://docs.meteor.com/about/modern-build-stack/rspack-bundler-integration.html#custom-rspack-config-js).
48
+
49
+ ## Development
50
+
51
+ ### Install dependencies
52
+
53
+ ```bash
54
+ npm install
55
+ ```
56
+
57
+ ### Version bumping
58
+
59
+ Use `npm run bump` to update the version in `package.json` before publishing.
60
+
61
+ ```bash
62
+ npm run bump -- <major|minor|patch> [--beta]
63
+ ```
64
+
65
+ **Standard bumps** increment the version and remove any prerelease suffix:
66
+
67
+ ```bash
68
+ npm run bump -- patch # 1.0.1 -> 1.0.2
69
+ npm run bump -- minor # 1.0.1 -> 1.1.0
70
+ npm run bump -- major # 1.0.1 -> 2.0.0
71
+ ```
72
+
73
+ **Beta bumps** append or increment a `-beta.N` prerelease suffix:
74
+
75
+ ```bash
76
+ npm run bump -- patch --beta # 1.0.1 -> 1.0.2-beta.0
77
+ npm run bump -- patch --beta # 1.0.2-beta.0 -> 1.0.2-beta.1
78
+ npm run bump -- patch --beta # 1.0.2-beta.1 -> 1.0.2-beta.2
79
+ ```
80
+
81
+ If you change the bump level while on a beta, the base version updates and the beta counter resets:
82
+
83
+ ```bash
84
+ npm run bump -- minor --beta # 1.0.2-beta.2 -> 1.1.0-beta.0
85
+ npm run bump -- major --beta # 1.1.0-beta.0 -> 2.0.0-beta.0
86
+ ```
87
+
88
+ ### Publishing a beta release
89
+
90
+ After bumping to a beta version, publish to the `beta` dist-tag:
91
+
92
+ ```bash
93
+ npm run bump -- patch --beta
94
+ npm run publish:beta
95
+ ```
96
+
97
+ Users can then install the beta with:
98
+
99
+ ```bash
100
+ npm install @meteorjs/rspack@beta
101
+ ```
102
+
103
+ You can pass extra flags to `npm publish` through the script:
104
+
105
+ ```bash
106
+ npm run publish:beta -- --dry-run
107
+ ```
108
+
109
+ ### Publishing an official release
110
+
111
+ After bumping to a stable version, publish with the default `latest` tag:
112
+
113
+ ```bash
114
+ npm run bump -- patch
115
+ npm publish
116
+ ```
117
+
118
+ ### Typical workflows
119
+
120
+ **Beta iteration**: ship multiple beta builds for the same upcoming patch:
121
+
122
+ ```bash
123
+ npm run bump -- patch --beta # 1.0.1 -> 1.0.2-beta.0
124
+ npm run publish:beta
125
+ # ... fix issues ...
126
+ npm run bump -- patch --beta # 1.0.2-beta.0 -> 1.0.2-beta.1
127
+ npm run publish:beta
128
+ ```
129
+
130
+ **Promote beta to stable**: once the beta is ready, bump to the stable version and publish:
131
+
132
+ ```bash
133
+ npm run bump -- patch # 1.0.2-beta.1 -> 1.0.3
134
+ npm publish
135
+ ```
136
+
137
+ **Direct stable release**: skip the beta phase entirely:
138
+
139
+ ```bash
140
+ npm run bump -- minor # 1.0.1 -> 1.1.0
141
+ npm publish
142
+ ```
package/index.d.ts CHANGED
@@ -57,10 +57,21 @@ type MeteorEnv = Record<string, any> & {
57
57
  */
58
58
  splitVendorChunk: () => Record<string, object>;
59
59
  /**
60
- * Extend Rspack SWC loader config.
60
+ * Extend the SWC loader config by smart-merging custom options on top of
61
+ * Meteor's defaults. Only the properties you specify are overridden;
62
+ * everything else is preserved.
63
+ * @param swcConfig - SWC loader options to merge with defaults
61
64
  * @returns A config object with SWC loader config
62
65
  */
63
66
  extendSwcConfig: (swcConfig: SwcLoaderOptions) => Record<string, object>;
67
+ /**
68
+ * Replace the SWC loader config entirely, discarding Meteor's defaults.
69
+ * Use this when you need full control over SWC options and don't want any
70
+ * automatic merging with Meteor's built-in configuration.
71
+ * @param swcConfig - Complete SWC loader options (replaces defaults)
72
+ * @returns A config object with SWC loader config
73
+ */
74
+ replaceSwcConfig: (swcConfig: SwcLoaderOptions) => Record<string, object>;
64
75
  /**
65
76
  * Extend Rspack configs.
66
77
  * @returns A config object with merged configs
@@ -75,6 +86,12 @@ type MeteorEnv = Record<string, any> & {
75
86
  disablePlugins: (
76
87
  matchers: string | RegExp | ((plugin: any, index: number) => boolean) | Array<string | RegExp | ((plugin: any, index: number) => boolean)>
77
88
  ) => Record<string, any>;
89
+ /**
90
+ * Omit `Meteor.isDevelopment` and `Meteor.isProduction` from the DefinePlugin so
91
+ * the bundle is not tied to a specific Meteor environment (portable / isomorphic builds).
92
+ * @returns A config fragment with `meteor.enablePortableBuild: true`
93
+ */
94
+ enablePortableBuild: () => Record<string, any>;
78
95
  }
79
96
 
80
97
  export type ConfigFactory = (
@@ -0,0 +1,184 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ /**
5
+ * Extract local file dependencies from a config file by parsing require/import statements using AST
6
+ * @param {string} configFilePath - Path to the config file to parse
7
+ * @returns {string[]} - Array of absolute paths to local dependencies
8
+ */
9
+ function extractLocalDependencies(configFilePath) {
10
+ if (!configFilePath || !fs.existsSync(configFilePath)) {
11
+ return [];
12
+ }
13
+
14
+ try {
15
+ const swc = require('@swc/core');
16
+ const content = fs.readFileSync(configFilePath, 'utf-8');
17
+ const configDir = path.dirname(configFilePath);
18
+ const projectDir = process.cwd();
19
+ const dependencies = [];
20
+
21
+ // Parse the file into an AST
22
+ const ast = swc.parseSync(content, {
23
+ syntax: 'ecmascript',
24
+ dynamicImport: true,
25
+ target: 'es2020',
26
+ });
27
+
28
+ // Visit all nodes to find import/require statements
29
+ visitNode(ast, (node) => {
30
+ let modulePath = null;
31
+
32
+ // Handle require() calls: require('./plugin')
33
+ if (node.type === 'CallExpression' &&
34
+ node.callee.type === 'Identifier' &&
35
+ node.callee.value === 'require' &&
36
+ node.arguments.length > 0) {
37
+ const arg = node.arguments[0];
38
+ if (arg.expression?.type === 'StringLiteral') {
39
+ modulePath = arg.expression.value;
40
+ }
41
+ }
42
+
43
+ // Handle dynamic import() calls: import('./plugin')
44
+ if (node.type === 'CallExpression' &&
45
+ node.callee.type === 'Import' &&
46
+ node.arguments.length > 0) {
47
+ const arg = node.arguments[0];
48
+ if (arg.expression?.type === 'StringLiteral') {
49
+ modulePath = arg.expression.value;
50
+ }
51
+ }
52
+
53
+ // Handle static imports: import x from './plugin'
54
+ if (node.type === 'ImportDeclaration' && node.source?.type === 'StringLiteral') {
55
+ modulePath = node.source.value;
56
+ }
57
+
58
+ // Handle export re-exports: export * from './plugin'
59
+ if (node.type === 'ExportAllDeclaration' && node.source?.type === 'StringLiteral') {
60
+ modulePath = node.source.value;
61
+ }
62
+
63
+ // Handle named export re-exports: export { x } from './plugin'
64
+ if (node.type === 'ExportNamedDeclaration' && node.source?.type === 'StringLiteral') {
65
+ modulePath = node.source.value;
66
+ }
67
+
68
+ // If we found a module path, try to resolve it
69
+ if (modulePath) {
70
+ const resolvedPath = resolveLocalModule(modulePath, configDir, projectDir);
71
+ if (resolvedPath) {
72
+ dependencies.push(resolvedPath);
73
+ }
74
+ }
75
+ });
76
+
77
+ // Remove duplicates
78
+ return [...new Set(dependencies)];
79
+ } catch (error) {
80
+ console.warn('[Rspack Cache] Failed to parse config dependencies:', error.message);
81
+ return [];
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Recursively visit all nodes in an AST
87
+ * @param {Object} node - AST node
88
+ * @param {Function} callback - Function to call for each node
89
+ */
90
+ function visitNode(node, callback) {
91
+ if (!node || typeof node !== 'object') {
92
+ return;
93
+ }
94
+
95
+ callback(node);
96
+
97
+ // Visit all properties of the node
98
+ for (const key in node) {
99
+ if (Object.prototype.hasOwnProperty.call(node, key)) {
100
+ const value = node[key];
101
+ if (Array.isArray(value)) {
102
+ value.forEach(child => visitNode(child, callback));
103
+ } else if (typeof value === 'object') {
104
+ visitNode(value, callback);
105
+ }
106
+ }
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Resolve a module path to an absolute path if it's a local file
112
+ * @param {string} modulePath - Module path from require/import statement
113
+ * @param {string} configDir - Directory containing the config file
114
+ * @param {string} projectDir - Project root directory
115
+ * @returns {string|null} - Resolved absolute path or null
116
+ */
117
+ function resolveLocalModule(modulePath, configDir, projectDir) {
118
+ // Only process relative paths (starts with . or ..)
119
+ if (!modulePath.startsWith('.')) {
120
+ return null;
121
+ }
122
+
123
+ try {
124
+ let resolvedPath = path.resolve(configDir, modulePath);
125
+ const extensions = ['.js', '.mjs', '.cjs', '.ts', '.json'];
126
+
127
+ // If the path exists as-is, check if it's a directory needing index resolution
128
+ if (fs.existsSync(resolvedPath)) {
129
+ if (fs.statSync(resolvedPath).isDirectory()) {
130
+ let found = false;
131
+ for (const ext of extensions) {
132
+ const indexPath = path.join(resolvedPath, `index${ext}`);
133
+ if (fs.existsSync(indexPath)) {
134
+ resolvedPath = indexPath;
135
+ found = true;
136
+ break;
137
+ }
138
+ }
139
+ if (!found) {
140
+ return null;
141
+ }
142
+ }
143
+ } else {
144
+ // Try common extensions if file doesn't exist as-is
145
+ let found = false;
146
+
147
+ for (const ext of extensions) {
148
+ const pathWithExt = resolvedPath + ext;
149
+ if (fs.existsSync(pathWithExt)) {
150
+ resolvedPath = pathWithExt;
151
+ found = true;
152
+ break;
153
+ }
154
+ }
155
+
156
+ // If still not found, return null
157
+ if (!found) {
158
+ return null;
159
+ }
160
+ }
161
+
162
+ // Verify file is within project (not node_modules)
163
+ const resolvedReal = fs.realpathSync(resolvedPath);
164
+ const projectReal = fs.realpathSync(projectDir);
165
+
166
+ const isWithinProject =
167
+ resolvedReal === projectReal ||
168
+ resolvedReal.startsWith(projectReal + path.sep);
169
+ const hasNodeModulesSegment = resolvedReal.split(path.sep).includes('node_modules');
170
+
171
+ if (isWithinProject && !hasNodeModulesSegment) {
172
+ return resolvedPath;
173
+ }
174
+ } catch (error) {
175
+ // Silently ignore resolution errors
176
+ }
177
+
178
+ return null;
179
+ }
180
+
181
+ module.exports = {
182
+ extractLocalDependencies,
183
+ resolveLocalModule,
184
+ };
@@ -0,0 +1,121 @@
1
+ const path = require('path');
2
+ const fs = require('fs');
3
+ const { cleanOmittedPaths } = require("./mergeRulesSplitOverlap.js");
4
+ const { mergeMeteorRspackFragments } = require("./meteorRspackConfigFactory.js");
5
+
6
+ // Helper function to load and process config files
7
+ async function loadAndProcessConfig(configPath, configType, Meteor, argv, disableWarnings) {
8
+ try {
9
+ // Load the config file
10
+ let config;
11
+ if (path.extname(configPath) === '.mjs') {
12
+ // For ESM modules, we need to use dynamic import
13
+ const fileUrl = `file://${configPath}`;
14
+ const module = await import(fileUrl);
15
+ config = module.default || module;
16
+ } else {
17
+ // For CommonJS modules, we can use require
18
+ config = require(configPath)?.default || require(configPath);
19
+ }
20
+
21
+ // Process the config
22
+ const rawConfig = typeof config === 'function' ? config(Meteor, argv) : config;
23
+ const resolvedConfig = await Promise.resolve(rawConfig);
24
+ const userConfig = resolvedConfig && '0' in resolvedConfig ? resolvedConfig[0] : resolvedConfig;
25
+
26
+ // Define omitted paths and warning function
27
+ const omitPaths = [
28
+ "name",
29
+ "target",
30
+ "entry",
31
+ "output.path",
32
+ "output.filename",
33
+ ...(Meteor.isServer ? ["optimization.splitChunks", "optimization.runtimeChunk"] : []),
34
+ ].filter(Boolean);
35
+
36
+ const warningFn = path => {
37
+ if (disableWarnings) return;
38
+ console.warn(
39
+ `[${configType}] Ignored custom "${path}" — reserved for Meteor-Rspack integration.`,
40
+ );
41
+ };
42
+
43
+ // Clean omitted paths and merge Meteor Rspack fragments
44
+ let nextConfig = cleanOmittedPaths(userConfig, {
45
+ omitPaths,
46
+ warningFn,
47
+ });
48
+ nextConfig = mergeMeteorRspackFragments(nextConfig);
49
+
50
+ return nextConfig;
51
+ } catch (error) {
52
+ console.error(`Error loading ${configType} from ${configPath}:`, error);
53
+ if (configType === 'rspack.config.js') {
54
+ throw error; // Only rethrow for project config
55
+ }
56
+ return null;
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Loads both the user's Rspack configuration and its potential override.
62
+ *
63
+ * @param {string|undefined} projectConfigPath
64
+ * @param {object} Meteor
65
+ * @param {object} argv
66
+ * @returns {Promise<{ nextUserConfig: object|null, nextOverrideConfig: object|null }>}
67
+ */
68
+ async function loadUserAndOverrideConfig(projectConfigPath, Meteor, argv) {
69
+ let nextUserConfig = null;
70
+ let nextOverrideConfig = null;
71
+
72
+ const projectDir = process.cwd();
73
+ const isMeteorPackageConfig = projectDir.includes("/packages/rspack");
74
+
75
+ if (projectConfigPath) {
76
+ const configDir = path.dirname(projectConfigPath);
77
+ const configFileName = path.basename(projectConfigPath);
78
+ const configExt = path.extname(configFileName);
79
+ const configNameWithoutExt = configFileName.replace(configExt, '');
80
+ const configNameFull = `${configNameWithoutExt}.override${configExt}`;
81
+ const overrideConfigPath = path.join(configDir, configNameFull);
82
+
83
+ if (fs.existsSync(overrideConfigPath)) {
84
+ nextOverrideConfig = await loadAndProcessConfig(
85
+ overrideConfigPath,
86
+ configNameFull,
87
+ Meteor,
88
+ argv,
89
+ Meteor.isAngularEnabled
90
+ );
91
+ }
92
+
93
+ if (fs.existsSync(projectConfigPath) && !isMeteorPackageConfig) {
94
+ // Check if there's a .mjs or .cjs version of the config file
95
+ const mjsConfigPath = projectConfigPath.replace(/\.js$/, '.mjs');
96
+ const cjsConfigPath = projectConfigPath.replace(/\.js$/, '.cjs');
97
+
98
+ let projectConfigPathToUse = projectConfigPath;
99
+ if (fs.existsSync(mjsConfigPath)) {
100
+ projectConfigPathToUse = mjsConfigPath;
101
+ } else if (fs.existsSync(cjsConfigPath)) {
102
+ projectConfigPathToUse = cjsConfigPath;
103
+ }
104
+
105
+ nextUserConfig = await loadAndProcessConfig(
106
+ projectConfigPathToUse,
107
+ 'rspack.config.js',
108
+ Meteor,
109
+ argv,
110
+ Meteor.isAngularEnabled
111
+ );
112
+ }
113
+ }
114
+
115
+ return { nextUserConfig, nextOverrideConfig };
116
+ }
117
+
118
+ module.exports = {
119
+ loadAndProcessConfig,
120
+ loadUserAndOverrideConfig,
121
+ };
@@ -132,10 +132,14 @@ function splitVendorChunk() {
132
132
  }
133
133
 
134
134
  /**
135
- * Extend SWC loader config
136
- * Usage: extendSwcConfig()
135
+ * Extend SWC loader config by smart-merging custom options on top of Meteor's
136
+ * defaults (via `mergeSplitOverlap`). Only the properties you specify are
137
+ * overridden; everything else is preserved.
137
138
  *
138
- * @returns {Record<string, object>} `{ meteorRspackConfigX: { optimization: { ... } } }`
139
+ * Usage: Meteor.extendSwcConfig({ jsc: { parser: { decorators: true } } })
140
+ *
141
+ * @param {object} swcConfig - SWC loader options to merge with defaults
142
+ * @returns {Record<string, object>} config fragment for spreading into rspack config
139
143
  */
140
144
  function extendSwcConfig(swcConfig) {
141
145
  return prepareMeteorRspackConfig({
@@ -152,6 +156,44 @@ function extendSwcConfig(swcConfig) {
152
156
  });
153
157
  }
154
158
 
159
+ /**
160
+ * Replace the SWC loader config entirely, discarding Meteor's defaults.
161
+ * Use this when you need full control over SWC options and don't want any
162
+ * automatic merging with Meteor's built-in configuration.
163
+ *
164
+ * Usage: Meteor.replaceSwcConfig({ jsc: { parser: { syntax: 'typescript' }, target: 'es2020' } })
165
+ *
166
+ * @param {object} swcConfig - Complete SWC loader options (replaces defaults)
167
+ * @returns {Record<string, object>} config fragment for spreading into rspack config
168
+ */
169
+ function replaceSwcConfig(swcConfig) {
170
+ return prepareMeteorRspackConfig({
171
+ module: {
172
+ rules: [
173
+ {
174
+ test: /\.(?:[mc]?js|jsx|[mc]?ts|tsx)$/i,
175
+ exclude: /node_modules|\.meteor\/local/,
176
+ loader: 'builtin:swc-loader',
177
+ options: swcConfig,
178
+ },
179
+ ],
180
+ },
181
+ });
182
+ }
183
+
184
+ /**
185
+ * Signal that `Meteor.isDevelopment` and `Meteor.isProduction` should be omitted
186
+ * from DefinePlugin, making the bundle portable across Meteor environments.
187
+ * Usage: return Meteor.enablePortableBuild() in your rspack.config.js
188
+ *
189
+ * @returns {Record<string, object>} config fragment with `meteor.enablePortableBuild: true`
190
+ */
191
+ function enablePortableBuild() {
192
+ return prepareMeteorRspackConfig({
193
+ "meteor.enablePortableBuild": true,
194
+ });
195
+ }
196
+
155
197
  /**
156
198
  * Remove plugins from a Rspack config by name, RegExp, predicate, or array of them.
157
199
  * When using a function predicate, it receives both the plugin and its index in the plugins array.
@@ -214,7 +256,9 @@ module.exports = {
214
256
  setCache,
215
257
  splitVendorChunk,
216
258
  extendSwcConfig,
259
+ replaceSwcConfig,
217
260
  makeWebNodeBuiltinsAlias,
218
261
  disablePlugins,
219
262
  outputMeteorRspack,
263
+ enablePortableBuild,
220
264
  };
package/lib/swc.js CHANGED
@@ -9,11 +9,38 @@ const vm = require('vm');
9
9
  function getMeteorAppSwcrc(file = '.swcrc') {
10
10
  try {
11
11
  const filePath = `${process.cwd()}/${file}`;
12
- if (file.endsWith('.js')) {
12
+ if (file.endsWith('.js') || file.endsWith('.ts')) {
13
13
  let content = fs.readFileSync(filePath, 'utf-8');
14
- // Check if the content uses ES module syntax (export default)
14
+
15
+ if (file.endsWith('.ts')) {
16
+ try {
17
+ const swc = require('@swc/core');
18
+ const result = swc.transformSync(content, {
19
+ jsc: {
20
+ parser: {
21
+ syntax: 'typescript',
22
+ },
23
+ target: 'es2015',
24
+ },
25
+ });
26
+ content = result.code;
27
+ } catch (swcError) {
28
+ content = content
29
+ .replace(/import\s+type\s+.*?from\s+['"][^'"]+['"];?/g, '')
30
+ .replace(/import\s+.*?from\s+['"][^'"]+['"];?/g, '')
31
+ .replace(/import\s+['"][^'"]+['"];?/g, '')
32
+ .replace(/export\s+default\s+/, 'module.exports = ')
33
+ .replace(/export\s+/g, '')
34
+ .replace(/:\s*\w+(\[\])?(\s*=)/g, '$2')
35
+ .replace(/\(([^)]*?):\s*\w+(\[\])?\)/g, '($1)')
36
+ .replace(/\):\s*\w+(\[\])?\s*\{/g, ') {')
37
+ .replace(/interface\s+\w+\s*\{[^}]*\}/g, '')
38
+ .replace(/type\s+\w+\s*=\s*[^;]+;/g, '')
39
+ .replace(/as\s+\w+(\[\])?/g, '');
40
+ }
41
+ }
42
+
15
43
  if (content.includes('export default')) {
16
- // Transform ES module syntax to CommonJS
17
44
  content = content.replace(/export\s+default\s+/, 'module.exports = ');
18
45
  }
19
46
  const script = new vm.Script(`
@@ -27,7 +54,9 @@ function getMeteorAppSwcrc(file = '.swcrc') {
27
54
  })()
28
55
  `);
29
56
  const context = vm.createContext({ process });
30
- return script.runInContext(context);
57
+ const result = script.runInContext(context);
58
+ // Handle CJS interop wrapper (e.g. { __esModule: true, default: config })
59
+ return result && result.__esModule && result.default ? result.default : result;
31
60
  } else {
32
61
  // For .swcrc and other JSON files, parse as JSON
33
62
  return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
@@ -45,12 +74,13 @@ function getMeteorAppSwcrc(file = '.swcrc') {
45
74
  function getMeteorAppSwcConfig() {
46
75
  const hasSwcRc = fs.existsSync(`${process.cwd()}/.swcrc`);
47
76
  const hasSwcJs = !hasSwcRc && fs.existsSync(`${process.cwd()}/swc.config.js`);
77
+ const hasSwcTs = !hasSwcRc && !hasSwcJs && fs.existsSync(`${process.cwd()}/swc.config.ts`);
48
78
 
49
- if (!hasSwcRc && !hasSwcJs) {
79
+ if (!hasSwcRc && !hasSwcJs && !hasSwcTs) {
50
80
  return undefined;
51
81
  }
52
82
 
53
- const swcFile = hasSwcJs ? 'swc.config.js' : '.swcrc';
83
+ const swcFile = hasSwcTs ? 'swc.config.ts' : hasSwcJs ? 'swc.config.js' : '.swcrc';
54
84
  const config = getMeteorAppSwcrc(swcFile);
55
85
 
56
86
  // Set baseUrl to process.cwd() if it exists
package/lib/test.js CHANGED
@@ -2,6 +2,10 @@ const fs = require('fs');
2
2
  const path = require('path');
3
3
  const { createIgnoreRegex, createIgnoreGlobConfig } = require("./ignore.js");
4
4
 
5
+ // Normalize a path to always use forward slashes (POSIX style).
6
+ // Module identifiers in bundled JS must use '/' regardless of OS.
7
+ const toPosix = (p) => p.replace(/\\/g, '/');
8
+
5
9
  /**
6
10
  * Generates eager test files dynamically
7
11
  * @param {Object} options - Options for generating the test file
@@ -58,14 +62,14 @@ const generateEagerTestFile = ({
58
62
  : "/\\.(?:test|spec)s?\\.[^.]+$/";
59
63
 
60
64
  const content = `${
61
- globalImportPath ? `import '${globalImportPath}';\n\n` : ""
65
+ globalImportPath ? `import '${toPosix(globalImportPath)}';\n\n` : ""
62
66
  }${
63
67
  excludeMeteorIgnoreRegex
64
68
  ? `const MeteorIgnoreRegex = ${excludeMeteorIgnoreRegex.toString()};`
65
69
  : ""
66
70
  }
67
71
  {
68
- const ctx = import.meta.webpackContext('${projectDir}', {
72
+ const ctx = import.meta.webpackContext('${toPosix(projectDir)}', {
69
73
  recursive: true,
70
74
  regExp: ${regExp},
71
75
  exclude: ${excludeFoldersRegex.toString()},
@@ -81,9 +85,9 @@ const generateEagerTestFile = ({
81
85
  }).forEach(ctx);
82
86
  ${
83
87
  extraEntry
84
- ? `const extra = import.meta.webpackContext('${path.dirname(
88
+ ? `const extra = import.meta.webpackContext('${toPosix(path.dirname(
85
89
  extraEntry
86
- )}', {
90
+ ))}', {
87
91
  recursive: false,
88
92
  regExp: ${new RegExp(`${path.basename(extraEntry)}$`).toString()},
89
93
  mode: 'eager',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meteorjs/rspack",
3
- "version": "1.1.0-beta.30",
3
+ "version": "1.1.0-beta.32",
4
4
  "description": "Configuration logic for using Rspack in Meteor projects",
5
5
  "main": "index.js",
6
6
  "type": "commonjs",
@@ -14,6 +14,7 @@
14
14
  },
15
15
  "peerDependencies": {
16
16
  "@rspack/cli": ">=1.3.0",
17
- "@rspack/core": ">=1.3.0"
17
+ "@rspack/core": ">=1.3.0",
18
+ "@swc/core": ">=1.3.0"
18
19
  }
19
20
  }