Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(@angular-devkit/build-angular): display warning when preserveWhitespaces in set in the tsconfig provided to the server builder #25038

Merged
merged 1 commit into from Apr 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -8,7 +8,8 @@

import { BuilderContext, BuilderOutput, createBuilder } from '@angular-devkit/architect';
import { runWebpack } from '@angular-devkit/build-webpack';
import * as path from 'path';
import { readFile } from 'node:fs/promises';
import * as path from 'node:path';
import { Observable, concatMap, from } from 'rxjs';
import webpack, { Configuration } from 'webpack';
import { ExecutionTransformer } from '../../transforms';
Expand Down Expand Up @@ -191,6 +192,8 @@ async function initialize(
// Purge old build disk cache.
await purgeStaleBuildCache(context);

await checkTsConfigForPreserveWhitespacesSetting(context, options.tsConfig);

const browserslist = (await import('browserslist')).default;
const originalOutputPath = options.outputPath;
// Assets are processed directly by the builder except when watching
Expand Down Expand Up @@ -223,6 +226,32 @@ async function initialize(
return { config: transformedConfig, i18n, projectRoot, projectSourceRoot };
}

async function checkTsConfigForPreserveWhitespacesSetting(
context: BuilderContext,
tsConfigPath: string,
): Promise<void> {
// We don't use the `readTsConfig` method on purpose here.
// To only catch cases were `preserveWhitespaces` is set directly in the `tsconfig.server.json`,
// which in the majority of cases will cause a mistmatch between client and server builds.
// Technically we should check if `tsconfig.server.json` and `tsconfig.app.json` values match.

// But:
// 1. It is not guaranteed that `tsconfig.app.json` is used to build the client side of this app.
// 2. There is no easy way to access the build build config from the server builder.
// 4. This will no longer be an issue with a single compilation model were the same tsconfig is used for both browser and server builds.
const content = await readFile(path.join(context.workspaceRoot, tsConfigPath), 'utf-8');
const { parse } = await import('jsonc-parser');
const tsConfig = parse(content, [], { allowTrailingComma: true });
if (tsConfig.angularCompilerOptions?.preserveWhitespaces !== undefined) {
context.logger.warn(
`"preserveWhitespaces" was set in "${tsConfigPath}". ` +
'Make sure that this setting is set consistently in both "tsconfig.server.json" for your server side ' +
'and "tsconfig.app.json" for your client side. A mismatched value will cause hydration to break.\n' +
'For more information see: https://angular.io/guide/hydration#preserve-whitespaces',
);
}
}

/**
* Add `@angular/platform-server` exports.
* This is needed so that DI tokens can be referenced and set at runtime outside of the bundle.
Expand Down
@@ -0,0 +1,47 @@
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/

import { logging } from '@angular-devkit/core';
import { execute } from '../../index';
import { BASE_OPTIONS, SERVER_BUILDER_INFO, describeBuilder } from '../setup';

describeBuilder(execute, SERVER_BUILDER_INFO, (harness) => {
describe('Behavior: "preserveWhitespaces warning"', () => {
it('should not show warning when "preserveWhitespaces" is not set.', async () => {
harness.useTarget('server', {
...BASE_OPTIONS,
});

const { logs } = await harness.executeOnce();
expect(logs).not.toContain(
jasmine.objectContaining<logging.LogEntry>({
message: jasmine.stringMatching('"preserveWhitespaces" was set in'),
}),
);
});
it('should show warning when "preserveWhitespaces" is set.', async () => {
harness.useTarget('server', {
...BASE_OPTIONS,
});

await harness.modifyFile('src/tsconfig.server.json', (content) => {
const tsconfig = JSON.parse(content);
(tsconfig.angularCompilerOptions ??= {}).preserveWhitespaces = false;

return JSON.stringify(tsconfig);
});

const { logs } = await harness.executeOnce();
expect(logs).toContain(
jasmine.objectContaining<logging.LogEntry>({
message: jasmine.stringMatching('"preserveWhitespaces" was set in'),
}),
);
});
});
});