Merge pull request #13729 from meteor/modern-legacy-selftests

Test coverage for modern build stack config
This commit is contained in:
Nacho Codoñer
2025-05-06 20:17:43 +02:00
committed by GitHub
28 changed files with 1674 additions and 10 deletions

View File

@@ -914,8 +914,10 @@ function isExcludedConfig(name, excludeList = [], startsWith) {
});
}
const disableTextColors = Boolean(JSON.parse(process.env.METEOR_DISABLE_COLORS || "false"));
function color(text, code) {
return `\x1b[${code}m${text}\x1b[0m`;
return disableTextColors ? text : `\x1b[${code}m${text}\x1b[0m`;
}
function logTranspilation({

View File

@@ -405,7 +405,7 @@ function maybeSuggestRaisingWatchLimit(error: Error & { errno: number }) {
}
export const watch = Profile(
"safeWatcher.watch",
"safeWatcher.watchLegacy",
(absPath: string, callback: EntryCallback) => {
const entry = acquireWatcher(absPath, callback);
return {

View File

@@ -375,17 +375,23 @@ function startNewEntry(absPath: string): Entry {
* This registers the callback on the internally managed entry and
* ensures that a Parcel watcher is subscribed to a covering directory.
*/
export const watch = Profile(
"safeWatcher.watch",
export function watch (absPath: string, callback: ChangeCallback): SafeWatcher {
// @ts-ignore
if (!global.modernWatcher) {
// @ts-ignore
return watchLegacy(absPath, callback);
}
// @ts-ignore
return watchModern(absPath, callback);
};
const watchModern =
Profile(
"safeWatcher.watchModern",
(
absPath: string,
callback: ChangeCallback
): SafeWatcher => {
// @ts-ignore
if (!global.modernWatcher) {
// @ts-ignore
return watchLegacy(absPath, callback);
}
absPath = toPosixPath(absPath);
// If the path should be ignored, immediately return a noop SafeWatcher.

1
tools/tests/apps/modern/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
node_modules/

View File

@@ -0,0 +1,19 @@
# This file contains information which helps Meteor properly upgrade your
# app when you run 'meteor update'. You should check it into version control
# with your project.
notices-for-0.9.0
notices-for-0.9.1
0.9.4-platform-file
notices-for-facebook-graph-api-2
1.2.0-standard-minifiers-package
1.2.0-meteor-platform-split
1.2.0-cordova-changes
1.2.0-breaking-changes
1.3.0-split-minifiers-package
1.4.0-remove-old-dev-bundle-link
1.4.1-add-shell-server-package
1.4.3-split-account-service-packages
1.5-add-dynamic-import-package
1.7-split-underscore-from-meteor-base
1.8.3-split-jquery-from-blaze

View File

@@ -0,0 +1 @@
local

View File

@@ -0,0 +1,7 @@
# This file contains a token that is unique to your project.
# Check it into your repository along with the rest of this directory.
# It can be used for purposes such as:
# - ensuring you don't accidentally deploy one app on top of another
# - providing package authors with aggregated statistics
xh4qomttjyd.znxg6je45aan

View File

@@ -0,0 +1,15 @@
# Meteor packages used by this project, one per line.
# Check this file (and the other files in this directory) into your repository.
#
# 'meteor add' and 'meteor remove' will edit this file for you,
# but you can also edit it by hand.
meteor # Shared foundation for all Meteor packages
static-html # Define static page content in .html files
standard-minifier-css # CSS minifier run for production mode
standard-minifier-js # JS minifier run for production mode
es5-shim # ECMAScript 5 compatibility for older browsers
ecmascript # Enable ECMAScript2015+ syntax in app code
shell-server # Server-side component of the `meteor shell` command
webapp # Serves a Meteor app over HTTP
server-render # Support for server-side rendering

View File

@@ -0,0 +1,2 @@
server
browser

View File

@@ -0,0 +1 @@
none

View File

@@ -0,0 +1,39 @@
babel-compiler@7.2.4
babel-runtime@1.3.0
base64@1.0.11
blaze-tools@1.0.10
boilerplate-generator@1.6.0
caching-compiler@1.2.1
caching-html-compiler@1.1.3
dynamic-import@0.5.1
ecmascript@0.12.4
ecmascript-runtime@0.7.0
ecmascript-runtime-client@0.8.0
ecmascript-runtime-server@0.7.1
ejson@1.1.0
es5-shim@4.8.0
fetch@0.1.0
html-tools@1.0.11
htmljs@1.0.11
inter-process-messaging@0.1.0
logging@1.1.20
meteor@1.9.3
minifier-css@1.4.1
minifier-js@2.4.0
modern-browsers@0.1.4-beta181.16
modules@0.13.0
modules-runtime@0.10.3
promise@0.11.2
random@1.1.0
routepolicy@1.1.0
server-render@0.3.1
shell-server@0.4.0
spacebars-compiler@1.1.3
standard-minifier-css@1.5.2
standard-minifier-js@2.4.0
static-html@1.2.2
templating-tools@1.1.2
tracker@1.2.1
underscore@1.0.11
webapp@1.7.3-beta181.16
webapp-hashing@1.0.9

View File

@@ -0,0 +1,8 @@
{
"jsc": {
"baseUrl": "./",
"paths": {
"@swcAlias/*": ["swcAlias/*"]
}
}
}

View File

@@ -0,0 +1,4 @@
body {
padding: 10px;
font-family: sans-serif;
}

View File

@@ -0,0 +1,21 @@
<head>
<title>Minimal Meteor app</title>
</head>
<body>
<h1>Minimal Meteor app</h1>
<p>
This Meteor app uses as few Meteor packages as possible, to keep the
client JavaScript bundle as small as possible.
</p>
<em id="server-render-target"></em>
<h2>Learn Meteor!</h2>
<ul>
<li><a href="https://www.meteor.com/try" target="_blank">Do the Tutorial</a></li>
<li><a href="http://guide.meteor.com" target="_blank">Follow the Guide</a></li>
<li><a href="https://docs.meteor.com" target="_blank">Read the Docs</a></li>
<li><a href="https://forums.meteor.com" target="_blank">Discussions</a></li>
</ul>
</body>

View File

@@ -0,0 +1 @@
Meteor.startup(() => {});

1119
tools/tests/apps/modern/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,19 @@
{
"name": "modern",
"private": true,
"scripts": {
"start": "meteor run"
},
"dependencies": {
"@babel/runtime": "^7.23.5",
"meteor-node-stubs": "^1.2.12",
"react": "^18.3.1"
},
"meteor": {
"mainModule": {
"client": "client/main.js",
"server": "server/main.js"
},
"testModule": "tests/main.js"
}
}

View File

@@ -0,0 +1 @@
import '@swcAlias/main';

View File

@@ -0,0 +1,5 @@
import React from 'react';
console.log('custom-component.js');
export default <></>;

View File

@@ -0,0 +1,5 @@
import React from 'react';
console.log('javascript-component.jsx');
export default <></>;

View File

@@ -0,0 +1,3 @@
console.log('javascript.js');
export default {};

View File

@@ -0,0 +1,10 @@
import { Meteor } from "meteor/meteor";
Meteor.startup(() => {});
try {
const errorStack = new Error().stack;
console.log(errorStack);
} catch (e) {
console.log(e);
}

View File

@@ -0,0 +1,5 @@
import React from 'react';
console.log('typescript-component.tsx');
export default <></>;

View File

@@ -0,0 +1,3 @@
console.log('typescript.ts');
export default {};

View File

@@ -0,0 +1 @@
console.log('swcAlias/main.js alias resolved');

View File

359
tools/tests/modern.js Normal file
View File

@@ -0,0 +1,359 @@
var selftest = require('../tool-testing/selftest.js');
var Sandbox = selftest.Sandbox;
// No need for a high value since the asserts already wait long enough to pass tests
const waitToStart = 5;
async function writeModernConfig(s, modernConfig) {
const json = JSON.parse(s.read("package.json"));
json.meteor = {
...json.meteor,
modern: modernConfig,
};
s.write("package.json", JSON.stringify(json, null, 2) + "\n");
}
selftest.define("modern build stack - legacy", async function () {
const s = new Sandbox();
await s.init();
await s.createApp("modern", "modern");
await s.cd("modern");
s.set("METEOR_PROFILE", "0");
await writeModernConfig(s, false);
const run = s.run();
run.waitSecs(waitToStart);
await run.match("App running at");
/* check legacy stack */
await run.match(/Babel\.compile/, false, true);
await run.match(/safeWatcher\.watchLegacy/, false, true);
await run.match(/_findSources for web\.browser.legacy/, false, true);
/* check debug stack */
await run.match(/server\/main\.js:6:22/, false, true);
await run.stop();
});
selftest.define("modern build stack", async function () {
const s = new Sandbox();
await s.init();
await s.createApp("modern", "modern");
await s.cd("modern");
s.set("METEOR_PROFILE", "0");
await writeModernConfig(s, true);
const run = s.run();
run.waitSecs(waitToStart);
await run.match("App running at");
/* check modern stack */
await run.match(/SWC\.compile/, false, true);
await run.match(/safeWatcher\.watchModern/, false, true);
await run.match(/_findSources for web\.browser/, false, true);
run.forbid(/Babel\.compile/, false, true);
run.forbid(/_findSources for web\.browser\.legacy/, false, true);
/* check debug stack */
await run.match(/server\/main\.js:6:22/, false, true);
await run.stop();
});
selftest.define("modern build stack - disable transpiler", async function () {
const s = new Sandbox();
await s.init();
await s.createApp("modern", "modern");
await s.cd("modern");
s.set("METEOR_PROFILE", "0");
await writeModernConfig(s, { transpiler: false });
const run = s.run();
run.waitSecs(waitToStart);
await run.match("App running at");
/* disable transpiler */
run.forbid(/SWC\.compile/, false, true);
await run.match(/Babel\.compile/, false, true);
/* Keep rest of modern build stack */
await run.match(/safeWatcher\.watchModern/, false, true);
await run.match(/_findSources for web\.browser/, false, true);
run.forbid(/_findSources for web\.browser\.legacy/, false, true);
await run.stop();
});
selftest.define("modern build stack - disable watcher", async function () {
const s = new Sandbox();
await s.init();
await s.createApp("modern", "modern");
await s.cd("modern");
s.set("METEOR_PROFILE", "0");
await writeModernConfig(s, { watcher: false });
const run = s.run();
run.waitSecs(waitToStart);
await run.match("App running at");
/* disable watcher */
run.forbid(/safeWatcher\.watchModern/, false, true);
await run.match(/safeWatcher\.watchLegacy/, false, true);
/* Keep rest of modern build stack */
await run.match(/SWC\.compile/, false, true);
await run.match(/_findSources for web\.browser/, false, true);
run.forbid(/_findSources for web\.browser\.legacy/, false, true);
await run.stop();
});
selftest.define("modern build stack - disable webArchOnly", async function () {
const s = new Sandbox();
await s.init();
await s.createApp("modern", "modern");
await s.cd("modern");
s.set("METEOR_PROFILE", "0");
await writeModernConfig(s, { webArchOnly: false });
const run = s.run();
run.waitSecs(waitToStart);
await run.match("App running at");
/* disable webArchOnly */
await run.match(/_findSources for web\.browser/, false, true);
await run.match(/_findSources for web\.browser\.legacy/, false, true);
/* Keep rest of modern build stack */
await run.match(/safeWatcher\.watchModern/, false, true);
await run.match(/SWC\.compile/, false, true);
await run.stop();
});
selftest.define("modern build stack - transpiler boolean-like options", async function () {
const s = new Sandbox();
await s.init();
await s.createApp("modern", "modern");
await s.cd("modern");
process.env.METEOR_DISABLE_COLORS = true;
await writeModernConfig(s, {
transpiler: {
verbose: true,
},
});
const run = s.run();
run.waitSecs(waitToStart);
await run.match("App running at");
/* check transpiler options */
await run.match(/\[Transpiler] Used SWC.*\(app\)/, false, true);
await run.match(/\[Transpiler] Used SWC.*\(package\)/, false, true);
await writeModernConfig(s, {
transpiler: {
verbose: true,
excludeApp: true,
},
});
await run.match(/\[Transpiler] Used Babel.*\(app\)/, false, true);
await writeModernConfig(s, {
transpiler: {
verbose: true,
excludePackages: true,
},
});
await run.match(/\[Transpiler] Used Babel.*\(package\)/, false, true);
await run.stop();
});
selftest.define("modern build stack - transpiler string-like options", async function () {
const s = new Sandbox();
await s.init();
await s.createApp("modern", "modern");
await s.cd("modern");
process.env.METEOR_DISABLE_COLORS = true;
await writeModernConfig(s, {
transpiler: {
verbose: true,
},
});
const run = s.run();
run.waitSecs(waitToStart);
await run.match("App running at");
/* check transpiler options */
await run.match(/\[Transpiler] Used SWC.*\(app\)/, false, true);
await run.match(/\[Transpiler] Used SWC.*\(package\)/, false, true);
await writeModernConfig(s, {
transpiler: {
verbose: true,
excludeApp: ['main.js'],
},
});
await run.match(/\[Transpiler] Used Babel.*\(app\)/, false, true);
await writeModernConfig(s, {
transpiler: {
verbose: true,
excludePackages: ['ejson'],
},
});
await run.match(/\[Transpiler] Used Babel.*\(package\)/, false, true);
await run.stop();
});
async function writConfig(s, config) {
const json = JSON.parse(s.read("package.json"));
json.meteor = {
...json.meteor,
...config,
};
s.write("package.json", JSON.stringify(json, null, 2) + "\n");
}
async function writeSwcrcConfig(s, config) {
let json = JSON.parse(s.read("package.json"));
json = {
...json,
...config,
};
s.write(".swcrc", JSON.stringify(json, null, 2) + "\n");
}
selftest.define("modern build stack - transpiler custom .swcrc", async function () {
const s = new Sandbox();
await s.init();
await s.createApp("modern", "modern");
await s.cd("modern");
await writConfig(s, {
modern: true,
mainModule: {
client: 'client/main.js',
server: 'server/alias.js',
},
});
const run = s.run();
run.waitSecs(waitToStart);
await run.match("App running at");
/* custom .swcrc and alias resolution */
await run.match(/alias resolved/, false, true);
await run.stop();
});
selftest.define("modern build stack - transpiler files", async function () {
const s = new Sandbox();
await s.init();
await s.createApp("modern", "modern");
await s.cd("modern");
await writConfig(s, {
modern: true,
mainModule: {
client: 'client/main.js',
server: 'server/javascript.js',
},
});
const run = s.run();
run.waitSecs(waitToStart);
await run.match("App running at");
await run.match(/javascript\.js/, false, true);
await writConfig(s, {
modern: true,
mainModule: {
client: 'client/main.js',
server: 'server/javascript-component.jsx',
},
});
await run.match(/javascript-component\.jsx/, false, true);
await writConfig(s, {
modern: true,
mainModule: {
client: 'client/main.js',
server: 'server/typescript.ts',
},
});
await run.match(/typescript\.ts/, false, true);
await writConfig(s, {
modern: true,
mainModule: {
client: 'client/main.js',
server: 'server/typescript-component.tsx',
},
});
await run.match(/typescript-component\.tsx/, false, true);
await writeSwcrcConfig(s, {
jsc: {
parser: {
syntax: 'typescript',
tsx: true,
jsx: true,
},
},
});
await writConfig(s, {
modern: true,
mainModule: {
client: 'client/main.js',
server: 'server/custom-component.js',
},
});
await run.match(/custom-component\.js/, false, true);
await run.stop();
});

View File

@@ -184,13 +184,20 @@ export default class Run {
// may be a regular expression or a string. Consume stdout up to
// that point. If this pattern does not appear after a timeout (or
// the program exits before emitting the pattern), fail.
match(pattern, _strict) {
match(pattern, _strict, fullBuffer) {
this._ensureStarted();
let timeout = this.baseTimeout + this.extraTime;
timeout *= timeoutScaleFactor;
this.extraTime = 0;
Console.simpleDebug('match', pattern);
if (fullBuffer) {
return this.stdoutMatcher.matchAsync(pattern, {
timeout,
strict: _strict,
matchFullBuffer: fullBuffer,
});
}
return this.stdoutMatcher.match(pattern, timeout, _strict);
}