Skip to content

Commit c824525

Browse files
committed
refactor: narrow compilation API PR scope
1 parent 638d30e commit c824525

5 files changed

Lines changed: 0 additions & 282 deletions

File tree

packages/platform/src/lib/options.ts

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -200,26 +200,6 @@ export interface Options {
200200
*/
201201
useAngularCompilationAPI?: boolean;
202202

203-
/**
204-
* Forward Angular selectorless compilation support into
205-
* `@analogjs/vite-plugin-angular`.
206-
*
207-
* This toggles Angular's compiler-wide selectorless mode. Analog may
208-
* otherwise infer a default from file-based pages or route entry points,
209-
* including additional page roots discovered from libraries. That auto
210-
* mode remains the default for backwards compatibility with existing
211-
* selectorless route components, so set this explicitly when you need to
212-
* pin the behavior.
213-
*
214-
* Also accepted at `vite.experimental.enableSelectorless` for backwards
215-
* compatibility.
216-
*
217-
* Has no effect when `vite` is set to `false`.
218-
*/
219-
enableSelectorless?: PluginOptions['experimental'] extends object
220-
? NonNullable<PluginOptions['experimental']>['enableSelectorless']
221-
: boolean;
222-
223203
/**
224204
* Enable typed route table generation for type-safe navigation.
225205
*

packages/platform/src/lib/platform-plugin.spec.ts

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -141,43 +141,6 @@ describe('platformPlugin', () => {
141141
);
142142
});
143143

144-
it('forwards experimental.enableSelectorless to the Angular vite plugin', () => {
145-
platformPlugin({
146-
experimental: {
147-
enableSelectorless: false,
148-
},
149-
});
150-
151-
expect(angularSpy).toHaveBeenCalledWith(
152-
expect.objectContaining({
153-
experimental: expect.objectContaining({
154-
enableSelectorless: false,
155-
}),
156-
}),
157-
);
158-
});
159-
160-
it('prefers top-level selectorless config over legacy vite.experimental config', () => {
161-
platformPlugin({
162-
experimental: {
163-
enableSelectorless: false,
164-
},
165-
vite: {
166-
experimental: {
167-
enableSelectorless: true,
168-
},
169-
},
170-
});
171-
172-
expect(angularSpy).toHaveBeenCalledWith(
173-
expect.objectContaining({
174-
experimental: expect.objectContaining({
175-
enableSelectorless: false,
176-
}),
177-
}),
178-
);
179-
});
180-
181144
it('does not force semantic type checking onto the dev hot path by default', () => {
182145
platformPlugin();
183146

packages/platform/src/lib/platform-plugin.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -59,19 +59,11 @@ export function platformPlugin(opts: Options = {}): Plugin[] {
5959
);
6060
}
6161

62-
// Keep the top-level Analog experimental surface and the legacy
63-
// `vite.experimental` compatibility surface in lockstep. Missing one of
64-
// these compiler flags here makes app config appear correct while the
65-
// lower-level Angular plugin still sees a different value.
6662
const useAngularCompilationAPI =
6763
platformOptions.experimental?.useAngularCompilationAPI ??
6864
viteExperimental?.useAngularCompilationAPI;
69-
const enableSelectorless =
70-
platformOptions.experimental?.enableSelectorless ??
71-
viteExperimental?.enableSelectorless;
7265
debugPlatform('experimental options resolved', {
7366
useAngularCompilationAPI: !!useAngularCompilationAPI,
74-
enableSelectorless,
7567
typedRouter: platformOptions.experimental?.typedRouter,
7668
stylePipeline: !!platformOptions.experimental?.stylePipeline,
7769
});
@@ -140,7 +132,6 @@ export function platformPlugin(opts: Options = {}): Plugin[] {
140132
experimental: {
141133
...(viteExperimental ?? {}),
142134
useAngularCompilationAPI,
143-
enableSelectorless,
144135
},
145136
}),
146137
)),

packages/vite-plugin-angular/src/lib/angular-vite-plugin-live-reload.spec.ts

Lines changed: 0 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,6 @@ async function setupLiveReloadPlugin(options: {
3232
filename: string;
3333
}>;
3434
include?: string[];
35-
experimental?: {
36-
enableSelectorless?: boolean;
37-
};
3835
stylePreprocessor?: (
3936
code: string,
4037
filename: string,
@@ -143,7 +140,6 @@ async function setupLiveReloadPlugin(options: {
143140
workspaceRoot: resolvedWorkspaceRoot,
144141
experimental: {
145142
useAngularCompilationAPI: true,
146-
enableSelectorless: options.experimental?.enableSelectorless,
147143
},
148144
}).find((entry) => entry.name === '@analogjs/vite-plugin-angular') as any;
149145

@@ -276,110 +272,6 @@ describe('angular hmr style preprocessing', () => {
276272
},
277273
);
278274

279-
it(
280-
'allows selectorless compilation to be disabled explicitly',
281-
{ timeout: 15_000 },
282-
async () => {
283-
const workspaceRoot = mkdtempSync(
284-
join(tmpdir(), 'analog-live-reload-selectorless-explicit-off-'),
285-
);
286-
temporaryWorkspaceRoots.add(workspaceRoot);
287-
mkdirSync(join(workspaceRoot, 'src/app/pages'), { recursive: true });
288-
writeFileSync(
289-
join(workspaceRoot, 'src/app/pages/home.page.ts'),
290-
`
291-
import { Component } from '@angular/core';
292-
293-
@Component({
294-
template: '<p>Home</p>',
295-
})
296-
export default class HomePageComponent {}
297-
`,
298-
);
299-
300-
const { initialize } = await setupLiveReloadPlugin({
301-
workspaceRoot,
302-
experimental: {
303-
enableSelectorless: false,
304-
},
305-
});
306-
307-
const initializeCall = initialize.mock.calls[0];
308-
expect(initializeCall).toBeTruthy();
309-
310-
const mutateTsCompilerOptions = initializeCall?.[2] as
311-
| ((options: Record<string, unknown>) => Record<string, unknown>)
312-
| undefined;
313-
314-
expect(mutateTsCompilerOptions).toBeTypeOf('function');
315-
316-
const mutated = mutateTsCompilerOptions?.({});
317-
318-
// Explicit app config must win even when the route heuristic would have
319-
// enabled selectorless by default.
320-
expect(mutated?._enableSelectorless).toBeUndefined();
321-
},
322-
);
323-
324-
it(
325-
'defaults selectorless compilation off when the app has no file-based pages',
326-
{ timeout: 15_000 },
327-
async () => {
328-
const { initialize } = await setupLiveReloadPlugin({});
329-
330-
const initializeCall = initialize.mock.calls[0];
331-
expect(initializeCall).toBeTruthy();
332-
333-
const mutateTsCompilerOptions = initializeCall?.[2] as
334-
| ((options: Record<string, unknown>) => Record<string, unknown>)
335-
| undefined;
336-
337-
expect(mutateTsCompilerOptions).toBeTypeOf('function');
338-
339-
const mutated = mutateTsCompilerOptions?.({});
340-
341-
expect(mutated?._enableSelectorless).toBeUndefined();
342-
},
343-
);
344-
345-
it(
346-
'defaults selectorless compilation on when the app has file-based pages',
347-
{ timeout: 15_000 },
348-
async () => {
349-
const workspaceRoot = mkdtempSync(
350-
join(tmpdir(), 'analog-live-reload-selectorless-pages-'),
351-
);
352-
temporaryWorkspaceRoots.add(workspaceRoot);
353-
mkdirSync(join(workspaceRoot, 'src/app/pages'), { recursive: true });
354-
writeFileSync(
355-
join(workspaceRoot, 'src/app/pages/home.page.ts'),
356-
`
357-
import { Component } from '@angular/core';
358-
359-
@Component({
360-
template: '<p>Home</p>',
361-
})
362-
export default class HomePageComponent {}
363-
`,
364-
);
365-
366-
const { initialize } = await setupLiveReloadPlugin({ workspaceRoot });
367-
368-
const initializeCall = initialize.mock.calls[0];
369-
expect(initializeCall).toBeTruthy();
370-
371-
const mutateTsCompilerOptions = initializeCall?.[2] as
372-
| ((options: Record<string, unknown>) => Record<string, unknown>)
373-
| undefined;
374-
375-
expect(mutateTsCompilerOptions).toBeTypeOf('function');
376-
377-
const mutated = mutateTsCompilerOptions?.({});
378-
379-
expect(mutated?._enableSelectorless).toBe(true);
380-
},
381-
);
382-
383275
it('prepends content via stylePreprocessor through the HMR stylesheet path', async () => {
384276
const prepender = (code: string, _filename: string) =>
385277
`@reference "../styles/tailwind.css";\n${code}`;

packages/vite-plugin-angular/src/lib/angular-vite-plugin.ts

Lines changed: 0 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -177,20 +177,6 @@ export interface PluginOptions {
177177
experimental?: {
178178
useAngularCompilationAPI?: boolean;
179179
useAnalogCompiler?: boolean;
180-
/**
181-
* Controls Angular's experimental selectorless compilation mode.
182-
*
183-
* Defaults to `true` only when Angular 20+ applications use file-based
184-
* pages or route entry points that rely on selectorless compilation.
185-
* That auto mode is preserved for backwards compatibility with existing
186-
* Analog route files that intentionally omit `selector`.
187-
* Angular treats this as a compiler-wide mode, so once enabled it applies
188-
* to the whole program, including workspace libraries pulled in through
189-
* `include` globs or tsconfig references.
190-
* Set this explicitly to pin behavior when route discovery would otherwise
191-
* auto-enable selectorless for a larger app graph than intended.
192-
*/
193-
enableSelectorless?: boolean;
194180
/**
195181
* Compilation output mode for the Analog compiler.
196182
* - `'full'` (default): Emit final Ivy definitions for application builds.
@@ -317,55 +303,6 @@ export function normalizeIncludeGlob(
317303
return normalizePath(resolve(normalizedWorkspaceRoot, normalizedGlob));
318304
}
319305

320-
function isSelectorlessRouteFile(file: string): boolean {
321-
const normalized = normalizePath(file);
322-
return (
323-
normalized.endsWith('.page.ts') ||
324-
normalized.includes('/src/app/routes/') ||
325-
normalized.includes('/app/routes/')
326-
);
327-
}
328-
329-
/**
330-
* Infer selectorless support from route/page entry points.
331-
*
332-
* This heuristic is intentionally broad because file-based routing commonly
333-
* relies on selectorless components. The resulting Angular compiler flag is
334-
* program-wide, not route-scoped, so a matching route-like include can affect
335-
* every file in the current Angular program.
336-
*/
337-
function detectSelectorlessRouteUsage(
338-
root: string,
339-
workspaceRoot: string,
340-
includeGlobs: string[],
341-
): { enabled: boolean; routeFileCount: number } {
342-
const normalizedRoot = normalizePath(resolve(root));
343-
const normalizedWorkspaceRoot = normalizePath(resolve(workspaceRoot));
344-
const appRouteGlobs = [
345-
`${normalizedRoot}/src/app/pages/**/*.page.ts`,
346-
`${normalizedRoot}/src/app/routes/**/*.ts`,
347-
`${normalizedRoot}/app/routes/**/*.ts`,
348-
];
349-
const routeLikeIncludeGlobs = includeGlobs
350-
.map((glob) => normalizeIncludeGlob(normalizedWorkspaceRoot, glob))
351-
.filter(
352-
(glob) =>
353-
glob.includes('.page.') ||
354-
glob.includes('/pages/') ||
355-
glob.includes('/app/routes/') ||
356-
glob.includes('/src/app/routes/'),
357-
);
358-
const routeFiles = globSync([...appRouteGlobs, ...routeLikeIncludeGlobs], {
359-
absolute: true,
360-
dot: true,
361-
onlyFiles: true,
362-
}).filter(isSelectorlessRouteFile);
363-
364-
return {
365-
enabled: routeFiles.length > 0,
366-
routeFileCount: routeFiles.length,
367-
};
368-
}
369306
const classNames = new Map();
370307
export function evictDeletedFileMetadata(
371308
file: string,
@@ -508,7 +445,6 @@ export function buildStylePreprocessor(
508445
export function angular(options?: PluginOptions): Plugin[] {
509446
applyDebugOption(options?.debug, options?.workspaceRoot);
510447
const liveReload = options?.liveReload ?? true;
511-
const explicitEnableSelectorless = options?.experimental?.enableSelectorless;
512448

513449
/**
514450
* Normalize plugin options so defaults
@@ -535,7 +471,6 @@ export function angular(options?: PluginOptions): Plugin[] {
535471
fileReplacements: options?.fileReplacements ?? [],
536472
useAngularCompilationAPI:
537473
options?.experimental?.useAngularCompilationAPI ?? false,
538-
enableSelectorless: explicitEnableSelectorless,
539474
hasTailwindCss: !!options?.tailwindCss,
540475
tailwindCss: options?.tailwindCss,
541476
stylePreprocessor: buildStylePreprocessor(options),
@@ -1080,34 +1015,6 @@ export function angular(options?: PluginOptions): Plugin[] {
10801015
configResolved(config) {
10811016
resolvedConfig = config;
10821017

1083-
if (typeof pluginOptions.enableSelectorless === 'undefined') {
1084-
// Preserve the explicit option as the authoritative value. The route
1085-
// scan is only a default because Angular applies selectorless to the
1086-
// whole program once the compiler option is enabled. We still keep
1087-
// the heuristic default for compatibility with checked-in Analog apps
1088-
// and tests that use selectorless route components without an
1089-
// explicit flag.
1090-
const selectorlessRouteUsage = detectSelectorlessRouteUsage(
1091-
config.root,
1092-
pluginOptions.workspaceRoot,
1093-
pluginOptions.include,
1094-
);
1095-
pluginOptions.enableSelectorless =
1096-
angularFullVersion >= 200000 && selectorlessRouteUsage.enabled;
1097-
debugCompilationApi('selectorless default resolved', {
1098-
angularVersion: angularFullVersion,
1099-
root: config.root,
1100-
routeFileCount: selectorlessRouteUsage.routeFileCount,
1101-
selectorlessEnabled: pluginOptions.enableSelectorless,
1102-
});
1103-
} else {
1104-
debugCompilationApi('selectorless resolved from explicit option', {
1105-
angularVersion: angularFullVersion,
1106-
root: config.root,
1107-
selectorlessEnabled: pluginOptions.enableSelectorless,
1108-
});
1109-
}
1110-
11111018
if (pluginOptions.hasTailwindCss) {
11121019
validateTailwindConfig(config, watchMode);
11131020
}
@@ -2890,7 +2797,6 @@ export function angular(options?: PluginOptions): Plugin[] {
28902797
shouldExternalize: shouldExternalizeStyles(),
28912798
externalRuntimeStyles: !!tsCompilerOptions['externalRuntimeStyles'],
28922799
hmrEnabled: !!tsCompilerOptions['_enableHmr'],
2893-
selectorlessEnabled: pluginOptions.enableSelectorless,
28942800
});
28952801

28962802
if (tsCompilerOptions.compilationMode === 'partial') {
@@ -2899,13 +2805,6 @@ export function angular(options?: PluginOptions): Plugin[] {
28992805
tsCompilerOptions['supportJitMode'] = true;
29002806
}
29012807

2902-
if (pluginOptions.enableSelectorless) {
2903-
// Angular reads selectorless as a compiler-wide mode rather than a
2904-
// per-route transform, so keep this mutation in sync with every
2905-
// compilation path that feeds Angular compiler options.
2906-
tsCompilerOptions['_enableSelectorless'] = true;
2907-
}
2908-
29092808
if (!isTest && config.build?.lib) {
29102809
tsCompilerOptions['declaration'] = true;
29112810
tsCompilerOptions['declarationMap'] = watchMode;
@@ -3193,7 +3092,6 @@ export function angular(options?: PluginOptions): Plugin[] {
31933092
shouldExternalize: shouldExternalizeStyles(),
31943093
externalRuntimeStyles: !!tsCompilerOptions['externalRuntimeStyles'],
31953094
hmrEnabled: !!tsCompilerOptions['_enableHmr'],
3196-
selectorlessEnabled: pluginOptions.enableSelectorless,
31973095
});
31983096

31993097
if (tsCompilerOptions['compilationMode'] === 'partial') {
@@ -3202,12 +3100,6 @@ export function angular(options?: PluginOptions): Plugin[] {
32023100
tsCompilerOptions['supportJitMode'] = true;
32033101
}
32043102

3205-
if (pluginOptions.enableSelectorless) {
3206-
// Keep the legacy NgtscProgram path aligned with the Compilation API
3207-
// path above. Selectorless is still compiler-wide here.
3208-
tsCompilerOptions['_enableSelectorless'] = true;
3209-
}
3210-
32113103
if (!isTest && config.build?.lib) {
32123104
tsCompilerOptions['declaration'] = true;
32133105
tsCompilerOptions['declarationMap'] = watchMode;

0 commit comments

Comments
 (0)