The uglify-es minifier sometimes inlines (0,Package.modules.meteorInstall)
as a callee expression rather than declaring a variable, so this commit
adds support for that minification technique when computing bundle size
statistics for the bundle-visualizer package.
One symptom of this bug: while the bundle-visualizer displays packages
minified in this way with the correct total size, they appear not to have
any node_modules.
cc @abernix
* Update the default CSS parsing/combining/minifying tools
The `minifier-css` package is currently using outdated
(and abandoned) npm packages (`css-parse` and `css-stringify`),
as part of its parsing/minification process. This commit
replaces those packages with the robust, modern and maintained
`postcss` package.
* Adjust CSS source file fallback value
* Self test adjustments and cleanup
* Disable sourcesContent generation by postcss
The `standard-minifier-css` package is already associating
source content with the source map, so we don't need to
do this twice.
* Add History.md entry covering backwards compatibility details
* Bump major version due to backwards compatibility breaking changes
* Bump minor versions
* Code review changes (boolean formatting, concat to spread)
Fixes the error reported in the 1.6.1 pull request. (Thanks, @yorrd!)
It's worth nothing that the `DDP._allSubscriptionsReady` function which
was broken is used by the deprecated `spiderable` package. Since
`spiderable` is deprecated, it's advisable to look into ways to stop
depending on it.
Refs: https://github.com/meteor/meteor/pull/9274#issuecomment-345358178.
Since bundle-visualizer is a non-core package, it could be used with a
version of ecmascript that does not imply dynamic-import, though it
definitely requires support for `import()` to function properly.
Since the new version of ecmascript depends directly on dynamic-import,
it's important that it's a version of dynamic-import that does not depend
indirectly on ecmascript, and I'm not sure if the constraint solver can
figure that out easily without a little help.
Even a weak dependency affects the load order of packages by forcing the
depended-on package to load before the dependent package. Waiting until
Meteor.startup to check for Package["browser-policy-common"] dynamically
does not have this problem.
If you're trying to visualize the bundle of an application that does not
use ddp-client, it's annoying if bundle-visualizer pulls in all those
dependencies just to support itself.
cc @abernix
Now that dynamic-import no longer depends indirectly on ecmascript, the
ecmascript package can finally guarantee support for dynamic `import()`,
as it rightfully should.
Now that we're using HTTP POST requests to fetch dynamic modules, it's
more important to make fewer requests when possible, given the higher
latency of HTTP requests compared to WebSocket messages.
The trick is to wait until the next tick of the event loop before actually
sending the request, so that multiple dynamic import() calls in quick
succession are treated as a single request, and all the modules they
require can be returned in a single response object.
For example, we want code like this
const [
React,
ReactDOM
] = await Promise.all([
import("react"),
import("react-dom")
]);
to result in one HTTP POST request for both `react` and `react-dom`, as
well as all their dependencies, rather than two separate requests.
Indeed, that is what happens, since both import() calls take place in the
same tick of the event loop.
Letting dynamic-import depend on packages that depend on ecmascript means
ecmascript and its dependencies can't depend on dynamic-import. This
commit gives us back that flexibility.
Each time the server starts, the dynamic-import/server.js module creates a
secret key for accessing dynamic server modules, then exposes that key to
the server-side dynamic-import/client.js module, and no one else.
If client.setSecretKey has been called, that key will be included as a
query parameter in each /__dynamicImport POST request. If it matches the
actual secret key, access is granted to the corresponding dynamic modules;
otherwise, only web.browser dynamic modules are made available.
Ever since Meteor 1.5 first shipped, dynamic modules have been fetched
over a WebSocket, which is appealing because sockets have very little
latency and metadata overhead per round-trip.
However, using a WebSocket requires first establishing a socket connection
to the server, which takes time and may require a WebSocket polyfill.
An even more subtle problem is that we cannot use dynamic imports in any
of the code that implements the ddp-client package, as long as the
dynamic-import package depends on ddp-client.
By switching from WebSockets to HTTP POST requests, this commit radically
reduces the dependencies of the dynamic-import package, while preserving
(or even exceeding) the benefits of socket-based dynamic module fetching:
1. The client makes a single HTTP POST request for the exact set of
dynamic modules that it needs, which is much cheaper/faster than making
several/many HTTP requests in parallel, with or without HTTP/2.
2. Because the client uses a permanent cache (IndexedDB) to avoid
requesting any modules it has ever previously received, the lack of
HTTP caching for POST requests is not a problem.
3. Because the HTTP response contains all the requested dynamic modules in
a single JSON payload, gzip compression works across modules, which
produces a smaller total response size than if each individual module
was compressed by itself.
4. Although HTTP requests have more latency than WebSocket messages, the
ability to make HTTP requests begins much sooner than a WebSocket
connection can be established, which more than makes up for the latency
disadvantage.
5. HTTP requests are a little easier to inspect and debug in the dev tools
than WebSocket frames.
6. The new /__dynamicImport HTTP endpoint is a raw Connect/Express-style
endpoint, so it bypasses the Meteor method-calling system altogether,
which eliminates some additional overhead.
7. Fetching dynamic modules no longer competes with other WebSocket
traffic such as DDP and Livedata.
8. Although the implementation of the /__dynamicImport endpoint is a bit
too complicated to allow serving dynamic modules from a CDN, that
remains a possibility for future experimentation. In other words, how
modules are fetched is still just an implementation detail of the
`meteorInstall.fetch` callback.
9. As with the WebSocket implementation, the module server is totally
stateless, so it should be easy to scale up if necessary.
I wish I had appreciated the advantages of an HTTP-based implementation
sooner, but I think the transition will be pretty seamless, since the new
implementation is completely backwards compatible with the old.