forked from angular/angular-cli
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1113 from angular/main
Create a new pull request by comparing changes across two branches
- Loading branch information
Showing
30 changed files
with
1,793 additions
and
316 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
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
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
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,150 @@ | ||
/** | ||
* @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.dev/license | ||
*/ | ||
|
||
import { lookup as lookupMimeType } from 'mrmime'; | ||
import { AngularServerApp } from './app'; | ||
import { Hooks } from './hooks'; | ||
import { getPotentialLocaleIdFromUrl } from './i18n'; | ||
import { getAngularAppEngineManifest } from './manifest'; | ||
|
||
/** | ||
* Angular server application engine. | ||
* Manages Angular server applications (including localized ones), handles rendering requests, | ||
* and optionally transforms index HTML before rendering. | ||
*/ | ||
export class AngularAppEngine { | ||
/** | ||
* Hooks for extending or modifying the behavior of the server application. | ||
* @internal This property is accessed by the Angular CLI when running the dev-server. | ||
*/ | ||
static hooks = new Hooks(); | ||
|
||
/** | ||
* Hooks for extending or modifying the behavior of the server application. | ||
* This instance can be used to attach custom functionality to various events in the server application lifecycle. | ||
* @internal | ||
*/ | ||
get hooks(): Hooks { | ||
return AngularAppEngine.hooks; | ||
} | ||
|
||
/** | ||
* Specifies if the application is operating in development mode. | ||
* This property controls the activation of features intended for production, such as caching mechanisms. | ||
* @internal | ||
*/ | ||
static isDevMode = false; | ||
|
||
/** | ||
* The manifest for the server application. | ||
*/ | ||
private readonly manifest = getAngularAppEngineManifest(); | ||
|
||
/** | ||
* Map of locale strings to corresponding `AngularServerApp` instances. | ||
* Each instance represents an Angular server application. | ||
*/ | ||
private readonly appsCache = new Map<string, AngularServerApp>(); | ||
|
||
/** | ||
* Renders an HTTP request using the appropriate Angular server application and returns a response. | ||
* | ||
* This method determines the entry point for the Angular server application based on the request URL, | ||
* and caches the server application instances for reuse. If the application is in development mode, | ||
* the cache is bypassed and a new instance is created for each request. | ||
* | ||
* If the request URL appears to be for a file (excluding `/index.html`), the method returns `null`. | ||
* A request to `https://www.example.com/page/index.html` will render the Angular route | ||
* corresponding to `https://www.example.com/page`. | ||
* | ||
* @param request - The incoming HTTP request object to be rendered. | ||
* @param requestContext - Optional additional context for the request, such as metadata. | ||
* @returns A promise that resolves to a Response object, or `null` if the request URL represents a file (e.g., `./logo.png`) | ||
* rather than an application route. | ||
*/ | ||
async render(request: Request, requestContext?: unknown): Promise<Response | null> { | ||
// Skip if the request looks like a file but not `/index.html`. | ||
const url = new URL(request.url); | ||
const { pathname } = url; | ||
if (isFileLike(pathname) && !pathname.endsWith('/index.html')) { | ||
return null; | ||
} | ||
|
||
const entryPoint = this.getEntryPointFromUrl(url); | ||
if (!entryPoint) { | ||
return null; | ||
} | ||
|
||
const [locale, loadModule] = entryPoint; | ||
let serverApp = this.appsCache.get(locale); | ||
if (!serverApp) { | ||
const { AngularServerApp } = await loadModule(); | ||
serverApp = new AngularServerApp({ | ||
isDevMode: AngularAppEngine.isDevMode, | ||
hooks: this.hooks, | ||
}); | ||
|
||
if (!AngularAppEngine.isDevMode) { | ||
this.appsCache.set(locale, serverApp); | ||
} | ||
} | ||
|
||
return serverApp.render(request, requestContext); | ||
} | ||
|
||
/** | ||
* Retrieves the entry point path and locale for the Angular server application based on the provided URL. | ||
* | ||
* This method determines the appropriate entry point and locale for rendering the application by examining the URL. | ||
* If there is only one entry point available, it is returned regardless of the URL. | ||
* Otherwise, the method extracts a potential locale identifier from the URL and looks up the corresponding entry point. | ||
* | ||
* @param url - The URL used to derive the locale and determine the entry point. | ||
* @returns An array containing: | ||
* - The first element is the locale extracted from the URL. | ||
* - The second element is a function that returns a promise resolving to an object with the `AngularServerApp` type. | ||
* | ||
* Returns `null` if no matching entry point is found for the extracted locale. | ||
*/ | ||
private getEntryPointFromUrl(url: URL): | ||
| [ | ||
locale: string, | ||
loadModule: () => Promise<{ | ||
AngularServerApp: typeof AngularServerApp; | ||
}>, | ||
] | ||
| null { | ||
// Find bundle for locale | ||
const { entryPoints, basePath } = this.manifest; | ||
if (entryPoints.size === 1) { | ||
return entryPoints.entries().next().value; | ||
} | ||
|
||
const potentialLocale = getPotentialLocaleIdFromUrl(url, basePath); | ||
const entryPoint = entryPoints.get(potentialLocale); | ||
|
||
return entryPoint ? [potentialLocale, entryPoint] : null; | ||
} | ||
} | ||
|
||
/** | ||
* Determines if the given pathname corresponds to a file-like resource. | ||
* | ||
* @param pathname - The pathname to check. | ||
* @returns True if the pathname appears to be a file, false otherwise. | ||
*/ | ||
function isFileLike(pathname: string): boolean { | ||
const dotIndex = pathname.lastIndexOf('.'); | ||
if (dotIndex === -1) { | ||
return false; | ||
} | ||
|
||
const extension = pathname.slice(dotIndex); | ||
|
||
return extension === '.ico' || !!lookupMimeType(extension); | ||
} |
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,104 @@ | ||
/** | ||
* @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.dev/license | ||
*/ | ||
|
||
import { Hooks } from './hooks'; | ||
import { getAngularAppManifest } from './manifest'; | ||
import { ServerRenderContext, render } from './render'; | ||
|
||
/** | ||
* Configuration options for initializing a `AngularServerApp` instance. | ||
*/ | ||
export interface AngularServerAppOptions { | ||
/** | ||
* Indicates whether the application is in development mode. | ||
* | ||
* When set to `true`, the application runs in development mode with additional debugging features. | ||
*/ | ||
isDevMode?: boolean; | ||
|
||
/** | ||
* Optional hooks for customizing the server application's behavior. | ||
*/ | ||
hooks?: Hooks; | ||
} | ||
|
||
/** | ||
* Represents a locale-specific Angular server application managed by the server application engine. | ||
* | ||
* The `AngularServerApp` class handles server-side rendering and asset management for a specific locale. | ||
*/ | ||
export class AngularServerApp { | ||
/** | ||
* The manifest associated with this server application. | ||
* @internal | ||
*/ | ||
readonly manifest = getAngularAppManifest(); | ||
|
||
/** | ||
* Hooks for extending or modifying the behavior of the server application. | ||
* This instance can be used to attach custom functionality to various events in the server application lifecycle. | ||
* @internal | ||
*/ | ||
readonly hooks: Hooks; | ||
|
||
/** | ||
* Specifies if the server application is operating in development mode. | ||
* This property controls the activation of features intended for production, such as caching mechanisms. | ||
* @internal | ||
*/ | ||
readonly isDevMode: boolean; | ||
|
||
/** | ||
* Creates a new `AngularServerApp` instance with the provided configuration options. | ||
* | ||
* @param options - The configuration options for the server application. | ||
* - `isDevMode`: Flag indicating if the application is in development mode. | ||
* - `hooks`: Optional hooks for customizing application behavior. | ||
*/ | ||
constructor(options: AngularServerAppOptions) { | ||
this.isDevMode = options.isDevMode ?? false; | ||
this.hooks = options.hooks ?? new Hooks(); | ||
} | ||
|
||
/** | ||
* Renders a response for the given HTTP request using the server application. | ||
* | ||
* This method processes the request and returns a response based on the specified rendering context. | ||
* | ||
* @param request - The incoming HTTP request to be rendered. | ||
* @param requestContext - Optional additional context for rendering, such as request metadata. | ||
* @param serverContext - The rendering context. | ||
* | ||
* @returns A promise that resolves to the HTTP response object resulting from the rendering. | ||
*/ | ||
render( | ||
request: Request, | ||
requestContext?: unknown, | ||
serverContext: ServerRenderContext = ServerRenderContext.SSR, | ||
): Promise<Response> { | ||
return render(this, request, serverContext, requestContext); | ||
} | ||
|
||
/** | ||
* Retrieves the content of a server-side asset using its path. | ||
* | ||
* This method fetches the content of a specific asset defined in the server application's manifest. | ||
* | ||
* @param path - The path to the server asset. | ||
* @returns A promise that resolves to the asset content as a string. | ||
* @throws Error If the asset path is not found in the manifest, an error is thrown. | ||
*/ | ||
async getServerAsset(path: string): Promise<string> { | ||
const asset = this.manifest.assets[path]; | ||
if (!asset) { | ||
throw new Error(`Server asset '${path}' does not exist.`); | ||
} | ||
|
||
return asset(); | ||
} | ||
} |
File renamed without changes.
File renamed without changes.
File renamed without changes.
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,37 @@ | ||
/** | ||
* @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.dev/license | ||
*/ | ||
|
||
import { ɵConsole } from '@angular/core'; | ||
|
||
/** | ||
* Custom implementation of the Angular Console service that filters out specific log messages. | ||
* | ||
* This class extends the internal Angular `ɵConsole` class to provide customized logging behavior. | ||
* It overrides the `log` method to suppress logs that match certain predefined messages. | ||
*/ | ||
export class Console extends ɵConsole { | ||
/** | ||
* A set of log messages that should be ignored and not printed to the console. | ||
*/ | ||
private readonly ignoredLogs = new Set(['Angular is running in development mode.']); | ||
|
||
/** | ||
* Logs a message to the console if it is not in the set of ignored messages. | ||
* | ||
* @param message - The message to log to the console. | ||
* | ||
* This method overrides the `log` method of the `ɵConsole` class. It checks if the | ||
* message is in the `ignoredLogs` set. If it is not, it delegates the logging to | ||
* the parent class's `log` method. Otherwise, the message is suppressed. | ||
*/ | ||
override log(message: string): void { | ||
if (!this.ignoredLogs.has(message)) { | ||
super.log(message); | ||
} | ||
} | ||
} |
Oops, something went wrong.