Merge pull request #7995 from meteor/babel-runtime-revamp

Rely on npm babel-runtime instead of Meteor helper implementations.
This commit is contained in:
Ben Newman
2016-11-02 17:49:44 -04:00
committed by GitHub
22 changed files with 164 additions and 450 deletions

View File

@@ -3,12 +3,7 @@
"meteor-babel-helpers": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/meteor-babel-helpers/-/meteor-babel-helpers-0.0.3.tgz",
"from": "https://registry.npmjs.org/meteor-babel-helpers/-/meteor-babel-helpers-0.0.3.tgz"
},
"regenerator-runtime": {
"version": "0.9.5",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.9.5.tgz",
"from": "regenerator-runtime@0.9.5"
"from": "meteor-babel-helpers@0.0.3"
}
}
}

View File

@@ -1,396 +1,34 @@
var hasOwn = Object.prototype.hasOwnProperty;
var S = typeof Symbol === "function" ? Symbol : {};
var iteratorSymbol = S.iterator || "@@iterator";
var hasDefineProperty = false;
try {
// IE 8 has a broken Object.defineProperty, so feature-test by
// trying to call it.
Object.defineProperty({}, 'x', {});
hasDefineProperty = true;
} catch (e) {}
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
exports.meteorBabelHelpers = require("meteor-babel-helpers");
// Returns true if a given absolute identifier will be provided at runtime
// by the babel-runtime package.
exports.checkHelper = function checkHelper(id) {
var parts = id.split("/");
var index = 0;
// Skip over leading / and node_modules.
if (parts[index] === "") ++index;
if (parts[index] === "node_modules") ++index;
if (parts[index] !== "babel-runtime") {
return false;
}
// Skip over babel-runtime.
++index;
if (parts.length - index === 2) {
return parts[index] === "helpers" &&
hasOwn.call(BabelRuntime, stripDotJS(parts[index + 1]));
}
if (parts.length - index === 1) {
return stripDotJS(parts[index]) === "regenerator";
}
// There used to be more complicated logic here, when the babel-runtime
// package provided helper implementations of its own, but now this
// function exists just for backwards compatibility.
return false;
};
function stripDotJS(name) {
return name.replace(/\.js$/, "");
try {
var regeneratorRuntime = require("babel-runtime/regenerator");
} catch (e) {
throw new Error([
"The babel-runtime npm package could not be found in your node_modules ",
"directory. Please run the following command to install it:",
"",
" meteor npm install --save babel-runtime",
""
].join("\n"));
}
var BabelRuntime = {
// es6.templateLiterals
// Constructs the object passed to the tag function in a tagged
// template literal.
taggedTemplateLiteralLoose: function (strings, raw) {
// Babel's own version of this calls Object.freeze on `strings` and
// `strings.raw`, but it doesn't seem worth the compatibility and
// performance concerns. If you're writing code against this helper,
// don't add properties to these objects.
strings.raw = raw;
return strings;
},
// es6.classes
// Checks that a class constructor is being called with `new`, and throws
// an error if it is not.
classCallCheck: function (instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
},
// es6.classes
inherits: function (subClass, superClass) {
if (typeof superClass !== "function" && superClass !== null) {
throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
}
if (superClass) {
if (Object.create) {
// All but IE 8
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: {
value: subClass,
enumerable: false,
writable: true,
configurable: true
}
});
} else {
// IE 8 path. Slightly worse for modern browsers, because `constructor`
// is enumerable and shows up in the inspector unnecessarily.
// It's not an "own" property of any instance though.
//
// For correctness when writing code,
// don't enumerate all the own-and-inherited properties of an instance
// of a class and expect not to find `constructor` (but who does that?).
var F = function () {
this.constructor = subClass;
};
F.prototype = superClass && superClass.prototype;
subClass.prototype = new F();
}
if (Object.setPrototypeOf) {
Object.setPrototypeOf(subClass, superClass);
} else {
subClass.__proto__ = superClass;
}
}
},
defineProperty: function (obj, key, value) {
if (hasDefineProperty && (key in obj)) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
},
createClass: function (Constructor, protoProps, staticProps) {
if (! hasDefineProperty) {
// e.g. `class Foo { get bar() {} }`. If you try to use getters and
// setters in IE 8, you will get a big nasty error, with or without
// Babel. I don't know of any other syntax features besides getters
// and setters that will trigger this error.
throw new Error(
"Your browser does not support this type of class property. " +
"For example, Internet Explorer 8 does not support getters and " +
"setters.");
}
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
},
"typeof": function (obj) {
return obj && obj.constructor === Symbol ? "symbol" : typeof obj;
},
possibleConstructorReturn: function (self, call) {
if (! self) {
throw new ReferenceError(
"this hasn't been initialised - super() hasn't been called"
);
}
var callType = typeof call;
if (call &&
callType === "function" ||
callType === "object") {
return call;
}
return self;
},
interopRequireDefault: function (obj) {
return obj && obj.__esModule ? obj : { 'default': obj };
},
interopRequireWildcard: function (obj) {
if (obj && obj.__esModule) {
return obj;
}
var newObj = {};
if (obj != null) {
for (var key in obj) {
if (hasOwn.call(obj, key)) {
newObj[key] = obj[key];
}
}
}
newObj["default"] = obj;
return newObj;
},
interopExportWildcard: function (obj, defaults) {
var newObj = defaults({}, obj);
delete newObj["default"];
return newObj;
},
defaults: function (obj, defaults) {
Object.getOwnPropertyNames(defaults).forEach(function (key) {
var desc = Object.getOwnPropertyDescriptor(defaults, key);
if (desc && desc.configurable && typeof obj[key] === "undefined") {
Object.defineProperty(obj, key, desc);
}
});
return obj;
},
// es7.objectRestSpread and react (JSX)
"extends": Object.assign || (function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (hasOwn.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
}),
// es6.destructuring
objectWithoutProperties: function (obj, keys) {
var target = {};
outer: for (var i in obj) {
if (! hasOwn.call(obj, i)) continue;
for (var j = 0; j < keys.length; j++) {
if (keys[j] === i) continue outer;
}
target[i] = obj[i];
}
return target;
},
// es6.destructuring
objectDestructuringEmpty: function (obj) {
if (obj == null) throw new TypeError("Cannot destructure undefined");
},
// es6.spread
bind: Function.prototype.bind || (function () {
var isCallable = function (value) { return typeof value === 'function'; };
var $Object = Object;
var to_string = Object.prototype.toString;
var array_slice = Array.prototype.slice;
var array_concat = Array.prototype.concat;
var array_push = Array.prototype.push;
var max = Math.max;
var Empty = function Empty() {};
// Copied from es5-shim.js (3ac7942). See original for more comments.
return function bind(that) {
var target = this;
if (!isCallable(target)) {
throw new TypeError('Function.prototype.bind called on incompatible ' + target);
}
var args = array_slice.call(arguments, 1);
var bound;
var binder = function () {
if (this instanceof bound) {
var result = target.apply(
this,
array_concat.call(args, array_slice.call(arguments))
);
if ($Object(result) === result) {
return result;
}
return this;
} else {
return target.apply(
that,
array_concat.call(args, array_slice.call(arguments))
);
}
};
var boundLength = max(0, target.length - args.length);
var boundArgs = [];
for (var i = 0; i < boundLength; i++) {
array_push.call(boundArgs, '$' + i);
}
// Create a Function from source code so that it has the right `.length`.
// Probably not important for Babel. This code violates CSPs that ban
// `eval`, but the browsers that need this polyfill don't have CSP!
bound = Function('binder', 'return function (' + boundArgs.join(',') + '){ return binder.apply(this, arguments); }')(binder);
if (target.prototype) {
Empty.prototype = target.prototype;
bound.prototype = new Empty();
Empty.prototype = null;
}
return bound;
};
})(),
toConsumableArray: function (arr) {
if (Array.isArray(arr)) {
for (var i = arr.length - 1, arr2 = Array(i + 1); i >= 0; --i) {
arr2[i] = arr[i];
}
return arr2;
}
return Array.from(arr);
},
toArray: function (arr) {
return Array.isArray(arr) ? arr : Array.from(arr);
},
slicedToArray: function (iterable, limit) {
if (Array.isArray(iterable)) {
return iterable;
}
if (iterable) {
var it = iterable[iteratorSymbol]();
var result = [];
var info;
if (typeof limit !== "number") {
limit = Infinity;
}
while (result.length < limit &&
! (info = it.next()).done) {
result.push(info.value);
}
return result;
}
throw new TypeError(
"Invalid attempt to destructure non-iterable instance"
);
},
slice: Array.prototype.slice
};
// Use meteorInstall to install all of the above helper functions within
// node_modules/babel-runtime/helpers.
Object.keys(BabelRuntime).forEach(function (helperName) {
var helpers = {};
helpers[helperName + ".js"] = function (require, exports, module) {
module.exports = BabelRuntime[helperName];
if (regeneratorRuntime &&
typeof Promise === "function" &&
typeof Promise.asyncApply === "function") {
// If Promise.asyncApply is defined, use it to wrap calls to
// runtime.async so that the entire async function will run in its own
// Fiber, not just the code that comes after the first await.
var realAsync = regeneratorRuntime.async;
regeneratorRuntime.async = function () {
return Promise.asyncApply(realAsync, regeneratorRuntime, arguments);
};
meteorInstall({
node_modules: {
"babel-runtime": {
helpers: helpers
}
}
});
});
// Use meteorInstall to install the regenerator runtime at
// node_modules/babel-runtime/regenerator.
meteorInstall({
node_modules: {
"babel-runtime": {
"regenerator.js": function (r, e, module) {
// Note that we use the require function provided to the
// babel-runtime.js file, not the one named 'r' above.
var runtime = require("regenerator-runtime");
// If Promise.asyncApply is defined, use it to wrap calls to
// runtime.async so that the entire async function will run in its
// own Fiber, not just the code that comes after the first await.
if (typeof Promise === "function" &&
typeof Promise.asyncApply === "function") {
var realAsync = runtime.async;
runtime.async = function () {
return Promise.asyncApply(realAsync, runtime, arguments);
};
}
module.exports = runtime;
}
}
}
});
}

View File

@@ -1,12 +1,11 @@
Package.describe({
name: "babel-runtime",
summary: "Runtime support for output of Babel transpiler",
version: '0.1.13-beta.0',
version: '1.0.0-beta.0',
documentation: 'README.md'
});
Npm.depends({
"regenerator-runtime": "0.9.5",
"meteor-babel-helpers": "0.0.3"
});

View File

@@ -144,9 +144,11 @@ main.registerCommand({
projectDir: options.appDir,
allowIncompatibleUpdate: options['allow-incompatible-update']
});
main.captureAndExit("=> Errors while initializing project:", function () {
projectContext.prepareProjectForBuild();
});
projectContext.packageMapDelta.displayOnConsole();
});

View File

@@ -1566,6 +1566,11 @@ function doTestCommand(options) {
projectContextOptions.projectDir = testRunnerAppDir;
projectContextOptions.projectDirForLocalPackages = options.appDir;
require("./default-npm-deps.js").install(testRunnerAppDir);
if (buildmessage.jobHasMessages()) {
return;
}
// Find any packages mentioned by a path instead of a package name. We will
// load them explicitly into the catalog.
var packagesByPath = _.filter(options.args, function (p) {

View File

@@ -0,0 +1,48 @@
import buildmessage from "../utils/buildmessage.js";
import {
pathJoin,
statOrNull,
writeFile,
unlink,
} from "../fs/files.js";
const INSTALL_JOB_MESSAGE = "installing dependencies from package.json";
export function install(appDir) {
const packageJsonPath = pathJoin(appDir, "package.json");
const needTempPackageJson = ! statOrNull(packageJsonPath);
if (needTempPackageJson) {
const { dependencies } = require("../static-assets/skel/package.json");
// Write a minimial package.json with the same dependencies as the
// default new-app package.json file.
writeFile(
packageJsonPath,
JSON.stringify({ dependencies }, null, 2) + "\n",
"utf8",
);
}
const ok = buildmessage.enterJob(INSTALL_JOB_MESSAGE, function () {
const { runNpmCommand } = require("../isobuild/meteor-npm.js");
const installResult = runNpmCommand(["install"], appDir);
if (! installResult.success) {
buildmessage.error(
"Could not install npm dependencies for test-packages: " +
installResult.error);
return false;
}
return true;
});
if (needTempPackageJson) {
// Clean up the temporary package.json file created above.
unlink(packageJsonPath);
}
return ok;
}

View File

@@ -191,16 +191,22 @@ export const optimisticHashOrNull = makeOptimistic("hashOrNull", (...args) => {
});
export const optimisticReadJsonOrNull =
makeOptimistic("readJsonOrNull", (...args) => {
makeOptimistic("readJsonOrNull", (path, options) => {
try {
var buffer = optimisticReadFile(...args);
return JSON.parse(optimisticReadFile(path, options));
} catch (e) {
if (e.code !== "ENOENT") {
throw e;
if (e.code === "ENOENT") {
return null;
}
return null;
if (e instanceof SyntaxError &&
options && options.allowSyntaxError) {
return null;
}
throw e;
}
return JSON.parse(buffer);
});
const optimisticIsSymbolicLink = wrap(path => {

View File

@@ -771,15 +771,6 @@ class ResourceSlot {
}
}
let babelRuntime;
function checkBabelRuntimeHelper(id) {
if (! babelRuntime) {
babelRuntime = require("../tool-env/isopackets.js")
.load("runtime")["babel-runtime"];
}
return babelRuntime.checkHelper(id);
}
export class PackageSourceBatch {
constructor(unibuild, processor, {
sourceRoot,
@@ -1097,11 +1088,8 @@ export class PackageSourceBatch {
this._warnAboutMissingModules(allMissingNodeModules);
const meteorProvidesBabelRuntime = map.has("babel-runtime");
scannerMap.forEach((scanner, name) => {
const isApp = ! name;
const isWeb = scanner.isWeb();
const outputFiles = scanner.getOutputFiles();
if (isApp) {
@@ -1109,23 +1097,6 @@ export class PackageSourceBatch {
outputFiles.forEach(file => {
const parts = file.installPath.split("/");
if (meteorProvidesBabelRuntime || ! isWeb) {
// If the Meteor babel-runtime package is installed, it will
// provide implementations for babel-runtime/helpers/* and
// babel-runtime/regenerator at runtime, so we should filter
// out any node_modules/babel-runtime/* modules from the app.
// If the Meteor babel-runtime package is not installed, then
// we should rely on node_modules/babel-runtime/* instead. On
// the server that still means removing bundled files here and
// relying on programs/server/npm/node_modules/babel-runtime,
// but on the web these bundled files are all we have, so we'd
// better not remove them.
if (checkBabelRuntimeHelper(file.installPath)) {
return;
}
}
const nodeModulesIndex = parts.indexOf("node_modules");
if (nodeModulesIndex === -1 || (nodeModulesIndex === 0 &&
@@ -1202,12 +1173,6 @@ export class PackageSourceBatch {
return;
}
if (checkBabelRuntimeHelper(id)) {
// Don't print warnings for babel-runtime/helpers/* modules,
// since we provide most of those.
return;
}
if (! _.has(topLevelMissingIDs, packageDir)) {
// This information will be used to recommend installing npm
// packages below.

View File

@@ -431,6 +431,8 @@ function copyNpmPackageWithSymlinkedNodeModules(fromPkgDir, toPkgDir) {
});
}
const portableCache = Object.create(null);
const isPortable = Profile("meteorNpm.isPortable", dir => {
const lstat = optimisticLStat(dir);
if (! lstat.isDirectory()) {
@@ -449,10 +451,18 @@ const isPortable = Profile("meteorNpm.isPortable", dir => {
// put .meteor-portable files only in the individual top-level package
// directories, so that they will get cleared away the next time those
// packages are (re)installed.
const result = optimisticReadJsonOrNull(portableFile);
const result = _.has(portableCache, portableFile)
? portableCache[portableFile]
: optimisticReadJsonOrNull(portableFile, {
// Make optimisticReadJsonOrNull return null if there's a
// SyntaxError when parsing the .meteor-portable file.
allowSyntaxError: true
});
if (result) {
return result;
}
} else {
// Clean up any .meteor-portable files we mistakenly wrote in
// directories that do not contain package.json files. #7296
@@ -471,8 +481,16 @@ const isPortable = Profile("meteorNpm.isPortable", dir => {
fs.writeFile(
portableFile,
JSON.stringify(result) + "\n",
error => {},
error => {
// Once the asynchronous write finishes (successful or not), we no
// longer need to cache the written value in memory.
delete portableCache[portableFile];
},
);
// Cache the result immediately in memory so that the asynchronous
// write won't confuse synchronous optimisticReadJsonOrNull calls.
portableCache[portableFile] = result;
}
return result;

View File

@@ -5,6 +5,7 @@
"start": "meteor run"
},
"dependencies": {
"meteor-node-stubs": "~0.2.0"
"meteor-node-stubs": "~0.2.0",
"babel-runtime": "6.18.0"
}
}

View File

@@ -7,6 +7,7 @@
"exit-normally": "echo \"This script will exit normally\" && exit 0"
},
"dependencies": {
"babel-runtime": "^6.18.0",
"meteor-node-stubs": "~0.2.0"
}
}

View File

@@ -15,7 +15,7 @@ tracker@1.1.1 # Meteor's client-side reactive programming librar
es5-shim@4.6.15 # ECMAScript 5 compatibility for older browsers.
ecmascript@0.5.9 # Enable ECMAScript2015+ syntax in app code
coffeescript@1.11.1_2
coffeescript
modules-test-package
avital:mocha
standard-minifier-css@1.3.2

View File

@@ -5,6 +5,7 @@
"private": true,
"dependencies": {
"aws-sdk": "^2.2.41",
"babel-runtime": "^6.18.0",
"github": "^0.2.4",
"idle-gc": "^1.0.1",
"meteor-node-stubs": "^0.2.0",

View File

@@ -14,6 +14,8 @@ if (process.env.RUN_ONCE_OUTCOME === "hang") {
if (process.env.RUN_ONCE_OUTCOME === "mongo") {
var test = new Mongo.Collection('test');
test.insert({ value: 86 });
process.exit(test.findOne().value);
Meteor.startup(function () {
test.insert({ value: 86 });
process.exit(test.findOne().value);
});
}

View File

@@ -5,6 +5,7 @@
"start": "meteor run"
},
"dependencies": {
"babel-runtime": "^6.18.0",
"meteor-node-stubs": "~0.2.0"
}
}

View File

@@ -20,13 +20,26 @@ selftest.define("create", function () {
selftest.fail("Failed to add a version specifier to `meteor-base` package");
}
const packageJson = JSON.parse(s.read("package.json"));
if (! packageJson.dependencies.hasOwnProperty("babel-runtime")) {
selftest.fail("New app package.json does not depend on babel-runtime");
}
// Install basic packages like babel-runtime and meteor-node-stubs from
// package.json.
run = s.run("npm", "install");
run.waitSecs(15);
run.expectExit(0);
// Now, can we run it?
run = s.run();
run.waitSecs(15);
run.waitSecs(60);
run.match("foobar");
run.match("proxy.");
// Do not print out the changes to the versions file!
run.waitSecs(15);
run.read("\n=>");
run.waitSecs(5);
run.match("MongoDB");
run.waitSecs(5);
run.match("your app");

View File

@@ -20,7 +20,7 @@ selftest.define("npm", ["net"], function () {
run = s.run("--once", "--raw-logs");
run.tellMongo(MONGO_LISTENING);
if (i === 0) {
run.waitSecs(2);
run.waitSecs(30);
// use match instead of read because on a built release we can
// also get an update message here.
run.match(

View File

@@ -18,12 +18,19 @@ var tmpDir = function () {
};
var makeProjectContext = function (appName) {
var testAppDir = files.pathJoin(
files.convertToStandardPath(__dirname), appName);
var projectDir = files.mkdtemp("test-bundler-assets");
files.cp_r(files.pathJoin(files.convertToStandardPath(__dirname), appName),
projectDir);
files.cp_r(testAppDir, projectDir);
require("../../cli/default-npm-deps.js").install(projectDir);
var projectContext = new projectContextModule.ProjectContext({
projectDir: projectDir
});
doOrThrow(function () {
projectContext.prepareProjectForBuild();
});

View File

@@ -174,13 +174,14 @@ selftest.define("run --once", ["yet-unsolved-windows-failure"], function () {
run.forbidAll("updated");
s.unlink('empty.js');
s.write('.meteor/release', originalRelease);
});
// Try it with a real Mongo. Make sure that it actually starts one.
s = new Sandbox;
selftest.define("run --once with real Mongo", function () {
var s = new Sandbox;
s.createApp("onceapp", "once");
s.cd("onceapp");
s.set("RUN_ONCE_OUTCOME", "mongo");
run = s.run("--once");
var run = s.run("--once");
run.waitSecs(30);
run.expectExit(86);
});

View File

@@ -6,31 +6,41 @@ selftest.define("meteor shell", function () {
s.createApp("meteor-shell-test", "shell");
s.cd("meteor-shell-test");
var server = s.run("run", "--once");
server.waitSecs(45);
var server = s.run();
server.waitSecs(60);
server.match("App running at");
var shell = s.run("shell");
// First try a simple one-line expression.
shell.write("{server:Meteor.isServer}\n");
shell.proc.stdin.end();
shell.waitSecs(10);
shell.match('{"server":true}');
shell.expectExit(0);
shell = s.run("shell");
// Now try with a bunch of newlines in the input.
shell.write("500+\n4000\n+60\n\n+\n7\n");
shell.proc.stdin.end();
shell.waitSecs(10);
shell.match("4567");
shell.expectExit(0);
shell = s.run("shell");
// Now use the shell to make the server output something.
shell.write('console.log("oyez")\n');
shell.proc.stdin.end();
shell.waitSecs(10);
server.match("oyez");
shell.expectExit(0);
shell = s.run("shell");
// Now check something set by the test app.
shell.write('Meteor.checkMeFromShell\n');
shell.proc.stdin.end();
shell.waitSecs(10);
shell.match("oky dok");
shell.expectExit(0);
server.stop();
});

View File

@@ -57,7 +57,6 @@ export const ISOPACKETS = {
'cordova-support': ['boilerplate-generator', 'logging', 'webapp-hashing',
'xmlbuilder'],
'logging': ['logging'],
'runtime': ['babel-runtime'],
};
// Caches isopackets in memory (each isopacket only needs to be loaded

View File

@@ -703,6 +703,8 @@ _.extend(Sandbox.prototype, {
upgradersFile.appendUpgraders(upgraders.allUpgraders());
}
require("../cli/default-npm-deps.js").install(absoluteTo);
if (options.dontPrepareApp) {
return;
}