Skip to content

Commit

Permalink
fix(@angular-devkit/build-angular): downlevel class fields with Safar…
Browse files Browse the repository at this point in the history
…i <= v15 for esbuild

To provide a workaround for a Safari bug involving class fields and variable scoping,
the esbuild-based browser application builder will now downlevel class fields if Safari
(desktop or iOS) v15.x or earlier is within the target browsers for an application.
This is an esbuild variant of the fix for the Webpack-based builder. For more details
regarding the issue, please see: #24357

(cherry picked from commit cf2f30a)
  • Loading branch information
clydin authored and alan-agius4 committed Dec 2, 2022
1 parent 20e0742 commit 24f4b51
Showing 1 changed file with 52 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -253,19 +253,7 @@ function createCodeBundleOptions(
entryNames: outputNames.bundles,
assetNames: outputNames.media,
target,
supported: {
// Native async/await is not supported with Zone.js. Disabling support here will cause
// esbuild to downlevel async/await and for await...of to a Zone.js supported form. However, esbuild
// does not currently support downleveling async generators. Instead babel is used within the JS/TS
// loader to perform the downlevel transformation.
// NOTE: If esbuild adds support in the future, the babel support for async generators can be disabled.
'async-await': false,
// V8 currently has a performance defect involving object spread operations that can cause signficant
// degradation in runtime performance. By not supporting the language feature here, a downlevel form
// will be used instead which provides a workaround for the performance issue.
// For more details: https://bugs.chromium.org/p/v8/issues/detail?id=11536
'object-rest-spread': false,
},
supported: getFeatureSupport(target),
mainFields: ['es2020', 'browser', 'module', 'main'],
conditions: ['es2020', 'es2015', 'module'],
resolveExtensions: ['.ts', '.tsx', '.mjs', '.js'],
Expand Down Expand Up @@ -314,6 +302,57 @@ function createCodeBundleOptions(
};
}

/**
* Generates a syntax feature object map for Angular applications based on a list of targets.
* A full set of feature names can be found here: https://esbuild.github.io/api/#supported
* @param target An array of browser/engine targets in the format accepted by the esbuild `target` option.
* @returns An object that can be used with the esbuild build `supported` option.
*/
function getFeatureSupport(target: string[]): BuildOptions['supported'] {
const supported: Record<string, boolean> = {
// Native async/await is not supported with Zone.js. Disabling support here will cause
// esbuild to downlevel async/await and for await...of to a Zone.js supported form. However, esbuild
// does not currently support downleveling async generators. Instead babel is used within the JS/TS
// loader to perform the downlevel transformation.
// NOTE: If esbuild adds support in the future, the babel support for async generators can be disabled.
'async-await': false,
// V8 currently has a performance defect involving object spread operations that can cause signficant
// degradation in runtime performance. By not supporting the language feature here, a downlevel form
// will be used instead which provides a workaround for the performance issue.
// For more details: https://bugs.chromium.org/p/v8/issues/detail?id=11536
'object-rest-spread': false,
};

// Detect Safari browser versions that have a class field behavior bug
// See: https://github.com/angular/angular-cli/issues/24355#issuecomment-1333477033
// See: https://github.com/WebKit/WebKit/commit/e8788a34b3d5f5b4edd7ff6450b80936bff396f2
let safariClassFieldScopeBug = false;
for (const browser of target) {
let majorVersion;
if (browser.startsWith('ios')) {
majorVersion = Number(browser.slice(3, 5));
} else if (browser.startsWith('safari')) {
majorVersion = Number(browser.slice(6, 8));
} else {
continue;
}
// Technically, 14.0 is not broken but rather does not have support. However, the behavior
// is identical since it would be set to false by esbuild if present as a target.
if (majorVersion === 14 || majorVersion === 15) {
safariClassFieldScopeBug = true;
break;
}
}
// If class field support cannot be used set to false; otherwise leave undefined to allow
// esbuild to use `target` to determine support.
if (safariClassFieldScopeBug) {
supported['class-field'] = false;
supported['class-static-field'] = false;
}

return supported;
}

function createGlobalStylesBundleOptions(
options: NormalizedBrowserOptions,
target: string[],
Expand Down

0 comments on commit 24f4b51

Please sign in to comment.