@softarc/native-federation 4.0.0-RC6 → 4.0.0-RC8

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@softarc/native-federation",
3
- "version": "4.0.0-RC6",
3
+ "version": "4.0.0-RC8",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "dependencies": {
package/src/internal.d.ts CHANGED
@@ -3,7 +3,7 @@ export { hashFile } from './lib/utils/hash-file.js';
3
3
  export * from './lib/utils/errors.js';
4
4
  export { logger, setLogLevel } from './lib/utils/logger.js';
5
5
  export type { MappedPath } from './lib/domain/utils/mapped-path.contract.js';
6
- export { RebuildQueue } from './lib/utils/rebuild-queue.js';
6
+ export { RebuildQueue, type TrackResult } from './lib/utils/rebuild-queue.js';
7
7
  export { AbortedError } from './lib/utils/errors.js';
8
8
  export { createBuildResultMap, lookupInResultMap, popFromResultMap, } from './lib/utils/build-result-map.js';
9
9
  export { writeImportMap } from './lib/core/write-import-map.js';
@@ -9,16 +9,10 @@ import { resolveProjectName } from '../utils/config-utils.js';
9
9
  import { addExternalsToCache } from './federation-cache.js';
10
10
  import path from 'path';
11
11
  export async function buildForFederation(config, fedOptions, externals, signal) {
12
- // 1. Caching
12
+ // 1. Setup
13
13
  fedOptions.federationCache.cachePath = path.join(fedOptions.federationCache.cachePath, resolveProjectName(config));
14
- const start = process.hrtime();
15
- // 2. Shared mappings and exposed modules
16
- const artifactInfo = await bundleExposedAndMappings(config, fedOptions, externals, undefined, signal);
17
- logger.measure(start, '[build artifacts] - To bundle all mappings and exposed.');
18
- if (signal?.aborted)
19
- throw new AbortedError('[buildForFederation] After exposed-and-mappings bundle');
20
- const exposedInfo = !artifactInfo ? describeExposed(config, fedOptions) : artifactInfo.exposes;
21
- // 3. Externals
14
+ logger.info('Building federation artifacts');
15
+ // 2. Externals
22
16
  if (fedOptions.federationCache.externals.length > 0) {
23
17
  logger.info('Checksum matched, re-using cached externals.');
24
18
  }
@@ -61,6 +55,13 @@ export async function buildForFederation(config, fedOptions, externals, signal)
61
55
  if (signal?.aborted)
62
56
  throw new AbortedError('[buildForFederation] After separate-node bundle');
63
57
  }
58
+ // 2. Shared mappings and exposed modules
59
+ const start = process.hrtime();
60
+ const artifactInfo = await bundleExposedAndMappings(config, fedOptions, externals, undefined, signal);
61
+ logger.measure(start, '[build artifacts] - To bundle all mappings and exposed.');
62
+ if (signal?.aborted)
63
+ throw new AbortedError('[buildForFederation] After exposed-and-mappings bundle');
64
+ const exposedInfo = !artifactInfo ? describeExposed(config, fedOptions) : artifactInfo.exposes;
64
65
  const sharedMappingInfo = !artifactInfo
65
66
  ? describeSharedMappings(config, fedOptions)
66
67
  : artifactInfo.mappings;
@@ -22,7 +22,6 @@ export async function bundleExposedAndMappings(config, fedOptions, externals, mo
22
22
  });
23
23
  const entryPoints = [...shared, ...exposes];
24
24
  const hash = !fedOptions.dev;
25
- logger.info('Building federation artifacts');
26
25
  let result;
27
26
  try {
28
27
  if (!modifiedFiles) {
@@ -5,7 +5,7 @@ export interface ExternalConfig {
5
5
  version?: string;
6
6
  includeSecondaries?: boolean;
7
7
  platform?: 'browser' | 'node';
8
- build?: 'default' | 'separate';
8
+ build?: 'default' | 'separate' | 'package';
9
9
  shareScope?: string;
10
10
  packageInfo?: {
11
11
  entryPoint: string;
@@ -1,2 +1,2 @@
1
- import { NormalizedFederationConfig } from '../domain/config/federation-config.contract.js';
1
+ import type { NormalizedFederationConfig } from '../domain/config/federation-config.contract.js';
2
2
  export declare function resolveProjectName(config: NormalizedFederationConfig): string;
@@ -1,6 +1,19 @@
1
+ export type TrackResult<T = never> = {
2
+ type: 'completed';
3
+ result: {
4
+ success: boolean;
5
+ cancelled?: boolean;
6
+ };
7
+ } | {
8
+ type: 'interrupted';
9
+ value: T;
10
+ };
1
11
  export declare class RebuildQueue {
2
12
  private activeBuilds;
3
13
  private buildCounter;
4
- enqueue(rebuildFn: (signal: AbortSignal) => Promise<void>): Promise<void>;
14
+ track<T = never>(rebuildFn: (signal: AbortSignal) => Promise<{
15
+ success: boolean;
16
+ cancelled?: boolean;
17
+ }>, interruptPromise?: Promise<T>): Promise<TrackResult<T>>;
5
18
  dispose(): void;
6
19
  }
@@ -2,41 +2,61 @@ import { logger } from './logger.js';
2
2
  export class RebuildQueue {
3
3
  activeBuilds = new Map();
4
4
  buildCounter = 0;
5
- async enqueue(rebuildFn) {
5
+ async track(rebuildFn, interruptPromise) {
6
6
  const buildId = ++this.buildCounter;
7
- const pendingCancellations = Array.from(this.activeBuilds.values()).map(buildInfo => {
8
- buildInfo.controller.abort();
9
- return buildInfo.buildFinished.promise;
7
+ const pendingCancellations = Array.from(this.activeBuilds.values()).map(buildControl => {
8
+ buildControl.controller.abort();
9
+ return buildControl.buildFinished.promise;
10
10
  });
11
11
  if (pendingCancellations.length > 0) {
12
12
  logger.info(`Aborting ${pendingCancellations.length} bundling task(s)..`);
13
- }
14
- if (pendingCancellations.length > 0) {
15
13
  await Promise.all(pendingCancellations);
16
14
  }
17
- let buildFinished;
15
+ let resolveCompletion;
18
16
  const completionPromise = new Promise(resolve => {
19
- buildFinished = resolve;
17
+ resolveCompletion = resolve;
20
18
  });
21
19
  const control = {
22
20
  controller: new AbortController(),
23
21
  buildFinished: {
24
- resolve: buildFinished,
22
+ resolve: resolveCompletion,
25
23
  promise: completionPromise,
26
24
  },
27
25
  };
28
26
  this.activeBuilds.set(buildId, control);
27
+ const buildPromise = rebuildFn(control.controller.signal)
28
+ .then(result => ({ type: 'completed', result }))
29
+ .catch(error => ({ type: 'completed', result: { success: false, error } }));
30
+ let trackResult;
29
31
  try {
30
- await rebuildFn(control.controller.signal);
32
+ if (interruptPromise) {
33
+ const interruptResult = interruptPromise.then(value => ({
34
+ type: 'interrupted',
35
+ value,
36
+ }));
37
+ const raceResult = await Promise.race([buildPromise, interruptResult]);
38
+ if (raceResult.type === 'interrupted') {
39
+ control.controller.abort();
40
+ await buildPromise;
41
+ trackResult = raceResult;
42
+ }
43
+ else {
44
+ trackResult = raceResult;
45
+ }
46
+ }
47
+ else {
48
+ trackResult = await buildPromise;
49
+ }
31
50
  }
32
51
  finally {
33
52
  control.buildFinished.resolve();
34
53
  this.activeBuilds.delete(buildId);
35
54
  }
55
+ return trackResult;
36
56
  }
37
57
  dispose() {
38
- for (const [_, buildInfo] of this.activeBuilds) {
39
- buildInfo.controller.abort();
58
+ for (const buildControl of this.activeBuilds.values()) {
59
+ buildControl.controller.abort();
40
60
  }
41
61
  this.activeBuilds.clear();
42
62
  }