George Kalpakas acd8734937 fix(service-worker): throw when using the unsupported versionedFiles option in config (#33903)
In 5d5c94d83, the deprecated `versionedFiles` option from the SW
asset-group configuration in `ngsw-config.json`. As a result, the
option would be silently ignored and the runtime behavior of the SW
would change (i.e. some files might not be cached and available offline
any more). This change could be easily go unnoticed by the developer.

This commit ensures this does not happen by throwing a build-time error,
when detecting the unsupported `versionedFiles` option with an error
message prompting the user to use the `files` option instead.

Jira issue: [FW-1727](https://angular-team.atlassian.net/browse/FW-1727)

PR Close #33903
2019-11-21 11:43:58 -08:00

185 lines
5.4 KiB
TypeScript

/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {Generator} from '../src/generator';
import {AssetGroup} from '../src/in';
import {MockFilesystem} from '../testing/mock';
describe('Generator', () => {
beforeEach(() => spyOn(Date, 'now').and.returnValue(1234567890123));
it('generates a correct config', async() => {
const fs = new MockFilesystem({
'/index.html': 'This is a test',
'/main.css': 'This is a CSS file',
'/main.js': 'This is a JS file',
'/main.ts': 'This is a TS file',
'/test.txt': 'Another test',
'/foo/test.html': 'Another test',
'/ignored/x.html': 'should be ignored',
});
const gen = new Generator(fs, '/test');
const config = await gen.process({
appData: {
test: true,
},
index: '/index.html',
assetGroups: [{
name: 'test',
resources: {
files: [
'/**/*.html',
'/**/*.?s',
'!/ignored/**',
'/**/*.txt',
],
urls: [
'/absolute/**',
'/some/url?with+escaped+chars',
'relative/*.txt',
],
},
}],
dataGroups: [{
name: 'other',
urls: [
'/api/**',
'relapi/**',
'https://example.com/**/*?with+escaped+chars',
],
cacheConfig: {
maxSize: 100,
maxAge: '3d',
timeout: '1m',
},
}],
navigationUrls: [
'/included/absolute/**',
'!/excluded/absolute/**',
'/included/some/url/with+escaped+chars',
'!excluded/relative/*.txt',
'!/api/?*',
'http://example.com/included',
'!http://example.com/excluded',
],
});
expect(config).toEqual({
configVersion: 1,
timestamp: 1234567890123,
appData: {
test: true,
},
index: '/test/index.html',
assetGroups: [{
name: 'test',
installMode: 'prefetch',
updateMode: 'prefetch',
urls: [
'/test/foo/test.html',
'/test/index.html',
'/test/main.js',
'/test/main.ts',
'/test/test.txt',
],
patterns: [
'\\/absolute\\/.*',
'\\/some\\/url\\?with\\+escaped\\+chars',
'\\/test\\/relative\\/[^/]*\\.txt',
],
}],
dataGroups: [{
name: 'other',
patterns: [
'\\/api\\/.*',
'\\/test\\/relapi\\/.*',
'https:\\/\\/example\\.com\\/(?:.+\\/)?[^/]*\\?with\\+escaped\\+chars',
],
strategy: 'performance',
maxSize: 100,
maxAge: 259200000,
timeoutMs: 60000,
version: 1,
}],
navigationUrls: [
{positive: true, regex: '^\\/included\\/absolute\\/.*$'},
{positive: false, regex: '^\\/excluded\\/absolute\\/.*$'},
{positive: true, regex: '^\\/included\\/some\\/url\\/with\\+escaped\\+chars$'},
{positive: false, regex: '^\\/test\\/excluded\\/relative\\/[^/]*\\.txt$'},
{positive: false, regex: '^\\/api\\/[^/][^/]*$'},
{positive: true, regex: '^http:\\/\\/example\\.com\\/included$'},
{positive: false, regex: '^http:\\/\\/example\\.com\\/excluded$'},
],
hashTable: {
'/test/foo/test.html': '18f6f8eb7b1c23d2bb61bff028b83d867a9e4643',
'/test/index.html': 'a54d88e06612d820bc3be72877c74f257b561b19',
'/test/main.js': '41347a66676cdc0516934c76d9d13010df420f2c',
'/test/main.ts': '7d333e31f0bfc4f8152732bb211a93629484c035',
'/test/test.txt': '18f6f8eb7b1c23d2bb61bff028b83d867a9e4643',
},
});
});
it('uses default `navigationUrls` if not provided', async() => {
const fs = new MockFilesystem({
'/index.html': 'This is a test',
});
const gen = new Generator(fs, '/test');
const config = await gen.process({
index: '/index.html',
});
expect(config).toEqual({
configVersion: 1,
timestamp: 1234567890123,
appData: undefined,
index: '/test/index.html',
assetGroups: [],
dataGroups: [],
navigationUrls: [
{positive: true, regex: '^\\/.*$'},
{positive: false, regex: '^\\/(?:.+\\/)?[^/]*\\.[^/]*$'},
{positive: false, regex: '^\\/(?:.+\\/)?[^/]*__[^/]*$'},
{positive: false, regex: '^\\/(?:.+\\/)?[^/]*__[^/]*\\/.*$'},
],
hashTable: {},
});
});
it('throws if the obsolete `versionedFiles` is used', async() => {
const fs = new MockFilesystem({
'/index.html': 'This is a test',
'/main.js': 'This is a JS file',
});
const gen = new Generator(fs, '/test');
try {
await gen.process({
index: '/index.html',
assetGroups: [{
name: 'test',
resources: {
files: [
'/*.html',
],
versionedFiles: [
'/*.js',
],
} as AssetGroup['resources'] &
{versionedFiles: string[]},
}],
});
throw new Error('Processing should have failed due to \'versionedFiles\'.');
} catch (err) {
expect(err).toEqual(new Error(
'Asset-group \'test\' in \'ngsw-config.json\' uses the \'versionedFiles\' option, ' +
'which is no longer supported. Use \'files\' instead.'));
}
});
});