feat(@angular/build): integrate Chrome automatic workspace folders

This commit integrates automatic Chrome DevTools workspace folder connection into the vite dev-server process, leveraging the experimental feature available in Chrome Canary, as described in the Chrome DevTools documentation https://chromium.googlesource.com/devtools/devtools-frontend/+/main/docs/ecosystem/automatic_workspace_folders.md
This commit is contained in:
Alan Agius 2025-03-05 15:56:53 +00:00
parent 896d98a313
commit 3c9172159c
4 changed files with 59 additions and 0 deletions

View File

@ -908,6 +908,7 @@ export async function setupServer(
templateUpdates,
ssrMode,
resetComponentUpdates: () => templateUpdates.clear(),
projectRoot: serverOptions.projectRoot,
}),
createRemoveIdPrefixPlugin(externalMetadata.explicitBrowser),
await createAngularSsrTransformPlugin(serverOptions.workspaceRoot),

View File

@ -0,0 +1,51 @@
/**
* @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 { randomUUID } from 'node:crypto';
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
import { join } from 'node:path';
import type { Connect } from 'vite';
const CHROME_DEVTOOLS_ROUTE = '/.well-known/appspecific/com.chrome.devtools.json';
export function createChromeDevtoolsMiddleware(
cacheDir: string,
projectRoot: string,
): Connect.NextHandleFunction {
let devtoolsConfig: string;
const devtoolsConfigPath = join(cacheDir, 'com.chrome.devtools.json');
return function chromeDevtoolsMiddleware(req, res, next) {
if (req.url !== CHROME_DEVTOOLS_ROUTE) {
next();
return;
}
// We store the UUID and re-use it to ensure Chrome does not repeatedly ask for permissions when restarting the dev server.
try {
devtoolsConfig ??= readFileSync(devtoolsConfigPath, 'utf-8');
} catch {
const devtoolsConfigJson = {
workspace: {
root: projectRoot,
uuid: randomUUID(),
},
};
devtoolsConfig = JSON.stringify(devtoolsConfigJson, undefined, 2);
try {
mkdirSync(cacheDir, { recursive: true });
writeFileSync(devtoolsConfigPath, devtoolsConfig);
} catch {}
}
res.setHeader('Content-Type', 'application/json');
res.end(devtoolsConfig);
};
}

View File

@ -15,3 +15,4 @@ export {
} from './ssr-middleware';
export { createAngularHeadersMiddleware } from './headers-middleware';
export { createAngularComponentMiddleware } from './component-middleware';
export { createChromeDevtoolsMiddleware } from './chrome-devtools-middleware';

View File

@ -17,6 +17,7 @@ import {
createAngularIndexHtmlMiddleware,
createAngularSsrExternalMiddleware,
createAngularSsrInternalMiddleware,
createChromeDevtoolsMiddleware,
} from '../middlewares';
import { AngularMemoryOutputFiles, AngularOutputAssets } from '../utils';
@ -54,6 +55,7 @@ interface AngularSetupMiddlewaresPluginOptions {
templateUpdates: Map<string, string>;
ssrMode: ServerSsrMode;
resetComponentUpdates: () => void;
projectRoot: string;
}
async function createEncapsulateStyle(): Promise<
@ -99,6 +101,10 @@ export function createAngularSetupMiddlewaresPlugin(
),
);
server.middlewares.use(
createChromeDevtoolsMiddleware(server.config.cacheDir, options.projectRoot),
);
extensionMiddleware?.forEach((middleware) => server.middlewares.use(middleware));
// Returning a function, installs middleware after the main transform middleware but