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(vite-plugin-angular): use JavaScript transformer from Angular Devkit for build optimizations #1302

Merged
merged 2 commits into from
Aug 28, 2024
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
1 change: 1 addition & 0 deletions apps/ng-app/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export default defineConfig(({ mode }) => ({
plugins: [
analog({
ssr: false,
static: true,
vite: {
experimental: {
supportAnalogFormat: true,
Expand Down
106 changes: 29 additions & 77 deletions packages/vite-plugin-angular/src/lib/angular-build-optimizer-plugin.ts
Original file line number Diff line number Diff line change
@@ -1,68 +1,44 @@
import { Plugin } from 'vite';
import { transformAsync } from '@babel/core';
import { createEs2015LinkerPlugin as linkerPluginCreator } from '@angular/compiler-cli/linker/babel';

import { angularApplicationPreset, requiresLinking } from './utils/devkit.js';
import { JavaScriptTransformer } from './utils/devkit.js';

export function buildOptimizerPlugin({
isProd,
supportedBrowsers,
}: {
isProd: boolean;
supportedBrowsers: string[];
}): Plugin {
const javascriptTransformer = new JavaScriptTransformer(
{
sourcemap: false,
thirdPartySourcemaps: false,
advancedOptimizations: true,
jit: true,
},
1
);

return {
name: '@analogjs/vite-plugin-angular-optimizer',
apply: 'build',
config(userConfig) {
config() {
return {
esbuild: !userConfig.esbuild
? {
legalComments: 'none',
keepNames: false,
define: isProd
? {
ngDevMode: 'false',
ngJitMode: 'false',
ngI18nClosureMode: 'false',
}
: undefined,
supported: {
// Native async/await is not supported with Zone.js. Disabling support here will cause
// esbuild to downlevel async/await to a Zone.js supported form.
'async-await': false,
// Zone.js also does not support async generators or async iterators. However, esbuild does
// not currently support downleveling either of them. Instead babel is used within the JS/TS
// loader to perform the downlevel transformation. They are both disabled here to allow
// esbuild to handle them in the future if support is ever added.
// NOTE: If esbuild adds support in the future, the babel support for these can be disabled.
'async-generator': false,
'for-await': false,
'class-field': false,
'class-static-field': false,
},
}
: {
define: isProd
? {
ngDevMode: 'false',
ngJitMode: 'false',
ngI18nClosureMode: 'false',
}
: undefined,
},
esbuild: {
define: isProd
? {
ngDevMode: 'false',
ngJitMode: 'false',
ngI18nClosureMode: 'false',
}
: undefined,
},
};
},
async transform(code, id) {
if (/\.[cm]?js$/.test(id)) {
const angularPackage = /[\\/]node_modules[\\/]@angular[\\/]/.test(id);
const forceAsyncTransformation =
!/[\\/][_f]?esm2015[\\/]/.test(id) &&
/for\s+await\s*\(|async\s+function\s*\*/.test(code);
const shouldLink = await requiresLinking(id, code);
const useInputSourcemap = (!isProd ? undefined : false) as undefined;
const angularPackage = /fesm20/.test(id);

if (!forceAsyncTransformation && !isProd && !shouldLink) {
if (!angularPackage) {
return {
code: isProd
? code.replace(/^\/\/# sourceMappingURL=[^\r\n]*/gm, '')
Expand All @@ -73,38 +49,14 @@ export function buildOptimizerPlugin({
};
}

const result = await transformAsync(code, {
filename: id,
inputSourceMap: useInputSourcemap,
sourceMaps: !isProd ? 'inline' : false,
compact: false,
configFile: false,
babelrc: false,
browserslistConfigFile: false,
plugins: [],
presets: [
[
angularApplicationPreset,
{
angularLinker: {
shouldLink,
jitMode: false,
linkerPluginCreator,
},
forceAsyncTransformation,
supportedBrowsers,
optimize: isProd && {
looseEnums: angularPackage,
pureTopLevel: angularPackage,
},
},
],
],
});
const result: Uint8Array = await javascriptTransformer.transformData(
id,
code,
false
);

return {
code: result?.code || '',
map: result?.map as any,
code: Buffer.from(result).toString(),
};
}

Expand Down
40 changes: 2 additions & 38 deletions packages/vite-plugin-angular/src/lib/angular-vite-plugin.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { CompilerHost, NgtscProgram } from '@angular/compiler-cli';
import { transformAsync } from '@babel/core';
import { resolve } from 'node:path';

import * as compilerCli from '@angular/compiler-cli';
Expand All @@ -25,7 +24,6 @@ import { jitPlugin } from './angular-jit-plugin.js';
import { buildOptimizerPlugin } from './angular-build-optimizer-plugin.js';

import {
angularApplicationPreset,
createJitResourceTransformer,
SourceFileCache,
} from './utils/devkit.js';
Expand Down Expand Up @@ -377,10 +375,6 @@ export function angular(options?: PluginOptions): Plugin[] {
};
}

const forceAsyncTransformation =
/for\s+await\s*\(|async\s+function\s*\*/.test(data);
const useInputSourcemap = (!isProd ? undefined : false) as undefined;

if (
(id.endsWith('.analog') || id.endsWith('.agx')) &&
pluginOptions.supportAnalogFormat &&
Expand All @@ -396,39 +390,9 @@ export function angular(options?: PluginOptions): Plugin[] {
}
}

if (!forceAsyncTransformation && !isProd) {
return {
code: data,
map: null,
};
}

const babelResult = await transformAsync(data, {
filename: id,
inputSourceMap: (useInputSourcemap
? undefined
: false) as undefined,
sourceMaps: !isProd ? 'inline' : false,
compact: false,
configFile: false,
babelrc: false,
browserslistConfigFile: false,
plugins: [],
presets: [
[
angularApplicationPreset,
{
supportedBrowsers: pluginOptions.supportedBrowsers,
forceAsyncTransformation,
optimize: isProd && {},
},
],
],
});

return {
code: babelResult?.code ?? '',
map: babelResult?.map,
code: data,
map: null,
};
}

Expand Down
22 changes: 0 additions & 22 deletions packages/vite-plugin-angular/src/lib/utils/devkit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,13 @@ import * as sfc from './source-file-cache.js';
const require = createRequire(import.meta.url);

const angularVersion = Number(VERSION.major);
let requiresLinking: Function;
let sourceFileCache: any;
let cjt: Function;
let jt: any;
let angularApplicationPreset: Function;

if (angularVersion < 15) {
throw new Error('AnalogJS is not compatible with Angular v14 and lower');
} else if (angularVersion >= 15 && angularVersion < 16) {
const app = require('@angular-devkit/build-angular/src/babel/presets/application.js');
const wbl = require('@angular-devkit/build-angular/src/babel/webpack-loader.js');
const cp = require('@angular-devkit/build-angular/src/builders/browser-esbuild/compiler-plugin.js');
const {
createJitResourceTransformer,
Expand All @@ -25,14 +21,10 @@ if (angularVersion < 15) {
JavaScriptTransformer,
} = require('@angular-devkit/build-angular/src/builders/browser-esbuild/javascript-transformer.js');

requiresLinking = wbl.requiresLinking;
sourceFileCache = cp.SourceFileCache;
cjt = createJitResourceTransformer;
jt = JavaScriptTransformer;
angularApplicationPreset = app.default;
} else if (angularVersion >= 16 && angularVersion < 18) {
const app = require('@angular-devkit/build-angular/src/tools/babel/presets/application.js');
const wbl = require('@angular-devkit/build-angular/src/tools/babel/webpack-loader.js');
const cp = require('@angular-devkit/build-angular/src/tools/esbuild/angular/compiler-plugin.js');
const {
createJitResourceTransformer,
Expand All @@ -41,14 +33,6 @@ if (angularVersion < 15) {
JavaScriptTransformer,
} = require('@angular-devkit/build-angular/src/tools/esbuild/javascript-transformer.js');

/**
* Workaround for compatibility with Angular 16.2+
*/
if (typeof wbl['requiresLinking'] !== 'undefined') {
requiresLinking = wbl.requiresLinking;
} else if (typeof app['requiresLinking'] !== 'undefined') {
requiresLinking = app['requiresLinking'];
}
/**
* Workaround for compatibility with Angular 17.0+
*/
Expand All @@ -60,25 +44,19 @@ if (angularVersion < 15) {

cjt = createJitResourceTransformer;
jt = JavaScriptTransformer;
angularApplicationPreset = app.default;
} else {
const {
createJitResourceTransformer,
JavaScriptTransformer,
SourceFileCache,
} = require('@angular/build/private');
const app = require('@angular-devkit/build-angular/src/tools/babel/presets/application.js');

requiresLinking = app.requiresLinking;
sourceFileCache = SourceFileCache;
cjt = createJitResourceTransformer;
jt = JavaScriptTransformer;
angularApplicationPreset = app.default;
}

export {
requiresLinking,
angularApplicationPreset,
cjt as createJitResourceTransformer,
jt as JavaScriptTransformer,
sourceFileCache as SourceFileCache,
Expand Down
Loading