Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@
"unenv": "^1.10.0",
"verdaccio": "6.0.2",
"verdaccio-auth-memory": "^10.0.0",
"vite": "5.4.11",
"vite": "6.0.0",
"watchpack": "2.4.2",
"webpack": "5.96.1",
"webpack-dev-middleware": "7.4.2",
Expand Down
2 changes: 1 addition & 1 deletion packages/angular/build/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
"rollup": "4.27.4",
"sass": "1.81.0",
"semver": "7.6.3",
"vite": "5.4.11",
"vite": "6.0.0",
"watchpack": "2.4.2"
},
"optionalDependencies": {
Expand Down
42 changes: 13 additions & 29 deletions packages/angular/build/src/builders/dev-server/vite-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,6 @@ export async function* serveWithVite(
}

// To avoid disconnecting the array objects from the option, these arrays need to be mutated instead of replaced.
let requiresServerRestart = false;
if (result.detail?.['externalMetadata']) {
const { implicitBrowser, implicitServer, explicit } = result.detail[
'externalMetadata'
Expand All @@ -283,15 +282,6 @@ export async function* serveWithVite(
);
const implicitBrowserFiltered = implicitBrowser.filter((m) => !isAbsoluteUrl(m));

if (browserOptions.ssr && serverOptions.prebundle !== false) {
const previousImplicitServer = new Set(externalMetadata.implicitServer);
// Restart the server to force SSR dep re-optimization when a dependency has been added.
// This is a workaround for: https://github.com/vitejs/vite/issues/14896
requiresServerRestart = implicitServerFiltered.some(
(dep) => !previousImplicitServer.has(dep),
);
}

// Empty Arrays to avoid growing unlimited with every re-build.
externalMetadata.explicitBrowser.length = 0;
externalMetadata.explicitServer.length = 0;
Expand All @@ -317,20 +307,14 @@ export async function* serveWithVite(
...new Set([...server.config.server.fs.allow, ...assetFiles.values()]),
];

if (requiresServerRestart) {
// Restart the server to force SSR dep re-optimization when a dependency has been added.
// This is a workaround for: https://github.com/vitejs/vite/issues/14896
await server.restart();
} else {
await handleUpdate(
normalizePath,
generatedFiles,
server,
serverOptions,
context.logger,
componentStyles,
);
}
await handleUpdate(
normalizePath,
generatedFiles,
server,
serverOptions,
context.logger,
componentStyles,
);
} else {
const projectName = context.target?.project;
if (!projectName) {
Expand Down Expand Up @@ -475,6 +459,11 @@ async function handleUpdate(
return;
}

if (destroyAngularServerAppCalled) {
// Trigger module evaluation before reload to initiate dependency optimization.
await server.ssrLoadModule('/main.server.mjs');
}

if (serverOptions.hmr) {
if (updatedFiles.every((f) => f.endsWith('.css'))) {
let requiresReload = false;
Expand Down Expand Up @@ -705,11 +694,6 @@ export async function setupServer(
// the Vite client-side code for browser reloading. These would be available by default but when
// the `allow` option is explicitly configured, they must be included manually.
allow: [cacheDir, join(serverOptions.workspaceRoot, 'node_modules'), ...assets.values()],

// Temporary disable cached FS checks.
// This is because we configure `config.base` to a virtual directory which causes `getRealPath` to fail.
// See: https://github.com/vitejs/vite/blob/b2873ac3936de25ca8784327cb9ef16bd4881805/packages/vite/src/node/fsUtils.ts#L45-L67
cachedChecks: false,
},
// This is needed when `externalDependencies` is used to prevent Vite load errors.
// NOTE: If Vite adds direct support for externals, this can be removed.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/

import remapping, { SourceMapInput } from '@ampproject/remapping';
import type { SourceDescription } from 'rollup';
import type { Plugin } from 'vite';
import { loadEsmModule } from '../../../utils/load-esm';

Expand All @@ -15,28 +16,19 @@ export async function createAngularSsrTransformPlugin(workspaceRoot: string): Pr

return {
name: 'vite:angular-ssr-transform',
enforce: 'pre',
async configureServer(server) {
const originalssrTransform = server.ssrTransform;
enforce: 'post',
transform(code, _id, { ssr, inMap }: { ssr?: boolean; inMap?: SourceMapInput } = {}) {
if (!ssr || !inMap) {
return null;
}

server.ssrTransform = async (code, map, url, originalCode) => {
const result = await originalssrTransform(code, null, url, originalCode);
if (!result || !result.map || !map) {
return result;
}
const remappedMap = remapping([inMap], () => null);
// Set the sourcemap root to the workspace root. This is needed since we set a virtual path as root.
remappedMap.sourceRoot = normalizePath(workspaceRoot) + '/';

const remappedMap = remapping(
[result.map as SourceMapInput, map as SourceMapInput],
() => null,
);

// Set the sourcemap root to the workspace root. This is needed since we set a virtual path as root.
remappedMap.sourceRoot = normalizePath(workspaceRoot) + '/';

return {
...result,
map: remappedMap as (typeof result)['map'],
};
return {
code,
map: remappedMap as SourceDescription['map'],
};
},
};
Expand Down
13 changes: 7 additions & 6 deletions tests/legacy-cli/e2e/tests/vite/reuse-dep-optimization-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,18 @@ export default async function () {
await execAndWaitForOutputToMatch(
'ng',
['serve', '--port', `${port}`],
/Dependencies bundled/,
/bundle generation complete/,
// Use CI:0 to force caching
{ DEBUG: 'vite:deps', CI: '0' },
);

// Make request so that vite writes the cache.
const response = await fetch(`http://localhost:${port}/main.js`);
assert(response.ok, `Expected 'response.ok' to be 'true'.`);

// Wait for vite to write to FS and stablize.
await waitForAnyProcessOutputToMatch(/dependencies optimized/, 5000);
await Promise.all([
waitForAnyProcessOutputToMatch(/dependencies optimized/, 5000),
fetch(`http://localhost:${port}/main.js`).then((r) =>
assert(r.ok, `Expected 'response.ok' to be 'true'.`),
),
]);

// Terminate the dev-server
await killAllProcesses();
Expand Down
49 changes: 49 additions & 0 deletions tests/legacy-cli/e2e/tests/vite/ssr-new-dep-optimization.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import assert from 'node:assert';
import { ng, waitForAnyProcessOutputToMatch } from '../../utils/process';
import { installWorkspacePackages, uninstallPackage } from '../../utils/packages';
import { ngServe, useSha } from '../../utils/project';
import { getGlobalVariable } from '../../utils/env';
import { readFile, writeFile } from '../../utils/fs';

export default async function () {
assert(
getGlobalVariable('argv')['esbuild'],
'This test should not be called in the Webpack suite.',
);

// Enable caching to test real development workflow.
await ng('cache', 'clean');
await ng('cache', 'on');

// Forcibly remove in case another test doesn't clean itself up.
await uninstallPackage('@angular/ssr');
await ng('add', '@angular/ssr', '--server-routing', '--skip-confirmation', '--skip-install');
await useSha();
await installWorkspacePackages();

const port = await ngServe();
await validateResponse('/', /Hello,/);

await Promise.all([
waitForAnyProcessOutputToMatch(
/new dependencies optimized: @angular\/platform-browser\/animations\/async/,
6000,
),
writeFile(
'src/app/app.config.ts',
`
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
${(await readFile('src/app/app.config.ts')).replace('provideRouter(routes),', 'provideAnimationsAsync(), provideRouter(routes),')}
`,
),
]);

// Verify the app still works.
await validateResponse('/', /Hello,/);

async function validateResponse(pathname: string, match: RegExp): Promise<void> {
const response = await fetch(new URL(pathname, `http://localhost:${port}`));
const text = await response.text();
assert.match(text, match);
}
}
60 changes: 56 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,7 @@ __metadata:
rollup: "npm:4.27.4"
sass: "npm:1.81.0"
semver: "npm:7.6.3"
vite: "npm:5.4.11"
vite: "npm:6.0.0"
watchpack: "npm:2.4.2"
peerDependencies:
"@angular/compiler": ^19.0.0
Expand Down Expand Up @@ -782,7 +782,7 @@ __metadata:
unenv: "npm:^1.10.0"
verdaccio: "npm:6.0.2"
verdaccio-auth-memory: "npm:^10.0.0"
vite: "npm:5.4.11"
vite: "npm:6.0.0"
watchpack: "npm:2.4.2"
webpack: "npm:5.96.1"
webpack-dev-middleware: "npm:7.4.2"
Expand Down Expand Up @@ -15366,7 +15366,7 @@ __metadata:
languageName: node
linkType: hard

"postcss@npm:8.4.49, postcss@npm:^8.2.14, postcss@npm:^8.4.33, postcss@npm:^8.4.43, postcss@npm:^8.4.47":
"postcss@npm:8.4.49, postcss@npm:^8.2.14, postcss@npm:^8.4.33, postcss@npm:^8.4.43, postcss@npm:^8.4.47, postcss@npm:^8.4.49":
version: 8.4.49
resolution: "postcss@npm:8.4.49"
dependencies:
Expand Down Expand Up @@ -16358,7 +16358,7 @@ __metadata:
languageName: node
linkType: hard

"rollup@npm:4.27.4, rollup@npm:^4.20.0, rollup@npm:^4.24.0, rollup@npm:^4.4.0":
"rollup@npm:4.27.4, rollup@npm:^4.20.0, rollup@npm:^4.23.0, rollup@npm:^4.24.0, rollup@npm:^4.4.0":
version: 4.27.4
resolution: "rollup@npm:4.27.4"
dependencies:
Expand Down Expand Up @@ -18761,6 +18761,58 @@ __metadata:
languageName: node
linkType: hard

"vite@npm:6.0.0":
version: 6.0.0
resolution: "vite@npm:6.0.0"
dependencies:
esbuild: "npm:^0.24.0"
fsevents: "npm:~2.3.3"
postcss: "npm:^8.4.49"
rollup: "npm:^4.23.0"
peerDependencies:
"@types/node": ^18.0.0 || ^20.0.0 || >=22.0.0
jiti: ">=1.21.0"
less: "*"
lightningcss: ^1.21.0
sass: "*"
sass-embedded: "*"
stylus: "*"
sugarss: "*"
terser: ^5.16.0
tsx: ^4.8.1
yaml: ^2.4.2
dependenciesMeta:
fsevents:
optional: true
peerDependenciesMeta:
"@types/node":
optional: true
jiti:
optional: true
less:
optional: true
lightningcss:
optional: true
sass:
optional: true
sass-embedded:
optional: true
stylus:
optional: true
sugarss:
optional: true
terser:
optional: true
tsx:
optional: true
yaml:
optional: true
bin:
vite: bin/vite.js
checksum: 10c0/2516144161c108452691ec1379aefd8f3201da8f225e628cb1cdb7b35b92d856d990cd31f1c36c0f36517346efe181ea3c096a04bc846c5b0d1416b7b7111af8
languageName: node
linkType: hard

"void-elements@npm:^2.0.0":
version: 2.0.1
resolution: "void-elements@npm:2.0.1"
Expand Down