@bleedingdev/modern-js-app-tools-extensions 3.9.0-ultramodern.12 → 3.9.0-ultramodern.13
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/cjs/backend-federation-build/codegen.js +7 -2
- package/dist/cjs/backend-federation-build/config.js +86 -36
- package/dist/cjs/backend-federation-build/emit.js +17 -13
- package/dist/cjs/cloudflare/delivery-unit.js +16 -26
- package/dist/cjs/cloudflare/index.js +7 -1
- package/dist/cjs/cloudflare-builder.js +13 -3
- package/dist/cjs/cloudflare-output-contract.js +1 -0
- package/dist/cjs/cloudflare-output-verifier/identity.js +26 -24
- package/dist/cjs/cloudflare-output-verifier/index.js +10 -0
- package/dist/cjs/cloudflare-output-verifier/manifest.js +46 -0
- package/dist/cjs/cloudflare-output-verifier/mutation-policy.js +7 -1
- package/dist/cjs/deploy-output/npmAliases.js +28 -3
- package/dist/cjs/release-envelope/framework-output.js +62 -49
- package/dist/cjs/release-envelope/index.js +10 -4
- package/dist/esm/backend-federation-build/codegen.mjs +7 -2
- package/dist/esm/backend-federation-build/config.mjs +78 -31
- package/dist/esm/backend-federation-build/emit.mjs +18 -14
- package/dist/esm/cloudflare/delivery-unit.mjs +16 -26
- package/dist/esm/cloudflare/index.mjs +7 -1
- package/dist/esm/cloudflare-builder.mjs +13 -3
- package/dist/esm/cloudflare-output-contract.mjs +1 -0
- package/dist/esm/cloudflare-output-verifier/identity.mjs +26 -24
- package/dist/esm/cloudflare-output-verifier/index.mjs +10 -0
- package/dist/esm/cloudflare-output-verifier/manifest.mjs +46 -0
- package/dist/esm/cloudflare-output-verifier/mutation-policy.mjs +7 -1
- package/dist/esm/deploy-output/npmAliases.mjs +28 -3
- package/dist/esm/release-envelope/framework-output.mjs +62 -49
- package/dist/esm/release-envelope/index.mjs +10 -4
- package/dist/esm-node/backend-federation-build/codegen.mjs +7 -2
- package/dist/esm-node/backend-federation-build/config.mjs +78 -31
- package/dist/esm-node/backend-federation-build/emit.mjs +18 -14
- package/dist/esm-node/cloudflare/delivery-unit.mjs +16 -26
- package/dist/esm-node/cloudflare/index.mjs +7 -1
- package/dist/esm-node/cloudflare-builder.mjs +13 -3
- package/dist/esm-node/cloudflare-output-contract.mjs +1 -0
- package/dist/esm-node/cloudflare-output-verifier/identity.mjs +26 -24
- package/dist/esm-node/cloudflare-output-verifier/index.mjs +10 -0
- package/dist/esm-node/cloudflare-output-verifier/manifest.mjs +46 -0
- package/dist/esm-node/cloudflare-output-verifier/mutation-policy.mjs +7 -1
- package/dist/esm-node/deploy-output/npmAliases.mjs +28 -3
- package/dist/esm-node/release-envelope/framework-output.mjs +62 -49
- package/dist/esm-node/release-envelope/index.mjs +10 -4
- package/dist/types/backend-federation-build/codegen.d.ts +30 -0
- package/dist/types/backend-federation-build/config.d.ts +32 -13
- package/dist/types/cloudflare/delivery-unit.d.ts +1 -1
- package/dist/types/cloudflare-builder.d.ts +1 -0
- package/dist/types/cloudflare-output-contract.d.ts +1 -1
- package/package.json +20 -20
- package/src/backend-federation-build/codegen.ts +6 -2
- package/src/backend-federation-build/config.ts +207 -71
- package/src/backend-federation-build/emit.ts +33 -22
- package/src/cloudflare/delivery-unit.ts +39 -33
- package/src/cloudflare/index.ts +20 -6
- package/src/cloudflare-builder.ts +18 -4
- package/src/cloudflare-output-contract.ts +1 -0
- package/src/cloudflare-output-verifier/identity.ts +4 -0
- package/src/cloudflare-output-verifier/index.ts +20 -0
- package/src/cloudflare-output-verifier/manifest.ts +77 -0
- package/src/cloudflare-output-verifier/mutation-policy.ts +11 -1
- package/src/deploy-output/npmAliases.ts +52 -3
- package/src/release-envelope/framework-output.ts +163 -90
- package/src/release-envelope/index.ts +26 -5
|
@@ -4,11 +4,11 @@ import { ULTRAMODERN_BUILD_ARTIFACT_PATH, isUltramodernBuildArtifact, nonEmptySt
|
|
|
4
4
|
import { fs } from "@modern-js/utils";
|
|
5
5
|
import { resolveUltramodernReleaseIdentity } from "../release-identity.mjs";
|
|
6
6
|
import { isRecord } from "./utils.mjs";
|
|
7
|
-
const
|
|
7
|
+
const TOPOLOGY_PATH = 'topology/reference-topology.json';
|
|
8
8
|
const findWorkspaceRoot = async (appDirectory)=>{
|
|
9
9
|
let current = node_path.resolve(appDirectory);
|
|
10
10
|
for(;;){
|
|
11
|
-
if (await fs.pathExists(node_path.join(current,
|
|
11
|
+
if (await fs.pathExists(node_path.join(current, TOPOLOGY_PATH))) return current;
|
|
12
12
|
const parent = node_path.dirname(current);
|
|
13
13
|
if (parent === current) return;
|
|
14
14
|
current = parent;
|
|
@@ -25,36 +25,25 @@ const stampReleaseIdentity = (identity, workspaceRoot)=>{
|
|
|
25
25
|
})
|
|
26
26
|
};
|
|
27
27
|
};
|
|
28
|
-
const
|
|
28
|
+
const resolveTopologyApp = async (appDirectory)=>{
|
|
29
29
|
const workspaceRoot = await findWorkspaceRoot(appDirectory);
|
|
30
30
|
if (!workspaceRoot) return;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
if (!isRecord(compactConfig)) return {
|
|
40
|
-
workspaceRoot
|
|
41
|
-
};
|
|
42
|
-
const topology = isRecord(compactConfig.topology) ? compactConfig.topology : void 0;
|
|
43
|
-
const apps = Array.isArray(topology?.apps) ? topology.apps : void 0;
|
|
44
|
-
if (!apps) return {
|
|
45
|
-
app: compactConfig,
|
|
46
|
-
workspaceRoot
|
|
47
|
-
};
|
|
31
|
+
const topology = await fs.readJSON(node_path.join(workspaceRoot, TOPOLOGY_PATH));
|
|
32
|
+
if (!isRecord(topology) || !isRecord(topology.shell) || !Array.isArray(topology.verticals) || void 0 !== topology.shells && !Array.isArray(topology.shells)) throw new Error('[cloudflare-delivery-unit] Invalid declared reference topology.');
|
|
33
|
+
const apps = [
|
|
34
|
+
topology.shell,
|
|
35
|
+
...topology.verticals,
|
|
36
|
+
...Array.isArray(topology.shells) ? topology.shells : []
|
|
37
|
+
];
|
|
48
38
|
const resolvedAppDirectory = node_path.resolve(appDirectory);
|
|
49
39
|
const app = apps.find((candidate)=>{
|
|
50
40
|
if (!isRecord(candidate)) return false;
|
|
51
41
|
const appPath = nonEmptyString(candidate.path);
|
|
52
42
|
return void 0 !== appPath && node_path.resolve(workspaceRoot, appPath.replace(/^\.\/+/u, '')) === resolvedAppDirectory;
|
|
53
43
|
});
|
|
44
|
+
if (!isRecord(app)) throw new Error(`[cloudflare-delivery-unit] ${appDirectory} is missing from the declared reference topology.`);
|
|
54
45
|
return {
|
|
55
|
-
|
|
56
|
-
app
|
|
57
|
-
} : {},
|
|
46
|
+
app,
|
|
58
47
|
workspaceRoot
|
|
59
48
|
};
|
|
60
49
|
};
|
|
@@ -81,10 +70,11 @@ const createDeliveryUnitStamp = (identity, app)=>{
|
|
|
81
70
|
};
|
|
82
71
|
};
|
|
83
72
|
const resolveTopologyDeliveryUnit = async (appDirectory)=>{
|
|
84
|
-
const resolved = await
|
|
73
|
+
const resolved = await resolveTopologyApp(appDirectory);
|
|
85
74
|
if (!resolved?.app) return;
|
|
86
75
|
const identity = toDeliveryUnitIdentity(resolved.app.deliveryUnit);
|
|
87
|
-
|
|
76
|
+
if (!identity) throw new Error('[cloudflare-delivery-unit] Declared app is missing a valid delivery-unit identity.');
|
|
77
|
+
return createDeliveryUnitStamp(stampReleaseIdentity(identity, resolved.workspaceRoot), resolved.app);
|
|
88
78
|
};
|
|
89
79
|
const resolveWorkerDeliveryUnitStamp = async (appDirectory)=>{
|
|
90
80
|
const buildArtifactPath = node_path.join(appDirectory, ULTRAMODERN_BUILD_ARTIFACT_PATH);
|
|
@@ -95,7 +85,7 @@ const resolveWorkerDeliveryUnitStamp = async (appDirectory)=>{
|
|
|
95
85
|
identity = toDeliveryUnitIdentity(artifact.deliveryUnit);
|
|
96
86
|
}
|
|
97
87
|
if (!identity) return;
|
|
98
|
-
const resolved = await
|
|
88
|
+
const resolved = await resolveTopologyApp(appDirectory);
|
|
99
89
|
if (resolved) identity = stampReleaseIdentity(identity, resolved.workspaceRoot);
|
|
100
90
|
return createDeliveryUnitStamp(identity, resolved?.app);
|
|
101
91
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import "node:module";
|
|
2
2
|
import node_path from "node:path";
|
|
3
|
+
import { BACKEND_FEDERATION_MANIFEST_FILE, BACKEND_FEDERATION_REMOTE_ENTRY_FILE, ULTRAMODERN_BUILD_ARTIFACT_FILE } from "@modern-js/backend-federation-contracts";
|
|
3
4
|
import { fs } from "@modern-js/utils";
|
|
4
5
|
import { createCloudflareOutputPlan } from "../cloudflare-output-plan.mjs";
|
|
5
6
|
import { assertCloudflareOutput } from "../cloudflare-output-verifier/index.mjs";
|
|
@@ -37,7 +38,7 @@ const createCloudflarePreset = ({ api, appContext, modernConfig })=>{
|
|
|
37
38
|
await fs.remove(outputDirectory);
|
|
38
39
|
},
|
|
39
40
|
async writeOutput () {
|
|
40
|
-
if (releaseEnvelopeEnabled
|
|
41
|
+
if (releaseEnvelopeEnabled) await emitFrameworkMicroVerticalReleaseEnvelope({
|
|
41
42
|
apiOnly,
|
|
42
43
|
distDirectory,
|
|
43
44
|
target: 'cloudflare'
|
|
@@ -46,6 +47,11 @@ const createCloudflarePreset = ({ api, appContext, modernConfig })=>{
|
|
|
46
47
|
await fs.copy(distDirectory, publicDirectory, {
|
|
47
48
|
filter: (src)=>{
|
|
48
49
|
const relativePath = node_path.relative(distDirectory, src).replace(/\\/gu, '/');
|
|
50
|
+
if (apiOnly && '' !== relativePath && ![
|
|
51
|
+
BACKEND_FEDERATION_MANIFEST_FILE,
|
|
52
|
+
BACKEND_FEDERATION_REMOTE_ENTRY_FILE,
|
|
53
|
+
ULTRAMODERN_BUILD_ARTIFACT_FILE
|
|
54
|
+
].includes(relativePath)) return false;
|
|
49
55
|
return 'release' !== relativePath && !relativePath.startsWith('release/') && 'public' !== relativePath && !relativePath.startsWith('public/') && shouldCopyToPublicAssets(src, distDirectory, publicAssetExcludes);
|
|
50
56
|
}
|
|
51
57
|
});
|
|
@@ -206,9 +206,9 @@ const prependBundlerChain = (environment, handler)=>{
|
|
|
206
206
|
};
|
|
207
207
|
const getWorkerEntries = (environment, normalizedConfig, appContext)=>{
|
|
208
208
|
const configuredEntries = environment.source?.entry;
|
|
209
|
-
if (!configuredEntries) return;
|
|
210
|
-
const entries = rewriteWorkerEntries(configuredEntries);
|
|
211
209
|
const effectApiEntry = getEffectBffEntry(normalizedConfig, appContext);
|
|
210
|
+
if (!configuredEntries && !effectApiEntry) return;
|
|
211
|
+
const entries = configuredEntries ? rewriteWorkerEntries(configuredEntries) : {};
|
|
212
212
|
return effectApiEntry ? {
|
|
213
213
|
...entries,
|
|
214
214
|
[BFF_EFFECT_WORKER_ENTRY_NAME]: [
|
|
@@ -288,7 +288,14 @@ const createCloudflareBundlerChain = (appContext, workerEntryNames)=>{
|
|
|
288
288
|
};
|
|
289
289
|
function getCloudflareBuilderEnvironments({ appContext, environments, normalizedConfig, resolveDeployProvider = resolveStdEnvDeployProvider }) {
|
|
290
290
|
if (!isCloudflareWorkerDeploy(normalizedConfig, resolveDeployProvider)) return environments;
|
|
291
|
-
const workerEnvironment = environments[SERVICE_WORKER_ENVIRONMENT_NAME]
|
|
291
|
+
const workerEnvironment = environments[SERVICE_WORKER_ENVIRONMENT_NAME] ?? (appContext.apiOnly ? {
|
|
292
|
+
output: {
|
|
293
|
+
target: 'web-worker'
|
|
294
|
+
},
|
|
295
|
+
source: {
|
|
296
|
+
entry: {}
|
|
297
|
+
}
|
|
298
|
+
} : void 0);
|
|
292
299
|
if (!workerEnvironment) return environments;
|
|
293
300
|
const workerEntries = getWorkerEntries(workerEnvironment, normalizedConfig, appContext);
|
|
294
301
|
if (!workerEntries) return environments;
|
|
@@ -304,6 +311,9 @@ function getCloudflareBuilderEnvironments({ appContext, environments, normalized
|
|
|
304
311
|
entry: workerEntries
|
|
305
312
|
}
|
|
306
313
|
}, createCloudflareBundlerChain(appContext, Object.keys(workerEntries)));
|
|
314
|
+
if (appContext.apiOnly) return {
|
|
315
|
+
[SERVICE_WORKER_ENVIRONMENT_NAME]: cloudflareWorkerEnvironment
|
|
316
|
+
};
|
|
307
317
|
return {
|
|
308
318
|
...environments,
|
|
309
319
|
[SERVICE_WORKER_ENVIRONMENT_NAME]: cloudflareWorkerEnvironment
|
|
@@ -25,30 +25,32 @@ const verifyDeliveryUnitIdentity = (issues, manifest, manifestPath, declared)=>{
|
|
|
25
25
|
]){
|
|
26
26
|
const marker = stamped.surfaces[surface];
|
|
27
27
|
const expected = declared?.surfaces?.[surface];
|
|
28
|
-
if (
|
|
29
|
-
if (
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
28
|
+
if (expected || marker) {
|
|
29
|
+
if (declared?.surfaces && !expected) {
|
|
30
|
+
if (marker) addIssue(issues, {
|
|
31
|
+
code: 'delivery-unit-drift',
|
|
32
|
+
message: `Cloudflare worker manifest declares an unexpected ${surface} delivery-unit surface for this topology profile.`,
|
|
33
|
+
path: manifestPath
|
|
34
|
+
});
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (!marker || 'object' != typeof marker) {
|
|
38
|
+
addIssue(issues, {
|
|
39
|
+
code: 'missing-delivery-unit',
|
|
40
|
+
message: `Cloudflare worker manifest is missing the ${surface} delivery-unit surface marker.`,
|
|
41
|
+
path: manifestPath
|
|
42
|
+
});
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
for (const field of DELIVERY_UNIT_IDENTITY_FIELDS){
|
|
46
|
+
const markerValue = deliveryUnitIdentityFieldValue(marker, field);
|
|
47
|
+
const stampedValue = deliveryUnitIdentityFieldValue(stamped, field);
|
|
48
|
+
assertEqual(issues, markerValue, stampedValue, {
|
|
49
|
+
code: 'delivery-unit-drift',
|
|
50
|
+
message: `Cloudflare worker manifest ${surface} surface deliveryUnit.${field} must derive from one delivery-unit record (expected ${stampedValue ?? 'undefined'}, received ${markerValue ?? 'undefined'}).`,
|
|
51
|
+
path: manifestPath
|
|
52
|
+
});
|
|
53
|
+
}
|
|
52
54
|
}
|
|
53
55
|
}
|
|
54
56
|
};
|
|
@@ -73,6 +73,16 @@ const verifyCloudflareOutput = async (options)=>{
|
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
if (wrangler) {
|
|
76
|
+
if ('string' != typeof wrangler.name || 0 === wrangler.name.trim().length) addIssue(issues, {
|
|
77
|
+
code: 'invalid-wrangler',
|
|
78
|
+
message: 'wrangler.json name must be a non-empty worker name.',
|
|
79
|
+
path: wranglerPath
|
|
80
|
+
});
|
|
81
|
+
if ('string' != typeof wrangler.compatibility_date || !/^\d{4}-\d{2}-\d{2}$/u.test(wrangler.compatibility_date)) addIssue(issues, {
|
|
82
|
+
code: 'invalid-wrangler',
|
|
83
|
+
message: 'wrangler.json compatibility_date must use YYYY-MM-DD.',
|
|
84
|
+
path: wranglerPath
|
|
85
|
+
});
|
|
76
86
|
assertEqual(issues, wrangler.main, outputPlan.wrangler.main, {
|
|
77
87
|
code: 'invalid-wrangler',
|
|
78
88
|
message: `wrangler.json main must be ${outputPlan.wrangler.main}.`,
|
|
@@ -43,6 +43,52 @@ const verifyManifestShape = (issues, manifest, manifestPath)=>{
|
|
|
43
43
|
message: 'Cloudflare output manifest routeSpec.routes must be array.',
|
|
44
44
|
path: manifestPath
|
|
45
45
|
});
|
|
46
|
+
const security = manifest.security;
|
|
47
|
+
if (security && 'object' == typeof security && 'boolean' == typeof security.enabled) {
|
|
48
|
+
const cors = security.cors;
|
|
49
|
+
if (!cors || 'object' != typeof cors || 'boolean' != typeof cors.assets || !Array.isArray(cors.allowedOrigins) || !Array.isArray(cors.allowedMethods) || !Array.isArray(cors.allowedHeaders)) addIssue(issues, {
|
|
50
|
+
code: 'invalid-manifest',
|
|
51
|
+
message: 'Cloudflare output manifest security.cors is incomplete.',
|
|
52
|
+
path: manifestPath
|
|
53
|
+
});
|
|
54
|
+
if (security.enabled) {
|
|
55
|
+
const headers = security.headers;
|
|
56
|
+
const csp = security.contentSecurityPolicy;
|
|
57
|
+
const noindex = security.noindex;
|
|
58
|
+
if (!headers || 'object' != typeof headers || ![
|
|
59
|
+
'string',
|
|
60
|
+
'boolean'
|
|
61
|
+
].includes(typeof headers.referrerPolicy) || ![
|
|
62
|
+
'string',
|
|
63
|
+
'boolean'
|
|
64
|
+
].includes(typeof headers.contentTypeOptions) || ![
|
|
65
|
+
'string',
|
|
66
|
+
'boolean'
|
|
67
|
+
].includes(typeof headers.permissionsPolicy)) addIssue(issues, {
|
|
68
|
+
code: 'invalid-manifest',
|
|
69
|
+
message: 'Cloudflare output manifest security.headers is incomplete.',
|
|
70
|
+
path: manifestPath
|
|
71
|
+
});
|
|
72
|
+
if (!csp || ![
|
|
73
|
+
'off',
|
|
74
|
+
'report-only',
|
|
75
|
+
'enforce'
|
|
76
|
+
].includes(csp.mode) || !csp.directives || 'object' != typeof csp.directives || Array.isArray(csp.directives) || Object.values(csp.directives).some((value)=>!Array.isArray(value) || value.some((entry)=>'string' != typeof entry))) addIssue(issues, {
|
|
77
|
+
code: 'invalid-manifest',
|
|
78
|
+
message: 'Cloudflare output manifest security.contentSecurityPolicy is invalid.',
|
|
79
|
+
path: manifestPath
|
|
80
|
+
});
|
|
81
|
+
if (!noindex || 'boolean' != typeof noindex.workersDev || 'boolean' != typeof noindex.localhost || !Array.isArray(noindex.previewHostnames)) addIssue(issues, {
|
|
82
|
+
code: 'invalid-manifest',
|
|
83
|
+
message: 'Cloudflare output manifest security.noindex is incomplete.',
|
|
84
|
+
path: manifestPath
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
} else addIssue(issues, {
|
|
88
|
+
code: 'invalid-manifest',
|
|
89
|
+
message: 'Cloudflare output manifest security.enabled must be a boolean.',
|
|
90
|
+
path: manifestPath
|
|
91
|
+
});
|
|
46
92
|
if (manifest.bff?.runtimeFramework === 'effect') assertEqual(issues, manifest.bff.dispatcherExport, BFF_EFFECT_WORKER_DISPATCHER_EXPORT, {
|
|
47
93
|
code: 'invalid-manifest',
|
|
48
94
|
message: `Cloudflare Effect BFF manifest dispatcherExport must be ${BFF_EFFECT_WORKER_DISPATCHER_EXPORT}.`,
|
|
@@ -85,7 +85,13 @@ const verifyCloudflareOutputMutationPolicy = async (options)=>{
|
|
|
85
85
|
const files = (await Promise.all(options.scanRoots.map((scanRoot)=>walkFiles(node_path.resolve(scanRoot))))).flat();
|
|
86
86
|
for (const file of files){
|
|
87
87
|
if (excludedFiles.has(file)) continue;
|
|
88
|
-
|
|
88
|
+
let source;
|
|
89
|
+
try {
|
|
90
|
+
source = await promises.readFile(file, 'utf-8');
|
|
91
|
+
} catch (error) {
|
|
92
|
+
if ('ENOENT' === error.code) continue;
|
|
93
|
+
throw error;
|
|
94
|
+
}
|
|
89
95
|
for (const forbidden of FORBIDDEN_MUTATION_PATTERNS)if (forbidden.pattern.test(source)) addIssue(issues, {
|
|
90
96
|
code: forbidden.code,
|
|
91
97
|
message: forbidden.message,
|
|
@@ -5,6 +5,10 @@ const dependencyKeys = [
|
|
|
5
5
|
'dependencies',
|
|
6
6
|
'optionalDependencies'
|
|
7
7
|
];
|
|
8
|
+
const rootDependencyKeys = [
|
|
9
|
+
...dependencyKeys,
|
|
10
|
+
'devDependencies'
|
|
11
|
+
];
|
|
8
12
|
const exactVersionPattern = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/u;
|
|
9
13
|
async function readPackageJson(filePath) {
|
|
10
14
|
return JSON.parse(await promises.readFile(filePath, 'utf8'));
|
|
@@ -20,14 +24,34 @@ function parseNpmAlias(aliasName, specifier) {
|
|
|
20
24
|
targetVersion: target.slice(versionSeparator + 1)
|
|
21
25
|
};
|
|
22
26
|
}
|
|
23
|
-
function aliasesFromPackageJson(packageJson) {
|
|
27
|
+
function aliasesFromPackageJson(packageJson, keys = dependencyKeys) {
|
|
24
28
|
const aliases = [];
|
|
25
|
-
for (const dependencyKey of
|
|
29
|
+
for (const dependencyKey of keys)for (const [aliasName, specifier] of Object.entries(packageJson[dependencyKey] ?? {})){
|
|
26
30
|
const alias = parseNpmAlias(aliasName, specifier);
|
|
27
31
|
if (alias) aliases.push(alias);
|
|
28
32
|
}
|
|
29
33
|
return aliases;
|
|
30
34
|
}
|
|
35
|
+
async function catalogAliasesFromInstalledPackages(appDirectory, packageJson) {
|
|
36
|
+
const aliases = [];
|
|
37
|
+
for (const dependencyKey of rootDependencyKeys)for (const [aliasName, specifier] of Object.entries(packageJson[dependencyKey] ?? {})){
|
|
38
|
+
if (!specifier.startsWith('catalog:')) continue;
|
|
39
|
+
let installedPackage;
|
|
40
|
+
try {
|
|
41
|
+
installedPackage = await readPackageJson(node_path.join(appDirectory, 'node_modules', aliasName, 'package.json'));
|
|
42
|
+
} catch (error) {
|
|
43
|
+
if ('ENOENT' === error.code) continue;
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
if (!installedPackage.name || !installedPackage.version) throw new Error(`Installed catalog package ${aliasName} has no identity`);
|
|
47
|
+
if (installedPackage.name !== aliasName) aliases.push({
|
|
48
|
+
aliasName,
|
|
49
|
+
targetName: installedPackage.name,
|
|
50
|
+
targetVersion: installedPackage.version
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
return aliases;
|
|
54
|
+
}
|
|
31
55
|
async function collectPackageRecords(nodeModulesDirectory) {
|
|
32
56
|
const records = [];
|
|
33
57
|
const visitedNodeModules = new Set();
|
|
@@ -149,7 +173,8 @@ async function preserveNpmAliases({ appDirectory, outputDirectory, implicitAlias
|
|
|
149
173
|
const records = await collectPackageRecords(node_path.join(outputDirectory, 'node_modules'));
|
|
150
174
|
const appPackageJson = await readPackageJson(node_path.join(appDirectory, 'package.json'));
|
|
151
175
|
const rootAliases = [
|
|
152
|
-
...aliasesFromPackageJson(appPackageJson),
|
|
176
|
+
...aliasesFromPackageJson(appPackageJson, rootDependencyKeys),
|
|
177
|
+
...await catalogAliasesFromInstalledPackages(appDirectory, appPackageJson),
|
|
153
178
|
...implicitAliases
|
|
154
179
|
].filter((alias)=>alias.aliasName !== alias.targetName);
|
|
155
180
|
const emittedRootAliases = [];
|