support modules config to allow load in Meteor specific files or folders for plugins to act

This commit is contained in:
Nacho Codoñer
2025-09-04 17:24:15 +02:00
parent 100ef0d54f
commit 8635fcf2eb
8 changed files with 123 additions and 1 deletions

View File

@@ -21,7 +21,9 @@ const {
isMeteorLessProject,
isMeteorScssProject,
getMeteorEnvPackageDirs,
getMeteorAppConfig,
} = require('meteor/tools-core/lib/meteor');
const { buildUnignorePatterns } = require('meteor/tools-core/lib/ignore');
import { getInitialEntrypoints } from './build-context';
@@ -92,6 +94,7 @@ function getFileExtensionsToIgnore() {
* @returns {void}
*/
export function configureMeteorForRspack() {
const meteorAppConfig = getMeteorAppConfig();
const initialEntrypoints = getInitialEntrypoints();
// Ignore node_modules to prevent Meteor from processing them
@@ -180,9 +183,13 @@ export function configureMeteorForRspack() {
),
];
const filesToIgnore = [...rootFilesToIgnore, ...extraFilesToIgnore];
const unignoredFilesAndFolders = buildUnignorePatterns(
meteorAppConfig?.modules || [],
{ skipLevel: 1 },
);
const meteorAppIgnores = `${foldersToIgnore.join(' ')} ${filesToIgnore.join(
' ',
)}`;
)} ${unignoredFilesAndFolders.join(' ')}`.trim();
setMeteorAppIgnore(meteorAppIgnores);
if (isMeteorAppDebug() || isMeteorAppConfigModernVerbose()) {

View File

@@ -0,0 +1,87 @@
/**
* Build gitignore-style "unignore" patterns for specific files/folders.
*
* Rules:
* - Files: !a/ !a/b/ !a/b/c.txt
* - Folders (must end with '/'):
* !a/ !a/b/ !a/b/c/ !a/b/c/**
*
* @param {string[]} inputPaths Paths to keep. Use '/' for dirs (e.g. 'assets/public/').
* @param {Object} [options]
* @param {boolean} [options.includeAllAncestors=true] If false, only include the immediate parent dir.
* @param {boolean} [options.includeGlobForDirs=true] Emit '**' for directories.
* @param {number} [options.skipLevel=0] Skip this many levels from the beginning.
* @returns {string[]} Negation patterns, in correct order.
*/
export function buildUnignorePatterns(inputPaths, {
includeAllAncestors = true,
includeGlobForDirs = true,
skipLevel = 0,
} = {}) {
const out = [];
const seen = new Set();
const push = (p) => {
if (!seen.has(p)) {
seen.add(p);
out.push(p);
}
};
for (let raw of inputPaths) {
if (!raw || typeof raw !== 'string') continue;
// Normalize: forward slashes, drop leading './', collapse double slashes
let anchored = raw.startsWith('/');
let p = raw.replace(/\\/g, '/')
.replace(/^\.\/+/, '')
.replace(/\/{2,}/g, '/');
// detect dir by trailing slash
const isDir = p.endsWith('/');
// strip leading + trailing slashes for splitting, but remember anchoring
const core = p.replace(/^\/+/, '').replace(/\/+$/, '');
if (!core) continue;
const parts = core.split('/');
// Process based on skipLevel
if (skipLevel >= parts.length) {
// Skip everything if skipLevel is greater than or equal to the number of parts
continue;
}
// Ancestors (top-down)
if (includeAllAncestors) {
// Start from skipLevel + 1 to skip the specified number of levels
const startLevel = Math.max(1, skipLevel + 1);
for (let i = startLevel; i <= parts.length - 1; i++) {
const anc = (anchored ? '/' : '') + parts.slice(0, i).join('/') + '/';
push('!' + anc);
}
} else if (parts.length > 1) {
// Only immediate parent
// For minimal mode with skipLevel, we need to check if the parent is at a level we should skip
if (skipLevel < parts.length - 1) {
// Check if the parent's level is greater than skipLevel
const parentLevel = parts.length - 1;
if (parentLevel > skipLevel) {
const parent = (anchored ? '/' : '') + parts.slice(0, parts.length - 1).join('/') + '/';
push('!' + parent);
}
}
}
// Add the file/directory pattern
if (isDir) {
const dir = (anchored ? '/' : '') + parts.join('/') + '/';
push('!' + dir);
if (includeGlobForDirs) push('!' + dir + '**');
} else {
const file = (anchored ? '/' : '') + parts.join('/');
push('!' + file);
}
}
return out;
}

View File

@@ -5,3 +5,4 @@ export * from './lib/process';
export * from './lib/global-state';
export * from './lib/git';
export * from './lib/string';
export * from './lib/ignore';

View File

@@ -33,6 +33,7 @@
"client": "client/main.jsx",
"server": "server/main.js"
},
"modules": ["styles/module.css"],
"modern": true
}
}

View File

@@ -0,0 +1,3 @@
body {
align-content: center;
}

View File

@@ -167,6 +167,7 @@ export async function assertConsoleEval(code, expectedResult, options = {}) {
try {
// Evaluate the code in the browser context
const result = await page.evaluate(code);
console.log("--> (assertions.js-Line: 170)\n result: ", result);
if (exactMatch) {
// Check for exact match

View File

@@ -27,6 +27,10 @@ describe('ReactRouter App Bundling /', () => {
await assertBodyStyles({
'white-space': 'break-spaces',
});
// Meteor modules config
await assertBodyStyles({
'align-content': 'center',
});
// Custom html rspack plugin options
await assertMetaTags({
'theme-color': '#4285f4',
@@ -50,6 +54,10 @@ describe('ReactRouter App Bundling /', () => {
await assertBodyStyles({
'white-space': 'break-spaces',
});
// Meteor modules config
await assertBodyStyles({
'align-content': 'center',
});
// Custom html rspack plugin options
await assertMetaTags({
'theme-color': '#4285f4',

View File

@@ -174,6 +174,20 @@ Ensure your app defines these entry files with the correct paths where each modu
Defining entry points improves performance even with the Meteor bundler, as Meteor stops scanning and eagerly loading unnecessary files. For Meteor-Rspack integration, this is required, since it does not support automatic code discovery for efficiency.
In Meteor-Rspack integration, all app code is ignored by Meteor and handled by Rspack. By default, Meteor still processes eagerly CSS and HTML files in the entry folder (e.g. `client/`).
If you need Meteor to handle CSS or HTML files outside the main entry folder, add them to the `modules` field. This field accepts an array of strings, each pointing to a file or folder, except those inside the reserved `imports` folder for scripts.
``` json
{
"meteor": {
"modules": ["styles/main.css"]
}
}
```
With this, Meteor will process these files, merge stylesheets, generate the final HTML, and support files a Meteor plugin may use, except for JS or script code now handled by Rspack. You can also process CSS and HTML files directly with Rspack using loaders from imports in your app code, as mentioned in ["CSS, Less and SCSS"](#css-less-and-scss) or ["HtmlRspackPlugin"](#htmlrspackplugin). If you prefer Meteor's loading approach, you can still rely on it.
### Nested Imports
Nested imports are a feature of Meteors bundler, not supported in standard bundlers. Meteor introduced them during a time when bundling standards were still evolving and experimented with its own approach. This feature comes from the [`reify` module](https://github.com/benjamn/reify/tree/main) and works with Babel transpilation. SWC doesn't support them since they were never standardized.