Skip to content

Commit 2c114d9

Browse files
just-jebMatt Lewis
andcommitted
feat(custom-esbuild): expose builder options to plugins
Enable plugins to access builder options and target information through factory functions. This is a non-breaking enhancement - existing plugins continue to work unchanged. Plugins can now be defined as factory functions that receive builderOptions and target parameters, enabling more sophisticated plugins that can adapt their behavior based on the current build target and configuration. Co-authored-by: Matt Lewis <[email protected]>
1 parent 44a69ac commit 2c114d9

File tree

4 files changed

+89
-26
lines changed

4 files changed

+89
-26
lines changed

packages/custom-esbuild/src/application/index.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import * as path from 'node:path';
22
import { BuilderContext, createBuilder } from '@angular-devkit/architect';
3-
import { buildApplication } from '@angular-devkit/build-angular';
3+
import { buildApplication } from '@angular/build';
44
import { getSystemPath, json, normalize } from '@angular-devkit/core';
5-
import type { ApplicationBuilderExtensions } from '@angular/build/src/builders/application/options';
65
import { defer, switchMap } from 'rxjs';
76

87
import { loadPlugins } from '../load-plugins';
@@ -17,7 +16,14 @@ export function buildCustomEsbuildApplication(
1716
const tsConfig = path.join(workspaceRoot, options.tsConfig);
1817

1918
return defer(async () => {
20-
const codePlugins = await loadPlugins(options.plugins, workspaceRoot, tsConfig, context.logger);
19+
const codePlugins = await loadPlugins(
20+
options.plugins,
21+
workspaceRoot,
22+
tsConfig,
23+
context.logger,
24+
options,
25+
context.target
26+
);
2127

2228
const indexHtmlTransformer = options.indexHtmlTransformer
2329
? await loadIndexHtmlTransformer(
@@ -28,7 +34,7 @@ export function buildCustomEsbuildApplication(
2834
)
2935
: undefined;
3036

31-
return { codePlugins, indexHtmlTransformer } as ApplicationBuilderExtensions;
37+
return { codePlugins, indexHtmlTransformer };
3238
}).pipe(switchMap(extensions => buildApplication(options, context, extensions)));
3339
}
3440

packages/custom-esbuild/src/dev-server/index.ts

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
DevServerBuilderOptions,
55
DevServerBuilderOutput,
66
executeDevServerBuilder,
7-
} from '@angular-devkit/build-angular';
7+
} from '@angular/build';
88
import { getSystemPath, json, normalize } from '@angular-devkit/core';
99
import { Observable, from, switchMap } from 'rxjs';
1010
import { loadModule } from '@angular-builders/common';
@@ -55,7 +55,9 @@ export function executeCustomDevServerBuilder(
5555
buildOptions.plugins,
5656
workspaceRoot,
5757
tsConfig,
58-
context.logger
58+
context.logger,
59+
options,
60+
context.target
5961
);
6062

6163
const indexHtmlTransformer = buildOptions.indexHtmlTransformer
@@ -69,13 +71,10 @@ export function executeCustomDevServerBuilder(
6971

7072
patchBuilderContext(context, buildTarget);
7173

72-
return {
73-
transforms: { indexHtml: indexHtmlTransformer },
74-
extensions: { middleware, buildPlugins },
75-
};
74+
return { middleware, buildPlugins, indexHtmlTransformer };
7675
}),
77-
switchMap(({ transforms, extensions }) =>
78-
executeDevServerBuilder(options, context, transforms, extensions)
76+
switchMap((extensions) =>
77+
executeDevServerBuilder(options, context, extensions)
7978
)
8079
);
8180
}
Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import { loadPlugins } from './load-plugins';
2+
import { Target } from '@angular-devkit/architect';
3+
import { Plugin } from 'esbuild';
4+
import { CustomEsbuildApplicationSchema } from './custom-esbuild-schema';
25

36
describe('loadPlugin', () => {
47
beforeEach(() => {
@@ -7,20 +10,54 @@ describe('loadPlugin', () => {
710
});
811

912
it('should load a plugin without configuration', async () => {
10-
const pluginFactory = jest.fn();
13+
const mockPlugin = { name: 'mock' } as Plugin;
14+
jest.mock('test/test-plugin.js', () => mockPlugin, { virtual: true });
15+
const plugin = await loadPlugins(
16+
['test-plugin.js'],
17+
'./test',
18+
'./tsconfig.json',
19+
null as any,
20+
{} as any,
21+
{} as any
22+
);
23+
24+
expect(plugin).toEqual([mockPlugin]);
25+
});
26+
27+
it('should load a plugin factory without configuration and pass options and target', async () => {
28+
const mockPlugin = { name: 'mock' } as Plugin;
29+
const pluginFactory = jest.fn().mockReturnValue(mockPlugin);
30+
const mockOptions = { tsConfig: './tsconfig.json' } as CustomEsbuildApplicationSchema;
31+
const mockTarget = { target: 'test' } as Target;
1132
jest.mock('test/test-plugin.js', () => pluginFactory, { virtual: true });
12-
const plugin = await loadPlugins(['test-plugin.js'], './test', './tsconfig.json', null as any);
33+
const plugin = await loadPlugins(
34+
['test-plugin.js'],
35+
'./test',
36+
'./tsconfig.json',
37+
null as any,
38+
mockOptions,
39+
mockTarget
40+
);
1341

14-
expect(pluginFactory).not.toHaveBeenCalled();
15-
expect(plugin).toBeDefined();
42+
expect(pluginFactory).toHaveBeenCalledWith(mockOptions, mockTarget);
43+
expect(plugin).toEqual([mockPlugin]);
1644
});
1745

1846
it('should load a plugin with configuration', async () => {
1947
const pluginFactory = jest.fn();
48+
const mockOptions = { tsConfig: './tsconfig.json' } as CustomEsbuildApplicationSchema;
49+
const mockTarget = { target: 'test' } as Target;
2050
jest.mock('test/test-plugin.js', () => pluginFactory, { virtual: true });
21-
const plugin = await loadPlugins([{ path: 'test-plugin.js', options: { test: 'test' } }], './test', './tsconfig.json', null as any);
51+
const plugin = await loadPlugins(
52+
[{ path: 'test-plugin.js', options: { test: 'test' } }],
53+
'./test',
54+
'./tsconfig.json',
55+
null as any,
56+
mockOptions,
57+
mockTarget
58+
);
2259

23-
expect(pluginFactory).toHaveBeenCalledWith({ test: 'test' });
60+
expect(pluginFactory).toHaveBeenCalledWith({ test: 'test' }, mockOptions, mockTarget);
2461
expect(plugin).toBeDefined();
2562
});
2663
});

packages/custom-esbuild/src/load-plugins.ts

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,46 @@ import * as path from 'node:path';
22
import type { Plugin } from 'esbuild';
33
import type { logging } from '@angular-devkit/core';
44
import { loadModule } from '@angular-builders/common';
5-
import { PluginConfig } from './custom-esbuild-schema';
5+
import {
6+
CustomEsbuildApplicationSchema,
7+
CustomEsbuildDevServerSchema,
8+
PluginConfig,
9+
} from './custom-esbuild-schema';
10+
import { Target } from '@angular-devkit/architect';
611

712
export async function loadPlugins(
813
pluginConfig: PluginConfig[] | undefined,
914
workspaceRoot: string,
1015
tsConfig: string,
1116
logger: logging.LoggerApi,
17+
builderOptions: CustomEsbuildApplicationSchema | CustomEsbuildDevServerSchema,
18+
target: Target
1219
): Promise<Plugin[]> {
1320
const plugins = await Promise.all(
1421
(pluginConfig || []).map(async pluginConfig => {
15-
if (typeof pluginConfig === 'string') {
16-
return loadModule<Plugin | Plugin[]>(path.join(workspaceRoot, pluginConfig), tsConfig, logger);
22+
if (typeof pluginConfig === 'string') {
23+
const pluginsOrFactory = await loadModule<
24+
| Plugin
25+
| Plugin[]
26+
| ((
27+
options: CustomEsbuildApplicationSchema | CustomEsbuildDevServerSchema,
28+
target: Target
29+
) => Plugin | Plugin[])
30+
>(path.join(workspaceRoot, pluginConfig), tsConfig, logger);
31+
if (typeof pluginsOrFactory === 'function') {
32+
return pluginsOrFactory(builderOptions, target);
1733
} else {
18-
const pluginFactory = await loadModule<(...args: any[]) => Plugin>(path.join(workspaceRoot, pluginConfig.path), tsConfig, logger);
19-
return pluginFactory(pluginConfig.options);
34+
return pluginsOrFactory;
2035
}
21-
22-
},
23-
),
36+
} else {
37+
const pluginFactory = await loadModule<(...args: any[]) => Plugin>(
38+
path.join(workspaceRoot, pluginConfig.path),
39+
tsConfig,
40+
logger
41+
);
42+
return pluginFactory(pluginConfig.options, builderOptions, target);
43+
}
44+
})
2445
);
2546

2647
return plugins.flat();

0 commit comments

Comments
 (0)