-
-
Notifications
You must be signed in to change notification settings - Fork 31
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(ssr): add link asset preload header service (#3343)
- Loading branch information
Showing
33 changed files
with
653 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
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,52 @@ | ||
module.exports = { | ||
extends: '../../.eslintrc.js', | ||
ignorePatterns: [ | ||
'!**/*' | ||
], | ||
overrides: [ | ||
{ | ||
files: [ | ||
'*.ts' | ||
], | ||
parserOptions: { | ||
project: [ | ||
'libs/ssr/tsconfig.lib.json', | ||
'libs/ssr/tsconfig.spec.json' | ||
], | ||
createDefaultProgram: true | ||
}, | ||
rules: { | ||
'@angular-eslint/component-class-suffix': [ | ||
'error', | ||
{ | ||
suffixes: [ | ||
'Component', | ||
'Container' | ||
] | ||
} | ||
], | ||
'@angular-eslint/component-selector': [ | ||
'error', | ||
{ | ||
type: 'lib', | ||
prefix: 'kebab-case' | ||
} | ||
], | ||
'@angular-eslint/directive-selector': [ | ||
'error', | ||
{ | ||
type: 'attribute', | ||
prefix: 'lib', | ||
style: 'camelCase' | ||
} | ||
], | ||
} | ||
}, | ||
{ | ||
files: [ | ||
'*.html' | ||
], | ||
rules: {} | ||
} | ||
] | ||
} |
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,16 @@ | ||
# @daffodil/ssr | ||
`@daffodil/ssr` contains utilities useful for dealing with and adding features to the Angular SSR. | ||
|
||
## Installation | ||
To install `@daffodil/ssr`, use the following commands in your terminal. | ||
|
||
Install with npm: | ||
```bash | ||
npm install @daffodil/ssr --save | ||
``` | ||
|
||
Install with yarn: | ||
|
||
```bash | ||
yarn add @daffodil/ssr | ||
``` |
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,6 @@ | ||
{ | ||
"$schema": "../../../node_modules/ng-packagr/ng-entrypoint.schema.json", | ||
"lib": { | ||
"entryFile": "src/index.ts" | ||
} | ||
} |
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 @@ | ||
export * from './public_api'; |
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 @@ | ||
export * from './response/public_api'; |
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,47 @@ | ||
import { Response } from 'express'; | ||
|
||
import { DaffSsrExpressResponse } from './class'; | ||
|
||
describe('@daffodil/ssr/express | DaffSsrExpressResponse', () => { | ||
let service: DaffSsrExpressResponse; | ||
let responseSpy: jasmine.SpyObj<Response>; | ||
|
||
beforeEach(() => { | ||
responseSpy = jasmine.createSpyObj('Response', [ | ||
'getHeader', | ||
'setHeader', | ||
'appendHeader', | ||
'status', | ||
]); | ||
|
||
service = new DaffSsrExpressResponse(responseSpy); | ||
}); | ||
|
||
describe('get', () => { | ||
it('should get headers to the express response', () => { | ||
responseSpy.getHeader.withArgs('name').and.returnValue('value'); | ||
expect(service.get('name')).toEqual('value'); | ||
}); | ||
}); | ||
|
||
describe('set', () => { | ||
it('should set headers to the express response', () => { | ||
service.set('name', 'value'); | ||
expect(responseSpy.setHeader).toHaveBeenCalledWith('name', 'value'); | ||
}); | ||
}); | ||
|
||
describe('append', () => { | ||
it('should append headers to the express response', () => { | ||
service.append('name', 'value'); | ||
expect(responseSpy.appendHeader).toHaveBeenCalledWith('name', 'value'); | ||
}); | ||
}); | ||
|
||
describe('status', () => { | ||
it('should set the status code on the express response', () => { | ||
service.status(404); | ||
expect(responseSpy.status).toHaveBeenCalledWith(404); | ||
}); | ||
}); | ||
}); |
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,30 @@ | ||
import { Response } from 'express'; | ||
|
||
import { DaffSsrResponse } from '@daffodil/ssr'; | ||
|
||
/** | ||
* A response that interfaces with the express request. | ||
* | ||
* @inheritdoc | ||
*/ | ||
export class DaffSsrExpressResponse implements DaffSsrResponse { | ||
constructor( | ||
protected response: Response, | ||
) {} | ||
|
||
get(header: string): Array<string> | string { | ||
return String(this.response.getHeader(header)) || ''; | ||
} | ||
|
||
set(name: string, value: Array<string> | string): void { | ||
this.response.setHeader(name, value); | ||
} | ||
|
||
append(name: string, value: Array<string> | string): void { | ||
this.response.appendHeader(name, value); | ||
} | ||
|
||
status(code: number): void { | ||
this.response.status(code); | ||
} | ||
} |
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,12 @@ | ||
import { Response } from 'express'; | ||
|
||
import { provideDaffSsrResponse } from '@daffodil/ssr'; | ||
|
||
import { DaffSsrExpressResponse } from './class'; | ||
|
||
/** | ||
* Provides `DaffSsrExpressResponse` to `DAFF_SSR_RESPONSE`. | ||
*/ | ||
export const provideDaffSsrExpressResponse = (response: Response) => | ||
provideDaffSsrResponse(new DaffSsrExpressResponse(response)); | ||
|
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,2 @@ | ||
export * from './provider'; | ||
export * from './class'; |
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,4 @@ | ||
# Platforms | ||
There are various server platforms that can be used with angular SSR. The following list enumerates the platforms that Daffodil supports and the corresponding guide for setting up that platform. | ||
|
||
- [Express](/libs/ssr/guides/platforms/express.md) |
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,28 @@ | ||
# Express | ||
Express is the default and recommended Angular SSR platform. | ||
|
||
To get started, provide the response token in your common engine render: | ||
|
||
`server.ts` | ||
```ts | ||
import { provideDaffSsrExpressResponse } from '@daffodil/ssr/express' | ||
|
||
// the rest of the file from https://angular.dev/guide/ssr#configure-server-side-rendering is omitted | ||
|
||
server.get('*', (req, res, next) => { | ||
const {protocol, originalUrl, baseUrl, headers} = req; | ||
commonEngine | ||
.render({ | ||
bootstrap, | ||
documentFilePath: indexHtml, | ||
url: `${protocol}://${headers.host}${originalUrl}`, | ||
publicPath: browserDistFolder, | ||
providers: [ | ||
{provide: APP_BASE_HREF, useValue: req.baseUrl}, | ||
provideDaffSsrExpressResponse(res), | ||
], | ||
}) | ||
.then((html) => res.send(html)) | ||
.catch((err) => next(err)); | ||
}); | ||
``` |
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,61 @@ | ||
# Usage | ||
|
||
## Setting the Response Status Code | ||
Inject the `DAFF_SSR_RESPONSE` and call `status` to set the status code on the SSR document response. | ||
|
||
app.component.ts | ||
```ts | ||
import { DaffSsrResponse, DAFF_SSR_RESPONSE } from '@daffodil/ssr' | ||
|
||
@Component() | ||
class AppComponent { | ||
constructor( | ||
@Inject(DAFF_SSR_RESPONSE) private response: DaffSsrResponse | ||
) { | ||
this.response.status(404); | ||
} | ||
} | ||
``` | ||
|
||
## Adding a Response Header | ||
Inject the `DAFF_SSR_RESPONSE` and call `append` to add a header to the SSR document response. | ||
|
||
app.component.ts | ||
```ts | ||
import { DaffSsrResponse, DAFF_SSR_RESPONSE } from '@daffodil/ssr' | ||
|
||
@Component() | ||
class AppComponent { | ||
constructor( | ||
@Inject(DAFF_SSR_RESPONSE) private response: DaffSsrResponse | ||
) { | ||
this.response.append('Link', '<https://www.mydomain.com>; rel=preconnect'); | ||
} | ||
} | ||
``` | ||
|
||
## Asset Preloading | ||
Preloading certain important assets can boost the initial render of a page. By including [`Link` headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Link) on the SSR document response, the browser can load these assets while parsing the HTML. | ||
|
||
Daffodil provides a `DaffSsrHeaderLinkAssetPreloader` service to assist this process. The following example demonstrates how to preload an asset that is needed for the initial render of a page. | ||
|
||
```ts | ||
import { | ||
DaffSsrHeaderLinkAssetPreloader, | ||
DaffSsrHeadersLinkPreloadAssetKind, | ||
DaffSsrHeadersLinkPreloadAssetPriority, | ||
} from '@daffodil/ssr' | ||
|
||
@Component() | ||
class AppComponent { | ||
constructor( | ||
private assetPreloadService: DaffSsrHeaderLinkAssetPreloader | ||
) { | ||
this.assetPreloadService.addHeader( | ||
'/asset/logo.png', | ||
DaffSsrHeadersLinkPreloadAssetKind.IMAGE, | ||
DaffSsrHeadersLinkPreloadAssetPriority.HIGH, | ||
); | ||
} | ||
} | ||
``` |
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,10 @@ | ||
baseConfiguration = require('../../tools/karma/karma.conf'); | ||
|
||
module.exports = function (config) { | ||
baseConfiguration(config); | ||
config.set({ | ||
coverageIstanbulReporter: { | ||
dir: require('path').join(__dirname, '../../coverage/libs/ssr'), | ||
}, | ||
}); | ||
}; |
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,7 @@ | ||
{ | ||
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json", | ||
"dest": "../../dist/ssr", | ||
"lib": { | ||
"entryFile": "src/index.ts" | ||
} | ||
} |
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,7 @@ | ||
{ | ||
"$schema": "../../node_modules/ng-packagr/ng-package.schema.json", | ||
"dest": "../../dist/ssr", | ||
"lib": { | ||
"entryFile": "src/index.ts" | ||
} | ||
} |
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,39 @@ | ||
{ | ||
"name": "@daffodil/ssr", | ||
"nx": { | ||
"targets": { | ||
"build": { | ||
"outputs": ["{workspaceRoot}/dist/ssr"] | ||
} | ||
} | ||
}, | ||
"version": "0.0.0-PLACEHOLDER", | ||
"description": "Adds features and provides utils for the Angular ssr.", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/graycoreio/daffodil" | ||
}, | ||
"author": "Graycore LLC", | ||
"license": "MIT", | ||
"bugs": { | ||
"url": "https://github.com/graycoreio/daffodil/issues" | ||
}, | ||
"scripts": { | ||
"build": "ng build ssr --configuration production", | ||
"lint": "cd ../.. && ng lint ssr", | ||
"lint:fix": "npm run lint -- --fix", | ||
"test": "ng test ssr --watch=false --browsers=ChromeHeadless", | ||
"publish": "cd ../../dist/ssr && npm publish --access=public" | ||
}, | ||
"peerDependencies": { | ||
"@angular/common": "0.0.0-PLACEHOLDER", | ||
"@angular/core": "0.0.0-PLACEHOLDER", | ||
"@daffodil/core": "0.0.0-PLACEHOLDER", | ||
"@angular/ssr": "0.0.0-PLACEHOLDER", | ||
"rxjs": "0.0.0-PLACEHOLDER", | ||
"express": "0.0.0-PLACEHOLDER" | ||
}, | ||
"devDependencies": { | ||
"@daffodil/core": "0.0.0-PLACEHOLDER" | ||
} | ||
} |
Oops, something went wrong.