@angular-architects/native-federation-v4 21.1.4 → 21.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/README.md +7 -7
  2. package/migration-collection.json +1 -1
  3. package/package.json +16 -8
  4. package/src/builders/build/builder.d.ts +2 -1
  5. package/src/builders/build/builder.d.ts.map +1 -1
  6. package/src/builders/build/builder.js +117 -113
  7. package/src/builders/build/schema.d.ts +1 -0
  8. package/src/builders/build/schema.json +3 -0
  9. package/src/builders/build/setup-builder-env-variables.d.ts +2 -0
  10. package/src/builders/build/setup-builder-env-variables.d.ts.map +1 -0
  11. package/src/builders/build/setup-builder-env-variables.js +9 -0
  12. package/src/plugin/externals-skip-list.js +1 -1
  13. package/src/utils/angular-bundler.d.ts +12 -0
  14. package/src/utils/angular-bundler.d.ts.map +1 -0
  15. package/src/utils/angular-bundler.js +167 -0
  16. package/src/utils/angular-esbuild-adapter.d.ts +5 -5
  17. package/src/utils/angular-esbuild-adapter.d.ts.map +1 -1
  18. package/src/utils/angular-esbuild-adapter.js +81 -261
  19. package/src/utils/node-modules-bundler.d.ts +12 -0
  20. package/src/utils/node-modules-bundler.d.ts.map +1 -0
  21. package/src/utils/node-modules-bundler.js +113 -0
  22. package/src/utils/create-compiler-options.d.ts +0 -5
  23. package/src/utils/create-compiler-options.d.ts.map +0 -1
  24. package/src/utils/create-compiler-options.js +0 -42
  25. package/src/utils/event-source.d.ts +0 -10
  26. package/src/utils/event-source.d.ts.map +0 -1
  27. package/src/utils/event-source.js +0 -10
  28. package/src/utils/mem-resuts.d.ts +0 -29
  29. package/src/utils/mem-resuts.d.ts.map +0 -1
  30. package/src/utils/mem-resuts.js +0 -50
  31. package/src/utils/rebuild-events.d.ts +0 -8
  32. package/src/utils/rebuild-events.d.ts.map +0 -1
  33. package/src/utils/rebuild-events.js +0 -4
@@ -0,0 +1,167 @@
1
+ import * as esbuild from 'esbuild';
2
+ import * as path from 'path';
3
+ import * as fs from 'fs';
4
+ import { createRequire } from 'node:module';
5
+ import { isDeepStrictEqual } from 'node:util';
6
+ import JSON5 from 'json5';
7
+ import { transformSupportedBrowsersToTargets, getSupportedBrowsers, generateSearchDirectories, findTailwindConfiguration, loadPostcssConfiguration, } from '@angular/build/private';
8
+ import { normalizeOptimization, normalizeSourceMaps, } from '@angular-devkit/build-angular/src/utils/index.js';
9
+ import { createSharedMappingsPlugin } from './shared-mappings-plugin.js';
10
+ import { createAwaitableCompilerPlugin } from './create-awaitable-compiler-plugin.js';
11
+ export async function createAngularEsbuildContext(builderOptions, context, entryPoints, external, outdir, tsConfigPath, mappedPaths, cache, dev, hash = false, chunks, platform, optimizedMappings) {
12
+ const workspaceRoot = context.workspaceRoot;
13
+ const projectMetadata = await context.getProjectMetadata(context.target.project);
14
+ const projectRoot = path.join(workspaceRoot, projectMetadata['root'] ?? '');
15
+ const browsers = getSupportedBrowsers(projectRoot, context.logger);
16
+ const target = transformSupportedBrowsersToTargets(browsers);
17
+ const optimizationOptions = normalizeOptimization(builderOptions.optimization);
18
+ const sourcemapOptions = normalizeSourceMaps(builderOptions.sourceMap);
19
+ const searchDirectories = await generateSearchDirectories([projectRoot, workspaceRoot]);
20
+ const postcssConfiguration = await loadPostcssConfiguration(searchDirectories);
21
+ const tailwindConfiguration = postcssConfiguration
22
+ ? undefined
23
+ : await getTailwindConfig(searchDirectories);
24
+ const outputNames = {
25
+ bundles: '[name]',
26
+ media: 'media/[name]',
27
+ };
28
+ let fileReplacements;
29
+ if (builderOptions.fileReplacements) {
30
+ for (const replacement of builderOptions.fileReplacements) {
31
+ fileReplacements ??= {};
32
+ fileReplacements[path.join(workspaceRoot, replacement.replace)] = path.join(workspaceRoot, replacement.with);
33
+ }
34
+ }
35
+ tsConfigPath = createTsConfigForFederation(workspaceRoot, tsConfigPath, entryPoints, optimizedMappings);
36
+ const pluginOptions = {
37
+ sourcemap: !!sourcemapOptions.scripts && (sourcemapOptions.hidden ? 'external' : true),
38
+ thirdPartySourcemaps: sourcemapOptions.vendor,
39
+ tsconfig: tsConfigPath,
40
+ jit: false,
41
+ advancedOptimizations: !dev,
42
+ fileReplacements,
43
+ sourceFileCache: cache.bundlerCache,
44
+ loadResultCache: cache.bundlerCache.loadResultCache,
45
+ incremental: true,
46
+ includeTestMetadata: !optimizationOptions.scripts,
47
+ };
48
+ const stylesheetBundlerOptions = {
49
+ workspaceRoot,
50
+ inlineFonts: !!optimizationOptions.fonts.inline,
51
+ optimization: !!optimizationOptions.styles.minify,
52
+ sourcemap:
53
+ // Hidden component stylesheet sourcemaps are inaccessible which is effectively
54
+ // the same as being disabled. Disabling has the advantage of avoiding the overhead
55
+ // of sourcemap processing.
56
+ sourcemapOptions.styles && !sourcemapOptions.hidden ? 'linked' : false,
57
+ outputNames,
58
+ includePaths: builderOptions.stylePreprocessorOptions?.includePaths,
59
+ sass: builderOptions?.stylePreprocessorOptions?.sass,
60
+ externalDependencies: external,
61
+ target,
62
+ inlineStyleLanguage: builderOptions.inlineStyleLanguage ?? 'css',
63
+ preserveSymlinks: builderOptions.preserveSymlinks,
64
+ tailwindConfiguration,
65
+ postcssConfiguration,
66
+ cacheOptions: {
67
+ enabled: true,
68
+ basePath: cache.cachePath,
69
+ path: cache.cachePath,
70
+ },
71
+ };
72
+ const commonjsPluginModule = await import('@chialab/esbuild-plugin-commonjs');
73
+ const commonjsPlugin = commonjsPluginModule.default;
74
+ stylesheetBundlerOptions.externalDependencies = [];
75
+ const [compilerPlugin, pluginDisposed] = createAwaitableCompilerPlugin(pluginOptions, stylesheetBundlerOptions);
76
+ const config = {
77
+ entryPoints: entryPoints.map(ep => ({
78
+ in: ep.fileName,
79
+ out: path.parse(ep.outName).name,
80
+ })),
81
+ outdir,
82
+ entryNames: hash ? '[name]-[hash]' : '[name]',
83
+ write: false,
84
+ external,
85
+ logLevel: 'warning',
86
+ bundle: true,
87
+ sourcemap: sourcemapOptions.scripts,
88
+ minify: !dev,
89
+ supported: {
90
+ 'async-await': false,
91
+ 'object-rest-spread': false,
92
+ },
93
+ splitting: chunks,
94
+ platform: platform ?? 'browser',
95
+ format: 'esm',
96
+ target: target,
97
+ logLimit: 0,
98
+ plugins: [
99
+ compilerPlugin,
100
+ ...(mappedPaths && mappedPaths.length > 0 ? [createSharedMappingsPlugin(mappedPaths)] : []),
101
+ commonjsPlugin(),
102
+ ],
103
+ define: {
104
+ ...(!dev ? { ngDevMode: 'false' } : {}),
105
+ ngJitMode: 'false',
106
+ },
107
+ ...(builderOptions.loader ? { loader: builderOptions.loader } : {}),
108
+ resolveExtensions: ['.ts', '.tsx', '.mjs', '.js', '.cjs'],
109
+ };
110
+ const ctx = await esbuild.context(config);
111
+ return { ctx, pluginDisposed };
112
+ }
113
+ async function getTailwindConfig(searchDirectories) {
114
+ const tailwindConfigurationPath = findTailwindConfiguration(searchDirectories);
115
+ if (!tailwindConfigurationPath) {
116
+ return undefined;
117
+ }
118
+ return {
119
+ file: tailwindConfigurationPath,
120
+ package: createRequire(tailwindConfigurationPath).resolve('tailwindcss'),
121
+ };
122
+ }
123
+ /**
124
+ * Creates a tsconfig.federation.json that includes the federation entry points.
125
+ */
126
+ function createTsConfigForFederation(workspaceRoot, tsConfigPath, entryPoints, optimizedMappings) {
127
+ const fullTsConfigPath = path.join(workspaceRoot, tsConfigPath);
128
+ const tsconfigDir = path.dirname(fullTsConfigPath);
129
+ const tsconfigAsString = fs.readFileSync(fullTsConfigPath, 'utf-8');
130
+ const tsconfig = JSON5.parse(tsconfigAsString);
131
+ tsconfig.files = entryPoints
132
+ .filter(ep => ep.fileName.startsWith('.'))
133
+ .map(ep => path.relative(tsconfigDir, ep.fileName).replace(/\\\\/g, '/'));
134
+ if (optimizedMappings) {
135
+ const filtered = entryPoints
136
+ .filter(ep => !ep.fileName.startsWith('.'))
137
+ .map(ep => path.relative(tsconfigDir, ep.fileName).replace(/\\\\/g, '/'));
138
+ if (!tsconfig.include) {
139
+ tsconfig.include = [];
140
+ }
141
+ for (const ep of filtered) {
142
+ if (!tsconfig.include.includes(ep)) {
143
+ tsconfig.include.push(ep);
144
+ }
145
+ }
146
+ }
147
+ const content = JSON5.stringify(tsconfig, null, 2);
148
+ const tsconfigFedPath = path.join(tsconfigDir, 'tsconfig.federation.json');
149
+ if (!doesFileExistAndJsonEqual(tsconfigFedPath, content)) {
150
+ fs.writeFileSync(tsconfigFedPath, JSON.stringify(tsconfig, null, 2));
151
+ }
152
+ return tsconfigFedPath;
153
+ }
154
+ function doesFileExistAndJsonEqual(filePath, content) {
155
+ if (!fs.existsSync(filePath)) {
156
+ return false;
157
+ }
158
+ try {
159
+ const currentContent = fs.readFileSync(filePath, 'utf-8');
160
+ const currentJson = JSON5.parse(currentContent);
161
+ const newJson = JSON5.parse(content);
162
+ return isDeepStrictEqual(currentJson, newJson);
163
+ }
164
+ catch {
165
+ return false;
166
+ }
167
+ }
@@ -2,9 +2,9 @@ import { type NFBuildAdapter } from '@softarc/native-federation';
2
2
  import * as esbuild from 'esbuild';
3
3
  import type { BuilderContext } from '@angular-devkit/architect';
4
4
  import type { ApplicationBuilderOptions } from '@angular/build';
5
- import { type RebuildEvents } from './rebuild-events.js';
6
- export type MemResultHandler = (outfiles: esbuild.OutputFile[], outdir?: string) => void;
7
- export declare function setMemResultHandler(handler: MemResultHandler): void;
8
- export declare function createAngularBuildAdapter(builderOptions: ApplicationBuilderOptions, context: BuilderContext, rebuildRequested?: RebuildEvents): NFBuildAdapter;
9
- export declare function loadEsmModule<T>(modulePath: string | URL): Promise<T>;
5
+ export interface EsbuildContextResult {
6
+ ctx: esbuild.BuildContext;
7
+ pluginDisposed: Promise<void>;
8
+ }
9
+ export declare function createAngularBuildAdapter(builderOptions: ApplicationBuilderOptions, context: BuilderContext): NFBuildAdapter;
10
10
  //# sourceMappingURL=angular-esbuild-adapter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"angular-esbuild-adapter.d.ts","sourceRoot":"","sources":["../../../src/utils/angular-esbuild-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,cAAc,EAIpB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,KAAK,OAAO,MAAM,SAAS,CAAC;AAWnC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAShE,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AAQhE,OAAO,EAAE,KAAK,aAAa,EAAe,MAAM,qBAAqB,CAAC;AAMtE,MAAM,MAAM,gBAAgB,GAAG,CAAC,QAAQ,EAAE,OAAO,CAAC,UAAU,EAAE,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;AAKzF,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAEnE;AAED,wBAAgB,yBAAyB,CACvC,cAAc,EAAE,yBAAyB,EACzC,OAAO,EAAE,cAAc,EACvB,gBAAgB,GAAE,aAAiC,GAClD,cAAc,CA2FhB;AAkSD,wBAAgB,aAAa,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC,CAAC,CAAC,CAErE"}
1
+ {"version":3,"file":"angular-esbuild-adapter.d.ts","sourceRoot":"","sources":["../../../src/utils/angular-esbuild-adapter.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,cAAc,EAKpB,MAAM,4BAA4B,CAAC;AAGpC,OAAO,KAAK,OAAO,MAAM,SAAS,CAAC;AAEnC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AAKhE,MAAM,WAAW,oBAAoB;IACnC,GAAG,EAAE,OAAO,CAAC,YAAY,CAAC;IAC1B,cAAc,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/B;AA+FD,wBAAgB,yBAAyB,CACvC,cAAc,EAAE,yBAAyB,EACzC,OAAO,EAAE,cAAc,GACtB,cAAc,CAqHhB"}
@@ -1,276 +1,31 @@
1
- import { logger, AbortedError } from '@softarc/native-federation/internal';
2
- import * as esbuild from 'esbuild';
3
- import { transformSupportedBrowsersToTargets, getSupportedBrowsers, generateSearchDirectories, findTailwindConfiguration, loadPostcssConfiguration, } from '@angular/build/private';
4
- import { createCompilerPluginOptions } from './create-compiler-options.js';
5
- import { normalizeOptimization, normalizeSourceMaps, } from '@angular-devkit/build-angular/src/utils/index.js';
6
- import { createRequire } from 'node:module';
7
1
  import * as fs from 'fs';
8
2
  import * as path from 'path';
9
- import { createSharedMappingsPlugin } from './shared-mappings-plugin.js';
10
- import { transformAsync } from '@babel/core';
11
- import { RebuildHubs } from './rebuild-events.js';
12
- import JSON5 from 'json5';
13
- import { isDeepStrictEqual } from 'node:util';
14
- import { createAwaitableCompilerPlugin } from './create-awaitable-compiler-plugin.js';
15
- let _memResultHandler;
16
- // Todo: figure out if still necessary
17
- export function setMemResultHandler(handler) {
18
- _memResultHandler = handler;
19
- }
20
- export function createAngularBuildAdapter(builderOptions, context, rebuildRequested = new RebuildHubs()) {
21
- return async (options) => {
22
- const { entryPoints, tsConfigPath, external, outdir, mappedPaths, kind, watch, dev, hash, chunks, platform, optimizedMappings, signal, } = options;
23
- setNgServerMode();
24
- const files = await runEsbuild(builderOptions, context, entryPoints, external, outdir, tsConfigPath, mappedPaths, watch, rebuildRequested, dev, kind, chunks, hash, undefined, undefined, undefined, platform, optimizedMappings, signal);
25
- if (kind === 'shared-package') {
26
- const scriptFiles = files.filter(f => f.endsWith('.js') || f.endsWith('.mjs'));
27
- for (const file of scriptFiles) {
28
- link(file, !!dev);
29
- }
30
- }
31
- return files.map(fileName => ({ fileName }));
32
- };
33
- async function link(outfile, dev) {
34
- const code = fs.readFileSync(outfile, 'utf-8');
35
- try {
36
- const linkerEsm = await loadEsmModule('@angular/compiler-cli/linker/babel');
37
- const linker = linkerEsm.default;
38
- const result = await transformAsync(code, {
39
- filename: outfile,
40
- compact: !dev,
41
- configFile: false,
42
- babelrc: false,
43
- minified: !dev,
44
- browserslistConfigFile: false,
45
- plugins: [linker],
46
- });
47
- if (!result)
48
- logger.warn(`File ${outfile} could not be linked.`);
49
- if (!result?.code) {
50
- logger.warn(`File ${outfile} seems to be empty.`);
51
- return;
52
- }
53
- fs.writeFileSync(outfile, result.code, 'utf-8');
54
- }
55
- catch (e) {
56
- logger.error('error linking');
57
- if (fs.existsSync(`${outfile}.error`)) {
58
- fs.unlinkSync(`${outfile}.error`);
59
- }
60
- fs.renameSync(outfile, `${outfile}.error`);
61
- throw e;
62
- }
63
- }
64
- }
65
- async function runEsbuild(builderOptions, context, entryPoints, external, outdir, tsConfigPath, mappedPaths, watch, rebuildRequested = new RebuildHubs(), dev, kind, chunks, hash = false, plugins = null, absWorkingDir = undefined, logLevel = 'warning', platform, optimizedMappings, signal) {
66
- if (signal?.aborted) {
67
- throw new AbortedError('[angular-esbuild-adapter] Before building');
68
- }
69
- const workspaceRoot = context.workspaceRoot;
70
- const projectMetadata = await context.getProjectMetadata(context.target.project);
71
- const projectRoot = path.join(workspaceRoot, projectMetadata['root'] ?? '');
72
- const browsers = getSupportedBrowsers(projectRoot, context.logger);
73
- const target = transformSupportedBrowsersToTargets(browsers);
74
- const optimizationOptions = normalizeOptimization(builderOptions.optimization);
75
- const sourcemapOptions = normalizeSourceMaps(builderOptions.sourceMap);
76
- const searchDirectories = await generateSearchDirectories([projectRoot, workspaceRoot]);
77
- const postcssConfiguration = await loadPostcssConfiguration(searchDirectories);
78
- const tailwindConfiguration = postcssConfiguration
79
- ? undefined
80
- : await getTailwindConfig(searchDirectories);
81
- const outputNames = {
82
- bundles: '[name]',
83
- media: 'media/[name]',
84
- };
85
- let fileReplacements;
86
- if (builderOptions.fileReplacements) {
87
- for (const replacement of builderOptions.fileReplacements) {
88
- fileReplacements ??= {};
89
- fileReplacements[path.join(workspaceRoot, replacement.replace)] = path.join(workspaceRoot, replacement.with);
90
- }
91
- }
92
- if (!optimizedMappings) {
93
- tsConfigPath = createTsConfigForFederation(workspaceRoot, tsConfigPath, entryPoints);
94
- }
95
- const pluginOptions = createCompilerPluginOptions({
96
- workspaceRoot,
97
- optimizationOptions,
98
- sourcemapOptions,
99
- tsconfig: tsConfigPath,
100
- outputNames,
101
- fileReplacements,
102
- externalDependencies: external,
103
- preserveSymlinks: builderOptions.preserveSymlinks,
104
- stylePreprocessorOptions: builderOptions.stylePreprocessorOptions,
105
- advancedOptimizations: !dev,
106
- inlineStyleLanguage: builderOptions.inlineStyleLanguage,
107
- jit: false,
108
- tailwindConfiguration,
109
- postcssConfiguration,
110
- }, target, undefined);
111
- const commonjsPluginModule = await import('@chialab/esbuild-plugin-commonjs');
112
- const commonjsPlugin = commonjsPluginModule.default;
113
- pluginOptions.styleOptions.externalDependencies = [];
114
- const [compilerPlugin, pluginDisposed] = createAwaitableCompilerPlugin(pluginOptions.pluginOptions, pluginOptions.styleOptions);
115
- const config = {
116
- entryPoints: entryPoints.map(ep => ({
117
- in: ep.fileName,
118
- out: path.parse(ep.outName).name,
119
- })),
120
- outdir,
121
- entryNames: hash ? '[name]-[hash]' : '[name]',
122
- write: false,
123
- absWorkingDir,
124
- external,
125
- logLevel,
126
- bundle: true,
127
- sourcemap: sourcemapOptions.scripts,
128
- minify: !dev,
129
- supported: {
130
- 'async-await': false,
131
- 'object-rest-spread': false,
132
- },
133
- splitting: chunks, //kind === 'mapping-or-exposed',
134
- platform: platform ?? 'browser',
135
- format: 'esm',
136
- target: target,
137
- logLimit: kind === 'shared-package' ? 1 : 0,
138
- plugins: plugins || [
139
- compilerPlugin,
140
- ...(mappedPaths && mappedPaths.length > 0 ? [createSharedMappingsPlugin(mappedPaths)] : []),
141
- commonjsPlugin(),
142
- ],
143
- define: {
144
- ...(!dev ? { ngDevMode: 'false' } : {}),
145
- ngJitMode: 'false',
146
- },
147
- ...(builderOptions.loader ? { loader: builderOptions.loader } : {}),
148
- resolveExtensions: ['.ts', '.tsx', '.mjs', '.js', '.cjs'],
149
- };
150
- const ctx = await esbuild.context(config);
151
- try {
152
- const abortHandler = async () => {
153
- await ctx.cancel();
154
- await ctx.dispose();
155
- await pluginDisposed;
156
- };
157
- if (signal) {
158
- signal.addEventListener('abort', abortHandler, { once: true });
159
- }
160
- const result = await ctx.rebuild();
161
- const memOnly = dev && kind === 'mapping-or-exposed' && !!_memResultHandler;
162
- const writtenFiles = writeResult(result, outdir, !!memOnly);
163
- if (watch) {
164
- // Also hardcoded disabled?
165
- registerForRebuilds(kind ?? 'shared-package', rebuildRequested, ctx, outdir, !!memOnly);
166
- }
167
- else {
168
- if (signal)
169
- signal.removeEventListener('abort', abortHandler);
170
- await ctx.dispose();
171
- await pluginDisposed;
172
- }
173
- return writtenFiles;
174
- }
175
- catch (error) {
176
- // ESBuild throws an error if the request is cancelled.
177
- // if it is, it's changed to an 'AbortedError'
178
- if (signal?.aborted && error instanceof Error && error.message.includes('canceled')) {
179
- throw new AbortedError('[runEsbuild] ESBuild was canceled.');
180
- }
181
- throw error;
182
- }
183
- }
184
- async function getTailwindConfig(searchDirectories) {
185
- const tailwindConfigurationPath = findTailwindConfiguration(searchDirectories);
186
- if (!tailwindConfigurationPath) {
187
- return undefined;
188
- }
189
- return {
190
- file: tailwindConfigurationPath,
191
- package: createRequire(tailwindConfigurationPath).resolve('tailwindcss'),
192
- };
193
- }
194
- function createTsConfigForFederation(workspaceRoot, tsConfigPath, entryPoints) {
195
- const fullTsConfigPath = path.join(workspaceRoot, tsConfigPath);
196
- const tsconfigDir = path.dirname(fullTsConfigPath);
197
- const filtered = entryPoints
198
- .filter(ep => !ep.fileName.includes('/node_modules/') && !ep.fileName.startsWith('.'))
199
- .map(ep => path.relative(tsconfigDir, ep.fileName).replace(/\\\\/g, '/'));
200
- const tsconfigAsString = fs.readFileSync(fullTsConfigPath, 'utf-8');
201
- const tsconfig = JSON5.parse(tsconfigAsString);
202
- if (!tsconfig.include) {
203
- tsconfig.include = [];
204
- }
205
- for (const ep of filtered) {
206
- if (!tsconfig.include.includes(ep)) {
207
- tsconfig.include.push(ep);
208
- }
209
- }
210
- const content = JSON5.stringify(tsconfig, null, 2);
211
- const tsconfigFedPath = path.join(tsconfigDir, 'tsconfig.federation.json');
212
- if (!doesFileExistAndJsonEqual(tsconfigFedPath, content)) {
213
- fs.writeFileSync(tsconfigFedPath, JSON.stringify(tsconfig, null, 2));
214
- }
215
- tsConfigPath = tsconfigFedPath;
216
- return tsConfigPath;
217
- }
218
- /**
219
- * Checks if a file exists and if its content is equal to the provided content.
220
- * If the file does not exist, it returns false.
221
- * If the file or its content is invalid JSON, it returns false.
222
- * @param {string} path - The path to the file
223
- * @param {string} content - The content to compare with
224
- * @returns {boolean} - Returns true if the file exists and its content is equal to the provided content
225
- */
226
- function doesFileExistAndJsonEqual(path, content) {
227
- if (!fs.existsSync(path)) {
228
- return false;
229
- }
230
- try {
231
- const currentContent = fs.readFileSync(path, 'utf-8');
232
- const currentJson = JSON5.parse(currentContent);
233
- const newJson = JSON5.parse(content);
234
- return isDeepStrictEqual(currentJson, newJson);
235
- }
236
- catch {
237
- return false;
3
+ import { AbortedError } from '@softarc/native-federation/internal';
4
+ import * as esbuild from 'esbuild';
5
+ import { createAngularEsbuildContext } from './angular-bundler.js';
6
+ import { createNodeModulesEsbuildContext } from './node-modules-bundler.js';
7
+ async function createEsbuildContext(builderOptions, context, entryPoints, external, outdir, tsConfigPath, mappedPaths, cache, dev, isNodeModules, hash = false, chunks, platform, optimizedMappings) {
8
+ if (isNodeModules) {
9
+ return createNodeModulesEsbuildContext(builderOptions, context, entryPoints, external, outdir, mappedPaths, cache, dev, hash, chunks, platform);
238
10
  }
11
+ return createAngularEsbuildContext(builderOptions, context, entryPoints, external, outdir, tsConfigPath, mappedPaths, cache, dev, hash, chunks, platform, optimizedMappings);
239
12
  }
240
- function writeResult(result, outdir, memOnly) {
13
+ function writeResult(result, outdir) {
241
14
  const writtenFiles = [];
242
- if (memOnly) {
243
- _memResultHandler(result.outputFiles ?? [], outdir);
244
- }
245
15
  for (const outFile of result.outputFiles ?? []) {
246
16
  const fileName = path.basename(outFile.path);
247
17
  const filePath = path.join(outdir, fileName);
248
- if (!memOnly) {
249
- fs.writeFileSync(filePath, outFile.text);
250
- }
18
+ fs.writeFileSync(filePath, outFile.text);
251
19
  writtenFiles.push(filePath);
252
20
  }
253
- if (!memOnly) {
254
- // for (const asset of result.outputFiles)
255
- }
256
21
  return writtenFiles;
257
22
  }
258
- function registerForRebuilds(kind, rebuildRequested, ctx, outdir, memOnly) {
259
- if (kind !== 'shared-package') {
260
- rebuildRequested.rebuild.register(async () => {
261
- const result = await ctx.rebuild();
262
- writeResult(result, outdir, memOnly);
263
- });
264
- }
265
- }
266
- export function loadEsmModule(modulePath) {
267
- return new Function('modulePath', `return import(modulePath);`)(modulePath);
268
- }
269
- //
270
- // Usually, ngServerMode is set during bundling. However, we need to infer this
271
- // value at runtime as we are using the same shared bundle for @angular/core
272
- // on the server and in the browser.
273
- //
23
+ /**
24
+ * Patches @angular/core to infer ngServerMode at runtime.
25
+ * Usually, ngServerMode is set during bundling. However, we need to infer this
26
+ * value at runtime as we are using the same shared bundle for @angular/core
27
+ * on the server and in the browser.
28
+ */
274
29
  function setNgServerMode() {
275
30
  const fileToPatch = 'node_modules/@angular/core/fesm2022/core.mjs';
276
31
  const lineToAdd = `if (typeof globalThis.ngServerMode ==='undefined') globalThis.ngServerMode = (typeof window === 'undefined') ? true : false;`;
@@ -287,3 +42,68 @@ function setNgServerMode() {
287
42
  console.error('Error patching file ', fileToPatch, '\nIs it write-protected?');
288
43
  }
289
44
  }
45
+ export function createAngularBuildAdapter(builderOptions, context) {
46
+ const bundleContextCache = new Map();
47
+ const dispose = async (name) => {
48
+ if (name) {
49
+ if (!bundleContextCache.has(name))
50
+ throw new Error(`Could not dispose of non-existing build '${name}'`);
51
+ const entry = bundleContextCache.get(name);
52
+ await entry.ctx.dispose();
53
+ await entry.pluginDisposed;
54
+ bundleContextCache.delete(name);
55
+ return;
56
+ }
57
+ const disposals = [];
58
+ for (const [, entry] of bundleContextCache) {
59
+ disposals.push((async () => {
60
+ await entry.ctx.dispose();
61
+ await entry.pluginDisposed;
62
+ })());
63
+ }
64
+ bundleContextCache.clear();
65
+ await Promise.all(disposals);
66
+ await esbuild.stop();
67
+ };
68
+ const setup = async (options) => {
69
+ const { entryPoints, tsConfigPath, external, outdir, mappedPaths, bundleName, isNodeModules, dev, chunks, hash, platform, optimizedMappings, cache, } = options;
70
+ setNgServerMode();
71
+ if (bundleContextCache.has(bundleName)) {
72
+ return;
73
+ }
74
+ const { ctx, pluginDisposed } = await createEsbuildContext(builderOptions, context, entryPoints, external, outdir, tsConfigPath, mappedPaths, cache, dev, isNodeModules, hash, chunks, platform, optimizedMappings);
75
+ bundleContextCache.set(bundleName, {
76
+ ctx,
77
+ pluginDisposed,
78
+ outdir,
79
+ cache,
80
+ isNodeModules,
81
+ dev: !!dev,
82
+ name: bundleName,
83
+ });
84
+ };
85
+ const build = async (name, opts = {}) => {
86
+ const bundleContext = bundleContextCache.get(name);
87
+ if (!bundleContext) {
88
+ throw new Error(`No context found for build "${name}". Call setup() first.`);
89
+ }
90
+ if (opts?.signal?.aborted) {
91
+ throw new AbortedError('[build] Aborted before rebuild');
92
+ }
93
+ try {
94
+ if (opts.files) {
95
+ bundleContext.cache.bundlerCache.invalidate(new Set(opts.files));
96
+ }
97
+ const result = await bundleContext.ctx.rebuild();
98
+ const writtenFiles = writeResult(result, bundleContext.outdir);
99
+ return writtenFiles.map(fileName => ({ fileName }));
100
+ }
101
+ catch (error) {
102
+ if (opts?.signal?.aborted && error instanceof Error && error.message.includes('canceled')) {
103
+ throw new AbortedError('[build] ESBuild rebuild was canceled.');
104
+ }
105
+ throw error;
106
+ }
107
+ };
108
+ return { setup, build, dispose };
109
+ }
@@ -0,0 +1,12 @@
1
+ import * as esbuild from 'esbuild';
2
+ import { type SourceFileCache } from '@angular/build/private';
3
+ import type { BuilderContext } from '@angular-devkit/architect';
4
+ import type { ApplicationBuilderOptions } from '@angular/build';
5
+ import type { EntryPoint, FederationCache } from '@softarc/native-federation';
6
+ import type { MappedPath } from '@softarc/native-federation/internal';
7
+ export interface NodeModulesBundleResult {
8
+ ctx: esbuild.BuildContext;
9
+ pluginDisposed: Promise<void>;
10
+ }
11
+ export declare function createNodeModulesEsbuildContext(builderOptions: ApplicationBuilderOptions, context: BuilderContext, entryPoints: EntryPoint[], external: string[], outdir: string, mappedPaths: MappedPath[], cache: FederationCache<SourceFileCache>, dev?: boolean, hash?: boolean, chunks?: boolean, platform?: 'browser' | 'node'): Promise<NodeModulesBundleResult>;
12
+ //# sourceMappingURL=node-modules-bundler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node-modules-bundler.d.ts","sourceRoot":"","sources":["../../../src/utils/node-modules-bundler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,SAAS,CAAC;AAInC,OAAO,EAKL,KAAK,eAAe,EACrB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAIhE,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,gBAAgB,CAAC;AAChE,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC9E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qCAAqC,CAAC;AAwDtE,MAAM,WAAW,uBAAuB;IACtC,GAAG,EAAE,OAAO,CAAC,YAAY,CAAC;IAC1B,cAAc,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/B;AAaD,wBAAsB,+BAA+B,CACnD,cAAc,EAAE,yBAAyB,EACzC,OAAO,EAAE,cAAc,EACvB,WAAW,EAAE,UAAU,EAAE,EACzB,QAAQ,EAAE,MAAM,EAAE,EAClB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,UAAU,EAAE,EACzB,KAAK,EAAE,eAAe,CAAC,eAAe,CAAC,EACvC,GAAG,CAAC,EAAE,OAAO,EACb,IAAI,GAAE,OAAe,EACrB,MAAM,CAAC,EAAE,OAAO,EAChB,QAAQ,CAAC,EAAE,SAAS,GAAG,MAAM,GAC5B,OAAO,CAAC,uBAAuB,CAAC,CA4ElC"}