Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion libs/native-federation-core/src/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export { loadFederationConfig } from './lib/core/load-federation-config';
export { writeFederationInfo } from './lib/core/write-federation-info';
export { writeImportMap } from './lib/core/write-import-map';
export { MappedPath } from './lib/utils/mapped-paths';

export { RebuildQueue } from './lib/utils/rebuild-queue';
export {
findRootTsConfigJson,
share,
Expand All @@ -33,4 +33,5 @@ export {
} from './lib/core/federation-builder';
export * from './lib/utils/build-result-map';
export { hashFile } from './lib/utils/hash-file';
export * from './lib/utils/errors';
export { logger, setLogLevel } from './lib/utils/logger';
1 change: 1 addition & 0 deletions libs/native-federation-core/src/lib/core/build-adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export interface BuildAdapterOptions {
hash: boolean;
platform?: 'browser' | 'node';
optimizedMappings?: boolean;
signal?: AbortSignal;
}

export interface BuildResult {
Expand Down
25 changes: 24 additions & 1 deletion libs/native-federation-core/src/lib/core/build-for-federation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@ import { FederationOptions } from './federation-options';
import { writeFederationInfo } from './write-federation-info';
import { writeImportMap } from './write-import-map';
import { logger } from '../utils/logger';
import { AbortedError } from '../utils/errors';

export interface BuildParams {
skipMappingsAndExposed: boolean;
skipShared: boolean;
signal?: AbortSignal;
}

export const defaultBuildParams: BuildParams = {
Expand All @@ -35,19 +37,27 @@ export async function buildForFederation(
externals: string[],
buildParams = defaultBuildParams
): Promise<FederationInfo> {
const signal = buildParams.signal;

let artefactInfo: ArtefactInfo | undefined;

if (!buildParams.skipMappingsAndExposed) {
const start = process.hrtime();
artefactInfo = await bundleExposedAndMappings(
config,
fedOptions,
externals
externals,
signal
);
logger.measure(
start,
'[build artifacts] - To bundle all mappings and exposed.'
);

if (signal?.aborted)
throw new AbortedError(
'[buildForFederation] After exposed-and-mappings bundle'
);
}

const exposedInfo = !artefactInfo
Expand Down Expand Up @@ -77,6 +87,10 @@ export async function buildForFederation(
Object.keys(sharedBrowser).forEach((packageName) =>
cachedSharedPackages.add(packageName)
);
if (signal?.aborted)
throw new AbortedError(
'[buildForFederation] After shared-browser bundle'
);
}

if (Object.keys(sharedServer).length > 0) {
Expand All @@ -96,6 +110,8 @@ export async function buildForFederation(
Object.keys(sharedServer).forEach((packageName) =>
cachedSharedPackages.add(packageName)
);
if (signal?.aborted)
throw new AbortedError('[buildForFederation] After shared-node bundle');
}

if (Object.keys(separateBrowser).length > 0) {
Expand All @@ -115,6 +131,10 @@ export async function buildForFederation(
Object.keys(separateBrowser).forEach((packageName) =>
cachedSharedPackages.add(packageName)
);
if (signal?.aborted)
throw new AbortedError(
'[buildForFederation] After separate-browser bundle'
);
}

if (Object.keys(separateServer).length > 0) {
Expand All @@ -135,6 +155,9 @@ export async function buildForFederation(
cachedSharedPackages.add(packageName)
);
}

if (signal?.aborted)
throw new AbortedError('[buildForFederation] After separate-node bundle');
}

const sharedMappingInfo = !artefactInfo
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { bundle } from '../utils/build-utils';
import { logger } from '../utils/logger';
import { normalize } from '../utils/normalize';
import { FederationOptions } from './federation-options';
import { AbortedError } from '../utils/errors';

export interface ArtefactInfo {
mappings: SharedInfo[];
Expand All @@ -20,8 +21,15 @@ export interface ArtefactInfo {
export async function bundleExposedAndMappings(
config: NormalizedFederationConfig,
fedOptions: FederationOptions,
externals: string[]
externals: string[],
signal?: AbortSignal
): Promise<ArtefactInfo> {
if (signal?.aborted) {
throw new AbortedError(
'[bundle-exposed-and-mappings] Aborted before bundling'
);
}

const shared = config.sharedMappings.map((sm) => {
const entryPoint = sm.path;
const tmp = sm.key.replace(/[^A-Za-z0-9]/g, '_');
Expand Down Expand Up @@ -53,9 +61,17 @@ export async function bundleExposedAndMappings(
kind: 'mapping-or-exposed',
hash,
optimizedMappings: config.features.ignoreUnusedDeps,
signal,
});
if (signal?.aborted) {
throw new AbortedError(
'[bundle-exposed-and-mappings] Aborted after bundle'
);
}
} catch (error) {
logger.error('Error building federation artefacts');
if (!(error instanceof AbortedError)) {
logger.error('Error building federation artefacts');
}
throw error;
}

Expand Down
7 changes: 7 additions & 0 deletions libs/native-federation-core/src/lib/utils/errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export class AbortedError extends Error {
constructor(message: string) {
super(message);
this.name = 'AbortedError';
Object.setPrototypeOf(this, AbortedError.prototype);
}
}
46 changes: 46 additions & 0 deletions libs/native-federation-core/src/lib/utils/rebuild-queue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { logger } from './logger';

export class RebuildQueue {
private activeBuilds: Map<number, AbortController> = new Map();
private buildCounter = 0;

async enqueue(rebuildFn: () => Promise<void>): Promise<void> {
const buildId = ++this.buildCounter;

for (const [id, controller] of this.activeBuilds) {
controller.abort();
}
if (this.activeBuildCount > 0)
logger.debug(
`Aborted ${this.activeBuildCount} previous bundling task(s)`
);
this.activeBuilds.clear();

const controller = new AbortController();
this.activeBuilds.set(buildId, controller);

try {
await rebuildFn();
} finally {
this.activeBuilds.delete(buildId);
}
}

abort(): void {
for (const [_, controller] of this.activeBuilds) {
controller.abort();
}
this.activeBuilds.clear();
}

get signal(): AbortSignal | undefined {
if (this.activeBuilds.size === 0) return undefined;

const latestBuildId = Math.max(...this.activeBuilds.keys());
return this.activeBuilds.get(latestBuildId)?.signal;
}

get activeBuildCount(): number {
return this.activeBuilds.size;
}
}
67 changes: 54 additions & 13 deletions libs/native-federation/src/builders/build/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import {
logger,
setBuildAdapter,
setLogLevel,
RebuildQueue,
AbortedError,
} from '@softarc/native-federation/build';
import {
createAngularBuildAdapter,
Expand Down Expand Up @@ -373,8 +375,9 @@ export async function* runBuilder(
indexHtmlTransformer: transformIndexHtml(nfOptions),
});

const rebuildQueue = new RebuildQueue();

try {
// builderRun.output.subscribe(async (output) => {
for await (const output of builderRun) {
lastResult = output;

Expand All @@ -399,8 +402,29 @@ export async function* runBuilder(
// }

if (!first && (nfOptions.dev || watch)) {
setTimeout(async () => {
try {
rebuildQueue
.enqueue(async () => {
const signal = rebuildQueue.signal;
if (signal?.aborted) {
throw new AbortedError('Build canceled before starting');
}

await new Promise((resolve, reject) => {
const timeout = setTimeout(resolve, nfOptions.rebuildDelay);

if (signal) {
const abortHandler = () => {
clearTimeout(timeout);
reject(new AbortedError('[builder] During delay.'));
};
signal.addEventListener('abort', abortHandler, { once: true });
}
});

if (signal?.aborted) {
throw new AbortedError('[builder] Before federation build.');
}

const start = process.hrtime();
federationResult = await buildForFederation(
config,
Expand All @@ -409,9 +433,14 @@ export async function* runBuilder(
{
skipMappingsAndExposed: false,
skipShared: true,
signal,
}
);

if (signal?.aborted) {
throw new AbortedError('[builder] After federation build.');
}

if (hasLocales && localeFilter) {
translateFederationArtefacts(
i18n,
Expand All @@ -421,27 +450,39 @@ export async function* runBuilder(
);
}

if (signal?.aborted) {
throw new AbortedError(
'[builder] After federation translations.'
);
}

logger.info('Done!');

// Notifies about build completion
if (isLocalDevelopment) {
federationBuildNotifier.broadcastBuildCompletion();
}
logger.measure(start, 'To rebuild nf.');
} catch (error) {
logger.error('Federation rebuild failed!');

// Notifies about build failure
if (isLocalDevelopment) {
federationBuildNotifier.broadcastBuildError(error);
logger.measure(start, 'To rebuild the federation artifacts.');
})
.catch((error) => {
if (error instanceof AbortedError) {
logger.warn('Rebuild was canceled.');
if (options.verbose)
logger.warn('Cancellation point: ' + error?.message);
} else {
logger.error('Federation rebuild failed!');
if (options.verbose) console.error(error);
if (isLocalDevelopment) {
federationBuildNotifier.broadcastBuildError(error);
}
}
}
}, nfOptions.rebuildDelay);
});
}

first = false;
}
} finally {
rebuildQueue.abort();

if (isLocalDevelopment) {
federationBuildNotifier.stopEventServer();
}
Expand Down
Loading