mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
Merge remote-tracking branch 'origin/release-3.0' into feature/meteor3-npm-tests
This commit is contained in:
@@ -56,6 +56,9 @@
|
||||
* `boilerplate-generator`:
|
||||
- `toHTML` is no longer available (it was already deprecated). Use `toHTMLStream` instead.
|
||||
|
||||
* `ddp`:
|
||||
- Added method `Meteor.isAsyncCall` that can be used to check if the current method call is async or not.
|
||||
|
||||
* `oauth`:
|
||||
- `_endOfPopupResponseTemplate` and `_endOfRedirectResponseTemplate` are no longer a property but now a function that returns a promise of the same value as before
|
||||
- the following server methods are now async:
|
||||
|
||||
@@ -80,6 +80,12 @@ with this ID has already been made. Alternatively, you can use
|
||||
|
||||
Read more about methods and how to use them in the [Methods](http://guide.meteor.com/methods.html) article in the Meteor Guide.
|
||||
|
||||
{% apibox "Meteor.isAsyncCall" %}
|
||||
|
||||
This method can be used to determine if the current method invocation is
|
||||
asynchronous. It returns true if the method is running on the server and came from
|
||||
an async call(`Meteor.callAsync`)
|
||||
|
||||
{% apibox "DDPCommon.MethodInvocation#userId" %}
|
||||
|
||||
The user id is an arbitrary string — typically the id of the user record
|
||||
|
||||
@@ -188,16 +188,15 @@ function getDefaultsForNode8(features) {
|
||||
);
|
||||
// TODO [fibers]: instead of removing the code below, consider this comment:
|
||||
// https://github.com/meteor/meteor/pull/12471/files#r1089610144
|
||||
const isFiberDisabled = process.env.DISABLE_FIBERS === '1';
|
||||
const ignoreAsyncPlugin = process.env.IGNORE_ASYNC_PLUGIN === '1';
|
||||
|
||||
if (!ignoreAsyncPlugin) {
|
||||
if (!features.useNativeAsyncAwait && !ignoreAsyncPlugin) {
|
||||
combined.plugins.push([
|
||||
require('./plugins/async-await.js'),
|
||||
{
|
||||
// Do not transform `await x` to `Promise.await(x)`, since Node
|
||||
// 8 has native support for await expressions.
|
||||
useNativeAsyncAwait: isFiberDisabled,
|
||||
// Even though Node 8 supports native async/await, it is not
|
||||
// compatible with fibers.
|
||||
useNativeAsyncAwait: false,
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
74
npm-packages/meteor-babel/plugins/async-await.js
Normal file
74
npm-packages/meteor-babel/plugins/async-await.js
Normal file
@@ -0,0 +1,74 @@
|
||||
"use strict";
|
||||
|
||||
module.exports = function (babel) {
|
||||
const t = babel.types;
|
||||
|
||||
return {
|
||||
name: "transform-meteor-async-await",
|
||||
visitor: {
|
||||
Function: {
|
||||
exit: function (path) {
|
||||
const node = path.node;
|
||||
if (!node.async) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The original function becomes a non-async function that
|
||||
// returns a Promise.
|
||||
node.async = false;
|
||||
|
||||
// The inner function should inherit lexical environment items
|
||||
// like `this`, `super`, and `arguments` from the outer
|
||||
// function, and arrow functions provide exactly that behavior.
|
||||
const innerFn = t.arrowFunctionExpression(
|
||||
// The inner function has no parameters of its own, but can
|
||||
// refer to the outer parameters of the original function.
|
||||
[],
|
||||
node.body,
|
||||
// The inner function called by Promise.asyncApply should be
|
||||
// async if we have native async/await support.
|
||||
!!this.opts.useNativeAsyncAwait
|
||||
);
|
||||
|
||||
const promiseResultExpression = t.callExpression(
|
||||
t.memberExpression(
|
||||
t.identifier("Promise"),
|
||||
t.identifier("asyncApply"),
|
||||
false
|
||||
), [innerFn]
|
||||
);
|
||||
|
||||
// Calling the async function with Promise.asyncApply is
|
||||
// important to ensure that the part before the first await
|
||||
// expression runs synchronously in its own Fiber, even when
|
||||
// there is native support for async/await.
|
||||
if (node.type === "ArrowFunctionExpression") {
|
||||
node.body = promiseResultExpression;
|
||||
} else {
|
||||
node.body = t.blockStatement([
|
||||
t.returnStatement(promiseResultExpression)
|
||||
]);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
AwaitExpression: function (path) {
|
||||
if (this.opts.useNativeAsyncAwait) {
|
||||
// No need to transform await expressions if we have native
|
||||
// support for them.
|
||||
return;
|
||||
}
|
||||
|
||||
const node = path.node;
|
||||
path.replaceWith(t.callExpression(
|
||||
t.memberExpression(
|
||||
t.identifier("Promise"),
|
||||
t.identifier(node.all ? "awaitAll" : "await"),
|
||||
false
|
||||
),
|
||||
[node.argument]
|
||||
));
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -3,6 +3,8 @@
|
||||
"dependencies": {
|
||||
"@meteorjs/babel": {
|
||||
"version": "7.19.0-beta.1",
|
||||
"resolved": "https://registry.npmjs.org/@meteorjs/babel/-/babel-7.19.0-beta.1.tgz",
|
||||
"integrity": "sha512-4dy7oSXEo6Eb2PHfPkMX0VVnkQJ9Kb6Qv6/ssiXOqQRtTRpBAgeWeMzUd42u/8VzxG6l8NoNqIhPSOHZjC2usg==",
|
||||
"dependencies": {
|
||||
"@ampproject/remapping": {
|
||||
"version": "2.1.1",
|
||||
@@ -1188,11 +1190,6 @@
|
||||
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
|
||||
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="
|
||||
},
|
||||
"fibers": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fibers/-/fibers-5.0.0.tgz",
|
||||
"integrity": "sha512-UpGv/YAZp7mhKHxDvC1tColrroGRX90sSvh8RMZV9leo+e5+EkRVgCEZPlmXeo3BUNQTZxUaVdLskq1Q2FyCPg=="
|
||||
},
|
||||
"find-up": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
|
||||
|
||||
@@ -96,6 +96,8 @@ BCp.processOneFileForTarget = function (inputFile, source) {
|
||||
|
||||
features.topLevelAwait = arch.startsWith('os.') || enableClientTLA
|
||||
|
||||
features.useNativeAsyncAwait = Meteor.isFibersDisabled;
|
||||
|
||||
if (! features.hasOwnProperty("jscript")) {
|
||||
// Perform some additional transformations to improve compatibility
|
||||
// in older browsers (e.g. wrapping named function expressions, per
|
||||
|
||||
@@ -47,6 +47,7 @@ Meteor.connection = DDP.connect(ddpUrl, {
|
||||
[
|
||||
'subscribe',
|
||||
'methods',
|
||||
'isAsyncCall',
|
||||
'call',
|
||||
'callAsync',
|
||||
'apply',
|
||||
|
||||
@@ -511,6 +511,17 @@ export class Connection {
|
||||
return handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Tells if the method call came from a call or a callAsync.
|
||||
* @alias Meteor.isAsyncCall
|
||||
* @locus Anywhere
|
||||
* @memberOf Meteor
|
||||
* @importFromPackage meteor
|
||||
* @returns boolean
|
||||
*/
|
||||
isAsyncCall(){
|
||||
return DDP._CurrentMethodInvocation._isCallAsyncMethodRunning()
|
||||
}
|
||||
methods(methods) {
|
||||
Object.entries(methods).forEach(([name, func]) => {
|
||||
if (typeof func !== 'function') {
|
||||
@@ -722,7 +733,6 @@ export class Connection {
|
||||
|
||||
_apply(name, stubCallValue, args, options, callback) {
|
||||
const self = this;
|
||||
|
||||
// We were passed 3 arguments. They may be either (name, args, options)
|
||||
// or (name, args, callback)
|
||||
if (!callback && typeof options === 'function') {
|
||||
|
||||
@@ -378,3 +378,9 @@ Meteor.methods({
|
||||
resultByValueArrays[testId].push(value);
|
||||
}
|
||||
});
|
||||
/// Helper for "livedata - isAsync call"
|
||||
Meteor.methods({
|
||||
isCallAsync: function () {
|
||||
return Meteor.isAsyncCall()
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1208,6 +1208,12 @@ testAsyncMulti('livedata - methods with nested stubs', [
|
||||
},
|
||||
]);
|
||||
|
||||
Tinytest.addAsync('livedata - isAsync call', async function (test) {
|
||||
Meteor.call('isCallAsync', (err, result) => test.equal(result, false))
|
||||
const result = await Meteor.callAsync('isCallAsync', { returnStubValue: true })
|
||||
test.equal(result, true)
|
||||
})
|
||||
|
||||
// XXX some things to test in greater detail:
|
||||
// staying in simulation mode
|
||||
// time warp
|
||||
|
||||
@@ -1726,6 +1726,17 @@ Object.assign(Server.prototype, {
|
||||
self.sessions.delete(session.id);
|
||||
},
|
||||
|
||||
/**
|
||||
* @summary Tells if the method call came from a call or a callAsync.
|
||||
* @locus Anywhere
|
||||
* @memberOf Meteor
|
||||
* @importFromPackage meteor
|
||||
* @returns boolean
|
||||
*/
|
||||
isAsyncCall: function(){
|
||||
return DDP._CurrentMethodInvocation._isCallAsyncMethodRunning()
|
||||
},
|
||||
|
||||
/**
|
||||
* @summary Defines functions that can be invoked over the network by clients.
|
||||
* @locus Anywhere
|
||||
@@ -1759,7 +1770,20 @@ Object.assign(Server.prototype, {
|
||||
const options = args[0]?.hasOwnProperty('returnStubValue')
|
||||
? args.shift()
|
||||
: {};
|
||||
return this.applyAsync(name, args, options);
|
||||
DDP._CurrentMethodInvocation._set();
|
||||
DDP._CurrentMethodInvocation._setCallAsyncMethodRunning(true);
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
DDP._CurrentCallAsyncInvocation._set({ name, hasCallAsyncParent: true });
|
||||
this.applyAsync(name, args, { isFromCallAsync: true, ...options })
|
||||
.then(resolve)
|
||||
.catch(reject)
|
||||
.finally(() => {
|
||||
DDP._CurrentCallAsyncInvocation._set();
|
||||
});
|
||||
});
|
||||
return promise.finally(() =>
|
||||
DDP._CurrentMethodInvocation._setCallAsyncMethodRunning(false)
|
||||
);
|
||||
},
|
||||
|
||||
apply: function (name, args, options, callback) {
|
||||
@@ -1771,7 +1795,6 @@ Object.assign(Server.prototype, {
|
||||
} else {
|
||||
options = options || {};
|
||||
}
|
||||
|
||||
const promise = this.applyAsync(name, args, options);
|
||||
|
||||
// Return the result in whichever way the caller asked for it. Note that we
|
||||
@@ -1799,7 +1822,6 @@ Object.assign(Server.prototype, {
|
||||
new Meteor.Error(404, `Method '${name}' not found`)
|
||||
);
|
||||
}
|
||||
|
||||
// If this is a method call from within another method or publish function,
|
||||
// get the user state from the outer method or publish function, otherwise
|
||||
// don't allow setUserId to be called
|
||||
|
||||
@@ -14,6 +14,7 @@ Meteor.refresh = async function (notification) {
|
||||
_.each(
|
||||
[
|
||||
'publish',
|
||||
'isAsyncCall',
|
||||
'methods',
|
||||
'call',
|
||||
'callAsync',
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
Meteor.isFibersDisabled = true;
|
||||
|
||||
Meteor._isPromise = (r) => {
|
||||
return r && typeof r.then === 'function';
|
||||
};
|
||||
|
||||
@@ -2,7 +2,11 @@ const getAslStore = () => (Meteor.isServer && global?.asyncLocalStorage?.getStor
|
||||
const getValueFromAslStore = key => getAslStore()[key];
|
||||
const updateAslStore = (key, value) => getAslStore()[key] = value;
|
||||
|
||||
Meteor._isFibersEnabled = !process.env.DISABLE_FIBERS && Meteor.isServer;
|
||||
const bootstrap = global.__meteor_bootstrap__;
|
||||
|
||||
Meteor.isFibersDisabled = !!(bootstrap && bootstrap.isFibersDisabled);
|
||||
Meteor._isFibersEnabled = !Meteor.isFibersDisabled;
|
||||
|
||||
Meteor._getAslStore = getAslStore;
|
||||
Meteor._getValueFromAslStore = getValueFromAslStore;
|
||||
Meteor._updateAslStore = updateAslStore;
|
||||
|
||||
@@ -207,7 +207,7 @@ body {
|
||||
color: #ea5555;
|
||||
}
|
||||
|
||||
pre {
|
||||
.exception pre {
|
||||
color: var(--primary-white);
|
||||
color: #F9FAFB;
|
||||
}
|
||||
|
||||
@@ -369,7 +369,8 @@ Previous builder: ${previousBuilder.outputPath}, this builder: ${outputPath}`
|
||||
const babel = require("@meteorjs/babel");
|
||||
const commonBabelOptions = babel.getDefaultOptions({
|
||||
nodeMajorVersion: parseInt(process.versions.node),
|
||||
typescript: true
|
||||
typescript: true,
|
||||
useNativeAsyncAwait: true
|
||||
});
|
||||
commonBabelOptions.sourceMaps = true;
|
||||
|
||||
|
||||
@@ -472,8 +472,19 @@ Object.assign(Isopack.prototype, {
|
||||
// case right.)
|
||||
}, async function () {
|
||||
// Make a new Plugin API object for this plugin.
|
||||
var Plugin = self._makePluginApi(name);
|
||||
await plugin.load({ Plugin: Plugin, Profile: Profile });
|
||||
const Plugin = self._makePluginApi(name);
|
||||
const __meteor_bootstrap__ = {
|
||||
isFibersDisabled: true,
|
||||
// Set to null to tell Meteor.startup to call hooks immediately
|
||||
// XXX: should we fully support startup hooks in build plugins?
|
||||
startupHooks: null
|
||||
};
|
||||
|
||||
await plugin.load({
|
||||
Plugin,
|
||||
Profile,
|
||||
__meteor_bootstrap__
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -60,6 +60,7 @@ var Module = function (options) {
|
||||
// options
|
||||
self.useGlobalNamespace = options.useGlobalNamespace;
|
||||
self.combinedServePath = options.combinedServePath;
|
||||
self.addEagerRequires = !!options.addEagerRequires;
|
||||
};
|
||||
|
||||
Object.assign(Module.prototype, {
|
||||
@@ -209,6 +210,10 @@ Object.assign(Module.prototype, {
|
||||
if (file.mainModule) {
|
||||
result.mainModulePath = file.absModuleId;
|
||||
}
|
||||
|
||||
if (self.addEagerRequires) {
|
||||
chunks.push(`\nrequire(${JSON.stringify(file.absModuleId)});`);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -1151,6 +1156,10 @@ export var fullLink = Profile("linker.fullLink", async function (inputFiles, {
|
||||
bundleArch,
|
||||
useGlobalNamespace: isApp,
|
||||
combinedServePath,
|
||||
// To support `/client/compatibility`, we can't use the runtime for the
|
||||
// app on the client when TLA is disabled since it wraps all of
|
||||
// the app code in a function. Instead, we have the module add eager requires.
|
||||
addEagerRequires: !bundleArch.startsWith('os.') && isApp && !enableClientTLA
|
||||
});
|
||||
|
||||
// Check if the core-runtime package will already be loaded
|
||||
|
||||
@@ -184,7 +184,7 @@ export class HMRServer {
|
||||
}
|
||||
}
|
||||
|
||||
compare({ name, arch, hmrAvailable, files, cacheKey }, getFileOutput) {
|
||||
async compare({ name, arch, hmrAvailable, files, cacheKey }, getFileOutput) {
|
||||
if (this.firstBuild = null) {
|
||||
this.firstBuild = Date.now();
|
||||
}
|
||||
@@ -248,20 +248,36 @@ export class HMRServer {
|
||||
onlyReplaceableChanges &&
|
||||
removedFilePaths.length === 0;
|
||||
|
||||
function saveFileDetails(file) {
|
||||
async function saveFileDetails(file) {
|
||||
|
||||
const content = await getFileOutput(file);
|
||||
return {
|
||||
content: getFileOutput(file).toStringWithSourceMap({}),
|
||||
content: content.toStringWithSourceMap({}),
|
||||
path: file.absModuleId,
|
||||
meteorInstallOptions: file.meteorInstallOptions
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: try to improve the performance of this
|
||||
const iterWithFn = async (iter, fn) => {
|
||||
let arr = [];
|
||||
for (let i = 0; i < iter.length; i++) {
|
||||
try {
|
||||
const d = await fn(iter[i]);
|
||||
arr.push(d);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
const result = {
|
||||
fileHashes,
|
||||
unreloadableHashes: unreloadable,
|
||||
reloadable,
|
||||
addedFiles: reloadable ? addedFiles.map(saveFileDetails) : [],
|
||||
changedFiles: reloadable ? changedFiles.map(saveFileDetails) : [],
|
||||
addedFiles: reloadable ? await iterWithFn(addedFiles, saveFileDetails) : [],
|
||||
changedFiles: reloadable ? await iterWithFn(changedFiles, saveFileDetails) : [],
|
||||
linkedAt: Date.now(),
|
||||
id: this._createId(),
|
||||
name
|
||||
|
||||
@@ -33,7 +33,8 @@ var starJson = JSON.parse(fs.readFileSync(path.join(buildDir, "star.json")));
|
||||
__meteor_bootstrap__ = {
|
||||
startupHooks: [],
|
||||
serverDir: serverDir,
|
||||
configJson: configJson
|
||||
configJson: configJson,
|
||||
isFibersDisabled: true
|
||||
};
|
||||
|
||||
__meteor_runtime_config__ = {
|
||||
@@ -508,3 +509,4 @@ var runMain = Profile("Run main()", async function () {
|
||||
process.exit(1)
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -11,7 +11,8 @@ function babelRegister() {
|
||||
const cacheDir = path.join(meteorPath, ".babel-cache");
|
||||
const babelOptions = meteorBabel.getDefaultOptions({
|
||||
nodeMajorVersion: parseInt(process.versions.node),
|
||||
typescript: true
|
||||
typescript: true,
|
||||
useNativeAsyncAwait: true
|
||||
});
|
||||
|
||||
// Make sure that source maps are included in the generated code for
|
||||
|
||||
@@ -303,7 +303,10 @@ var loadIsopacketFromDisk = async function (isopacketName) {
|
||||
// An incredibly minimalist version of the environment from
|
||||
// tools/server/boot.js. Kind of a hack.
|
||||
var env = {
|
||||
__meteor_bootstrap__: { startupHooks: [] },
|
||||
__meteor_bootstrap__: {
|
||||
startupHooks: [],
|
||||
isFibersDisabled: true
|
||||
},
|
||||
__meteor_runtime_config__: { meteorRelease: "ISOPACKET" }
|
||||
};
|
||||
env.Profile = Profile;
|
||||
|
||||
Reference in New Issue
Block a user