Skip to content
Draft
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
3de0537
test(vite-plugin-nitro): add snapshot test for Vite config mutations
benpsnyder Apr 13, 2026
8826989
feat(vite-plugin-angular): enhance debugging documentation and improv…
benpsnyder Apr 13, 2026
6f72fa1
feat: add error handling for @reference in comments and improve direc…
benpsnyder Apr 13, 2026
43a5438
chore: update dependencies and configurations for Docusaurus and Angu…
benpsnyder Apr 13, 2026
70263db
chore: update debugging documentation with additional package overrid…
benpsnyder Apr 13, 2026
efcb7f2
chore: update debugging documentation to clarify usage of catalog and…
benpsnyder Apr 13, 2026
e76e35a
docs: enhance documentation on duplicate analog() registrations and S…
benpsnyder Apr 14, 2026
9f39aa7
Merge remote-tracking branch 'origin/alpha' into feat/support-snyder-…
benpsnyder Apr 14, 2026
18312c0
Merge remote-tracking branch 'origin/alpha' into feat/support-snyder-…
benpsnyder Apr 14, 2026
229b9e4
chore: update comments for clarity in Angular and Nitro plugins, and …
benpsnyder Apr 14, 2026
3155d19
feat: enhance configuration handling and improve test stability by cl…
benpsnyder Apr 14, 2026
e0b491b
chore: refine comments and enhance test stability in Angular and Nitr…
benpsnyder Apr 14, 2026
71b31a9
feat: improve handling of CSS comments and references, ensuring quote…
benpsnyder Apr 14, 2026
69af095
feat: add function to detect @reference text in comments and refine C…
benpsnyder Apr 14, 2026
ac4a4ca
feat: refactor CSS directive handling by extracting utility functions…
benpsnyder Apr 14, 2026
ac2dd07
feat: implement TailwindReferenceError handling in JIT plugin and aug…
benpsnyder Apr 14, 2026
00a43bd
fix: update console warning implementation in JIT plugin tests to ret…
benpsnyder Apr 14, 2026
49e660b
feat: add support for selectorless compilation in Angular plugin, enh…
benpsnyder Apr 15, 2026
6679136
Merge remote-tracking branch 'analogjs/alpha' into feat/snyder-suppor…
benpsnyder Apr 15, 2026
cb5feab
feat(vite-plugin-angular): enhance live reload plugin with Windows pa…
benpsnyder Apr 15, 2026
638d30e
feat(vite-plugin-angular): improve selectorless compilation handling …
benpsnyder Apr 15, 2026
c824525
refactor: narrow compilation API PR scope
benpsnyder Apr 15, 2026
8241741
docs: expand maintainer debugging workflow
benpsnyder Apr 15, 2026
6bad91e
docs: update debugging guide with techniques and improved descriptions
benpsnyder Apr 15, 2026
8f93220
feat(vite-plugin-angular): enhance live reload tests with additional …
benpsnyder Apr 15, 2026
7859392
Merge remote-tracking branch 'analogjs/alpha' into docs/debugging-tec…
benpsnyder Apr 15, 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
100 changes: 85 additions & 15 deletions docs/debugging.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ When debugging package changes in this monorepo, prefer the project-level Nx tar
# Focus on Angular plugin HMR/style behavior
DEBUG=analog:angular:hmr,analog:angular:styles pnpm nx test vite-plugin-angular

# Focus on platform routing output
DEBUG=analog:platform:routes pnpm nx build platform
# Focus on Compilation API inclusion, emit registration, and transform misses
DEBUG=analog:angular:compilation-api,analog:angular:compiler,analog:angular:emit pnpm nx test vite-plugin-angular

# Focus on the style-pipeline integration seam in a served app
DEBUG=analog:platform:style-pipeline,analog:angular:style-pipeline pnpm nx serve your-app
Expand All @@ -47,6 +47,16 @@ first so each `dist` folder contains its generated `package.json`. The source
package manifests still contain `catalog:` and `workspace:*` references that are
only rewritten during Analog's release-style build pipeline.

Prefer `link:` for active local debugging.

- `link:` keeps the consumer wired to the live built `dist` directories through
symlinks.
- `file:` can stage a snapshot into the consumer's `.pnpm` store. If you later
rebuild Analog without changing the version or specifier, pnpm may decide the
consumer install is already current and keep serving stale package contents.
- If you must use `file:`, always verify the active installed package contents,
not just the Analog source tree or `dist`.

### Local checkout example

`pnpm-workspace.yaml`
Expand All @@ -57,26 +67,26 @@ packages:
- 'libs/**'

overrides:
'@analogjs/platform': file:/path/to/analog/packages/platform/dist
'@analogjs/router': file:/path/to/analog/packages/router/dist
'@analogjs/vite-plugin-angular': file:/path/to/analog/packages/vite-plugin-angular/dist
'@analogjs/vite-plugin-nitro': file:/path/to/analog/packages/vite-plugin-nitro/dist
'@analogjs/vitest-angular': file:/path/to/analog/packages/vitest-angular/dist
'@analogjs/platform': link:/path/to/analog/packages/platform/dist
'@analogjs/router': link:/path/to/analog/packages/router/dist
'@analogjs/vite-plugin-angular': link:/path/to/analog/packages/vite-plugin-angular/dist
'@analogjs/vite-plugin-nitro': link:/path/to/analog/packages/vite-plugin-nitro/dist
'@analogjs/vitest-angular': link:/path/to/analog/packages/vitest-angular/dist
```

Root `package.json`

```json
{
"dependencies": {
"@analogjs/platform": "file:/path/to/analog/packages/platform/dist"
"@analogjs/platform": "link:/path/to/analog/packages/platform/dist"
},
"overrides": {
"@analogjs/platform": "file:/path/to/analog/packages/platform/dist",
"@analogjs/router": "file:/path/to/analog/packages/router/dist",
"@analogjs/vite-plugin-angular": "file:/path/to/analog/packages/vite-plugin-angular/dist",
"@analogjs/vite-plugin-nitro": "file:/path/to/analog/packages/vite-plugin-nitro/dist",
"@analogjs/vitest-angular": "file:/path/to/analog/packages/vitest-angular/dist"
"@analogjs/platform": "link:/path/to/analog/packages/platform/dist",
"@analogjs/router": "link:/path/to/analog/packages/router/dist",
"@analogjs/vite-plugin-angular": "link:/path/to/analog/packages/vite-plugin-angular/dist",
"@analogjs/vite-plugin-nitro": "link:/path/to/analog/packages/vite-plugin-nitro/dist",
"@analogjs/vitest-angular": "link:/path/to/analog/packages/vitest-angular/dist"
}
}
```
Expand All @@ -88,13 +98,52 @@ can still resolve transitive packages like `@analogjs/vite-plugin-angular` and
:::

:::note
pnpm currently does not allow `file:` entries in `catalog`, so local checkout
wiring needs direct `file:` overrides instead of `catalog:` indirection.
If the consumer workspace uses pnpm `catalog` entries, switch both the catalog
entries and the consumer overrides together. Mixed states are a common source of
confusing resolution behavior.
:::

If your app also uses other published Analog packages such as
`@analogjs/content` or `@analogjs/storybook-angular`, pin those the same way.

### Consumer validation checklist

After rebuilding Analog and refreshing the consumer install:

```bash
# Confirm the active install points at your local dist output
readlink node_modules/@analogjs/vite-plugin-angular

# Confirm the dev server is actually listening
curl -k -I https://localhost:4200/

# Confirm the shell HTML and stylesheet entry are present
lightpanda fetch \
--insecure-disable-tls-host-verification \
--wait-until done \
--wait-ms 12000 \
--dump html \
https://localhost:4200/ | sed -n '1,80p'
```

What to trust:

- `curl` is the fastest readiness check.
- `Lightpanda` is useful for reachability, shell HTML, and asset-presence smoke
tests.
- Treat the consumer dev server logs as the source of truth for Angular
compilation failures such as:
- `contains Angular decorators but is not in the TypeScript program`
- missing Angular emit output
- JIT fallback for `@Injectable()` / `@Component()`

What not to over-trust:

- `Lightpanda` can surface browser-client or HMR-transport behavior that does
not necessarily mean Analog failed to compile the app correctly.
- A successful Analog rebuild does not prove the consumer is executing the new
package code unless the consumer install is verified.

### GitHub branch example

If you want the same pattern from a GitHub branch instead of a local path, pnpm
Expand Down Expand Up @@ -136,6 +185,27 @@ those manifests still contain unresolved `catalog:` and `workspace:*`
specifiers.
:::

## Useful Angular Debug Scopes

Start with the smallest scope set that answers the question:

```bash
DEBUG=analog:angular:compilation-api,analog:angular:compiler pnpm nx serve your-app
DEBUG=analog:angular:emit pnpm nx serve your-app
DEBUG=analog:angular:emit:v pnpm nx serve your-app
```

Recommended interpretation:

- `analog:angular:compilation-api`
- wrapper tsconfig generation, initialization, high-level compilation flow
- `analog:angular:compiler`
- compiler-option mutations and transform-path decisions
- `analog:angular:emit`
- root-name expansion, emit registration, transform misses/hits
- `analog:angular:emit:v`
- per-file root lists, output registration, and normalized emitter lookups

## Notes

- Use the repo root unless you have a specific reason to run inside a package subdirectory.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ let cachedViteActual: typeof import('vite');
let cachedDevkitActual: typeof import('./utils/devkit.js');

async function setupLiveReloadPlugin(options: {
emitAffectedFiles?: Array<{
contents: string;
filename: string;
}>;
include?: string[];
stylePreprocessor?: (
code: string,
Expand Down Expand Up @@ -102,7 +106,9 @@ async function setupLiveReloadPlugin(options: {
initialize,
update: vi.fn(),
diagnoseFiles: vi.fn().mockResolvedValue({ errors: [], warnings: [] }),
emitAffectedFiles: vi.fn().mockResolvedValue([]),
emitAffectedFiles: vi
.fn()
.mockResolvedValue(options.emitAffectedFiles ?? []),
});

let transformStylesheet:
Expand All @@ -125,7 +131,6 @@ async function setupLiveReloadPlugin(options: {

const { angular } = await import('./angular-vite-plugin');
const plugin = angular({
tsconfig: `${resolvedWorkspaceRoot}/tsconfig.base.json`,
liveReload: true,
include: options.include,
tsconfig: resolvedTsconfig,
Expand Down Expand Up @@ -456,4 +461,154 @@ describe('angular hmr style preprocessing', () => {
}
},
);

it(
'wraps the compilation API tsconfig when project references and tsconfig paths add source roots',
{ timeout: 15_000 },
async () => {
const tempWorkspaceRoot = mkdtempSync(
join(tmpdir(), 'analog-compilation-api-refs-paths-'),
);
const normalize = (value: string) => value.replaceAll('\\', '/');

try {
mkdirSync(join(tempWorkspaceRoot, 'src/app'), { recursive: true });
mkdirSync(join(tempWorkspaceRoot, 'libs/shared/feature/src'), {
recursive: true,
});
mkdirSync(join(tempWorkspaceRoot, 'libs/shared/extra/src'), {
recursive: true,
});

writeFileSync(
join(tempWorkspaceRoot, 'src/app/app.component.ts'),
'export const app = true;\n',
);
writeFileSync(
join(
tempWorkspaceRoot,
'libs/shared/feature/src/feature.component.ts',
),
'export const feature = true;\n',
);
writeFileSync(
join(tempWorkspaceRoot, 'libs/shared/extra/src/index.ts'),
'export const extra = true;\n',
);
writeFileSync(
join(tempWorkspaceRoot, 'libs/shared/feature/tsconfig.lib.json'),
JSON.stringify(
{
compilerOptions: {
composite: true,
},
include: ['src/**/*.ts'],
},
null,
2,
),
);
writeFileSync(
join(tempWorkspaceRoot, 'tsconfig.base.json'),
JSON.stringify(
{
compilerOptions: {
baseUrl: '.',
module: 'esnext',
moduleResolution: 'bundler',
paths: {
'@shared/extra': ['libs/shared/extra/src/index.ts'],
},
target: 'es2022',
},
files: ['./src/app/app.component.ts'],
references: [{ path: './libs/shared/feature/tsconfig.lib.json' }],
},
null,
2,
),
);

const { initialize } = await setupLiveReloadPlugin({
tsconfig: join(tempWorkspaceRoot, 'tsconfig.base.json'),
workspaceRoot: tempWorkspaceRoot,
});

const [generatedTsconfigPath] = initialize.mock.calls[0] as [string];
expect(normalize(generatedTsconfigPath)).not.toBe(
normalize(join(tempWorkspaceRoot, 'tsconfig.base.json')),
);

const generatedConfig = JSON.parse(
readFileSync(generatedTsconfigPath, 'utf-8'),
) as {
extends: string;
files: string[];
references?: Array<{ path: string }>;
};

expect(generatedConfig.extends).toBe(
normalize(join(tempWorkspaceRoot, 'tsconfig.base.json')),
);
expect(generatedConfig.files).toEqual(
expect.arrayContaining([
normalize(join(tempWorkspaceRoot, 'src/app/app.component.ts')),
normalize(
join(
tempWorkspaceRoot,
'libs/shared/feature/src/feature.component.ts',
),
),
normalize(
join(tempWorkspaceRoot, 'libs/shared/extra/src/index.ts'),
),
]),
);
expect(generatedConfig.references).toEqual([
{ path: './libs/shared/feature/tsconfig.lib.json' },
]);
} finally {
rmSync(tempWorkspaceRoot, { force: true, recursive: true });
}
},
);

it(
'resolves Angular emit output for Windows /@fs/ transform ids',
{ timeout: 15_000 },
async () => {
const windowsComponentId = 'C:/project/src/app/demo.component.ts';
const emittedCode = 'export const demo = true;\n';
const { plugin } = await setupLiveReloadPlugin({
emitAffectedFiles: [
{
filename: windowsComponentId,
contents: emittedCode,
},
],
});

const transformed = await plugin.transform.handler.call(
{
addWatchFile: vi.fn(),
error: vi.fn(),
warn: vi.fn(),
},
`
import { Component } from '@angular/core';

@Component({
template: '<p>Demo</p>',
})
export class DemoComponent {}
`,
`/@fs/${windowsComponentId}`,
);

expect(transformed).toEqual({
code: emittedCode,
map: null,
});
},
);
});
Loading
Loading