In [glob patterns][1], the `*` wildcard is supposed to match 0 or more
characters.
For reference:
- This is also how `*` works in other implementations, such as
`.gitignore` files or Firebase hosting config.
- Some popular JS implementations (e.g. [minimatch][2], [micromatch][3])
work differently, matching 1 or more character (but not 0).
This commit "fixes" the minimal glob support in
`@angular/service-worker` to allow `*` to also match 0 characters.
[1]: https://en.wikipedia.org/wiki/Glob_%28programming%29
[2]: https://www.npmjs.com/package/minimatch
[3]: https://www.npmjs.com/package/micromatch
PR Close#23339
The ServiceWorker will redirect navigation requests that don't match any
`asset` or `data` group to the specified index file. The rules for a
request to be classified as a navigation request are as follows:
1. Its `mode` must be `navigation`.
2. It must accept a `text/html` response.
3. Its URL must match certain criteria (see below).
By default, a navigation request can have any URL except for:
1. URLs containing `__`.
2. URLs to files (i.e. containing a file extension in the last path
segment).
While these rules are fine in many cases, sometimes it is desirable to
configure different rules for the URLs of navigation requests (e.g.
ignore specific URLs and pass them through to the server).
This commit adds support for specifying an optional `navigationUrls`
list in `ngsw-config.json`, which contains URLs or simple globs
(currently only recognizing `!`, `*` and `**`).
Only requests whose URLs match any of the positive URLs/patterns and
none of the negative ones (i.e. URLs/patterns starting with `!`) will be
considered navigation requests (and handled accordingly by the SW).
(This is an alternative implementation to #23025.)
Fixes#20404
PR Close#23339
Under some circumstances (possibly related to opening Chrome DevTools),
requests are made with `cache: 'only-if-cached'` and `mode: 'no-cors'`.
These request will eventually fail, because `only-if-cached` is only
allowed to be used with `mode: 'same-origin'`.
This is likely a bug in Chrome DevTools.
This commit avoids errors related to such requests by not handling them.
Fixes#22362
PR Close#22883
Previously, when trying to fetch `ngsw.json` (e.g. during
`checkForUpdate()`) while either the client or the server were offline,
the ServiceWorker would enter a degrade mode, where only existing
clients would be served. This essentially meant that the ServiceWorker
didn't work offline.
This commit fixes it by differentiating offline errors and not entering
degraded mode. The ServiceWorker will remain in the current mode until
connectivity to the server is restored.
Fixes#21636
PR Close#22883
'remove' method not removing url from state.map
'accessed' method not removing 'previous' reference from existing node when it becomes the head
Fixes#22218Fixes#22768
PR Close#22769
This lets projects like Material change ng_package "bundle index" files to non-conflicting paths
Currently packages like @angular/core ship with the generated metadata
in a path like 'core.js' which overwrites one of the inputs.
Angular material puts the generated file in a path like 'index.js'
Either way these files generated by ng_module rules have the potential
to collide with inputs given by the user, which results in an error.
Instead, give users the freedom to choose a different non-conflicting name.
Also this refactors the ng_package rule, removing the redundant
secondary_entry_points attribute.
Instead, we assume that any ng_module in the deps with a module_name
attribute is a secondary entry point.
PR Close#22814
This flag is picked up by webpack v4 and used for more agressive optimizations.
Our code is already side-effect free, because that's what we needed for build-optimizer to work.
PR Close#22785
Angular Package Format v6 stops bundling files in the esm5 and esm2015
directories, now that Webpack 4 can tree-shake per-file.
Adds some missing files like package.json to make packages closer to
what we publish today.
Refactor ng_package to be a type of npm_package and re-use the packaging
action from that rule.
PR Close#22782
We now create npm packages to cover all the public api assertions in tools/public_api_guard.
We no longer depend on ts-api-guardian from npm - it is now stale since the repository was archived.
There is no longer a gulp task to enforce or accept the public API, this is in CircleCI as part of running all bazel test targets.
PR Close#22639
BREAKING CHANGE: after this change, npm and yarn will issue incompatible peerDependencies warning
We don't expect this to actually break an application, but the application/library package.json
will need to be updated to provide tslib 1.9.0 or higher.
PR Close#22667
"ng update" supports having multiple packages as part of a group which should be updated together, meaning that e.g. calling "ng update @angular/core" would be equivalent to updating all packages of the group (that are part of the package.json already).
In order to support the grouping feature, the package.json of the version the user is updating to needs to include an "ng-update" key that points to this metadata.
The entire specification for the update workflow can be found here: 2e8b12a4ef/docs/specifications/update.md
PR Close#22482
Service Workers can be tricky to work with in production, and often
it becomes necessary to deactivate an existing SW. This is trickier
than one might imagine - as long as clients on the old SW may exist
it is important to serve some script at the old SW URL. This commit
adds safety-worker.js to the published NPM package, which is useful
for that purpose. On install the SW unregisters itself which safely
and gradually allows older clients to update.
PR Close#21863
This helps ensure we use the same tsconfig.json file for all compilations.
Next steps are to make it the same tsconfig.json file used by the editor
PR Close#20964
When the SW fetches URLs listed in a manifest with hashes, it checks
the content hash against the manifest to make sure it has the correct
version of the URL. In the event of a mismatch, the SW is supposed to
consider the manifest invalid, and avoid using it. There are 3 cases
to consider by which this can happen.
Case 1: during the initial SW installation, a manifest is activated
without waiting for every URL to be fully loaded. In the background,
every prefetch URL listed by the manifest is requested and cached.
One such prefetch request could fail the hash test, and cause the
manifest to be treated as invalid. In such a case, the SW should
enter a state of EXISTING_CLIENTS_ONLY, as the latest manifest is
invalid.
This case works today.
Case 2: during the initial SW installation, as in Case 1, a manifest
is activated without waiting for each URL to fully load. However,
it's possible that the application could request a URL with a bad
hash before background initialization tries to load that URL. This
happens if, for example, the application has a broken index.html.
In this case, the SW should enter a state of EXISTING_CLIENTS_ONLY,
and serve the request from the network instead.
What happens today is that the internal error escapes the SW and
is returned as a rejected Promise to respondWith(), causing a
browser-level error that the site cannot be loaded, breaking the
site.
This change allows the SW to detect the error and enter the correct
state, falling back on the network if needed.
Case 3: during checkForUpdate(), the SW will try to fully cache the
new update before making it the latest version. Failure here is
complicated - if the page fails to load due to transient network
conditions (timeouts, 500s, etc), then it makes sense to continue
serving the existing cached version, and attempt to activate the
update on the next cycle.
If the page fails due to non-transient conditions though (400 error,
hash mismatch, etc), then the SW should consider the updated
manifest invalid, and enter a state of EXISTING_CLIENTS_ONLY.
Currently, all errors are treated as transient.
This change causes the SW to treat all errors during updates as
non-transient, which can cause the SW to unnecessarily enter a
safe mode. A future change can allow the SW to remain in normal mode
if the error is provably transient.
PR Close#21288
- Add tests target for `test`, `test_node_only` and `test_web` in `core` package.
- Created a `_testing_init` pseudo package where bootstrap code for tests is kept.
- Moved `source_map_util` from `test` to `testing` so to prevent circular dependency.
- Removed `visibility:public` for testing `BUILD` packages.
PR Close#21053
Currently the Service Worker checks for updates only on SW startup,
an event which happens frequently but also nondeterministically. This
makes it hard for developers to observe the update process or reason
about how updates will be delivered to users. This problem is
exacerbated by the DevTools behavior of keeping the SW alive
indefinitely while opened, effectively preventing the page from
updating at all.
This change causes the SW to additionally check for updates on
navigation requests (app page reloads). This creates deterministic
update behavior, and is much easier for developers to reason about.
It does leave the old update-on-SW-startup behavior in place, as
removing that would be a breaking change.
Fixes#20877
Add enough BUILD files to make it possible to
`bazel build packages/core/test`
Also re-format BUILD.bazel files with Buildifier.
Add a CI lint check that they stay formatted.
PR Close#20768
The package.json esm2015 points to the wrong path.
"esm15" should be "esm2015"
Service Worker can't be compiled with use of Closure Compiler
PR Close#20800
Not every application is served from the domain root. The Service
Worker made a bad assumption that it would be, and so requested
/ngsw.json from the domain root.
This change corrects this assumption, and requests ngsw.json without
the leading slash. This causes the request to be interpreted
relative to the SW origin, which will be the application root.
The Service Worker contains a mechanism by which it will postMessage
itself a signal to initialize its caches. Through this mechanism,
initialization happens asynchronously while keeping the SW process
alive.
Unfortunately in Firefox, the SW does not have the ability to
postMessage itself during the activation event. This prevents the
above mechanism from working, and the SW initializes on the next
fetch event, which is often too late.
Therefore, this change has the application wait for SW changes and
tells each new SW to initialize itself. This happens in addition to
the self-signal that the SW attempts to send (as self-signaling is
more reliable). That way even on browsers such as Firefox,
initialization happens eagerly.
Currently a bug exists where attempting to inject SwPush crashes the
application if Service Workers are unsupported. This happens because
SwPush doesn't properly detect that navigator.serviceWorker isn't
set.
This change ensures that all passive observation of SwPush and
SwUpdate doesn't cause crashes, and that calling methods to perform
actions on them results in rejected Promises. It's up to applications
to detect when those services are not available, and refrain from
attempting to use them.
To that end, this change also adds an `isSupported` getter to both
services, so users don't have to rely on feature detection directly
with browser APIs. Currently this simply detects whether the SW API
is present, but in the future it will be expanded to detect whether
a particular browser supports specific APIs (such as push
notifications, for example).
Currently, the way to not use the SW is to not install its module.
However, this means that you can't inject any of its services.
This change adds a ServiceWorkerModule.disabled() MWP, that still
registers all of the right providers but acts as if the browser does
not support Service Workers.
Observable.merge was called using .call() as if it were an operator
and not an Observable factory. This removes the .call() and uses
the factory properly.
PR Close#19962
Importing ServiceWorkerModule.register() will schedule registration of
the Service Worker inside an APP_INITIALIZER. Previously, the Promise
returned by navigator.serviceWorker.register() was returned from the
initializer function. This has the unwanted side effect of blocking
initialization until the SW is registered. Even worse, if the SW script
fails to load, this can cause the app initialization to fail.
The solution is to not return the registration promise from the
initializer function, essentially decoupling registration from the rest
of the initialization flow.
This change is not unit testable as there are no mocks/adapters yet for
navigator.serviceWorker. A future integration test should cover this case
with better fidelity.
PR Close#19936
Currently, the SwUpdate service doesn't receive messages from the SW.
This is because it attempts to subscribe to the 'message' event on
ServiceWorkerRegistration, when really messages are emitted by the
ServiceWorkerContainer.
This change moves to listening on ServiceWorkerContainer and changes
the mocks to reflect the way the browser actually works.
PR Close#19954
There is no difference in runtime (yet) between versioned and unversioned
files. Theoretically, the SW does not have to cache-bust versioned files,
but the SW doesn't cache bust files on the first request anyway, so in the
common case it doesn't matter. If the hash doesn't match, the SW will cache
bust the file to be sure, which is technically unnecessary, but since the
file itself is versioned, the likelihood of this happening is rare.
This fixes a critical bug where versioned files were erroneously not included
in the hashTable in the generated manifest. This could lead to applications
not updating if only versioned files changed in between versions.
PR Close#19837
PushEvent.data is not the data object itself, but an instance representing
the data in wire format, with methods to synchronously decode it to JSON,
ArrayBuffer, etc. NGSW assumes all push data is in JSON format.
PR Close#19764