@meteorjs/rspack 0.0.51 → 0.0.52

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/lib/ignore.js CHANGED
@@ -33,14 +33,9 @@ function cleanWildcardEntry(entry) {
33
33
  * the parsed entries.
34
34
  *
35
35
  * @param {string} projectDir - The project directory path
36
- * @param {Object} options - Options for processing ignore entries
37
- * @param {boolean} options.foldersOnly - If true, returns only folder entries
38
- * @returns {Array<string>} - Array of ignore entries
36
+ * @returns {Object} - Object with rootFolders and nestedFolders arrays
39
37
  */
40
- const getMeteorIgnoreEntries = function (projectDir, options) {
41
- options = options || {};
42
- const foldersOnly = !!options.foldersOnly;
43
-
38
+ const getMeteorIgnoreEntries = function (projectDir) {
44
39
  const meteorIgnorePath = path.join(projectDir, '.meteorignore');
45
40
 
46
41
  // Check if .meteorignore file exists
@@ -50,39 +45,35 @@ const getMeteorIgnoreEntries = function (projectDir, options) {
50
45
 
51
46
  // Process each line in the file
52
47
  entries = fileContent.split(/\r?\n/).filter(line => {
53
- // Skip empty lines and comments
54
- return line.trim() !== '' && !line.trim().startsWith('#');
55
- });
48
+ // Trim the line
49
+ const trimmedLine = line.trim();
50
+ // Skip empty lines, comments, and negation entries (starting with !)
51
+ return trimmedLine !== '' && !trimmedLine.startsWith('#') && !trimmedLine.startsWith('!');
52
+ }).map(line => line.trim()); // Ensure all lines are trimmed
56
53
 
57
54
  // Clean all entries from wildcard patterns (*/** parts)
58
55
  entries = entries.map(entry => {
59
56
  return cleanWildcardEntry(entry);
60
57
  }).filter(entry => entry !== null);
61
58
 
62
- if (foldersOnly) {
63
- // Filter to include only entries that are likely to be folders
64
- entries = entries.filter(entry => {
65
- // Entries ending with / are definitely folders
66
- if (entry.endsWith('/')) {
67
- return true;
68
- }
69
-
70
- // Try to determine if it's a folder by checking if it exists
71
- // and is a directory
72
- try {
73
- const fullPath = path.join(projectDir, entry);
74
- return fs.existsSync(fullPath) && fs.statSync(fullPath).isDirectory();
75
- } catch (e) {
76
- // If we can't determine, assume it's not a folder
77
- return false;
78
- }
79
- });
80
- }
59
+ // Separate entries into rootFolders and nestedFolders
60
+ const rootFolders = [];
61
+ const nestedFolders = [];
62
+
63
+ entries.forEach(entry => {
64
+ // If entry starts with / or ./, it's a root folder
65
+ if (entry.startsWith('/') || entry.startsWith('./')) {
66
+ rootFolders.push(entry);
67
+ } else {
68
+ // Otherwise, it's a nested folder
69
+ nestedFolders.push(entry);
70
+ }
71
+ });
81
72
 
82
- return entries;
73
+ return { rootFolders, nestedFolders };
83
74
  } catch (e) {
84
- // If the file doesn't exist or can't be read, return an empty array
85
- return [];
75
+ // If the file doesn't exist or can't be read, return empty arrays
76
+ return { rootFolders: [], nestedFolders: [] };
86
77
  }
87
78
  };
88
79
 
@@ -90,28 +81,92 @@ const getMeteorIgnoreEntries = function (projectDir, options) {
90
81
  * Creates a regex pattern to ignore specified folders.
91
82
  * The pattern will match paths where the specified folders appear as complete path segments.
92
83
  * Special regex characters in folder names are automatically escaped.
93
- * @param {string[]} folders - Array of folder names to ignore
84
+ * @param {Object|string[]} options - Options object
85
+ * @param {string[]} [options.nestedFolders] - Array of folder names to ignore anywhere in the path
86
+ * @param {string[]} [options.rootFolders] - Array of folder names that should only match at the root level
94
87
  * @returns {RegExp} - Regex pattern to ignore the specified folders
95
88
  */
96
- function createIgnoreFoldersRegex(folders) {
97
- if (!Array.isArray(folders) || folders.length === 0) {
98
- throw new Error('folders must be a non-empty array');
89
+ function createIgnoreFoldersRegex(options) {
90
+ const nestedFolders = options.nestedFolders || [];
91
+ const rootFolders = options.rootFolders || [];
92
+
93
+ if (!Array.isArray(nestedFolders) || nestedFolders.length === 0) {
94
+ throw new Error('nestedFolders must be a non-empty array');
99
95
  }
100
96
 
97
+ // If rootFolders is not provided or empty, use the original behavior
98
+ if (!rootFolders || !Array.isArray(rootFolders) || rootFolders.length === 0) {
99
+ // Escape special regex characters in folder names
100
+ const escapedFolders = nestedFolders.map(folder =>
101
+ folder.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
102
+ );
103
+
104
+ // Join folder names with | for the regex pattern
105
+ const foldersPattern = escapedFolders.join('|');
106
+
107
+ // Create a regex that matches paths where the specified folders appear as complete path segments
108
+ // Format: /(^|\/)(folder1|folder2|folder3)(\/|$)/
109
+ return new RegExp(`(^|\\/)(${foldersPattern})(\\/|$)`);
110
+ }
111
+
112
+ // Handle both rootFolders and nestedFolders
101
113
  // Escape special regex characters in folder names
102
- const escapedFolders = folders.map(folder =>
114
+ const escapedNestedFolders = nestedFolders.map(folder =>
115
+ folder.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
116
+ );
117
+
118
+ const escapedRootFolders = rootFolders.map(folder =>
103
119
  folder.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
104
120
  );
105
121
 
106
- // Join folder names with | for the regex pattern
107
- const foldersPattern = escapedFolders.join('|');
122
+ // Join folder names with | for the regex patterns
123
+ const nestedFoldersPattern = escapedNestedFolders.join('|');
124
+ const rootFoldersPattern = escapedRootFolders.join('|');
125
+
126
+ // Create a regex that matches:
127
+ // 1. Root folders at the beginning of the path: /^(folderRootOnly)(\/|$)/
128
+ // 2. Nested folders anywhere in the path: /(^|\/)(folderAny1|folderAny2)(\/|$)/
129
+ const pattern = `^(${rootFoldersPattern})(\\/|$)|(^|\\/)(${nestedFoldersPattern})(\\/|$)`;
130
+ return new RegExp(pattern);
131
+ }
132
+
133
+ /**
134
+ * Creates a glob config array for ignoring specified folders.
135
+ * For nested folders, the pattern will be "**/" + folder + "/**".
136
+ * For root folders, the pattern will be folder + "/**".
137
+ * @param {Object} options - Options object
138
+ * @param {string[]} [options.nestedFolders] - Array of folder names to ignore anywhere in the path
139
+ * @param {string[]} [options.rootFolders] - Array of folder names that should only match at the root level
140
+ * @returns {string[]} - Array of glob patterns to ignore the specified folders
141
+ */
142
+ function createIgnoreGlobConfig(options = {}) {
143
+ const nestedFolders = options.nestedFolders || [];
144
+ const rootFolders = options.rootFolders || [];
145
+ const globPatterns = [];
146
+
147
+ // Create glob patterns for nested folders: **/{nestedFolder}/**
148
+ if (Array.isArray(nestedFolders) && nestedFolders.length > 0) {
149
+ nestedFolders.forEach(folder => {
150
+ // Remove leading ./ or / if present
151
+ const cleanFolder = folder.replace(/^(\.\/|\/)/g, '');
152
+ globPatterns.push(`**/${cleanFolder}/**`);
153
+ });
154
+ }
155
+
156
+ // Create glob patterns for root folders: {rootFolder}/**
157
+ if (Array.isArray(rootFolders) && rootFolders.length > 0) {
158
+ rootFolders.forEach(folder => {
159
+ // Remove leading ./ or / if present
160
+ const cleanFolder = folder.replace(/^(\.\/|\/)/g, '');
161
+ globPatterns.push(`${cleanFolder}/**`);
162
+ });
163
+ }
108
164
 
109
- // Create a regex that matches paths where the specified folders appear as complete path segments
110
- // Format: /(^|\/)(folder1|folder2|folder3)(\/|$)/
111
- return new RegExp(`(^|\\/)(${foldersPattern})(\\/|$)`);
165
+ return globPatterns;
112
166
  }
113
167
 
114
168
  module.exports = {
115
169
  createIgnoreFoldersRegex,
116
170
  getMeteorIgnoreEntries,
171
+ createIgnoreGlobConfig,
117
172
  };
package/lib/test.js CHANGED
@@ -1,29 +1,40 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
- const { createIgnoreFoldersRegex, getMeteorIgnoreEntries } = require("./ignore.js");
3
+ const { createIgnoreFoldersRegex } = require("./ignore.js");
4
4
 
5
5
  /**
6
6
  * Generates eager test files dynamically
7
7
  * @param {Object} options - Options for generating the test file
8
8
  * @param {boolean} options.isAppTest - Whether this is an app test
9
9
  * @param {string} options.projectDir - The project directory
10
+ * @param {string} options.buildContext - The build context
11
+ * @param {string} options.rootFolders
12
+ * @param {string} options.nestedFolders
10
13
  * @returns {string} The path to the generated file
11
14
  */
12
- const generateEagerTestFile = ({ isAppTest, projectDir }) => {
15
+ const generateEagerTestFile = ({
16
+ isAppTest,
17
+ projectDir,
18
+ buildContext,
19
+ rootFolders,
20
+ nestedFolders,
21
+ }) => {
13
22
  const distDir = path.resolve(projectDir, ".meteor/local/test");
14
23
  if (!fs.existsSync(distDir)) {
15
24
  fs.mkdirSync(distDir, { recursive: true });
16
25
  }
17
26
 
18
- const ignoredFolders = getMeteorIgnoreEntries(projectDir, {
19
- foldersOnly: true,
27
+ const excludeFoldersRegex = createIgnoreFoldersRegex({
28
+ nestedFolders: [
29
+ "node_modules",
30
+ ".meteor",
31
+ "public",
32
+ "private",
33
+ buildContext,
34
+ ...nestedFolders,
35
+ ],
36
+ rootFolders,
20
37
  });
21
- const excludeFoldersRegex = createIgnoreFoldersRegex([
22
- "node_modules",
23
- ".meteor",
24
- "_build",
25
- ...ignoredFolders,
26
- ]);
27
38
 
28
39
  const filename = isAppTest ? "eager-app-tests.mjs" : "eager-tests.mjs";
29
40
  const filePath = path.resolve(distDir, filename);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meteorjs/rspack",
3
- "version": "0.0.51",
3
+ "version": "0.0.52",
4
4
  "description": "Configuration logic for using Rspack in Meteor projects",
5
5
  "main": "index.js",
6
6
  "type": "commonjs",
package/rspack.config.js CHANGED
@@ -8,8 +8,8 @@ const { cleanOmittedPaths, mergeSplitOverlap } = require("./lib/mergeRulesSplitO
8
8
  const { getMeteorAppSwcConfig } = require('./lib/swc.js');
9
9
  const HtmlRspackPlugin = require('./plugins/HtmlRspackPlugin.js');
10
10
  const { RequireExternalsPlugin } = require('./plugins/RequireExtenalsPlugin.js');
11
- const { createIgnoreFoldersRegex, getMeteorIgnoreEntries } = require("./lib/ignore.js");
12
11
  const { generateEagerTestFile } = require("./lib/test.js");
12
+ const { getMeteorIgnoreEntries, createIgnoreGlobConfig } = require("./lib/ignore");
13
13
 
14
14
  // Safe require that doesn't throw if the module isn't found
15
15
  function safeRequire(moduleName) {
@@ -103,11 +103,6 @@ function keepOutsideBuild() {
103
103
  };
104
104
  }
105
105
 
106
- // Watch options shared across both builds
107
- const defaultWatchOptions = {
108
- ignored: ['**/.meteor/local/**', '**/dist/**'],
109
- };
110
-
111
106
  /**
112
107
  * @param {{ isClient: boolean; isServer: boolean; isDevelopment?: boolean; isProduction?: boolean; isTest?: boolean }} Meteor
113
108
  * @param {{ mode?: string; clientEntry?: string; serverEntry?: string; clientOutputFolder?: string; serverOutputFolder?: string; chunksContext?: string; assetsContext?: string; serverAssetsContext?: string }} argv
@@ -165,8 +160,8 @@ module.exports = async function (inMeteor = {}, argv = {}) {
165
160
  const bannerOutput = JSON.parse(Meteor.bannerOutput || process.env.RSPACK_BANNER || '""');
166
161
 
167
162
  // Determine output directories
168
- const clientOutputDir = path.resolve(process.cwd(), 'public');
169
- const serverOutputDir = path.resolve(process.cwd(), 'private');
163
+ const clientOutputDir = path.resolve(projectDir, 'public');
164
+ const serverOutputDir = path.resolve(projectDir, 'private');
170
165
 
171
166
  // Determine context for bundles and assets
172
167
  const buildContext = Meteor.buildContext || '_build';
@@ -174,7 +169,7 @@ module.exports = async function (inMeteor = {}, argv = {}) {
174
169
  const chunksContext = Meteor.chunksContext || 'build-chunks';
175
170
 
176
171
  // Determine build output and pass to Meteor
177
- const buildOutputDir = path.resolve(process.cwd(), buildContext, outputDir);
172
+ const buildOutputDir = path.resolve(projectDir, buildContext, outputDir);
178
173
  Meteor.buildOutputDir = buildOutputDir;
179
174
 
180
175
  // Add HtmlRspackPlugin function to Meteor
@@ -199,18 +194,24 @@ module.exports = async function (inMeteor = {}, argv = {}) {
199
194
  });
200
195
  };
201
196
 
202
- // Set watch options
197
+ // Get Meteor ignore entries
198
+ const { rootFolders, nestedFolders } = getMeteorIgnoreEntries(projectDir);
199
+
200
+ // Set default watch options
203
201
  const watchOptions = {
204
- ...defaultWatchOptions,
205
- ...(isTest &&
206
- isTestEager && {
207
- ignored: [
208
- ...defaultWatchOptions.ignored,
209
- `**/${buildContext}/**`,
210
- '**/.meteor/local/**',
211
- '**/node_modules/**',
202
+ ignored: [
203
+ ...createIgnoreGlobConfig({
204
+ rootFolders,
205
+ nestedFolders: [
206
+ ".meteor/local",
207
+ "dist",
208
+ ...(isTest && isTestEager
209
+ ? [buildContext, ".meteor/local", "node_modules"]
210
+ : []),
211
+ ...(nestedFolders || []),
212
212
  ],
213
213
  }),
214
+ ],
214
215
  };
215
216
 
216
217
  if (Meteor.isDebug || Meteor.isVerbose) {
@@ -374,10 +375,22 @@ module.exports = async function (inMeteor = {}, argv = {}) {
374
375
 
375
376
  const serverEntry =
376
377
  isTest && isTestEager && isTestFullApp
377
- ? generateEagerTestFile({ isAppTest: true, projectDir })
378
+ ? generateEagerTestFile({
379
+ isAppTest: true,
380
+ projectDir,
381
+ buildContext,
382
+ rootFolders,
383
+ nestedFolders,
384
+ })
378
385
  : isTest && isTestEager
379
- ? generateEagerTestFile({ isAppTest: false, projectDir })
380
- : path.resolve(process.cwd(), buildContext, entryPath);
386
+ ? generateEagerTestFile({
387
+ isAppTest: false,
388
+ projectDir,
389
+ buildContext,
390
+ rootFolders,
391
+ nestedFolders,
392
+ })
393
+ : path.resolve(projectDir, buildContext, entryPath);
381
394
  const serverNameConfig = `[${(isTest && 'test-') || ''}${
382
395
  (isTestModule && 'module') || 'server'
383
396
  }-rspack]`;
@@ -408,7 +421,7 @@ module.exports = async function (inMeteor = {}, argv = {}) {
408
421
  resolve: {
409
422
  extensions,
410
423
  alias,
411
- modules: ['node_modules', path.resolve(process.cwd())],
424
+ modules: ['node_modules', path.resolve(projectDir)],
412
425
  conditionNames: ['import', 'require', 'node', 'default'],
413
426
  },
414
427
  externals,
@@ -441,7 +454,7 @@ module.exports = async function (inMeteor = {}, argv = {}) {
441
454
 
442
455
  // Load and apply project-level overrides for the selected build
443
456
  // Check if we're in a Meteor package directory by looking at the path
444
- const isMeteorPackageConfig = process.cwd().includes('/packages/rspack');
457
+ const isMeteorPackageConfig = projectDir.includes('/packages/rspack');
445
458
  if (fs.existsSync(projectConfigPath) && !isMeteorPackageConfig) {
446
459
  // Check if there's a .mjs or .cjs version of the config file
447
460
  const mjsConfigPath = projectConfigPath.replace(/\.js$/, '.mjs');