@meteorjs/rspack 0.0.50 → 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 +172 -0
- package/lib/test.js +61 -0
- package/package.json +1 -1
- package/rspack.config.js +40 -23
- package/entries/eager-app-tests.mjs +0 -9
- package/entries/eager-tests.mjs +0 -9
package/lib/ignore.js
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
var fs = require('fs');
|
|
2
|
+
var path = require('path');
|
|
3
|
+
|
|
4
|
+
// Cleans an entry from wildcard patterns (*/**)
|
|
5
|
+
function cleanWildcardEntry(entry) {
|
|
6
|
+
// If it's an extension pattern like *.ext, skip it
|
|
7
|
+
if (entry.match(/\*\.[^\/]+$/)) {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Handle patterns like my-folder/**/* by extracting the folder part
|
|
12
|
+
if (entry.includes('/**/')) {
|
|
13
|
+
const folderContext = entry.split('/**/')[0].replace(/\/+$/, '');
|
|
14
|
+
if (folderContext) {
|
|
15
|
+
return folderContext;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Otherwise, extract the folder context by removing the wildcard part
|
|
20
|
+
if (entry.includes('*')) {
|
|
21
|
+
const folderContext = entry.split('*')[0].replace(/\/+$/, '');
|
|
22
|
+
if (folderContext) {
|
|
23
|
+
return folderContext;
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return entry;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Reads the .meteorignore file from the given project directory and returns
|
|
33
|
+
* the parsed entries.
|
|
34
|
+
*
|
|
35
|
+
* @param {string} projectDir - The project directory path
|
|
36
|
+
* @returns {Object} - Object with rootFolders and nestedFolders arrays
|
|
37
|
+
*/
|
|
38
|
+
const getMeteorIgnoreEntries = function (projectDir) {
|
|
39
|
+
const meteorIgnorePath = path.join(projectDir, '.meteorignore');
|
|
40
|
+
|
|
41
|
+
// Check if .meteorignore file exists
|
|
42
|
+
let entries = [];
|
|
43
|
+
try {
|
|
44
|
+
const fileContent = fs.readFileSync(meteorIgnorePath, 'utf8');
|
|
45
|
+
|
|
46
|
+
// Process each line in the file
|
|
47
|
+
entries = fileContent.split(/\r?\n/).filter(line => {
|
|
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
|
|
53
|
+
|
|
54
|
+
// Clean all entries from wildcard patterns (*/** parts)
|
|
55
|
+
entries = entries.map(entry => {
|
|
56
|
+
return cleanWildcardEntry(entry);
|
|
57
|
+
}).filter(entry => entry !== null);
|
|
58
|
+
|
|
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
|
+
});
|
|
72
|
+
|
|
73
|
+
return { rootFolders, nestedFolders };
|
|
74
|
+
} catch (e) {
|
|
75
|
+
// If the file doesn't exist or can't be read, return empty arrays
|
|
76
|
+
return { rootFolders: [], nestedFolders: [] };
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Creates a regex pattern to ignore specified folders.
|
|
82
|
+
* The pattern will match paths where the specified folders appear as complete path segments.
|
|
83
|
+
* Special regex characters in folder names are automatically escaped.
|
|
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
|
|
87
|
+
* @returns {RegExp} - Regex pattern to ignore the specified folders
|
|
88
|
+
*/
|
|
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');
|
|
95
|
+
}
|
|
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
|
|
113
|
+
// Escape special regex characters in folder names
|
|
114
|
+
const escapedNestedFolders = nestedFolders.map(folder =>
|
|
115
|
+
folder.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
const escapedRootFolders = rootFolders.map(folder =>
|
|
119
|
+
folder.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
120
|
+
);
|
|
121
|
+
|
|
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
|
+
}
|
|
164
|
+
|
|
165
|
+
return globPatterns;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
module.exports = {
|
|
169
|
+
createIgnoreFoldersRegex,
|
|
170
|
+
getMeteorIgnoreEntries,
|
|
171
|
+
createIgnoreGlobConfig,
|
|
172
|
+
};
|
package/lib/test.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const { createIgnoreFoldersRegex } = require("./ignore.js");
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Generates eager test files dynamically
|
|
7
|
+
* @param {Object} options - Options for generating the test file
|
|
8
|
+
* @param {boolean} options.isAppTest - Whether this is an app test
|
|
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
|
|
13
|
+
* @returns {string} The path to the generated file
|
|
14
|
+
*/
|
|
15
|
+
const generateEagerTestFile = ({
|
|
16
|
+
isAppTest,
|
|
17
|
+
projectDir,
|
|
18
|
+
buildContext,
|
|
19
|
+
rootFolders,
|
|
20
|
+
nestedFolders,
|
|
21
|
+
}) => {
|
|
22
|
+
const distDir = path.resolve(projectDir, ".meteor/local/test");
|
|
23
|
+
if (!fs.existsSync(distDir)) {
|
|
24
|
+
fs.mkdirSync(distDir, { recursive: true });
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const excludeFoldersRegex = createIgnoreFoldersRegex({
|
|
28
|
+
nestedFolders: [
|
|
29
|
+
"node_modules",
|
|
30
|
+
".meteor",
|
|
31
|
+
"public",
|
|
32
|
+
"private",
|
|
33
|
+
buildContext,
|
|
34
|
+
...nestedFolders,
|
|
35
|
+
],
|
|
36
|
+
rootFolders,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const filename = isAppTest ? "eager-app-tests.mjs" : "eager-tests.mjs";
|
|
40
|
+
const filePath = path.resolve(distDir, filename);
|
|
41
|
+
const regExp = isAppTest
|
|
42
|
+
? "/\\.app-(?:test|spec)s?\\.[^.]+$/"
|
|
43
|
+
: "/\\.(?:test|spec)s?\\.[^.]+$/";
|
|
44
|
+
|
|
45
|
+
const content = `{
|
|
46
|
+
const ctx = import.meta.webpackContext('/', {
|
|
47
|
+
recursive: true,
|
|
48
|
+
regExp: ${regExp},
|
|
49
|
+
exclude: ${excludeFoldersRegex.toString()},
|
|
50
|
+
mode: 'eager',
|
|
51
|
+
});
|
|
52
|
+
ctx.keys().forEach(ctx);
|
|
53
|
+
}`;
|
|
54
|
+
|
|
55
|
+
fs.writeFileSync(filePath, content);
|
|
56
|
+
return filePath;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
module.exports = {
|
|
60
|
+
generateEagerTestFile,
|
|
61
|
+
};
|
package/package.json
CHANGED
package/rspack.config.js
CHANGED
|
@@ -8,6 +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 { generateEagerTestFile } = require("./lib/test.js");
|
|
12
|
+
const { getMeteorIgnoreEntries, createIgnoreGlobConfig } = require("./lib/ignore");
|
|
11
13
|
|
|
12
14
|
// Safe require that doesn't throw if the module isn't found
|
|
13
15
|
function safeRequire(moduleName) {
|
|
@@ -101,11 +103,6 @@ function keepOutsideBuild() {
|
|
|
101
103
|
};
|
|
102
104
|
}
|
|
103
105
|
|
|
104
|
-
// Watch options shared across both builds
|
|
105
|
-
const defaultWatchOptions = {
|
|
106
|
-
ignored: ['**/.meteor/local/**', '**/dist/**'],
|
|
107
|
-
};
|
|
108
|
-
|
|
109
106
|
/**
|
|
110
107
|
* @param {{ isClient: boolean; isServer: boolean; isDevelopment?: boolean; isProduction?: boolean; isTest?: boolean }} Meteor
|
|
111
108
|
* @param {{ mode?: string; clientEntry?: string; serverEntry?: string; clientOutputFolder?: string; serverOutputFolder?: string; chunksContext?: string; assetsContext?: string; serverAssetsContext?: string }} argv
|
|
@@ -137,7 +134,8 @@ module.exports = async function (inMeteor = {}, argv = {}) {
|
|
|
137
134
|
const swcExternalHelpers = !!Meteor.swcExternalHelpers;
|
|
138
135
|
const isNative = !!Meteor.isNative;
|
|
139
136
|
const mode = isProd ? 'production' : 'development';
|
|
140
|
-
const
|
|
137
|
+
const projectDir = process.cwd();
|
|
138
|
+
const projectConfigPath = Meteor.projectConfigPath || path.resolve(projectDir, 'rspack.config.js');
|
|
141
139
|
|
|
142
140
|
const isTypescriptEnabled = Meteor.isTypescriptEnabled || false;
|
|
143
141
|
const isJsxEnabled =
|
|
@@ -162,8 +160,8 @@ module.exports = async function (inMeteor = {}, argv = {}) {
|
|
|
162
160
|
const bannerOutput = JSON.parse(Meteor.bannerOutput || process.env.RSPACK_BANNER || '""');
|
|
163
161
|
|
|
164
162
|
// Determine output directories
|
|
165
|
-
const clientOutputDir = path.resolve(
|
|
166
|
-
const serverOutputDir = path.resolve(
|
|
163
|
+
const clientOutputDir = path.resolve(projectDir, 'public');
|
|
164
|
+
const serverOutputDir = path.resolve(projectDir, 'private');
|
|
167
165
|
|
|
168
166
|
// Determine context for bundles and assets
|
|
169
167
|
const buildContext = Meteor.buildContext || '_build';
|
|
@@ -171,7 +169,7 @@ module.exports = async function (inMeteor = {}, argv = {}) {
|
|
|
171
169
|
const chunksContext = Meteor.chunksContext || 'build-chunks';
|
|
172
170
|
|
|
173
171
|
// Determine build output and pass to Meteor
|
|
174
|
-
const buildOutputDir = path.resolve(
|
|
172
|
+
const buildOutputDir = path.resolve(projectDir, buildContext, outputDir);
|
|
175
173
|
Meteor.buildOutputDir = buildOutputDir;
|
|
176
174
|
|
|
177
175
|
// Add HtmlRspackPlugin function to Meteor
|
|
@@ -196,18 +194,24 @@ module.exports = async function (inMeteor = {}, argv = {}) {
|
|
|
196
194
|
});
|
|
197
195
|
};
|
|
198
196
|
|
|
199
|
-
//
|
|
197
|
+
// Get Meteor ignore entries
|
|
198
|
+
const { rootFolders, nestedFolders } = getMeteorIgnoreEntries(projectDir);
|
|
199
|
+
|
|
200
|
+
// Set default watch options
|
|
200
201
|
const watchOptions = {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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 || []),
|
|
209
212
|
],
|
|
210
213
|
}),
|
|
214
|
+
],
|
|
211
215
|
};
|
|
212
216
|
|
|
213
217
|
if (Meteor.isDebug || Meteor.isVerbose) {
|
|
@@ -368,12 +372,25 @@ module.exports = async function (inMeteor = {}, argv = {}) {
|
|
|
368
372
|
experiments: { css: true },
|
|
369
373
|
};
|
|
370
374
|
|
|
375
|
+
|
|
371
376
|
const serverEntry =
|
|
372
377
|
isTest && isTestEager && isTestFullApp
|
|
373
|
-
?
|
|
378
|
+
? generateEagerTestFile({
|
|
379
|
+
isAppTest: true,
|
|
380
|
+
projectDir,
|
|
381
|
+
buildContext,
|
|
382
|
+
rootFolders,
|
|
383
|
+
nestedFolders,
|
|
384
|
+
})
|
|
374
385
|
: isTest && isTestEager
|
|
375
|
-
?
|
|
376
|
-
|
|
386
|
+
? generateEagerTestFile({
|
|
387
|
+
isAppTest: false,
|
|
388
|
+
projectDir,
|
|
389
|
+
buildContext,
|
|
390
|
+
rootFolders,
|
|
391
|
+
nestedFolders,
|
|
392
|
+
})
|
|
393
|
+
: path.resolve(projectDir, buildContext, entryPath);
|
|
377
394
|
const serverNameConfig = `[${(isTest && 'test-') || ''}${
|
|
378
395
|
(isTestModule && 'module') || 'server'
|
|
379
396
|
}-rspack]`;
|
|
@@ -404,7 +421,7 @@ module.exports = async function (inMeteor = {}, argv = {}) {
|
|
|
404
421
|
resolve: {
|
|
405
422
|
extensions,
|
|
406
423
|
alias,
|
|
407
|
-
modules: ['node_modules', path.resolve(
|
|
424
|
+
modules: ['node_modules', path.resolve(projectDir)],
|
|
408
425
|
conditionNames: ['import', 'require', 'node', 'default'],
|
|
409
426
|
},
|
|
410
427
|
externals,
|
|
@@ -437,7 +454,7 @@ module.exports = async function (inMeteor = {}, argv = {}) {
|
|
|
437
454
|
|
|
438
455
|
// Load and apply project-level overrides for the selected build
|
|
439
456
|
// Check if we're in a Meteor package directory by looking at the path
|
|
440
|
-
const isMeteorPackageConfig =
|
|
457
|
+
const isMeteorPackageConfig = projectDir.includes('/packages/rspack');
|
|
441
458
|
if (fs.existsSync(projectConfigPath) && !isMeteorPackageConfig) {
|
|
442
459
|
// Check if there's a .mjs or .cjs version of the config file
|
|
443
460
|
const mjsConfigPath = projectConfigPath.replace(/\.js$/, '.mjs');
|