Skip to content

Commit 49b6106

Browse files
author
Angular Builds
committed
ed95a92 refactor(@angular-devkit/build-angular): move Angular memory Vite plugin into separate file
1 parent b7b018e commit 49b6106

File tree

5 files changed

+272
-218
lines changed

5 files changed

+272
-218
lines changed

package.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
{
22
"name": "@angular-devkit/build-angular",
3-
"version": "17.1.0+sha-85ac2f3",
3+
"version": "17.1.0+sha-ed95a92",
44
"description": "Angular Webpack Build Facade",
55
"main": "src/index.js",
66
"typings": "src/index.d.ts",
77
"builders": "builders.json",
88
"dependencies": {
99
"@ampproject/remapping": "2.2.1",
10-
"@angular-devkit/architect": "github:angular/angular-devkit-architect-builds#85ac2f3",
11-
"@angular-devkit/build-webpack": "github:angular/angular-devkit-build-webpack-builds#85ac2f3",
12-
"@angular-devkit/core": "github:angular/angular-devkit-core-builds#85ac2f3",
10+
"@angular-devkit/architect": "github:angular/angular-devkit-architect-builds#ed95a92",
11+
"@angular-devkit/build-webpack": "github:angular/angular-devkit-build-webpack-builds#ed95a92",
12+
"@angular-devkit/core": "github:angular/angular-devkit-core-builds#ed95a92",
1313
"@babel/core": "7.23.7",
1414
"@babel/generator": "7.23.6",
1515
"@babel/helper-annotate-as-pure": "7.22.5",
@@ -20,7 +20,7 @@
2020
"@babel/preset-env": "7.23.7",
2121
"@babel/runtime": "7.23.7",
2222
"@discoveryjs/json-ext": "0.5.7",
23-
"@ngtools/webpack": "github:angular/ngtools-webpack-builds#85ac2f3",
23+
"@ngtools/webpack": "github:angular/ngtools-webpack-builds#ed95a92",
2424
"@vitejs/plugin-basic-ssl": "1.0.2",
2525
"ansi-colors": "4.1.3",
2626
"autoprefixer": "10.4.16",

src/builders/dev-server/vite-server.js

Lines changed: 13 additions & 212 deletions
Original file line numberDiff line numberDiff line change
@@ -34,19 +34,17 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3434
};
3535
Object.defineProperty(exports, "__esModule", { value: true });
3636
exports.setupServer = exports.serveWithVite = void 0;
37-
const remapping_1 = __importDefault(require("@ampproject/remapping"));
38-
const mrmime_1 = require("mrmime");
3937
const node_assert_1 = __importDefault(require("node:assert"));
4038
const promises_1 = require("node:fs/promises");
4139
const node_path_1 = require("node:path");
4240
const bundler_context_1 = require("../../tools/esbuild/bundler-context");
4341
const javascript_transformer_1 = require("../../tools/esbuild/javascript-transformer");
4442
const rxjs_esm_resolution_plugin_1 = require("../../tools/esbuild/rxjs-esm-resolution-plugin");
4543
const utils_1 = require("../../tools/esbuild/utils");
44+
const angular_memory_plugin_1 = require("../../tools/vite/angular-memory-plugin");
4645
const i18n_locale_plugin_1 = require("../../tools/vite/i18n-locale-plugin");
4746
const utils_2 = require("../../utils");
4847
const load_esm_1 = require("../../utils/load-esm");
49-
const render_page_1 = require("../../utils/server-rendering/render-page");
5048
const supported_browsers_1 = require("../../utils/supported-browsers");
5149
const webpack_browser_config_1 = require("../../utils/webpack-browser-config");
5250
const application_1 = require("../application");
@@ -324,7 +322,6 @@ function analyzeResultFiles(normalizePath, htmlIndexPath, resultFiles, generated
324322
}
325323
}
326324
}
327-
// eslint-disable-next-line max-lines-per-function
328325
async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks, externalMetadata, ssr, prebundleTransformer, target, prebundleLoaderExtensions, extensionMiddleware, indexHtmlTransformer, thirdPartySourcemaps = false) {
329326
const proxy = await (0, utils_2.loadProxyConfiguration)(serverOptions.workspaceRoot, serverOptions.proxyConfig, true);
330327
// dynamically import Vite for ESM compatibility
@@ -409,189 +406,18 @@ async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks,
409406
},
410407
plugins: [
411408
(0, i18n_locale_plugin_1.createAngularLocaleDataPlugin)(),
412-
{
413-
name: 'vite:angular-memory',
414-
// Ensures plugin hooks run before built-in Vite hooks
415-
enforce: 'pre',
416-
async resolveId(source, importer) {
417-
// Prevent vite from resolving an explicit external dependency (`externalDependencies` option)
418-
if (externalMetadata.explicit.includes(source)) {
419-
// This is still not ideal since Vite will still transform the import specifier to
420-
// `/@id/${source}` but is currently closer to a raw external than a resolved file path.
421-
return source;
422-
}
423-
if (importer && source[0] === '.' && importer.startsWith(virtualProjectRoot)) {
424-
// Remove query if present
425-
const [importerFile] = importer.split('?', 1);
426-
source =
427-
'/' +
428-
normalizePath((0, node_path_1.join)((0, node_path_1.dirname)((0, node_path_1.relative)(virtualProjectRoot, importerFile)), source));
429-
}
430-
const [file] = source.split('?', 1);
431-
if (outputFiles.has(file)) {
432-
return (0, node_path_1.join)(virtualProjectRoot, source);
433-
}
434-
},
435-
load(id) {
436-
const [file] = id.split('?', 1);
437-
const relativeFile = '/' + normalizePath((0, node_path_1.relative)(virtualProjectRoot, file));
438-
const codeContents = outputFiles.get(relativeFile)?.contents;
439-
if (codeContents === undefined) {
440-
if (relativeFile.endsWith('/node_modules/vite/dist/client/client.mjs')) {
441-
return loadViteClientCode(file);
442-
}
443-
return;
444-
}
445-
const code = Buffer.from(codeContents).toString('utf-8');
446-
const mapContents = outputFiles.get(relativeFile + '.map')?.contents;
447-
return {
448-
// Remove source map URL comments from the code if a sourcemap is present.
449-
// Vite will inline and add an additional sourcemap URL for the sourcemap.
450-
code: mapContents ? code.replace(/^\/\/# sourceMappingURL=[^\r\n]*/gm, '') : code,
451-
map: mapContents && Buffer.from(mapContents).toString('utf-8'),
452-
};
453-
},
454-
configureServer(server) {
455-
const originalssrTransform = server.ssrTransform;
456-
server.ssrTransform = async (code, map, url, originalCode) => {
457-
const result = await originalssrTransform(code, null, url, originalCode);
458-
if (!result || !result.map || !map) {
459-
return result;
460-
}
461-
const remappedMap = (0, remapping_1.default)([result.map, map], () => null);
462-
// Set the sourcemap root to the workspace root. This is needed since we set a virtual path as root.
463-
remappedMap.sourceRoot = normalizePath(serverOptions.workspaceRoot) + '/';
464-
return {
465-
...result,
466-
map: remappedMap,
467-
};
468-
};
469-
// Assets and resources get handled first
470-
server.middlewares.use(function angularAssetsMiddleware(req, res, next) {
471-
if (req.url === undefined || res.writableEnded) {
472-
return;
473-
}
474-
// Parse the incoming request.
475-
// The base of the URL is unused but required to parse the URL.
476-
const pathname = pathnameWithoutBasePath(req.url, server.config.base);
477-
const extension = (0, node_path_1.extname)(pathname);
478-
// Rewrite all build assets to a vite raw fs URL
479-
const assetSourcePath = assets.get(pathname);
480-
if (assetSourcePath !== undefined) {
481-
// Workaround to disable Vite transformer middleware.
482-
// See: https://github.com/vitejs/vite/blob/746a1daab0395f98f0afbdee8f364cb6cf2f3b3f/packages/vite/src/node/server/middlewares/transform.ts#L201 and
483-
// https://github.com/vitejs/vite/blob/746a1daab0395f98f0afbdee8f364cb6cf2f3b3f/packages/vite/src/node/server/transformRequest.ts#L204-L206
484-
req.headers.accept = 'text/html';
485-
// The encoding needs to match what happens in the vite static middleware.
486-
// ref: https://github.com/vitejs/vite/blob/d4f13bd81468961c8c926438e815ab6b1c82735e/packages/vite/src/node/server/middlewares/static.ts#L163
487-
req.url = `${server.config.base}@fs/${encodeURI(assetSourcePath)}`;
488-
next();
489-
return;
490-
}
491-
// Resource files are handled directly.
492-
// Global stylesheets (CSS files) are currently considered resources to workaround
493-
// dev server sourcemap issues with stylesheets.
494-
if (extension !== '.js' && extension !== '.html') {
495-
const outputFile = outputFiles.get(pathname);
496-
if (outputFile?.servable) {
497-
const mimeType = (0, mrmime_1.lookup)(extension);
498-
if (mimeType) {
499-
res.setHeader('Content-Type', mimeType);
500-
}
501-
res.setHeader('Cache-Control', 'no-cache');
502-
if (serverOptions.headers) {
503-
Object.entries(serverOptions.headers).forEach(([name, value]) => res.setHeader(name, value));
504-
}
505-
res.end(outputFile.contents);
506-
return;
507-
}
508-
}
509-
next();
510-
});
511-
if (extensionMiddleware?.length) {
512-
extensionMiddleware.forEach((middleware) => server.middlewares.use(middleware));
513-
}
514-
// Returning a function, installs middleware after the main transform middleware but
515-
// before the built-in HTML middleware
516-
return () => {
517-
function angularSSRMiddleware(req, res, next) {
518-
const url = req.originalUrl;
519-
if (
520-
// Skip if path is not defined.
521-
!url ||
522-
// Skip if path is like a file.
523-
// NOTE: We use a regexp to mitigate against matching requests like: /browse/pl.0ef59752c0cd457dbf1391f08cbd936f
524-
/^\.[a-z]{2,4}$/i.test((0, node_path_1.extname)(url.split('?')[0]))) {
525-
next();
526-
return;
527-
}
528-
const rawHtml = outputFiles.get('/index.server.html')?.contents;
529-
if (!rawHtml) {
530-
next();
531-
return;
532-
}
533-
transformIndexHtmlAndAddHeaders(url, rawHtml, res, next, async (html) => {
534-
const { content } = await (0, render_page_1.renderPage)({
535-
document: html,
536-
route: new URL(req.originalUrl ?? '/', server.resolvedUrls?.local[0]).toString(),
537-
serverContext: 'ssr',
538-
loadBundle: (uri) =>
539-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
540-
server.ssrLoadModule(uri.slice(1)),
541-
// Files here are only needed for critical CSS inlining.
542-
outputFiles: {},
543-
// TODO: add support for critical css inlining.
544-
inlineCriticalCss: false,
545-
});
546-
return indexHtmlTransformer && content
547-
? await indexHtmlTransformer(content)
548-
: content;
549-
});
550-
}
551-
if (ssr) {
552-
server.middlewares.use(angularSSRMiddleware);
553-
}
554-
server.middlewares.use(function angularIndexMiddleware(req, res, next) {
555-
if (!req.url) {
556-
next();
557-
return;
558-
}
559-
// Parse the incoming request.
560-
// The base of the URL is unused but required to parse the URL.
561-
const pathname = pathnameWithoutBasePath(req.url, server.config.base);
562-
if (pathname === '/' || pathname === `/index.html`) {
563-
const rawHtml = outputFiles.get('/index.html')?.contents;
564-
if (rawHtml) {
565-
transformIndexHtmlAndAddHeaders(req.url, rawHtml, res, next, indexHtmlTransformer);
566-
return;
567-
}
568-
}
569-
next();
570-
});
571-
};
572-
function transformIndexHtmlAndAddHeaders(url, rawHtml, res, next, additionalTransformer) {
573-
server
574-
.transformIndexHtml(url, Buffer.from(rawHtml).toString('utf-8'))
575-
.then(async (processedHtml) => {
576-
if (additionalTransformer) {
577-
const content = await additionalTransformer(processedHtml);
578-
if (!content) {
579-
next();
580-
return;
581-
}
582-
processedHtml = content;
583-
}
584-
res.setHeader('Content-Type', 'text/html');
585-
res.setHeader('Cache-Control', 'no-cache');
586-
if (serverOptions.headers) {
587-
Object.entries(serverOptions.headers).forEach(([name, value]) => res.setHeader(name, value));
588-
}
589-
res.end(processedHtml);
590-
})
591-
.catch((error) => next(error));
592-
}
593-
},
594-
},
409+
(0, angular_memory_plugin_1.createAngularMemoryPlugin)({
410+
workspaceRoot: serverOptions.workspaceRoot,
411+
virtualProjectRoot,
412+
outputFiles,
413+
assets,
414+
ssr,
415+
external: externalMetadata.explicit,
416+
indexHtmlTransformer,
417+
extensionMiddleware,
418+
extraHeaders: serverOptions.headers,
419+
normalizePath,
420+
}),
595421
],
596422
// Browser only optimizeDeps. (This does not run for SSR dependencies).
597423
optimizeDeps: getDepOptimizationConfig({
@@ -627,31 +453,6 @@ async function setupServer(serverOptions, outputFiles, assets, preserveSymlinks,
627453
return configuration;
628454
}
629455
exports.setupServer = setupServer;
630-
/**
631-
* Reads the resolved Vite client code from disk and updates the content to remove
632-
* an unactionable suggestion to update the Vite configuration file to disable the
633-
* error overlay. The Vite configuration file is not present when used in the Angular
634-
* CLI.
635-
* @param file The absolute path to the Vite client code.
636-
* @returns
637-
*/
638-
async function loadViteClientCode(file) {
639-
const originalContents = await (0, promises_1.readFile)(file, 'utf-8');
640-
let contents = originalContents.replace('You can also disable this overlay by setting', '');
641-
contents = contents.replace(
642-
// eslint-disable-next-line max-len
643-
'<code part="config-option-name">server.hmr.overlay</code> to <code part="config-option-value">false</code> in <code part="config-file-name">vite.config.js.</code>', '');
644-
(0, node_assert_1.default)(originalContents !== contents, 'Failed to update Vite client error overlay text.');
645-
return contents;
646-
}
647-
function pathnameWithoutBasePath(url, basePath) {
648-
const parsedUrl = new URL(url, 'http://localhost');
649-
const pathname = decodeURIComponent(parsedUrl.pathname);
650-
// slice(basePath.length - 1) to retain the trailing slash
651-
return basePath !== '/' && pathname.startsWith(basePath)
652-
? pathname.slice(basePath.length - 1)
653-
: pathname;
654-
}
655456
function getDepOptimizationConfig({ disabled, exclude, include, target, prebundleTransformer, ssr, loader, thirdPartySourcemaps, }) {
656457
const plugins = [
657458
{
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
import type { Connect, Plugin } from 'vite';
9+
export interface AngularMemoryPluginOptions {
10+
workspaceRoot: string;
11+
virtualProjectRoot: string;
12+
outputFiles: Map<string, {
13+
contents: Uint8Array;
14+
servable: boolean;
15+
}>;
16+
assets: Map<string, string>;
17+
ssr: boolean;
18+
external?: string[];
19+
extensionMiddleware?: Connect.NextHandleFunction[];
20+
extraHeaders?: Record<string, string>;
21+
indexHtmlTransformer?: (content: string) => Promise<string>;
22+
normalizePath: (path: string) => string;
23+
}
24+
export declare function createAngularMemoryPlugin(options: AngularMemoryPluginOptions): Plugin;

0 commit comments

Comments
 (0)