-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: introduce DocumentMetaMiddleware
- Loading branch information
Showing
2 changed files
with
104 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
import { Middleware } from './Middleware'; | ||
import { Request } from './Request'; | ||
import { Meta, Response } from './Response'; | ||
|
||
export type TitleBuilder = (title: string | undefined, response: Response) => string; | ||
export type MetaBuilder = (meta: Meta|undefined, response: Response) => Meta; | ||
|
||
/** | ||
* Middleware to set the document title and meta tags upon navigation. | ||
*/ | ||
export class DocumentMetaMiddleware extends Middleware{ | ||
/** | ||
* Get the owner document. | ||
*/ | ||
protected document?: Document; | ||
|
||
/** | ||
* Title builder function. | ||
*/ | ||
protected titleBuilder: TitleBuilder; | ||
|
||
/** | ||
* Meta builder function. | ||
*/ | ||
protected metaBuilder: MetaBuilder; | ||
|
||
/** | ||
* Meta tags from last invocation. | ||
*/ | ||
protected currentMeta: Meta = {}; | ||
|
||
/** | ||
* Middleware rule constructor. | ||
* @param document The owner document. | ||
* @param titleBuilder The title builder function. | ||
* @param metaBuilder The meta builder function. | ||
*/ | ||
public constructor(document: Document | undefined = window.document, titleBuilder?: TitleBuilder, metaBuilder?: MetaBuilder) { | ||
super({}); | ||
|
||
this.document = document; | ||
this.titleBuilder = titleBuilder || ((title) => title || ''); | ||
this.metaBuilder = metaBuilder || ((meta) => meta || {}); | ||
} | ||
|
||
/** | ||
* Set document title and meta tags. | ||
* @inheritdoc | ||
*/ | ||
public hookAfter(request: Readonly<Request>, response: Response): Response { | ||
this.setTitle(this.titleBuilder(response.title, response)); | ||
this.setMeta(this.metaBuilder(response.meta, response)); | ||
|
||
return response; | ||
} | ||
|
||
/** | ||
* Update title. | ||
* @param string The title string. | ||
*/ | ||
protected setTitle(title: string): void { | ||
if (this.document === undefined) { | ||
return; | ||
} | ||
|
||
this.document.title = title; | ||
} | ||
|
||
/** | ||
* Update meta tags. | ||
* @param current Metadata for current state. | ||
* @param previous Previous metadata. | ||
*/ | ||
protected setMeta(meta: Meta): void { | ||
if (this.document === undefined) { | ||
return; | ||
} | ||
|
||
const head = this.document.head; | ||
Object.entries(meta).forEach(([name, content]) => { | ||
let meta = head.querySelector(`meta[name="${name}"]`); | ||
if (meta !== null) { | ||
meta.setAttribute('content', content); | ||
|
||
return; | ||
} | ||
|
||
meta = head.ownerDocument.createElement('meta'); | ||
meta.setAttribute('name', name); | ||
meta.setAttribute('content', content); | ||
head.appendChild(meta); | ||
}); | ||
|
||
Object.keys(this.currentMeta) | ||
.filter((name) => !(name in meta)) | ||
.forEach((name) => { | ||
const meta = head.querySelector(`meta[name="${name}"]`); | ||
if (meta !== null) { | ||
meta.remove(); | ||
} | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters