Skip to content

fix(@angular-devkit/build-angular): downlevel class private methods when targeting Safari <=v15 #24413

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

Merged
merged 1 commit into from
Dec 12, 2022
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,10 @@ export default function (api: unknown, options: ApplicationPresetOptions) {
// downlevel class properties by ensuring the class properties Babel plugin
// is always included- regardless of the preset-env targets.
if (options.supportedBrowsers.some((b) => safariClassFieldScopeBugBrowsers.has(b))) {
includePlugins.push('@babel/plugin-proposal-class-properties');
includePlugins.push(
'@babel/plugin-proposal-class-properties',
'@babel/plugin-proposal-private-methods',
);
}

presets.push([
Expand Down
64 changes: 39 additions & 25 deletions tests/legacy-cli/e2e/tests/misc/safari-15-class-properties.ts
Original file line number Diff line number Diff line change
@@ -1,56 +1,70 @@
import { expectFileToExist, readFile, writeFile } from '../../utils/fs';
import assert from 'node:assert';
import { expectFileToExist, readFile, writeFile, replaceInFile } from '../../utils/fs';
import { ng } from '../../utils/process';
import { updateJsonFile } from '../../utils/project';

const unexpectedStaticFieldErrorMessage =
'Found unexpected static field. This indicates that the Safari <=v15 ' +
'workaround for a scope variable tracking is not working. ' +
'See: https://github.com/angular/angular-cli/pull/24357';

export default async function () {
await updateJsonFile('angular.json', (workspace) => {
const build = workspace.projects['test-project'].architect.build;
build.defaultConfiguration = undefined;
build.options = {
...build.options,
optimization: false,
outputHashing: 'none',
};
});
// Add a private method
await replaceInFile(
'src/app/app.component.ts',
`title = 'test-project';`,
`
#myPrivateMethod() { return 1 }

constructor() {
console.log(this.#myPrivateMethod)
}

title = 'test-project';`,
);

// Matches two types of static fields that indicate that the Safari bug
// may still occur. With the workaround this should not appear in bundles.
// - static { this.ecmp = bla }
// - static #_ = this.ecmp = bla
const staticIndicatorRegex = /static\s+(\{|#[_\d]+\s+=)/;

await ng('build');
await ng('build', '--configuration=development');
await expectFileToExist('dist/test-project/main.js');
const mainContent = await readFile('dist/test-project/main.js');

// TODO: This default cause can be removed in the future when Safari v15
// is longer included in the default browserlist configuration of CLI apps.
if (staticIndicatorRegex.test(mainContent)) {
throw new Error(unexpectedStaticFieldErrorMessage);
}
assert.doesNotMatch(mainContent, staticIndicatorRegex, unexpectedStaticFieldErrorMessage);

await writeFile('.browserslistrc', 'last 1 chrome version');

await ng('build');
await ng('build', '--configuration=development');
await expectFileToExist('dist/test-project/main.js');
const mainContentChromeLatest = await readFile('dist/test-project/main.js');

if (!staticIndicatorRegex.test(mainContentChromeLatest)) {
throw new Error('Expected static fields to be used when Safari <=v15 is not targeted.');
}
assert.match(
mainContentChromeLatest,
staticIndicatorRegex,
'Expected static fields to be used when Safari <=v15 is not targeted.',
);
assert.match(
mainContentChromeLatest,
/#myPrivateMethod/,
'Expected private method to be used when Safari <=v15 is not targeted.',
);

await writeFile('.browserslistrc', 'Safari <=15');

await ng('build');
await ng('build', '--configuration=development');
await expectFileToExist('dist/test-project/main.js');
const mainContentSafari15Explicit = await readFile('dist/test-project/main.js');
assert.doesNotMatch(
mainContentSafari15Explicit,
staticIndicatorRegex,
unexpectedStaticFieldErrorMessage,
);

if (staticIndicatorRegex.test(mainContentSafari15Explicit)) {
throw new Error(unexpectedStaticFieldErrorMessage);
}
assert.match(
mainContentSafari15Explicit,
/var _myPrivateMethod/,
'Expected private method to be downlevelled when Safari <=v15 is targeted',
);
}