@softarc/native-federation 4.3.1 → 4.3.2
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/dist/internal.d.ts +3 -0
- package/dist/internal.js +20 -0
- package/dist/lib/config/with-native-federation.js +2 -1
- package/dist/lib/core/build/build-for-federation.d.ts +7 -0
- package/dist/lib/core/build/build-for-federation.js +32 -134
- package/dist/lib/core/build/bundle-shared.d.ts +7 -0
- package/dist/lib/core/build/bundle-shared.js +38 -7
- package/dist/lib/core/build/rebuild-for-federation.d.ts +8 -0
- package/dist/lib/core/build/rebuild-for-federation.js +26 -0
- package/dist/lib/core/build/resolve-shared-dirs.d.ts +16 -0
- package/dist/lib/core/build/resolve-shared-dirs.js +66 -0
- package/dist/lib/core/build/shared-bundle-plan.d.ts +22 -0
- package/dist/lib/core/build/shared-bundle-plan.js +70 -0
- package/dist/lib/core/build/synthesize-cjs-exports.d.ts +12 -0
- package/dist/lib/core/build/synthesize-cjs-exports.js +58 -0
- package/dist/lib/core/cache/cache-persistence.d.ts +2 -2
- package/dist/lib/core/cache/cache-persistence.js +7 -4
- package/dist/lib/core/federation-builder.d.ts +2 -1
- package/dist/lib/core/federation-builder.js +3 -0
- package/dist/lib/core/output/densify-externals.js +1 -0
- package/dist/lib/domain/config/federation-config.contract.d.ts +2 -0
- package/dist/lib/domain/utils/file-watcher.contract.d.ts +7 -1
- package/dist/lib/domain/utils/io-port.contract.d.ts +14 -3
- package/dist/lib/utils/file-watcher.d.ts +1 -1
- package/dist/lib/utils/file-watcher.js +23 -6
- package/dist/lib/utils/io/node-io-adapter.js +77 -21
- package/dist/lib/utils/package/cjs-named-exports.d.ts +21 -0
- package/dist/lib/utils/package/cjs-named-exports.js +36 -0
- package/dist/lib/utils/package/esm-detection.d.ts +13 -0
- package/dist/lib/utils/package/esm-detection.js +29 -0
- package/package.json +3 -3
package/dist/internal.d.ts
CHANGED
|
@@ -11,5 +11,8 @@ export { RebuildQueue, type TrackResult } from './lib/core/rebuild-queue.js';
|
|
|
11
11
|
export { writeImportMap } from './lib/core/output/write-import-map.js';
|
|
12
12
|
export { getDefaultCachePath, getChecksum } from './lib/core/cache/cache-persistence.js';
|
|
13
13
|
export { isESMExport, type ExportCondition, type ExportEntry, } from './lib/utils/package/package-info.js';
|
|
14
|
+
export { isCjsCandidate, classifyByExtension, hasEsmSyntax, type ModuleFormat, } from './lib/utils/package/esm-detection.js';
|
|
15
|
+
export { isIdentifierName, planCjsWrap, buildSyntheticCjsEntry, isEsmInteropError, } from './lib/utils/package/cjs-named-exports.js';
|
|
14
16
|
export type { NfFileWatcher, NfFileWatcherOptions, } from './lib/domain/utils/file-watcher.contract.js';
|
|
15
17
|
export { syncNfFileWatcher, createNfWatcher } from './lib/utils/file-watcher.js';
|
|
18
|
+
export { linkedSharedDirs } from './lib/core/build/resolve-shared-dirs.js';
|
package/dist/internal.js
CHANGED
|
@@ -8,15 +8,35 @@ import { getDefaultCachePath, getChecksum } from "./lib/core/cache/cache-persist
|
|
|
8
8
|
import {
|
|
9
9
|
isESMExport
|
|
10
10
|
} from "./lib/utils/package/package-info.js";
|
|
11
|
+
import {
|
|
12
|
+
isCjsCandidate,
|
|
13
|
+
classifyByExtension,
|
|
14
|
+
hasEsmSyntax
|
|
15
|
+
} from "./lib/utils/package/esm-detection.js";
|
|
16
|
+
import {
|
|
17
|
+
isIdentifierName,
|
|
18
|
+
planCjsWrap,
|
|
19
|
+
buildSyntheticCjsEntry,
|
|
20
|
+
isEsmInteropError
|
|
21
|
+
} from "./lib/utils/package/cjs-named-exports.js";
|
|
11
22
|
import { syncNfFileWatcher, createNfWatcher } from "./lib/utils/file-watcher.js";
|
|
23
|
+
import { linkedSharedDirs } from "./lib/core/build/resolve-shared-dirs.js";
|
|
12
24
|
export {
|
|
13
25
|
RebuildQueue,
|
|
26
|
+
buildSyntheticCjsEntry,
|
|
27
|
+
classifyByExtension,
|
|
14
28
|
createNfWatcher,
|
|
15
29
|
getChecksum,
|
|
16
30
|
getDefaultCachePath,
|
|
31
|
+
hasEsmSyntax,
|
|
17
32
|
hashFile,
|
|
33
|
+
isCjsCandidate,
|
|
18
34
|
isESMExport,
|
|
35
|
+
isEsmInteropError,
|
|
36
|
+
isIdentifierName,
|
|
37
|
+
linkedSharedDirs,
|
|
19
38
|
logger,
|
|
39
|
+
planCjsWrap,
|
|
20
40
|
setLogLevel,
|
|
21
41
|
syncNfFileWatcher,
|
|
22
42
|
writeImportMap
|
|
@@ -20,7 +20,8 @@ function withNativeFederation(config) {
|
|
|
20
20
|
ignoreUnusedDeps: config.features?.ignoreUnusedDeps ?? true,
|
|
21
21
|
denseChunking: config.features?.denseChunking ?? false,
|
|
22
22
|
denseExternals: config.features?.denseExternals ?? false,
|
|
23
|
-
integrityHashes: config.features?.integrityHashes ?? false
|
|
23
|
+
integrityHashes: config.features?.integrityHashes ?? false,
|
|
24
|
+
synthesizeCjsExports: config.features?.synthesizeCjsExports ?? true
|
|
24
25
|
},
|
|
25
26
|
...config.shareScope && { shareScope: config.shareScope }
|
|
26
27
|
};
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import type { FederationInfo } from '../../domain/core/federation-info.contract.js';
|
|
2
2
|
import type { NormalizedFederationOptions } from '../../domain/core/federation-options.contract.js';
|
|
3
3
|
import type { NormalizedFederationConfig } from '../../domain/config/federation-config.contract.js';
|
|
4
|
+
import { type SharedBundlePlan } from './shared-bundle-plan.js';
|
|
4
5
|
export declare function buildForFederation(config: NormalizedFederationConfig, fedOptions: NormalizedFederationOptions, externals: string[], signal?: AbortSignal): Promise<FederationInfo>;
|
|
6
|
+
/**
|
|
7
|
+
* Bundles shared/separate externals per plan and populates the federation cache.
|
|
8
|
+
* Shared bundles run sequentially (with signal checks); separate bundles run in
|
|
9
|
+
* parallel. Shared by the initial build and the watch rebuild.
|
|
10
|
+
*/
|
|
11
|
+
export declare function executeSharedBundlePlans(plans: SharedBundlePlan[], config: NormalizedFederationConfig, fedOptions: NormalizedFederationOptions, signal?: AbortSignal): Promise<void>;
|
|
@@ -8,9 +8,9 @@ import { densifyExternals } from "../output/densify-externals.js";
|
|
|
8
8
|
import { writeFederationInfo } from "../output/write-federation-info.js";
|
|
9
9
|
import { writeImportMap } from "../output/write-import-map.js";
|
|
10
10
|
import { logger } from "../../utils/logger.js";
|
|
11
|
-
import { inferPackageFromSecondary, normalizePackageName } from "../../utils/normalize.js";
|
|
12
11
|
import { AbortedError } from "../../utils/errors.js";
|
|
13
12
|
import { addExternalsToCache } from "../cache/federation-cache.js";
|
|
13
|
+
import { planSharedBundles } from "./shared-bundle-plan.js";
|
|
14
14
|
import path from "path";
|
|
15
15
|
async function buildForFederation(config, fedOptions, externals, signal) {
|
|
16
16
|
fedOptions.federationCache.cachePath = path.join(
|
|
@@ -19,66 +19,7 @@ async function buildForFederation(config, fedOptions, externals, signal) {
|
|
|
19
19
|
);
|
|
20
20
|
logger.info("Building federation artifacts");
|
|
21
21
|
logger.notice("Skip packages you don't want to share in your federation config");
|
|
22
|
-
|
|
23
|
-
config.shared
|
|
24
|
-
);
|
|
25
|
-
if (Object.keys(sharedBrowser).length > 0) {
|
|
26
|
-
logger.info(`Bundling external npm packages with bundle type 'browser-shared'`);
|
|
27
|
-
const start2 = process.hrtime();
|
|
28
|
-
const sharedPackageInfoBrowser = await bundleShared(
|
|
29
|
-
sharedBrowser,
|
|
30
|
-
config,
|
|
31
|
-
fedOptions,
|
|
32
|
-
externals,
|
|
33
|
-
{ platform: "browser", bundleName: "browser-shared", chunks: config.chunks }
|
|
34
|
-
);
|
|
35
|
-
logger.measure(start2, "Step 2.1) Bundling all shared browser externals");
|
|
36
|
-
addExternalsToCache(fedOptions.federationCache, sharedPackageInfoBrowser);
|
|
37
|
-
if (signal?.aborted) throw new AbortedError("[buildForFederation] After shared-browser bundle");
|
|
38
|
-
}
|
|
39
|
-
if (Object.keys(sharedServer).length > 0) {
|
|
40
|
-
logger.info(`Bundling external npm packages with bundle type 'server-shared'`);
|
|
41
|
-
const start2 = process.hrtime();
|
|
42
|
-
const sharedPackageInfoServer = await bundleShared(
|
|
43
|
-
sharedServer,
|
|
44
|
-
config,
|
|
45
|
-
fedOptions,
|
|
46
|
-
externals,
|
|
47
|
-
{ platform: "node", bundleName: "node-shared", chunks: config.chunks }
|
|
48
|
-
);
|
|
49
|
-
logger.measure(start2, "Step 2.1) Bundling all shared node externals");
|
|
50
|
-
addExternalsToCache(fedOptions.federationCache, sharedPackageInfoServer);
|
|
51
|
-
if (signal?.aborted) throw new AbortedError("[buildForFederation] After shared-node bundle");
|
|
52
|
-
}
|
|
53
|
-
if (Object.keys(separateBrowser).length > 0) {
|
|
54
|
-
logger.info(`Bundling external npm packages with bundle type 'browser-separate'`);
|
|
55
|
-
const start2 = process.hrtime();
|
|
56
|
-
const separatePackageInfoBrowser = await bundleSeparatePackages(
|
|
57
|
-
separateBrowser,
|
|
58
|
-
externals,
|
|
59
|
-
config,
|
|
60
|
-
fedOptions,
|
|
61
|
-
{ platform: "browser" }
|
|
62
|
-
);
|
|
63
|
-
logger.measure(start2, "Step 2.2) Bundling all separate browser external packages");
|
|
64
|
-
addExternalsToCache(fedOptions.federationCache, separatePackageInfoBrowser);
|
|
65
|
-
if (signal?.aborted)
|
|
66
|
-
throw new AbortedError("[buildForFederation] After separate-browser bundle");
|
|
67
|
-
}
|
|
68
|
-
if (Object.keys(separateServer).length > 0) {
|
|
69
|
-
logger.info(`Bundling external npm packages with bundle type 'node-separate'`);
|
|
70
|
-
const start2 = process.hrtime();
|
|
71
|
-
const separatePackageInfoServer = await bundleSeparatePackages(
|
|
72
|
-
separateServer,
|
|
73
|
-
externals,
|
|
74
|
-
config,
|
|
75
|
-
fedOptions,
|
|
76
|
-
{ platform: "node" }
|
|
77
|
-
);
|
|
78
|
-
logger.measure(start2, "Step 2.2) Bundling all separate node external packages");
|
|
79
|
-
addExternalsToCache(fedOptions.federationCache, separatePackageInfoServer);
|
|
80
|
-
}
|
|
81
|
-
if (signal?.aborted) throw new AbortedError("[buildForFederation] After separate-node bundle");
|
|
22
|
+
await executeSharedBundlePlans(planSharedBundles(config, externals), config, fedOptions, signal);
|
|
82
23
|
const start = process.hrtime();
|
|
83
24
|
const artifactInfo = await bundleExposedAndMappings(
|
|
84
25
|
config,
|
|
@@ -122,81 +63,38 @@ async function buildForFederation(config, fedOptions, externals, signal) {
|
|
|
122
63
|
writeImportMap(fedOptions.federationCache, fedOptions, federationInfo.integrity);
|
|
123
64
|
return federationInfo;
|
|
124
65
|
}
|
|
125
|
-
async function
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
}
|
|
135
|
-
|
|
66
|
+
async function executeSharedBundlePlans(plans, config, fedOptions, signal) {
|
|
67
|
+
for (const plan of plans.filter((p) => p.kind === "shared")) {
|
|
68
|
+
logger.info(`Bundling external npm packages with bundle type '${plan.bundleName}'`);
|
|
69
|
+
const start = process.hrtime();
|
|
70
|
+
const info = await bundleShared(plan.entries, config, fedOptions, plan.externals, {
|
|
71
|
+
platform: plan.platform,
|
|
72
|
+
bundleName: plan.bundleName,
|
|
73
|
+
chunks: plan.chunks
|
|
74
|
+
});
|
|
75
|
+
logger.measure(start, `Step 2.1) Bundling '${plan.bundleName}' externals`);
|
|
76
|
+
addExternalsToCache(fedOptions.federationCache, info);
|
|
77
|
+
if (signal?.aborted)
|
|
78
|
+
throw new AbortedError(`[buildForFederation] After ${plan.bundleName} bundle`);
|
|
136
79
|
}
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
fedOptions,
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
const buildResults = await Promise.all(bundlePromises);
|
|
153
|
-
return buildResults.reduce(
|
|
154
|
-
(acc, r) => {
|
|
155
|
-
let chunks = acc.chunks;
|
|
156
|
-
if (r.chunks) {
|
|
157
|
-
chunks = { ...acc.chunks ?? {}, ...r.chunks };
|
|
158
|
-
}
|
|
159
|
-
let integrity = acc.integrity;
|
|
160
|
-
if (r.integrity) {
|
|
161
|
-
integrity = { ...acc.integrity ?? {}, ...r.integrity };
|
|
162
|
-
}
|
|
163
|
-
return {
|
|
164
|
-
externals: [...acc.externals, ...r.externals],
|
|
165
|
-
chunks,
|
|
166
|
-
integrity
|
|
167
|
-
};
|
|
168
|
-
},
|
|
169
|
-
{ externals: [] }
|
|
170
|
-
);
|
|
171
|
-
}
|
|
172
|
-
function splitShared(shared) {
|
|
173
|
-
const sharedServer = {};
|
|
174
|
-
const sharedBrowser = {};
|
|
175
|
-
const separateBrowser = {};
|
|
176
|
-
const separateServer = {};
|
|
177
|
-
for (const key in shared) {
|
|
178
|
-
const obj = shared[key];
|
|
179
|
-
if (obj?.platform === "node") {
|
|
180
|
-
if (obj.build === "default") {
|
|
181
|
-
sharedServer[key] = obj;
|
|
182
|
-
} else {
|
|
183
|
-
separateServer[key] = obj;
|
|
184
|
-
}
|
|
185
|
-
} else if (obj?.platform === "browser") {
|
|
186
|
-
if (obj.build === "default") {
|
|
187
|
-
sharedBrowser[key] = obj;
|
|
188
|
-
} else {
|
|
189
|
-
separateBrowser[key] = obj;
|
|
190
|
-
}
|
|
191
|
-
}
|
|
80
|
+
const separatePlans = plans.filter((p) => p.kind === "separate");
|
|
81
|
+
if (separatePlans.length > 0) {
|
|
82
|
+
const start = process.hrtime();
|
|
83
|
+
const results = await Promise.all(
|
|
84
|
+
separatePlans.map(
|
|
85
|
+
(plan) => bundleShared(plan.entries, config, fedOptions, plan.externals, {
|
|
86
|
+
platform: plan.platform,
|
|
87
|
+
bundleName: plan.bundleName,
|
|
88
|
+
chunks: plan.chunks
|
|
89
|
+
})
|
|
90
|
+
)
|
|
91
|
+
);
|
|
92
|
+
logger.measure(start, "Step 2.2) Bundling all separate external packages");
|
|
93
|
+
for (const info of results) addExternalsToCache(fedOptions.federationCache, info);
|
|
94
|
+
if (signal?.aborted) throw new AbortedError("[buildForFederation] After separate bundle");
|
|
192
95
|
}
|
|
193
|
-
return {
|
|
194
|
-
sharedBrowser,
|
|
195
|
-
sharedServer,
|
|
196
|
-
separateBrowser,
|
|
197
|
-
separateServer
|
|
198
|
-
};
|
|
199
96
|
}
|
|
200
97
|
export {
|
|
201
|
-
buildForFederation
|
|
98
|
+
buildForFederation,
|
|
99
|
+
executeSharedBundlePlans
|
|
202
100
|
};
|
|
@@ -5,6 +5,7 @@ import type { HashPort, IoPort } from '../../domain/utils/io-port.contract.js';
|
|
|
5
5
|
import { type NormalizedFederationOptions } from '../../domain/core/federation-options.contract.js';
|
|
6
6
|
import type { NormalizedExternalConfig } from '../../domain/config/external-config.contract.js';
|
|
7
7
|
import type { NFBuildAdapter } from '../../domain/core/build-adapter.contract.js';
|
|
8
|
+
import { type ModuleEvaluator } from './synthesize-cjs-exports.js';
|
|
8
9
|
export declare function bundleShared(sharedBundles: Record<string, NormalizedExternalConfig>, config: NormalizedFederationConfig, fedOptions: NormalizedFederationOptions, externals: string[], buildOptions: {
|
|
9
10
|
platform: 'browser' | 'node';
|
|
10
11
|
bundleName: string;
|
|
@@ -18,6 +19,12 @@ interface BundleSharedDeps {
|
|
|
18
19
|
io: IoPort;
|
|
19
20
|
repo: PackageJsonRepository;
|
|
20
21
|
adapter: NFBuildAdapter;
|
|
22
|
+
/**
|
|
23
|
+
* Loads a module by absolute path at build time (Node `require`) and returns its
|
|
24
|
+
* runtime exports, from which a CJS external's named exports are then enumerated.
|
|
25
|
+
* Omitted → no named-export synthesis; externals stay default-only.
|
|
26
|
+
*/
|
|
27
|
+
evaluateModule?: ModuleEvaluator;
|
|
21
28
|
}
|
|
22
29
|
export declare function bundleSharedCore(deps: BundleSharedDeps, sharedBundles: Record<string, NormalizedExternalConfig>, config: NormalizedFederationConfig, fedOptions: NormalizedFederationOptions, externals: string[], buildOptions: {
|
|
23
30
|
platform: 'browser' | 'node';
|
|
@@ -9,12 +9,21 @@ import { DEFAULT_EXTERNAL_LIST } from "./default-external-list.js";
|
|
|
9
9
|
import { isSourceFile, transformChunkImports } from "./rewrite-chunk-imports.js";
|
|
10
10
|
import { toChunkImport } from "../../domain/core/chunk.js";
|
|
11
11
|
import { cacheEntryCore, getChecksumCore, getFilename } from "../cache/cache-persistence.js";
|
|
12
|
+
import { linkedContentSignals } from "./resolve-shared-dirs.js";
|
|
12
13
|
import { computeIntegrityMapCore } from "./compute-integrity.js";
|
|
13
14
|
import { fileURLToPath } from "url";
|
|
14
15
|
import { getBuildAdapter } from "./build-adapter.js";
|
|
16
|
+
import { synthesizeCjsNamedExportsEntry } from "./synthesize-cjs-exports.js";
|
|
17
|
+
import { createRequire } from "module";
|
|
15
18
|
async function bundleShared(sharedBundles, config, fedOptions, externals, buildOptions) {
|
|
19
|
+
const requireFromWorkspace = createRequire(path.join(fedOptions.workspaceRoot, "index.js"));
|
|
16
20
|
return bundleSharedCore(
|
|
17
|
-
{
|
|
21
|
+
{
|
|
22
|
+
io: nodeIo,
|
|
23
|
+
repo: sharedPackageJsonRepository,
|
|
24
|
+
adapter: getBuildAdapter(),
|
|
25
|
+
evaluateModule: (absPath) => requireFromWorkspace(absPath)
|
|
26
|
+
},
|
|
18
27
|
sharedBundles,
|
|
19
28
|
config,
|
|
20
29
|
fedOptions,
|
|
@@ -25,13 +34,21 @@ async function bundleShared(sharedBundles, config, fedOptions, externals, buildO
|
|
|
25
34
|
async function bundleSharedCore(deps, sharedBundles, config, fedOptions, externals, buildOptions) {
|
|
26
35
|
const builderPackageJson = readBuilderPackageJson(deps.io, fileURLToPath(import.meta.url));
|
|
27
36
|
const builderVersion = parseBuilderVersion(builderPackageJson);
|
|
37
|
+
const folder = fedOptions.packageJson ? path.dirname(fedOptions.packageJson) : fedOptions.workspaceRoot;
|
|
38
|
+
const contentSignals = linkedContentSignals(
|
|
39
|
+
Object.keys(sharedBundles),
|
|
40
|
+
folder,
|
|
41
|
+
deps.io,
|
|
42
|
+
deps.repo
|
|
43
|
+
);
|
|
28
44
|
const checksum = getChecksumCore(
|
|
29
45
|
deps.io,
|
|
30
46
|
sharedBundles,
|
|
31
47
|
fedOptions.dev ? "1" : "0",
|
|
32
|
-
builderVersion
|
|
48
|
+
builderVersion,
|
|
49
|
+
config.features.synthesizeCjsExports,
|
|
50
|
+
contentSignals
|
|
33
51
|
);
|
|
34
|
-
const folder = fedOptions.packageJson ? path.dirname(fedOptions.packageJson) : fedOptions.workspaceRoot;
|
|
35
52
|
const bundleCache = cacheEntryCore(
|
|
36
53
|
deps.io,
|
|
37
54
|
fedOptions.federationCache.cachePath,
|
|
@@ -67,8 +84,22 @@ async function bundleSharedCore(deps, sharedBundles, config, fedOptions, externa
|
|
|
67
84
|
const configState = `BUNDLER_CHUNKS;${builderVersion};${JSON.stringify(config)}`;
|
|
68
85
|
const entryPoints = packageInfos.map((pi) => {
|
|
69
86
|
const encName = pi.packageName.replace(/[^A-Za-z0-9]/g, "_");
|
|
70
|
-
const outName = createOutName(
|
|
71
|
-
|
|
87
|
+
const outName = createOutName(
|
|
88
|
+
deps.io,
|
|
89
|
+
pi,
|
|
90
|
+
configState,
|
|
91
|
+
fedOptions,
|
|
92
|
+
encName,
|
|
93
|
+
contentSignals[pi.packageName] ?? ""
|
|
94
|
+
);
|
|
95
|
+
const synthetic = deps.evaluateModule && config.features.synthesizeCjsExports ? synthesizeCjsNamedExportsEntry(
|
|
96
|
+
deps.io,
|
|
97
|
+
deps.evaluateModule,
|
|
98
|
+
pi,
|
|
99
|
+
fedOptions.federationCache.cachePath,
|
|
100
|
+
outName
|
|
101
|
+
) : null;
|
|
102
|
+
return { fileName: synthetic ?? pi.entryPoint, outName };
|
|
72
103
|
});
|
|
73
104
|
const fullOutputPath = path.join(fedOptions.workspaceRoot, fedOptions.outputPath);
|
|
74
105
|
const useDefaultExternalList = buildOptions.platform === "browser" && !config.features.ignoreUnusedDeps;
|
|
@@ -177,8 +208,8 @@ function applyRenames(bundleResult, entryPoints, renamed) {
|
|
|
177
208
|
if (next) ep.outName = next;
|
|
178
209
|
}
|
|
179
210
|
}
|
|
180
|
-
function createOutName(io, pi, configState, fedOptions, encName) {
|
|
181
|
-
const hashBase = pi.version + "_" + pi.entryPoint + "_" + configState;
|
|
211
|
+
function createOutName(io, pi, configState, fedOptions, encName, contentSignal = "") {
|
|
212
|
+
const hashBase = pi.version + "_" + pi.entryPoint + "_" + configState + (contentSignal ? "_" + contentSignal : "");
|
|
182
213
|
const hash = calcHashCore(io, hashBase);
|
|
183
214
|
const outName = fedOptions.dev ? `${encName}.${hash}-dev.js` : `${encName}.${hash}.js`;
|
|
184
215
|
return outName;
|
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import type { FederationInfo } from '../../domain/core/federation-info.contract.js';
|
|
2
2
|
import type { NormalizedFederationOptions } from '../../domain/core/federation-options.contract.js';
|
|
3
3
|
import type { NormalizedFederationConfig } from '../../domain/config/federation-config.contract.js';
|
|
4
|
+
import type { IoPort } from '../../domain/utils/io-port.contract.js';
|
|
5
|
+
import type { PackageJsonRepository } from '../../domain/utils/package-json.contract.js';
|
|
4
6
|
export declare function rebuildForFederation(config: NormalizedFederationConfig, fedOptions: NormalizedFederationOptions, externals: string[], modifiedFiles: string[], signal?: AbortSignal): Promise<FederationInfo>;
|
|
7
|
+
/**
|
|
8
|
+
* When a modified file belongs to a shared package (e.g. npm-linked dev dep),
|
|
9
|
+
* re-bundle the affected bundle(s); unchanged bundles hit their cache, so
|
|
10
|
+
* `federationCache.externals` regenerates cheaply. No package touched → no-op.
|
|
11
|
+
*/
|
|
12
|
+
export declare function rebuildAffectedExternals(config: NormalizedFederationConfig, fedOptions: NormalizedFederationOptions, externals: string[], modifiedFiles: string[], signal?: AbortSignal, io?: IoPort, repo?: PackageJsonRepository): Promise<void>;
|
|
@@ -7,8 +7,15 @@ import { writeFederationInfo } from "../output/write-federation-info.js";
|
|
|
7
7
|
import { writeImportMap } from "../output/write-import-map.js";
|
|
8
8
|
import { logger } from "../../utils/logger.js";
|
|
9
9
|
import { AbortedError } from "../../utils/errors.js";
|
|
10
|
+
import { planSharedBundles } from "./shared-bundle-plan.js";
|
|
11
|
+
import { executeSharedBundlePlans } from "./build-for-federation.js";
|
|
12
|
+
import { affectedSharedKeys, resolveSharedPackageDirs } from "./resolve-shared-dirs.js";
|
|
13
|
+
import { cacheEntryCore, getFilename } from "../cache/cache-persistence.js";
|
|
14
|
+
import { nodeIo } from "../../utils/io/node-io-adapter.js";
|
|
15
|
+
import { sharedPackageJsonRepository } from "../../utils/package/package-info.js";
|
|
10
16
|
async function rebuildForFederation(config, fedOptions, externals, modifiedFiles, signal) {
|
|
11
17
|
const federationCache = fedOptions.federationCache;
|
|
18
|
+
await rebuildAffectedExternals(config, fedOptions, externals, modifiedFiles, signal);
|
|
12
19
|
logger.info(`Re-bundling all internal libraries and exposed modules..'`);
|
|
13
20
|
const start = process.hrtime();
|
|
14
21
|
const artifactInfo = await bundleExposedAndMappings(
|
|
@@ -47,6 +54,25 @@ async function rebuildForFederation(config, fedOptions, externals, modifiedFiles
|
|
|
47
54
|
writeImportMap(federationCache, fedOptions, federationInfo.integrity);
|
|
48
55
|
return federationInfo;
|
|
49
56
|
}
|
|
57
|
+
async function rebuildAffectedExternals(config, fedOptions, externals, modifiedFiles, signal, io = nodeIo, repo = sharedPackageJsonRepository) {
|
|
58
|
+
if (modifiedFiles.length === 0) return;
|
|
59
|
+
const plans = planSharedBundles(config, externals);
|
|
60
|
+
if (plans.length === 0) return;
|
|
61
|
+
const dirs = resolveSharedPackageDirs(config, fedOptions, io, repo);
|
|
62
|
+
const affected = affectedSharedKeys(modifiedFiles, dirs, io);
|
|
63
|
+
const affectedPlans = plans.filter((p) => p.keys.some((k) => affected.has(k)));
|
|
64
|
+
if (affectedPlans.length === 0) return;
|
|
65
|
+
const federationCache = fedOptions.federationCache;
|
|
66
|
+
for (const plan of affectedPlans) {
|
|
67
|
+
logger.info(`Detected change in linked shared package(s); re-bundling '${plan.bundleName}'.`);
|
|
68
|
+
cacheEntryCore(io, federationCache.cachePath, getFilename(plan.bundleName, fedOptions.dev)).clear();
|
|
69
|
+
}
|
|
70
|
+
federationCache.externals = [];
|
|
71
|
+
federationCache.chunks = void 0;
|
|
72
|
+
federationCache.integrity = void 0;
|
|
73
|
+
await executeSharedBundlePlans(plans, config, fedOptions, signal);
|
|
74
|
+
}
|
|
50
75
|
export {
|
|
76
|
+
rebuildAffectedExternals,
|
|
51
77
|
rebuildForFederation
|
|
52
78
|
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { NormalizedFederationConfig } from '../../domain/config/federation-config.contract.js';
|
|
2
|
+
import type { NormalizedFederationOptions } from '../../domain/core/federation-options.contract.js';
|
|
3
|
+
import type { FileReaderPort } from '../../domain/utils/io-port.contract.js';
|
|
4
|
+
import type { PackageJsonRepository } from '../../domain/utils/package-json.contract.js';
|
|
5
|
+
/** Each `config.shared` key → its realpath'd package dir, so watcher, checksum, and
|
|
6
|
+
* file-mapping agree on one identity for symlinked (npm-linked) deps. */
|
|
7
|
+
export declare function resolveSharedPackageDirs(config: NormalizedFederationConfig, fedOptions: NormalizedFederationOptions, io?: FileReaderPort, repo?: PackageJsonRepository): Map<string, string>;
|
|
8
|
+
/** Realpath'd dirs of symlinked shared packages — the bounded watch set.
|
|
9
|
+
* Deduped, since secondaries share a package dir. */
|
|
10
|
+
export declare function linkedSharedDirs(config: NormalizedFederationConfig, fedOptions: NormalizedFederationOptions, io?: FileReaderPort, repo?: PackageJsonRepository): string[];
|
|
11
|
+
/** Per-key content signal (max mtime of the resolved dir) for symlinked deps only.
|
|
12
|
+
* Registry deps get no signal, keeping their checksum version-only. (Every key is
|
|
13
|
+
* still resolved: detecting the symlink requires the realpath + lstat.) */
|
|
14
|
+
export declare function linkedContentSignals(keys: string[], folder: string, io?: FileReaderPort, repo?: PackageJsonRepository): Record<string, string>;
|
|
15
|
+
/** `config.shared` keys whose package directory contains at least one modified file. */
|
|
16
|
+
export declare function affectedSharedKeys(modifiedFiles: readonly string[], dirs: Map<string, string>, io?: FileReaderPort): Set<string>;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import * as path from "path";
|
|
2
|
+
import { nodeIo } from "../../utils/io/node-io-adapter.js";
|
|
3
|
+
import { sharedPackageJsonRepository } from "../../utils/package/package-info.js";
|
|
4
|
+
import { toPosix } from "../../utils/path-patterns.js";
|
|
5
|
+
const folderOf = (fedOptions) => fedOptions.packageJson ? path.dirname(fedOptions.packageJson) : fedOptions.workspaceRoot;
|
|
6
|
+
function resolveEntries(keys, folder, io, repo) {
|
|
7
|
+
const out = [];
|
|
8
|
+
for (const key of keys) {
|
|
9
|
+
const pkgJsonPath = repo.findDepPackageJson(key, folder);
|
|
10
|
+
if (!pkgJsonPath) continue;
|
|
11
|
+
const pkgDir = path.dirname(pkgJsonPath);
|
|
12
|
+
out.push({
|
|
13
|
+
key,
|
|
14
|
+
realDir: toPosix(io.realpath(pkgDir)),
|
|
15
|
+
isSymlink: !!io.stat(pkgDir)?.isSymbolicLink
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
return out;
|
|
19
|
+
}
|
|
20
|
+
function resolveSharedPackageDirs(config, fedOptions, io = nodeIo, repo = sharedPackageJsonRepository) {
|
|
21
|
+
const entries = resolveEntries(Object.keys(config.shared), folderOf(fedOptions), io, repo);
|
|
22
|
+
return new Map(entries.map((e) => [e.key, e.realDir]));
|
|
23
|
+
}
|
|
24
|
+
function linkedSharedDirs(config, fedOptions, io = nodeIo, repo = sharedPackageJsonRepository) {
|
|
25
|
+
const entries = resolveEntries(Object.keys(config.shared), folderOf(fedOptions), io, repo);
|
|
26
|
+
return [...new Set(entries.filter((e) => e.isSymlink).map((e) => e.realDir))];
|
|
27
|
+
}
|
|
28
|
+
function maxMtime(io, dir) {
|
|
29
|
+
let max = 0;
|
|
30
|
+
const walk = (d) => {
|
|
31
|
+
for (const name of io.readDir(d)) {
|
|
32
|
+
const full = path.join(d, name);
|
|
33
|
+
if (io.isDirectory(full)) walk(full);
|
|
34
|
+
else {
|
|
35
|
+
let s = io.stat(full);
|
|
36
|
+
if (s?.isSymbolicLink) s = io.stat(io.realpath(full));
|
|
37
|
+
if (s && s.mtimeMs > max) max = s.mtimeMs;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
walk(dir);
|
|
42
|
+
return max;
|
|
43
|
+
}
|
|
44
|
+
function linkedContentSignals(keys, folder, io = nodeIo, repo = sharedPackageJsonRepository) {
|
|
45
|
+
const signals = {};
|
|
46
|
+
for (const entry of resolveEntries(keys, folder, io, repo)) {
|
|
47
|
+
if (entry.isSymlink) signals[entry.key] = String(maxMtime(io, entry.realDir));
|
|
48
|
+
}
|
|
49
|
+
return signals;
|
|
50
|
+
}
|
|
51
|
+
function affectedSharedKeys(modifiedFiles, dirs, io = nodeIo) {
|
|
52
|
+
const affected = /* @__PURE__ */ new Set();
|
|
53
|
+
if (modifiedFiles.length === 0 || dirs.size === 0) return affected;
|
|
54
|
+
const realFiles = modifiedFiles.map((f) => toPosix(io.realpath(f)));
|
|
55
|
+
for (const [key, dir] of dirs) {
|
|
56
|
+
const prefix = dir.endsWith("/") ? dir : dir + "/";
|
|
57
|
+
if (realFiles.some((f) => f === dir || f.startsWith(prefix))) affected.add(key);
|
|
58
|
+
}
|
|
59
|
+
return affected;
|
|
60
|
+
}
|
|
61
|
+
export {
|
|
62
|
+
affectedSharedKeys,
|
|
63
|
+
linkedContentSignals,
|
|
64
|
+
linkedSharedDirs,
|
|
65
|
+
resolveSharedPackageDirs
|
|
66
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { NormalizedFederationConfig } from '../../domain/config/federation-config.contract.js';
|
|
2
|
+
import type { NormalizedExternalConfig } from '../../domain/config/external-config.contract.js';
|
|
3
|
+
export interface SharedBundlePlan {
|
|
4
|
+
bundleName: string;
|
|
5
|
+
platform: 'browser' | 'node';
|
|
6
|
+
chunks: boolean;
|
|
7
|
+
entries: Record<string, NormalizedExternalConfig>;
|
|
8
|
+
externals: string[];
|
|
9
|
+
keys: string[];
|
|
10
|
+
kind: 'shared' | 'separate';
|
|
11
|
+
}
|
|
12
|
+
type SplitSharedResult = {
|
|
13
|
+
sharedServer: Record<string, NormalizedExternalConfig>;
|
|
14
|
+
sharedBrowser: Record<string, NormalizedExternalConfig>;
|
|
15
|
+
separateBrowser: Record<string, NormalizedExternalConfig>;
|
|
16
|
+
separateServer: Record<string, NormalizedExternalConfig>;
|
|
17
|
+
};
|
|
18
|
+
export declare function splitShared(shared: Record<string, NormalizedExternalConfig>): SplitSharedResult;
|
|
19
|
+
/** Single source of truth for package → bundle(name) mapping, shared by the initial
|
|
20
|
+
* build and the watch rebuild so both target the same cache entries. */
|
|
21
|
+
export declare function planSharedBundles(config: NormalizedFederationConfig, externals: string[]): SharedBundlePlan[];
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { inferPackageFromSecondary, normalizePackageName } from "../../utils/normalize.js";
|
|
2
|
+
function splitShared(shared) {
|
|
3
|
+
const sharedServer = {};
|
|
4
|
+
const sharedBrowser = {};
|
|
5
|
+
const separateBrowser = {};
|
|
6
|
+
const separateServer = {};
|
|
7
|
+
for (const key in shared) {
|
|
8
|
+
const obj = shared[key];
|
|
9
|
+
if (obj?.platform === "node") {
|
|
10
|
+
if (obj.build === "default") sharedServer[key] = obj;
|
|
11
|
+
else separateServer[key] = obj;
|
|
12
|
+
} else if (obj?.platform === "browser") {
|
|
13
|
+
if (obj.build === "default") sharedBrowser[key] = obj;
|
|
14
|
+
else separateBrowser[key] = obj;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return { sharedBrowser, sharedServer, separateBrowser, separateServer };
|
|
18
|
+
}
|
|
19
|
+
function planSeparate(separate, platform, externals) {
|
|
20
|
+
const grouped = {};
|
|
21
|
+
for (const [key, shared] of Object.entries(separate)) {
|
|
22
|
+
const packageName = shared.build === "separate" ? key : inferPackageFromSecondary(key);
|
|
23
|
+
if (!grouped[packageName]) grouped[packageName] = { chunks: shared.chunks, entries: {} };
|
|
24
|
+
grouped[packageName].entries[key] = shared;
|
|
25
|
+
}
|
|
26
|
+
return Object.entries(grouped).map(([packageName, group]) => ({
|
|
27
|
+
bundleName: `${platform}-${normalizePackageName(packageName)}`,
|
|
28
|
+
platform,
|
|
29
|
+
chunks: group.chunks,
|
|
30
|
+
entries: group.entries,
|
|
31
|
+
externals: externals.filter((e) => !e.startsWith(packageName)),
|
|
32
|
+
keys: Object.keys(group.entries),
|
|
33
|
+
kind: "separate"
|
|
34
|
+
}));
|
|
35
|
+
}
|
|
36
|
+
function planSharedBundles(config, externals) {
|
|
37
|
+
const { sharedBrowser, sharedServer, separateBrowser, separateServer } = splitShared(
|
|
38
|
+
config.shared
|
|
39
|
+
);
|
|
40
|
+
const plans = [];
|
|
41
|
+
if (Object.keys(sharedBrowser).length > 0) {
|
|
42
|
+
plans.push({
|
|
43
|
+
bundleName: "browser-shared",
|
|
44
|
+
platform: "browser",
|
|
45
|
+
chunks: config.chunks,
|
|
46
|
+
entries: sharedBrowser,
|
|
47
|
+
externals,
|
|
48
|
+
keys: Object.keys(sharedBrowser),
|
|
49
|
+
kind: "shared"
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
if (Object.keys(sharedServer).length > 0) {
|
|
53
|
+
plans.push({
|
|
54
|
+
bundleName: "node-shared",
|
|
55
|
+
platform: "node",
|
|
56
|
+
chunks: config.chunks,
|
|
57
|
+
entries: sharedServer,
|
|
58
|
+
externals,
|
|
59
|
+
keys: Object.keys(sharedServer),
|
|
60
|
+
kind: "shared"
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
plans.push(...planSeparate(separateBrowser, "browser", externals));
|
|
64
|
+
plans.push(...planSeparate(separateServer, "node", externals));
|
|
65
|
+
return plans;
|
|
66
|
+
}
|
|
67
|
+
export {
|
|
68
|
+
planSharedBundles,
|
|
69
|
+
splitShared
|
|
70
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { IoPort } from '../../domain/utils/io-port.contract.js';
|
|
2
|
+
import type { PackageInfo } from '../../domain/utils/package-json.contract.js';
|
|
3
|
+
/** Evaluates a module at build time (Node `require`) and returns its value, or throws. */
|
|
4
|
+
export type ModuleEvaluator = (absPath: string) => unknown;
|
|
5
|
+
/**
|
|
6
|
+
* For a CommonJS shared external, `require()`s it, enumerates its runtime named
|
|
7
|
+
* exports, and writes a synthetic ESM entry re-exporting them.
|
|
8
|
+
*
|
|
9
|
+
* Returns the synthetic entry path, or `null` to keep the original entry (not a CJS
|
|
10
|
+
* candidate, no names to wrap, or a `require()` failure → default-only fallback).
|
|
11
|
+
*/
|
|
12
|
+
export declare function synthesizeCjsNamedExportsEntry(io: IoPort, evaluateModule: ModuleEvaluator, pi: PackageInfo, cachePath: string, outName: string): string | null;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import * as path from "path";
|
|
2
|
+
import { logger } from "../../utils/logger.js";
|
|
3
|
+
import { isCjsCandidate } from "../../utils/package/esm-detection.js";
|
|
4
|
+
import {
|
|
5
|
+
planCjsWrap,
|
|
6
|
+
buildSyntheticCjsEntry,
|
|
7
|
+
isEsmInteropError
|
|
8
|
+
} from "../../utils/package/cjs-named-exports.js";
|
|
9
|
+
const SYNTHETIC_DIR = ".nf-cjs-entries";
|
|
10
|
+
const warned = /* @__PURE__ */ new Set();
|
|
11
|
+
function nearestPackageType(io, fromDir) {
|
|
12
|
+
let dir = fromDir;
|
|
13
|
+
for (; ; ) {
|
|
14
|
+
const pkg = path.join(dir, "package.json");
|
|
15
|
+
if (io.exists(pkg)) {
|
|
16
|
+
try {
|
|
17
|
+
const type = JSON.parse(io.readText(pkg))?.type;
|
|
18
|
+
return type === "module" || type === "commonjs" ? type : void 0;
|
|
19
|
+
} catch {
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
const parent = path.dirname(dir);
|
|
23
|
+
if (parent === dir) return void 0;
|
|
24
|
+
dir = parent;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function synthesizeCjsNamedExportsEntry(io, evaluateModule, pi, cachePath, outName) {
|
|
28
|
+
const entryPoint = pi.entryPoint;
|
|
29
|
+
const candidate = isCjsCandidate({
|
|
30
|
+
esm: pi.esm,
|
|
31
|
+
entryPoint,
|
|
32
|
+
packageType: nearestPackageType(io, path.dirname(entryPoint)),
|
|
33
|
+
readSource: () => io.readText(entryPoint)
|
|
34
|
+
});
|
|
35
|
+
if (!candidate) return null;
|
|
36
|
+
let plan;
|
|
37
|
+
try {
|
|
38
|
+
plan = planCjsWrap(evaluateModule(entryPoint));
|
|
39
|
+
} catch (err) {
|
|
40
|
+
if (!isEsmInteropError(err) && !warned.has(entryPoint)) {
|
|
41
|
+
warned.add(entryPoint);
|
|
42
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
43
|
+
logger.warn(
|
|
44
|
+
`[native-federation] Could not enumerate named exports of "${entryPoint}" at build time (${detail}); falling back to default-only. Named imports from this package may fail across the module boundary.`
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
if (!plan.wrap) return null;
|
|
50
|
+
const dir = path.join(cachePath, SYNTHETIC_DIR);
|
|
51
|
+
io.mkdirp(dir);
|
|
52
|
+
const syntheticPath = path.join(dir, outName);
|
|
53
|
+
io.writeText(syntheticPath, buildSyntheticCjsEntry(entryPoint, plan.keys));
|
|
54
|
+
return syntheticPath;
|
|
55
|
+
}
|
|
56
|
+
export {
|
|
57
|
+
synthesizeCjsNamedExportsEntry
|
|
58
|
+
};
|
|
@@ -3,8 +3,8 @@ import type { ChunkInfo, IntegrityMap, SharedInfo } from '../../domain/core/fede
|
|
|
3
3
|
import type { FileReaderPort, FileWriterPort, HashPort } from '../../domain/utils/io-port.contract.js';
|
|
4
4
|
export declare const getDefaultCachePath: (workspaceRoot: string) => string;
|
|
5
5
|
export declare const getFilename: (title: string, dev?: boolean) => string;
|
|
6
|
-
export declare const getChecksum: (shared: Record<string, NormalizedExternalConfig>, dev: "1" | "0", builderVersion?: string) => string;
|
|
7
|
-
export declare const getChecksumCore: (hash: HashPort, shared: Record<string, NormalizedExternalConfig>, dev: "1" | "0", builderVersion?: string) => string;
|
|
6
|
+
export declare const getChecksum: (shared: Record<string, NormalizedExternalConfig>, dev: "1" | "0", builderVersion?: string, synthesizeCjsExports?: boolean, contentSignals?: Record<string, string>) => string;
|
|
7
|
+
export declare const getChecksumCore: (hash: HashPort, shared: Record<string, NormalizedExternalConfig>, dev: "1" | "0", builderVersion?: string, synthesizeCjsExports?: boolean, contentSignals?: Record<string, string>) => string;
|
|
8
8
|
export type CacheMetadata = {
|
|
9
9
|
checksum: string;
|
|
10
10
|
externals: SharedInfo[];
|
|
@@ -6,12 +6,15 @@ const getFilename = (title, dev) => {
|
|
|
6
6
|
const devSuffix = dev ? "-dev" : "";
|
|
7
7
|
return `${title}${devSuffix}.meta.json`;
|
|
8
8
|
};
|
|
9
|
-
const getChecksum = (shared, dev, builderVersion = "") => getChecksumCore(nodeIo, shared, dev, builderVersion);
|
|
10
|
-
const getChecksumCore = (hash, shared, dev, builderVersion = "") => {
|
|
9
|
+
const getChecksum = (shared, dev, builderVersion = "", synthesizeCjsExports = true, contentSignals = {}) => getChecksumCore(nodeIo, shared, dev, builderVersion, synthesizeCjsExports, contentSignals);
|
|
10
|
+
const getChecksumCore = (hash, shared, dev, builderVersion = "", synthesizeCjsExports = true, contentSignals = {}) => {
|
|
11
11
|
const denseExternals = Object.keys(shared).sort().reduce((clean, external) => {
|
|
12
|
-
|
|
12
|
+
const version = shared[external].version ? `@${shared[external].version}` : "";
|
|
13
|
+
const signal = contentSignals[external] ? `#${contentSignals[external]}` : "";
|
|
14
|
+
return clean + ":" + external + version + signal;
|
|
13
15
|
}, "deps");
|
|
14
|
-
|
|
16
|
+
const cjs = synthesizeCjsExports ? "1" : "0";
|
|
17
|
+
return hash.hash("sha256", denseExternals + `:dev=${dev}:builder=${builderVersion}:cjs=${cjs}`).hex();
|
|
15
18
|
};
|
|
16
19
|
const cacheEntryCore = (io, pathToCache, fileName) => {
|
|
17
20
|
const metadataFile = path.join(pathToCache, fileName);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { FederationInfo } from '../domain/core/federation-info.contract.js';
|
|
2
2
|
import type { NormalizedFederationConfig } from '../domain/config/federation-config.contract.js';
|
|
3
|
-
import { type FederationOptions } from '../domain/core/federation-options.contract.js';
|
|
3
|
+
import { type FederationOptions, type NormalizedFederationOptions } from '../domain/core/federation-options.contract.js';
|
|
4
4
|
import type { NFBuildAdapter } from '../domain/core/build-adapter.contract.js';
|
|
5
5
|
export interface BuildHelperParams {
|
|
6
6
|
options: FederationOptions;
|
|
@@ -19,5 +19,6 @@ export declare const federationBuilder: {
|
|
|
19
19
|
readonly federationInfo: FederationInfo;
|
|
20
20
|
readonly externals: string[];
|
|
21
21
|
readonly config: NormalizedFederationConfig;
|
|
22
|
+
readonly options: NormalizedFederationOptions;
|
|
22
23
|
};
|
|
23
24
|
export {};
|
|
@@ -40,6 +40,7 @@ function densifyExternals(shared) {
|
|
|
40
40
|
if (entry.version !== void 0) dense.version = entry.version;
|
|
41
41
|
if (entry.shareScope !== void 0) dense.shareScope = entry.shareScope;
|
|
42
42
|
if (entry.bundle !== void 0) dense.bundle = entry.bundle;
|
|
43
|
+
if (entry.pool !== void 0) dense.pool = entry.pool;
|
|
43
44
|
if (entry.dev !== void 0) dense.dev = entry.dev;
|
|
44
45
|
groupIndex.set(key, result.length);
|
|
45
46
|
result.push(dense);
|
|
@@ -21,6 +21,7 @@ export interface FederationConfig {
|
|
|
21
21
|
denseChunking?: boolean;
|
|
22
22
|
denseExternals?: boolean;
|
|
23
23
|
integrityHashes?: boolean;
|
|
24
|
+
synthesizeCjsExports?: boolean;
|
|
24
25
|
};
|
|
25
26
|
}
|
|
26
27
|
export interface NormalizedFederationConfig {
|
|
@@ -39,5 +40,6 @@ export interface NormalizedFederationConfig {
|
|
|
39
40
|
denseChunking: boolean;
|
|
40
41
|
denseExternals: boolean;
|
|
41
42
|
integrityHashes: boolean;
|
|
43
|
+
synthesizeCjsExports: boolean;
|
|
42
44
|
};
|
|
43
45
|
}
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
export interface NfFileWatcherOptions {
|
|
2
2
|
onChange?: (path: string) => void;
|
|
3
|
+
pollIntervalMs?: number;
|
|
4
|
+
debounceMs?: number;
|
|
5
|
+
}
|
|
6
|
+
interface AddPathsOptions {
|
|
7
|
+
poll?: boolean;
|
|
3
8
|
}
|
|
4
9
|
export interface NfFileWatcher {
|
|
5
|
-
addPaths(paths: string | readonly string[]): void;
|
|
10
|
+
addPaths(paths: string | readonly string[], opts?: AddPathsOptions): void;
|
|
6
11
|
close(): Promise<void>;
|
|
7
12
|
get(): ReadonlySet<string>;
|
|
8
13
|
clear(): void;
|
|
9
14
|
mutate(fn: (dirtyPaths: Set<string>) => void): void;
|
|
10
15
|
}
|
|
16
|
+
export {};
|
|
@@ -3,6 +3,10 @@ export interface Digest {
|
|
|
3
3
|
hex(): string;
|
|
4
4
|
base64(): string;
|
|
5
5
|
}
|
|
6
|
+
export interface StatInfo {
|
|
7
|
+
mtimeMs: number;
|
|
8
|
+
isSymbolicLink: boolean;
|
|
9
|
+
}
|
|
6
10
|
export interface FileReaderPort {
|
|
7
11
|
readText(path: string): string;
|
|
8
12
|
readBytes(path: string): Uint8Array;
|
|
@@ -13,6 +17,8 @@ export interface FileReaderPort {
|
|
|
13
17
|
isDirectory(path: string): boolean;
|
|
14
18
|
/** Immediate child entry names (not full paths). Empty array on ENOENT, never throws. */
|
|
15
19
|
readDir(path: string): string[];
|
|
20
|
+
realpath(path: string): string;
|
|
21
|
+
stat(path: string): StatInfo | null;
|
|
16
22
|
}
|
|
17
23
|
export interface FileWriterPort {
|
|
18
24
|
writeText(path: string, data: string): void;
|
|
@@ -31,15 +37,20 @@ export interface HashPort {
|
|
|
31
37
|
export interface WatchHandle {
|
|
32
38
|
close(): void;
|
|
33
39
|
}
|
|
40
|
+
interface WatchOptions {
|
|
41
|
+
recursive: boolean;
|
|
42
|
+
poll?: {
|
|
43
|
+
intervalMs: number;
|
|
44
|
+
};
|
|
45
|
+
}
|
|
34
46
|
export interface WatchPort {
|
|
35
47
|
/**
|
|
36
48
|
* For a recursive directory watch `onEvent` receives the changed entry's
|
|
37
49
|
* filename relative to `path`; for a file it receives the path itself (or
|
|
38
50
|
* null when the platform omits it).
|
|
39
51
|
*/
|
|
40
|
-
watch(path: string, opts:
|
|
41
|
-
recursive: boolean;
|
|
42
|
-
}, onEvent: (filename: string | null) => void): WatchHandle;
|
|
52
|
+
watch(path: string, opts: WatchOptions, onEvent: (filename: string | null) => void): WatchHandle;
|
|
43
53
|
}
|
|
44
54
|
export interface IoPort extends FileReaderPort, FileWriterPort, GlobPort, HashPort, WatchPort {
|
|
45
55
|
}
|
|
56
|
+
export {};
|
|
@@ -4,4 +4,4 @@ export declare function createNfWatcher(options?: NfFileWatcherOptions): NfFileW
|
|
|
4
4
|
export declare function createNfWatcherCore(io: WatchPort & FileReaderPort, options?: NfFileWatcherOptions): NfFileWatcher;
|
|
5
5
|
export declare function syncNfFileWatcher(watcher: NfFileWatcher, bundlerCache: {
|
|
6
6
|
keys(): IterableIterator<string>;
|
|
7
|
-
}): void;
|
|
7
|
+
}, linkedDirs?: readonly string[]): void;
|
|
@@ -7,22 +7,37 @@ function createNfWatcher(options = {}) {
|
|
|
7
7
|
}
|
|
8
8
|
function createNfWatcherCore(io, options = {}) {
|
|
9
9
|
const { onChange } = options;
|
|
10
|
+
const pollIntervalMs = options.pollIntervalMs ?? 300;
|
|
11
|
+
const debounceMs = options.debounceMs ?? 0;
|
|
10
12
|
const watchers = /* @__PURE__ */ new Map();
|
|
11
13
|
const dirtyPaths = /* @__PURE__ */ new Set();
|
|
12
|
-
const
|
|
14
|
+
const deliver = (path) => {
|
|
13
15
|
if (onChange) onChange(path);
|
|
14
16
|
else dirtyPaths.add(path);
|
|
15
17
|
};
|
|
18
|
+
const pending = /* @__PURE__ */ new Set();
|
|
19
|
+
let flushTimer;
|
|
20
|
+
const flush = () => {
|
|
21
|
+
for (const p of pending) deliver(p);
|
|
22
|
+
pending.clear();
|
|
23
|
+
};
|
|
24
|
+
const notify = (path) => {
|
|
25
|
+
if (debounceMs <= 0) return deliver(path);
|
|
26
|
+
pending.add(path);
|
|
27
|
+
if (flushTimer) clearTimeout(flushTimer);
|
|
28
|
+
flushTimer = setTimeout(flush, debounceMs);
|
|
29
|
+
flushTimer.unref?.();
|
|
30
|
+
};
|
|
16
31
|
return {
|
|
17
|
-
addPaths(paths) {
|
|
32
|
+
addPaths(paths, opts) {
|
|
18
33
|
const list = typeof paths === "string" ? [paths] : [...paths];
|
|
34
|
+
const poll = opts?.poll ? { intervalMs: pollIntervalMs } : void 0;
|
|
19
35
|
for (const p of list) {
|
|
20
36
|
if (watchers.has(p)) continue;
|
|
21
37
|
try {
|
|
22
|
-
const
|
|
23
|
-
const handle = isDir ? io.watch(p, { recursive: true }, (filename) => {
|
|
38
|
+
const handle = io.isDirectory(p) ? io.watch(p, { recursive: true, poll }, (filename) => {
|
|
24
39
|
if (filename) notify(toPosix(join(p, filename)));
|
|
25
|
-
}) : io.watch(p, { recursive: false }, () => notify(toPosix(p)));
|
|
40
|
+
}) : io.watch(p, { recursive: false, poll }, () => notify(toPosix(p)));
|
|
26
41
|
watchers.set(p, handle);
|
|
27
42
|
} catch {
|
|
28
43
|
logger.debug(`Could not watch path '${p}'.`);
|
|
@@ -33,6 +48,7 @@ function createNfWatcherCore(io, options = {}) {
|
|
|
33
48
|
clear: () => dirtyPaths.clear(),
|
|
34
49
|
mutate: (fn) => fn(dirtyPaths),
|
|
35
50
|
async close() {
|
|
51
|
+
if (flushTimer) clearTimeout(flushTimer);
|
|
36
52
|
for (const handle of watchers.values()) {
|
|
37
53
|
handle.close();
|
|
38
54
|
}
|
|
@@ -40,9 +56,10 @@ function createNfWatcherCore(io, options = {}) {
|
|
|
40
56
|
}
|
|
41
57
|
};
|
|
42
58
|
}
|
|
43
|
-
function syncNfFileWatcher(watcher, bundlerCache) {
|
|
59
|
+
function syncNfFileWatcher(watcher, bundlerCache, linkedDirs = []) {
|
|
44
60
|
const files = [...bundlerCache.keys()].filter((k) => !k.includes("node_modules"));
|
|
45
61
|
if (files.length) watcher.addPaths(files);
|
|
62
|
+
if (linkedDirs.length) watcher.addPaths(linkedDirs, { poll: true });
|
|
46
63
|
}
|
|
47
64
|
export {
|
|
48
65
|
createNfWatcher,
|
|
@@ -1,48 +1,64 @@
|
|
|
1
1
|
import * as fs from "fs";
|
|
2
|
+
import * as path from "path";
|
|
2
3
|
import * as crypto from "crypto";
|
|
3
4
|
import fg from "fast-glob";
|
|
4
5
|
const nodeIo = {
|
|
5
|
-
readText(
|
|
6
|
-
return fs.readFileSync(
|
|
6
|
+
readText(path2) {
|
|
7
|
+
return fs.readFileSync(path2, "utf-8");
|
|
7
8
|
},
|
|
8
|
-
readBytes(
|
|
9
|
-
return fs.readFileSync(
|
|
9
|
+
readBytes(path2) {
|
|
10
|
+
return fs.readFileSync(path2);
|
|
10
11
|
},
|
|
11
|
-
exists(
|
|
12
|
-
return fs.existsSync(
|
|
12
|
+
exists(path2) {
|
|
13
|
+
return fs.existsSync(path2);
|
|
13
14
|
},
|
|
14
|
-
isFile(
|
|
15
|
+
isFile(path2) {
|
|
15
16
|
try {
|
|
16
|
-
return fs.statSync(
|
|
17
|
+
return fs.statSync(path2).isFile();
|
|
17
18
|
} catch {
|
|
18
19
|
return false;
|
|
19
20
|
}
|
|
20
21
|
},
|
|
21
|
-
isDirectory(
|
|
22
|
+
isDirectory(path2) {
|
|
22
23
|
try {
|
|
23
|
-
return fs.statSync(
|
|
24
|
+
return fs.statSync(path2).isDirectory();
|
|
24
25
|
} catch {
|
|
25
26
|
return false;
|
|
26
27
|
}
|
|
27
28
|
},
|
|
28
|
-
readDir(
|
|
29
|
+
readDir(path2) {
|
|
29
30
|
try {
|
|
30
|
-
return fs.readdirSync(
|
|
31
|
+
return fs.readdirSync(path2);
|
|
31
32
|
} catch {
|
|
32
33
|
return [];
|
|
33
34
|
}
|
|
34
35
|
},
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
realpath(path2) {
|
|
37
|
+
try {
|
|
38
|
+
return fs.realpathSync(path2);
|
|
39
|
+
} catch {
|
|
40
|
+
return path2;
|
|
41
|
+
}
|
|
37
42
|
},
|
|
38
|
-
|
|
39
|
-
|
|
43
|
+
stat(path2) {
|
|
44
|
+
try {
|
|
45
|
+
const s = fs.lstatSync(path2);
|
|
46
|
+
return { mtimeMs: s.mtimeMs, isSymbolicLink: s.isSymbolicLink() };
|
|
47
|
+
} catch {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
writeText(path2, data) {
|
|
52
|
+
fs.writeFileSync(path2, data, "utf-8");
|
|
53
|
+
},
|
|
54
|
+
mkdirp(path2) {
|
|
55
|
+
fs.mkdirSync(path2, { recursive: true });
|
|
40
56
|
},
|
|
41
57
|
copyFile(from, to) {
|
|
42
58
|
fs.copyFileSync(from, to);
|
|
43
59
|
},
|
|
44
|
-
remove(
|
|
45
|
-
fs.unlinkSync(
|
|
60
|
+
remove(path2) {
|
|
61
|
+
fs.unlinkSync(path2);
|
|
46
62
|
},
|
|
47
63
|
globFiles(pattern, opts) {
|
|
48
64
|
return fg.sync(pattern, { cwd: opts.cwd, onlyFiles: true, deep: Infinity });
|
|
@@ -54,15 +70,55 @@ const nodeIo = {
|
|
|
54
70
|
base64: () => sum.digest("base64")
|
|
55
71
|
};
|
|
56
72
|
},
|
|
57
|
-
watch(
|
|
73
|
+
watch(watchPath, opts, onEvent) {
|
|
74
|
+
if (opts.poll) return pollWatch(watchPath, opts.recursive, opts.poll.intervalMs, onEvent);
|
|
58
75
|
const watcher = opts.recursive ? fs.watch(
|
|
59
|
-
|
|
76
|
+
watchPath,
|
|
60
77
|
{ recursive: true },
|
|
61
78
|
(_event, filename) => onEvent(filename ? filename.toString() : null)
|
|
62
|
-
) : fs.watch(
|
|
79
|
+
) : fs.watch(watchPath, () => onEvent(watchPath));
|
|
63
80
|
return { close: () => watcher.close() };
|
|
64
81
|
}
|
|
65
82
|
};
|
|
83
|
+
function pollWatch(root, recursive, intervalMs, onEvent) {
|
|
84
|
+
const snapshot = () => {
|
|
85
|
+
const out = /* @__PURE__ */ new Map();
|
|
86
|
+
const walk = (dir) => {
|
|
87
|
+
let entries;
|
|
88
|
+
try {
|
|
89
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
90
|
+
} catch {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
for (const entry of entries) {
|
|
94
|
+
const full = path.join(dir, entry.name);
|
|
95
|
+
if (entry.isDirectory()) {
|
|
96
|
+
if (recursive) walk(full);
|
|
97
|
+
} else {
|
|
98
|
+
try {
|
|
99
|
+
out.set(path.relative(root, full), fs.statSync(full).mtimeMs);
|
|
100
|
+
} catch {
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
walk(root);
|
|
106
|
+
return out;
|
|
107
|
+
};
|
|
108
|
+
let prev = snapshot();
|
|
109
|
+
const timer = setInterval(() => {
|
|
110
|
+
const next = snapshot();
|
|
111
|
+
for (const [rel, mtime] of next) {
|
|
112
|
+
if (prev.get(rel) !== mtime) onEvent(rel);
|
|
113
|
+
}
|
|
114
|
+
for (const rel of prev.keys()) {
|
|
115
|
+
if (!next.has(rel)) onEvent(rel);
|
|
116
|
+
}
|
|
117
|
+
prev = next;
|
|
118
|
+
}, intervalMs);
|
|
119
|
+
timer.unref?.();
|
|
120
|
+
return { close: () => clearInterval(timer) };
|
|
121
|
+
}
|
|
66
122
|
export {
|
|
67
123
|
nodeIo
|
|
68
124
|
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** A key is re-exportable only if it is a syntactically valid identifier name. */
|
|
2
|
+
export declare const isIdentifierName: (name: string) => boolean;
|
|
3
|
+
/**
|
|
4
|
+
* Decides whether a `require()`d CommonJS value needs a synthetic named-export
|
|
5
|
+
* wrapper, and which names to re-export. UMD/CJS packages (e.g. dayjs) assign
|
|
6
|
+
* named exports dynamically, invisible to static lexers; enumerating the value
|
|
7
|
+
* recovers them. ES-module namespaces and `__esModule` interop objects are
|
|
8
|
+
* skipped — a bundler already exposes those statically.
|
|
9
|
+
*/
|
|
10
|
+
export declare const planCjsWrap: (mod: unknown) => {
|
|
11
|
+
wrap: boolean;
|
|
12
|
+
keys: string[];
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Synthetic ESM entry re-exporting the CommonJS default plus each discovered name.
|
|
16
|
+
* The `export { local as key }` clause lets reserved-word keys (`class`, `default`)
|
|
17
|
+
* pass without escaping.
|
|
18
|
+
*/
|
|
19
|
+
export declare const buildSyntheticCjsEntry: (importPath: string, keys: string[]) => string;
|
|
20
|
+
/** True when a `require()` failure just means the file was ESM after all. */
|
|
21
|
+
export declare const isEsmInteropError: (err: unknown) => boolean;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const isIdentifierName = (name) => /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name);
|
|
2
|
+
const planCjsWrap = (mod) => {
|
|
3
|
+
if (mod === null || typeof mod !== "object" && typeof mod !== "function") {
|
|
4
|
+
return { wrap: false, keys: [] };
|
|
5
|
+
}
|
|
6
|
+
if (mod[Symbol.toStringTag] === "Module") {
|
|
7
|
+
return { wrap: false, keys: [] };
|
|
8
|
+
}
|
|
9
|
+
if (mod["__esModule"]) {
|
|
10
|
+
return { wrap: false, keys: [] };
|
|
11
|
+
}
|
|
12
|
+
const keys = Object.keys(mod).filter(
|
|
13
|
+
(key) => key !== "default" && key !== "__esModule" && isIdentifierName(key)
|
|
14
|
+
);
|
|
15
|
+
return { wrap: keys.length > 0, keys };
|
|
16
|
+
};
|
|
17
|
+
const buildSyntheticCjsEntry = (importPath, keys) => {
|
|
18
|
+
const spec = JSON.stringify(importPath);
|
|
19
|
+
const lines = [`import _nfDefault from ${spec};`, `export default _nfDefault;`];
|
|
20
|
+
if (keys.length > 0) {
|
|
21
|
+
keys.forEach((key, i) => lines.push(`const _nf${i} = _nfDefault[${JSON.stringify(key)}];`));
|
|
22
|
+
lines.push(`export { ${keys.map((key, i) => `_nf${i} as ${key}`).join(", ")} };`);
|
|
23
|
+
}
|
|
24
|
+
return lines.join("\n") + "\n";
|
|
25
|
+
};
|
|
26
|
+
const isEsmInteropError = (err) => {
|
|
27
|
+
const code = err?.code;
|
|
28
|
+
if (code === "ERR_REQUIRE_ESM" || code === "ERR_REQUIRE_ASYNC_MODULE") return true;
|
|
29
|
+
return err instanceof SyntaxError && /Unexpected token 'export'|Cannot use import statement|export|import statement/.test(err.message);
|
|
30
|
+
};
|
|
31
|
+
export {
|
|
32
|
+
buildSyntheticCjsEntry,
|
|
33
|
+
isEsmInteropError,
|
|
34
|
+
isIdentifierName,
|
|
35
|
+
planCjsWrap
|
|
36
|
+
};
|
|
@@ -3,3 +3,16 @@
|
|
|
3
3
|
* or ambiguous (`undefined`).
|
|
4
4
|
*/
|
|
5
5
|
export declare const isESMExport: (e: string) => boolean | undefined;
|
|
6
|
+
export type ModuleFormat = 'esm' | 'cjs' | 'unknown';
|
|
7
|
+
/** Node's format rule keyed on extension alone; `.js` stays ambiguous. */
|
|
8
|
+
export declare const classifyByExtension: (entryPoint: string) => ModuleFormat;
|
|
9
|
+
/** Does the source contain top-level ESM `import`/`export`? Dynamic `import()` is excluded (legal in CJS). */
|
|
10
|
+
export declare const hasEsmSyntax: (source: string) => boolean;
|
|
11
|
+
export declare const isCjsCandidate: (input: {
|
|
12
|
+
esm?: boolean;
|
|
13
|
+
entryPoint: string;
|
|
14
|
+
/** Nearest package.json `type` for an ambiguous `.js`. */
|
|
15
|
+
packageType?: "module" | "commonjs";
|
|
16
|
+
/** Lazy source for fallback content sniff; only read for an ambiguous `.js`. */
|
|
17
|
+
readSource?: () => string;
|
|
18
|
+
}) => boolean;
|
|
@@ -5,6 +5,35 @@ const isESMExport = (e) => {
|
|
|
5
5
|
if (e === "cjs" || e === "commonjs") return false;
|
|
6
6
|
return void 0;
|
|
7
7
|
};
|
|
8
|
+
const classifyByExtension = (entryPoint) => {
|
|
9
|
+
if (entryPoint.endsWith(".mjs")) return "esm";
|
|
10
|
+
if (entryPoint.endsWith(".cjs")) return "cjs";
|
|
11
|
+
if (entryPoint.endsWith(".js")) return "unknown";
|
|
12
|
+
return "esm";
|
|
13
|
+
};
|
|
14
|
+
const hasEsmSyntax = (source) => {
|
|
15
|
+
const head = source.slice(0, 16384);
|
|
16
|
+
const exportStmt = /(?:^|[;\n}])\s*export\s*(?:\{|\*|default\b|const\b|let\b|var\b|function\b|async\b|class\b)/;
|
|
17
|
+
const importStmt = /(?:^|[;\n}])\s*import\s*(?:[A-Za-z_$]|\{|\*|['"])/;
|
|
18
|
+
return exportStmt.test(head) || importStmt.test(head);
|
|
19
|
+
};
|
|
20
|
+
const isCjsCandidate = (input) => {
|
|
21
|
+
if (input.esm === true) return false;
|
|
22
|
+
const fmt = classifyByExtension(input.entryPoint);
|
|
23
|
+
if (fmt === "esm") return false;
|
|
24
|
+
if (fmt === "cjs") return true;
|
|
25
|
+
if (input.packageType === "module") return false;
|
|
26
|
+
if (input.readSource) {
|
|
27
|
+
try {
|
|
28
|
+
if (hasEsmSyntax(input.readSource())) return false;
|
|
29
|
+
} catch {
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return true;
|
|
33
|
+
};
|
|
8
34
|
export {
|
|
35
|
+
classifyByExtension,
|
|
36
|
+
hasEsmSyntax,
|
|
37
|
+
isCjsCandidate,
|
|
9
38
|
isESMExport
|
|
10
39
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@softarc/native-federation",
|
|
3
|
-
"version": "4.3.
|
|
3
|
+
"version": "4.3.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"packageManager": "pnpm@11.11.0",
|
|
@@ -28,11 +28,11 @@
|
|
|
28
28
|
"jiti": "^2.6.1",
|
|
29
29
|
"jsdom": "^29.0.0",
|
|
30
30
|
"knip": "^6.20.0",
|
|
31
|
-
"prettier": "^3.
|
|
31
|
+
"prettier": "^3.9.4",
|
|
32
32
|
"tslib": "^2.3.0",
|
|
33
33
|
"typescript": "~6.0.0",
|
|
34
34
|
"typescript-eslint": "^8.61.0",
|
|
35
|
-
"vite": "^8.
|
|
35
|
+
"vite": "^8.1.3",
|
|
36
36
|
"vitest": "^4.0.0"
|
|
37
37
|
},
|
|
38
38
|
"exports": {
|