@angular-architects/native-federation-v4 21.1.6 → 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.
- package/package.json +11 -3
- package/src/builders/build/builder.d.ts.map +1 -1
- package/src/builders/build/builder.js +73 -52
- package/src/builders/build/schema.d.ts +1 -0
- package/src/builders/build/schema.json +3 -0
- package/src/builders/build/setup-builder-env-variables.js +3 -1
- package/src/utils/angular-bundler.d.ts +12 -0
- package/src/utils/angular-bundler.d.ts.map +1 -0
- package/src/utils/angular-bundler.js +167 -0
- package/src/utils/angular-esbuild-adapter.d.ts +5 -1
- package/src/utils/angular-esbuild-adapter.d.ts.map +1 -1
- package/src/utils/angular-esbuild-adapter.js +59 -238
- package/src/utils/node-modules-bundler.d.ts +12 -0
- package/src/utils/node-modules-bundler.d.ts.map +1 -0
- package/src/utils/node-modules-bundler.js +113 -0
- package/src/utils/create-compiler-options.d.ts +0 -6
- package/src/utils/create-compiler-options.d.ts.map +0 -1
- package/src/utils/create-compiler-options.js +0 -41
- package/src/utils/mem-resuts.d.ts +0 -29
- package/src/utils/mem-resuts.d.ts.map +0 -1
- package/src/utils/mem-resuts.js +0 -50
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@angular-architects/native-federation-v4",
|
|
3
|
-
"version": "21.1.
|
|
3
|
+
"version": "21.1.7",
|
|
4
4
|
"generators": "./collection.json",
|
|
5
5
|
"builders": "./builders.json",
|
|
6
6
|
"schematics": "./collection.json",
|
|
@@ -9,9 +9,17 @@
|
|
|
9
9
|
"name": "Manfred Steyer",
|
|
10
10
|
"url": "http://www.angulararchitects.io"
|
|
11
11
|
},
|
|
12
|
+
"peerDependencies": {
|
|
13
|
+
"@softarc/native-federation": "^4.0.0-RC8",
|
|
14
|
+
"@softarc/native-federation-runtime": "^4.0.0-RC8",
|
|
15
|
+
"@angular-devkit/architect": ">=0.2102.0",
|
|
16
|
+
"@angular-devkit/build-angular": ">=21.2.0",
|
|
17
|
+
"@angular-devkit/core": ">=21.2.0",
|
|
18
|
+
"@angular/build": ">=21.2.0"
|
|
19
|
+
},
|
|
12
20
|
"dependencies": {
|
|
13
|
-
"@softarc/native-federation": "4.0.0-
|
|
14
|
-
"@softarc/native-federation-runtime": "4.0.0-
|
|
21
|
+
"@softarc/native-federation": "4.0.0-RC8",
|
|
22
|
+
"@softarc/native-federation-runtime": "4.0.0-RC8",
|
|
15
23
|
"@angular-devkit/architect": "^0.2102.0",
|
|
16
24
|
"@angular-devkit/build-angular": "^21.2.0",
|
|
17
25
|
"@angular-devkit/core": "^21.2.0",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../../../src/builders/build/builder.ts"],"names":[],"mappings":"AAAA,OAAO,kCAAkC,CAAC;AAS1C,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,aAAa,EAGnB,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../../../src/builders/build/builder.ts"],"names":[],"mappings":"AAAA,OAAO,kCAAkC,CAAC;AAS1C,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,aAAa,EAGnB,MAAM,2BAA2B,CAAC;AAgCnC,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AA2CnD,wBAAuB,UAAU,CAC/B,gBAAgB,EAAE,eAAe,EACjC,OAAO,EAAE,cAAc,GACtB,aAAa,CAAC,aAAa,CAAC,CAkY9B;wBA6C2C,GAAG;AAA/C,wBAAgD"}
|
|
@@ -6,9 +6,7 @@ import { buildApplication } from '@angular/build';
|
|
|
6
6
|
import { buildApplicationInternal, serveWithVite, SourceFileCache } from '@angular/build/private';
|
|
7
7
|
import { createBuilder, targetFromTargetString, } from '@angular-devkit/architect';
|
|
8
8
|
import { normalizeOptions } from '@angular-devkit/build-angular/src/builders/dev-server/options.js';
|
|
9
|
-
import { buildForFederation, rebuildForFederation, getExternals, loadFederationConfig, normalizeFederationOptions, setBuildAdapter, createFederationCache,
|
|
10
|
-
// type FederationCache,
|
|
11
|
-
} from '@softarc/native-federation';
|
|
9
|
+
import { buildForFederation, rebuildForFederation, getExternals, loadFederationConfig, normalizeFederationOptions, setBuildAdapter, createFederationCache, } from '@softarc/native-federation';
|
|
12
10
|
import { logger, setLogLevel, RebuildQueue, AbortedError, getDefaultCachePath, } from '@softarc/native-federation/internal';
|
|
13
11
|
import { createAngularBuildAdapter } from '../../utils/angular-esbuild-adapter.js';
|
|
14
12
|
import { existsSync, mkdirSync, rmSync } from 'fs';
|
|
@@ -38,6 +36,7 @@ const createInternalAngularBuilder = (_) => (options, context, pluginsOrExtensio
|
|
|
38
36
|
else {
|
|
39
37
|
extensions = pluginsOrExtensions;
|
|
40
38
|
}
|
|
39
|
+
// Todo: share cache with Angular builder: https://github.com/angular/angular-cli/pull/32527
|
|
41
40
|
// options.codeBundleCache = nfOptions.federationCache.bundlerCache;
|
|
42
41
|
return buildApplicationInternal(options, context, extensions);
|
|
43
42
|
};
|
|
@@ -117,7 +116,7 @@ export async function* runBuilder(nfBuilderOptions, context) {
|
|
|
117
116
|
const devServerOutputPath = !differentDevServerOutputPath
|
|
118
117
|
? browserOutputPath
|
|
119
118
|
: path.join(outputOptions.base, outputOptions.browser, localeFilter[0]);
|
|
120
|
-
const entryPoint = path.join(path.dirname(options.tsConfig), 'src/main.ts');
|
|
119
|
+
const entryPoint = nfBuilderOptions.entryPoint ?? path.join(path.dirname(options.tsConfig), 'src/main.ts');
|
|
121
120
|
const cachePath = getDefaultCachePath(context.workspaceRoot);
|
|
122
121
|
const nfOptions = normalizeFederationOptions({
|
|
123
122
|
workspaceRoot: context.workspaceRoot,
|
|
@@ -188,7 +187,6 @@ export async function* runBuilder(nfBuilderOptions, context) {
|
|
|
188
187
|
},
|
|
189
188
|
];
|
|
190
189
|
let first = true;
|
|
191
|
-
let lastResult;
|
|
192
190
|
if (existsSync(nfOptions.outputPath)) {
|
|
193
191
|
rmSync(nfOptions.outputPath, { recursive: true });
|
|
194
192
|
}
|
|
@@ -228,62 +226,85 @@ export async function* runBuilder(nfBuilderOptions, context) {
|
|
|
228
226
|
indexHtmlTransformer: transformIndexHtml(nfBuilderOptions),
|
|
229
227
|
});
|
|
230
228
|
const rebuildQueue = new RebuildQueue();
|
|
229
|
+
const builderIterator = builderRun[Symbol.asyncIterator]();
|
|
230
|
+
let ngBuildStatus = { success: false };
|
|
231
231
|
try {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
if (
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
};
|
|
247
|
-
signal.addEventListener('abort', abortHandler, { once: true });
|
|
232
|
+
let buildResult = await builderIterator.next();
|
|
233
|
+
while (!buildResult.done) {
|
|
234
|
+
if (buildResult.value)
|
|
235
|
+
ngBuildStatus = buildResult.value;
|
|
236
|
+
if (!ngBuildStatus.success) {
|
|
237
|
+
logger.warn('Skipping federation artifacts because Angular build failed.');
|
|
238
|
+
buildResult = await builderIterator.next();
|
|
239
|
+
}
|
|
240
|
+
else if (!first && (nfBuilderOptions.dev || watch)) {
|
|
241
|
+
const nextOutputPromise = builderIterator.next();
|
|
242
|
+
const trackResult = await rebuildQueue.track(async (signal) => {
|
|
243
|
+
try {
|
|
244
|
+
if (signal?.aborted) {
|
|
245
|
+
throw new AbortedError('Build canceled before starting');
|
|
248
246
|
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
247
|
+
await new Promise((resolve, reject) => {
|
|
248
|
+
const timeout = setTimeout(resolve, Math.max(10, nfBuilderOptions.rebuildDelay));
|
|
249
|
+
if (signal) {
|
|
250
|
+
const abortHandler = () => {
|
|
251
|
+
clearTimeout(timeout);
|
|
252
|
+
reject(new AbortedError('[builder] During delay.'));
|
|
253
|
+
};
|
|
254
|
+
signal.addEventListener('abort', abortHandler, { once: true });
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
if (signal?.aborted) {
|
|
258
|
+
throw new AbortedError('[builder] Before federation build.');
|
|
259
|
+
}
|
|
260
|
+
const start = process.hrtime();
|
|
261
|
+
// Todo: Invalidate all source files, Angular doesn't provide a way to give the invalidated files yet.
|
|
262
|
+
// ref: https://github.com/angular/angular-cli/pull/32527
|
|
263
|
+
const keys = [...nfOptions.federationCache.bundlerCache.keys()].filter(k => !k.includes('node_modules'));
|
|
264
|
+
federationResult = await rebuildForFederation(config, nfOptions, externals, keys, signal);
|
|
265
|
+
if (signal?.aborted) {
|
|
266
|
+
throw new AbortedError('[builder] After federation build.');
|
|
267
|
+
}
|
|
268
|
+
if (hasLocales && localeFilter) {
|
|
269
|
+
translateFederationArtifacts(i18n, localeFilter, outputOptions.base, federationResult);
|
|
270
|
+
}
|
|
271
|
+
if (signal?.aborted) {
|
|
272
|
+
throw new AbortedError('[builder] After federation translations.');
|
|
273
|
+
}
|
|
274
|
+
logger.info('Done!');
|
|
275
|
+
if (isLocalDevelopment) {
|
|
276
|
+
federationBuildNotifier.broadcastBuildCompletion();
|
|
277
|
+
}
|
|
278
|
+
logger.measure(start, 'To rebuild the federation artifacts.');
|
|
279
|
+
return { success: true };
|
|
277
280
|
}
|
|
278
|
-
|
|
281
|
+
catch (error) {
|
|
282
|
+
if (error instanceof AbortedError) {
|
|
283
|
+
logger.verbose('Rebuild was canceled. Cancellation point: ' + error?.message);
|
|
284
|
+
federationBuildNotifier.broadcastBuildCancellation();
|
|
285
|
+
return { success: false, cancelled: true };
|
|
286
|
+
}
|
|
279
287
|
logger.error('Federation rebuild failed!');
|
|
280
288
|
if (options.verbose)
|
|
281
289
|
console.error(error);
|
|
282
290
|
if (isLocalDevelopment) {
|
|
283
291
|
federationBuildNotifier.broadcastBuildError(error);
|
|
284
292
|
}
|
|
293
|
+
return { success: false };
|
|
285
294
|
}
|
|
286
|
-
});
|
|
295
|
+
}, nextOutputPromise);
|
|
296
|
+
if (trackResult.type === 'completed') {
|
|
297
|
+
if (!trackResult.result.cancelled) {
|
|
298
|
+
yield { success: trackResult.result.success };
|
|
299
|
+
}
|
|
300
|
+
buildResult = await nextOutputPromise;
|
|
301
|
+
}
|
|
302
|
+
else {
|
|
303
|
+
buildResult = trackResult.value;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
buildResult = await builderIterator.next();
|
|
287
308
|
}
|
|
288
309
|
first = false;
|
|
289
310
|
}
|
|
@@ -295,7 +316,7 @@ export async function* runBuilder(nfBuilderOptions, context) {
|
|
|
295
316
|
federationBuildNotifier.stopEventServer();
|
|
296
317
|
}
|
|
297
318
|
}
|
|
298
|
-
yield
|
|
319
|
+
yield ngBuildStatus;
|
|
299
320
|
}
|
|
300
321
|
function removeBaseHref(req, baseHref) {
|
|
301
322
|
let url = req.url ?? '';
|
|
@@ -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 AngularBundleResult {
|
|
8
|
+
ctx: esbuild.BuildContext;
|
|
9
|
+
pluginDisposed: Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
export declare function createAngularEsbuildContext(builderOptions: ApplicationBuilderOptions, context: BuilderContext, entryPoints: EntryPoint[], external: string[], outdir: string, tsConfigPath: string, mappedPaths: MappedPath[], cache: FederationCache<SourceFileCache>, dev?: boolean, hash?: boolean, chunks?: boolean, platform?: 'browser' | 'node', optimizedMappings?: boolean): Promise<AngularBundleResult>;
|
|
12
|
+
//# sourceMappingURL=angular-bundler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"angular-bundler.d.ts","sourceRoot":"","sources":["../../../src/utils/angular-bundler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,SAAS,CAAC;AAOnC,OAAO,EAML,KAAK,eAAe,EAGrB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAOhE,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;AAKtE,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,OAAO,CAAC,YAAY,CAAC;IAC1B,cAAc,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/B;AAED,wBAAsB,2BAA2B,CAC/C,cAAc,EAAE,yBAAyB,EACzC,OAAO,EAAE,cAAc,EACvB,WAAW,EAAE,UAAU,EAAE,EACzB,QAAQ,EAAE,MAAM,EAAE,EAClB,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,EACpB,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,EAC7B,iBAAiB,CAAC,EAAE,OAAO,GAC1B,OAAO,CAAC,mBAAmB,CAAC,CAkI9B"}
|
|
@@ -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
|
+
}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { type NFBuildAdapter } from '@softarc/native-federation';
|
|
2
|
+
import * as esbuild from 'esbuild';
|
|
2
3
|
import type { BuilderContext } from '@angular-devkit/architect';
|
|
3
4
|
import type { ApplicationBuilderOptions } from '@angular/build';
|
|
5
|
+
export interface EsbuildContextResult {
|
|
6
|
+
ctx: esbuild.BuildContext;
|
|
7
|
+
pluginDisposed: Promise<void>;
|
|
8
|
+
}
|
|
4
9
|
export declare function createAngularBuildAdapter(builderOptions: ApplicationBuilderOptions, context: BuilderContext): NFBuildAdapter;
|
|
5
|
-
export declare function loadEsmModule<T>(modulePath: string | URL): Promise<T>;
|
|
6
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":"
|
|
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,74 +1,101 @@
|
|
|
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 {
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
import {
|
|
13
|
-
|
|
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);
|
|
10
|
+
}
|
|
11
|
+
return createAngularEsbuildContext(builderOptions, context, entryPoints, external, outdir, tsConfigPath, mappedPaths, cache, dev, hash, chunks, platform, optimizedMappings);
|
|
12
|
+
}
|
|
13
|
+
function writeResult(result, outdir) {
|
|
14
|
+
const writtenFiles = [];
|
|
15
|
+
for (const outFile of result.outputFiles ?? []) {
|
|
16
|
+
const fileName = path.basename(outFile.path);
|
|
17
|
+
const filePath = path.join(outdir, fileName);
|
|
18
|
+
fs.writeFileSync(filePath, outFile.text);
|
|
19
|
+
writtenFiles.push(filePath);
|
|
20
|
+
}
|
|
21
|
+
return writtenFiles;
|
|
22
|
+
}
|
|
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
|
+
*/
|
|
29
|
+
function setNgServerMode() {
|
|
30
|
+
const fileToPatch = 'node_modules/@angular/core/fesm2022/core.mjs';
|
|
31
|
+
const lineToAdd = `if (typeof globalThis.ngServerMode ==='undefined') globalThis.ngServerMode = (typeof window === 'undefined') ? true : false;`;
|
|
32
|
+
try {
|
|
33
|
+
if (fs.existsSync(fileToPatch)) {
|
|
34
|
+
let content = fs.readFileSync(fileToPatch, 'utf-8');
|
|
35
|
+
if (!content.includes(lineToAdd)) {
|
|
36
|
+
content = lineToAdd + '\n' + content;
|
|
37
|
+
fs.writeFileSync(fileToPatch, content);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
console.error('Error patching file ', fileToPatch, '\nIs it write-protected?');
|
|
43
|
+
}
|
|
44
|
+
}
|
|
14
45
|
export function createAngularBuildAdapter(builderOptions, context) {
|
|
15
|
-
const
|
|
46
|
+
const bundleContextCache = new Map();
|
|
16
47
|
const dispose = async (name) => {
|
|
17
48
|
if (name) {
|
|
18
|
-
if (!
|
|
49
|
+
if (!bundleContextCache.has(name))
|
|
19
50
|
throw new Error(`Could not dispose of non-existing build '${name}'`);
|
|
20
|
-
const entry =
|
|
51
|
+
const entry = bundleContextCache.get(name);
|
|
21
52
|
await entry.ctx.dispose();
|
|
22
53
|
await entry.pluginDisposed;
|
|
23
|
-
|
|
54
|
+
bundleContextCache.delete(name);
|
|
24
55
|
return;
|
|
25
56
|
}
|
|
26
|
-
// Dispose all if no specific build provided
|
|
27
57
|
const disposals = [];
|
|
28
|
-
for (const [, entry] of
|
|
58
|
+
for (const [, entry] of bundleContextCache) {
|
|
29
59
|
disposals.push((async () => {
|
|
30
60
|
await entry.ctx.dispose();
|
|
31
61
|
await entry.pluginDisposed;
|
|
32
62
|
})());
|
|
33
63
|
}
|
|
34
|
-
|
|
64
|
+
bundleContextCache.clear();
|
|
35
65
|
await Promise.all(disposals);
|
|
66
|
+
await esbuild.stop();
|
|
36
67
|
};
|
|
37
68
|
const setup = async (options) => {
|
|
38
69
|
const { entryPoints, tsConfigPath, external, outdir, mappedPaths, bundleName, isNodeModules, dev, chunks, hash, platform, optimizedMappings, cache, } = options;
|
|
39
70
|
setNgServerMode();
|
|
40
|
-
if (
|
|
71
|
+
if (bundleContextCache.has(bundleName)) {
|
|
41
72
|
return;
|
|
42
73
|
}
|
|
43
|
-
const { ctx, pluginDisposed } = await createEsbuildContext(builderOptions, context, entryPoints, external, outdir, tsConfigPath, mappedPaths, cache
|
|
44
|
-
|
|
74
|
+
const { ctx, pluginDisposed } = await createEsbuildContext(builderOptions, context, entryPoints, external, outdir, tsConfigPath, mappedPaths, cache, dev, isNodeModules, hash, chunks, platform, optimizedMappings);
|
|
75
|
+
bundleContextCache.set(bundleName, {
|
|
45
76
|
ctx,
|
|
46
77
|
pluginDisposed,
|
|
47
78
|
outdir,
|
|
79
|
+
cache,
|
|
48
80
|
isNodeModules,
|
|
49
81
|
dev: !!dev,
|
|
50
82
|
name: bundleName,
|
|
51
|
-
entryPoints,
|
|
52
|
-
workspaceRoot: context.workspaceRoot,
|
|
53
83
|
});
|
|
54
84
|
};
|
|
55
85
|
const build = async (name, opts = {}) => {
|
|
56
|
-
const
|
|
57
|
-
if (!
|
|
86
|
+
const bundleContext = bundleContextCache.get(name);
|
|
87
|
+
if (!bundleContext) {
|
|
58
88
|
throw new Error(`No context found for build "${name}". Call setup() first.`);
|
|
59
89
|
}
|
|
60
90
|
if (opts?.signal?.aborted) {
|
|
61
91
|
throw new AbortedError('[build] Aborted before rebuild');
|
|
62
92
|
}
|
|
63
93
|
try {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (cached.isNodeModules) {
|
|
67
|
-
const scriptFiles = writtenFiles.filter(f => f.endsWith('.js') || f.endsWith('.mjs'));
|
|
68
|
-
for (const file of scriptFiles) {
|
|
69
|
-
await link(file, cached.dev);
|
|
70
|
-
}
|
|
94
|
+
if (opts.files) {
|
|
95
|
+
bundleContext.cache.bundlerCache.invalidate(new Set(opts.files));
|
|
71
96
|
}
|
|
97
|
+
const result = await bundleContext.ctx.rebuild();
|
|
98
|
+
const writtenFiles = writeResult(result, bundleContext.outdir);
|
|
72
99
|
return writtenFiles.map(fileName => ({ fileName }));
|
|
73
100
|
}
|
|
74
101
|
catch (error) {
|
|
@@ -80,209 +107,3 @@ export function createAngularBuildAdapter(builderOptions, context) {
|
|
|
80
107
|
};
|
|
81
108
|
return { setup, build, dispose };
|
|
82
109
|
}
|
|
83
|
-
async function link(outfile, dev) {
|
|
84
|
-
const code = fs.readFileSync(outfile, 'utf-8');
|
|
85
|
-
try {
|
|
86
|
-
const linkerEsm = await loadEsmModule('@angular/compiler-cli/linker/babel');
|
|
87
|
-
const linker = linkerEsm.default;
|
|
88
|
-
const result = await transformAsync(code, {
|
|
89
|
-
filename: outfile,
|
|
90
|
-
compact: !dev,
|
|
91
|
-
configFile: false,
|
|
92
|
-
babelrc: false,
|
|
93
|
-
minified: !dev,
|
|
94
|
-
browserslistConfigFile: false,
|
|
95
|
-
plugins: [linker],
|
|
96
|
-
});
|
|
97
|
-
if (!result)
|
|
98
|
-
logger.warn(`File ${outfile} could not be linked.`);
|
|
99
|
-
if (!result?.code) {
|
|
100
|
-
logger.warn(`File ${outfile} seems to be empty.`);
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
fs.writeFileSync(outfile, result.code, 'utf-8');
|
|
104
|
-
}
|
|
105
|
-
catch (e) {
|
|
106
|
-
logger.error('error linking');
|
|
107
|
-
if (fs.existsSync(`${outfile}.error`)) {
|
|
108
|
-
fs.unlinkSync(`${outfile}.error`);
|
|
109
|
-
}
|
|
110
|
-
fs.renameSync(outfile, `${outfile}.error`);
|
|
111
|
-
throw e;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
async function createEsbuildContext(builderOptions, context, entryPoints, external, outdir, tsConfigPath, mappedPaths, sourceFileCache, dev, isNodeModules, hash = false, chunks, platform, optimizedMappings) {
|
|
115
|
-
const workspaceRoot = context.workspaceRoot;
|
|
116
|
-
const projectMetadata = await context.getProjectMetadata(context.target.project);
|
|
117
|
-
const projectRoot = path.join(workspaceRoot, projectMetadata['root'] ?? '');
|
|
118
|
-
const browsers = getSupportedBrowsers(projectRoot, context.logger);
|
|
119
|
-
const target = transformSupportedBrowsersToTargets(browsers);
|
|
120
|
-
const optimizationOptions = normalizeOptimization(builderOptions.optimization);
|
|
121
|
-
const sourcemapOptions = normalizeSourceMaps(builderOptions.sourceMap);
|
|
122
|
-
const searchDirectories = await generateSearchDirectories([projectRoot, workspaceRoot]);
|
|
123
|
-
const postcssConfiguration = await loadPostcssConfiguration(searchDirectories);
|
|
124
|
-
const tailwindConfiguration = postcssConfiguration
|
|
125
|
-
? undefined
|
|
126
|
-
: await getTailwindConfig(searchDirectories);
|
|
127
|
-
const outputNames = {
|
|
128
|
-
bundles: '[name]',
|
|
129
|
-
media: 'media/[name]',
|
|
130
|
-
};
|
|
131
|
-
let fileReplacements;
|
|
132
|
-
if (builderOptions.fileReplacements) {
|
|
133
|
-
for (const replacement of builderOptions.fileReplacements) {
|
|
134
|
-
fileReplacements ??= {};
|
|
135
|
-
fileReplacements[path.join(workspaceRoot, replacement.replace)] = path.join(workspaceRoot, replacement.with);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
if (!optimizedMappings) {
|
|
139
|
-
tsConfigPath = createTsConfigForFederation(workspaceRoot, tsConfigPath, entryPoints);
|
|
140
|
-
}
|
|
141
|
-
const pluginOptions = createCompilerPluginOptions({
|
|
142
|
-
workspaceRoot,
|
|
143
|
-
optimizationOptions,
|
|
144
|
-
sourcemapOptions,
|
|
145
|
-
tsconfig: tsConfigPath,
|
|
146
|
-
outputNames,
|
|
147
|
-
fileReplacements,
|
|
148
|
-
externalDependencies: external,
|
|
149
|
-
preserveSymlinks: builderOptions.preserveSymlinks,
|
|
150
|
-
stylePreprocessorOptions: builderOptions.stylePreprocessorOptions,
|
|
151
|
-
advancedOptimizations: !dev,
|
|
152
|
-
inlineStyleLanguage: builderOptions.inlineStyleLanguage,
|
|
153
|
-
jit: false,
|
|
154
|
-
tailwindConfiguration,
|
|
155
|
-
postcssConfiguration,
|
|
156
|
-
incremental: !isNodeModules,
|
|
157
|
-
}, target, sourceFileCache);
|
|
158
|
-
const commonjsPluginModule = await import('@chialab/esbuild-plugin-commonjs');
|
|
159
|
-
const commonjsPlugin = commonjsPluginModule.default;
|
|
160
|
-
pluginOptions.styleOptions.externalDependencies = [];
|
|
161
|
-
const [compilerPlugin, pluginDisposed] = createAwaitableCompilerPlugin(pluginOptions.pluginOptions, pluginOptions.styleOptions);
|
|
162
|
-
const config = {
|
|
163
|
-
entryPoints: entryPoints.map(ep => ({
|
|
164
|
-
in: ep.fileName,
|
|
165
|
-
out: path.parse(ep.outName).name,
|
|
166
|
-
})),
|
|
167
|
-
outdir,
|
|
168
|
-
entryNames: hash ? '[name]-[hash]' : '[name]',
|
|
169
|
-
write: false,
|
|
170
|
-
external,
|
|
171
|
-
logLevel: 'warning',
|
|
172
|
-
bundle: true,
|
|
173
|
-
sourcemap: sourcemapOptions.scripts,
|
|
174
|
-
minify: !dev,
|
|
175
|
-
supported: {
|
|
176
|
-
'async-await': false,
|
|
177
|
-
'object-rest-spread': false,
|
|
178
|
-
},
|
|
179
|
-
splitting: chunks,
|
|
180
|
-
platform: platform ?? 'browser',
|
|
181
|
-
format: 'esm',
|
|
182
|
-
target: target,
|
|
183
|
-
logLimit: isNodeModules ? 1 : 0,
|
|
184
|
-
plugins: [
|
|
185
|
-
compilerPlugin,
|
|
186
|
-
...(mappedPaths && mappedPaths.length > 0 ? [createSharedMappingsPlugin(mappedPaths)] : []),
|
|
187
|
-
commonjsPlugin(),
|
|
188
|
-
],
|
|
189
|
-
define: {
|
|
190
|
-
...(!dev ? { ngDevMode: 'false' } : {}),
|
|
191
|
-
ngJitMode: 'false',
|
|
192
|
-
},
|
|
193
|
-
...(builderOptions.loader ? { loader: builderOptions.loader } : {}),
|
|
194
|
-
resolveExtensions: ['.ts', '.tsx', '.mjs', '.js', '.cjs'],
|
|
195
|
-
};
|
|
196
|
-
const ctx = await esbuild.context(config);
|
|
197
|
-
return { ctx, pluginDisposed };
|
|
198
|
-
}
|
|
199
|
-
async function getTailwindConfig(searchDirectories) {
|
|
200
|
-
const tailwindConfigurationPath = findTailwindConfiguration(searchDirectories);
|
|
201
|
-
if (!tailwindConfigurationPath) {
|
|
202
|
-
return undefined;
|
|
203
|
-
}
|
|
204
|
-
return {
|
|
205
|
-
file: tailwindConfigurationPath,
|
|
206
|
-
package: createRequire(tailwindConfigurationPath).resolve('tailwindcss'),
|
|
207
|
-
};
|
|
208
|
-
}
|
|
209
|
-
function createTsConfigForFederation(workspaceRoot, tsConfigPath, entryPoints) {
|
|
210
|
-
const fullTsConfigPath = path.join(workspaceRoot, tsConfigPath);
|
|
211
|
-
const tsconfigDir = path.dirname(fullTsConfigPath);
|
|
212
|
-
const filtered = entryPoints
|
|
213
|
-
.filter(ep => !ep.fileName.includes('/node_modules/') && !ep.fileName.startsWith('.'))
|
|
214
|
-
.map(ep => path.relative(tsconfigDir, ep.fileName).replace(/\\\\/g, '/'));
|
|
215
|
-
const tsconfigAsString = fs.readFileSync(fullTsConfigPath, 'utf-8');
|
|
216
|
-
const tsconfig = JSON5.parse(tsconfigAsString);
|
|
217
|
-
if (!tsconfig.include) {
|
|
218
|
-
tsconfig.include = [];
|
|
219
|
-
}
|
|
220
|
-
for (const ep of filtered) {
|
|
221
|
-
if (!tsconfig.include.includes(ep)) {
|
|
222
|
-
tsconfig.include.push(ep);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
const content = JSON5.stringify(tsconfig, null, 2);
|
|
226
|
-
const tsconfigFedPath = path.join(tsconfigDir, 'tsconfig.federation.json');
|
|
227
|
-
if (!doesFileExistAndJsonEqual(tsconfigFedPath, content)) {
|
|
228
|
-
fs.writeFileSync(tsconfigFedPath, JSON.stringify(tsconfig, null, 2));
|
|
229
|
-
}
|
|
230
|
-
tsConfigPath = tsconfigFedPath;
|
|
231
|
-
return tsConfigPath;
|
|
232
|
-
}
|
|
233
|
-
/**
|
|
234
|
-
* Checks if a file exists and if its content is equal to the provided content.
|
|
235
|
-
* If the file does not exist, it returns false.
|
|
236
|
-
* If the file or its content is invalid JSON, it returns false.
|
|
237
|
-
* @param {string} path - The path to the file
|
|
238
|
-
* @param {string} content - The content to compare with
|
|
239
|
-
* @returns {boolean} - Returns true if the file exists and its content is equal to the provided content
|
|
240
|
-
*/
|
|
241
|
-
function doesFileExistAndJsonEqual(path, content) {
|
|
242
|
-
if (!fs.existsSync(path)) {
|
|
243
|
-
return false;
|
|
244
|
-
}
|
|
245
|
-
try {
|
|
246
|
-
const currentContent = fs.readFileSync(path, 'utf-8');
|
|
247
|
-
const currentJson = JSON5.parse(currentContent);
|
|
248
|
-
const newJson = JSON5.parse(content);
|
|
249
|
-
return isDeepStrictEqual(currentJson, newJson);
|
|
250
|
-
}
|
|
251
|
-
catch {
|
|
252
|
-
return false;
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
function writeResult(result, outdir) {
|
|
256
|
-
const writtenFiles = [];
|
|
257
|
-
for (const outFile of result.outputFiles ?? []) {
|
|
258
|
-
const fileName = path.basename(outFile.path);
|
|
259
|
-
const filePath = path.join(outdir, fileName);
|
|
260
|
-
fs.writeFileSync(filePath, outFile.text);
|
|
261
|
-
writtenFiles.push(filePath);
|
|
262
|
-
}
|
|
263
|
-
return writtenFiles;
|
|
264
|
-
}
|
|
265
|
-
export function loadEsmModule(modulePath) {
|
|
266
|
-
return new Function('modulePath', `return import(modulePath);`)(modulePath);
|
|
267
|
-
}
|
|
268
|
-
//
|
|
269
|
-
// Usually, ngServerMode is set during bundling. However, we need to infer this
|
|
270
|
-
// value at runtime as we are using the same shared bundle for @angular/core
|
|
271
|
-
// on the server and in the browser.
|
|
272
|
-
//
|
|
273
|
-
function setNgServerMode() {
|
|
274
|
-
const fileToPatch = 'node_modules/@angular/core/fesm2022/core.mjs';
|
|
275
|
-
const lineToAdd = `if (typeof globalThis.ngServerMode ==='undefined') globalThis.ngServerMode = (typeof window === 'undefined') ? true : false;`;
|
|
276
|
-
try {
|
|
277
|
-
if (fs.existsSync(fileToPatch)) {
|
|
278
|
-
let content = fs.readFileSync(fileToPatch, 'utf-8');
|
|
279
|
-
if (!content.includes(lineToAdd)) {
|
|
280
|
-
content = lineToAdd + '\n' + content;
|
|
281
|
-
fs.writeFileSync(fileToPatch, content);
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
catch {
|
|
286
|
-
console.error('Error patching file ', fileToPatch, '\nIs it write-protected?');
|
|
287
|
-
}
|
|
288
|
-
}
|
|
@@ -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"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import * as esbuild from 'esbuild';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as fs from 'fs';
|
|
4
|
+
import { transformSupportedBrowsersToTargets, getSupportedBrowsers, JavaScriptTransformer, Cache, } from '@angular/build/private';
|
|
5
|
+
import { normalizeSourceMaps } from '@angular-devkit/build-angular/src/utils/index.js';
|
|
6
|
+
import { createSharedMappingsPlugin } from './shared-mappings-plugin.js';
|
|
7
|
+
const LINKER_DECLARATION_PREFIX = 'ɵɵngDeclare';
|
|
8
|
+
/**
|
|
9
|
+
* Excludes @angular/core and @angular/compiler which define the declarations
|
|
10
|
+
* and would cause false positives.
|
|
11
|
+
*/
|
|
12
|
+
function requiresLinking(filePath, source) {
|
|
13
|
+
if (/[\\/]@angular[\\/](?:compiler|core)[\\/]/.test(filePath)) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
return source.includes(LINKER_DECLARATION_PREFIX);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Creates an esbuild plugin that applies the Angular linker to partially compiled
|
|
20
|
+
* Angular libraries like a design system.
|
|
21
|
+
*
|
|
22
|
+
* Uses Angular's JavaScriptTransformer which handles linking internally.
|
|
23
|
+
*/
|
|
24
|
+
function createAngularLinkerPlugin(jsTransformer, advancedOptimizations) {
|
|
25
|
+
return {
|
|
26
|
+
name: 'angular-linker',
|
|
27
|
+
setup(build) {
|
|
28
|
+
build.onLoad({ filter: /\.m?js$/ }, async (args) => {
|
|
29
|
+
const contents = await fs.promises.readFile(args.path, 'utf-8');
|
|
30
|
+
const needsLinking = requiresLinking(args.path, contents);
|
|
31
|
+
if (!needsLinking && !advancedOptimizations) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
const result = await jsTransformer.transformData(args.path, contents, !needsLinking, undefined);
|
|
35
|
+
return {
|
|
36
|
+
contents: Buffer.from(result).toString('utf-8'),
|
|
37
|
+
loader: 'js',
|
|
38
|
+
};
|
|
39
|
+
});
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const jsTransformerCacheStores = new Map();
|
|
44
|
+
function getOrCreateJsTransformerCacheStore(cachePath) {
|
|
45
|
+
let store = jsTransformerCacheStores.get(cachePath);
|
|
46
|
+
if (!store) {
|
|
47
|
+
store = new Map();
|
|
48
|
+
jsTransformerCacheStores.set(cachePath, store);
|
|
49
|
+
}
|
|
50
|
+
return store;
|
|
51
|
+
}
|
|
52
|
+
export async function createNodeModulesEsbuildContext(builderOptions, context, entryPoints, external, outdir, mappedPaths, cache, dev, hash = false, chunks, platform) {
|
|
53
|
+
const workspaceRoot = context.workspaceRoot;
|
|
54
|
+
const projectMetadata = await context.getProjectMetadata(context.target.project);
|
|
55
|
+
const projectRoot = path.join(workspaceRoot, projectMetadata['root'] ?? '');
|
|
56
|
+
const browsers = getSupportedBrowsers(projectRoot, context.logger);
|
|
57
|
+
const target = transformSupportedBrowsersToTargets(browsers);
|
|
58
|
+
const sourcemapOptions = normalizeSourceMaps(builderOptions.sourceMap);
|
|
59
|
+
const commonjsPluginModule = await import('@chialab/esbuild-plugin-commonjs');
|
|
60
|
+
const commonjsPlugin = commonjsPluginModule.default;
|
|
61
|
+
// Create JavaScriptTransformer for handling Angular partial compilation linking
|
|
62
|
+
const advancedOptimizations = !dev;
|
|
63
|
+
const jsTransformerCacheStore = getOrCreateJsTransformerCacheStore(cache.cachePath);
|
|
64
|
+
const jsTransformerCache = new Cache(jsTransformerCacheStore, 'jstransformer');
|
|
65
|
+
const jsTransformer = new JavaScriptTransformer({
|
|
66
|
+
sourcemap: !!sourcemapOptions.scripts,
|
|
67
|
+
thirdPartySourcemaps: false,
|
|
68
|
+
advancedOptimizations,
|
|
69
|
+
jit: false,
|
|
70
|
+
}, 1, // maxThreads - keep low for node_modules bundling
|
|
71
|
+
jsTransformerCache);
|
|
72
|
+
const config = {
|
|
73
|
+
entryPoints: entryPoints.map(ep => ({
|
|
74
|
+
in: ep.fileName,
|
|
75
|
+
out: path.parse(ep.outName).name,
|
|
76
|
+
})),
|
|
77
|
+
outdir,
|
|
78
|
+
entryNames: hash ? '[name]-[hash]' : '[name]',
|
|
79
|
+
write: false,
|
|
80
|
+
external,
|
|
81
|
+
logLevel: 'warning',
|
|
82
|
+
bundle: true,
|
|
83
|
+
sourcemap: sourcemapOptions.scripts,
|
|
84
|
+
minify: !dev,
|
|
85
|
+
supported: {
|
|
86
|
+
'async-await': false,
|
|
87
|
+
'object-rest-spread': false,
|
|
88
|
+
},
|
|
89
|
+
splitting: chunks,
|
|
90
|
+
platform: platform ?? 'browser',
|
|
91
|
+
format: 'esm',
|
|
92
|
+
target: target,
|
|
93
|
+
logLimit: 1,
|
|
94
|
+
plugins: [
|
|
95
|
+
createAngularLinkerPlugin(jsTransformer, advancedOptimizations),
|
|
96
|
+
...(mappedPaths && mappedPaths.length > 0 ? [createSharedMappingsPlugin(mappedPaths)] : []),
|
|
97
|
+
commonjsPlugin(),
|
|
98
|
+
],
|
|
99
|
+
define: {
|
|
100
|
+
...(!dev ? { ngDevMode: 'false' } : {}),
|
|
101
|
+
ngJitMode: 'false',
|
|
102
|
+
},
|
|
103
|
+
...(builderOptions.loader ? { loader: builderOptions.loader } : {}),
|
|
104
|
+
resolveExtensions: ['.mjs', '.js', '.cjs'],
|
|
105
|
+
};
|
|
106
|
+
const ctx = await esbuild.context(config);
|
|
107
|
+
const originalDispose = ctx.dispose.bind(ctx);
|
|
108
|
+
ctx.dispose = async () => {
|
|
109
|
+
await originalDispose();
|
|
110
|
+
await jsTransformer.close();
|
|
111
|
+
};
|
|
112
|
+
return { ctx, pluginDisposed: Promise.resolve() };
|
|
113
|
+
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { type SourceFileCache } from '@angular/build/private';
|
|
2
|
-
export declare function createCompilerPluginOptions(options: any, target: string[], sourceFileCache?: SourceFileCache): {
|
|
3
|
-
pluginOptions: any[0];
|
|
4
|
-
styleOptions: any[1];
|
|
5
|
-
};
|
|
6
|
-
//# sourceMappingURL=create-compiler-options.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"create-compiler-options.d.ts","sourceRoot":"","sources":["../../../src/utils/create-compiler-options.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE9D,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,GAAG,EACZ,MAAM,EAAE,MAAM,EAAE,EAChB,eAAe,CAAC,EAAE,eAAe,GAChC;IACD,aAAa,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;IACtB,YAAY,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;CACtB,CAwDA"}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
// Taken from https://github.com/angular/angular-cli/blob/main/packages/angular/build/src/tools/esbuild/compiler-plugin-options.ts
|
|
2
|
-
// Currently, this type cannot be accessed from the outside
|
|
3
|
-
export function createCompilerPluginOptions(options, target, sourceFileCache) {
|
|
4
|
-
const { workspaceRoot, optimizationOptions, sourcemapOptions, tsconfig, outputNames, fileReplacements, externalDependencies, preserveSymlinks, stylePreprocessorOptions, advancedOptimizations, inlineStyleLanguage, jit, cacheOptions, tailwindConfiguration, postcssConfiguration, publicPath, } = options;
|
|
5
|
-
return {
|
|
6
|
-
// JS/TS options
|
|
7
|
-
pluginOptions: {
|
|
8
|
-
sourcemap: !!sourcemapOptions.scripts && (sourcemapOptions.hidden ? 'external' : true),
|
|
9
|
-
thirdPartySourcemaps: sourcemapOptions.vendor,
|
|
10
|
-
tsconfig,
|
|
11
|
-
jit,
|
|
12
|
-
advancedOptimizations,
|
|
13
|
-
fileReplacements,
|
|
14
|
-
sourceFileCache,
|
|
15
|
-
loadResultCache: sourceFileCache?.loadResultCache,
|
|
16
|
-
incremental: !!options.watch,
|
|
17
|
-
},
|
|
18
|
-
// Component stylesheet options
|
|
19
|
-
styleOptions: {
|
|
20
|
-
workspaceRoot,
|
|
21
|
-
inlineFonts: !!optimizationOptions.fonts.inline,
|
|
22
|
-
optimization: !!optimizationOptions.styles.minify,
|
|
23
|
-
sourcemap:
|
|
24
|
-
// Hidden component stylesheet sourcemaps are inaccessible which is effectively
|
|
25
|
-
// the same as being disabled. Disabling has the advantage of avoiding the overhead
|
|
26
|
-
// of sourcemap processing.
|
|
27
|
-
sourcemapOptions.styles && !sourcemapOptions.hidden ? 'linked' : false,
|
|
28
|
-
outputNames,
|
|
29
|
-
includePaths: stylePreprocessorOptions?.includePaths,
|
|
30
|
-
sass: stylePreprocessorOptions?.sass,
|
|
31
|
-
externalDependencies,
|
|
32
|
-
target,
|
|
33
|
-
inlineStyleLanguage,
|
|
34
|
-
preserveSymlinks,
|
|
35
|
-
tailwindConfiguration,
|
|
36
|
-
postcssConfiguration,
|
|
37
|
-
cacheOptions,
|
|
38
|
-
publicPath,
|
|
39
|
-
},
|
|
40
|
-
};
|
|
41
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import type { OutputFile } from 'esbuild';
|
|
2
|
-
export interface BuildResult {
|
|
3
|
-
fileName: string;
|
|
4
|
-
get(): Uint8Array | Buffer;
|
|
5
|
-
}
|
|
6
|
-
export declare class EsBuildResult implements BuildResult {
|
|
7
|
-
private outputFile;
|
|
8
|
-
private fullOutDir?;
|
|
9
|
-
get fileName(): string;
|
|
10
|
-
constructor(outputFile: OutputFile, fullOutDir?: string | undefined);
|
|
11
|
-
get(): Uint8Array;
|
|
12
|
-
}
|
|
13
|
-
export interface NgCliAssetFile {
|
|
14
|
-
source: string;
|
|
15
|
-
destination: string;
|
|
16
|
-
}
|
|
17
|
-
export declare class NgCliAssetResult implements BuildResult {
|
|
18
|
-
get fileName(): string;
|
|
19
|
-
private file;
|
|
20
|
-
constructor(assetFile: NgCliAssetFile);
|
|
21
|
-
get(): Buffer;
|
|
22
|
-
}
|
|
23
|
-
export declare class MemResults {
|
|
24
|
-
private map;
|
|
25
|
-
add(result: BuildResult[]): void;
|
|
26
|
-
get(fileName: string): BuildResult | undefined;
|
|
27
|
-
getFileNames(): string[];
|
|
28
|
-
}
|
|
29
|
-
//# sourceMappingURL=mem-resuts.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mem-resuts.d.ts","sourceRoot":"","sources":["../../../src/utils/mem-resuts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAI1C,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,IAAI,UAAU,GAAG,MAAM,CAAC;CAC5B;AAED,qBAAa,aAAc,YAAW,WAAW;IASnC,OAAO,CAAC,UAAU;IAAc,OAAO,CAAC,UAAU,CAAC;IAR/D,IAAI,QAAQ,WAMX;gBAEmB,UAAU,EAAE,UAAU,EAAU,UAAU,CAAC,EAAE,MAAM,YAAA;IAEvE,GAAG,IAAI,UAAU;CAGlB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,gBAAiB,YAAW,WAAW;IAClD,IAAW,QAAQ,IAAI,MAAM,CAE5B;IAED,OAAO,CAAC,IAAI,CAAiB;gBAEjB,SAAS,EAAE,cAAc;IAIrC,GAAG,IAAI,MAAM;CAGd;AAED,qBAAa,UAAU;IACrB,OAAO,CAAC,GAAG,CAAkC;IAEtC,GAAG,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,IAAI;IAMhC,GAAG,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAI9C,YAAY,IAAI,MAAM,EAAE;CAGhC"}
|
package/src/utils/mem-resuts.js
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import * as fs from 'fs';
|
|
2
|
-
import * as path from 'path';
|
|
3
|
-
export class EsBuildResult {
|
|
4
|
-
outputFile;
|
|
5
|
-
fullOutDir;
|
|
6
|
-
get fileName() {
|
|
7
|
-
if (this.fullOutDir) {
|
|
8
|
-
return unify(path.relative(this.fullOutDir, this.outputFile.path));
|
|
9
|
-
}
|
|
10
|
-
else {
|
|
11
|
-
return unify(this.outputFile.path);
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
constructor(outputFile, fullOutDir) {
|
|
15
|
-
this.outputFile = outputFile;
|
|
16
|
-
this.fullOutDir = fullOutDir;
|
|
17
|
-
}
|
|
18
|
-
get() {
|
|
19
|
-
return this.outputFile.contents;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
export class NgCliAssetResult {
|
|
23
|
-
get fileName() {
|
|
24
|
-
return unify(this.file.destination);
|
|
25
|
-
}
|
|
26
|
-
file;
|
|
27
|
-
constructor(assetFile) {
|
|
28
|
-
this.file = assetFile;
|
|
29
|
-
}
|
|
30
|
-
get() {
|
|
31
|
-
return fs.readFileSync(this.file.source);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
export class MemResults {
|
|
35
|
-
map = new Map();
|
|
36
|
-
add(result) {
|
|
37
|
-
for (const file of result) {
|
|
38
|
-
this.map.set(file.fileName, file);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
get(fileName) {
|
|
42
|
-
return this.map.get(fileName);
|
|
43
|
-
}
|
|
44
|
-
getFileNames() {
|
|
45
|
-
return [...this.map.keys()];
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
function unify(path) {
|
|
49
|
-
return path?.replace(/\\/g, '/');
|
|
50
|
-
}
|