Skip to content

Commit c22378a

Browse files
feat(platform)!: migrate to nitro/vite and split analog() into analog() + angular() + nitro() (#2343)
Closes #2035 Closes #2188 BREAKING CHANGES `@analogjs/vite-plugin-nitro` → `nitro/vite` `@analogjs/platform`'s `analog()` now bundles Nitro v3's first-party `nitro/vite` plugin instead of `@analogjs/vite-plugin-nitro`. Standalone users (those who used `@analogjs/vite-plugin-nitro` directly) must install `nitro` as a dev dep and add `nitro()` to their Vite plugin chain alongside `analog()`. BEFORE: ```ts // vite.config.ts import analog from '@analogjs/platform'; import { defineConfig } from 'vite'; export default defineConfig({ plugins: [analog({ /* ... */ })], }); ``` AFTER: ```ts // vite.config.ts import analog from '@analogjs/platform'; import angular from '@analogjs/vite-plugin-angular'; import { nitro } from 'nitro/vite'; import { defineConfig } from 'vite'; export default defineConfig({ plugins: [ analog({ /* ... */ }), angular({ /* ... vite object */ }), nitro({ /* ... nitro object*/ }) ], }); ```
1 parent 0dfbd7f commit c22378a

79 files changed

Lines changed: 4952 additions & 907 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ stats.html
6262

6363
# Nitro
6464
.nitro
65+
.output
6566
/migrations.json
6667

6768
/.env
@@ -85,4 +86,4 @@ gradle.properties
8586
.cursor
8687
.claude
8788
gradle.properties
88-
*.tsbuildinfo
89+
*.tsbuildinfo

apps/analog-app/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"@analogjs/platform": "workspace:*",
1414
"@analogjs/storybook-angular": "workspace:*",
1515
"@analogjs/vite-plugin-angular": "workspace:*",
16-
"@analogjs/vitest-angular": "workspace:*"
16+
"@analogjs/vitest-angular": "workspace:*",
17+
"nitro": "catalog:"
1718
}
1819
}

apps/analog-app/project.json

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,14 @@
77
"tags": [],
88
"targets": {
99
"build": {
10-
"executor": "@nx/vite:build",
10+
"executor": "@analogjs/platform:vite",
1111
"dependsOn": [
1212
"platform:build",
1313
"router:build",
1414
"my-package:build",
1515
"top-bar:build"
1616
],
17-
"outputs": [
18-
"{options.outputPath}",
19-
"{workspaceRoot}/dist/apps/analog-app/.nitro",
20-
"{workspaceRoot}/dist/apps/analog-app/ssr",
21-
"{workspaceRoot}/dist/apps/analog-app/analog"
22-
],
17+
"outputs": ["{workspaceRoot}/dist/apps/analog-app/analog"],
2318
"options": {
2419
"configFile": "apps/analog-app/vite.config.ts",
2520
"outputPath": "dist/apps/analog-app/client"

apps/analog-app/vite.config.ts

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
/// <reference types="vitest" />
22

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

42+
const discoveredLibs = discoverLibraryRoutes(resolve(__dirname, '../..'));
43+
const explicitLibPages = useBuiltWorkspaceLibs
44+
? []
45+
: ['/libs/my-package/src/**/*.ts', '/libs/top-bar/src/**/*.ts'];
46+
4047
return {
4148
root: __dirname,
4249
publicDir: 'src/public',
4350
build: {
44-
outDir: '../../dist/apps/analog-app/client',
45-
emptyOutDir: true,
4651
reportCompressedSize: true,
4752
target: ['es2020'],
4853
},
@@ -58,11 +63,9 @@ export default defineConfig(async ({ mode, command }) => {
5863
content: {
5964
highlighter: 'prism',
6065
},
61-
include: useBuiltWorkspaceLibs
62-
? []
63-
: ['/libs/my-package/src/**/*.ts', '/libs/top-bar/src/**/*.ts'],
64-
discoverRoutes: true,
65-
fileReplacements,
66+
additionalPagesDirs: discoveredLibs.additionalPagesDirs,
67+
additionalContentDirs: discoveredLibs.additionalContentDirs,
68+
additionalAPIDirs: discoveredLibs.additionalAPIDirs,
6669
prerender: {
6770
routes: [
6871
'/',
@@ -79,23 +82,25 @@ export default defineConfig(async ({ mode, command }) => {
7982
host: base,
8083
},
8184
},
82-
inlineStylesExtension: 'scss',
83-
fastCompile: true,
8485
experimental: {
8586
typedRouter: true,
8687
},
87-
nitro: {
88-
routeRules: {
89-
'/client': {
90-
ssr: false,
91-
},
92-
'/cart/**': {
93-
ssr: false,
94-
},
95-
'/404.html': {
96-
ssr: false,
97-
},
98-
},
88+
}),
89+
angular({
90+
include: [
91+
...explicitLibPages,
92+
...pageGlobs(discoveredLibs.additionalPagesDirs),
93+
],
94+
additionalContentDirs: discoveredLibs.additionalContentDirs,
95+
inlineStylesExtension: 'scss',
96+
fileReplacements,
97+
fastCompile: true,
98+
}),
99+
nitro({
100+
routeRules: {
101+
'/client': { ssr: false },
102+
'/cart/**': { ssr: false },
103+
'/404.html': { ssr: false },
99104
},
100105
}),
101106
{

apps/blog-app/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
"@analogjs/router": "workspace:*"
88
},
99
"devDependencies": {
10-
"@analogjs/platform": "workspace:*"
10+
"@analogjs/platform": "workspace:*",
11+
"@analogjs/vite-plugin-angular": "workspace:*",
12+
"nitro": "catalog:"
1113
}
1214
}

apps/blog-app/project.json

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,9 @@
77
"tags": [],
88
"targets": {
99
"build": {
10-
"executor": "@nx/vite:build",
10+
"executor": "@analogjs/platform:vite",
1111
"dependsOn": ["platform:build", "router:build", "content:build"],
12-
"outputs": [
13-
"{options.outputPath}",
14-
"{workspaceRoot}/dist/apps/blog-app/.nitro",
15-
"{workspaceRoot}/dist/apps/blog-app/ssr",
16-
"{workspaceRoot}/dist/apps/blog-app/analog"
17-
],
12+
"outputs": ["{workspaceRoot}/dist/apps/blog-app/analog"],
1813
"options": {
1914
"configFile": "apps/blog-app/vite.config.ts",
2015
"outputPath": "dist/apps/blog-app/client"
File renamed without changes.
File renamed without changes.

apps/blog-app/vite.config.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
/// <reference types="vitest" />
22

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

@@ -26,14 +28,11 @@ export default defineConfig(() => {
2628
exclude: getWorkspaceDependencyExcludes(__dirname),
2729
},
2830
build: {
29-
outDir: '../../dist/apps/blog-app/client',
30-
emptyOutDir: true,
3131
reportCompressedSize: true,
3232
target: ['es2020'],
3333
},
3434
plugins: [
3535
analog({
36-
liveReload: true,
3736
content: {
3837
highlighter: 'shiki',
3938
shikiOptions: {
@@ -84,11 +83,14 @@ export default defineConfig(() => {
8483
host: 'https://analog-blog.netlify.app',
8584
},
8685
},
87-
nitro: {
88-
prerender: {
89-
autoSubfolderIndex: false,
90-
failOnError: true,
91-
},
86+
}),
87+
angular({
88+
liveReload: true,
89+
}),
90+
nitro({
91+
prerender: {
92+
autoSubfolderIndex: false,
93+
failOnError: true,
9294
},
9395
}),
9496
],

apps/docs-app/docs/guides/migrating-v2-to-v3.md

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,172 @@ Analog v3 no longer supports Angular v16. Upgrade the workspace to Angular v17 o
3737

3838
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.
3939

40+
### `analog()`, `angular()`, and `nitro()` are now separate plugins
41+
42+
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.
43+
44+
`@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`).
45+
46+
Before:
47+
48+
```ts
49+
import { defineConfig } from 'vite';
50+
import analog from '@analogjs/platform';
51+
52+
export default defineConfig(() => ({
53+
plugins: [
54+
analog({
55+
ssr: true,
56+
apiPrefix: 'api',
57+
vite: {
58+
inlineStylesExtension: 'scss',
59+
fastCompile: true,
60+
},
61+
fileReplacements: [
62+
{ replace: 'src/environment.ts', with: 'src/environment.prod.ts' },
63+
],
64+
nitro: {
65+
routeRules: { '/admin/**': { ssr: false } },
66+
},
67+
prerender: {
68+
routes: ['/', '/about'],
69+
},
70+
}),
71+
],
72+
}));
73+
```
74+
75+
After:
76+
77+
```ts
78+
import { resolve } from 'node:path';
79+
import { defineConfig } from 'vite';
80+
import analog from '@analogjs/platform';
81+
import angular from '@analogjs/vite-plugin-angular';
82+
import { nitro } from 'nitro/vite';
83+
84+
export default defineConfig(() => ({
85+
server: {
86+
// Vite 8's strict fs only allows reads under config.root; nitro/vite's
87+
// env-runner reaches pnpm content-hash paths at the workspace root
88+
// when loading its own dev runtime.
89+
fs: { allow: [resolve(__dirname, '../..')] },
90+
},
91+
plugins: [
92+
analog({
93+
ssr: true,
94+
apiPrefix: 'api',
95+
prerender: {
96+
routes: ['/', '/about'],
97+
},
98+
}),
99+
angular({
100+
workspaceRoot: resolve(__dirname, '../..'),
101+
inlineStylesExtension: 'scss',
102+
fastCompile: true,
103+
fileReplacements: [
104+
{ replace: 'src/environment.ts', with: 'src/environment.prod.ts' },
105+
],
106+
}),
107+
nitro({
108+
routeRules: { '/admin/**': { ssr: false } },
109+
}),
110+
],
111+
}));
112+
```
113+
114+
Add `@analogjs/vite-plugin-angular` and `nitro` to the app's `devDependencies`:
115+
116+
```json
117+
{
118+
"devDependencies": {
119+
"@analogjs/platform": "...",
120+
"@analogjs/vite-plugin-angular": "...",
121+
"nitro": "..."
122+
}
123+
}
124+
```
125+
126+
#### Options that moved off `analog()`
127+
128+
These options used to live on `analog()`. Pass them to `angular()` or `nitro()` directly:
129+
130+
| v2 location | v3 location |
131+
| ------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------- |
132+
| `analog({ vite: {...} })` | spread directly into `angular({...})` |
133+
| `analog({ jit })`, `disableTypeChecking`, `liveReload`, `inlineStylesExtension`, `fileReplacements`, `fastCompile`, `fastCompileMode`, `include` | `angular({...})` |
134+
| `analog({ tailwindCss: {...} })` | `angular({ tailwindCss: {...} })` |
135+
| `analog({ experimental: { useAngularCompilationAPI: true } })` | `angular({ experimental: { useAngularCompilationAPI: true } })` |
136+
| `analog({ experimental: { stylePipeline: { angularPlugins: [...] } } })` | `angular({ stylePipeline: { plugins: [...] } })` |
137+
| `analog({ nitro: {...} })` | `nitro({...})` (first arg) |
138+
| `analog({ vite: false })` | drop `angular()` from the plugins array |
139+
140+
`analog()` retains `ssr`, `apiPrefix`, `entryServer`, `content`, `prerender`, `i18n`, `discoverRoutes`, `additionalPagesDirs`/`additionalContentDirs`/`additionalAPIDirs`, `debug`, and `experimental.typedRouter`/`experimental.stylePipeline`.
141+
142+
#### Workspace library globs
143+
144+
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()`:
145+
146+
```ts
147+
import analog, { discoverLibraryRoutes, pageGlobs } from '@analogjs/platform';
148+
import angular from '@analogjs/vite-plugin-angular';
149+
150+
const libs = discoverLibraryRoutes(resolve(__dirname, '../..'));
151+
152+
plugins: [
153+
analog({
154+
additionalPagesDirs: libs.additionalPagesDirs,
155+
additionalContentDirs: libs.additionalContentDirs,
156+
additionalAPIDirs: libs.additionalAPIDirs,
157+
}),
158+
angular({
159+
include: pageGlobs(libs.additionalPagesDirs),
160+
additionalContentDirs: libs.additionalContentDirs,
161+
}),
162+
nitro(),
163+
];
164+
```
165+
166+
#### Nx build target
167+
168+
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.
169+
170+
Before (`apps/<app>/project.json`):
171+
172+
```json
173+
{
174+
"build": {
175+
"executor": "@nx/vite:build",
176+
"outputs": [
177+
"{options.outputPath}",
178+
"{workspaceRoot}/dist/apps/<app>/.nitro",
179+
"{workspaceRoot}/dist/apps/<app>/ssr",
180+
"{workspaceRoot}/dist/apps/<app>/analog"
181+
],
182+
"options": {
183+
"configFile": "apps/<app>/vite.config.ts",
184+
"outputPath": "dist/apps/<app>/client"
185+
}
186+
}
187+
}
188+
```
189+
190+
After:
191+
192+
```json
193+
{
194+
"build": {
195+
"executor": "nx:run-commands",
196+
"outputs": ["{workspaceRoot}/apps/<app>/.output"],
197+
"options": {
198+
"command": "vite build -c apps/<app>/vite.config.ts"
199+
}
200+
}
201+
}
202+
```
203+
204+
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.
205+
40206
### Content rendering now requires an explicit highlighter
41207

42208
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.
@@ -152,6 +318,11 @@ Keep automated migration tooling focused on the breaking changes above:
152318

153319
- require Angular v17 or newer before applying v3 changes
154320
- replace deep or internal imports with public package entrypoints
321+
- 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))
322+
- @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
323+
- 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
324+
- 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`
325+
- 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
155326
- add explicit `analog({ content: { highlighter: 'shiki' } })` config when the app renders markdown content
156327
- add `withContentRoutes()` from `@analogjs/router/content` when the app uses markdown page routes
157328
- flag `analog({ i18n: ... })`, `provideI18n()`, `injectSwitchLocale()`, `loadTranslationsRuntime()`, or content locale helpers as removed v3 APIs

0 commit comments

Comments
 (0)