You're Invited:Meet the Socket Team at RSAC and BSidesSF 2026, March 23–26.RSVP
Socket
Book a DemoSign in
Socket

@softarc/native-federation

Package Overview
Dependencies
Maintainers
3
Versions
76
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@softarc/native-federation - npm Package Compare versions

Comparing version
4.0.0-RC5
to
4.0.0-RC6
+8
src/lib/core/federation-cache.d.ts
import type { FederationCache } from '../domain/core/federation-cache.contract.js';
import type { ChunkInfo, SharedInfo } from '../domain/core/federation-info.contract.js';
export declare function createFederationCache(cachePath: string): FederationCache<undefined>;
export declare function createFederationCache<TBundlerCache>(cachePath: string, bundlerCache: TBundlerCache): FederationCache<TBundlerCache>;
export declare function addExternalsToCache(cache: FederationCache, { externals, chunks }: {
externals: SharedInfo[];
chunks?: ChunkInfo;
}): void;
export function createFederationCache(cachePath, bundlerCache) {
return { externals: [], cachePath, bundlerCache };
}
export function addExternalsToCache(cache, { externals, chunks }) {
cache.externals.push(...externals);
if (chunks) {
if (!cache.chunks)
cache.chunks = {};
cache.chunks = { ...cache.chunks, ...chunks };
}
}
import type { FederationCache } from '../../domain.js';
import type { FederationOptions, NormalizedFederationOptions } from '../domain/core/federation-options.contract.js';
export declare function normalizeFederationOptions(options: FederationOptions): NormalizedFederationOptions<undefined>;
export declare function normalizeFederationOptions<TBundlerCache>(options: FederationOptions, cache: FederationCache<TBundlerCache>): NormalizedFederationOptions<TBundlerCache>;
import { getDefaultCachePath } from '../utils/cache-persistence.js';
import { createFederationCache } from './federation-cache.js';
export function normalizeFederationOptions(options, cache) {
const federationCache = cache ??
createFederationCache(getDefaultCachePath(options.workspaceRoot));
return {
...options,
federationCache,
};
}
import type { FederationInfo } from '../domain/core/federation-info.contract.js';
import type { NormalizedFederationOptions } from '../domain/core/federation-options.contract.js';
import type { NormalizedFederationConfig } from '../domain/config/federation-config.contract.js';
export declare function rebuildForFederation(config: NormalizedFederationConfig, fedOptions: NormalizedFederationOptions, externals: string[], modifiedFiles: string[], signal?: AbortSignal): Promise<FederationInfo>;
import { bundleExposedAndMappings, describeExposed, describeSharedMappings, } from './bundle-exposed-and-mappings.js';
import { writeFederationInfo } from './write-federation-info.js';
import { writeImportMap } from './write-import-map.js';
import { logger } from '../utils/logger.js';
import { AbortedError } from '../utils/errors.js';
export async function rebuildForFederation(config, fedOptions, externals, modifiedFiles, signal) {
const federationCache = fedOptions.federationCache;
const start = process.hrtime();
// Shared mappings and exposed modules
const artifactInfo = await bundleExposedAndMappings(config, fedOptions, externals, modifiedFiles, signal);
logger.measure(start, '[build artifacts] - To re-bundle all mappings and exposed.');
if (signal?.aborted)
throw new AbortedError('[buildForFederation] After exposed-and-mappings bundle');
const exposedInfo = !artifactInfo ? describeExposed(config, fedOptions) : artifactInfo.exposes;
const sharedMappingInfo = !artifactInfo
? describeSharedMappings(config, fedOptions)
: artifactInfo.mappings;
const sharedExternals = [...federationCache.externals, ...sharedMappingInfo];
const buildNotificationsEndpoint = fedOptions.buildNotifications?.enable && fedOptions.dev
? fedOptions.buildNotifications?.endpoint
: undefined;
const federationInfo = {
name: config.name,
shared: sharedExternals,
exposes: exposedInfo,
buildNotificationsEndpoint,
};
if (federationCache.chunks) {
federationInfo.chunks = federationCache.chunks;
}
if (artifactInfo?.chunks) {
federationInfo.chunks = { ...(federationInfo.chunks ?? {}), ...artifactInfo?.chunks };
}
writeFederationInfo(federationInfo, fedOptions);
writeImportMap(federationCache, fedOptions);
return federationInfo;
}
import type { ChunkInfo, SharedInfo } from './federation-info.contract.js';
export type FederationCache<TBundlerCache = unknown> = {
externals: SharedInfo[];
chunks?: ChunkInfo;
bundlerCache: TBundlerCache;
cachePath: string;
};
import type { NormalizedExternalConfig } from '../domain/config/external-config.contract.js';
import type { ChunkInfo, SharedInfo } from '../domain/core/federation-info.contract.js';
export declare const getDefaultCachePath: (workspaceRoot: string) => string;
export declare const getFilename: (title: string, dev?: boolean) => string;
export declare const getChecksum: (shared: Record<string, NormalizedExternalConfig>, dev: "1" | "0") => string;
export declare const cacheEntry: (pathToCache: string, fileName: string) => {
getMetadata: (checksum: string) => {
checksum: string;
externals: SharedInfo[];
chunks?: ChunkInfo;
files: string[];
} | undefined;
persist: (payload: {
checksum: string;
externals: SharedInfo[];
chunks?: ChunkInfo;
files: string[];
}) => void;
copyFiles: (fullOutputPath: string) => void;
clear: () => void;
};
import path from 'path';
import fs from 'fs';
import crypto from 'crypto';
import { logger } from './logger.js';
export const getDefaultCachePath = (workspaceRoot) => path.join(workspaceRoot, 'node_modules/.cache/native-federation');
export const getFilename = (title, dev) => {
const devSuffix = dev ? '-dev' : '';
return `${title}${devSuffix}.meta.json`;
};
export const getChecksum = (shared, dev) => {
const denseExternals = Object.keys(shared)
.sort()
.reduce((clean, external) => {
return (clean + ':' + external + (shared[external].version ? `@${shared[external].version}` : ''));
}, 'deps');
return crypto
.createHash('sha256')
.update(denseExternals + `:dev=${dev}`)
.digest('hex');
};
export const cacheEntry = (pathToCache, fileName) => ({
getMetadata: (checksum) => {
const metadataFile = path.join(pathToCache, fileName);
if (!fs.existsSync(pathToCache) || !fs.existsSync(metadataFile))
return undefined;
const cachedResult = JSON.parse(fs.readFileSync(metadataFile, 'utf-8'));
if (cachedResult.checksum !== checksum)
return undefined;
return cachedResult;
},
persist: (payload) => {
fs.writeFileSync(path.join(pathToCache, fileName), JSON.stringify(payload), 'utf-8');
},
copyFiles: (fullOutputPath) => {
const metadataFile = path.join(pathToCache, fileName);
if (!fs.existsSync(metadataFile))
throw new Error('Error copying artifacts to dist, metadata file could not be found.');
const cachedResult = JSON.parse(fs.readFileSync(metadataFile, 'utf-8'));
fs.mkdirSync(path.dirname(fullOutputPath), { recursive: true });
cachedResult.files.forEach(file => {
const cachedFile = path.join(pathToCache, file);
const distFileName = path.join(fullOutputPath, file);
if (fs.existsSync(cachedFile)) {
fs.copyFileSync(cachedFile, distFileName);
}
});
},
clear: () => {
const metadataFile = path.join(pathToCache, fileName);
if (!fs.existsSync(pathToCache)) {
fs.mkdirSync(pathToCache, { recursive: true });
logger.debug(`Creating cache folder '${pathToCache}' for '${fileName}'.`);
return;
}
if (!fs.existsSync(metadataFile))
return;
logger.debug(`Purging cached bundle '${metadataFile}'.`);
const cachedResult = JSON.parse(fs.readFileSync(metadataFile, 'utf-8'));
cachedResult.files.forEach(file => {
const cachedFile = path.join(pathToCache, file);
if (fs.existsSync(cachedFile))
fs.unlinkSync(cachedFile);
});
fs.unlinkSync(metadataFile);
},
});
+1
-1
{
"name": "@softarc/native-federation",
"version": "4.0.0-RC5",
"version": "4.0.0-RC6",
"type": "module",

@@ -5,0 +5,0 @@ "license": "MIT",

export { setBuildAdapter } from './lib/core/build-adapter.js';
export { buildForFederation } from './lib/core/build-for-federation.js';
export { rebuildForFederation } from './lib/core/rebuild-for-federation.js';
export { normalizeFederationOptions } from './lib/core/normalize-federation-options.js';
export { createFederationCache } from './lib/core/federation-cache.js';
export { bundleExposedAndMappings } from './lib/core/bundle-exposed-and-mappings.js';

@@ -4,0 +7,0 @@ export { getExternals } from './lib/core/get-externals.js';

export { setBuildAdapter } from './lib/core/build-adapter.js';
export { buildForFederation } from './lib/core/build-for-federation.js';
export { rebuildForFederation } from './lib/core/rebuild-for-federation.js';
export { normalizeFederationOptions } from './lib/core/normalize-federation-options.js';
export { createFederationCache } from './lib/core/federation-cache.js';
export { bundleExposedAndMappings } from './lib/core/bundle-exposed-and-mappings.js';

@@ -4,0 +7,0 @@ export { getExternals } from './lib/core/get-externals.js';

@@ -12,1 +12,2 @@ export * from './lib/utils/build-result-map.js';

export type { NormalizedFederationConfig } from './lib/domain/config/federation-config.contract.js';
export { getDefaultCachePath, getChecksum } from './lib/utils/cache-persistence.js';

@@ -9,1 +9,2 @@ export * from './lib/utils/build-result-map.js';

export { writeImportMap } from './lib/core/write-import-map.js';
export { getDefaultCachePath, getChecksum } from './lib/utils/cache-persistence.js';
import { logger } from '../utils/logger.js';
let _buildAdapter = async () => {
// TODO: add logger
logger.error('NF is missing a build adapter!');
return [];
};
let _buildAdapter = null;
export function setBuildAdapter(buildAdapter) {

@@ -11,3 +7,7 @@ _buildAdapter = buildAdapter;

export function getBuildAdapter() {
if (!_buildAdapter) {
logger.error('Please set a BuildAdapter!');
throw new Error('BuildAdapter not set');
}
return _buildAdapter;
}
import type { FederationInfo } from '../domain/core/federation-info.contract.js';
import type { FederationOptions } from '../domain/core/federation-options.contract.js';
import type { NormalizedFederationOptions } from '../domain/core/federation-options.contract.js';
import type { NormalizedFederationConfig } from '../domain/config/federation-config.contract.js';
import type { BuildParams } from '../domain/core/build-params.contract.js';
export declare const defaultBuildParams: BuildParams;
export declare function buildForFederation(config: NormalizedFederationConfig, fedOptions: FederationOptions, externals: string[], buildParams?: BuildParams): Promise<FederationInfo>;
export declare function buildForFederation(config: NormalizedFederationConfig, fedOptions: NormalizedFederationOptions, externals: string[], signal?: AbortSignal): Promise<FederationInfo>;

@@ -6,28 +6,22 @@ import { bundleExposedAndMappings, describeExposed, describeSharedMappings, } from './bundle-exposed-and-mappings.js';

import { logger } from '../utils/logger.js';
import { getCachePath } from './../utils/bundle-caching.js';
import { normalizePackageName } from '../utils/normalize.js';
import { AbortedError } from '../utils/errors.js';
import { resolveProjectName } from '../utils/config-utils.js';
export const defaultBuildParams = {
skipMappingsAndExposed: false,
skipShared: false,
};
const sharedCache = { externals: [] };
export async function buildForFederation(config, fedOptions, externals, buildParams = defaultBuildParams) {
const signal = buildParams.signal;
let artifactInfo;
if (!buildParams.skipMappingsAndExposed) {
const start = process.hrtime();
artifactInfo = await bundleExposedAndMappings(config, fedOptions, 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');
}
import { addExternalsToCache } from './federation-cache.js';
import path from 'path';
export async function buildForFederation(config, fedOptions, externals, signal) {
// 1. Caching
fedOptions.federationCache.cachePath = path.join(fedOptions.federationCache.cachePath, resolveProjectName(config));
const start = process.hrtime();
// 2. Shared mappings and exposed modules
const artifactInfo = await bundleExposedAndMappings(config, fedOptions, externals, undefined, 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 = !artifactInfo ? describeExposed(config, fedOptions) : artifactInfo.exposes;
const cacheProjectFolder = resolveProjectName(config);
const pathToCache = getCachePath(fedOptions.workspaceRoot, cacheProjectFolder);
if (!buildParams.skipShared && sharedCache.externals.length > 0) {
// 3. Externals
if (fedOptions.federationCache.externals.length > 0) {
logger.info('Checksum matched, re-using cached externals.');
}
if (!buildParams.skipShared && sharedCache.externals.length === 0) {
if (fedOptions.federationCache.externals.length === 0) {
const { sharedBrowser, sharedServer, separateBrowser, separateServer } = splitShared(config.shared);

@@ -37,5 +31,5 @@ if (Object.keys(sharedBrowser).length > 0) {

const start = process.hrtime();
const sharedPackageInfoBrowser = await bundleShared(sharedBrowser, config, fedOptions, externals, 'browser', { pathToCache, bundleName: 'browser-shared' });
const sharedPackageInfoBrowser = await bundleShared(sharedBrowser, config, fedOptions, externals, { platform: 'browser', bundleName: 'browser-shared' });
logger.measure(start, '[build artifacts] - To bundle all shared browser externals');
addToCache(sharedPackageInfoBrowser);
addExternalsToCache(fedOptions.federationCache, sharedPackageInfoBrowser);
if (signal?.aborted)

@@ -47,5 +41,5 @@ throw new AbortedError('[buildForFederation] After shared-browser bundle');

const start = process.hrtime();
const sharedPackageInfoServer = await bundleShared(sharedServer, config, fedOptions, externals, 'node', { pathToCache, bundleName: 'node-shared' });
const sharedPackageInfoServer = await bundleShared(sharedServer, config, fedOptions, externals, { platform: 'node', bundleName: 'node-shared' });
logger.measure(start, '[build artifacts] - To bundle all shared node externals');
addToCache(sharedPackageInfoServer);
addExternalsToCache(fedOptions.federationCache, sharedPackageInfoServer);
if (signal?.aborted)

@@ -57,5 +51,5 @@ throw new AbortedError('[buildForFederation] After shared-node bundle');

const start = process.hrtime();
const separatePackageInfoBrowser = await bundleSeparatePackages(separateBrowser, externals, config, fedOptions, 'browser', pathToCache);
const separatePackageInfoBrowser = await bundleSeparatePackages(separateBrowser, externals, config, fedOptions, { platform: 'browser' });
logger.measure(start, '[build artifacts] - To bundle all separate browser externals');
addToCache(separatePackageInfoBrowser);
addExternalsToCache(fedOptions.federationCache, separatePackageInfoBrowser);
if (signal?.aborted)

@@ -67,5 +61,5 @@ throw new AbortedError('[buildForFederation] After separate-browser bundle');

const start = process.hrtime();
const separatePackageInfoServer = await bundleSeparatePackages(separateServer, externals, config, fedOptions, 'node', pathToCache);
const separatePackageInfoServer = await bundleSeparatePackages(separateServer, externals, config, fedOptions, { platform: 'node' });
logger.measure(start, '[build artifacts] - To bundle all separate node externals');
addToCache(separatePackageInfoServer);
addExternalsToCache(fedOptions.federationCache, separatePackageInfoServer);
}

@@ -78,3 +72,3 @@ if (signal?.aborted)

: artifactInfo.mappings;
const sharedExternals = [...sharedCache.externals, ...sharedMappingInfo];
const sharedExternals = [...fedOptions.federationCache.externals, ...sharedMappingInfo];
if (config?.shareScope) {

@@ -95,4 +89,4 @@ Object.values(sharedExternals).forEach(external => {

};
if (sharedCache.chunks) {
federationInfo.chunks = sharedCache.chunks;
if (fedOptions.federationCache.chunks) {
federationInfo.chunks = fedOptions.federationCache.chunks;
}

@@ -103,13 +97,5 @@ if (artifactInfo?.chunks) {

writeFederationInfo(federationInfo, fedOptions);
writeImportMap(sharedCache, fedOptions);
writeImportMap(fedOptions.federationCache, fedOptions);
return federationInfo;
}
function addToCache({ externals, chunks }) {
sharedCache.externals.push(...externals);
if (chunks) {
if (!sharedCache.chunks)
sharedCache.chunks = {};
sharedCache.chunks = { ...sharedCache.chunks, ...chunks };
}
}
function inferPackageFromSecondary(secondary) {

@@ -122,3 +108,3 @@ const parts = secondary.split('/');

}
async function bundleSeparatePackages(separateBrowser, externals, config, fedOptions, platform, pathToCache) {
async function bundleSeparatePackages(separateBrowser, externals, config, fedOptions, buildOptions) {
const groupedByPackage = {};

@@ -133,3 +119,6 @@ for (const [key, shared] of Object.entries(separateBrowser)) {

const bundlePromises = Object.entries(groupedByPackage).map(async ([packageName, sharedGroup]) => {
return bundleShared(sharedGroup, config, fedOptions, externals.filter(e => !e.startsWith(packageName)), platform, { pathToCache, bundleName: `${platform}-${normalizePackageName(packageName)}` });
return bundleShared(sharedGroup, config, fedOptions, externals.filter(e => !e.startsWith(packageName)), {
platform: buildOptions.platform,
bundleName: `${buildOptions.platform}-${normalizePackageName(packageName)}`,
});
});

@@ -136,0 +125,0 @@ const buildResults = await Promise.all(bundlePromises);

import type { ArtifactInfo, ExposesInfo, SharedInfo } from '../domain/core/federation-info.contract.js';
import type { NormalizedFederationConfig } from '../domain/config/federation-config.contract.js';
import { type FederationOptions } from '../domain/core/federation-options.contract.js';
export declare function bundleExposedAndMappings(config: NormalizedFederationConfig, fedOptions: FederationOptions, externals: string[], signal?: AbortSignal): Promise<ArtifactInfo>;
export declare function describeExposed(config: NormalizedFederationConfig, options: FederationOptions): Array<ExposesInfo>;
export declare function describeSharedMappings(config: NormalizedFederationConfig, fedOptions: FederationOptions): Array<SharedInfo>;
import { type NormalizedFederationOptions } from '../domain/core/federation-options.contract.js';
export declare function bundleExposedAndMappings(config: NormalizedFederationConfig, fedOptions: NormalizedFederationOptions, externals: string[], modifiedFiles?: string[], signal?: AbortSignal): Promise<ArtifactInfo>;
export declare function describeExposed(config: NormalizedFederationConfig, options: NormalizedFederationOptions): Array<ExposesInfo>;
export declare function describeSharedMappings(config: NormalizedFederationConfig, fedOptions: NormalizedFederationOptions): Array<SharedInfo>;
import fs from 'fs';
import path from 'path';
import { createBuildResultMap, popFromResultMap } from '../utils/build-result-map.js';
import { bundle } from '../utils/build-utils.js';
import { logger } from '../utils/logger.js';

@@ -9,3 +8,4 @@ import { normalize } from '../utils/normalize.js';

import { rewriteChunkImports } from '../utils/rewrite-chunk-imports.js';
export async function bundleExposedAndMappings(config, fedOptions, externals, signal) {
import { getBuildAdapter } from './build-adapter.js';
export async function bundleExposedAndMappings(config, fedOptions, externals, modifiedFiles, signal) {
if (signal?.aborted) {

@@ -29,16 +29,23 @@ throw new AbortedError('[bundle-exposed-and-mappings] Aborted before bundling');

try {
result = await bundle({
entryPoints,
outdir: fedOptions.outputPath,
tsConfigPath: fedOptions.tsConfig,
external: externals,
dev: !!fedOptions.dev,
watch: fedOptions.watch,
mappedPaths: config.sharedMappings,
kind: 'mapping-or-exposed',
chunks: (typeof fedOptions.chunks === 'boolean' && fedOptions.chunks) ||
(typeof fedOptions.chunks === 'object' && !!fedOptions.chunks.enable),
hash,
optimizedMappings: config.features.ignoreUnusedDeps,
if (!modifiedFiles) {
await getBuildAdapter().setup({
entryPoints,
outdir: fedOptions.outputPath,
tsConfigPath: fedOptions.tsConfig,
external: externals,
dev: !!fedOptions.dev,
watch: fedOptions.watch,
mappedPaths: config.sharedMappings,
bundleName: 'mapping-or-exposed',
chunks: (typeof fedOptions.chunks === 'boolean' && fedOptions.chunks) ||
(typeof fedOptions.chunks === 'object' && !!fedOptions.chunks.enable),
hash,
optimizedMappings: config.features.ignoreUnusedDeps,
isNodeModules: false,
cache: fedOptions.federationCache,
});
}
result = await getBuildAdapter().build('mapping-or-exposed', {
signal,
files: modifiedFiles,
});

@@ -45,0 +52,0 @@ if (signal?.aborted) {

import type { NormalizedFederationConfig } from '../domain/config/federation-config.contract.js';
import type { SharedInfo } from '../domain/core/federation-info.contract.js';
import { type FederationOptions } from '../domain/core/federation-options.contract.js';
import { type NormalizedFederationOptions } from '../domain/core/federation-options.contract.js';
import type { NormalizedExternalConfig } from '../domain/config/external-config.contract.js';
export declare function bundleShared(sharedBundles: Record<string, NormalizedExternalConfig>, config: NormalizedFederationConfig, fedOptions: FederationOptions, externals: string[], platform: "browser" | "node" | undefined, buildOptions: {
pathToCache: string;
export declare function bundleShared(sharedBundles: Record<string, NormalizedExternalConfig>, config: NormalizedFederationConfig, fedOptions: NormalizedFederationOptions, externals: string[], buildOptions: {
platform: 'browser' | 'node';
bundleName: string;

@@ -8,0 +8,0 @@ }): Promise<{

import * as path from 'path';
import * as fs from 'fs';
import { bundle } from '../utils/build-utils.js';
import { getPackageInfo } from '../utils/package-info.js';

@@ -10,5 +9,6 @@ import { logger } from '../utils/logger.js';

import { toChunkImport } from '../domain/core/chunk.js';
import { cacheEntry, getChecksum, getFilename } from './../utils/bundle-caching.js';
import { cacheEntry, getChecksum, getFilename } from '../utils/cache-persistence.js';
import { fileURLToPath } from 'url';
export async function bundleShared(sharedBundles, config, fedOptions, externals, platform = 'browser', buildOptions) {
import { getBuildAdapter } from './build-adapter.js';
export async function bundleShared(sharedBundles, config, fedOptions, externals, buildOptions) {
const checksum = getChecksum(sharedBundles, fedOptions.dev ? '1' : '0');

@@ -18,3 +18,3 @@ const folder = fedOptions.packageJson

: fedOptions.workspaceRoot;
const bundleCache = cacheEntry(buildOptions.pathToCache, getFilename(buildOptions.bundleName, fedOptions.dev));
const bundleCache = cacheEntry(fedOptions.federationCache.cachePath, getFilename(buildOptions.bundleName, fedOptions.dev));
if (fedOptions?.cacheExternalArtifacts) {

@@ -51,25 +51,29 @@ const cacheMetadata = bundleCache.getMetadata(checksum);

const expectedResults = allEntryPoints.map(ep => path.join(fullOutputPath, ep.outName));
const entryPoints = allEntryPoints.filter(ep => !fs.existsSync(path.join(buildOptions.pathToCache, ep.outName)));
const entryPoints = allEntryPoints.filter(ep => !fs.existsSync(path.join(fedOptions.federationCache.cachePath, ep.outName)));
// If we build for the browser and don't remote unused deps from the shared config,
// we need to exclude typical node libs to avoid compilation issues
const useDefaultExternalList = platform === 'browser' && !config.features.ignoreUnusedDeps;
const useDefaultExternalList = buildOptions.platform === 'browser' && !config.features.ignoreUnusedDeps;
const additionalExternals = useDefaultExternalList ? DEFAULT_EXTERNAL_LIST : [];
let bundleResult = null;
try {
bundleResult = await bundle({
await getBuildAdapter().setup({
entryPoints,
tsConfigPath: fedOptions.tsConfig,
external: [...additionalExternals, ...externals],
outdir: buildOptions.pathToCache,
outdir: fedOptions.federationCache.cachePath,
mappedPaths: config.sharedMappings,
dev: fedOptions.dev,
kind: 'shared-package',
bundleName: buildOptions.bundleName,
isNodeModules: true,
hash: false,
chunks: (typeof fedOptions.chunks === 'boolean' && fedOptions.chunks) ||
(typeof fedOptions.chunks === 'object' && !!fedOptions.chunks.enable),
platform,
platform: buildOptions.platform,
optimizedMappings: config.features.ignoreUnusedDeps,
cache: fedOptions.federationCache,
});
bundleResult = await getBuildAdapter().build(buildOptions.bundleName);
await getBuildAdapter().dispose(buildOptions.bundleName);
const cachedFiles = bundleResult.map(br => path.basename(br.fileName));
rewriteImports(cachedFiles, buildOptions.pathToCache);
rewriteImports(cachedFiles, fedOptions.federationCache.cachePath);
}

@@ -76,0 +80,0 @@ catch (e) {

@@ -29,5 +29,2 @@ const NODE_PACKAGES = [

];
export const DEFAULT_EXTERNAL_LIST = NODE_PACKAGES.flatMap((p) => [
p,
'node:' + p,
]);
export const DEFAULT_EXTERNAL_LIST = NODE_PACKAGES.flatMap(p => [p, 'node:' + p]);

@@ -10,3 +10,3 @@ import type { FederationInfo } from '../domain/core/federation-info.contract.js';

declare function init(params: BuildHelperParams): Promise<void>;
declare function build(buildParams?: import("../../domain.js").BuildParams): Promise<void>;
declare function build(signal?: AbortSignal): Promise<void>;
export declare const federationBuilder: {

@@ -13,0 +13,0 @@ init: typeof init;

import { getConfigContext, usePackageJson, useWorkspace } from '../config/configuration-context.js';
import { setBuildAdapter } from './build-adapter.js';
import { buildForFederation, defaultBuildParams } from './build-for-federation.js';
import { buildForFederation } from './build-for-federation.js';
import { getExternals } from './get-externals.js';
import { loadFederationConfig } from './load-federation-config.js';
import { normalizeFederationOptions } from './normalize-federation-options.js';
let externals = [];

@@ -12,3 +13,3 @@ let config;

setBuildAdapter(params.adapter);
fedOptions = params.options;
fedOptions = normalizeFederationOptions(params.options);
useWorkspace(params.options.workspaceRoot);

@@ -20,4 +21,4 @@ usePackageJson(params.options.packageJson);

}
async function build(buildParams = defaultBuildParams) {
fedInfo = await buildForFederation(config, fedOptions, externals, buildParams);
async function build(signal) {
fedInfo = await buildForFederation(config, fedOptions, externals, signal);
}

@@ -24,0 +25,0 @@ export const federationBuilder = {

import type { MappedPath } from '../utils/mapped-path.contract.js';
export type NFBuildAdapter = (options: NFBuildAdapterOptions) => Promise<NFBuildAdapterResult[]>;
export type BuildKind = 'shared-package' | 'shared-mapping' | 'exposed' | 'mapping-or-exposed';
import type { FederationCache } from './federation-cache.contract.js';
export interface NFBuildAdapter {
setup(options: NFBuildAdapterOptions): Promise<void>;
build(name: string, opts?: {
files?: string[];
signal?: AbortSignal;
}): Promise<NFBuildAdapterResult[]>;
dispose(name?: string): Promise<void>;
}
export interface EntryPoint {

@@ -9,18 +16,17 @@ fileName: string;

}
export interface NFBuildAdapterOptions {
export interface NFBuildAdapterOptions<TBundlerCache = unknown> {
entryPoints: EntryPoint[];
tsConfigPath?: string;
external: Array<string>;
external: string[];
outdir: string;
mappedPaths: MappedPath[];
packageName?: string;
esm?: boolean;
bundleName: string;
isNodeModules: boolean;
dev?: boolean;
watch?: boolean;
chunks?: boolean;
kind: BuildKind;
hash: boolean;
platform?: 'browser' | 'node';
optimizedMappings?: boolean;
signal?: AbortSignal;
cache: FederationCache<TBundlerCache>;
}

@@ -27,0 +33,0 @@ export interface NFBuildAdapterResult {

import type { BuildNotificationOptions } from './build-notification-options.contract.js';
import type { FederationCache } from './federation-cache.contract.js';
export interface FederationOptions {

@@ -19,1 +20,4 @@ workspaceRoot: string;

}
export interface NormalizedFederationOptions<TBundlerCache = unknown> extends FederationOptions {
federationCache: FederationCache<TBundlerCache>;
}
export type { SharedInfo, FederationInfo, ExposesInfo, ArtifactInfo, ChunkInfo, } from './federation-info.contract.js';
export { type BuildNotificationOptions, BuildNotificationType, } from './build-notification-options.contract.js';
export type { FederationOptions } from './federation-options.contract.js';
export type { BuildKind, EntryPoint, NFBuildAdapterOptions, NFBuildAdapter, NFBuildAdapterResult, } from './build-adapter.contract.js';
export type { BuildParams } from './build-params.contract.js';
export type { FederationOptions, NormalizedFederationOptions, } from './federation-options.contract.js';
export type { EntryPoint, NFBuildAdapterOptions, NFBuildAdapter, NFBuildAdapterResult, } from './build-adapter.contract.js';
export { CHUNK_PREFIX, toChunkImport } from './chunk.js';
export type { FederationCache } from './federation-cache.contract.js';

@@ -18,9 +18,9 @@ import * as fs from 'fs';

entries
.filter((entry) => entry.isDirectory())
.forEach((entry) => search(path.join(dir, entry.name), segmentIndex + 1));
.filter(entry => entry.isDirectory())
.forEach(entry => search(path.join(dir, entry.name), segmentIndex + 1));
}
else {
entries
.filter((entry) => entry.name === segment)
.forEach((entry) => search(path.join(dir, entry.name), segmentIndex + 1));
.filter(entry => entry.name === segment)
.forEach(entry => search(path.join(dir, entry.name), segmentIndex + 1));
}

@@ -27,0 +27,0 @@ }

export interface BuildParams {
skipMappingsAndExposed: boolean;
skipShared: boolean;
signal?: AbortSignal;
}
import type { NFBuildAdapterOptions } from '../domain/core/build-adapter.contract.js';
export declare function bundle(options: NFBuildAdapterOptions): Promise<import("../domain/core/build-adapter.contract.js").NFBuildAdapterResult[]>;
import { getBuildAdapter } from '../core/build-adapter.js';
export async function bundle(options) {
const adapter = getBuildAdapter();
return await adapter(options);
}
import type { NormalizedExternalConfig } from '../domain/config/external-config.contract.js';
import type { ChunkInfo, SharedInfo } from '../domain/core/federation-info.contract.js';
export declare const getCachePath: (workspaceRoot: string, project: string) => string;
export declare const getFilename: (title: string, dev?: boolean) => string;
export declare const getChecksum: (shared: Record<string, NormalizedExternalConfig>, dev: "1" | "0") => string;
export declare const cacheEntry: (pathToCache: string, fileName: string) => {
getMetadata: (checksum: string) => {
checksum: string;
externals: SharedInfo[];
chunks?: ChunkInfo;
files: string[];
} | undefined;
persist: (payload: {
checksum: string;
externals: SharedInfo[];
chunks?: ChunkInfo;
files: string[];
}) => void;
copyFiles: (fullOutputPath: string) => void;
clear: () => void;
};
import path from 'path';
import fs from 'fs';
import crypto from 'crypto';
import { logger } from '../utils/logger.js';
export const getCachePath = (workspaceRoot, project) => path.join(workspaceRoot, 'node_modules/.cache/native-federation', project);
export const getFilename = (title, dev) => {
const devSuffix = dev ? '-dev' : '';
return `${title}${devSuffix}.meta.json`;
};
export const getChecksum = (shared, dev) => {
const denseExternals = Object.keys(shared)
.sort()
.reduce((clean, external) => {
return (clean + ':' + external + (shared[external].version ? `@${shared[external].version}` : ''));
}, 'deps');
return crypto
.createHash('sha256')
.update(denseExternals + `:dev=${dev}`)
.digest('hex');
};
export const cacheEntry = (pathToCache, fileName) => ({
getMetadata: (checksum) => {
const metadataFile = path.join(pathToCache, fileName);
if (!fs.existsSync(pathToCache) || !fs.existsSync(metadataFile))
return undefined;
const cachedResult = JSON.parse(fs.readFileSync(metadataFile, 'utf-8'));
if (cachedResult.checksum !== checksum)
return undefined;
return cachedResult;
},
persist: (payload) => {
fs.writeFileSync(path.join(pathToCache, fileName), JSON.stringify(payload), 'utf-8');
},
copyFiles: (fullOutputPath) => {
const metadataFile = path.join(pathToCache, fileName);
if (!fs.existsSync(metadataFile))
throw new Error('Error copying artifacts to dist, metadata file could not be found.');
const cachedResult = JSON.parse(fs.readFileSync(metadataFile, 'utf-8'));
fs.mkdirSync(path.dirname(fullOutputPath), { recursive: true });
cachedResult.files.forEach(file => {
const cachedFile = path.join(pathToCache, file);
const distFileName = path.join(fullOutputPath, file);
if (fs.existsSync(cachedFile)) {
fs.copyFileSync(cachedFile, distFileName);
}
});
},
clear: () => {
const metadataFile = path.join(pathToCache, fileName);
if (!fs.existsSync(pathToCache)) {
fs.mkdirSync(pathToCache, { recursive: true });
logger.debug(`Creating cache folder '${pathToCache}' for '${fileName}'.`);
return;
}
if (!fs.existsSync(metadataFile))
return;
logger.debug(`Purging cached bundle '${metadataFile}'.`);
const cachedResult = JSON.parse(fs.readFileSync(metadataFile, 'utf-8'));
cachedResult.files.forEach(file => {
const cachedFile = path.join(pathToCache, file);
if (fs.existsSync(cachedFile))
fs.unlinkSync(cachedFile);
});
fs.unlinkSync(metadataFile);
},
});