Merge branch 'master' into devel

This commit is contained in:
Ben Newman
2017-05-30 15:27:50 -04:00
129 changed files with 4072 additions and 587 deletions

View File

@@ -5,6 +5,106 @@
this is to prevent duplicate name error on reloads. Initial data is now
properly serialized.
## v1.5, 2017-05-30
* The `meteor-base` package implies a new `dynamic-import` package, which
provides runtime support for [the proposed ECMAScript dynamic
`import(...)` syntax](https://github.com/tc39/proposal-dynamic-import),
enabling asynchronous module fetching or "code splitting." If your app
does not use the `meteor-base` package, you can use the package by
simply running `meteor add dynamic-import`. See this [blog
post](https://blog.meteor.com/meteor-1-5-react-loadable-f029a320e59c)
and [PR #8327](https://github.com/meteor/meteor/pull/8327) for more
information about how dynamic `import(...)` works in Meteor, and how to
use it in your applications.
* The `ecmascript-runtime` package, which provides polyfills for various
new ECMAScript runtime APIs and language features, has been split into
`ecmascript-runtime-client` and `ecmascript-runtime-server`, to reflect
the different needs of browsers versus Node 4. The client runtime now
relies on the `core-js` library found in the `node_modules` directory of
the application, rather than a private duplicate installed via
`Npm.depends`. This is unlikely to be a disruptive change for most
developers, since the `babel-runtime` npm package is expected to be
installed, and `core-js` is a dependency of `babel-runtime`, so
`node_modules/core-js` should already be present. If that's not the
case, just run `meteor npm install --save core-js` to install it.
* The `npm` npm package has been upgraded to version 4.6.1.
* The `meteor-babel` npm package has been upgraded to version 0.21.4,
enabling the latest Reify compiler and the transform-class-properties
plugin, among other improvements.
* The `reify` npm package has been upgraded to version 0.11.21, fixing
[issue #8595](https://github.com/meteor/meteor/issues/8595) and
improving compilation and runtime performance.
> Note: With this version of Reify, `import` declarations are compiled to
`module.watch(require(id), ...)` instead of `module.importSync(id, ...)`
or the older `module.import(id, ...)`. The behavior of the compiled code
should be the same as before, but the details seemed different enough to
warrant a note.
* The `install` npm package has been upgraded to version 0.10.1.
* The `meteor-promise` npm package has been upgraded to version 0.8.4.
* The `uglify-js` npm package has been upgraded to version 3.0.13, fixing
[#8704](https://github.com/meteor/meteor/issues/8704).
* If you're using the `standard-minifier-js` Meteor package, as most
Meteor developers do, it will now produce a detailed analysis of package
and module sizes within your production `.js` bundle whenever you run
`meteor build` or `meteor run --production`. These data are served by
the application web server at the same URL as the minified `.js` bundle,
except with a `.stats.json` file extension instead of `.js`. If you're
using a different minifier plugin, and would like to support similar
functionality, refer to
[these](https://github.com/meteor/meteor/pull/8327/commits/084801237a8c288d99ec82b0fbc1c76bdf1aab16)
[commits](https://github.com/meteor/meteor/pull/8327/commits/1c8bc7353e9a8d526880634a58c506b423c4a55e)
for inspiration.
* To visualize the bundle size data produced by `standard-minifier-js`,
run `meteor add bundle-visualizer` and then start your development
server in production mode with `meteor run --production`. Be sure to
remove the `bundle-visualizer` package before actually deploying your
app, or the visualization will be displayed to your users.
* If you've been developing an app with multiple versions of Meteor, or
testing with beta versions, and you haven't recently run `meteor reset`,
your `.meteor/local/bundler-cache` directory may have become quite
large. This is just a friendly reminder that this directory is perfectly
safe to delete, and Meteor will repopulate it with only the most recent
cached bundles.
* Apps created with `meteor create --bare` now use the `static-html`
package for processing `.html` files instead of `blaze-html-templates`,
to avoid large unnecessary dependencies like the `jquery` package.
* Babel plugins now receive file paths without leading `/` characters,
which should prevent confusion about whether the path should be treated
as absolute. [PR #8610](https://github.com/meteor/meteor/pull/8610)
* It is now possible to override the Cordova iOS and/or Android
compatibility version by setting the `METEOR_CORDOVA_COMPAT_VERSION_IOS`
and/or `METEOR_CORDOVA_COMPAT_VERSION_ANDROID` environment variables.
[PR #8581](https://github.com/meteor/meteor/pull/8581)
* Modules in `node_modules` directories will no longer automatically have
access to the `Buffer` polyfill on the client, since that polyfill
contributed more than 22KB of minified JavaScript to the client bundle,
and was rarely used. If you really need the Buffer API on the client,
you should now obtain it explicitly with `require("buffer").Buffer`.
[Issue #8645](https://github.com/meteor/meteor/issues/8645).
* Packages in `node_modules` directories are now considered non-portable
(and thus may be automatically rebuilt for the current architecture), if
their `package.json` files contain any of the following install hooks:
`install`, `preinstall`, or `postinstall`. Previously, a package was
considered non-portable only if it contained any `.node` binary modules.
[Issue #8225](https://github.com/meteor/meteor/issues/8225)
## v1.4.4.3, 2017-05-22
* Node has been upgraded to version 4.8.3.
@@ -310,6 +410,8 @@
fixing [#8021](https://github.com/meteor/meteor/issues/8021) and
[#7662](https://github.com/meteor/meteor/issues/7662).
* The `reify` npm package has been upgraded to 0.4.7.
* Added support for frame-ancestors CSP option in browser-policy.
[#7970](https://github.com/meteor/meteor/pull/7970)

2
meteor
View File

@@ -1,6 +1,6 @@
#!/usr/bin/env bash
BUNDLE_VERSION=4.7.27
BUNDLE_VERSION=4.8.17
# OS Check. Put here because here is where we download the precompiled
# bundles that are arch specific.

View File

@@ -81,9 +81,9 @@
"from": "babel-helper-is-void-0@>=0.0.1 <0.0.2"
},
"babel-helper-mark-eval-scopes": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/babel-helper-mark-eval-scopes/-/babel-helper-mark-eval-scopes-0.0.3.tgz",
"from": "babel-helper-mark-eval-scopes@>=0.0.3 <0.0.4"
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/babel-helper-mark-eval-scopes/-/babel-helper-mark-eval-scopes-0.1.1.tgz",
"from": "babel-helper-mark-eval-scopes@>=0.1.1 <0.2.0"
},
"babel-helper-optimise-call-expression": {
"version": "6.24.1",
@@ -96,9 +96,9 @@
"from": "babel-helper-regex@>=6.24.1 <7.0.0"
},
"babel-helper-remove-or-void": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/babel-helper-remove-or-void/-/babel-helper-remove-or-void-0.0.1.tgz",
"from": "babel-helper-remove-or-void@>=0.0.1 <0.0.2"
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/babel-helper-remove-or-void/-/babel-helper-remove-or-void-0.1.1.tgz",
"from": "babel-helper-remove-or-void@>=0.1.1 <0.2.0"
},
"babel-helper-replace-supers": {
"version": "6.24.1",
@@ -131,15 +131,15 @@
"from": "babel-plugin-minify-constant-folding@>=0.0.4 <0.0.5",
"dependencies": {
"jsesc": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.0.tgz",
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.1.tgz",
"from": "jsesc@>=2.4.0 <3.0.0"
}
}
},
"babel-plugin-minify-dead-code-elimination": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/babel-plugin-minify-dead-code-elimination/-/babel-plugin-minify-dead-code-elimination-0.1.4.tgz",
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/babel-plugin-minify-dead-code-elimination/-/babel-plugin-minify-dead-code-elimination-0.1.6.tgz",
"from": "babel-plugin-minify-dead-code-elimination@>=0.1.3 <0.2.0"
},
"babel-plugin-minify-flip-comparisons": {
@@ -199,6 +199,11 @@
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-generators/-/babel-plugin-syntax-async-generators-6.13.0.tgz",
"from": "babel-plugin-syntax-async-generators@>=6.13.0 <7.0.0"
},
"babel-plugin-syntax-class-properties": {
"version": "6.13.0",
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz",
"from": "babel-plugin-syntax-class-properties@>=6.8.0 <7.0.0"
},
"babel-plugin-syntax-dynamic-import": {
"version": "6.18.0",
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz",
@@ -224,6 +229,11 @@
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz",
"from": "babel-plugin-syntax-trailing-function-commas@>=6.22.0 <7.0.0"
},
"babel-plugin-transform-class-properties": {
"version": "6.24.1",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz",
"from": "babel-plugin-transform-class-properties@>=6.24.1 <7.0.0"
},
"babel-plugin-transform-es2015-arrow-functions": {
"version": "6.22.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz",
@@ -270,9 +280,9 @@
"from": "babel-plugin-transform-es2015-modules-commonjs@>=6.22.0 <7.0.0"
},
"babel-plugin-transform-es2015-modules-reify": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-reify/-/babel-plugin-transform-es2015-modules-reify-0.7.0.tgz",
"from": "babel-plugin-transform-es2015-modules-reify@>=0.7.0 <0.8.0"
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-reify/-/babel-plugin-transform-es2015-modules-reify-0.11.0.tgz",
"from": "babel-plugin-transform-es2015-modules-reify@>=0.11.0 <0.12.0"
},
"babel-plugin-transform-es2015-object-super": {
"version": "6.24.1",
@@ -330,18 +340,18 @@
"from": "babel-plugin-transform-inline-consecutive-adds@>=0.0.2 <0.0.3"
},
"babel-plugin-transform-member-expression-literals": {
"version": "6.8.1",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-member-expression-literals/-/babel-plugin-transform-member-expression-literals-6.8.1.tgz",
"version": "6.8.3",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-member-expression-literals/-/babel-plugin-transform-member-expression-literals-6.8.3.tgz",
"from": "babel-plugin-transform-member-expression-literals@>=6.8.1 <7.0.0"
},
"babel-plugin-transform-merge-sibling-variables": {
"version": "6.8.2",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-merge-sibling-variables/-/babel-plugin-transform-merge-sibling-variables-6.8.2.tgz",
"version": "6.8.4",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-merge-sibling-variables/-/babel-plugin-transform-merge-sibling-variables-6.8.4.tgz",
"from": "babel-plugin-transform-merge-sibling-variables@>=6.8.2 <7.0.0"
},
"babel-plugin-transform-minify-booleans": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-minify-booleans/-/babel-plugin-transform-minify-booleans-6.8.0.tgz",
"version": "6.8.2",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-minify-booleans/-/babel-plugin-transform-minify-booleans-6.8.2.tgz",
"from": "babel-plugin-transform-minify-booleans@>=6.8.0 <7.0.0"
},
"babel-plugin-transform-object-rest-spread": {
@@ -350,8 +360,8 @@
"from": "babel-plugin-transform-object-rest-spread@>=6.22.0 <7.0.0"
},
"babel-plugin-transform-property-literals": {
"version": "6.8.1",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-property-literals/-/babel-plugin-transform-property-literals-6.8.1.tgz",
"version": "6.8.3",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-property-literals/-/babel-plugin-transform-property-literals-6.8.3.tgz",
"from": "babel-plugin-transform-property-literals@>=6.8.1 <7.0.0"
},
"babel-plugin-transform-react-display-name": {
@@ -385,13 +395,13 @@
"from": "babel-plugin-transform-regexp-constructors@>=0.0.5 <0.0.6"
},
"babel-plugin-transform-remove-console": {
"version": "6.8.1",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-remove-console/-/babel-plugin-transform-remove-console-6.8.1.tgz",
"version": "6.8.3",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-remove-console/-/babel-plugin-transform-remove-console-6.8.3.tgz",
"from": "babel-plugin-transform-remove-console@>=6.8.0 <7.0.0"
},
"babel-plugin-transform-remove-debugger": {
"version": "6.8.1",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-remove-debugger/-/babel-plugin-transform-remove-debugger-6.8.1.tgz",
"version": "6.8.3",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-remove-debugger/-/babel-plugin-transform-remove-debugger-6.8.3.tgz",
"from": "babel-plugin-transform-remove-debugger@>=6.8.0 <7.0.0"
},
"babel-plugin-transform-remove-undefined": {
@@ -405,8 +415,8 @@
"from": "babel-plugin-transform-runtime@>=6.22.0 <7.0.0"
},
"babel-plugin-transform-simplify-comparison-operators": {
"version": "6.8.1",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-simplify-comparison-operators/-/babel-plugin-transform-simplify-comparison-operators-6.8.1.tgz",
"version": "6.8.3",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-simplify-comparison-operators/-/babel-plugin-transform-simplify-comparison-operators-6.8.3.tgz",
"from": "babel-plugin-transform-simplify-comparison-operators@>=6.8.1 <7.0.0"
},
"babel-plugin-transform-strict-mode": {
@@ -415,8 +425,8 @@
"from": "babel-plugin-transform-strict-mode@>=6.24.1 <7.0.0"
},
"babel-plugin-transform-undefined-to-void": {
"version": "6.8.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-undefined-to-void/-/babel-plugin-transform-undefined-to-void-6.8.0.tgz",
"version": "6.8.2",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-undefined-to-void/-/babel-plugin-transform-undefined-to-void-6.8.2.tgz",
"from": "babel-plugin-transform-undefined-to-void@>=6.8.0 <7.0.0"
},
"babel-preset-babili": {
@@ -465,8 +475,8 @@
"from": "babel-types@>=6.22.0 <7.0.0"
},
"babylon": {
"version": "6.16.1",
"resolved": "https://registry.npmjs.org/babylon/-/babylon-6.16.1.tgz",
"version": "6.17.1",
"resolved": "https://registry.npmjs.org/babylon/-/babylon-6.17.1.tgz",
"from": "babylon@>=6.15.0 <7.0.0"
},
"balanced-match": {
@@ -477,7 +487,7 @@
"brace-expansion": {
"version": "1.1.7",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.7.tgz",
"from": "brace-expansion@>=1.0.0 <2.0.0"
"from": "brace-expansion@>=1.1.7 <2.0.0"
},
"chalk": {
"version": "1.1.3",
@@ -500,8 +510,8 @@
"from": "core-js@>=2.4.0 <3.0.0"
},
"debug": {
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.3.tgz",
"version": "2.6.8",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz",
"from": "debug@>=2.1.1 <3.0.0"
},
"detect-indent": {
@@ -580,9 +590,9 @@
"from": "loose-envify@>=1.0.0 <2.0.0"
},
"meteor-babel": {
"version": "0.20.1",
"resolved": "https://registry.npmjs.org/meteor-babel/-/meteor-babel-0.20.1.tgz",
"from": "meteor-babel@0.20.1"
"version": "0.21.4",
"resolved": "https://registry.npmjs.org/meteor-babel/-/meteor-babel-0.21.4.tgz",
"from": "meteor-babel@0.21.4"
},
"meteor-babel-helpers": {
"version": "0.0.3",
@@ -590,8 +600,8 @@
"from": "meteor-babel-helpers@0.0.3"
},
"minimatch": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.3.tgz",
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"from": "minimatch@>=3.0.2 <4.0.0"
},
"minimist": {
@@ -599,15 +609,25 @@
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"from": "minimist@0.0.8"
},
"minipass": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.0.2.tgz",
"from": "minipass@>=2.0.0 <3.0.0"
},
"minizlib": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.0.3.tgz",
"from": "minizlib@>=1.0.3 <2.0.0"
},
"mkdirp": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"from": "mkdirp@>=0.5.1 <0.6.0"
},
"ms": {
"version": "0.7.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-0.7.2.tgz",
"from": "ms@0.7.2"
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"from": "ms@2.0.0"
},
"number-is-nan": {
"version": "1.0.1",
@@ -640,8 +660,8 @@
"from": "regenerate@>=1.2.1 <2.0.0"
},
"regenerator-runtime": {
"version": "0.10.3",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.3.tgz",
"version": "0.10.5",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz",
"from": "regenerator-runtime@>=0.10.0 <0.11.0"
},
"regenerator-transform": {
@@ -672,15 +692,20 @@
}
},
"reify": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/reify/-/reify-0.7.4.tgz",
"from": "reify@>=0.7.2 <0.8.0"
"version": "0.11.21",
"resolved": "https://registry.npmjs.org/reify/-/reify-0.11.21.tgz",
"from": "reify@>=0.11.18 <0.12.0"
},
"repeating": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz",
"from": "repeating@>=2.0.0 <3.0.0"
},
"semver": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
"from": "semver@>=5.3.0 <6.0.0"
},
"slash": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz",
@@ -692,8 +717,8 @@
"from": "source-map@>=0.5.0 <0.6.0"
},
"source-map-support": {
"version": "0.4.14",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.14.tgz",
"version": "0.4.15",
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.15.tgz",
"from": "source-map-support@>=0.4.2 <0.5.0"
},
"strip-ansi": {
@@ -707,14 +732,19 @@
"from": "supports-color@>=2.0.0 <3.0.0"
},
"to-fast-properties": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.2.tgz",
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz",
"from": "to-fast-properties@>=1.0.1 <2.0.0"
},
"trim-right": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz",
"from": "trim-right@>=1.0.1 <2.0.0"
},
"yallist": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz",
"from": "yallist@>=3.0.0 <4.0.0"
}
}
}

View File

@@ -21,6 +21,10 @@ Babel = {
// Deprecated, now a no-op.
validateExtraFeatures: Function.prototype,
parse: function (source) {
return Npm.require('meteor-babel').parse(source);
},
compile: function (source, options) {
var meteorBabel = Npm.require('meteor-babel');
options = options || getDefaultOptions();

View File

@@ -6,15 +6,15 @@ Package.describe({
// isn't possible because you can't publish a non-recommended
// release with package versions that don't have a pre-release
// identifier at the end (eg, -dev)
version: '6.18.2'
version: '6.19.1'
});
Npm.depends({
'meteor-babel': '0.20.1'
'meteor-babel': '0.21.4'
});
Package.onUse(function (api) {
api.use('ecmascript-runtime');
api.use('ecmascript-runtime', 'server');
api.addFiles([
'babel.js',

View File

@@ -73,7 +73,10 @@ Boilerplate.prototype._generateBoilerplateFromManifestAndSource =
if (item.type === 'css' && item.where === 'client') {
boilerplateBaseData.css.push(itemObj);
}
if (item.type === 'js' && item.where === 'client') {
if (item.type === 'js' && item.where === 'client' &&
// Dynamic JS modules should not be loaded eagerly in the
// initial HTML of the app.
! item.path.startsWith('dynamic/')) {
boilerplateBaseData.js.push(itemObj);
}
if (item.type === 'head') {

View File

@@ -1,15 +1,15 @@
Package.describe({
summary: "Generates the boilerplate html from program's manifest",
version: '1.0.11'
version: '1.1.0'
});
Package.onUse(function (api) {
api.use([
'underscore@1.0.9',
'spacebars-compiler@1.0.12',
'spacebars@1.0.12',
'htmljs@1.0.10',
'ui@1.0.11',
'underscore',
'spacebars-compiler',
'spacebars',
'htmljs',
'ui',
], 'server');
api.addFiles(['boilerplate-generator.js'], 'server');
api.export(['Boilerplate'], 'server');

View File

@@ -0,0 +1,13 @@
This package implements the `Module.prototype.dynamicImport(id)` runtime
API needed for fetching modules dynamically from the server, so that those
modules don't have to be included in the initial JavaScript bundle.
With this package installed, supporting the [dynamic `import(...)`
proposal](https://github.com/tc39/proposal-dynamic-import) is as easy as
compiling `import(...)` to `module.dynamicImport(...)`.
Any version of a module that has been fetched previously will be
permanently cached and should never need to be fetched again by the same
client, even after the window is closed or the browser is restarted.
Meteor 1.5 is necessary for this package to work properly.

View File

@@ -0,0 +1,43 @@
### Basic implementation:
- [x] Future-proof `findImportedModuleIdentifiers` for real `import(...)`
- [x] Source maps in development
- [x] Debugger stops at reasonable points in dev tools
- [x] Open another WebSocket? NO
- [x] Make `import(...)` work on the server
- [x] Modules are minified but not merged in production
- [x] Wrap modules with function to enable better minification
- [x] Babel transform from `import(...)` to `module.importAsync(...)`
- [x] Local module caching.
- [x] Prototype with `localStorage`.
- [x] Reimplement using `indexedDB` (much larger size limits).
- [x] Compact `previous` state representation
- [x] Improve dependency resolution in `packages/dynamic-import/server.js`
- [x] Report static import/syntax/etc. errors for async files
- [x] What about old/new versions of code?
- [x] What about package pseudo-globals (imports)?
- [x] What about dynamic stubs?
- [x] Avoid creating dynamic files on the server.
- [ ] ~~`Mp.dynamicImport` could be implemented without the fallback on the server if we were sure the server had no dynamic files.~~
- [x] Make sure client-only reloads work (revisit _read caching).
- [x] Make sure path manipulation is Windows-safe.
- [x] Install dynamic modules with correct `meteorInstall` options.
- [x] Tests!
### Future work:
- [ ] Batch multiple `__dynamicImport` method calls?
- [ ] Detect modules unevaluated during page load and recommend importing them dynamically.
- [ ] Quantify the impact of using `import(...)`.
- [ ] Report initial bundle sizes.
- [ ] Warn about `import(...)` calls before `Meteor.startup`, since they should probably be static.
- [ ] Analyze module graph to suggest dynamic cut points (e.g. in router callbacks).
- [ ] Warn if dynamically imported modules are imported statically elsewhere (killing the benefit of the dynamic import).
- [ ] Use `Cache-Control: immutable` for the initial bundle.
- [ ] Upgrade caching to `ServiceWorker` and `Cache` in supporting browsers (if actually faster!).
- [ ] Preload modules that are often dynamically imported, when page becomes idle.
- [ ] Allow the client to overfetch soon-to-be-needed modules to avoid waterfalls.
- [ ] Write [Meteor Guide](https://guide.meteor.com/) article about techniques for optimizing page load times.
- [ ] Inlining imports.
- [ ] Making eager modules in apps and packages lazy.
- [ ] Using dynamic `import(...)` in the right places.

View File

@@ -0,0 +1,189 @@
var hasOwn = Object.prototype.hasOwnProperty;
var dbPromise;
var canUseCache =
// The server doesn't benefit from dynamic module fetching, and almost
// certainly doesn't support IndexedDB.
Meteor.isClient &&
// Cordova bundles all modules into the monolithic initial bundle, so
// the dynamic module cache won't be necessary.
! Meteor.isCordova &&
// Caching can be confusing in development, and is designed to be a
// transparent optimization for production performance.
Meteor.isProduction;
function getIDB() {
if (typeof indexedDB !== "undefined") return indexedDB;
if (typeof webkitIndexedDB !== "undefined") return webkitIndexedDB;
if (typeof mozIndexedDB !== "undefined") return mozIndexedDB;
if (typeof OIndexedDB !== "undefined") return OIndexedDB;
if (typeof msIndexedDB !== "undefined") return msIndexedDB;
}
function withDB(callback) {
dbPromise = dbPromise || new Promise(function (resolve, reject) {
var idb = getIDB();
if (! idb) {
throw new Error("IndexedDB not available");
}
// Incrementing the version number causes all existing object stores
// to be deleted and recreates those specified by objectStoreMap.
var request = idb.open("MeteorDynamicImportCache", 2);
request.onupgradeneeded = function (event) {
var db = event.target.result;
// It's fine to delete existing object stores since onupgradeneeded
// is only called when we change the DB version number, and the data
// we're storing is disposable/reconstructible.
Array.from(db.objectStoreNames).forEach(db.deleteObjectStore, db);
Object.keys(objectStoreMap).forEach(function (name) {
db.createObjectStore(name, objectStoreMap[name]);
});
};
request.onerror = makeOnError(reject, "indexedDB.open");
request.onsuccess = function (event) {
resolve(event.target.result);
};
});
return dbPromise.then(callback, function (error) {
return callback(null);
});
}
var objectStoreMap = {
sourcesByVersion: { keyPath: "version" }
};
function makeOnError(reject, source) {
return function (event) {
reject(new Error(
"IndexedDB failure in " + source + " " +
JSON.stringify(event.target)
));
// Returning true from an onerror callback function prevents an
// InvalidStateError in Firefox during Private Browsing. Silencing
// that error is safe because we handle the error more gracefully by
// passing it to the Promise reject function above.
// https://github.com/meteor/meteor/issues/8697
return true;
};
}
var checkCount = 0;
exports.checkMany = function (versions) {
var ids = Object.keys(versions);
var sourcesById = Object.create(null);
// Initialize sourcesById with null values to indicate all sources are
// missing (unless replaced with actual sources below).
ids.forEach(function (id) {
sourcesById[id] = null;
});
if (! canUseCache) {
return Promise.resolve(sourcesById);
}
return withDB(function (db) {
if (! db) {
// We thought we could used IndexedDB, but something went wrong
// while opening the database, so err on the side of safety.
return sourcesById;
}
var txn = db.transaction([
"sourcesByVersion"
], "readonly");
var sourcesByVersion = txn.objectStore("sourcesByVersion");
++checkCount;
function finish() {
--checkCount;
return sourcesById;
}
return Promise.all(ids.map(function (id) {
return new Promise(function (resolve, reject) {
var version = versions[id];
if (version) {
var sourceRequest = sourcesByVersion.get(versions[id]);
sourceRequest.onerror = makeOnError(reject, "sourcesByVersion.get");
sourceRequest.onsuccess = function (event) {
var result = event.target.result;
if (result) {
sourcesById[id] = result.source;
}
resolve();
};
} else resolve();
});
})).then(finish, finish);
});
};
var pendingVersionsAndSourcesById = Object.create(null);
exports.setMany = function (versionsAndSourcesById) {
if (canUseCache) {
Object.assign(
pendingVersionsAndSourcesById,
versionsAndSourcesById
);
// Delay the call to flushSetMany so that it doesn't contribute to the
// amount of time it takes to call module.dynamicImport.
if (! flushSetMany.timer) {
flushSetMany.timer = setTimeout(flushSetMany, 100);
}
}
};
function flushSetMany() {
if (checkCount > 0) {
// If checkMany is currently underway, postpone the flush until later,
// since updating the cache is less important than reading from it.
return flushSetMany.timer = setTimeout(flushSetMany, 100);
}
flushSetMany.timer = null;
var versionsAndSourcesById = pendingVersionsAndSourcesById;
pendingVersionsAndSourcesById = Object.create(null);
return withDB(function (db) {
if (! db) {
// We thought we could used IndexedDB, but something went wrong
// while opening the database, so err on the side of safety.
return;
}
var setTxn = db.transaction([
"sourcesByVersion"
], "readwrite");
var sourcesByVersion = setTxn.objectStore("sourcesByVersion");
return Promise.all(
Object.keys(versionsAndSourcesById).map(function (id) {
var info = versionsAndSourcesById[id];
return new Promise(function (resolve, reject) {
var request = sourcesByVersion.put({
version: info.version,
source: info.source
});
request.onerror = makeOnError(reject, "sourcesByVersion.put");
request.onsuccess = resolve;
});
})
);
});
}

View File

@@ -0,0 +1,158 @@
var Module = module.constructor;
var cache = require("./cache.js");
// Call module.dynamicImport(id) to fetch a module and any/all of its
// dependencies that have not already been fetched, and evaluate them as
// soon as they arrive. This runtime API makes it very easy to implement
// ECMAScript dynamic import(...) syntax.
Module.prototype.dynamicImport = function (id) {
var module = this;
return module.prefetch(id).then(function () {
return getNamespace(module, id);
});
};
// Called by Module.prototype.prefetch if there are any missing dynamic
// modules that need to be fetched.
meteorInstall.fetch = function (ids) {
var tree = Object.create(null);
var versions = Object.create(null);
var dynamicVersions = require("./dynamic-versions.js");
var missing;
Object.keys(ids).forEach(function (id) {
var version = getFromTree(dynamicVersions, id);
if (version) {
versions[id] = version;
} else {
addToTree(missing = missing || Object.create(null), id, 1);
}
});
return cache.checkMany(versions).then(function (sources) {
Object.keys(sources).forEach(function (id) {
var source = sources[id];
if (source) {
var info = ids[id];
addToTree(tree, id, makeModuleFunction(id, source, info.options));
} else {
addToTree(missing = missing || Object.create(null), id, 1);
}
});
return missing && fetchMissing(missing).then(function (results) {
var versionsAndSourcesById = Object.create(null);
var flatResults = flattenModuleTree(results);
Object.keys(flatResults).forEach(function (id) {
var source = flatResults[id];
var info = ids[id];
addToTree(tree, id, makeModuleFunction(id, source, info.options));
var version = getFromTree(dynamicVersions, id);
if (version) {
versionsAndSourcesById[id] = {
version: version,
source: source
};
}
});
cache.setMany(versionsAndSourcesById);
});
}).then(function () {
return tree;
});
};
function flattenModuleTree(tree) {
var parts = [""];
var result = Object.create(null);
function walk(t) {
if (t && typeof t === "object") {
Object.keys(t).forEach(function (key) {
parts.push(key);
walk(t[key]);
parts.pop();
});
} else if (typeof t === "string") {
result[parts.join("/")] = t;
}
}
walk(tree);
return result;
}
function makeModuleFunction(id, source, options) {
// By calling (options && options.eval || eval) in a wrapper function,
// we delay the cost of parsing and evaluating the module code until the
// module is first imported.
return function () {
// If an options.eval function was provided in the second argument to
// meteorInstall when this bundle was first installed, use that
// function to parse and evaluate the dynamic module code in the scope
// of the package. Otherwise fall back to indirect (global) eval.
return (options && options.eval || eval)(
// Wrap the function(require,exports,module){...} expression in
// parentheses to force it to be parsed as an expression.
"(" + source + ")\n//# sourceURL=" + id
).apply(this, arguments);
};
}
function fetchMissing(missingTree) {
// Update lastFetchMissingPromise immediately, without waiting for
// the results to be delivered.
return new Promise(function (resolve, reject) {
Meteor.call(
"__dynamicImport",
missingTree,
function (error, resultsTree) {
error ? reject(error) : resolve(resultsTree);
}
);
});
}
function getFromTree(tree, id) {
id.split("/").every(function (part) {
return ! part || (tree = tree[part]);
});
return tree;
}
function addToTree(tree, id, value) {
var parts = id.split("/");
var lastIndex = parts.length - 1;
parts.forEach(function (part, i) {
if (part) {
tree = tree[part] = tree[part] ||
(i < lastIndex ? Object.create(null) : value);
}
});
}
function getNamespace(module, id) {
var namespace;
module.watch(module.require(id), {
"*": function (ns) {
namespace = ns;
}
});
// This helps with Babel interop, since we're not just returning the
// module.exports object.
Object.defineProperty(namespace, "__esModule", {
value: true,
enumerable: false
});
return namespace;
}

View File

@@ -0,0 +1,4 @@
// This magic double-underscored identifier gets replaced in
// tools/isobuild/bundler.js with a tree of hashes of all dynamic
// modules, for use in client.js and cache.js.
module.exports = __DYNAMIC_VERSIONS__;

View File

@@ -0,0 +1,23 @@
Package.describe({
name: "dynamic-import",
version: "0.1.0",
summary: "Runtime support for Meteor 1.5 dynamic import(...) syntax",
documentation: "README.md"
});
Package.onUse(function (api) {
// Do not allow this package to be used in pre-Meteor 1.5 apps.
api.use("isobuild:dynamic-import@1.5.0");
// Modify browser policy only if browser-policy packages are used.
api.use("browser-policy-content", { weak: true });
api.use("modules");
api.use("promise");
api.use("ddp");
api.use("check");
api.use("ecmascript", "server");
api.mainModule("client.js", "client");
api.mainModule("server.js", "server");
});

View File

@@ -0,0 +1,20 @@
const bpc = Package["browser-policy-content"];
const BP = bpc && bpc.BrowserPolicy;
const BPc = BP && BP.content;
if (BPc) {
// The ability to evaluate new code is essential for loading dynamic
// modules. Without eval, we would be forced to load modules using
// <script src=...> tags, and then there would be no way to save those
// modules to a local cache (or load them from the cache) without the
// unique response caching abilities of service workers, which are not
// available in all browsers, and cannot be polyfilled in a way that
// satisfies Content Security Policy eval restrictions. Moreover, eval
// allows us to evaluate dynamic module code in the original package
// scope, which would never be possible using <script> tags. If you're
// deploying an app in an environment that demands a Content Security
// Policy that forbids eval, your only option is to bundle all dynamic
// modules in the initial bundle. Fortunately, that works perfectly
// well; you just won't get the performance benefits of dynamic module
// fetching.
BPc.allowEval();
}

View File

@@ -0,0 +1,76 @@
import assert from "assert";
import { readFileSync } from "fs";
import {
join as pathJoin,
normalize as pathNormalize,
} from "path";
import { check } from "meteor/check";
import "./security.js";
import "./client.js";
const hasOwn = Object.prototype.hasOwnProperty;
Object.keys(dynamicImportInfo).forEach(platform => {
const info = dynamicImportInfo[platform];
if (info.dynamicRoot) {
info.dynamicRoot = pathNormalize(info.dynamicRoot);
}
});
Meteor.methods({
__dynamicImport(tree) {
check(tree, Object);
this.unblock();
const platform = this.connection ? "web.browser" : "server";
const pathParts = [];
function walk(node) {
if (node && typeof node === "object") {
Object.keys(node).forEach(name => {
pathParts.push(name);
node[name] = walk(node[name]);
assert.strictEqual(pathParts.pop(), name);
});
} else {
return read(pathParts, platform);
}
return node;
}
return walk(tree);
}
});
function read(pathParts, platform) {
const { dynamicRoot } = dynamicImportInfo[platform];
const absPath = pathNormalize(pathJoin(dynamicRoot, ...pathParts));
if (! absPath.startsWith(dynamicRoot)) {
throw new Meteor.Error("bad dynamic module path");
}
const cache = getCache(platform);
return hasOwn.call(cache, absPath)
? cache[absPath]
: cache[absPath] = readFileSync(absPath, "utf8");
}
const cachesByPlatform = Object.create(null);
function getCache(platform) {
return hasOwn.call(cachesByPlatform, platform)
? cachesByPlatform[platform]
: cachesByPlatform[platform] = Object.create(null);
}
process.on("message", msg => {
// The cache for the "web.browser" platform needs to be discarded
// whenever a client-only refresh occurs, so that new client code does
// not receive stale module data from __dynamicImport. This code handles
// the same message listened for by the autoupdate package.
if (msg && msg.refresh === "client") {
delete cachesByPlatform["web.browser"];
}
});

View File

@@ -0,0 +1,6 @@
# ecmascript-runtime-client
[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/ecmascript-runtime-client) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/ecmascript-runtime-client)
***
[![Build Status](https://travis-ci.org/meteor/ecmascript-runtime.svg?branch=master)](https://travis-ci.org/meteor/ecmascript-runtime)
Polyfills for new ECMAScript 2015 APIs like Map and Set

View File

@@ -0,0 +1,20 @@
Package.describe({
name: "ecmascript-runtime-client",
version: "0.4.1",
summary: "Polyfills for new ECMAScript 2015 APIs like Map and Set",
git: "https://github.com/meteor/meteor/tree/devel/packages/ecmascript-runtime-client",
documentation: "README.md"
});
Package.onUse(function(api) {
// If the es5-shim package is installed, make sure it loads before
// ecmascript-runtime-server, since the runtime uses some ES5 APIs like
// Object.defineProperties that are buggy in older browsers.
api.use("es5-shim", { weak: true });
api.use("modules", "client");
api.use("promise", "client");
api.mainModule("runtime.js", "client");
api.export("Symbol", "client");
api.export("Map", "client");
api.export("Set", "client");
});

View File

@@ -0,0 +1,52 @@
try {
require("core-js/modules/es6.symbol");
require("core-js/modules/es6.map");
require("core-js/modules/es6.set");
var core = function () {
try {
return require("core-js/modules/_core");
} catch (e) {
// Older versions of core-js had a different file layout.
return require("core-js/modules/$.core");
}
}();
} catch (e) {
throw new Error([
"The core-js npm package could not be found in your node_modules ",
"directory. Please run the following command to install it:",
"",
" meteor npm install --save core-js",
""
].join("\n"));
}
Symbol = exports.Symbol = core.Symbol;
Map = exports.Map = core.Map;
Set = exports.Set = core.Set;
// ECMAScript 2015 polyfills.
require("core-js/es6/array");
require("core-js/es6/function");
require("core-js/es6/math");
require("core-js/es6/object");
require("core-js/es6/string");
require("core-js/es6/weak-map");
require("core-js/es6/weak-set");
// ECMAScript 2017 polyfills.
require("core-js/es7/array");
require("core-js/es7/object");
// We want everything from the core-js/es6/number module except
// es6.number.constructor.
require('core-js/modules/es6.number.epsilon');
require('core-js/modules/es6.number.is-finite');
require('core-js/modules/es6.number.is-integer');
require('core-js/modules/es6.number.is-nan');
require('core-js/modules/es6.number.is-safe-integer');
require('core-js/modules/es6.number.max-safe-integer');
require('core-js/modules/es6.number.min-safe-integer');
require('core-js/modules/es6.number.parse-float');
require('core-js/modules/es6.number.parse-int');

View File

@@ -0,0 +1,9 @@
{
"dependencies": {
"core-js": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.4.1.tgz",
"from": "core-js@2.4.1"
}
}
}

View File

@@ -0,0 +1,6 @@
# ecmascript-runtime-server
[Source code of released version](https://github.com/meteor/meteor/tree/master/packages/ecmascript-runtime-server) | [Source code of development version](https://github.com/meteor/meteor/tree/devel/packages/ecmascript-runtime-server)
***
[![Build Status](https://travis-ci.org/meteor/ecmascript-runtime.svg?branch=master)](https://travis-ci.org/meteor/ecmascript-runtime)
Polyfills for new ECMAScript 2015 APIs like Map and Set

View File

@@ -0,0 +1,23 @@
Package.describe({
name: "ecmascript-runtime-server",
version: "0.4.1",
summary: "Polyfills for new ECMAScript 2015 APIs like Map and Set",
git: "https://github.com/meteor/meteor/tree/devel/packages/ecmascript-runtime-client",
documentation: "README.md"
});
Npm.depends({
"core-js": "2.4.1"
});
Package.onUse(function(api) {
// If the es5-shim package is installed, make sure it loads before
// ecmascript-runtime-server, since the runtime uses some ES5 APIs like
// Object.defineProperties that are buggy in older browsers.
api.use("es5-shim", { weak: true });
api.use(["modules", "promise"], "server");
api.mainModule("runtime.js", "server");
api.export("Symbol", "server");
api.export("Map", "server");
api.export("Set", "server");
});

View File

@@ -0,0 +1,71 @@
// The ecmascript-runtime-server package depends on its own copy of
// core-js using Npm.depends, so we don't have to check that core-js is
// available (as we do in ecmascript-runtime-client/runtime.js).
require("core-js/modules/es6.symbol");
require("core-js/modules/es6.map");
require("core-js/modules/es6.set");
var core = require("core-js/modules/_core");
Symbol = exports.Symbol = core.Symbol;
Map = exports.Map = core.Map;
Set = exports.Set = core.Set;
// List of polyfills generated by babel-preset-env with the following
// .babelrc configuration:
//
// {
// "presets": [
// ["env", {
// "targets": {
// "node": 4
// },
// "polyfill": true,
// "useBuiltIns": true
// }]
// ]
// }
//
// Note that the es6.reflect.* and es6.typed.* modules have been commented
// out for bundle size reasons.
// require("core-js/modules/es6.typed.array-buffer");
// require("core-js/modules/es6.typed.int8-array");
// require("core-js/modules/es6.typed.uint8-array");
// require("core-js/modules/es6.typed.uint8-clamped-array");
// require("core-js/modules/es6.typed.int16-array");
// require("core-js/modules/es6.typed.uint16-array");
// require("core-js/modules/es6.typed.int32-array");
// require("core-js/modules/es6.typed.uint32-array");
// require("core-js/modules/es6.typed.float32-array");
// require("core-js/modules/es6.typed.float64-array");
require("core-js/modules/es6.weak-map");
require("core-js/modules/es6.weak-set");
// require("core-js/modules/es6.reflect.apply");
// require("core-js/modules/es6.reflect.construct");
// require("core-js/modules/es6.reflect.define-property");
// require("core-js/modules/es6.reflect.delete-property");
// require("core-js/modules/es6.reflect.get");
// require("core-js/modules/es6.reflect.get-own-property-descriptor");
// require("core-js/modules/es6.reflect.get-prototype-of");
// require("core-js/modules/es6.reflect.has");
// require("core-js/modules/es6.reflect.is-extensible");
// require("core-js/modules/es6.reflect.own-keys");
// require("core-js/modules/es6.reflect.prevent-extensions");
// require("core-js/modules/es6.reflect.set");
// require("core-js/modules/es6.reflect.set-prototype-of");
require("core-js/modules/es6.function.bind");
require("core-js/modules/es6.function.name");
require("core-js/modules/es6.function.has-instance");
require("core-js/modules/es6.regexp.flags");
require("core-js/modules/es6.regexp.match");
require("core-js/modules/es6.regexp.replace");
require("core-js/modules/es6.regexp.split");
require("core-js/modules/es6.regexp.search");
require("core-js/modules/es6.array.from");
require("core-js/modules/es7.array.includes");
require("core-js/modules/es7.object.values");
require("core-js/modules/es7.object.entries");
require("core-js/modules/es7.object.get-own-property-descriptors");
require("core-js/modules/es7.string.pad-start");
require("core-js/modules/es7.string.pad-end");

View File

@@ -1,14 +0,0 @@
{
"dependencies": {
"core-js": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.4.1.tgz",
"from": "core-js@2.4.1"
},
"meteor-ecmascript-runtime": {
"version": "0.2.9",
"resolved": "https://registry.npmjs.org/meteor-ecmascript-runtime/-/meteor-ecmascript-runtime-0.2.9.tgz",
"from": "meteor-ecmascript-runtime@0.2.9"
}
}
}

View File

@@ -1,29 +1,14 @@
Package.describe({
name: "ecmascript-runtime",
version: "0.3.15",
version: "0.4.1",
summary: "Polyfills for new ECMAScript 2015 APIs like Map and Set",
git: "https://github.com/meteor/ecmascript-runtime",
documentation: "README.md"
});
Npm.depends({
"meteor-ecmascript-runtime": "0.2.9",
});
Package.onUse(function(api) {
// If the es5-shim package is installed, make sure it loads before
// ecmascript-runtime, since ecmascript-runtime uses some ES5 APIs like
// Object.defineProperties that are buggy in older browsers.
api.use("es5-shim@4.6.13", { weak: true });
api.use("modules@0.7.5");
api.use("promise@0.8.3");
api.mainModule("runtime.js");
api.export("Symbol");
api.export("Map");
api.export("Set");
api.imply("ecmascript-runtime-client", "client");
api.imply("ecmascript-runtime-server", "server");
});
Package.onTest(function(api) {

View File

@@ -1,6 +1,6 @@
Package.describe({
name: 'ecmascript',
version: '0.7.3',
version: '0.8.0',
summary: 'Compiler plugin that supports ES2015+ in all .js files',
documentation: 'README.md'
});

View File

@@ -1,6 +1,6 @@
Package.describe({
summary: "Facebook OAuth flow",
version: "1.3.0"
version: "1.3.1"
});
Package.onUse(function(api) {

View File

@@ -5,52 +5,44 @@
// trying to use it throws.
// Accessing window.localStorage can also immediately throw an error in IE (#1291).
var hasOwn = Object.prototype.hasOwnProperty;
var key = '_localstorage_test_' + Random.id();
var retrieved;
var storage;
try {
if (window.localStorage) {
window.localStorage.setItem(key, key);
retrieved = window.localStorage.getItem(key);
window.localStorage.removeItem(key);
storage = global.localStorage;
if (storage) {
storage.setItem(key, key);
retrieved = storage.getItem(key);
storage.removeItem(key);
}
} catch (e) {
// ... ignore
}
} catch (ignored) {}
if (key === retrieved) {
Meteor._localStorage = {
getItem: function (key) {
return window.localStorage.getItem(key);
},
setItem: function (key, value) {
window.localStorage.setItem(key, value);
},
removeItem: function (key) {
window.localStorage.removeItem(key);
}
};
Meteor._localStorage = storage;
}
if (!Meteor._localStorage) {
Meteor._debug(
"You are running a browser with no localStorage or userData "
+ "support. Logging in from one tab will not cause another "
+ "tab to be logged in.");
Meteor._localStorage = {
_data: {},
if (! Meteor._localStorage) {
if (Meteor.isClient) {
Meteor._debug(
"You are running a browser with no localStorage or userData "
+ "support. Logging in from one tab will not cause another "
+ "tab to be logged in.");
}
Meteor._localStorage = Object.create({
setItem: function (key, val) {
this._data[key] = val;
this[key] = val;
},
removeItem: function (key) {
delete this._data[key];
delete this[key];
},
getItem: function (key) {
var value = this._data[key];
if (value === undefined)
return null;
else
return value;
return hasOwn.call(this, key) ? this[key] : null;
}
};
});
}

View File

@@ -1,17 +1,15 @@
Package.describe({
summary: "Simulates local storage on IE 6,7 using userData",
version: "1.0.12"
version: "1.1.0"
});
Package.onUse(function (api) {
api.use('random', 'client');
api.addFiles('localstorage.js', 'client');
api.use('random');
api.addFiles('localstorage.js');
});
Package.onTest(function (api) {
api.use('localstorage', 'client');
api.use('localstorage');
api.use('tinytest');
api.addFiles('localstorage_tests.js', 'client');
api.addFiles('localstorage_tests.js');
});

View File

@@ -1,6 +1,6 @@
Package.describe({
name: 'meteor-base',
version: '1.0.4',
version: '1.1.0',
// Brief, one-line summary of the package.
summary: 'Packages that every Meteor app needs',
// By default, Meteor will default to using README.md for documentation.
@@ -25,6 +25,9 @@ Package.onUse(function(api) {
'ddp',
'livedata', // XXX COMPAT WITH PACKAGES BUILT FOR 0.9.0.
// Runtime support for Meteor 1.5 dynamic import(...) syntax.
'dynamic-import',
// Push code changes to the client and automatically reload the page
'hot-code-push'
]);

View File

@@ -1,6 +1,6 @@
Package.describe({
summary: "The Meteor command-line tool",
version: '1.4.4_3'
version: '1.5.0'
});
Package.includeTool();

View File

@@ -1,59 +1,14 @@
{
"dependencies": {
"align-text": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz",
"from": "align-text@>=0.1.3 <0.2.0"
"commander": {
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz",
"from": "commander@>=2.9.0 <2.10.0"
},
"camelcase": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz",
"from": "camelcase@>=1.0.2 <2.0.0"
},
"center-align": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz",
"from": "center-align@>=0.1.1 <0.2.0"
},
"cliui": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz",
"from": "cliui@>=2.1.0 <3.0.0"
},
"decamelize": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
"from": "decamelize@>=1.0.0 <2.0.0"
},
"is-buffer": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz",
"from": "is-buffer@>=1.0.2 <2.0.0"
},
"kind-of": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.1.0.tgz",
"from": "kind-of@>=3.0.2 <4.0.0"
},
"lazy-cache": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz",
"from": "lazy-cache@>=1.0.3 <2.0.0"
},
"longest": {
"graceful-readlink": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",
"from": "longest@>=1.0.1 <2.0.0"
},
"repeat-string": {
"version": "1.6.1",
"resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
"from": "repeat-string@>=1.5.2 <2.0.0"
},
"right-align": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz",
"from": "right-align@>=0.1.1 <0.2.0"
"resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz",
"from": "graceful-readlink@>=1.0.0"
},
"source-map": {
"version": "0.5.6",
@@ -61,29 +16,9 @@
"from": "source-map@>=0.5.1 <0.6.0"
},
"uglify-js": {
"version": "2.8.21",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.21.tgz",
"from": "uglify-js@2.8.21"
},
"uglify-to-browserify": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz",
"from": "uglify-to-browserify@>=1.0.0 <1.1.0"
},
"window-size": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz",
"from": "window-size@0.1.0"
},
"wordwrap": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz",
"from": "wordwrap@0.0.2"
},
"yargs": {
"version": "3.10.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz",
"from": "yargs@>=3.10.0 <3.11.0"
"version": "3.0.13",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.0.13.tgz",
"from": "uglify-js@3.0.13"
}
}
}

View File

@@ -5,14 +5,20 @@ meteorJsMinify = function (source) {
uglify = uglify || Npm.require("uglify-js");
try {
result.code = uglify.minify(source, {
fromString: true,
var uglifyResult = uglify.minify(source, {
compress: {
drop_debugger: false,
unused: false,
dead_code: false
}
}).code;
});
if (typeof uglifyResult.code === "string") {
result.code = uglifyResult.code;
} else {
throw uglifyResult.error ||
new Error("unknown uglify.minify failure");
}
} catch (e) {
// Although Babel.minify can handle a wider variety of ECMAScript

View File

@@ -1,10 +1,10 @@
Package.describe({
summary: "JavaScript minifier",
version: "2.0.0"
version: "2.1.0"
});
Npm.depends({
"uglify-js": "2.8.21"
"uglify-js": "3.0.13"
});
Package.onUse(function (api) {

View File

@@ -1,6 +1,6 @@
Package.describe({
summary: "Meteor's client-side datastore: a port of MongoDB to Javascript",
version: '1.0.23'
version: '1.2.0'
});
Package.onUse(function (api) {

View File

@@ -1,9 +1,9 @@
{
"dependencies": {
"install": {
"version": "0.8.8",
"resolved": "https://registry.npmjs.org/install/-/install-0.8.8.tgz",
"from": "install@0.8.8"
"version": "0.10.1",
"resolved": "https://registry.npmjs.org/install/-/install-0.10.1.tgz",
"from": "install@0.10.1"
}
}
}

View File

@@ -0,0 +1,5 @@
// On the client, make package resolution prefer the "browser" field of
// package.json files to the "main" field.
makeInstallerOptions.browser = true;
meteorInstall = makeInstaller(makeInstallerOptions);

View File

@@ -1,86 +0,0 @@
var options = {};
var hasOwn = options.hasOwnProperty;
// RegExp matching strings that don't start with a `.` or a `/`.
var topLevelIdPattern = /^[^./]/;
if (typeof Profile === "function" &&
process.env.METEOR_PROFILE) {
options.wrapRequire = function (require) {
return Profile(function (id) {
return "require(" + JSON.stringify(id) + ")";
}, require);
};
}
// On the client, make package resolution prefer the "browser" field of
// package.json files to the "main" field.
options.browser = Meteor.isClient;
// This function will be called whenever a module identifier that hasn't
// been installed is required. For backwards compatibility, and so that we
// can require binary dependencies on the server, we implement the
// fallback in terms of Npm.require.
options.fallback = function (id, parentId, error) {
// For simplicity, we honor only top-level module identifiers here.
// We could try to honor relative and absolute module identifiers by
// somehow combining `id` with `dir`, but we'd have to be really careful
// that the resulting modules were located in a known directory (not
// some arbitrary location on the file system), and we only really need
// the fallback for dependencies installed in node_modules directories.
if (topLevelIdPattern.test(id)) {
if (typeof Npm === "object" &&
typeof Npm.require === "function") {
return Npm.require(id);
}
}
throw error;
};
options.fallback.resolve = function (id, parentId, error) {
if (Meteor.isServer &&
topLevelIdPattern.test(id)) {
// Allow any top-level identifier to resolve to itself on the server,
// so that options.fallback can have a chance to handle it.
return id;
}
throw error;
};
meteorInstall = makeInstaller(options);
var Mp = meteorInstall.Module.prototype;
if (Meteor.isServer) {
Mp.useNode = function () {
if (typeof npmRequire !== "function") {
// Can't use Node if npmRequire is not defined.
return false;
}
var parts = this.id.split("/");
var start = 0;
if (parts[start] === "") ++start;
if (parts[start] === "node_modules" &&
parts[start + 1] === "meteor") {
start += 2;
}
if (parts.indexOf("node_modules", start) < 0) {
// Don't try to use Node for modules that aren't in node_modules
// directories.
return false;
}
try {
npmRequire.resolve(this.id);
} catch (e) {
return false;
}
this.exports = npmRequire(this.id);
return true;
};
}

View File

@@ -0,0 +1,10 @@
makeInstallerOptions = {};
if (typeof Profile === "function" &&
process.env.METEOR_PROFILE) {
makeInstallerOptions.wrapRequire = function (require) {
return Profile(function (id) {
return "require(" + JSON.stringify(id) + ")";
}, require);
};
}

View File

@@ -1,13 +1,13 @@
Package.describe({
name: "modules-runtime",
version: "0.7.10",
version: "0.8.0",
summary: "CommonJS module system",
git: "https://github.com/benjamn/install",
documentation: "README.md"
});
Npm.depends({
install: "0.8.8"
install: "0.10.1"
});
Package.onUse(function(api) {
@@ -18,7 +18,10 @@ Package.onUse(function(api) {
bare: true
});
api.addFiles("modules-runtime.js");
api.addFiles("options.js");
api.addFiles("client.js", "client");
api.addFiles("server.js", "server");
api.export("meteorInstall");
});

View File

@@ -0,0 +1,67 @@
// RegExp matching strings that don't start with a `.` or a `/`.
var topLevelIdPattern = /^[^./]/;
// This function will be called whenever a module identifier that hasn't
// been installed is required. For backwards compatibility, and so that we
// can require binary dependencies on the server, we implement the
// fallback in terms of Npm.require.
makeInstallerOptions.fallback = function (id, parentId, error) {
// For simplicity, we honor only top-level module identifiers here.
// We could try to honor relative and absolute module identifiers by
// somehow combining `id` with `dir`, but we'd have to be really careful
// that the resulting modules were located in a known directory (not
// some arbitrary location on the file system), and we only really need
// the fallback for dependencies installed in node_modules directories.
if (topLevelIdPattern.test(id)) {
if (typeof Npm === "object" &&
typeof Npm.require === "function") {
return Npm.require(id);
}
}
throw error;
};
makeInstallerOptions.fallback.resolve = function (id, parentId, error) {
if (topLevelIdPattern.test(id)) {
// Allow any top-level identifier to resolve to itself on the server,
// so that makeInstallerOptions.fallback has a chance to handle it.
return id;
}
throw error;
};
meteorInstall = makeInstaller(makeInstallerOptions);
var Module = meteorInstall.Module;
Module.prototype.useNode = function () {
if (typeof npmRequire !== "function") {
// Can't use Node if npmRequire is not defined.
return false;
}
var parts = this.id.split("/");
var start = 0;
if (parts[start] === "") ++start;
if (parts[start] === "node_modules" &&
parts[start + 1] === "meteor") {
start += 2;
}
if (parts.indexOf("node_modules", start) < 0) {
// Don't try to use Node for modules that aren't in node_modules
// directories.
return false;
}
try {
npmRequire.resolve(this.id);
} catch (e) {
return false;
}
this.exports = npmRequire(this.id);
return true;
};

View File

@@ -5,10 +5,30 @@
"resolved": "https://registry.npmjs.org/acorn/-/acorn-5.0.3.tgz",
"from": "acorn@>=5.0.0 <5.1.0"
},
"minipass": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.0.2.tgz",
"from": "minipass@>=2.0.0 <3.0.0"
},
"minizlib": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.0.3.tgz",
"from": "minizlib@>=1.0.3 <2.0.0"
},
"reify": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/reify/-/reify-0.7.4.tgz",
"from": "reify@0.7.4"
"version": "0.11.21",
"resolved": "https://registry.npmjs.org/reify/-/reify-0.11.21.tgz",
"from": "reify@0.11.21"
},
"semver": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
"from": "semver@>=5.3.0 <6.0.0"
},
"yallist": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.2.tgz",
"from": "yallist@>=3.0.0 <4.0.0"
}
}
}

View File

@@ -1,3 +0,0 @@
try {
Buffer = global.Buffer || require("buffer").Buffer;
} catch (noBuffer) {}

View File

@@ -1,6 +1,5 @@
require("./install-packages.js");
require("./stubs.js");
require("./buffer.js");
require("./process.js");
require("./reify.js");

View File

@@ -8,10 +8,10 @@ function install(name, mainModule) {
// /node_modules/meteor/<name>/index.js, in the rare but possible event
// that the package contains a file called index.js (#6590).
if (mainModule) {
meteorDir[name + ".js"] = [mainModule, function (require, e, module) {
module.exports = require(mainModule);
}];
if (typeof mainModule === "string") {
// Set up an alias from /node_modules/meteor/<package>.js to the main
// module, e.g. meteor/<package>/index.js.
meteorDir[name + ".js"] = mainModule;
} else {
// back compat with old Meteor packages
meteorDir[name + ".js"] = function (r, e, module) {

View File

@@ -1,12 +1,12 @@
Package.describe({
name: "modules",
version: "0.8.2",
version: "0.9.0",
summary: "CommonJS module system",
documentation: "README.md"
});
Npm.depends({
reify: "0.7.4"
reify: "0.11.21"
});
Package.onUse(function(api) {
@@ -15,6 +15,5 @@ Package.onUse(function(api) {
api.mainModule("client.js", "client");
api.mainModule("server.js", "server");
api.export("meteorInstall");
api.export("Buffer");
api.export("process");
});

View File

@@ -1,5 +1,5 @@
var Module = module.constructor;
require("reify/lib/runtime").enable(Module);
var Mp = Module.prototype;
require("reify/lib/runtime").enable(Mp);
Mp.importSync = Mp.importSync || Mp.import;
Mp.import = Mp.import || Mp.importSync;

View File

@@ -1,4 +1,3 @@
require("./install-packages.js");
require("./buffer.js");
require("./process.js");
require("./reify.js");

View File

@@ -9,7 +9,7 @@
Package.describe({
summary: "Adaptor for using MongoDB and Minimongo over DDP",
version: '1.1.17'
version: '1.1.18'
});
Npm.depends({

View File

@@ -0,0 +1 @@
node_modules

View File

@@ -0,0 +1,7 @@
This directory and the files immediately inside it are automatically generated
when you change this package's NPM dependencies. Commit the files in this
directory (npm-shrinkwrap.json, .gitignore, and this README) to source control
so that others run the same versions of sub-dependencies.
You should NOT check in the node_modules directory that Meteor automatically
creates; if you are using git, the .gitignore file tells git to ignore it.

View File

@@ -0,0 +1,189 @@
{
"dependencies": {
"commander": {
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz",
"from": "commander@>=2.0.0 <3.0.0"
},
"d3": {
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/d3/-/d3-4.8.0.tgz",
"from": "d3@4.8.0"
},
"d3-array": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.0.tgz",
"from": "d3-array@1.2.0"
},
"d3-axis": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-1.0.6.tgz",
"from": "d3-axis@1.0.6"
},
"d3-brush": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-1.0.4.tgz",
"from": "d3-brush@1.0.4"
},
"d3-chord": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-1.0.4.tgz",
"from": "d3-chord@1.0.4"
},
"d3-collection": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.3.tgz",
"from": "d3-collection@1.0.3"
},
"d3-color": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.0.3.tgz",
"from": "d3-color@1.0.3"
},
"d3-dispatch": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.3.tgz",
"from": "d3-dispatch@1.0.3"
},
"d3-drag": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-1.0.4.tgz",
"from": "d3-drag@1.0.4"
},
"d3-dsv": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-1.0.5.tgz",
"from": "d3-dsv@1.0.5"
},
"d3-ease": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.3.tgz",
"from": "d3-ease@1.0.3"
},
"d3-force": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/d3-force/-/d3-force-1.0.6.tgz",
"from": "d3-force@1.0.6"
},
"d3-format": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.2.0.tgz",
"from": "d3-format@1.2.0"
},
"d3-geo": {
"version": "1.6.3",
"resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.6.3.tgz",
"from": "d3-geo@1.6.3"
},
"d3-hierarchy": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.4.tgz",
"from": "d3-hierarchy@1.1.4"
},
"d3-interpolate": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.1.4.tgz",
"from": "d3-interpolate@1.1.4"
},
"d3-path": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.5.tgz",
"from": "d3-path@1.0.5"
},
"d3-polygon": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-1.0.3.tgz",
"from": "d3-polygon@1.0.3"
},
"d3-quadtree": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.3.tgz",
"from": "d3-quadtree@1.0.3"
},
"d3-queue": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/d3-queue/-/d3-queue-3.0.5.tgz",
"from": "d3-queue@3.0.5"
},
"d3-random": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/d3-random/-/d3-random-1.0.3.tgz",
"from": "d3-random@1.0.3"
},
"d3-request": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/d3-request/-/d3-request-1.0.5.tgz",
"from": "d3-request@1.0.5"
},
"d3-scale": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-1.0.5.tgz",
"from": "d3-scale@1.0.5"
},
"d3-selection": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.0.5.tgz",
"from": "d3-selection@1.0.5"
},
"d3-shape": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.0.6.tgz",
"from": "d3-shape@1.0.6"
},
"d3-time": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.0.6.tgz",
"from": "d3-time@1.0.6"
},
"d3-time-format": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.0.5.tgz",
"from": "d3-time-format@2.0.5"
},
"d3-timer": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.5.tgz",
"from": "d3-timer@1.0.5"
},
"d3-transition": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-1.0.4.tgz",
"from": "d3-transition@1.0.4"
},
"d3-voronoi": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.2.tgz",
"from": "d3-voronoi@1.1.2"
},
"d3-zoom": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-1.1.4.tgz",
"from": "d3-zoom@1.1.4"
},
"graceful-readlink": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz",
"from": "graceful-readlink@>=1.0.0"
},
"iconv-lite": {
"version": "0.4.17",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.17.tgz",
"from": "iconv-lite@>=0.4.0 <0.5.0"
},
"pretty-bytes": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-4.0.2.tgz",
"from": "pretty-bytes@4.0.2"
},
"rw": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz",
"from": "rw@>=1.0.0 <2.0.0"
},
"xmlhttprequest": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz",
"from": "xmlhttprequest@>=1.0.0 <2.0.0"
}
}
}

View File

@@ -0,0 +1 @@
# bundle-visualizer

View File

@@ -0,0 +1,3 @@
import { prefixedClass } from "./common.js";
export const rootContainer = prefixedClass("rootContainer");
export const mask = prefixedClass("mask");

View File

@@ -0,0 +1,46 @@
import { Meteor } from "meteor/meteor";
import {
classPrefix,
methodNameStats,
packageName,
} from "./common.js";
import * as classes from "./classNames.js";
import "./style.css";
Meteor.startup(() => {
import("./sunburst.js").then(s => main(s.Sunburst));
});
function main(builder) {
const { container, mask } = frameStage();
document.body.appendChild(mask);
document.body.appendChild(container);
Meteor.call(methodNameStats, (error, result) => {
if (error) {
console.error([
`${packageName}: Couldn't load stats for visualization.`,
"Are you using standard-minifier-js >= 2.1.0 as the minifier?",
].join(" "));
return;
}
// Load the JSON, which is `d3-hierarchy` digestible.
if (result) {
new builder({ container }).loadJson(result);
}
});
}
function frameStage() {
// Create the mask which will block out the main application.
const mask = document.createElement("div");
mask.setAttribute("class", `${classPrefix} ${classes.mask}`);
// Create the container which the SVG elements will be drawn into.
const container = document.createElement("div");
container.setAttribute("class", `${classPrefix} ${classes.rootContainer}`);
return { container, mask };
}

View File

@@ -0,0 +1,14 @@
export const packageName = "bundle-visualizer";
export const classPrefix = "meteorBundleVisualizer";
export const methodNameStats = `_meteor/${packageName}/stats`;
export const typeBundle = "bundle";
export const typePackage = "package";
export const typeNodeModules = "node_modules";
function capitalizeFirstLetter(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
export function prefixedClass(className) {
return `${classPrefix}${capitalizeFirstLetter(className)}`;
}

View File

@@ -0,0 +1,23 @@
Package.describe({
version: '1.0.1',
summary: 'Meteor bundle analysis and visualization.',
documentation: 'README.md',
});
Npm.depends({
"d3-selection": "1.0.5",
"d3-shape": "1.0.6",
"d3-hierarchy": "1.1.4",
"d3-transition": "1.0.4",
"pretty-bytes": "4.0.2",
});
Package.onUse(function(api) {
api.use('isobuild:dynamic-import@1.5.0');
api.use([
'ecmascript',
'dynamic-import',
]);
api.mainModule('server.js', 'server');
api.mainModule('client.js', 'client');
});

View File

@@ -0,0 +1,144 @@
import assert from "assert";
import { readFileSync as fsReadFileSync } from "fs";
import { Meteor } from "meteor/meteor";
import { WebAppInternals } from "meteor/webapp";
import {
methodNameStats,
packageName,
typeBundle,
typeNodeModules,
typePackage,
} from "./common.js";
if (Meteor.isProduction) {
console.warn([
`=> The "${packageName}" package is currently enabled. Visit your`,
"application in a web browser to view the client bundle analysis and",
"'meteor remove' the package before building/deploying the final bundle.",
].join(" "));
} else {
console.warn([
"=> In order to provide accurate measurements using minified bundles,",
`the "${packageName}" package requires running 'meteor --production'`,
"to simulate production bundling."
].join(" "));
}
function getStatBundles() {
const statFileFilter = f =>
f.type === "json" &&
f.absolutePath &&
f.absolutePath.endsWith(".stats.json");
// Read the stat file, but if it's in any way unusable just return null.
const readOrNull = file => {
try {
return JSON.parse(fsReadFileSync(file, "utf8"));
} catch (err) {
return null;
}
};
return Object.keys(WebAppInternals.staticFiles)
.map(staticFile => WebAppInternals.staticFiles[staticFile])
.filter(statFileFilter)
.map(statFile => ({
name: statFile.hash,
stats: readOrNull(statFile.absolutePath),
}));
}
function _childModules(node) {
return Object.keys(node)
.map(module => {
const result = {
name: module,
type: typeNodeModules,
};
if (typeof node[module] === "object") {
result.children = _childModules(node[module]);
} else {
result.size = node[module];
}
return result;
});
}
function d3TreeFromStats(stats) {
assert.strictEqual(typeof stats, "object",
"Must pass a stats object");
assert.strictEqual(typeof stats.minifiedBytesByPackage, "object",
"Stats object must contain a `minifiedBytesByPackage` object");
const sizeOrDetail = (name, node) => {
const result = {
name,
type: typePackage,
};
// A non-leaf is: [size (Number), limb (Object)]
// A leaf is size (Number)
if (Array.isArray(node)) {
const [, detail] = node;
result.children = _childModules(detail);
} else {
result.size = node;
}
return result;
};
// Main entry into the stats is the `minifiedBytesByPackage` attribute.
return Object.keys(stats.minifiedBytesByPackage)
.map(name =>
sizeOrDetail(name
// Change the "packages/bundle.js" name to "(bundle)"
.replace(/^[^\/]+\/(.*)\.js$/, "($1)"),
stats.minifiedBytesByPackage[name]));
}
Meteor.methods({
[methodNameStats]() {
const statBundles = getStatBundles();
// Silently return no data if not simulating production.
if (! Meteor.isProduction) {
return null;
}
if (! (statBundles && statBundles.length)) {
throw new Meteor.Error("no-stats-bundles", "Unable to retrieve stats");
}
const validStatBundles = statBundles.filter(statBundle => {
if (statBundle &&
statBundle.stats &&
statBundle.stats.minifier &&
statBundle.stats.minifier.name === "standard-minifier-js" &&
statBundle.stats.minifier.version.startsWith("2.1.")
) {
return true;
}
});
if (! validStatBundles.length) {
throw new Meteor.Error("no-valid-stats", "No valid stats bundles")
}
return {
name: "main",
children: validStatBundles.map((statBundle, index, array) => ({
// TODO: If multiple bundles, could
// show abbr. bundle names with:
// `...${bundle.name.substr(-3)}`,
name: "bundle" + (array.length > 1 ? ` (${index + 1})` : ""),
type: typeBundle,
children: d3TreeFromStats(statBundle.stats),
})),
};
}
});

View File

@@ -0,0 +1,102 @@
.meteorBundleVisualizer.meteorBundleVisualizerMask {
position: fixed;
left: 0;
right: 0;
top: 0;
bottom: 0;
background-color: #cdcdcd;
opacity: 0.5;
}
.meteorBundleVisualizer.meteorBundleVisualizerRootContainer {
position: absolute;
z-index: 99999;
top: 0;
font-family: 'Open Sans', sans-serif;
font-size: 12px;
font-weight: 400;
width: 960px;
height: 700px;
margin-top: 10px;
}
.meteorBundleVisualizer.meteorBundleVisualizerRootContainer
.meteorBundleVisualizerMain
{
float: left;
width: 950px;
}
.meteorBundleVisualizer.meteorBundleVisualizerRootContainer
.meteorBundleVisualizerSequence
{
width: 800px;
height: 70px;
}
.meteorBundleVisualizer.meteorBundleVisualizerRootContainer
.meteorBundleVisualizerSequence text,
{
font-weight: 600;
fill: #fff;
}
.meteorBundleVisualizer.meteorBundleVisualizerRootContainer
.meteorBundleVisualizerChart
{
position: relative;
}
.meteorBundleVisualizer.meteorBundleVisualizerRootContainer
.meteorBundleVisualizerChart path
{
stroke: #fff;
}
.meteorBundleVisualizer.meteorBundleVisualizerRootContainer
.meteorBundleVisualizerExplanation
{
position: absolute;
top: 260px;
left: 405px;
width: 140px;
text-align: center;
color: #000;
z-index: -1;
}
.meteorBundleVisualizer.meteorBundleVisualizerRootContainer
.meteorBundleVisualizerPercentage
{
font-size: 2.5em;
}
.meteorBundleVisualizer.meteorBundleVisualizerRootContainer
.meteorBundleVisualizerBytes
{
font-size: 2em;
font-weight: bold;
}
.meteorBundleVisualizer.meteorBundleVisualizerRootContainer
.meteorBundleVisualizerTrail
{
position: absolute;
font-family: 'Open Sans', sans-serif;
font-size: 12px;
left: 0;
min-width: 180px;
font-weight: bold;
}
.meteorBundleVisualizer.meteorBundleVisualizerRootContainer
.meteorBundleVisualizerTrail .meteorBundleVisualizerTrailSegment
{
height: 40px;
border-radius: 3px;
border: 2px dotted black;
text-align: center;
vertical-align: middle;
line-height: 40px;
margin-bottom: 5px;
}

View File

@@ -0,0 +1,275 @@
/**
Inspired-by, borrowed-from and improved-upon another sundial provided under
the Apache License:
https://bl.ocks.org/kerryrodden/766f8f6d31f645c39f488a0befa1e3c8
Copyright 2013 Google Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import assert from "assert";
import prettyBytes from "pretty-bytes";
// Make a custom "d3" object containing exactly what we need from the
// modularized d3 bundles.
const d3 = Object.assign({},
{ selectAll, select, mouse } = require("d3-selection"),
{ arc } = require("d3-shape"),
{ hierarchy, partition } = require("d3-hierarchy"),
{ keys, entries } = require("d3-collection"),
);
// This is imported only for its side effects, which affect the d3 namespace.
import "d3-transition";
import {
typeBundle,
typePackage,
typeNodeModules,
prefixedClass,
} from "./common.js";
import * as classes from "./classNames.js";
// Dimensions of sunburst.
const width = 950;
const height = 600;
const radius = Math.min(width, height) / 2;
// Mapping of step names to colors.
const DEFAULT_COLORS = {
"_default_": "#ababab",
[typeBundle]: "#de4f4f",
[typePackage]: "#de783b",
[typeNodeModules]: "#7b615c",
"meteor": "#6ab975",
"javascript": "#a173d1",
};
export class Sunburst {
constructor({
container,
colors = DEFAULT_COLORS,
} = {}) {
this.elements = {};
this.colors = colors;
this.totalSize = 0;
assert.strictEqual(typeof container, "object",
"Must pass a 'container' element");
this.elements.container = d3.select(container);
this.elements.main =
this.elements.container
.append("div")
.attr("class", prefixedClass("main"));
this.elements.sequence =
this.elements.main
.append("div")
.attr("class", prefixedClass("sequence"));
this.elements.chart =
this.elements.main
.append("div")
.attr("class", prefixedClass("chart"));
this.elements.explanation =
this.elements.chart
.append("div")
.attr("class", prefixedClass("explanation"));
this.elements.percentage =
this.elements.explanation
.append("span")
.attr("class", prefixedClass("percentage"));
// BR between percentage and bytes.
this.elements.explanation.append("br");
this.elements.bytes =
this.elements.explanation
.append("span")
.attr("class", prefixedClass("bytes"));
this.svg = this.elements.chart
.append("svg:svg")
.attr("width", width)
.attr("height", height);
this.vis = this.svg
.append("svg:g")
.attr("class", prefixedClass("top"))
.attr("transform", `translate(${width / 2},${height / 2})`);
this.partition = d3.partition()
.size([2 * Math.PI, radius * radius]);
this.arc = d3.arc()
.startAngle(d => d.x0)
.endAngle(d => d.x1)
.innerRadius(d => Math.sqrt(d.y0))
.outerRadius(d => Math.sqrt(d.y1));
}
getColor(data) {
if (data.type === typePackage) {
return this.colors[typePackage];
}
if (data.name.endsWith(".js")) {
return this.colors.javascript;
}
if (this.colors[data.name]) {
return this.colors[data.name];
}
return this.colors._default_;
}
initializeBreadcrumbTrail() {
// Add the svg area.
this.elements.trail =
this.elements.container
.append("div")
.attr("class", prefixedClass("trail"));
}
loadJson(json) {
// Basic setup of page elements.
this.initializeBreadcrumbTrail();
// Bounding circle underneath the sunburst, to make it easier to detect
// when the mouse leaves the parent g.
this.vis
.append("svg:circle")
.attr("r", radius)
.style("opacity", 0);
// Turn the data into a d3 hierarchy and calculate the sums.
this.root = d3.hierarchy(json)
.sum(d => d.size)
.sort((a, b) => b.value - a.value);
// For efficiency, filter nodes to keep only those large enough to see.
this.nodes = this
.partition(this.root)
.descendants()
.filter(d => d.x1 - d.x0 > 0.005); // 0.005 radians = 0.29 degrees
this.path = this.vis.data([json]).selectAll("path")
.data(this.nodes)
.enter()
.append("svg:path")
.attr("display", d => d.depth ? null : "none")
.attr("d", this.arc)
.attr("fill-rule", "evenodd")
.style("fill", d => this.getColor(d.data))
.style("opacity", 1)
.on("mouseover", this.mouseoverEvent());
// Add the mouseleave handler to the bounding circle.
this.vis.on("mouseleave", this.mouseleaveEvent());
// // Get total size of the tree = value of root node from partition.
this.totalSize = this.path.datum().value;
}
mouseoverEvent() {
const self = this;
return self.mouseover || (self.mouseover = function (d) {
const percentage = (100 * d.value / self.totalSize).toPrecision(3);
let percentageString = `${percentage}%`;
if (percentage < 0.1) {
percentageString = "< 0.1%";
}
self.elements.percentage
.text(percentageString);
self.elements.bytes
.text(prettyBytes(d.value || 0));
self.elements.explanation
.style("display", null);
const sequenceArray = d.ancestors().reverse();
sequenceArray.shift(); // remove root node from the array
self.updateBreadcrumbs(sequenceArray, percentageString);
// Fade all the segments.
d3.selectAll("path")
.style("opacity", 0.3);
// Then highlight only those that are an ancestor of the current segment.
self.vis.selectAll("path")
.filter((node) => sequenceArray.indexOf(node) >= 0)
.style("opacity", 1);
});
}
// Restore everything to full opacity when moving off the visualization.
mouseleaveEvent() {
const self = this;
return self.mouseleave || (self.mouseleave = function (d) {
// Hide the breadcrumb trail
self.elements.trail
.style("visibility", "hidden");
// Deactivate all segments during transition.
d3.selectAll("path").on("mouseover", null);
// Transition each segment to full opacity and then reactivate it.
d3.selectAll("path")
.transition()
.duration(1000)
.style("opacity", 1)
.on("end", function() {
d3.select(this).on("mouseover", self.mouseoverEvent());
});
self.elements.explanation
.style("display", "none");
});
}
// Update the breadcrumb trail to show the current sequence and percentage.
updateBreadcrumbs(nodeArray, percentageString) {
// Data join; key function combines name and depth (= position in sequence).
const trail = this.elements.trail
.selectAll("div")
.data(nodeArray, d => d.data.name + d.depth);
// Remove exiting nodes.
trail.exit().remove();
// Add breadcrumb and label for entering nodes.
const entering = trail.enter()
.append("div")
.attr("class", prefixedClass("trailSegment"))
.style("background-color", d => this.getColor(d.data))
.text(d => d.data.name);
// Merge enter and update selections; set position for all nodes.
entering
.merge(trail);
// Make the breadcrumb trail visible, if it's hidden.
this.elements.trail
.style("visibility", "");
}
}

View File

@@ -6,9 +6,9 @@
"from": "asap@>=2.0.3 <2.1.0"
},
"meteor-promise": {
"version": "0.8.0",
"resolved": "https://registry.npmjs.org/meteor-promise/-/meteor-promise-0.8.0.tgz",
"from": "meteor-promise@0.8.0"
"version": "0.8.4",
"resolved": "https://registry.npmjs.org/meteor-promise/-/meteor-promise-0.8.4.tgz",
"from": "meteor-promise@0.8.4"
},
"promise": {
"version": "7.1.1",

View File

@@ -1,18 +1,18 @@
Package.describe({
name: "promise",
version: "0.8.8",
version: "0.8.9",
summary: "ECMAScript 2015 Promise polyfill with Fiber support",
git: "https://github.com/meteor/promise",
documentation: "README.md"
});
Npm.depends({
"meteor-promise": "0.8.0",
"meteor-promise": "0.8.4",
"promise": "7.1.1"
});
Package.onUse(function(api) {
api.use("modules@0.7.6");
api.use("modules");
api.mainModule("client.js", "client");
api.mainModule("server.js", "server");
api.export("Promise");

View File

@@ -1,6 +1,6 @@
Package.describe({
summary: "Reactive dictionary",
version: '1.1.8'
version: '1.1.9'
});
Package.onUse(function (api) {

View File

@@ -1,6 +1,6 @@
Package.describe({
name: 'standard-minifier-js',
version: '2.0.0',
version: '2.1.0',
summary: 'Standard javascript minifiers used with Meteor apps by default.',
documentation: 'README.md',
});
@@ -9,9 +9,14 @@ Package.registerBuildPlugin({
name: "minifyStdJS",
use: [
'minifier-js',
'babel-compiler',
'ecmascript'
],
sources: [
'plugin/minify-js.js',
'plugin/stats.js',
'plugin/visitor.js',
'plugin/utils.js',
],
});

View File

@@ -1,3 +1,5 @@
import { extractModuleSizesTree } from "./stats.js";
Plugin.registerMinifier({
extensions: ['js'],
archMatching: 'web'
@@ -108,37 +110,52 @@ MeteorBabelMinifier.prototype.processFilesForBundle = function(files, options) {
}
}
var allJs = '';
files.forEach(function (file) {
// Don't reminify *.min.js.
if (/\.min\.js$/.test(file.getPathInBundle())) {
allJs += file.getContentsAsString();
} else {
var minified;
const toBeAdded = {
data: "",
stats: Object.create(null)
};
try {
minified = meteorJsMinify(file.getContentsAsString());
files.forEach(file => {
// Don't reminify *.min.js.
if (/\.min\.js$/.test(file.getPathInBundle())) {
toBeAdded.data += file.getContentsAsString();
} else {
var minified;
if (!(minified && typeof minified.code === "string")) {
throw new Error();
}
} catch (err) {
var filePath = file.getPathInBundle();
try {
minified = meteorJsMinify(file.getContentsAsString());
maybeThrowMinifyErrorBySourceFile(err, file);
err.message += " while minifying " + filePath;
throw err;
if (!(minified && typeof minified.code === "string")) {
throw new Error();
}
allJs += minified.code;
}
allJs += '\n\n';
} catch (err) {
var filePath = file.getPathInBundle();
Plugin.nudge();
});
maybeThrowMinifyErrorBySourceFile(err, file);
err.message += " while minifying " + filePath;
throw err;
}
const tree = extractModuleSizesTree(minified.code);
if (tree) {
toBeAdded.stats[file.getPathInBundle()] =
[Buffer.byteLength(minified.code), tree];
} else {
toBeAdded.stats[file.getPathInBundle()] =
Buffer.byteLength(minified.code);
}
toBeAdded.data += minified.code;
}
toBeAdded.data += '\n\n';
Plugin.nudge();
});
if (files.length) {
files[0].addJavaScript({ data: allJs });
files[0].addJavaScript(toBeAdded);
}
};

View File

@@ -0,0 +1,83 @@
import Visitor from "./visitor.js";
// This RegExp will be used to scan the source for calls to meteorInstall,
// taking into consideration that the function name may have been mangled
// to something other than "meteorInstall" by the minifier.
const meteorInstallRegExp = new RegExp([
// If meteorInstall is called by its unminified name, then that's what
// we should be looking for in the AST.
/\b(meteorInstall)\(\{/,
// If the meteorInstall function name has been minified, we can figure
// out its mangled name by examining the import assingment.
/\b(\w+)=Package.modules.meteorInstall\b/,
/\b(\w+)=Package\["modules-runtime"\].meteorInstall\b/,
].map(exp => exp.source).join("|"));
export function extractModuleSizesTree(source) {
const match = meteorInstallRegExp.exec(source);
if (match) {
const ast = Babel.parse(source);
const name = match[1] || match[2] || match[3];
meteorInstallVisitor.visit(ast, name, source);
return meteorInstallVisitor.tree;
}
}
const meteorInstallVisitor = new (class extends Visitor {
reset(root, meteorInstallName, source) {
this.name = meteorInstallName;
this.source = source;
this.tree = null;
}
visitCallExpression(node) {
if (this.tree !== null) {
return;
}
if (isIdWithName(node.callee, this.name)) {
const source = this.source;
function walk(expr) {
if (expr.type !== "ObjectExpression") {
return Buffer.byteLength(source.slice(expr.start, expr.end));
}
const contents = Object.create(null);
expr.properties.forEach(prop => {
const keyName = getKeyName(prop.key);
if (typeof keyName === "string") {
contents[keyName] = walk(prop.value);
}
});
return contents;
}
this.tree = walk(node.arguments[0]);
} else {
this.visitChildren(node);
}
}
});
function isIdWithName(node, name) {
return node &&
node.type === "Identifier" &&
node.name === name;
}
function getKeyName(key) {
if (key.type === "Identifier") {
return key.name;
}
if (key.type === "StringLiteral" ||
key.type === "Literal") {
return key.value;
}
return null;
}

View File

@@ -0,0 +1,26 @@
"use strict";
const codeOfA = "A".charCodeAt(0);
const codeOfZ = "Z".charCodeAt(0);
export function isObject(value) {
return typeof value === "object" && value !== null;
}
// Without a complete list of Node .type names, we have to settle for this
// fuzzy matching of object shapes. However, the infeasibility of
// maintaining a complete list of type names is one of the reasons we're
// using the FastPath/Visitor abstraction in the first place.
export function isNodeLike(value) {
return isObject(value) &&
! Array.isArray(value) &&
isCapitalized(value.type);
}
function isCapitalized(string) {
if (typeof string !== "string") {
return false;
}
const code = string.charCodeAt(0);
return code >= codeOfA && code <= codeOfZ;
}

View File

@@ -0,0 +1,57 @@
"use strict";
import {
isObject,
isNodeLike,
} from "./utils.js";
const codeOfUnderscore = "_".charCodeAt(0);
export default class Visitor {
visit(root) {
this.reset.apply(this, arguments);
this.visitWithoutReset(root);
}
visitWithoutReset(node) {
if (Array.isArray(node)) {
node.forEach(this.visitWithoutReset, this);
} else if (isNodeLike(node)) {
const method = this["visit" + node.type];
if (typeof method === "function") {
// The method must call this.visitChildren(node) to continue
// traversing.
method.call(this, node);
} else {
this.visitChildren(node);
}
}
}
visitChildren(node) {
if (! isNodeLike(node)) {
return;
}
const keys = Object.keys(node);
const keyCount = keys.length;
for (let i = 0; i < keyCount; ++i) {
const key = keys[i];
if (key === "loc" || // Ignore .loc.{start,end} objects.
// Ignore "private" properties added by Babel.
key.charCodeAt(0) === codeOfUnderscore) {
continue;
}
const child = node[key];
if (! isObject(child)) {
// Ignore properties whose values aren't objects.
continue;
}
this.visitWithoutReset(child);
}
}
}

View File

@@ -12,7 +12,7 @@ export METEOR_HOME=`pwd`
export PATH=$METEOR_HOME:$PATH
# synchronously get the dev bundle and NPM modules if they're not there.
./meteor --get-ready || exit 1
./meteor --help || exit 1
export URL='http://localhost:4096/'

View File

@@ -1,6 +1,6 @@
Package.describe({
summary: "Serves a Meteor app over HTTP",
version: '1.3.15'
version: '1.3.16'
});
Npm.depends({connect: "2.30.2",

View File

@@ -395,7 +395,8 @@ WebAppInternals.staticFilesMiddleware = function (staticFiles, req, res, next) {
info.sourceMapUrl);
}
if (info.type === "js") {
if (info.type === "js" ||
info.type === "dynamic js") {
res.setHeader("Content-Type", "application/javascript; charset=UTF-8");
} else if (info.type === "css") {
res.setHeader("Content-Type", "text/css; charset=UTF-8");

View File

@@ -1,6 +1,6 @@
{
"track": "METEOR",
"version": "1.4.4.3-rc.0",
"version": "1.5-rc.13",
"recommended": false,
"official": false,
"description": "Meteor"

View File

@@ -1,8 +1,7 @@
{
"track": "METEOR",
"version": "1.4.4.3",
"version": "1.5",
"recommended": false,
"official": true,
"patchFrom": ["1.4.4.2"],
"description": "The Official Meteor Distribution"
}

View File

@@ -7,7 +7,7 @@ UNAME=$(uname)
ARCH=$(uname -m)
MONGO_VERSION=3.2.12
NODE_VERSION=4.8.3
NPM_VERSION=4.5.0
NPM_VERSION=4.6.1
if [ "$UNAME" == "Linux" ] ; then
if [ "$ARCH" != "i686" -a "$ARCH" != "x86_64" ] ; then

View File

@@ -9,7 +9,7 @@ var packageJson = {
// Version is not important but is needed to prevent warnings.
version: "0.0.0",
dependencies: {
"meteor-promise": "0.8.0",
"meteor-promise": "0.8.4",
fibers: "1.0.15",
promise: "7.1.1",
// Not yet upgrading Underscore from 1.5.2 to 1.7.0 (which should be done

View File

@@ -11,17 +11,18 @@ var packageJson = {
dependencies: {
// Explicit dependency because we are replacing it with a bundled version
// and we want to make sure there are no dependencies on a higher version
npm: "4.5.0",
npm: "4.6.1",
"node-gyp": "3.6.0",
"node-pre-gyp": "0.6.34",
"meteor-babel": "0.20.1",
"meteor-promise": "0.8.0",
"meteor-babel": "0.21.4",
reify: "0.11.21",
"meteor-promise": "0.8.4",
fibers: "1.0.15",
promise: "7.1.1",
// So that Babel 6 can emit require("babel-runtime/helpers/...") calls.
"babel-runtime": "6.9.2",
// For various ES2015 polyfills, such as Map and Set.
"meteor-ecmascript-runtime": "0.2.9",
"meteor-ecmascript-runtime": "0.3.0",
// Not yet upgrading Underscore from 1.5.2 to 1.7.0 (which should be done
// in the package too) because we should consider using lodash instead
// (and there are backwards-incompatible changes either way).

View File

@@ -1788,8 +1788,10 @@ main.registerCommand({
" are available:");
_.each(nonlatestIndirectDeps, printItem);
Console.info([
"To update one or more of these packages, pass their names to ",
"`meteor update`, or just run `meteor update --all-packages`."
"These versions may not be compatible with your project.",
"To update one or more of these packages to their latest",
"compatible versions, pass their names to `meteor update`,",
"or just run `meteor update --all-packages`.",
].join("\n"));
}
}

View File

@@ -159,6 +159,7 @@ import Builder from './builder.js';
var compilerPluginModule = require('./compiler-plugin.js');
import { JsFile, CssFile } from './minifier-plugin.js';
var meteorNpm = require('./meteor-npm.js');
import { addToTree } from "./linker.js";
var files = require('../fs/files.js');
var archinfo = require('../utils/archinfo.js');
@@ -170,6 +171,7 @@ var packageVersionParser = require('../packaging/package-version-parser.js');
var release = require('../packaging/release.js');
import { load as loadIsopacket } from '../tool-env/isopackets.js';
import { CORDOVA_PLATFORM_VERSIONS } from '../cordova';
import { gzipSync } from "zlib";
// files to ignore when bundling. node has no globs, so use regexps
exports.ignoreFiles = [
@@ -693,7 +695,7 @@ class File {
setTargetPathFromRelPath(relPath) {
// XXX hack
if (relPath.match(/^packages\//) || relPath.match(/^assets\//)) {
if (relPath.match(/^(packages|assets|dynamic)\//)) {
this.targetPath = relPath;
} else {
this.targetPath = files.pathJoin('app', relPath);
@@ -1056,6 +1058,9 @@ class Target {
const jsOutputFilesMap = compilerPluginModule.PackageSourceBatch
.computeJsOutputFilesMap(sourceBatches);
const versions = {};
const dynamicImportFiles = new Set;
// Copy their resources into the bundle in order
sourceBatches.forEach((sourceBatch) => {
const unibuild = sourceBatch.unibuild;
@@ -1177,6 +1182,16 @@ class Target {
throw new Error('Unknown type ' + resource.type);
});
this.js.forEach(file => {
if (file.targetPath === "packages/dynamic-import.js") {
dynamicImportFiles.add(file);
}
if (file.targetPath.startsWith("dynamic/")) {
addToTree(file.hash(), file.targetPath, versions);
}
});
// Depend on the source files that produced these resources.
this.watchSet.merge(unibuild.watchSet);
@@ -1185,33 +1200,78 @@ class Target {
// XXX assumes that this merges cleanly
this.watchSet.merge(unibuild.pkg.pluginWatchSet);
});
dynamicImportFiles.forEach(file => {
file.setContents(
new Buffer(file.contents("utf8").replace(
"__DYNAMIC_VERSIONS__",
() => JSON.stringify(versions.dynamic || {})
), "utf8")
);
});
}
// Minify the JS in this target
minifyJs(minifierDef, minifyMode) {
const sources = _.map(this.js, function (file) {
return new JsFile(file, {
arch: this.arch
});
const staticFiles = [];
const dynamicFiles = [];
this.js.forEach(file => {
const jsf = new JsFile(file, { arch: this.arch });
if (file.targetPath.startsWith("dynamic/")) {
// Make sure file._hash is cached.
file.hash();
// Dynamic files consist of a single anonymous function
// expression, which some minifiers (e.g. UglifyJS) either fail to
// parse or mistakenly eliminate as dead code. To avoid these
// problems, we temporarily name the function __minifyJs.
file._contents = new Buffer(
file.contents()
.toString("utf8")
.replace(/^\s*function\s*\(/,
"function __minifyJs("),
"utf8"
);
dynamicFiles.push(jsf);
} else {
staticFiles.push(jsf);
}
});
var minifier = minifierDef.userPlugin.processFilesForBundle.bind(
minifierDef.userPlugin);
var minifier = minifierDef.userPlugin.processFilesForBundle
.bind(minifierDef.userPlugin);
buildmessage.enterJob('minifying app code', function () {
try {
var markedMinifier = buildmessage.markBoundary(minifier);
markedMinifier(sources, { minifyMode });
markedMinifier(staticFiles, { minifyMode });
dynamicFiles.forEach(file => {
markedMinifier([file], { minifyMode });
});
} catch (e) {
buildmessage.exception(e);
}
});
this.js = _.flatten(sources.map((source) => {
return source._minifiedFiles.map((file) => {
const js = [];
function handle(source, dynamic) {
source._minifiedFiles.forEach(file => {
// Remove the function name __minifyJs that was added above.
file.data = file.data
.toString("utf8")
.replace(/^\s*function\s+__minifyJs\s*\(/,
"function(");
const newFile = new File({
info: 'minified js',
data: new Buffer(file.data, 'utf8')
data: new Buffer(file.data, 'utf8'),
});
if (file.sourceMap) {
newFile.setSourceMap(file.sourceMap, '/');
}
@@ -1219,13 +1279,58 @@ class Target {
if (file.path) {
newFile.setUrlFromRelPath(file.path);
newFile.targetPath = file.path;
} else if (dynamic) {
const { targetPath } = source._source;
newFile.setUrlFromRelPath(targetPath);
newFile.targetPath = targetPath;
} else {
newFile.setUrlToHash('.js', '?meteor_js_resource=true');
}
return newFile;
js.push(newFile);
if (file.stats &&
! dynamic &&
minifyMode === "production") {
// If the minifier reported any statistics, serve those data as
// a .stats.json file alongside the newFile.
const contents = newFile.contents();
const statsFile = new File({
info: "bundle size stats JSON",
data: new Buffer(JSON.stringify({
minifier: {
name: minifierDef.isopack.name,
version: minifierDef.isopack.version,
},
totalMinifiedBytes: contents.length,
totalMinifiedGzipBytes: gzipSync(contents).length,
minifiedBytesByPackage: file.stats,
}, null, 2) + "\n", "utf8")
});
statsFile.url = newFile.url.replace(/\.js\b/, ".stats.json");
statsFile.targetPath =
newFile.targetPath.replace(/\.js\b/, ".stats.json");
statsFile.cacheable = true;
statsFile.type = "json";
if (statsFile.url !== newFile.url &&
statsFile.targetPath !== newFile.targetPath) {
// If the minifier used a file extension other than .js, the
// .replace calls above won't inject the .stats.json extension
// into the statsFile.{url,targetPath} strings, and it would
// be a mistake to serve the statsFile with the same URL as
// the real JS bundle. This should be a very uncommon case.
js.push(statsFile);
}
}
});
}));
}
staticFiles.forEach(file => handle(file, false));
dynamicFiles.forEach(file => handle(file, true));
this.js = js;
}
// For every source file we process, sets the domain name to
@@ -1437,7 +1542,7 @@ class ClientTarget extends Target {
const eachResource = function (f) {
["js", "css", "asset"].forEach((type) => {
this[type].forEach((file) => {
f(file, type);
f(file, file.type || type);
});
});
}.bind(this);
@@ -1493,9 +1598,57 @@ class ClientTarget extends Target {
manifestItem.size = file.size();
manifestItem.hash = file.hash();
writeFile(file, builder);
if (! file.targetPath.startsWith("dynamic/")) {
writeFile(file, builder);
manifest.push(manifestItem);
return;
}
// Another measure for preventing this file from being loaded
// eagerly as a <script> tag, in addition to manifestItem.path being
// prefixed with "dynamic/".
manifestItem.type = "dynamic js";
// Add the dynamic module to the manifest so that it can be
// requested via HTTP from the web server. Note, however, that we
// typically request dynamic modules via DDP, since we can compress
// the entire response more easily that way. We expose dynamic
// modules via HTTP here mostly to unlock future experimentation.
manifest.push(manifestItem);
if (manifestItem.sourceMap &&
manifestItem.sourceMapUrl) {
// If the file is a dynamic module, we don't embed its source map
// in the file itself (because base64-encoded data: URLs for
// source maps can be very large), but rather include a normal URL
// referring to the source map (as a comment), so that it can be
// loaded from the web server when needed.
writeFile(file, builder, {
sourceMapUrl: manifestItem.sourceMapUrl,
});
manifest.push({
type: "json",
path: manifestItem.sourceMap,
url: manifestItem.sourceMapUrl,
where: manifestItem.where,
cacheable: manifestItem.cacheable,
hash: manifestItem.hash,
});
// Now that we've written the module with a source map URL comment
// embedded in it, and also made sure the source map is exposed by
// the web server, we do not need to include the source map URL in
// the manifest, because then it would also be provided via the
// X-SourceMap HTTP header, redundantly.
delete manifestItem.sourceMap;
delete manifestItem.sourceMapUrl;
} else {
// If the dynamic module does not have a source map, just write it
// normally.
writeFile(file, builder);
}
});
['head', 'body'].forEach((type) => {
@@ -1966,11 +2119,11 @@ class JsImage {
);
var sourceMapFileName = files.pathBasename(loadItem.sourceMap);
// Remove any existing sourceMappingURL line. (eg, if roundtripping
// through JsImage.readFromDisk, don't end up with two!)
item.source = item.source.replace(
/\n\/\/# sourceMappingURL=.+\n?$/g, '');
item.source += "\n//# sourceMappingURL=" + sourceMapFileName + "\n";
item.source = addSourceMappingURL(item.source, sourceMapFileName);
if (item.sourceMapRoot) {
loadItem.sourceMapRoot = item.sourceMapRoot;
}
@@ -2007,7 +2160,9 @@ class JsImage {
});
}
load.push(loadItem);
if (! item.targetPath.startsWith("dynamic/")) {
load.push(loadItem);
}
});
const rebuildDirs = Object.create(null);
@@ -2351,7 +2506,7 @@ class ServerTarget extends JsImageTarget {
ServerTarget.prototype[method] = Profile(`ServerTarget#${method}`, ServerTarget.prototype[method]);
});
var writeFile = Profile("bundler writeFile", function (file, builder) {
var writeFile = Profile("bundler writeFile", function (file, builder, options) {
if (! file.targetPath) {
throw new Error("No targetPath?");
}
@@ -2363,9 +2518,33 @@ var writeFile = Profile("bundler writeFile", function (file, builder) {
// to wait until the server is actually driven by the manifest
// (rather than just serving all of the files in a certain
// directories)
builder.write(file.targetPath, { data: file.contents(), hash: file.hash() });
let data = file.contents();
const hash = file.hash();
if (options && options.sourceMapUrl) {
data = addSourceMappingURL(data, options.sourceMapUrl);
}
if (! Buffer.isBuffer(data)) {
data = new Buffer(data, "utf8");
}
builder.write(file.targetPath, { data, hash });
});
// The data argument may be either a Buffer or a string, but this function
// always returns a string.
function addSourceMappingURL(data, url) {
const dataString = data
// If data is a Buffer, convert it to a string.
.toString("utf8")
// Remove any existing source map comments.
.replace(/\n\/\/# sourceMappingURL=[^\n]+/g, "");
// Append the new source map comment to the end of the code.
return dataString + "\n//# sourceMappingURL=" + url + "\n";
}
// Writes a target a path in 'programs'
var writeTargetToPath = Profile(
"bundler writeTargetToPath",

View File

@@ -10,7 +10,6 @@ var _ = require('underscore');
var Profile = require('../tool-env/profile.js').Profile;
import {sha1, readAndWatchFileWithHash} from '../fs/watch.js';
import LRU from 'lru-cache';
import Fiber from 'fibers';
import {sourceMapLength} from '../utils/utils.js';
import {Console} from '../console/console.js';
import ImportScanner from './import-scanner.js';
@@ -62,7 +61,7 @@ import { isTestFilePath } from './test-files.js';
// Cache the (slightly post-processed) results of linker.fullLink.
const CACHE_SIZE = process.env.METEOR_LINKER_CACHE_SIZE || 1024*1024*100;
const CACHE_DEBUG = !! process.env.METEOR_TEST_PRINT_LINKER_CACHE_DEBUG;
const LINKER_CACHE_SALT = 13; // Increment this number to force relinking.
const LINKER_CACHE_SALT = 17; // Increment this number to force relinking.
const LINKER_CACHE = new LRU({
max: CACHE_SIZE,
// Cache is measured in bytes. We don't care about servePath.
@@ -1292,12 +1291,12 @@ export class PackageSourceBatch {
noLineNumbers: !isWeb
};
const cacheKey = sha1(JSON.stringify({
LINKER_CACHE_SALT,
const fileHashes = [];
const cacheKeyPrefix = sha1(JSON.stringify({
linkerOptions,
files: jsResources.map((inputFile) => {
fileHashes.push(inputFile.hash);
return {
hash: inputFile.hash,
installPath: inputFile.installPath,
sourceMap: !! inputFile.sourceMap,
mainModule: inputFile.mainModule,
@@ -1307,20 +1306,25 @@ export class PackageSourceBatch {
};
})
}));
const cacheKeySuffix = sha1(JSON.stringify({
LINKER_CACHE_SALT,
fileHashes
}));
const cacheKey = `${cacheKeyPrefix}_${cacheKeySuffix}`;
{
const inMemoryCached = LINKER_CACHE.get(cacheKey);
if (inMemoryCached) {
if (CACHE_DEBUG) {
console.log('LINKER IN-MEMORY CACHE HIT:',
linkerOptions.name, bundleArch);
}
return inMemoryCached;
if (LINKER_CACHE.has(cacheKey)) {
if (CACHE_DEBUG) {
console.log('LINKER IN-MEMORY CACHE HIT:',
linkerOptions.name, bundleArch);
}
return LINKER_CACHE.get(cacheKey);
}
const cacheFilename = self.linkerCacheDir && files.pathJoin(
self.linkerCacheDir, cacheKey + '.cache');
const cacheFilename = self.linkerCacheDir &&
files.pathJoin(self.linkerCacheDir, cacheKey + '.cache');
const wildcardCacheFilename = cacheFilename &&
files.pathJoin(self.linkerCacheDir, cacheKeyPrefix + "_*.cache");
// The return value from _linkJS includes Buffers, but we want everything to
// be JSON for writing to the disk cache. This function converts the string
@@ -1395,7 +1399,13 @@ export class PackageSourceBatch {
LINKER_CACHE.set(cacheKey, ret);
if (cacheFilename) {
// Write asynchronously.
Fiber(() => files.writeFileAtomically(cacheFilename, retAsJSON)).run();
Promise.resolve().then(() => {
try {
files.rm_recursive(wildcardCacheFilename);
} finally {
files.writeFileAtomically(cacheFilename, retAsJSON);
}
});
}
}

View File

@@ -34,7 +34,7 @@ var compiler = exports;
// dependencies. (At least for now, packages only used in target creation (eg
// minifiers) don't require you to update BUILT_BY, though you will need to quit
// and rerun "meteor run".)
compiler.BUILT_BY = 'meteor/26';
compiler.BUILT_BY = 'meteor/29';
// This is a list of all possible architectures that a build can target. (Client
// is expanded into 'web.browser' and 'web.cordova')
@@ -1021,5 +1021,9 @@ export const KNOWN_ISOBUILD_FEATURE_PACKAGES = {
// One scenario is a package depending on a Cordova plugin or version
// that is only available on npm, which means downloading the plugin is not
// supported on versions of Cordova below 5.0.0.
'isobuild:cordova': ['5.4.0']
'isobuild:cordova': ['5.4.0'],
// This package requires functionality introduced in meteor-tool@1.5.0
// to enable dynamic module fetching via import(...).
'isobuild:dynamic-import': ['1.5.0'],
};

View File

@@ -52,8 +52,10 @@ const defaultExtensionHandlers = {
},
".json"(dataString) {
const file = this;
file.jsonData = JSON.parse(dataString);
return "module.exports = " +
JSON.stringify(JSON.parse(dataString), null, 2) +
JSON.stringify(file.jsonData, null, 2) +
";\n";
},
@@ -191,8 +193,11 @@ export default class ImportScanner {
const dotExt = "." + file.type;
const dataString = file.data.toString("utf8");
file.dataString = defaultExtensionHandlers[dotExt](
dataString, file.hash);
file.dataString = defaultExtensionHandlers[dotExt].call(
file,
dataString,
file.hash,
);
if (! (file.data instanceof Buffer) ||
file.dataString !== dataString) {
@@ -255,17 +260,10 @@ export default class ImportScanner {
file.installPath = this._getInstallPath(absTargetPath);
file.sourcePath = file.targetPath;
let relativeId = convertToPosixPath(pathRelative(
pathDirname(absSourcePath),
absTargetPath
));
// If the result of pathRelative does not already start with a "."
// or a "/", prepend a "./" to make it a valid relative identifier
// according to CommonJS syntax.
if ("./".indexOf(relativeId.charAt(0)) < 0) {
relativeId = "./" + relativeId;
}
const relativeId = this._getRelativeImportId(
absSourcePath,
absTargetPath,
);
// Set the contents of the source module to import the target
// module(s). Note that module.exports will be set to the exports of
@@ -336,7 +334,17 @@ export default class ImportScanner {
oldFile.dataString = combinedDataString;
oldFile.data = new Buffer(oldFile.dataString, "utf8");
oldFile.hash = sha1(oldFile.data);
oldFile.imported = oldFile.imported || newFile.imported;
// If either oldFile or newFile has been imported non-dynamically,
// then oldFile.imported needs to be === true. Otherwise we simply set
// oldFile.imported = oldFile.imported || newFile.imported, which
// could be either false, "dynamic", or "fake" (see addNodeModules).
oldFile.imported =
oldFile.imported === true ||
newFile.imported === true ||
oldFile.imported ||
newFile.imported;
oldFile.sourceMap = combinedSourceMap.toJSON();
if (! oldFile.sourceMap.mappings) {
oldFile.sourceMap = null;
@@ -346,7 +354,7 @@ export default class ImportScanner {
scanImports() {
this.outputFiles.forEach(file => {
if (! file.lazy || file.imported) {
this._scanFile(file);
this._scanFile(file, file.imported === "dynamic");
}
});
@@ -368,6 +376,11 @@ export default class ImportScanner {
try {
this._scanFile({
sourcePath: "fake.js",
// It's important that the fake.js file itself never gets
// scanned or bundled. See the _scanFile and getOutputFiles
// methods for logic that deals with file.imported values.
imported: "fake",
lazy: true,
// By specifying the .deps property of this fake file ahead of
// time, we can avoid calling findImportedModuleIdentifiers in the
// _scanFile method.
@@ -403,11 +416,14 @@ export default class ImportScanner {
};
}
getOutputFiles(options) {
getOutputFiles() {
// Return all installable output files that are either eager or
// imported by another module.
// imported (statically or dynamically).
return this.outputFiles.filter(file => {
return file.installPath && (! file.lazy || file.imported);
return file.installPath &&
(! file.lazy ||
file.imported === true ||
file.imported === "dynamic");
});
}
@@ -467,23 +483,61 @@ export default class ImportScanner {
return result;
}
_resolve(id, absPath) {
_resolve(parentFile, id, forDynamicImport = false) {
const absPath = pathJoin(this.sourceRoot, parentFile.sourcePath);
const resolved = this.resolver.resolve(id, absPath);
if (resolved === "missing") {
return this._onMissing(id, absPath);
return this._onMissing(parentFile, id, forDynamicImport);
}
if (resolved && resolved.packageJsonMap) {
const info = parentFile.deps[id];
info.helpers = info.helpers || {};
each(resolved.packageJsonMap, (pkg, path) => {
this._addPkgJsonToOutput(path, pkg);
const packageJsonFile =
this._addPkgJsonToOutput(path, pkg, forDynamicImport);
if (! parentFile.installPath) {
// If parentFile is not installable, then we won't return it
// from getOutputFiles, so we don't need to worry about
// recording any parentFile.deps[id].helpers.
return;
}
const relativeId = this._getRelativeImportId(
parentFile.installPath,
packageJsonFile.installPath
);
// Although not explicitly imported, any package.json modules
// involved in resolving this import should be recorded as
// implicit "helpers."
info.helpers[relativeId] = forDynamicImport;
});
}
return resolved;
}
_scanFile(file) {
_getRelativeImportId(parentPath, childPath) {
const relativeId = convertToPosixPath(pathRelative(
pathDirname(parentPath),
childPath
), true);
// If the result of pathRelative does not already start with a "." or
// a "/", prepend a "./" to make it a valid relative identifier
// according to CommonJS syntax.
if ("./".indexOf(relativeId.charAt(0)) < 0) {
return "./" + relativeId;
}
return relativeId;
}
_scanFile(file, forDynamicImport = false) {
const absPath = pathJoin(this.sourceRoot, file.sourcePath);
try {
@@ -501,7 +555,13 @@ export default class ImportScanner {
}
each(file.deps, (info, id) => {
const resolved = this._resolve(id, absPath);
// Asynchronous module fetching only really makes sense in the
// browser (even though it works equally well on the server), so
// it's better if forDynamicImport never becomes true on the server.
const dynamic = this.isWebBrowser() &&
(forDynamicImport || info.dynamic);
const resolved = this._resolve(file, id, dynamic);
if (! resolved) {
return;
}
@@ -526,16 +586,30 @@ export default class ImportScanner {
// as imported so we know to include them in the bundle if they
// are lazy. Eager files and files that we have imported before do
// not need to be scanned again. Lazy files that we have not
// imported before still need to be scanned, however.
// imported before still need to be scanned, however. Note that
// alreadyScanned will be "dynamic" (which is truthy) if the file
// has only been scanned because of a dynamic import(...).
const alreadyScanned = ! depFile.lazy || depFile.imported;
// Whether the file is eager or lazy, mark it as imported. For
// lazy files, this makes the difference between being included in
// or omitted from the bundle. For eager files, this just ensures
// we won't scan them again.
depFile.imported = true;
// we won't scan them again. If this scan began from a dynamic
// import(...), we set depFile.imported = "dynamic" unless it's
// already been set true.
depFile.imported = dynamic
? depFile.imported || "dynamic"
: true;
if (! alreadyScanned) {
const needsToBeScanned = ! alreadyScanned ||
// If the file has already been scanned, but only because of a
// dynamic import(...), then it needs to be scanned again, so that
// we mark it and its dependencies as non-dynamic. This will be
// cheaper than before because we've already computed depFile.deps.
(alreadyScanned === "dynamic" &&
depFile.imported === true);
if (needsToBeScanned) {
if (depFile.error) {
// Since this file is lazy, it might never have been imported,
// so any errors reported to InputFile#error were saved but
@@ -544,7 +618,7 @@ export default class ImportScanner {
buildmessage.error(depFile.error.message,
depFile.error.info);
} else {
this._scanFile(depFile);
this._scanFile(depFile, dynamic);
}
}
@@ -557,6 +631,8 @@ export default class ImportScanner {
return;
}
info.installPath = installPath;
// If the module is not readable, _readModule may return
// null. Otherwise it will return an object with .data, .dataString,
// and .hash properties.
@@ -570,7 +646,7 @@ export default class ImportScanner {
depFile.installPath = installPath;
depFile.servePath = installPath;
depFile.lazy = true;
depFile.imported = true;
depFile.imported = dynamic ? "dynamic" : true;
// Append this file to the output array and record its index.
this._addFile(absImportedPath, depFile);
@@ -591,12 +667,17 @@ export default class ImportScanner {
return;
}
this._scanFile(depFile);
this._scanFile(depFile, dynamic);
});
}
isWeb() {
return archMatches(this.bundleArch, "web");
// Returns true for web.cordova as well as web.browser.
return ! archMatches(this.bundleArch, "os");
}
isWebBrowser() {
return archMatches(this.bundleArch, "web.browser");
}
_readFile(absPath) {
@@ -653,7 +734,8 @@ export default class ImportScanner {
}
}
info.dataString = defaultExtensionHandlers[ext](
info.dataString = defaultExtensionHandlers[ext].call(
info,
info.dataString,
info.hash,
);
@@ -775,9 +857,12 @@ export default class ImportScanner {
}
// Called by this.resolver when a module identifier cannot be resolved.
_onMissing(id, absParentPath) {
_onMissing(parentFile, id, forDynamicImport = false) {
const isApp = ! this.name;
const parentFile = this._getFile(absParentPath);
const absParentPath = pathJoin(
this.sourceRoot,
parentFile.sourcePath,
);
if (isApp &&
Resolver.isNative(id) &&
@@ -786,27 +871,33 @@ export default class ImportScanner {
// a dependency on meteor-node-stubs/deps/<id>.js.
const stubId = Resolver.getNativeStubId(id);
if (isString(stubId) && stubId !== id) {
if (parentFile &&
parentFile.deps) {
parentFile.deps[stubId] = parentFile.deps[id];
}
return this._resolve(stubId, absParentPath);
const info = parentFile.deps[id];
// Although not explicitly imported, any stubs associated with
// this native import should be recorded as implicit "helpers."
info.helpers = info.helpers || {};
info.helpers[stubId] = forDynamicImport;
return this._resolve(parentFile, stubId, forDynamicImport);
}
}
const possiblySpurious =
parentFile &&
parentFile.deps &&
has(parentFile.deps, id) &&
parentFile.deps[id].possiblySpurious;
const info = {
packageName: this.name,
parentPath: absParentPath,
bundleArch: this.bundleArch,
possiblySpurious,
possiblySpurious: false,
dynamic: false,
};
if (parentFile &&
parentFile.deps &&
has(parentFile.deps, id)) {
const importInfo = parentFile.deps[id];
info.possiblySpurious = importInfo.possiblySpurious;
info.dynamic = importInfo.dynamic;
}
// If the imported identifier is neither absolute nor relative, but
// top-level, then it might be satisfied by a package installed in
// the top-level node_modules directory, and we should record the
@@ -827,15 +918,25 @@ export default class ImportScanner {
}
}
_addPkgJsonToOutput(pkgJsonPath, pkg) {
_addPkgJsonToOutput(pkgJsonPath, pkg, forDynamicImport = false) {
const file = this._getFile(pkgJsonPath);
if (file) {
// If the file already exists, don't modify or replace it.
return;
// If the file already exists, just update file.imported according
// to the forDynamicImport parameter.
file.imported = forDynamicImport
? file.imported || "dynamic"
: true;
return file;
}
const data = new Buffer(map(pkg, (value, key) => {
return `exports.${key} = ${JSON.stringify(value)};\n`;
const isIdentifier = /^[_$a-zA-Z]\w*$/.test(key);
const prop = isIdentifier
? "." + key
: "[" + JSON.stringify(key) + "]";
return `exports${prop} = ${JSON.stringify(value)};\n`;
}).join(""));
const relPkgJsonPath = pathRelative(this.sourceRoot, pkgJsonPath);
@@ -843,13 +944,14 @@ export default class ImportScanner {
const pkgFile = {
type: "js", // We represent the JSON module with JS.
data,
jsonData: pkg,
deps: {}, // Avoid accidentally re-scanning this file.
sourcePath: relPkgJsonPath,
installPath: this._getInstallPath(pkgJsonPath),
servePath: relPkgJsonPath,
hash: sha1(data),
lazy: true,
imported: true,
imported: forDynamicImport ? "dynamic" : true,
// Since _addPkgJsonToOutput is only ever called for package.json
// files that are involved in resolving package directories, and pkg
// is only a subset of the information in the actual package.json
@@ -865,6 +967,8 @@ export default class ImportScanner {
if (hash) {
this.watchSet.addFile(pkgJsonPath, hash);
}
return pkgFile;
}
}

View File

@@ -35,7 +35,8 @@ function tryToParse(source, hash) {
return ast;
}
var dependencyKeywordPattern = /\b(?:require|import|importSync|export)\b/g;
var dependencyKeywordPattern =
/\b(?:require|import|importSync|dynamicImport|export)\b/g;
/**
* The `findImportedModuleIdentifiers` function takes a string of module
@@ -95,9 +96,14 @@ export function findImportedModuleIdentifiers(source, hash) {
return addIdentifier(id, "require", requireIsBound);
}
id = getImportedModuleId(node);
if (typeof id === "string") {
return addIdentifier(id, "import", requireIsBound);
const importInfo = getImportedModuleInfo(node);
if (importInfo) {
return addIdentifier(
importInfo.id,
"import",
requireIsBound,
importInfo.dynamic
);
}
// Continue traversing the children of this node.
@@ -117,10 +123,17 @@ export function findImportedModuleIdentifiers(source, hash) {
}
}
function addIdentifier(id, type, requireIsBound) {
function addIdentifier(id, type, requireIsBound, isDynamic) {
const entry = hasOwn.call(identifiers, id)
? identifiers[id]
: identifiers[id] = { possiblySpurious: true };
: identifiers[id] = {
possiblySpurious: true,
dynamic: !! isDynamic
};
if (! isDynamic) {
entry.dynamic = false;
}
if (type === "require") {
// If the identifier comes from a require call, but require is not a
@@ -182,6 +195,61 @@ function isStringLiteral(node) {
typeof node.value === "string"));
}
function getImportedModuleInfo(node) {
switch (node.type) {
case "CallExpression":
if (node.callee.type === "Import" ||
isIdWithName(node.callee, "import")) {
const firstArg = node.arguments[0];
if (isStringLiteral(firstArg)) {
return {
id: firstArg.value,
dynamic: true,
};
}
} else if (node.callee.type === "MemberExpression" &&
isIdWithName(node.callee.object, "module")) {
const propertyName =
isPropertyWithName(node.callee.property, "import") ||
isPropertyWithName(node.callee.property, "importSync") ||
isPropertyWithName(node.callee.property, "dynamicImport");
if (propertyName) {
const dynamic = propertyName === "dynamicImport";
const args = node.arguments;
const argc = args.length;
if (argc > 0) {
const arg = args[0];
if (isStringLiteral(arg)) {
return {
id: arg.value,
dynamic,
};
}
}
}
}
return null;
case "ImportDeclaration":
case "ExportAllDeclaration":
case "ExportNamedDeclaration":
// The .source of an ImportDeclaration or Export{Named,All}Declaration
// is always a string-valued Literal node, if not null.
if (isNode(node.source)) {
return {
id: node.source.value,
dynamic: false,
};
}
return null;
}
}
function isPropertyWithName(node, name) {
if (isIdWithName(node, name) ||
(isStringLiteral(node) &&
@@ -190,33 +258,6 @@ function isPropertyWithName(node, name) {
}
}
function getImportedModuleId(node) {
if (node.type === "CallExpression" &&
node.callee.type === "MemberExpression" &&
isIdWithName(node.callee.object, "module") &&
(isPropertyWithName(node.callee.property, "import") ||
isPropertyWithName(node.callee.property, "importSync"))) {
const args = node.arguments;
const argc = args.length;
if (argc > 0) {
const arg = args[0];
if (isStringLiteral(arg)) {
return arg.value;
}
}
}
if (node.type === "ImportDeclaration" ||
node.type === "ExportAllDeclaration" ||
node.type === "ExportNamedDeclaration") {
// The .source of an ImportDeclaration or Export{Named,All}Declaration
// is always a string-valued Literal node, if not null.
if (isNode(node.source)) {
return node.source.value;
}
}
}
// Analyze the JavaScript source code `source` and return a dictionary of all
// globals which are assigned to in the package. The values in the dictionary
// are all `true`.

View File

@@ -162,13 +162,15 @@ _.extend(Module.prototype, {
servePath: self.combinedServePath,
};
const results = [result];
// An array of strings and SourceNode objects.
let chunks = [];
let fileCount = 0;
// Emit each file
if (self.meteorInstallOptions) {
const tree = self._buildModuleTree();
const tree = self._buildModuleTree(results, sourceWidth);
fileCount = self._chunkifyModuleTree(tree, chunks, sourceWidth);
result.exportsName =
self._chunkifyEagerRequires(chunks, fileCount, sourceWidth);
@@ -216,19 +218,20 @@ _.extend(Module.prototype, {
}
);
return [result];
return results;
}),
// Builds a tree of nested objects where the properties are names of
// files or directories, and the values are either nested objects
// (representing directories) or File objects (representing modules).
// Bare files and lazy files that are never imported are ignored.
_buildModuleTree() {
_buildModuleTree(results, sourceWidth) {
assert.ok(this.meteorInstallOptions);
// Tree of File objects for all non-dynamic modules.
const tree = {};
_.each(this.files, function (file) {
_.each(this.files, file => {
if (file.bare) {
// Bare files will be added in between the synchronous require
// calls in _chunkifyEagerRequires.
@@ -242,22 +245,57 @@ _.extend(Module.prototype, {
return;
}
const parts = file.installPath.split("/");
let t = tree;
_.each(parts, function (part, i) {
const isLastPart = i === parts.length - 1;
t = _.has(t, part)
? t[part]
: t[part] = isLastPart ? file : {};
});
if (file.isDynamic()) {
const servePath = "dynamic/" + file.installPath;
const { code: source, map } =
file.getPrelinkedOutput({
sourceWidth: sourceWidth,
noLineNumbers: this.noLineNumbers
}).toStringWithSourceMap({
file: servePath,
});
results.push({
source,
servePath,
sourceMap: map && map.toJSON(),
dynamic: true,
});
const stubArray = file.deps.slice(0);
if (file.installPath.endsWith("/package.json") &&
file.jsonData) {
const stub = {};
function tryMain(name) {
const value = file.jsonData[name];
if (_.isString(value)) {
stub[name] = value;
}
}
tryMain("browser");
tryMain("main");
stubArray.push(stub);
}
addToTree(stubArray, file.installPath, tree);
} else {
// If the file is not dynamic, then it should be included in the
// initial bundle, so we add it to the static tree.
addToTree(file, file.installPath, tree);
}
});
return tree;
},
// Takes the tree generated by _buildModuleTree and populates the chunks
// Take the tree generated in getPrelinkedFiles and populate the chunks
// array with strings and SourceNode objects that can be combined into a
// single SourceNode object. Returns the count of modules in the tree.
// single SourceNode object. Return the count of modules in the tree.
_chunkifyModuleTree(tree, chunks, sourceWidth) {
const self = this;
@@ -268,12 +306,18 @@ _.extend(Module.prototype, {
let moduleCount = 0;
function walk(t) {
if (t instanceof File) {
if (Array.isArray(t)) {
++moduleCount;
chunks.push(JSON.stringify(t, null, 2));
} else if (t instanceof File) {
++moduleCount;
chunks.push(t.getPrelinkedOutput({
sourceWidth,
noLineNumbers: self.noLineNumbers
}));
} else if (_.isObject(t)) {
chunks.push("{");
const keys = _.keys(t);
@@ -294,7 +338,7 @@ _.extend(Module.prototype, {
// allows us to call meteorInstall just once to install everything.
chunks.push("var require = meteorInstall(");
walk(tree);
chunks.push(",", JSON.stringify(self.meteorInstallOptions), ");");
chunks.push(",", self._stringifyInstallOptions(), ");");
if (moduleCount === 0) {
// If no files were actually added to the chunks array, roll back
@@ -305,6 +349,40 @@ _.extend(Module.prototype, {
return moduleCount;
},
_stringifyInstallOptions() {
let optionsString =
JSON.stringify(this.meteorInstallOptions, null, 2);
if (this.useGlobalNamespace) {
return optionsString;
}
if (! this.files.some(file => file.isDynamic())) {
// If the package contains no files that can be imported
// dynamically, then we don't need to provide an options.eval
// function for evaluating dynamic modules.
return optionsString;
}
assert.ok(optionsString.endsWith("\n}"));
// If this package is not using the global namespace, pass an
// options.eval method to meteorInstall, so that code added later can
// have access to the same shared package variables as other code in
// the package.
return optionsString.slice(0, optionsString.length - 2) + [
",",
" eval: function () {",
" return eval(arguments[0]);",
" }",
"}"
].join("\n");
},
_hasDynamicModules() {
return this.files.some(file => file.isDynamic());
},
// Adds require calls to the chunks array for all modules that should be
// eagerly evaluated, and also includes bare files in the appropriate
// order with respect to the require calls. Returns the name of the
@@ -344,6 +422,19 @@ _.extend(Module.prototype, {
}
});
// Insert the given value into the tree by splitting the path and
// creating/following nested objects properties named by each component of
// the split path.
export function addToTree(value, path, tree) {
const parts = path.split("/");
const lastIndex = parts.length - 1;
parts.forEach((part, i) => {
tree = _.has(tree, part)
? tree[part]
: tree[part] = i < lastIndex ? {} : value;
});
}
// Given 'symbolMap' like {Foo: 's1', 'Bar.Baz': 's2', 'Bar.Quux.A': 's3', 'Bar.Quux.B': 's4'}
// return something like
// {Foo: 's1', Bar: {Baz: 's2', Quux: {A: 's3', B: 's4'}}}
@@ -424,20 +515,16 @@ var File = function (inputFile, module) {
self.servePath = inputFile.servePath;
// Module identifiers imported or required by this module, if any.
if (Array.isArray(inputFile.deps)) {
self.deps = inputFile.deps;
} else if (inputFile.deps && typeof inputFile.deps === "object") {
self.deps = Object.keys(inputFile.deps);
} else {
self.deps = [];
}
// Excludes dynamically imported dependencies, and may exclude
// dependencies already included in the non-dynamic initial bundle.
self.deps = getNonDynamicDeps(inputFile.deps);
// True if the input file should not be evaluated eagerly.
self.lazy = inputFile.lazy; // could be `true`, `false` or `undefined` <sigh>
// True if the file is an eagerly evaluated entry point, or if some
// other file imports or requires it.
self.imported = !!inputFile.imported;
// True if the file is eagerly imported, "dynamic" if the file is
// dynamically imported.
self.imported = inputFile.imported;
// Boolean indicating whether this file is the main entry point module
// for its package.
@@ -450,10 +537,28 @@ var File = function (inputFile, module) {
// Is an Object, not a string.
self.sourceMap = inputFile.sourceMap;
// If inputFile is a JSON file, its parsed data will be exposed via the
// .jsonData property.
self.jsonData = inputFile.jsonData || null;
// The Module containing this file.
self.module = module;
};
function getNonDynamicDeps(inputFileDeps) {
const nonDynamicDeps = Object.create(null);
if (! _.isEmpty(inputFileDeps)) {
_.each(inputFileDeps, (info, id) => {
if (! info.dynamic) {
nonDynamicDeps[id] = info;
}
});
}
return Object.keys(nonDynamicDeps);
}
_.extend(File.prototype, {
// Return the globals in this file as an array of symbol names. For
// example: if the code references 'Foo.bar.baz' and 'Quux', and
@@ -511,21 +616,13 @@ _.extend(File.prototype, {
return this.module.meteorInstallOptions;
},
isDynamic() {
return this.lazy && this.imported === "dynamic";
},
_getClosureHeader() {
if (this._useMeteorInstall()) {
var header = "";
if (this.deps.length > 0) {
header += "[";
_.each(this.deps, dep => {
header += JSON.stringify(dep) + ",";
});
}
const headerParts = [
header,
"function("
];
const headerParts = ["function("];
if (this.source.match(/\b__dirname\b/)) {
headerParts.push("require,exports,module,__filename,__dirname");
@@ -548,14 +645,9 @@ _.extend(File.prototype, {
},
_getClosureFooter() {
if (this._useMeteorInstall()) {
var footer = "}";
if (this.deps.length > 0) {
footer += "]";
}
return footer;
}
return "}).call(this);\n";
return this._useMeteorInstall()
? "}"
: "}).call(this);\n";
},
// Options:
@@ -1057,6 +1149,10 @@ export var fullLink = Profile("linker.fullLink", function (inputFiles, {
var headerContent = (new Array(headerLines + 1).join(';'));
return _.map(prelinkedFiles, function (file) {
if (file.dynamic) {
return file;
}
if (file.sourceMap) {
var sourceMap = file.sourceMap;
sourceMap.mappings = headerContent + sourceMap.mappings;

View File

@@ -60,13 +60,9 @@ export class JsFile extends InputFile {
// - sourceMap
// - path
// - hash?
// - stats?
addJavaScript(options) {
const self = this;
self._minifiedFiles.push({
data: options.data,
sourceMap: options.sourceMap,
path: options.path
});
this._minifiedFiles.push({ ...options });
}
}
@@ -75,12 +71,9 @@ export class CssFile extends InputFile {
// - sourceMap
// - path
// - hash?
// - stats?
addStylesheet(options) {
this._minifiedFiles.push({
data: options.data,
sourceMap: options.sourceMap,
path: options.path
});
this._minifiedFiles.push({ ...options });
}
}

View File

@@ -297,17 +297,21 @@ export default class Resolver {
pkgSubset.version = pkg.version;
}
let main = pkg.main;
if (has(pkg, "main")) {
pkgSubset.main = pkg.main;
let main;
function tryMain(name) {
const value = pkg[name];
if (isString(value)) {
main = main || value;
pkgSubset[name] = value;
}
}
if (archMatches(this.targetArch, "web") &&
isString(pkg.browser)) {
main = pkg.browser;
pkgSubset.browser = pkg.browser;
if (archMatches(this.targetArch, "web")) {
tryMain("browser");
}
tryMain("main");
if (isString(main)) {
// The "main" field of package.json does not have to begin with ./
// to be considered relative, so first we try simply appending it to

View File

@@ -13,6 +13,8 @@ var Profile = require('./profile.js').Profile;
// This code is duplicated in tools/main.js.
var MIN_NODE_VERSION = 'v0.10.41';
var hasOwn = Object.prototype.hasOwnProperty;
if (require('semver').lt(process.version, MIN_NODE_VERSION)) {
process.stderr.write(
'Meteor requires Node ' + MIN_NODE_VERSION + ' or later.\n');
@@ -124,6 +126,34 @@ var startCheckForLiveParent = function (parentPid) {
}
};
var specialArgPaths = {
"packages/modules-runtime.js": function () {
return {
npmRequire: npmRequire,
Profile: Profile
};
},
"packages/dynamic-import.js": function (file) {
var dynamicImportInfo = {};
Object.keys(configJson.clientPaths).map(function (key) {
var programJsonPath = path.resolve(configJson.clientPaths[key]);
var programJson = require(programJsonPath);
dynamicImportInfo[key] = {
dynamicRoot: path.join(path.dirname(programJsonPath), "dynamic")
};
});
dynamicImportInfo.server = {
dynamicRoot: path.join(serverDir, "dynamic")
};
return { dynamicImportInfo: dynamicImportInfo };
}
};
var loadServerBundles = Profile("Load server bundles", function () {
_.each(serverJson.load, function (fileInfo) {
var code = fs.readFileSync(path.resolve(serverDir, fileInfo.path));
@@ -269,13 +299,17 @@ var loadServerBundles = Profile("Load server bundles", function () {
},
};
var isModulesRuntime =
fileInfo.path === "packages/modules-runtime.js";
var wrapParts = ["(function(Npm,Assets"];
if (isModulesRuntime) {
wrapParts.push(",npmRequire,Profile");
}
var specialArgs =
hasOwn.call(specialArgPaths, fileInfo.path) &&
specialArgPaths[fileInfo.path](fileInfo);
var specialKeys = Object.keys(specialArgs || {});
specialKeys.forEach(function (key) {
wrapParts.push("," + key);
});
// \n is necessary in case final line is a //-comment
wrapParts.push("){", code, "\n})");
var wrapped = wrapParts.join("");
@@ -296,9 +330,10 @@ var loadServerBundles = Profile("Load server bundles", function () {
// what require() uses to generate its errors.
var func = require('vm').runInThisContext(wrapped, scriptPath, true);
var args = [Npm, Assets];
if (isModulesRuntime) {
args.push(npmRequire, Profile);
}
specialKeys.forEach(function (key) {
args.push(specialArgs[key]);
});
Profile(fileInfo.path, func).apply(global, args);
});

View File

@@ -7,12 +7,12 @@
meteor-base # Packages every Meteor app needs to have
mobile-experience # Packages for a great mobile UX
mongo # The database Meteor supports right now
blaze-html-templates # Compile .html files into Meteor Blaze views
static-html # Define static page content in .html files
reactive-var # Reactive variable for tracker
tracker # Meteor's client-side reactive programming library
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.
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

View File

@@ -13,7 +13,7 @@ tracker # Meteor's client-side reactive programming library
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.
es5-shim # ECMAScript 5 compatibility for older browsers
ecmascript # Enable ECMAScript2015+ syntax in app code
kadira:flow-router # FlowRouter is a very simple router for Meteor

View File

@@ -13,7 +13,7 @@ tracker # Meteor's client-side reactive programming library
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.
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

View File

@@ -0,0 +1 @@
node_modules

View File

@@ -0,0 +1,15 @@
# 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

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
1ue5kcr1q6a4v2o7ro4

View File

@@ -0,0 +1,27 @@
# 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-base # Packages every Meteor app needs to have
mobile-experience # Packages for a great mobile UX
mongo # The database Meteor supports right now
blaze-html-templates@1.0.4 # Compile .html files into Meteor Blaze views
reactive-var # Reactive variable for tracker
jquery # Helpful client-side library
tracker # Meteor's client-side reactive programming library
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
autopublish # Publish all data to the clients (for prototyping)
insecure # Allow all DB writes from clients (for prototyping)
dynamic-import
dispatch:mocha-phantomjs
dispatch:mocha-browser
lazy-test-package
helper-package

View File

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

View File

@@ -0,0 +1 @@
none

View File

@@ -0,0 +1,2 @@
import moment from "moment";
export { moment };

View File

@@ -0,0 +1 @@
module.exports = { __esModule: true };

View File

@@ -0,0 +1,6 @@
import { strictEqual } from "assert";
export const name = module.id;
export const promise = import("./mutual-b").then(b => {
strictEqual(b.name, "/imports/mutual-b.js");
return b;
});

Some files were not shown because too many files have changed in this diff Show More