When a CSS (or compiled-to-CSS) module is lazy (e.g., in `imports/` or
`node_modules`) and not otherwise imported by another CSS module, Meteor
automatically turns it into a JS module so that it can be handled by the
`ImportScanner`, and imported dynamically by other JS modules.
Until now, there were two problems with CSS handled in this way: it did
not have proper source maps, and the CSS text was not minified in
production.
This commit introduces a special minification step for dynamic CSS, which
must take place before we create the dynamic JS module. Waiting for the
usual minification of CSS would be a mistake, since that happens long
after the `ImportScanner` and `Linker` have already processed JavaScript
modules. Modifying the contents of JS modules at that point would be
impossible without recomputing source maps, etc.
Since the JS module dynamically creates a `<style>` tag and appends it to
the `<head>` of the document, the code of the JS module has no meaningful
relationship to the lines of CSS text that are actually evaluated by the
browser, so it would be a mistake to give the JS module the same source
map as the original CSS resource.
Instead, when there is a source map, we write it out as an asset that can
be requested at runtime, and append a sourceMappingURL comment to the end
of the CSS text referring to this asset URL. Note that this only happens
in development, which makes sense because minification in production
invalidates the source map, and we don't want to expose source code in
production anyway.
According to traditional Meteor file loading rules, tests/ directories are
completely ignored: https://guide.meteor.com/structure.html#special-directories
However, if you specify a meteor.testModule in your package.json that
refers to a file inside a tests/ directory, Meteor should permit modules
to be loaded from tests/, as well as any modules that are imported by the
meteor.testModule entry point.
Fixes#9991.
Not including SockJS in the modern JS bundle was a nice bundle size
savings (28KB before gzip), but SockJS works better than a native
WebSocket for clients that are stuck in unusual networking situations, and
the fallback of using dynamic import() to load SockJS when the native
WebSocket failed was much slower than simply including SockJS in the
bundle and using it from the start.
Moreover, the new `meteor create --minimal` starter app does not use
socket-stream-client (nor DDP), so going back to including SockJS in both
the modern and the legacy bundles should have no impact on the minimal
modern bundle size.
If you want to continue using a native WebSocket instead of SockJS, you
can always pin the older version of the socket-stream-client package:
meteor add socket-stream-client@0.2.1
Now that compilation of compile-to-CSS files in imports/ and node_modules/
is actually lazy, we can safely call compileOneFileLater for all
inputFiles without worrying about accidental compilation.
If you're subclassing `CachingCompiler` or `MultiFileCachingCompiler`, you
can now implement a `compileOneFileLater` (emphasis on `Later`) to opt into
the new lazy compilation strategy.
If you implement this method, and `inputFile.supportsLazyCompilation` is
true, then the `addCompileResult` will not be called, though it is
probably a good idea to keep any existing `addCompileResult` methods, just
in case `inputFile.supportsLazyCompilation` is not truthy.
This will be an important part of a proper solution to the issues I
described (but failed to fix) in my broken PR #9968.
One limitation of Meteor's current compiler plugins system is that every
file we *might* use must be compiled before we know whether it *will* be
used by the application (which is something we only find out when we later
run the `ImportScanner`). More specifically, when inputFile.addJavaScript
is called, any information relevant to the current file must already have
been computed, even if the file will never be used.
This commit begins the process of supporting a lazy version of the
`inputFile.addJavaScript` method. For consistency, this interface is
supported by other methods like `inputFile.addStylesheet`, though it may
not make as much sense for non-JavaScript resources.
In this very basic initial implementation, the `lazyFinalizer` function is
called immediately, so that subsequent code can keep pretending that all
relevant information was eagerly provided.
The next step will be waiting to call `lazyFinalizer` until the last
possible moment, so that we can skip a potentially huge amount of
unnecessary compilation time.