@softarc/native-federation 4.0.0-RC8 → 4.0.0

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.
Files changed (53) hide show
  1. package/package.json +2 -2
  2. package/src/index.d.ts +1 -2
  3. package/src/index.js +1 -2
  4. package/src/internal.d.ts +4 -1
  5. package/src/internal.js +2 -0
  6. package/src/lib/config/default-skip-list.js +3 -15
  7. package/src/lib/config/remove-unused-deps.d.ts +3 -0
  8. package/src/lib/config/remove-unused-deps.js +10 -0
  9. package/src/lib/config/share-utils.d.ts +0 -1
  10. package/src/lib/config/share-utils.js +7 -13
  11. package/src/lib/config/with-native-federation.js +40 -61
  12. package/src/lib/core/build-for-federation.js +21 -22
  13. package/src/lib/core/bundle-exposed-and-mappings.js +15 -16
  14. package/src/lib/core/bundle-shared.d.ts +1 -0
  15. package/src/lib/core/bundle-shared.js +8 -7
  16. package/src/lib/core/federation-builder.d.ts +6 -1
  17. package/src/lib/core/federation-builder.js +18 -8
  18. package/src/lib/core/get-externals.js +1 -1
  19. package/src/lib/core/normalize-options.d.ts +12 -0
  20. package/src/lib/core/normalize-options.js +58 -0
  21. package/src/lib/core/rebuild-for-federation.js +2 -2
  22. package/src/lib/domain/config/external-config.contract.d.ts +3 -1
  23. package/src/lib/domain/config/federation-config.contract.d.ts +8 -2
  24. package/src/lib/domain/config/with-native-federation.contract.d.ts +2 -0
  25. package/src/lib/domain/config/with-native-federation.contract.js +1 -0
  26. package/src/lib/domain/core/build-adapter.contract.d.ts +12 -6
  27. package/src/lib/domain/core/federation-options.contract.d.ts +5 -5
  28. package/src/lib/domain/core/index.d.ts +1 -1
  29. package/src/lib/domain/utils/file-watcher.contract.d.ts +10 -0
  30. package/src/lib/domain/utils/file-watcher.contract.js +1 -0
  31. package/src/lib/domain/utils/index.d.ts +1 -1
  32. package/src/lib/domain/utils/mapped-path.contract.d.ts +1 -4
  33. package/src/lib/domain/utils/mapped-path.contract.js +4 -0
  34. package/src/lib/domain/utils/used-dependencies.contract.d.ts +5 -0
  35. package/src/lib/domain/utils/used-dependencies.contract.js +1 -0
  36. package/src/lib/utils/file-watcher.d.ts +5 -0
  37. package/src/lib/utils/file-watcher.js +51 -0
  38. package/src/lib/utils/get-used-dependencies.d.ts +7 -0
  39. package/src/lib/utils/get-used-dependencies.js +123 -0
  40. package/src/lib/utils/mapped-paths.d.ts +7 -7
  41. package/src/lib/utils/mapped-paths.js +14 -12
  42. package/src/lib/utils/resolve-wildcard-keys.d.ts +29 -2
  43. package/src/lib/utils/resolve-wildcard-keys.js +105 -38
  44. package/src/lib/core/default-server-deps-list.d.ts +0 -2
  45. package/src/lib/core/default-server-deps-list.js +0 -6
  46. package/src/lib/core/load-federation-config.d.ts +0 -3
  47. package/src/lib/core/load-federation-config.js +0 -18
  48. package/src/lib/core/normalize-federation-options.d.ts +0 -4
  49. package/src/lib/core/normalize-federation-options.js +0 -10
  50. package/src/lib/core/remove-unused-deps.d.ts +0 -2
  51. package/src/lib/core/remove-unused-deps.js +0 -90
  52. package/src/lib/utils/config-utils.d.ts +0 -2
  53. package/src/lib/utils/config-utils.js +0 -9
@@ -0,0 +1,12 @@
1
+ import type { NormalizedFederationConfig } from '../domain/config/federation-config.contract.js';
2
+ import type { FederationOptions, NormalizedFederationOptions } from '../domain/core/federation-options.contract.js';
3
+ import { type FederationCache } from '../../domain.js';
4
+ export declare function normalizeFederationOptions(options: FederationOptions): Promise<{
5
+ config: NormalizedFederationConfig;
6
+ options: NormalizedFederationOptions<undefined>;
7
+ }>;
8
+ export declare function normalizeFederationOptions<TBundlerCache>(options: FederationOptions, cache: FederationCache<TBundlerCache>): Promise<{
9
+ config: NormalizedFederationConfig;
10
+ options: NormalizedFederationOptions<TBundlerCache>;
11
+ }>;
12
+ export declare function resolveProjectName(name?: string): string;
@@ -0,0 +1,58 @@
1
+ import * as path from 'path';
2
+ import * as fs from 'fs';
3
+ import { pathToFileURL } from 'url';
4
+ import { removeUnusedDeps } from '../config/remove-unused-deps.js';
5
+ import { createFederationCache } from './federation-cache.js';
6
+ import { getDefaultCachePath } from '../utils/cache-persistence.js';
7
+ import { getUsedDependenciesFactory } from '../utils/get-used-dependencies.js';
8
+ import { logger } from '../utils/logger.js';
9
+ import { normalizePackageName } from '../utils/normalize.js';
10
+ export async function normalizeFederationOptions(options, cache) {
11
+ /**
12
+ * Step 1: normalizing config
13
+ */
14
+ const fullConfigPath = path.join(options.workspaceRoot, options.federationConfig);
15
+ const getUsedDeps = getUsedDependenciesFactory(options.workspaceRoot, options.entryPoints);
16
+ if (!fs.existsSync(fullConfigPath)) {
17
+ throw new Error('Expected ' + fullConfigPath);
18
+ }
19
+ let config = (await import(pathToFileURL(fullConfigPath).href))
20
+ ?.default;
21
+ /**
22
+ * Step 2: normalizing options
23
+ */
24
+ const federationCache = cache ??
25
+ createFederationCache(getDefaultCachePath(options.workspaceRoot));
26
+ const normalizedOptions = {
27
+ ...options,
28
+ entryPoints: options.entryPoints ?? Object.values(config.exposes ?? {}),
29
+ projectName: resolveProjectName(options.projectName ?? config.name),
30
+ cacheExternalArtifacts: options.cacheExternalArtifacts ?? true,
31
+ federationCache,
32
+ };
33
+ /**
34
+ * Step 3: Remove unused deps
35
+ */
36
+ if (config.features.ignoreUnusedDeps) {
37
+ config = removeUnusedDeps(getUsedDeps(config), config);
38
+ logger.info('Removed unused dependencies.');
39
+ logger.debug('This can be disabled per dependency/external using the "includeSecondaries: {keepAll: true}" property. Or in general by disabling the "ignoreUnusedDeps" feature. ');
40
+ }
41
+ else {
42
+ const withWildcard = Object.keys(config.sharedMappings).some(m => m.includes('*'));
43
+ if (withWildcard) {
44
+ logger.warn('Sharing mapped paths with wildcards (*) is only supported with ignoreUnusedDeps feature.');
45
+ config.sharedMappings = Object.entries(config.sharedMappings)
46
+ .filter(([_path]) => !_path.includes('*'))
47
+ .reduce((acc, [_path, _import]) => ({ ...acc, [_path]: _import }), {});
48
+ }
49
+ }
50
+ return { config, options: normalizedOptions };
51
+ }
52
+ export function resolveProjectName(name) {
53
+ if (!name || name.length < 1) {
54
+ logger.warn("Project name in 'federation.config.js' is empty, defaulting to 'shell' cache folder (could collide with other projects in the workspace).");
55
+ return 'shell';
56
+ }
57
+ return normalizePackageName(name);
58
+ }
@@ -5,10 +5,10 @@ import { logger } from '../utils/logger.js';
5
5
  import { AbortedError } from '../utils/errors.js';
6
6
  export async function rebuildForFederation(config, fedOptions, externals, modifiedFiles, signal) {
7
7
  const federationCache = fedOptions.federationCache;
8
+ logger.info(`Re-bundling all internal libraries and exposed modules..'`);
8
9
  const start = process.hrtime();
9
- // Shared mappings and exposed modules
10
10
  const artifactInfo = await bundleExposedAndMappings(config, fedOptions, externals, modifiedFiles, signal);
11
- logger.measure(start, '[build artifacts] - To re-bundle all mappings and exposed.');
11
+ logger.measure(start, 'To re-bundle all internal libraries and exposed modules.');
12
12
  if (signal?.aborted)
13
13
  throw new AbortedError('[buildForFederation] After exposed-and-mappings bundle');
14
14
  const exposedInfo = !artifactInfo ? describeExposed(config, fedOptions) : artifactInfo.exposes;
@@ -5,7 +5,8 @@ export interface ExternalConfig {
5
5
  version?: string;
6
6
  includeSecondaries?: boolean;
7
7
  platform?: 'browser' | 'node';
8
- build?: 'default' | 'separate' | 'package';
8
+ build?: 'separate' | 'package';
9
+ chunks?: boolean;
9
10
  shareScope?: string;
10
11
  packageInfo?: {
11
12
  entryPoint: string;
@@ -20,6 +21,7 @@ export interface NormalizedExternalConfig {
20
21
  version?: string;
21
22
  includeSecondaries?: boolean;
22
23
  shareScope?: string;
24
+ chunks: boolean;
23
25
  platform: 'browser' | 'node';
24
26
  build: 'default' | 'separate' | 'package';
25
27
  packageInfo?: {
@@ -1,29 +1,35 @@
1
1
  import type { PreparedSkipList, SkipList } from './skip-list.contract.js';
2
- import type { MappedPath } from '../utils/mapped-path.contract.js';
2
+ import type { PathToImport } from '../utils/mapped-path.contract.js';
3
3
  import type { NormalizedSharedExternalsConfig, SharedExternalsConfig } from './external-config.contract.js';
4
4
  export interface FederationConfig {
5
5
  name?: string;
6
6
  exposes?: Record<string, string>;
7
7
  shared?: SharedExternalsConfig;
8
+ platform?: 'browser' | 'node';
8
9
  sharedMappings?: Array<string>;
10
+ chunks?: boolean;
9
11
  skip?: SkipList;
10
12
  externals?: string[];
11
13
  shareScope?: string;
12
14
  features?: {
13
15
  mappingVersion?: boolean;
14
16
  ignoreUnusedDeps?: boolean;
17
+ denseChunking?: boolean;
15
18
  };
16
19
  }
17
20
  export interface NormalizedFederationConfig {
21
+ $type: 'classic';
18
22
  name: string;
19
23
  exposes: Record<string, string>;
20
24
  shared: NormalizedSharedExternalsConfig;
21
- sharedMappings: Array<MappedPath>;
25
+ sharedMappings: PathToImport;
22
26
  skip: PreparedSkipList;
27
+ chunks: boolean;
23
28
  externals: string[];
24
29
  shareScope?: string;
25
30
  features: {
26
31
  mappingVersion: boolean;
27
32
  ignoreUnusedDeps: boolean;
33
+ denseChunking: boolean;
28
34
  };
29
35
  }
@@ -0,0 +1,2 @@
1
+ import type { NormalizedFederationConfig } from './federation-config.contract.js';
2
+ export type WithNativeFederation = NormalizedFederationConfig;
@@ -1,9 +1,16 @@
1
- import type { MappedPath } from '../utils/mapped-path.contract.js';
1
+ import type { PathToImport } from '../utils/mapped-path.contract.js';
2
2
  import type { FederationCache } from './federation-cache.contract.js';
3
+ export interface NFBuildAdapterContext<TBundlerContext = unknown> {
4
+ ctx: TBundlerContext;
5
+ outdir: string;
6
+ dev: boolean;
7
+ name: string;
8
+ isMappingOrExposed: boolean;
9
+ }
3
10
  export interface NFBuildAdapter {
4
- setup(options: NFBuildAdapterOptions): Promise<void>;
11
+ setup(name: string, options: NFBuildAdapterOptions): Promise<void>;
5
12
  build(name: string, opts?: {
6
- files?: string[];
13
+ modifiedFiles?: string[];
7
14
  signal?: AbortSignal;
8
15
  }): Promise<NFBuildAdapterResult[]>;
9
16
  dispose(name?: string): Promise<void>;
@@ -18,9 +25,8 @@ export interface NFBuildAdapterOptions<TBundlerCache = unknown> {
18
25
  tsConfigPath?: string;
19
26
  external: string[];
20
27
  outdir: string;
21
- mappedPaths: MappedPath[];
22
- bundleName: string;
23
- isNodeModules: boolean;
28
+ mappedPaths: PathToImport;
29
+ isMappingOrExposed: boolean;
24
30
  dev?: boolean;
25
31
  watch?: boolean;
26
32
  chunks?: boolean;
@@ -4,19 +4,19 @@ export interface FederationOptions {
4
4
  workspaceRoot: string;
5
5
  outputPath: string;
6
6
  federationConfig: string;
7
+ projectName?: string;
7
8
  cacheExternalArtifacts?: boolean;
8
- chunks?: boolean | {
9
- enable: boolean;
10
- dense?: boolean;
11
- };
12
9
  tsConfig?: string;
13
10
  verbose?: boolean;
14
11
  dev?: boolean;
15
12
  watch?: boolean;
16
13
  packageJson?: string;
17
- entryPoint?: string;
14
+ entryPoints?: string[];
18
15
  buildNotifications?: BuildNotificationOptions;
19
16
  }
20
17
  export interface NormalizedFederationOptions<TBundlerCache = unknown> extends FederationOptions {
21
18
  federationCache: FederationCache<TBundlerCache>;
19
+ entryPoints: string[];
20
+ projectName: string;
21
+ cacheExternalArtifacts: boolean;
22
22
  }
@@ -1,6 +1,6 @@
1
1
  export type { SharedInfo, FederationInfo, ExposesInfo, ArtifactInfo, ChunkInfo, } from './federation-info.contract.js';
2
2
  export { type BuildNotificationOptions, BuildNotificationType, } from './build-notification-options.contract.js';
3
3
  export type { FederationOptions, NormalizedFederationOptions, } from './federation-options.contract.js';
4
- export type { EntryPoint, NFBuildAdapterOptions, NFBuildAdapter, NFBuildAdapterResult, } from './build-adapter.contract.js';
4
+ export type { EntryPoint, NFBuildAdapterOptions, NFBuildAdapter, NFBuildAdapterResult, NFBuildAdapterContext, } from './build-adapter.contract.js';
5
5
  export { CHUNK_PREFIX, toChunkImport } from './chunk.js';
6
6
  export type { FederationCache } from './federation-cache.contract.js';
@@ -0,0 +1,10 @@
1
+ export interface NfFileWatcherOptions {
2
+ onChange?: (path: string) => void;
3
+ }
4
+ export interface NfFileWatcher {
5
+ addPaths(paths: string | readonly string[]): void;
6
+ close(): Promise<void>;
7
+ get(): ReadonlySet<string>;
8
+ clear(): void;
9
+ mutate(fn: (dirtyPaths: Set<string>) => void): void;
10
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,2 +1,2 @@
1
1
  export type { KeyValuePair } from './keyvaluepair.contract.js';
2
- export type { MappedPath } from './mapped-path.contract.js';
2
+ export type { PathToImport } from './mapped-path.contract.js';
@@ -1,4 +1 @@
1
- export interface MappedPath {
2
- key: string;
3
- path: string;
4
- }
1
+ export type PathToImport = Record<string, string>;
@@ -1 +1,5 @@
1
+ // export interface MappedPath {
2
+ // key: string;
3
+ // path: string;
4
+ // }
1
5
  export {};
@@ -0,0 +1,5 @@
1
+ import type { PathToImport } from './mapped-path.contract.js';
2
+ export type UsedDependencies = {
3
+ external: Set<string>;
4
+ internal: PathToImport;
5
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,5 @@
1
+ import type { NfFileWatcher, NfFileWatcherOptions } from '../domain/utils/file-watcher.contract.js';
2
+ export declare function createNfWatcher(options?: NfFileWatcherOptions): NfFileWatcher;
3
+ export declare function syncNfFileWatcher(watcher: NfFileWatcher, bundlerCache: {
4
+ keys(): IterableIterator<string>;
5
+ }): void;
@@ -0,0 +1,51 @@
1
+ import { logger } from '@softarc/native-federation/internal';
2
+ import { watch, statSync } from 'fs';
3
+ import { join } from 'path';
4
+ const toUnix = (p) => p.replace(/\\/g, '/');
5
+ export function createNfWatcher(options = {}) {
6
+ const { onChange } = options;
7
+ const watchers = new Map();
8
+ const dirtyPaths = new Set();
9
+ const notify = (path) => {
10
+ if (onChange)
11
+ onChange(path);
12
+ else
13
+ dirtyPaths.add(path);
14
+ };
15
+ return {
16
+ addPaths(paths) {
17
+ const list = typeof paths === 'string' ? [paths] : [...paths];
18
+ for (const p of list) {
19
+ if (watchers.has(p))
20
+ continue;
21
+ try {
22
+ const isDir = statSync(p).isDirectory();
23
+ const w = isDir
24
+ ? watch(p, { recursive: true }, (_, filename) => {
25
+ if (filename)
26
+ notify(toUnix(join(p, filename)));
27
+ })
28
+ : watch(p, () => notify(toUnix(p)));
29
+ watchers.set(p, w);
30
+ }
31
+ catch {
32
+ logger.debug(`Could not watch path '${p}'.`);
33
+ }
34
+ }
35
+ },
36
+ get: () => dirtyPaths,
37
+ clear: () => dirtyPaths.clear(),
38
+ mutate: fn => fn(dirtyPaths),
39
+ async close() {
40
+ for (const w of watchers.values()) {
41
+ w.close();
42
+ }
43
+ watchers.clear();
44
+ },
45
+ };
46
+ }
47
+ export function syncNfFileWatcher(watcher, bundlerCache) {
48
+ const files = [...bundlerCache.keys()].filter(k => !k.includes('node_modules'));
49
+ if (files.length)
50
+ watcher.addPaths(files);
51
+ }
@@ -0,0 +1,7 @@
1
+ import { type PathToImport } from '../domain/utils/mapped-path.contract.js';
2
+ import { type UsedDependencies } from '../domain/utils/used-dependencies.contract.js';
3
+ export declare function getUsedDependenciesFactory(workspaceRoot: string, fallbackEntryPoints?: string[]): (config: {
4
+ name?: string;
5
+ exposes?: Record<string, string>;
6
+ sharedMappings: PathToImport;
7
+ }) => UsedDependencies;
@@ -0,0 +1,123 @@
1
+ import { getProjectData } from '@softarc/sheriff-core';
2
+ import { cwd } from 'process';
3
+ import { getPackageInfo } from './package-info.js';
4
+ import { getExternalImports as extractExternalImports } from './get-external-imports.js';
5
+ import * as path from 'path';
6
+ export function getUsedDependenciesFactory(workspaceRoot, fallbackEntryPoints) {
7
+ return config => {
8
+ let entryPoints = Object.values(config.exposes ?? {});
9
+ if (entryPoints.length < 1)
10
+ entryPoints = fallbackEntryPoints;
11
+ if (!entryPoints || entryPoints.length < 1)
12
+ throw new Error('[removeUnusedDeps] native-federation is missing an entryPoint! You can set it using the Federation options or by setting an exposed module in the Federation config file.');
13
+ const fileInfos = Object.values(entryPoints ?? []).reduce((acc, entryPoint) => ({
14
+ ...acc,
15
+ ...getProjectData(entryPoint, cwd(), {
16
+ includeExternalLibraries: true,
17
+ }),
18
+ }), {});
19
+ const usedPackageNames = new Set();
20
+ for (const fileInfo of Object.values(fileInfos)) {
21
+ for (const pckg of [
22
+ ...(fileInfo?.externalLibraries || []),
23
+ ...(fileInfo?.unresolvedImports || []),
24
+ ]) {
25
+ usedPackageNames.add(pckg);
26
+ }
27
+ }
28
+ return {
29
+ external: addTransientDeps(usedPackageNames, workspaceRoot),
30
+ internal: resolveUsedMappings(fileInfos, workspaceRoot, config.sharedMappings),
31
+ };
32
+ };
33
+ }
34
+ function addTransientDeps(packages, workspaceRoot) {
35
+ const packagesAndPeers = new Set([...packages]);
36
+ const discovered = new Set(packagesAndPeers);
37
+ const stack = [...packagesAndPeers];
38
+ while (stack.length > 0) {
39
+ const dep = stack.pop();
40
+ if (!dep) {
41
+ continue;
42
+ }
43
+ const pInfo = getPackageInfo(dep, workspaceRoot);
44
+ if (!pInfo) {
45
+ continue;
46
+ }
47
+ const peerDeps = extractExternalImports(pInfo.entryPoint);
48
+ for (const peerDep of peerDeps) {
49
+ if (!discovered.has(peerDep)) {
50
+ discovered.add(peerDep);
51
+ stack.push(peerDep);
52
+ packagesAndPeers.add(peerDep);
53
+ }
54
+ }
55
+ }
56
+ return packagesAndPeers;
57
+ }
58
+ function resolveUsedMappings(fileInfos, workspaceRoot, sharedMappings) {
59
+ const usedMappings = {};
60
+ for (const fileName of Object.keys(fileInfos)) {
61
+ const fullFileName = path.join(workspaceRoot, fileName);
62
+ if (isSharedMapping(fullFileName, sharedMappings))
63
+ continue;
64
+ const fileInfo = fileInfos[fileName];
65
+ if (!fileInfo)
66
+ continue;
67
+ // Check if any of this file's imports land in a shared mapping
68
+ for (const imp of fileInfo.imports ?? []) {
69
+ const fullImport = path.join(workspaceRoot, imp);
70
+ const match = matchMapping(fullImport, sharedMappings);
71
+ if (match)
72
+ usedMappings[fullImport] = match;
73
+ }
74
+ }
75
+ return usedMappings;
76
+ }
77
+ function isSharedMapping(filePath, sharedMappings) {
78
+ for (const sharedPath of Object.keys(sharedMappings)) {
79
+ const asteriskIndex = sharedPath.indexOf('*');
80
+ if (asteriskIndex !== -1) {
81
+ const prefix = sharedPath.substring(0, asteriskIndex);
82
+ if (filePath.startsWith(prefix))
83
+ return true;
84
+ }
85
+ else if (filePath.startsWith(sharedPath + path.sep) || filePath === sharedPath) {
86
+ return true;
87
+ }
88
+ }
89
+ return false;
90
+ }
91
+ function matchMapping(filePath, sharedMappings) {
92
+ for (const [sharedPath, sharedImport] of Object.entries(sharedMappings)) {
93
+ const asteriskIndex = sharedPath.indexOf('*');
94
+ if (asteriskIndex !== -1) {
95
+ const prefix = sharedPath.substring(0, asteriskIndex);
96
+ const suffix = sharedPath.substring(asteriskIndex + 1);
97
+ if (!filePath.startsWith(prefix))
98
+ continue;
99
+ if (suffix && !filePath.includes(suffix))
100
+ continue;
101
+ const captured = suffix
102
+ ? filePath.slice(prefix.length, filePath.indexOf(suffix, prefix.length))
103
+ : filePath.slice(prefix.length);
104
+ return sharedImport.replace('*', toImportPath(captured));
105
+ }
106
+ else if (filePath === sharedPath || isIndexOf(filePath, sharedPath)) {
107
+ return sharedImport;
108
+ }
109
+ }
110
+ return null;
111
+ }
112
+ /**
113
+ * Detect if it's a barrel file which is inferred by typescript
114
+ */
115
+ const INDEX_PATTERN = /\/index\.(ts|tsx|mts|cts|js|jsx|mjs|cjs)$/;
116
+ function isIndexOf(filePath, dirPath) {
117
+ return filePath.startsWith(dirPath + path.sep) && INDEX_PATTERN.test(filePath);
118
+ }
119
+ function toImportPath(filePath) {
120
+ const withoutExt = filePath.replace(/\.(ts|tsx|mts|cts|js|jsx|mjs|cjs)$/, '');
121
+ const normalized = withoutExt.replace(/\\/g, '/');
122
+ return normalized.endsWith('/index') ? normalized.slice(0, -6) : normalized;
123
+ }
@@ -1,7 +1,7 @@
1
- import type { MappedPath } from '../domain/utils/mapped-path.contract.js';
2
- export interface GetMappedPathsOptions {
3
- rootTsConfigPath: string;
4
- sharedMappings?: string[];
5
- rootPath?: string;
6
- }
7
- export declare function getMappedPaths({ rootTsConfigPath, sharedMappings, rootPath, }: GetMappedPathsOptions): Array<MappedPath>;
1
+ import type { PathToImport } from '../domain/utils/mapped-path.contract.js';
2
+ /**
3
+ * Will return user defined and tsconfig defined paths including their imports, might contain wildcards
4
+ * @param param0
5
+ * @returns
6
+ */
7
+ export declare function getRawMappedPaths(rootTsConfigPath: string, configuredSharedMappings?: string[], rootPath?: string): PathToImport;
@@ -1,31 +1,33 @@
1
1
  import * as path from 'path';
2
2
  import * as fs from 'fs';
3
3
  import JSON5 from 'json5';
4
- export function getMappedPaths({ rootTsConfigPath, sharedMappings, rootPath, }) {
5
- const result = [];
4
+ /**
5
+ * Will return user defined and tsconfig defined paths including their imports, might contain wildcards
6
+ * @param param0
7
+ * @returns
8
+ */
9
+ export function getRawMappedPaths(rootTsConfigPath, configuredSharedMappings, rootPath) {
10
+ const mappedPaths = {};
6
11
  if (!path.isAbsolute(rootTsConfigPath)) {
7
12
  throw new Error('SharedMappings.register: tsConfigPath needs to be an absolute path!');
8
13
  }
9
14
  if (!rootPath) {
10
15
  rootPath = path.normalize(path.dirname(rootTsConfigPath));
11
16
  }
12
- const shareAll = !sharedMappings;
13
- if (!sharedMappings) {
14
- sharedMappings = [];
17
+ const shareAll = !configuredSharedMappings;
18
+ if (!configuredSharedMappings) {
19
+ configuredSharedMappings = [];
15
20
  }
16
21
  const tsConfig = JSON5.parse(fs.readFileSync(rootTsConfigPath, { encoding: 'utf-8' }));
17
22
  const mappings = tsConfig?.compilerOptions?.paths;
18
23
  if (!mappings) {
19
- return result;
24
+ return mappedPaths;
20
25
  }
21
26
  for (const key in mappings) {
22
27
  const libPath = path.normalize(path.join(rootPath, mappings[key][0]));
23
- if (sharedMappings.includes(key) || shareAll) {
24
- result.push({
25
- key,
26
- path: libPath,
27
- });
28
+ if (configuredSharedMappings.includes(key) || shareAll) {
29
+ mappedPaths[libPath] = key;
28
30
  }
29
31
  }
30
- return result;
32
+ return mappedPaths;
31
33
  }
@@ -1,2 +1,29 @@
1
- import type { KeyValuePair } from '../domain/utils/keyvaluepair.contract.js';
2
- export declare function resolveWildcardKeys(keyPattern: string, valuePattern: string, cwd: string): KeyValuePair[];
1
+ export type KeyValuePair = {
2
+ key: string;
3
+ value: string;
4
+ };
5
+ /**
6
+ * Resolves tsconfig wildcard paths.
7
+ *
8
+ * In tsconfig.json, paths like `@features/*` → `libs/features/src/*` work as follows:
9
+ * - The `*` captures a single path segment (the module name)
10
+ * - When importing `@features/feature-a`, TypeScript captures `feature-a`
11
+ * - It then replaces `*` in the value pattern: `libs/features/src/feature-a`
12
+ *
13
+ * For discovery, we find all directories at the wildcard position that TypeScript
14
+ * would recognize as valid modules (directories with index files or package.json).
15
+ *
16
+ // @see https://www.typescriptlang.org/docs/handbook/modules/theory.html#module-resolution
17
+ */
18
+ export declare function resolveTsConfigWildcard(keyPattern: string, valuePattern: string, cwd: string): KeyValuePair[];
19
+ /**
20
+ * Resolves package.json exports wildcard patterns.
21
+ *
22
+ * In package.json exports, patterns like `./features/*.js` → `./src/features/*.js` work as follows:
23
+ * - The `*` is a literal string replacement that can include path separators
24
+ * - Importing `pkg/features/a/b.js` captures `a/b` and replaces `*` → `./src/features/a/b.js`
25
+ * - This matches actual files, not directories
26
+ *
27
+ * @see https://nodejs.org/api/packages.html#subpath-patterns
28
+ */
29
+ export declare function resolvePackageJsonExportsWildcard(keyPattern: string, valuePattern: string, cwd: string): KeyValuePair[];