fix(docs-infra): correctly serve `index.html` with a query string (#42547)
Previously, due to a bug in Firebase hosting, requests to `/index.html?<query>` would lead to an infinite redirect and eventually a failure. This affected, for example, cache-busting requests from the ServiceWorker, which look like: `/index.html?ngsw-cache-bust=...` For more details see https://github.com/angular/angular/issues/42518#issuecomment-858545483 This commit temporarily works around the bug by explicitly redirecting `/index.html?<query>` to `/?<query>`. Fixes #42518 PR Close #42547
This commit is contained in:
parent
828fde6e0d
commit
56a0582d79
|
@ -124,6 +124,10 @@
|
|||
{"type": 301, "source": "/testing", "destination": "/guide/testing"},
|
||||
{"type": 301, "source": "/testing/**", "destination": "/guide/testing"},
|
||||
|
||||
// Work around Firebase hosting bug with `/index.html?<query>` leading to infinite redirects.
|
||||
// See https://github.com/angular/angular/issues/42518#issuecomment-858545483 for details.
|
||||
{"type": 301, "source": "/index.html:query*", "destination": "/:query*"},
|
||||
|
||||
// Strip off the `.html` extension, because Firebase will not do this automatically any more
|
||||
// (unless the new URL points to an existing file, which is not necessarily the case here).
|
||||
{"type": 301, "source": "/:somePath*/:file.html", "destination": "/:somePath*/:file"},
|
||||
|
|
|
@ -188,6 +188,7 @@
|
|||
/guide/updating-to-version-10 --> https://v10.angular.io/guide/updating-to-version-10
|
||||
/guide/updating-to-version-11 --> https://v11.angular.io/guide/updating-to-version-11
|
||||
/guide/webpack --> https://v5.angular.io/guide/webpack
|
||||
/index.html?foo=bar --> /?foo=bar
|
||||
/news --> https://blog.angular.io/
|
||||
/news.html --> https://blog.angular.io/
|
||||
/start/data --> /start/start-data
|
||||
|
|
|
@ -137,6 +137,21 @@ describe('FirebaseRedirectSource', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should capture a named param not preceded by a slash', () => {
|
||||
testGlobMatch('/a/b:x', {
|
||||
named: ['x']
|
||||
}, {
|
||||
'/a/bc': {x: 'c'},
|
||||
'/a/bcd': {x: 'cd'},
|
||||
'/a/b-c': {x: '-c'},
|
||||
'/a': undefined,
|
||||
'/a/': undefined,
|
||||
'/a/b/': undefined,
|
||||
'/a/cd': undefined,
|
||||
'/a/b/c': undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it('should capture multiple named params', () => {
|
||||
testGlobMatch('/a/:b/:c', {
|
||||
named: ['b', 'c']
|
||||
|
@ -187,6 +202,35 @@ describe('FirebaseRedirectSource', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should capture a rest param not preceded by a slash', () => {
|
||||
testGlobMatch('/a:bc*', {
|
||||
rest: ['bc']
|
||||
}, {
|
||||
'/ab': {bc: 'b'},
|
||||
'/a/b': {bc: '/b'},
|
||||
'/a/bcd': {bc: '/bcd'},
|
||||
'/a/b/c': {bc: '/b/c'},
|
||||
'/a//': {bc: '//'},
|
||||
'/ab/c': {bc: 'b/c'},
|
||||
'/ab/c/': {bc: 'b/c/'},
|
||||
'/a': {bc: undefined},
|
||||
'/bc': undefined,
|
||||
});
|
||||
|
||||
testGlobMatch('/a/b:c*', {
|
||||
rest: ['c']
|
||||
}, {
|
||||
'/a/bc': {c: 'c'},
|
||||
'/a/bcd': {c: 'cd'},
|
||||
'/a/b/': {c: '/'},
|
||||
'/a/b/c/': {c: '/c/'},
|
||||
'/a/ba/c': {c: 'a/c'},
|
||||
'/a/ba/c/': {c: 'a/c/'},
|
||||
'/a/b': {c: undefined},
|
||||
'/a/': undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it('should capture a rest param mixed with a named param', () => {
|
||||
testGlobMatch('/:abc/:rest*', {
|
||||
named: ['abc'],
|
||||
|
|
|
@ -24,8 +24,8 @@ export class FirebaseRedirectSource {
|
|||
const star = /\*/g;
|
||||
const doubleStar = /(^|\/)\*\*($|\/)/g; // e.g. a/**/b or **/b or a/** but not a**b
|
||||
const modifiedPatterns = /(.)\(([^)]+)\)/g; // e.g. `@(a|b)
|
||||
const restParam = /\/:([A-Za-z]+)\*/g; // e.g. `:rest*`
|
||||
const namedParam = /\/:([A-Za-z]+)/g; // e.g. `:api`
|
||||
const restParam = /(\/?):([A-Za-z]+)\*/g; // e.g. `:rest*`
|
||||
const namedParam = /(\/?):([A-Za-z]+)/g; // e.g. `:api`
|
||||
const possiblyEmptyInitialSegments = /^\.🐷\//g; // e.g. `**/a` can also match `a`
|
||||
const possiblyEmptySegments = /\/\.🐷\//g; // e.g. `a/**/b` can also match `a/b`
|
||||
const willBeStar = /🐷/g; // e.g. `a**b` not matched by previous rule
|
||||
|
@ -35,12 +35,12 @@ export class FirebaseRedirectSource {
|
|||
const pattern = glob
|
||||
.replace(dot, '\\.')
|
||||
.replace(modifiedPatterns, replaceModifiedPattern)
|
||||
.replace(restParam, (_, param) => {
|
||||
.replace(restParam, (_, leadingSlash, groupName) => {
|
||||
// capture the rest of the string
|
||||
restNamedGroups.push(param);
|
||||
return `(?:/(?<${param}>.🐷))?`;
|
||||
restNamedGroups.push(groupName);
|
||||
return `(?:${leadingSlash}(?<${groupName}>.🐷))?`;
|
||||
})
|
||||
.replace(namedParam, `/(?<$1>[^/]+)`)
|
||||
.replace(namedParam, `$1(?<$2>[^/]+)`)
|
||||
.replace(doubleStar, '$1.🐷$2') // use the pig to avoid replacing ** in next rule
|
||||
.replace(star, '[^/]*') // match a single segment
|
||||
.replace(possiblyEmptyInitialSegments, '(?:.*)')// deal with **/ special cases
|
||||
|
|
Loading…
Reference in New Issue