Skip to content

Commit 537c7d6

Browse files
committed
Merge remote-tracking branch 'ybnd/cache-bust-dynamic-configuration-7.6_CLEAN' into cache-bust-dynamic-configuration-8.x
2 parents 716825b + 39b4e63 commit 537c7d6

File tree

6 files changed

+53
-14
lines changed

6 files changed

+53
-14
lines changed

server.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ function serverSideRender(req, res, next, sendToUser: boolean = true) {
252252
commonEngine
253253
.render({
254254
bootstrap,
255-
documentFilePath: hashedFileMapping.resolve(indexHtml),
255+
documentFilePath: indexHtml,
256256
inlineCriticalCss: environment.ssr.inlineCriticalCss,
257257
url: `${protocol}://${headers.host}${originalUrl}`,
258258
publicPath: DIST_FOLDER,
@@ -314,7 +314,11 @@ function serverSideRender(req, res, next, sendToUser: boolean = true) {
314314
* @param res current response
315315
*/
316316
function clientSideRender(req, res) {
317-
res.sendFile(hashedFileMapping.resolve(indexHtml));
317+
res.sendFile(indexHtml, {
318+
headers: {
319+
'Cache-Control': 'no-cache, no-store',
320+
},
321+
});
318322
}
319323

320324

@@ -325,7 +329,11 @@ function clientSideRender(req, res) {
325329
*/
326330
function addCacheControl(req, res, next) {
327331
// instruct browser to revalidate
328-
res.header('Cache-Control', environment.cache.control || 'max-age=604800');
332+
if (environment.cache.noCacheFiles.includes(req.originalUrl)) {
333+
res.header('Cache-Control', 'no-cache, no-store');
334+
} else {
335+
res.header('Cache-Control', environment.cache.control || 'max-age=604800');
336+
}
329337
next();
330338
}
331339

src/config/cache-config.interface.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ export interface CacheConfig extends Config {
77
};
88
// Cache-Control HTTP Header
99
control: string;
10+
// These static files should not be cached (paths relative to dist/browser, including the leading slash)
11+
noCacheFiles: string[]
1012
autoSync: AutoSyncConfig;
1113
// In-memory caches of server-side rendered (SSR) content. These caches can be used to limit the frequency
1214
// of re-generating SSR pages to improve performance.

src/config/config.server.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,12 @@ export const buildAppConfig = (destConfigPath?: string, mapping?: ServerHashedFi
256256
mapping.add(destConfigPath, content);
257257
if (!appConfig.ssr.enabled) {
258258
// If we're serving for CSR we can retrieve the configuration before JS is loaded/executed
259-
mapping.addHeadLink(destConfigPath, 'preload', 'fetch', 'anonymous');
259+
mapping.addHeadLink({
260+
path: destConfigPath,
261+
rel: 'preload',
262+
as: 'fetch',
263+
crossorigin: 'anonymous',
264+
});
260265
}
261266
}
262267

src/config/default-app-config.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ export class DefaultAppConfig implements AppConfig {
7676
},
7777
// Cache-Control HTTP Header
7878
control: 'max-age=604800', // revalidate browser
79+
// These static files should not be cached (paths relative to dist/browser, including the leading slash)
80+
noCacheFiles: [
81+
'/index.html', // see https://web.dev/articles/http-cache#unversioned-urls
82+
],
7983
autoSync: {
8084
defaultTime: 0,
8185
maxBufferSize: 100,

src/environments/environment.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ export const environment: BuildConfig = {
5757
},
5858
// msToLive: 1000, // 15 minutes
5959
control: 'max-age=60',
60+
// These static files should not be cached (paths relative to dist/browser, including the leading slash)
61+
noCacheFiles: [
62+
'/index.html', // see https://web.dev/articles/http-cache#unversioned-urls
63+
],
6064
autoSync: {
6165
defaultTime: 0,
6266
maxBufferSize: 100,

src/modules/dynamic-hash/hashed-file-mapping.server.ts

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,15 @@ import {
2929
ID,
3030
} from './hashed-file-mapping';
3131

32+
const HEAD_LINK_CLASS = 'hfm';
33+
34+
interface HeadLink {
35+
path: string;
36+
rel: string;
37+
as: string;
38+
crossorigin?: string;
39+
}
40+
3241
/**
3342
* Server-side implementation of {@link HashedFileMapping}.
3443
* Registers dynamically hashed files and stores them in index.html for the browser to use.
@@ -37,7 +46,7 @@ export class ServerHashedFileMapping extends HashedFileMapping {
3746
public readonly indexPath: string;
3847
private readonly indexContent: string;
3948

40-
protected readonly headLinks: Set<string> = new Set();
49+
protected readonly headLinks: Set<HeadLink> = new Set();
4150

4251
constructor(
4352
private readonly root: string,
@@ -114,7 +123,11 @@ export class ServerHashedFileMapping extends HashedFileMapping {
114123

115124
// We know this CSS is likely needed, so wecan avoid a FOUC by retrieving it in advance
116125
// Angular does the same for global styles, but doesn't "know" about out themes
117-
this.addHeadLink(p, 'prefetch', 'style');
126+
this.addHeadLink({
127+
path: p,
128+
rel: 'prefetch',
129+
as: 'style',
130+
});
118131

119132
this.ensureCompressedFilesAssumingUnchangedContent(p, hp, '.br');
120133
this.ensureCompressedFilesAssumingUnchangedContent(p, hp, '.gz');
@@ -124,14 +137,17 @@ export class ServerHashedFileMapping extends HashedFileMapping {
124137
/**
125138
* Include a head link for a given resource to the index HTML.
126139
*/
127-
addHeadLink(path: string, rel: string, as: string, crossorigin?: string) {
128-
const href = relative(this.root, this.resolve(path));
140+
addHeadLink(headLink: HeadLink) {
141+
this.headLinks.add(headLink);
142+
}
129143

130-
if (hasValue(crossorigin)) {
131-
this.headLinks.add(`<link rel="${rel}" as="${as}" crossorigin="${crossorigin}" href="${href}">`);
144+
private renderHeadLink(link: HeadLink): string {
145+
const href = relative(this.root, this.resolve(link.path));
132146

147+
if (hasValue(link.crossorigin)) {
148+
return `<link rel="${link.rel}" as="${link.as}" href="${href}" crossorigin="${link.crossorigin}" class="${HEAD_LINK_CLASS}">`;
133149
} else {
134-
this.headLinks.add(`<link rel="${rel}" as="${as}" href="${href}">`);
150+
return `<link rel="${link.rel}" as="${link.as}" href="${href}" class="${HEAD_LINK_CLASS}">`;
135151
}
136152
}
137153

@@ -156,15 +172,15 @@ export class ServerHashedFileMapping extends HashedFileMapping {
156172
}, {});
157173

158174
const root = parse(this.indexContent);
159-
root.querySelector(`script#${ID}`)?.remove();
175+
root.querySelectorAll(`script#${ID}, link.${HEAD_LINK_CLASS}`)?.forEach(e => e.remove());
160176
root.querySelector('head')
161177
.appendChild(`<script id="${ID}" type="application/json">${JSON.stringify(out)}</script>` as any);
162178

163179
for (const headLink of this.headLinks) {
164180
root.querySelector('head')
165-
.appendChild(headLink as any);
181+
.appendChild(this.renderHeadLink(headLink) as any);
166182
}
167183

168-
this.add(this.indexPath, root.toString());
184+
writeFileSync(this.indexPath, root.toString());
169185
}
170186
}

0 commit comments

Comments
 (0)