Updated the files.
This commit is contained in:
parent
1553e6b971
commit
753967d4f5
23418 changed files with 3784666 additions and 0 deletions
8
my-app/node_modules/@angular/ssr/esm2022/index.mjs
generated
vendored
Executable file
8
my-app/node_modules/@angular/ssr/esm2022/index.mjs
generated
vendored
Executable file
|
@ -0,0 +1,8 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google LLC 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
|
||||
*/
|
||||
export * from './public_api';
|
8
my-app/node_modules/@angular/ssr/esm2022/public_api.mjs
generated
vendored
Executable file
8
my-app/node_modules/@angular/ssr/esm2022/public_api.mjs
generated
vendored
Executable file
|
@ -0,0 +1,8 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google LLC 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
|
||||
*/
|
||||
export { CommonEngine } from './src/common-engine';
|
137
my-app/node_modules/@angular/ssr/esm2022/src/common-engine.mjs
generated
vendored
Executable file
137
my-app/node_modules/@angular/ssr/esm2022/src/common-engine.mjs
generated
vendored
Executable file
|
@ -0,0 +1,137 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google LLC 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 { renderApplication, renderModule, ɵSERVER_CONTEXT } from '@angular/platform-server';
|
||||
import * as fs from 'node:fs';
|
||||
import { dirname, join, normalize, resolve } from 'node:path';
|
||||
import { URL } from 'node:url';
|
||||
import { InlineCriticalCssProcessor } from './inline-css-processor';
|
||||
import { noopRunMethodAndMeasurePerf, printPerformanceLogs, runMethodAndMeasurePerf, } from './peformance-profiler';
|
||||
const SSG_MARKER_REGEXP = /ng-server-context=["']\w*\|?ssg\|?\w*["']/;
|
||||
/**
|
||||
* A common engine to use to server render an application.
|
||||
*/
|
||||
export class CommonEngine {
|
||||
options;
|
||||
templateCache = new Map();
|
||||
inlineCriticalCssProcessor;
|
||||
pageIsSSG = new Map();
|
||||
constructor(options) {
|
||||
this.options = options;
|
||||
this.inlineCriticalCssProcessor = new InlineCriticalCssProcessor({
|
||||
minify: false,
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Render an HTML document for a specific URL with specified
|
||||
* render options
|
||||
*/
|
||||
async render(opts) {
|
||||
const enablePerformanceProfiler = this.options?.enablePerformanceProfiler;
|
||||
const runMethod = enablePerformanceProfiler
|
||||
? runMethodAndMeasurePerf
|
||||
: noopRunMethodAndMeasurePerf;
|
||||
let html = await runMethod('Retrieve SSG Page', () => this.retrieveSSGPage(opts));
|
||||
if (html === undefined) {
|
||||
html = await runMethod('Render Page', () => this.renderApplication(opts));
|
||||
if (opts.inlineCriticalCss !== false) {
|
||||
const { content, errors, warnings } = await runMethod('Inline Critical CSS', () =>
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
this.inlineCriticalCss(html, opts));
|
||||
html = content;
|
||||
// eslint-disable-next-line no-console
|
||||
warnings?.forEach((m) => console.warn(m));
|
||||
// eslint-disable-next-line no-console
|
||||
errors?.forEach((m) => console.error(m));
|
||||
}
|
||||
}
|
||||
if (enablePerformanceProfiler) {
|
||||
printPerformanceLogs();
|
||||
}
|
||||
return html;
|
||||
}
|
||||
inlineCriticalCss(html, opts) {
|
||||
return this.inlineCriticalCssProcessor.process(html, {
|
||||
outputPath: opts.publicPath ?? (opts.documentFilePath ? dirname(opts.documentFilePath) : ''),
|
||||
});
|
||||
}
|
||||
async retrieveSSGPage(opts) {
|
||||
const { publicPath, documentFilePath, url } = opts;
|
||||
if (!publicPath || !documentFilePath || url === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
const { pathname } = new URL(url, 'resolve://');
|
||||
// Do not use `resolve` here as otherwise it can lead to path traversal vulnerability.
|
||||
// See: https://portswigger.net/web-security/file-path-traversal
|
||||
const pagePath = join(publicPath, pathname, 'index.html');
|
||||
if (this.pageIsSSG.get(pagePath)) {
|
||||
// Serve pre-rendered page.
|
||||
return fs.promises.readFile(pagePath, 'utf-8');
|
||||
}
|
||||
if (!pagePath.startsWith(normalize(publicPath))) {
|
||||
// Potential path traversal detected.
|
||||
return undefined;
|
||||
}
|
||||
if (pagePath === resolve(documentFilePath) || !(await exists(pagePath))) {
|
||||
// View matches with prerender path or file does not exist.
|
||||
this.pageIsSSG.set(pagePath, false);
|
||||
return undefined;
|
||||
}
|
||||
// Static file exists.
|
||||
const content = await fs.promises.readFile(pagePath, 'utf-8');
|
||||
const isSSG = SSG_MARKER_REGEXP.test(content);
|
||||
this.pageIsSSG.set(pagePath, isSSG);
|
||||
return isSSG ? content : undefined;
|
||||
}
|
||||
async renderApplication(opts) {
|
||||
const moduleOrFactory = this.options?.bootstrap ?? opts.bootstrap;
|
||||
if (!moduleOrFactory) {
|
||||
throw new Error('A module or bootstrap option must be provided.');
|
||||
}
|
||||
const extraProviders = [
|
||||
{ provide: ɵSERVER_CONTEXT, useValue: 'ssr' },
|
||||
...(opts.providers ?? []),
|
||||
...(this.options?.providers ?? []),
|
||||
];
|
||||
let document = opts.document;
|
||||
if (!document && opts.documentFilePath) {
|
||||
document = await this.getDocument(opts.documentFilePath);
|
||||
}
|
||||
const commonRenderingOptions = {
|
||||
url: opts.url,
|
||||
document,
|
||||
};
|
||||
return isBootstrapFn(moduleOrFactory)
|
||||
? renderApplication(moduleOrFactory, {
|
||||
platformProviders: extraProviders,
|
||||
...commonRenderingOptions,
|
||||
})
|
||||
: renderModule(moduleOrFactory, { extraProviders, ...commonRenderingOptions });
|
||||
}
|
||||
/** Retrieve the document from the cache or the filesystem */
|
||||
async getDocument(filePath) {
|
||||
let doc = this.templateCache.get(filePath);
|
||||
if (!doc) {
|
||||
doc = await fs.promises.readFile(filePath, 'utf-8');
|
||||
this.templateCache.set(filePath, doc);
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
}
|
||||
async function exists(path) {
|
||||
try {
|
||||
await fs.promises.access(path, fs.constants.F_OK);
|
||||
return true;
|
||||
}
|
||||
catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
function isBootstrapFn(value) {
|
||||
// We can differentiate between a module and a bootstrap function by reading compiler-generated `ɵmod` static property:
|
||||
return typeof value === 'function' && !('ɵmod' in value);
|
||||
}
|
181
my-app/node_modules/@angular/ssr/esm2022/src/inline-css-processor.mjs
generated
vendored
Executable file
181
my-app/node_modules/@angular/ssr/esm2022/src/inline-css-processor.mjs
generated
vendored
Executable file
|
@ -0,0 +1,181 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google LLC 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 Critters from 'critters';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
/**
|
||||
* Pattern used to extract the media query set by Critters in an `onload` handler.
|
||||
*/
|
||||
const MEDIA_SET_HANDLER_PATTERN = /^this\.media=["'](.*)["'];?$/;
|
||||
/**
|
||||
* Name of the attribute used to save the Critters media query so it can be re-assigned on load.
|
||||
*/
|
||||
const CSP_MEDIA_ATTR = 'ngCspMedia';
|
||||
/**
|
||||
* Script text used to change the media value of the link tags.
|
||||
*
|
||||
* NOTE:
|
||||
* We do not use `document.querySelectorAll('link').forEach((s) => s.addEventListener('load', ...)`
|
||||
* because this does not always fire on Chome.
|
||||
* See: https://github.com/angular/angular-cli/issues/26932 and https://crbug.com/1521256
|
||||
*/
|
||||
const LINK_LOAD_SCRIPT_CONTENT = [
|
||||
'(() => {',
|
||||
` const CSP_MEDIA_ATTR = '${CSP_MEDIA_ATTR}';`,
|
||||
' const documentElement = document.documentElement;',
|
||||
' const listener = (e) => {',
|
||||
' const target = e.target;',
|
||||
` if (!target || target.tagName !== 'LINK' || !target.hasAttribute(CSP_MEDIA_ATTR)) {`,
|
||||
' return;',
|
||||
' }',
|
||||
' target.media = target.getAttribute(CSP_MEDIA_ATTR);',
|
||||
' target.removeAttribute(CSP_MEDIA_ATTR);',
|
||||
// Remove onload listener when there are no longer styles that need to be loaded.
|
||||
' if (!document.head.querySelector(`link[${CSP_MEDIA_ATTR}]`)) {',
|
||||
` documentElement.removeEventListener('load', listener);`,
|
||||
' }',
|
||||
' };',
|
||||
// We use an event with capturing (the true parameter) because load events don't bubble.
|
||||
` documentElement.addEventListener('load', listener, true);`,
|
||||
'})();',
|
||||
].join('\n');
|
||||
class CrittersExtended extends Critters {
|
||||
optionsExtended;
|
||||
resourceCache;
|
||||
warnings = [];
|
||||
errors = [];
|
||||
initialEmbedLinkedStylesheet;
|
||||
addedCspScriptsDocuments = new WeakSet();
|
||||
documentNonces = new WeakMap();
|
||||
constructor(optionsExtended, resourceCache) {
|
||||
super({
|
||||
logger: {
|
||||
warn: (s) => this.warnings.push(s),
|
||||
error: (s) => this.errors.push(s),
|
||||
info: () => { },
|
||||
},
|
||||
logLevel: 'warn',
|
||||
path: optionsExtended.outputPath,
|
||||
publicPath: optionsExtended.deployUrl,
|
||||
compress: !!optionsExtended.minify,
|
||||
pruneSource: false,
|
||||
reduceInlineStyles: false,
|
||||
mergeStylesheets: false,
|
||||
// Note: if `preload` changes to anything other than `media`, the logic in
|
||||
// `embedLinkedStylesheetOverride` will have to be updated.
|
||||
preload: 'media',
|
||||
noscriptFallback: true,
|
||||
inlineFonts: true,
|
||||
});
|
||||
this.optionsExtended = optionsExtended;
|
||||
this.resourceCache = resourceCache;
|
||||
// We can't use inheritance to override `embedLinkedStylesheet`, because it's not declared in
|
||||
// the `Critters` .d.ts which means that we can't call the `super` implementation. TS doesn't
|
||||
// allow for `super` to be cast to a different type.
|
||||
this.initialEmbedLinkedStylesheet = this.embedLinkedStylesheet;
|
||||
this.embedLinkedStylesheet = this.embedLinkedStylesheetOverride;
|
||||
}
|
||||
async readFile(path) {
|
||||
let resourceContent = this.resourceCache.get(path);
|
||||
if (resourceContent === undefined) {
|
||||
resourceContent = await readFile(path, 'utf-8');
|
||||
this.resourceCache.set(path, resourceContent);
|
||||
}
|
||||
return resourceContent;
|
||||
}
|
||||
/**
|
||||
* Override of the Critters `embedLinkedStylesheet` method
|
||||
* that makes it work with Angular's CSP APIs.
|
||||
*/
|
||||
embedLinkedStylesheetOverride = async (link, document) => {
|
||||
if (link.getAttribute('media') === 'print' && link.next?.name === 'noscript') {
|
||||
// Workaround for https://github.com/GoogleChromeLabs/critters/issues/64
|
||||
// NB: this is only needed for the webpack based builders.
|
||||
const media = link.getAttribute('onload')?.match(MEDIA_SET_HANDLER_PATTERN);
|
||||
if (media) {
|
||||
link.removeAttribute('onload');
|
||||
link.setAttribute('media', media[1]);
|
||||
link?.next?.remove();
|
||||
}
|
||||
}
|
||||
const returnValue = await this.initialEmbedLinkedStylesheet(link, document);
|
||||
const cspNonce = this.findCspNonce(document);
|
||||
if (cspNonce) {
|
||||
const crittersMedia = link.getAttribute('onload')?.match(MEDIA_SET_HANDLER_PATTERN);
|
||||
if (crittersMedia) {
|
||||
// If there's a Critters-generated `onload` handler and the file has an Angular CSP nonce,
|
||||
// we have to remove the handler, because it's incompatible with CSP. We save the value
|
||||
// in a different attribute and we generate a script tag with the nonce that uses
|
||||
// `addEventListener` to apply the media query instead.
|
||||
link.removeAttribute('onload');
|
||||
link.setAttribute(CSP_MEDIA_ATTR, crittersMedia[1]);
|
||||
this.conditionallyInsertCspLoadingScript(document, cspNonce, link);
|
||||
}
|
||||
// Ideally we would hook in at the time Critters inserts the `style` tags, but there isn't
|
||||
// a way of doing that at the moment so we fall back to doing it any time a `link` tag is
|
||||
// inserted. We mitigate it by only iterating the direct children of the `<head>` which
|
||||
// should be pretty shallow.
|
||||
document.head.children.forEach((child) => {
|
||||
if (child.tagName === 'style' && !child.hasAttribute('nonce')) {
|
||||
child.setAttribute('nonce', cspNonce);
|
||||
}
|
||||
});
|
||||
}
|
||||
return returnValue;
|
||||
};
|
||||
/**
|
||||
* Finds the CSP nonce for a specific document.
|
||||
*/
|
||||
findCspNonce(document) {
|
||||
if (this.documentNonces.has(document)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
return this.documentNonces.get(document);
|
||||
}
|
||||
// HTML attribute are case-insensitive, but the parser used by Critters is case-sensitive.
|
||||
const nonceElement = document.querySelector('[ngCspNonce], [ngcspnonce]');
|
||||
const cspNonce = nonceElement?.getAttribute('ngCspNonce') || nonceElement?.getAttribute('ngcspnonce') || null;
|
||||
this.documentNonces.set(document, cspNonce);
|
||||
return cspNonce;
|
||||
}
|
||||
/**
|
||||
* Inserts the `script` tag that swaps the critical CSS at runtime,
|
||||
* if one hasn't been inserted into the document already.
|
||||
*/
|
||||
conditionallyInsertCspLoadingScript(document, nonce, link) {
|
||||
if (this.addedCspScriptsDocuments.has(document)) {
|
||||
return;
|
||||
}
|
||||
if (document.head.textContent.includes(LINK_LOAD_SCRIPT_CONTENT)) {
|
||||
// Script was already added during the build.
|
||||
this.addedCspScriptsDocuments.add(document);
|
||||
return;
|
||||
}
|
||||
const script = document.createElement('script');
|
||||
script.setAttribute('nonce', nonce);
|
||||
script.textContent = LINK_LOAD_SCRIPT_CONTENT;
|
||||
// Prepend the script to the head since it needs to
|
||||
// run as early as possible, before the `link` tags.
|
||||
document.head.insertBefore(script, link);
|
||||
this.addedCspScriptsDocuments.add(document);
|
||||
}
|
||||
}
|
||||
export class InlineCriticalCssProcessor {
|
||||
options;
|
||||
resourceCache = new Map();
|
||||
constructor(options) {
|
||||
this.options = options;
|
||||
}
|
||||
async process(html, options) {
|
||||
const critters = new CrittersExtended({ ...this.options, ...options }, this.resourceCache);
|
||||
const content = await critters.process(html);
|
||||
return {
|
||||
content,
|
||||
errors: critters.errors.length ? critters.errors : undefined,
|
||||
warnings: critters.warnings.length ? critters.warnings : undefined,
|
||||
};
|
||||
}
|
||||
}
|
50
my-app/node_modules/@angular/ssr/esm2022/src/peformance-profiler.mjs
generated
vendored
Executable file
50
my-app/node_modules/@angular/ssr/esm2022/src/peformance-profiler.mjs
generated
vendored
Executable file
|
@ -0,0 +1,50 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright Google LLC 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
|
||||
*/
|
||||
const PERFORMANCE_MARK_PREFIX = '🅰️';
|
||||
export function printPerformanceLogs() {
|
||||
let maxWordLength = 0;
|
||||
const benchmarks = [];
|
||||
for (const { name, duration } of performance.getEntriesByType('measure')) {
|
||||
if (!name.startsWith(PERFORMANCE_MARK_PREFIX)) {
|
||||
continue;
|
||||
}
|
||||
// `🅰️:Retrieve SSG Page` -> `Retrieve SSG Page:`
|
||||
const step = name.slice(PERFORMANCE_MARK_PREFIX.length + 1) + ':';
|
||||
if (step.length > maxWordLength) {
|
||||
maxWordLength = step.length;
|
||||
}
|
||||
benchmarks.push([step, `${duration.toFixed(1)}ms`]);
|
||||
performance.clearMeasures(name);
|
||||
}
|
||||
/* eslint-disable no-console */
|
||||
console.log('********** Performance results **********');
|
||||
for (const [step, value] of benchmarks) {
|
||||
const spaces = maxWordLength - step.length + 5;
|
||||
console.log(step + ' '.repeat(spaces) + value);
|
||||
}
|
||||
console.log('*****************************************');
|
||||
/* eslint-enable no-console */
|
||||
}
|
||||
export async function runMethodAndMeasurePerf(label, asyncMethod) {
|
||||
const labelName = `${PERFORMANCE_MARK_PREFIX}:${label}`;
|
||||
const startLabel = `start:${labelName}`;
|
||||
const endLabel = `end:${labelName}`;
|
||||
try {
|
||||
performance.mark(startLabel);
|
||||
return await asyncMethod();
|
||||
}
|
||||
finally {
|
||||
performance.mark(endLabel);
|
||||
performance.measure(labelName, startLabel, endLabel);
|
||||
performance.clearMarks(startLabel);
|
||||
performance.clearMarks(endLabel);
|
||||
}
|
||||
}
|
||||
export function noopRunMethodAndMeasurePerf(label, asyncMethod) {
|
||||
return asyncMethod();
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue