@softarc/native-federation
Advanced tools
| 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; | ||
| }; |
| export {}; |
| 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", |
+3
-0
| 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'; |
+3
-0
| 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'; |
+1
-0
@@ -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; | ||
| } |
| export {}; |
| 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); | ||
| }, | ||
| }); |
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
101784
5.5%103
4.04%2441
3.52%