Skip to content
Merged
Show file tree
Hide file tree
Changes from 53 commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
9baaf79
feat(platform): move Nitro orchestration helpers from vite-plugin-nitro
brandonroberts May 23, 2026
de768eb
feat(platform): add Angular linker Rolldown plugin for SSR optimizeDeps
brandonroberts May 23, 2026
853cecc
feat(platform): add analogNitroPlugin (NitroModule + SSR service wrap…
brandonroberts May 23, 2026
beda21b
feat(platform): wire analog() to nitro/vite + analogNitroPlugin
brandonroberts May 23, 2026
88919d3
feat(vite-plugin-nitro)!: deprecate package; orchestration moved to @…
brandonroberts May 23, 2026
278ed5f
refactor(platform): move routeRules x-analog-no-ssr injection into an…
brandonroberts May 24, 2026
20e8e33
feat(platform): export discoverLibraryRoutes and pageGlobs from main …
brandonroberts May 24, 2026
a6d1643
feat(platform)!: drop angular() from analog()'s plugin chain
brandonroberts May 24, 2026
0dd20ac
feat(platform)!: drop nitro() from analog()'s plugin chain
brandonroberts May 24, 2026
05e7fa0
chore: adopt separated analog()/angular()/nitro() plugin shape in ana…
brandonroberts May 24, 2026
4400ebe
fix(platform): register SSR wrapper via environments.ssr.build.rollup…
brandonroberts May 24, 2026
8a2f6cc
chore: switch analog-app build target to vite build CLI for buildApp …
brandonroberts May 24, 2026
d0c0616
fix: allow workspace fs reads so nitro/vite's env-runner can load dev…
brandonroberts May 24, 2026
4b7761c
fix: patch srvx@0.11.15 to await double-nested promises in toNodeHandler
brandonroberts May 24, 2026
7b938f7
fix(platform): install analog-owned SSR renderer to bypass Nitro auto…
brandonroberts May 24, 2026
16e73ab
fix(platform): use Nitro virtual indirection for SSR dispatch instead…
brandonroberts May 24, 2026
d9d842a
fix(platform): gate SSR renderer + add Nitro externals/sanitizer for …
brandonroberts May 25, 2026
7be302a
chore: migrate remaining demo apps to separated analog/angular/nitro …
brandonroberts May 25, 2026
03004b5
docs: add plugin-separation guidance to the v2 to v3 migration guide
brandonroberts May 25, 2026
b15b796
chore(create-analog): add nitro to template devDependencies
brandonroberts May 25, 2026
7e94cd7
chore(create-analog): wire templates to separated plugins (analog + a…
brandonroberts May 25, 2026
0580db2
feat(platform): add ng-update schematic for the separated-plugins mig…
brandonroberts May 25, 2026
99a009c
feat(platform): teach the separated-plugins schematic to move analog.…
brandonroberts May 25, 2026
3725bd1
feat(platform): rewrite argumentless analog() to analog() + angular()…
brandonroberts May 25, 2026
5f5ce88
feat(vite-plugin-angular): fall back to NX_WORKSPACE_ROOT before proc…
brandonroberts May 25, 2026
0537057
chore: drop explicit workspaceRoot from app vite configs
brandonroberts May 25, 2026
5eb6a83
fix: drop srvx@0.11.15 patch; no longer required
brandonroberts May 25, 2026
c0aa081
feat(vite-plugin-angular): expose architect builders as subpath exports
brandonroberts May 25, 2026
1a59b6a
feat(platform)!: ship architect builder shims for @analogjs/platform:…
brandonroberts May 25, 2026
6ac0f46
chore: route demo apps' build target through @analogjs/platform:vite
brandonroberts May 25, 2026
7d8a551
fix(platform): restore legacy dist/<rootDir>/analog output layout
brandonroberts May 25, 2026
1141f76
test(platform): make migrate-to-separated-plugins specs version-agnostic
brandonroberts May 25, 2026
fc961b0
fix(create-analog): add vite-plugin-angular and nitro to devDependencies
brandonroberts May 25, 2026
33554bd
fix: unblock tailwind-debug-app vitest run
brandonroberts May 25, 2026
06516a8
feat(platform)!: drop useAPIMiddleware and ssrBuildDir options
brandonroberts May 25, 2026
62a2292
Revert "feat(vite-plugin-nitro)!: deprecate package; orchestration mo…
brandonroberts May 25, 2026
e27db9c
chore(platform): drop now-unused vite-plugin-nitro dep edge
brandonroberts May 25, 2026
49bb45e
feat(platform): own server.fs.allow + gate output overrides to build
brandonroberts May 25, 2026
91390dd
chore: drop empty-object arg from nitro() calls in demo apps + docs
brandonroberts May 25, 2026
f17730e
chore(create-analog): inject nitro into pnpm scaffolds only
brandonroberts May 25, 2026
7cea109
chore: bump nitro to 3.0.260522-beta
brandonroberts May 25, 2026
94667f4
fix(platform): stop adding nitro to package.json in ng-update schematic
brandonroberts May 25, 2026
d923e15
fix(platform): honor staticData and outputSourceFile in prerender config
brandonroberts May 25, 2026
bfcf988
fix(platform): preserve query string on SSR request shim
brandonroberts May 25, 2026
0e1c47d
fix(platform): honor user apiPrefix in page handler mounts
brandonroberts May 25, 2026
d386a4e
fix(platform): strip route group parens from non-trailing segments
brandonroberts May 25, 2026
4d27f19
fix(platform): match apiPrefix as a full path segment in API middleware
brandonroberts May 25, 2026
57337a6
fix(platform): await async post-rendering hooks
brandonroberts May 25, 2026
d1f470c
fix(platform): keep inner dots in content file names
brandonroberts May 25, 2026
976d106
fix(platform): skip locale expansion for the bare /api route
brandonroberts May 25, 2026
c3e8ac0
refactor(platform): make XMLBuilder a type-only import
brandonroberts May 25, 2026
6db73b9
fix(platform): tighten vite-dev-server port schema to integer
brandonroberts May 25, 2026
2e72570
fix(platform): wire Vercel preset detection and runtime defaults
brandonroberts May 25, 2026
7aadebb
fix: refresh platform required-artifacts list for builder shim layout
brandonroberts May 25, 2026
d8c24f0
fix: make the prepare script Windows-compatible
brandonroberts May 25, 2026
ba33680
fix(platform): use const for the immutable analogCalls array in schem…
brandonroberts May 25, 2026
c6da6a7
fix(storybook-angular): guard against plugins without a name in viteF…
brandonroberts May 25, 2026
c4d9d51
Revert "fix(platform): honor user apiPrefix in page handler mounts"
brandonroberts May 25, 2026
5f3bd32
chore: move blog-app server routes under routes/api/
brandonroberts May 25, 2026
30566cc
fix(platform): wire Nitro serverFetch into SSR for in-process data loads
brandonroberts May 25, 2026
8b333be
fix(platform): hoist Netlify functions to workspace root and wrap SSR…
brandonroberts May 25, 2026
3632f63
fix(platform): import serverFetch from "nitro" root, not "nitro/app"
brandonroberts May 26, 2026
a957320
fix(platform): hoist Vercel and Cloudflare preset outputs to workspac…
brandonroberts May 26, 2026
8217182
fix(platform): rescue Nitro v3 prerender writes on Windows
brandonroberts May 26, 2026
df6f6a3
fix(platform): bridge Vite publicDir to Nitro publicAssets and preser…
brandonroberts May 26, 2026
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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ stats.html

# Nitro
.nitro
.output
/migrations.json

/.env
Expand All @@ -85,4 +86,4 @@ gradle.properties
.cursor
.claude
gradle.properties
*.tsbuildinfo
*.tsbuildinfo
3 changes: 2 additions & 1 deletion apps/analog-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"@analogjs/platform": "workspace:*",
"@analogjs/storybook-angular": "workspace:*",
"@analogjs/vite-plugin-angular": "workspace:*",
"@analogjs/vitest-angular": "workspace:*"
"@analogjs/vitest-angular": "workspace:*",
"nitro": "catalog:"
}
}
9 changes: 2 additions & 7 deletions apps/analog-app/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,14 @@
"tags": [],
"targets": {
"build": {
"executor": "@nx/vite:build",
"executor": "@analogjs/platform:vite",
"dependsOn": [
"platform:build",
"router:build",
"my-package:build",
"top-bar:build"
],
"outputs": [
"{options.outputPath}",
"{workspaceRoot}/dist/apps/analog-app/.nitro",
"{workspaceRoot}/dist/apps/analog-app/ssr",
"{workspaceRoot}/dist/apps/analog-app/analog"
],
"outputs": ["{workspaceRoot}/dist/apps/analog-app/analog"],
"options": {
"configFile": "apps/analog-app/vite.config.ts",
"outputPath": "dist/apps/analog-app/client"
Expand Down
49 changes: 27 additions & 22 deletions apps/analog-app/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/// <reference types="vitest" />

import analog from '@analogjs/platform';
import analog, { discoverLibraryRoutes, pageGlobs } from '@analogjs/platform';
import angular from '@analogjs/vite-plugin-angular';
import { nitro } from 'nitro/vite';
import { resolve } from 'node:path';
import { defineConfig, PluginOption } from 'vite';
import { getWorkspaceDependencyExcludes } from '../../tools/vite/get-workspace-dependency-excludes.js';
Expand Down Expand Up @@ -37,12 +39,15 @@ export default defineConfig(async ({ mode, command }) => {
},
];

const discoveredLibs = discoverLibraryRoutes(resolve(__dirname, '../..'));
const explicitLibPages = useBuiltWorkspaceLibs
? []
: ['/libs/my-package/src/**/*.ts', '/libs/top-bar/src/**/*.ts'];

return {
root: __dirname,
publicDir: 'src/public',
build: {
outDir: '../../dist/apps/analog-app/client',
emptyOutDir: true,
reportCompressedSize: true,
target: ['es2020'],
},
Expand All @@ -58,11 +63,9 @@ export default defineConfig(async ({ mode, command }) => {
content: {
highlighter: 'prism',
},
include: useBuiltWorkspaceLibs
? []
: ['/libs/my-package/src/**/*.ts', '/libs/top-bar/src/**/*.ts'],
discoverRoutes: true,
fileReplacements,
additionalPagesDirs: discoveredLibs.additionalPagesDirs,
additionalContentDirs: discoveredLibs.additionalContentDirs,
additionalAPIDirs: discoveredLibs.additionalAPIDirs,
prerender: {
routes: [
'/',
Expand All @@ -79,23 +82,25 @@ export default defineConfig(async ({ mode, command }) => {
host: base,
},
},
inlineStylesExtension: 'scss',
fastCompile: true,
experimental: {
typedRouter: true,
},
nitro: {
routeRules: {
'/client': {
ssr: false,
},
'/cart/**': {
ssr: false,
},
'/404.html': {
ssr: false,
},
},
}),
angular({
include: [
...explicitLibPages,
...pageGlobs(discoveredLibs.additionalPagesDirs),
],
additionalContentDirs: discoveredLibs.additionalContentDirs,
inlineStylesExtension: 'scss',
fileReplacements,
fastCompile: true,
}),
nitro({
routeRules: {
'/client': { ssr: false },
'/cart/**': { ssr: false },
'/404.html': { ssr: false },
},
}),
{
Expand Down
4 changes: 3 additions & 1 deletion apps/blog-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
"@analogjs/router": "workspace:*"
},
"devDependencies": {
"@analogjs/platform": "workspace:*"
"@analogjs/platform": "workspace:*",
"@analogjs/vite-plugin-angular": "workspace:*",
"nitro": "catalog:"
}
}
9 changes: 2 additions & 7 deletions apps/blog-app/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,9 @@
"tags": [],
"targets": {
"build": {
"executor": "@nx/vite:build",
"executor": "@analogjs/platform:vite",
"dependsOn": ["platform:build", "router:build", "content:build"],
"outputs": [
"{options.outputPath}",
"{workspaceRoot}/dist/apps/blog-app/.nitro",
"{workspaceRoot}/dist/apps/blog-app/ssr",
"{workspaceRoot}/dist/apps/blog-app/analog"
],
"outputs": ["{workspaceRoot}/dist/apps/blog-app/analog"],
"options": {
"configFile": "apps/blog-app/vite.config.ts",
"outputPath": "dist/apps/blog-app/client"
Expand Down
18 changes: 10 additions & 8 deletions apps/blog-app/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/// <reference types="vitest" />

import analog, { type PrerenderContentFile } from '@analogjs/platform';
import angular from '@analogjs/vite-plugin-angular';
import { nitro } from 'nitro/vite';
import { defineConfig } from 'vite';
import { getWorkspaceDependencyExcludes } from '../../tools/vite/get-workspace-dependency-excludes.js';

Expand All @@ -26,14 +28,11 @@ export default defineConfig(() => {
exclude: getWorkspaceDependencyExcludes(__dirname),
},
build: {
outDir: '../../dist/apps/blog-app/client',
emptyOutDir: true,
reportCompressedSize: true,
target: ['es2020'],
},
plugins: [
analog({
liveReload: true,
content: {
highlighter: 'shiki',
shikiOptions: {
Expand Down Expand Up @@ -84,11 +83,14 @@ export default defineConfig(() => {
host: 'https://analog-blog.netlify.app',
},
},
nitro: {
prerender: {
autoSubfolderIndex: false,
failOnError: true,
},
}),
angular({
liveReload: true,
}),
nitro({
prerender: {
autoSubfolderIndex: false,
failOnError: true,
},
}),
],
Expand Down
171 changes: 171 additions & 0 deletions apps/docs-app/docs/guides/migrating-v2-to-v3.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,172 @@ Analog v3 no longer supports Angular v16. Upgrade the workspace to Angular v17 o

Analog SFC support was removed and `.agx` files are no longer supported. Replace any remaining SFC usage with standard Angular components, markdown content files, or route/page files that use the current Analog conventions.

### `analog()`, `angular()`, and `nitro()` are now separate plugins

Analog v3 splits the Vite plugin chain into three explicit calls. `analog()` no longer internally invokes `@analogjs/vite-plugin-angular` or `nitro/vite` — you call them yourself. Pass each plugin only the options it owns.

`@analogjs/platform` v3 owns its own Nitro orchestration via `nitro/vite` directly and no longer composes `@analogjs/vite-plugin-nitro` internally. `@analogjs/vite-plugin-nitro` continues to ship as a standalone package for projects that want to wire it themselves; users coming from a v2 `analog({ nitro: {...} })` shape should migrate to the separated shape below (analog + angular + nitro from `nitro/vite`).

Before:

```ts
import { defineConfig } from 'vite';
import analog from '@analogjs/platform';

export default defineConfig(() => ({
plugins: [
analog({
ssr: true,
apiPrefix: 'api',
vite: {
inlineStylesExtension: 'scss',
fastCompile: true,
},
fileReplacements: [
{ replace: 'src/environment.ts', with: 'src/environment.prod.ts' },
],
nitro: {
routeRules: { '/admin/**': { ssr: false } },
},
prerender: {
routes: ['/', '/about'],
},
}),
],
}));
```

After:

```ts
import { resolve } from 'node:path';
import { defineConfig } from 'vite';
import analog from '@analogjs/platform';
import angular from '@analogjs/vite-plugin-angular';
import { nitro } from 'nitro/vite';

export default defineConfig(() => ({
server: {
// Vite 8's strict fs only allows reads under config.root; nitro/vite's
// env-runner reaches pnpm content-hash paths at the workspace root
// when loading its own dev runtime.
fs: { allow: [resolve(__dirname, '../..')] },
},
plugins: [
analog({
ssr: true,
apiPrefix: 'api',
prerender: {
routes: ['/', '/about'],
},
}),
angular({
workspaceRoot: resolve(__dirname, '../..'),
inlineStylesExtension: 'scss',
fastCompile: true,
fileReplacements: [
{ replace: 'src/environment.ts', with: 'src/environment.prod.ts' },
],
}),
nitro({
routeRules: { '/admin/**': { ssr: false } },
}),
],
}));
```

Add `@analogjs/vite-plugin-angular` and `nitro` to the app's `devDependencies`:

```json
{
"devDependencies": {
"@analogjs/platform": "...",
"@analogjs/vite-plugin-angular": "...",
"nitro": "..."
}
}
```

#### Options that moved off `analog()`

These options used to live on `analog()`. Pass them to `angular()` or `nitro()` directly:

| v2 location | v3 location |
| ------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------- |
| `analog({ vite: {...} })` | spread directly into `angular({...})` |
| `analog({ jit })`, `disableTypeChecking`, `liveReload`, `inlineStylesExtension`, `fileReplacements`, `fastCompile`, `fastCompileMode`, `include` | `angular({...})` |
| `analog({ tailwindCss: {...} })` | `angular({ tailwindCss: {...} })` |
| `analog({ experimental: { useAngularCompilationAPI: true } })` | `angular({ experimental: { useAngularCompilationAPI: true } })` |
| `analog({ experimental: { stylePipeline: { angularPlugins: [...] } } })` | `angular({ stylePipeline: { plugins: [...] } })` |
| `analog({ nitro: {...} })` | `nitro({...})` (first arg) |
| `analog({ vite: false })` | drop `angular()` from the plugins array |

`analog()` retains `ssr`, `apiPrefix`, `entryServer`, `content`, `prerender`, `i18n`, `discoverRoutes`, `additionalPagesDirs`/`additionalContentDirs`/`additionalAPIDirs`, `debug`, and `experimental.typedRouter`/`experimental.stylePipeline`.

#### Workspace library globs

If your v2 config used `discoverRoutes: true` to compile workspace library pages, the same helper is now exported from `@analogjs/platform`. Call it once and feed the result to both `analog()` and `angular()`:

```ts
import analog, { discoverLibraryRoutes, pageGlobs } from '@analogjs/platform';
import angular from '@analogjs/vite-plugin-angular';

const libs = discoverLibraryRoutes(resolve(__dirname, '../..'));

plugins: [
analog({
additionalPagesDirs: libs.additionalPagesDirs,
additionalContentDirs: libs.additionalContentDirs,
additionalAPIDirs: libs.additionalAPIDirs,
}),
angular({
include: pageGlobs(libs.additionalPagesDirs),
additionalContentDirs: libs.additionalContentDirs,
}),
nitro(),
];
```

#### Nx build target

If your app uses `@nx/vite:build`, switch it to `nx:run-commands` invoking `vite build`. `@nx/vite:build` iterates `builder.environments` but doesn't call `builder.buildApp()` — and `nitro/vite`'s prerender + final Nitro env build orchestration lives in the `buildApp` hook. Without the CLI's `buildApp` invocation, no prerender runs and the SSR/Nitro env outputs are skipped.

Before (`apps/<app>/project.json`):

```json
{
"build": {
"executor": "@nx/vite:build",
"outputs": [
"{options.outputPath}",
"{workspaceRoot}/dist/apps/<app>/.nitro",
"{workspaceRoot}/dist/apps/<app>/ssr",
"{workspaceRoot}/dist/apps/<app>/analog"
],
"options": {
"configFile": "apps/<app>/vite.config.ts",
"outputPath": "dist/apps/<app>/client"
}
}
}
```

After:

```json
{
"build": {
"executor": "nx:run-commands",
"outputs": ["{workspaceRoot}/apps/<app>/.output"],
"options": {
"command": "vite build -c apps/<app>/vite.config.ts"
}
}
}
```

Also drop the top-level `build.outDir` override in `vite.config.ts`. Under `nitro/vite`, the client environment's output is relocated to `<rootDir>/.output/public` by Nitro; the legacy `dist/apps/<app>/client` override no longer matches the active output path.

### Content rendering now requires an explicit highlighter

If your app renders markdown content, configure the content highlighter through the `analog()` plugin in `vite.config.ts`. New blog templates already do this, but older full-stack apps often do not.
Expand Down Expand Up @@ -152,6 +318,11 @@ Keep automated migration tooling focused on the breaking changes above:

- require Angular v17 or newer before applying v3 changes
- replace deep or internal imports with public package entrypoints
- split `analog()` into `analog() + angular() + nitro()`, moving each option to the plugin that now owns it (see [plugin separation](#analog-angular-and-nitro-are-now-separate-plugins))
- @analogjs/platform no longer composes @analogjs/vite-plugin-nitro internally; direct importers can either migrate to `@analogjs/platform` + `nitro/vite` (recommended) or continue using @analogjs/vite-plugin-nitro standalone
- add `@analogjs/vite-plugin-angular` to app `devDependencies` (the separated shape imports it directly). `nitro` is picked up as a transitive of `@analogjs/platform` for npm/yarn; pnpm users must add it to `devDependencies` explicitly
- replace `@nx/vite:build` with `nx:run-commands` invoking `vite build -c apps/<app>/vite.config.ts`; drop the legacy `build.outDir` override and update `outputs` to `apps/<app>/.output`
- add `server.fs.allow` pointing at the workspace root in `vite.config.ts` so Vite 8's strict fs allows nitro/vite's env runner to load its own dev runtime through pnpm content-hash paths
- add explicit `analog({ content: { highlighter: 'shiki' } })` config when the app renders markdown content
- add `withContentRoutes()` from `@analogjs/router/content` when the app uses markdown page routes
- flag `analog({ i18n: ... })`, `provideI18n()`, `injectSwitchLocale()`, `loadTranslationsRuntime()`, or content locale helpers as removed v3 APIs
Expand Down
3 changes: 2 additions & 1 deletion apps/opt-catchall-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
},
"devDependencies": {
"@analogjs/platform": "workspace:*",
"@analogjs/vite-plugin-angular": "workspace:*"
"@analogjs/vite-plugin-angular": "workspace:*",
"nitro": "catalog:"
}
}
Loading
Loading