Skip to content

Commit b5f1310

Browse files
committed
fix(@angular/build): ensure correct handling of index.output for SSR
Previously, the index file was not being renamed correctly when using server-side rendering (SSR). Closes: #29012
1 parent 3d1c52b commit b5f1310

File tree

2 files changed

+48
-15
lines changed

2 files changed

+48
-15
lines changed

packages/angular/build/src/builders/application/options.ts

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -327,24 +327,26 @@ export async function normalizeOptions(
327327
let indexOutput: string;
328328
// The output file will be created within the configured output path
329329
if (typeof options.index === 'string') {
330-
/**
331-
* If SSR is activated, create a distinct entry file for the `index.html`.
332-
* This is necessary because numerous server/cloud providers automatically serve the `index.html` as a static file
333-
* if it exists (handling SSG).
334-
*
335-
* For instance, accessing `foo.com/` would lead to `foo.com/index.html` being served instead of hitting the server.
336-
*
337-
* This approach can also be applied to service workers, where the `index.csr.html` is served instead of the prerendered `index.html`.
338-
*/
339-
const indexBaseName = path.basename(options.index);
340-
indexOutput =
341-
(ssrOptions || prerenderOptions) && indexBaseName === 'index.html'
342-
? INDEX_HTML_CSR
343-
: indexBaseName;
330+
indexOutput = options.index;
344331
} else {
345332
indexOutput = options.index.output || 'index.html';
346333
}
347334

335+
/**
336+
* If SSR is activated, create a distinct entry file for the `index.html`.
337+
* This is necessary because numerous server/cloud providers automatically serve the `index.html` as a static file
338+
* if it exists (handling SSG).
339+
*
340+
* For instance, accessing `foo.com/` would lead to `foo.com/index.html` being served instead of hitting the server.
341+
*
342+
* This approach can also be applied to service workers, where the `index.csr.html` is served instead of the prerendered `index.html`.
343+
*/
344+
const indexBaseName = path.basename(indexOutput);
345+
indexOutput =
346+
(ssrOptions || prerenderOptions) && indexBaseName === 'index.html'
347+
? INDEX_HTML_CSR
348+
: indexBaseName;
349+
348350
indexHtmlOptions = {
349351
input: path.join(
350352
workspaceRoot,

packages/angular/build/src/builders/application/tests/options/index_spec.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,21 @@
77
*/
88

99
import { buildApplication } from '../../index';
10+
import { OutputMode } from '../../schema';
1011
import { APPLICATION_BUILDER_INFO, BASE_OPTIONS, describeBuilder } from '../setup';
1112

1213
describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => {
1314
describe('Option: "index"', () => {
1415
beforeEach(async () => {
15-
// Application code is not needed for index tests
16+
await harness.modifyFile('src/tsconfig.app.json', (content) => {
17+
const tsConfig = JSON.parse(content);
18+
tsConfig.files ??= [];
19+
tsConfig.files.push('main.server.ts', 'server.ts');
20+
21+
return JSON.stringify(tsConfig);
22+
});
23+
24+
await harness.writeFile('src/server.ts', `console.log('Hello!');`);
1625
await harness.writeFile('src/main.ts', 'console.log("TEST");');
1726
});
1827

@@ -227,5 +236,27 @@ describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => {
227236
harness.expectFile('dist/browser/index.csr.html').toExist();
228237
harness.expectFile('dist/browser/index.html').toNotExist();
229238
});
239+
240+
it(`should generate 'index.csr.html' instead of 'index.html' when outputMode is 'Server' when 'output' is 'index.html'`, async () => {
241+
harness.useTarget('build', {
242+
...BASE_OPTIONS,
243+
outputMode: OutputMode.Server,
244+
index: {
245+
input: 'src/index.html',
246+
output: 'index.html',
247+
},
248+
server: 'src/main.server.ts',
249+
ssr: { entry: 'src/server.ts' },
250+
});
251+
252+
const { result } = await harness.executeOnce();
253+
expect(result?.success).toBeTrue();
254+
255+
harness.expectFile('dist/browser/index.csr.html').toExist();
256+
257+
const manifestFilePath = 'dist/server/angular-app-manifest.mjs';
258+
harness.expectFile(manifestFilePath).content.not.toContain('index.csr.html');
259+
harness.expectFile(manifestFilePath).content.toContain('index.html');
260+
});
230261
});
231262
});

0 commit comments

Comments
 (0)