@bleedingdev/modern-js-app-tools-extensions 3.9.0-ultramodern.8 → 3.9.3-ultramodern.1
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/README.md +23 -0
- package/dist/cjs/backend-federation-build/codegen.js +29 -18
- package/dist/cjs/backend-federation-build/config.js +84 -45
- package/dist/cjs/backend-federation-build/emit.js +63 -22
- package/dist/cjs/build-config/build-environment.js +25 -162
- package/dist/cjs/build-config/internal-effect-discovery.js +143 -0
- package/dist/cjs/build-config/public.js +2 -5
- package/dist/cjs/build-config/remote-address.js +58 -0
- package/dist/cjs/cloudflare/artifacts.js +8 -19
- package/dist/cjs/cloudflare/delivery-unit.js +125 -43
- package/dist/cjs/cloudflare/index.js +14 -4
- package/dist/cjs/cloudflare/native-renderer.js +100 -0
- package/dist/cjs/cloudflare/renderer-identity.js +106 -0
- package/dist/cjs/cloudflare/worker-manifest.js +54 -13
- package/dist/cjs/cloudflare/worker-options.js +48 -0
- package/dist/cjs/cloudflare/wrangler-config.js +210 -13
- package/dist/cjs/cloudflare-builder.js +307 -89
- package/dist/cjs/cloudflare-output-contract.js +36 -4
- package/dist/cjs/cloudflare-output-verifier/identity.js +112 -23
- 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/cloudflare-output-verifier/worker-bundles.js +10 -5
- package/dist/cjs/config.js +10 -5
- package/dist/cjs/deploy-output/npmAliases.js +92 -6
- package/dist/cjs/deploy-output/plugin.js +27 -6
- package/dist/cjs/deploy-output/public-assets.js +146 -0
- package/dist/cjs/deploy-output/target.js +57 -1
- package/dist/cjs/deploy-output/trace-files.js +99 -0
- package/dist/cjs/module-federation-config/exposes.js +70 -0
- package/dist/cjs/module-federation-config/index.js +73 -0
- package/dist/cjs/module-federation-config/inspect.js +103 -0
- package/dist/cjs/module-federation-config/object-literal.js +87 -0
- package/dist/cjs/module-federation-config/syntax.js +151 -0
- package/dist/cjs/module-federation-config/types.js +18 -0
- package/dist/cjs/native-type-checker.js +164 -18
- package/dist/cjs/policy-defaults.js +110 -0
- package/dist/cjs/release-envelope/canonical.js +2 -9
- package/dist/cjs/release-envelope/contract.js +68 -0
- package/dist/cjs/release-envelope/framework-output.js +296 -105
- package/dist/cjs/release-envelope/index.js +150 -36
- package/dist/cjs/release-envelope/plugin.js +24 -15
- package/dist/cjs/release-envelope/renderer-output-stamp.js +170 -0
- package/dist/cjs/release-envelope/types.js +10 -8
- package/dist/cjs/release-identity.js +8 -5
- package/dist/cjs/renderer-build-identity.js +134 -0
- package/dist/cjs/runtime-package-resolution.js +118 -0
- package/dist/cjs/templates/cloudflare-entry.001-bootstrap-security.mjs +14 -6
- package/dist/cjs/templates/cloudflare-entry.002-assets-routes.mjs +10 -2
- package/dist/cjs/templates/cloudflare-entry.004-rendering-css.mjs +403 -96
- package/dist/cjs/templates/cloudflare-entry.005-worker-dispatch.mjs +530 -103
- package/dist/cjs/templates/cloudflare-entry.006-fetch-handler.mjs +26 -7
- package/dist/cjs/templates/cloudflare-worker-route-data.mjs +1069 -0
- package/dist/cjs/workspace-topology.js +84 -0
- package/dist/esm/backend-federation-build/codegen.mjs +29 -18
- package/dist/esm/backend-federation-build/config.mjs +74 -38
- package/dist/esm/backend-federation-build/emit.mjs +64 -23
- package/dist/esm/build-config/build-environment.mjs +26 -160
- package/dist/esm/build-config/internal-effect-discovery.mjs +102 -0
- package/dist/esm/build-config/public.mjs +1 -1
- package/dist/esm/build-config/remote-address.mjs +20 -0
- package/dist/esm/cloudflare/artifacts.mjs +8 -19
- package/dist/esm/cloudflare/delivery-unit.mjs +124 -45
- package/dist/esm/cloudflare/index.mjs +15 -5
- package/dist/esm/cloudflare/native-renderer.mjs +48 -0
- package/dist/esm/cloudflare/renderer-identity.mjs +57 -0
- package/dist/esm/cloudflare/worker-manifest.mjs +55 -14
- package/dist/esm/cloudflare/worker-options.mjs +10 -0
- package/dist/esm/cloudflare/wrangler-config.mjs +203 -12
- package/dist/esm/cloudflare-builder.mjs +301 -89
- package/dist/esm/cloudflare-output-contract.mjs +29 -3
- package/dist/esm/cloudflare-output-verifier/identity.mjs +113 -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/cloudflare-output-verifier/worker-bundles.mjs +11 -6
- package/dist/esm/config.mjs +3 -1
- package/dist/esm/deploy-output/npmAliases.mjs +92 -6
- package/dist/esm/deploy-output/plugin.mjs +24 -6
- package/dist/esm/deploy-output/public-assets.mjs +82 -0
- package/dist/esm/deploy-output/target.mjs +50 -3
- package/dist/esm/deploy-output/trace-files.mjs +47 -0
- package/dist/esm/module-federation-config/exposes.mjs +18 -0
- package/dist/esm/module-federation-config/index.mjs +4 -0
- package/dist/esm/module-federation-config/inspect.mjs +65 -0
- package/dist/esm/module-federation-config/object-literal.mjs +43 -0
- package/dist/esm/module-federation-config/syntax.mjs +101 -0
- package/dist/esm/module-federation-config/types.mjs +0 -0
- package/dist/esm/native-type-checker.mjs +158 -18
- package/dist/esm/policy-defaults.mjs +51 -0
- package/dist/esm/release-envelope/canonical.mjs +2 -9
- package/dist/esm/release-envelope/contract.mjs +21 -0
- package/dist/esm/release-envelope/framework-output.mjs +297 -106
- package/dist/esm/release-envelope/index.mjs +149 -38
- package/dist/esm/release-envelope/plugin.mjs +24 -15
- package/dist/esm/release-envelope/renderer-output-stamp.mjs +112 -0
- package/dist/esm/release-envelope/types.mjs +2 -3
- package/dist/esm/release-identity.mjs +8 -5
- package/dist/esm/renderer-build-identity.mjs +82 -0
- package/dist/esm/runtime-package-resolution.mjs +60 -0
- package/dist/esm/templates/cloudflare-entry.001-bootstrap-security.mjs +14 -6
- package/dist/esm/templates/cloudflare-entry.002-assets-routes.mjs +10 -2
- package/dist/esm/templates/cloudflare-entry.004-rendering-css.mjs +403 -96
- package/dist/esm/templates/cloudflare-entry.005-worker-dispatch.mjs +530 -103
- package/dist/esm/templates/cloudflare-entry.006-fetch-handler.mjs +26 -7
- package/dist/esm/templates/cloudflare-worker-route-data.mjs +1069 -0
- package/dist/esm/workspace-topology.mjs +21 -0
- package/dist/esm-node/backend-federation-build/codegen.mjs +29 -18
- package/dist/esm-node/backend-federation-build/config.mjs +74 -38
- package/dist/esm-node/backend-federation-build/emit.mjs +64 -23
- package/dist/esm-node/build-config/build-environment.mjs +26 -160
- package/dist/esm-node/build-config/internal-effect-discovery.mjs +103 -0
- package/dist/esm-node/build-config/public.mjs +1 -1
- package/dist/esm-node/build-config/remote-address.mjs +21 -0
- package/dist/esm-node/cloudflare/artifacts.mjs +8 -19
- package/dist/esm-node/cloudflare/delivery-unit.mjs +124 -45
- package/dist/esm-node/cloudflare/index.mjs +15 -5
- package/dist/esm-node/cloudflare/native-renderer.mjs +49 -0
- package/dist/esm-node/cloudflare/renderer-identity.mjs +58 -0
- package/dist/esm-node/cloudflare/worker-manifest.mjs +55 -14
- package/dist/esm-node/cloudflare/worker-options.mjs +11 -0
- package/dist/esm-node/cloudflare/wrangler-config.mjs +203 -12
- package/dist/esm-node/cloudflare-builder.mjs +301 -89
- package/dist/esm-node/cloudflare-output-contract.mjs +29 -3
- package/dist/esm-node/cloudflare-output-verifier/identity.mjs +113 -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/cloudflare-output-verifier/worker-bundles.mjs +11 -6
- package/dist/esm-node/config.mjs +3 -1
- package/dist/esm-node/deploy-output/npmAliases.mjs +92 -6
- package/dist/esm-node/deploy-output/plugin.mjs +24 -6
- package/dist/esm-node/deploy-output/public-assets.mjs +83 -0
- package/dist/esm-node/deploy-output/target.mjs +50 -3
- package/dist/esm-node/deploy-output/trace-files.mjs +48 -0
- package/dist/esm-node/module-federation-config/exposes.mjs +19 -0
- package/dist/esm-node/module-federation-config/index.mjs +5 -0
- package/dist/esm-node/module-federation-config/inspect.mjs +66 -0
- package/dist/esm-node/module-federation-config/object-literal.mjs +44 -0
- package/dist/esm-node/module-federation-config/syntax.mjs +102 -0
- package/dist/esm-node/module-federation-config/types.mjs +1 -0
- package/dist/esm-node/native-type-checker.mjs +158 -18
- package/dist/esm-node/policy-defaults.mjs +52 -0
- package/dist/esm-node/release-envelope/canonical.mjs +2 -9
- package/dist/esm-node/release-envelope/contract.mjs +22 -0
- package/dist/esm-node/release-envelope/framework-output.mjs +297 -106
- package/dist/esm-node/release-envelope/index.mjs +149 -38
- package/dist/esm-node/release-envelope/plugin.mjs +24 -15
- package/dist/esm-node/release-envelope/renderer-output-stamp.mjs +113 -0
- package/dist/esm-node/release-envelope/types.mjs +2 -3
- package/dist/esm-node/release-identity.mjs +8 -5
- package/dist/esm-node/renderer-build-identity.mjs +83 -0
- package/dist/esm-node/runtime-package-resolution.mjs +61 -0
- package/dist/esm-node/templates/cloudflare-entry.001-bootstrap-security.mjs +14 -6
- package/dist/esm-node/templates/cloudflare-entry.002-assets-routes.mjs +10 -2
- package/dist/esm-node/templates/cloudflare-entry.004-rendering-css.mjs +403 -96
- package/dist/esm-node/templates/cloudflare-entry.005-worker-dispatch.mjs +530 -103
- package/dist/esm-node/templates/cloudflare-entry.006-fetch-handler.mjs +26 -7
- package/dist/esm-node/templates/cloudflare-worker-route-data.mjs +1069 -0
- package/dist/esm-node/workspace-topology.mjs +22 -0
- package/dist/types/backend-federation-build/codegen.d.ts +30 -0
- package/dist/types/backend-federation-build/config.d.ts +10 -57
- package/dist/types/backend-federation-build/emit.d.ts +12 -2
- package/dist/types/backend-federation-build/index.d.ts +1 -1
- package/dist/types/build-config/build-environment.d.ts +1 -9
- package/dist/types/build-config/internal-effect-discovery.d.ts +4 -0
- package/dist/types/build-config/public.d.ts +1 -1
- package/dist/types/build-config/remote-address.d.ts +3 -0
- package/dist/types/cloudflare/artifacts.d.ts +7 -6
- package/dist/types/cloudflare/constants.d.ts +1 -1
- package/dist/types/cloudflare/delivery-unit.d.ts +38 -7
- package/dist/types/cloudflare/native-renderer.d.ts +30 -0
- package/dist/types/cloudflare/renderer-identity.d.ts +25 -0
- package/dist/types/cloudflare/worker-manifest.d.ts +17 -2
- package/dist/types/cloudflare/worker-options.d.ts +20 -0
- package/dist/types/cloudflare/wrangler-config.d.ts +55 -2
- package/dist/types/cloudflare-builder.d.ts +51 -8
- package/dist/types/cloudflare-output-contract.d.ts +5 -3
- package/dist/types/cloudflare-output-verifier/identity.d.ts +7 -3
- package/dist/types/cloudflare-output-verifier/worker-bundles.d.ts +2 -2
- package/dist/types/config.d.ts +98 -13
- package/dist/types/deploy-output/plugin.d.ts +29 -9
- package/dist/types/deploy-output/public-assets.d.ts +51 -0
- package/dist/types/deploy-output/target.d.ts +28 -5
- package/dist/types/deploy-output/trace-files.d.ts +17 -0
- package/dist/types/module-federation-config/exposes.d.ts +11 -0
- package/dist/types/module-federation-config/index.d.ts +5 -0
- package/dist/types/module-federation-config/inspect.d.ts +2 -0
- package/dist/types/module-federation-config/object-literal.d.ts +4 -0
- package/dist/types/module-federation-config/syntax.d.ts +7 -0
- package/dist/types/module-federation-config/types.d.ts +20 -0
- package/dist/types/native-type-checker.d.ts +26 -3
- package/dist/types/policy-defaults.d.ts +56 -0
- package/dist/types/release-envelope/canonical.d.ts +5 -5
- package/dist/types/release-envelope/contract.d.mts +8 -0
- package/dist/types/release-envelope/framework-output.d.ts +23 -13
- package/dist/types/release-envelope/index.d.ts +17 -5
- package/dist/types/release-envelope/plugin.d.ts +10 -6
- package/dist/types/release-envelope/renderer-output-stamp.d.ts +34 -0
- package/dist/types/release-envelope/types.d.ts +42 -4
- package/dist/types/release-identity.d.ts +3 -2
- package/dist/types/renderer-build-identity.d.ts +30 -0
- package/dist/types/runtime-package-resolution.d.ts +28 -0
- package/dist/types/templates/cloudflare-worker-route-data.d.mts +14 -0
- package/dist/types/workspace-topology.d.ts +91 -0
- package/package.json +97 -26
- package/src/backend-federation-build/codegen.ts +28 -19
- package/src/backend-federation-build/config.ts +189 -123
- package/src/backend-federation-build/emit.ts +119 -38
- package/src/backend-federation-build/index.ts +4 -1
- package/src/build-config/build-environment.ts +59 -285
- package/src/build-config/internal-effect-discovery.ts +239 -0
- package/src/build-config/public.ts +0 -1
- package/src/build-config/remote-address.ts +33 -0
- package/src/cloudflare/artifacts.ts +22 -43
- package/src/cloudflare/delivery-unit.ts +283 -60
- package/src/cloudflare/index.ts +35 -19
- package/src/cloudflare/native-renderer.ts +117 -0
- package/src/cloudflare/renderer-identity.ts +171 -0
- package/src/cloudflare/worker-manifest.ts +108 -10
- package/src/cloudflare/worker-options.ts +60 -0
- package/src/cloudflare/wrangler-config.ts +390 -14
- package/src/cloudflare-builder.ts +543 -170
- package/src/cloudflare-output-contract.ts +45 -2
- package/src/cloudflare-output-verifier/identity.ts +138 -7
- 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/cloudflare-output-verifier/worker-bundles.ts +27 -11
- package/src/config.ts +101 -19
- package/src/deploy-output/npmAliases.ts +141 -4
- package/src/deploy-output/plugin.ts +55 -18
- package/src/deploy-output/public-assets.ts +224 -0
- package/src/deploy-output/target.ts +97 -14
- package/src/deploy-output/trace-files.ts +80 -0
- package/src/module-federation-config/exposes.ts +46 -0
- package/src/module-federation-config/index.ts +22 -0
- package/src/module-federation-config/inspect.ts +157 -0
- package/src/module-federation-config/object-literal.ts +76 -0
- package/src/module-federation-config/syntax.ts +212 -0
- package/src/module-federation-config/types.ts +22 -0
- package/src/native-type-checker.ts +281 -26
- package/src/policy-defaults.ts +157 -0
- package/src/release-envelope/canonical.ts +6 -18
- package/src/release-envelope/contract.mjs +28 -0
- package/src/release-envelope/framework-output.ts +681 -156
- package/src/release-envelope/index.ts +315 -60
- package/src/release-envelope/plugin.ts +54 -38
- package/src/release-envelope/renderer-output-stamp.ts +210 -0
- package/src/release-envelope/types.ts +75 -6
- package/src/release-identity.ts +19 -2
- package/src/renderer-build-identity.ts +168 -0
- package/src/runtime-package-resolution.ts +115 -0
- package/src/templates/cloudflare-entry.001-bootstrap-security.mjs +14 -6
- package/src/templates/cloudflare-entry.002-assets-routes.mjs +10 -2
- package/src/templates/cloudflare-entry.004-rendering-css.mjs +403 -96
- package/src/templates/cloudflare-entry.005-worker-dispatch.mjs +530 -103
- package/src/templates/cloudflare-entry.006-fetch-handler.mjs +26 -7
- package/src/templates/cloudflare-worker-route-data.mjs +1069 -0
- package/src/workspace-topology.ts +125 -0
- package/dist/cjs/templates/cloudflare-worker-fs-promises.mjs +0 -11
- package/dist/cjs/templates/cloudflare-worker-loadable-server.mjs +0 -281
- package/dist/cjs/templates/cloudflare-worker-mf-ssr-runtime-plugin.mjs +0 -14
- package/dist/cjs/templates/cloudflare-worker-path.mjs +0 -98
- package/dist/esm/templates/cloudflare-worker-fs-promises.mjs +0 -11
- package/dist/esm/templates/cloudflare-worker-loadable-server.mjs +0 -281
- package/dist/esm/templates/cloudflare-worker-mf-ssr-runtime-plugin.mjs +0 -14
- package/dist/esm/templates/cloudflare-worker-path.mjs +0 -98
- package/dist/esm-node/templates/cloudflare-worker-fs-promises.mjs +0 -11
- package/dist/esm-node/templates/cloudflare-worker-loadable-server.mjs +0 -281
- package/dist/esm-node/templates/cloudflare-worker-mf-ssr-runtime-plugin.mjs +0 -14
- package/dist/esm-node/templates/cloudflare-worker-path.mjs +0 -98
- package/dist/types/templates/cloudflare-worker-fs-promises.d.mts +0 -5
- package/dist/types/templates/cloudflare-worker-loadable-server.d.mts +0 -48
- package/dist/types/templates/cloudflare-worker-mf-ssr-runtime-plugin.d.mts +0 -13
- package/dist/types/templates/cloudflare-worker-path.d.mts +0 -21
- package/src/templates/cloudflare-worker-fs-promises.mjs +0 -11
- package/src/templates/cloudflare-worker-loadable-server.mjs +0 -281
- package/src/templates/cloudflare-worker-mf-ssr-runtime-plugin.mjs +0 -14
- package/src/templates/cloudflare-worker-path.mjs +0 -98
|
@@ -5,6 +5,7 @@ type PackageJson = {
|
|
|
5
5
|
name?: string;
|
|
6
6
|
version?: string;
|
|
7
7
|
dependencies?: Record<string, string>;
|
|
8
|
+
devDependencies?: Record<string, string>;
|
|
8
9
|
optionalDependencies?: Record<string, string>;
|
|
9
10
|
[key: string]: unknown;
|
|
10
11
|
};
|
|
@@ -21,6 +22,7 @@ export type NpmAlias = {
|
|
|
21
22
|
};
|
|
22
23
|
|
|
23
24
|
const dependencyKeys = ['dependencies', 'optionalDependencies'] as const;
|
|
25
|
+
const rootDependencyKeys = [...dependencyKeys, 'devDependencies'] as const;
|
|
24
26
|
const exactVersionPattern =
|
|
25
27
|
/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/u;
|
|
26
28
|
|
|
@@ -51,9 +53,12 @@ function parseNpmAlias(
|
|
|
51
53
|
};
|
|
52
54
|
}
|
|
53
55
|
|
|
54
|
-
function aliasesFromPackageJson(
|
|
56
|
+
function aliasesFromPackageJson(
|
|
57
|
+
packageJson: PackageJson,
|
|
58
|
+
keys: readonly (typeof rootDependencyKeys)[number][] = dependencyKeys,
|
|
59
|
+
): NpmAlias[] {
|
|
55
60
|
const aliases: NpmAlias[] = [];
|
|
56
|
-
for (const dependencyKey of
|
|
61
|
+
for (const dependencyKey of keys) {
|
|
57
62
|
for (const [aliasName, specifier] of Object.entries(
|
|
58
63
|
packageJson[dependencyKey] ?? {},
|
|
59
64
|
)) {
|
|
@@ -66,6 +71,119 @@ function aliasesFromPackageJson(packageJson: PackageJson): NpmAlias[] {
|
|
|
66
71
|
return aliases;
|
|
67
72
|
}
|
|
68
73
|
|
|
74
|
+
async function catalogAliasesFromInstalledPackages(
|
|
75
|
+
appDirectory: string,
|
|
76
|
+
packageJson: PackageJson,
|
|
77
|
+
): Promise<NpmAlias[]> {
|
|
78
|
+
const aliases: NpmAlias[] = [];
|
|
79
|
+
for (const dependencyKey of rootDependencyKeys) {
|
|
80
|
+
for (const [aliasName, specifier] of Object.entries(
|
|
81
|
+
packageJson[dependencyKey] ?? {},
|
|
82
|
+
)) {
|
|
83
|
+
if (!specifier.startsWith('catalog:')) {
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
let installedPackage: PackageJson;
|
|
87
|
+
try {
|
|
88
|
+
installedPackage = await readPackageJson(
|
|
89
|
+
path.join(appDirectory, 'node_modules', aliasName, 'package.json'),
|
|
90
|
+
);
|
|
91
|
+
} catch (error) {
|
|
92
|
+
if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
throw error;
|
|
96
|
+
}
|
|
97
|
+
if (!installedPackage.name || !installedPackage.version) {
|
|
98
|
+
throw new Error(
|
|
99
|
+
`Installed catalog package ${aliasName} has no identity`,
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
if (installedPackage.name !== aliasName) {
|
|
103
|
+
aliases.push({
|
|
104
|
+
aliasName,
|
|
105
|
+
targetName: installedPackage.name,
|
|
106
|
+
targetVersion: installedPackage.version,
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return aliases;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Overrides change the installed identity without changing the consumer's
|
|
115
|
+
// dependency specifier. Read source edges to restore names after emission.
|
|
116
|
+
async function installedAliasesByOwner(
|
|
117
|
+
appDirectory: string,
|
|
118
|
+
emittedOwners: Set<string>,
|
|
119
|
+
) {
|
|
120
|
+
const aliasesByOwner = new Map<string, NpmAlias[]>();
|
|
121
|
+
let rootAliases: NpmAlias[] = [];
|
|
122
|
+
const visited = new Set<string>();
|
|
123
|
+
const rootFile = await fs.realpath(path.join(appDirectory, 'package.json'));
|
|
124
|
+
const pending = [rootFile];
|
|
125
|
+
while (pending.length) {
|
|
126
|
+
const file = await fs.realpath(pending.pop()!);
|
|
127
|
+
if (visited.has(file)) continue;
|
|
128
|
+
visited.add(file);
|
|
129
|
+
const owner = await readPackageJson(file);
|
|
130
|
+
const aliases: NpmAlias[] = [];
|
|
131
|
+
const keys = file === rootFile ? rootDependencyKeys : dependencyKeys;
|
|
132
|
+
for (const key of keys) {
|
|
133
|
+
for (const name of Object.keys(owner[key] ?? {})) {
|
|
134
|
+
let directory = path.dirname(file);
|
|
135
|
+
while (true) {
|
|
136
|
+
const packageFile = path.join(
|
|
137
|
+
directory,
|
|
138
|
+
'node_modules',
|
|
139
|
+
name,
|
|
140
|
+
'package.json',
|
|
141
|
+
);
|
|
142
|
+
let target: PackageJson;
|
|
143
|
+
try {
|
|
144
|
+
target = await readPackageJson(packageFile);
|
|
145
|
+
} catch (error) {
|
|
146
|
+
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;
|
|
147
|
+
const parent = path.dirname(directory);
|
|
148
|
+
if (parent === directory) break;
|
|
149
|
+
directory = parent;
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
if (!target.name || !target.version) {
|
|
153
|
+
throw new Error(`Installed package ${name} has no identity`);
|
|
154
|
+
}
|
|
155
|
+
pending.push(packageFile);
|
|
156
|
+
if (target.name !== name) {
|
|
157
|
+
aliases.push({
|
|
158
|
+
aliasName: name,
|
|
159
|
+
targetName: target.name,
|
|
160
|
+
targetVersion: target.version,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (file === rootFile) rootAliases = aliases;
|
|
168
|
+
const identity = `${owner.name}@${owner.version}`;
|
|
169
|
+
if (emittedOwners.has(identity)) {
|
|
170
|
+
const existing = aliasesByOwner.get(identity);
|
|
171
|
+
const normalized = (items: NpmAlias[]) =>
|
|
172
|
+
items
|
|
173
|
+
.map(item => `${item.aliasName}=${aliasSpecifier(item)}`)
|
|
174
|
+
.sort()
|
|
175
|
+
.join('\n');
|
|
176
|
+
if (existing && normalized(existing) !== normalized(aliases)) {
|
|
177
|
+
throw new Error(
|
|
178
|
+
`Cannot preserve npm aliases for ${identity}: installed contexts resolve different dependency identities`,
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
aliasesByOwner.set(identity, aliases);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return { aliasesByOwner, rootAliases };
|
|
185
|
+
}
|
|
186
|
+
|
|
69
187
|
async function collectPackageRecords(
|
|
70
188
|
nodeModulesDirectory: string,
|
|
71
189
|
): Promise<PackageRecord[]> {
|
|
@@ -277,8 +395,21 @@ export async function preserveNpmAliases({
|
|
|
277
395
|
const appPackageJson = await readPackageJson(
|
|
278
396
|
path.join(appDirectory, 'package.json'),
|
|
279
397
|
);
|
|
398
|
+
const installedAliases = await installedAliasesByOwner(
|
|
399
|
+
appDirectory,
|
|
400
|
+
new Set(
|
|
401
|
+
records.map(
|
|
402
|
+
({ packageJson }) => `${packageJson.name}@${packageJson.version}`,
|
|
403
|
+
),
|
|
404
|
+
),
|
|
405
|
+
);
|
|
280
406
|
const rootAliases = [
|
|
281
|
-
...aliasesFromPackageJson(appPackageJson),
|
|
407
|
+
...aliasesFromPackageJson(appPackageJson, rootDependencyKeys),
|
|
408
|
+
...installedAliases.rootAliases,
|
|
409
|
+
...(await catalogAliasesFromInstalledPackages(
|
|
410
|
+
appDirectory,
|
|
411
|
+
appPackageJson,
|
|
412
|
+
)),
|
|
282
413
|
...implicitAliases,
|
|
283
414
|
].filter(alias => alias.aliasName !== alias.targetName);
|
|
284
415
|
|
|
@@ -302,7 +433,13 @@ export async function preserveNpmAliases({
|
|
|
302
433
|
}
|
|
303
434
|
|
|
304
435
|
for (const record of records) {
|
|
305
|
-
|
|
436
|
+
const ownerAliases = [
|
|
437
|
+
...aliasesFromPackageJson(record.packageJson),
|
|
438
|
+
...(installedAliases.aliasesByOwner.get(
|
|
439
|
+
`${record.packageJson.name}@${record.packageJson.version}`,
|
|
440
|
+
) ?? []),
|
|
441
|
+
];
|
|
442
|
+
for (const alias of ownerAliases.filter(
|
|
306
443
|
candidate => candidate.aliasName !== candidate.targetName,
|
|
307
444
|
)) {
|
|
308
445
|
const target = await resolveAliasTarget(
|
|
@@ -1,35 +1,34 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import type { NodePublicAssetConfig } from '../config';
|
|
3
4
|
import { preserveNpmAliases, readPackageIdentity } from './npmAliases';
|
|
4
5
|
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
NODE_PUBLIC_ASSET_SCOPE,
|
|
7
|
+
normalizeDeclaredPublicAssets,
|
|
8
|
+
stageDeclaredPublicAssets,
|
|
9
|
+
} from './public-assets';
|
|
10
|
+
import { getDeployingTarget, type ResolvedDeployTarget } from './target';
|
|
11
|
+
|
|
12
|
+
export interface DeployOutputAppContext {
|
|
13
|
+
appDirectory: string;
|
|
14
|
+
metaName: string;
|
|
15
|
+
deployTarget?: ResolvedDeployTarget;
|
|
16
|
+
}
|
|
8
17
|
|
|
9
18
|
export interface DeployOutputPluginApi {
|
|
10
|
-
getAppContext():
|
|
11
|
-
getNormalizedConfig(): DeployOutputConfig;
|
|
19
|
+
getAppContext(): DeployOutputAppContext;
|
|
12
20
|
onAfterDeploy(handler: () => Promise<void>): void;
|
|
13
21
|
}
|
|
14
22
|
|
|
15
|
-
export const createDeployOutputAliasesPlugin = ({
|
|
16
|
-
resolveDeployTarget = defaultResolveDeployTarget,
|
|
17
|
-
}: {
|
|
18
|
-
resolveDeployTarget?: (config: DeployOutputConfig) => string;
|
|
19
|
-
} = {}) => ({
|
|
23
|
+
export const createDeployOutputAliasesPlugin = () => ({
|
|
20
24
|
name: '@modern-js/deploy-output-aliases',
|
|
21
25
|
setup(api: DeployOutputPluginApi) {
|
|
22
26
|
api.onAfterDeploy(async () => {
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
if (
|
|
26
|
-
(metaName !== 'modern-js' &&
|
|
27
|
-
!config.deploy?.target &&
|
|
28
|
-
!process.env.MODERNJS_DEPLOY) ||
|
|
29
|
-
resolveDeployTarget(config) !== 'node'
|
|
30
|
-
) {
|
|
27
|
+
const appContext = api.getAppContext();
|
|
28
|
+
if (getDeployingTarget(appContext) !== 'node') {
|
|
31
29
|
return;
|
|
32
30
|
}
|
|
31
|
+
const { appDirectory } = appContext;
|
|
33
32
|
|
|
34
33
|
const entry = createRequire(__filename).resolve('@modern-js/prod-server');
|
|
35
34
|
const identity = await readPackageIdentity(entry);
|
|
@@ -47,3 +46,41 @@ export const createDeployOutputAliasesPlugin = ({
|
|
|
47
46
|
});
|
|
48
47
|
},
|
|
49
48
|
});
|
|
49
|
+
|
|
50
|
+
export interface DeployOutputPublicAssetsConfig {
|
|
51
|
+
deploy?: { node?: { publicAssets?: NodePublicAssetConfig[] } };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface DeployOutputPublicAssetsPluginApi {
|
|
55
|
+
getAppContext(): DeployOutputAppContext;
|
|
56
|
+
getNormalizedConfig(): DeployOutputPublicAssetsConfig;
|
|
57
|
+
onAfterDeploy(handler: () => Promise<void>): void;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Stage `deploy.node.publicAssets` into the Node deploy output. Cloudflare
|
|
62
|
+
* stages `deploy.worker.publicAssets` inside its deploy preset.
|
|
63
|
+
*/
|
|
64
|
+
export const createDeployOutputPublicAssetsPlugin = () => ({
|
|
65
|
+
name: '@modern-js/deploy-output-public-assets',
|
|
66
|
+
setup(api: DeployOutputPublicAssetsPluginApi) {
|
|
67
|
+
api.onAfterDeploy(async () => {
|
|
68
|
+
const appContext = api.getAppContext();
|
|
69
|
+
if (getDeployingTarget(appContext) !== 'node') {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const { appDirectory } = appContext;
|
|
73
|
+
const config = api.getNormalizedConfig();
|
|
74
|
+
|
|
75
|
+
await stageDeclaredPublicAssets({
|
|
76
|
+
appDirectory,
|
|
77
|
+
outputDirectory: path.join(appDirectory, '.output'),
|
|
78
|
+
assets: normalizeDeclaredPublicAssets(
|
|
79
|
+
config.deploy?.node?.publicAssets,
|
|
80
|
+
NODE_PUBLIC_ASSET_SCOPE,
|
|
81
|
+
),
|
|
82
|
+
scope: NODE_PUBLIC_ASSET_SCOPE,
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
},
|
|
86
|
+
});
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { normalizeRelativePath } from '../cloudflare/utils';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Deploy output directory that receives declared public assets on every
|
|
7
|
+
* target: Cloudflare Worker Static Assets and the Node deploy output.
|
|
8
|
+
*/
|
|
9
|
+
export const DECLARED_PUBLIC_ASSET_DIRECTORY = 'public';
|
|
10
|
+
|
|
11
|
+
export interface DeclaredPublicAssetConfig {
|
|
12
|
+
from: string;
|
|
13
|
+
to: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface DeclaredPublicAssetScope {
|
|
17
|
+
/** Config path used in errors, for example `deploy.node.publicAssets`. */
|
|
18
|
+
label: string;
|
|
19
|
+
/** Output described in destination errors. */
|
|
20
|
+
output: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface DeclaredPublicAsset {
|
|
24
|
+
from: string;
|
|
25
|
+
to: string;
|
|
26
|
+
index: number;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const CLOUDFLARE_PUBLIC_ASSET_SCOPE: DeclaredPublicAssetScope = {
|
|
30
|
+
label: 'deploy.worker.publicAssets',
|
|
31
|
+
output: 'Cloudflare public output',
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const NODE_PUBLIC_ASSET_SCOPE: DeclaredPublicAssetScope = {
|
|
35
|
+
label: 'deploy.node.publicAssets',
|
|
36
|
+
output: 'Node public output',
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export const normalizeDeclaredPublicAssets = (
|
|
40
|
+
assets: readonly DeclaredPublicAssetConfig[] | undefined,
|
|
41
|
+
scope: DeclaredPublicAssetScope,
|
|
42
|
+
): DeclaredPublicAsset[] =>
|
|
43
|
+
(assets ?? []).map((asset, index) => ({
|
|
44
|
+
from: normalizeRelativePath(
|
|
45
|
+
asset?.from,
|
|
46
|
+
`${scope.label}[${index}].from`,
|
|
47
|
+
'app root',
|
|
48
|
+
),
|
|
49
|
+
to: normalizeRelativePath(
|
|
50
|
+
asset?.to,
|
|
51
|
+
`${scope.label}[${index}].to`,
|
|
52
|
+
scope.output,
|
|
53
|
+
{ allowRoot: true },
|
|
54
|
+
),
|
|
55
|
+
index,
|
|
56
|
+
}));
|
|
57
|
+
|
|
58
|
+
interface DeclaredPublicAssetFile {
|
|
59
|
+
index: number;
|
|
60
|
+
/** Destination relative to the deploy output root, POSIX separators. */
|
|
61
|
+
logicalPath: string;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const isMissingPathError = (error: unknown) =>
|
|
65
|
+
error instanceof Error &&
|
|
66
|
+
'code' in error &&
|
|
67
|
+
(error as NodeJS.ErrnoException).code === 'ENOENT';
|
|
68
|
+
|
|
69
|
+
const pathExists = async (filePath: string) => {
|
|
70
|
+
try {
|
|
71
|
+
await fs.lstat(filePath);
|
|
72
|
+
return true;
|
|
73
|
+
} catch (error) {
|
|
74
|
+
if (isMissingPathError(error)) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const listDeclaredPublicAssetFiles = async (
|
|
82
|
+
appDirectory: string,
|
|
83
|
+
assets: readonly DeclaredPublicAsset[],
|
|
84
|
+
scope: DeclaredPublicAssetScope,
|
|
85
|
+
): Promise<DeclaredPublicAssetFile[]> => {
|
|
86
|
+
const files: DeclaredPublicAssetFile[] = [];
|
|
87
|
+
const owners = new Map<string, number>();
|
|
88
|
+
const add = (index: number, logicalPath: string) => {
|
|
89
|
+
const owner = owners.get(logicalPath);
|
|
90
|
+
if (owner !== undefined) {
|
|
91
|
+
throw new Error(
|
|
92
|
+
`${scope.label}[${index}] and ${scope.label}[${owner}] both stage ${JSON.stringify(logicalPath)}.`,
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
owners.set(logicalPath, index);
|
|
96
|
+
files.push({ index, logicalPath });
|
|
97
|
+
};
|
|
98
|
+
const visit = async (
|
|
99
|
+
asset: DeclaredPublicAsset,
|
|
100
|
+
sourcePath: string,
|
|
101
|
+
destination: string,
|
|
102
|
+
): Promise<void> => {
|
|
103
|
+
const stat = await fs.lstat(sourcePath);
|
|
104
|
+
if (stat.isSymbolicLink()) {
|
|
105
|
+
throw new Error(
|
|
106
|
+
`${scope.label}[${asset.index}].from must not contain symbolic links: ${path
|
|
107
|
+
.relative(appDirectory, sourcePath)
|
|
108
|
+
.replace(/\\/gu, '/')}`,
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
if (stat.isFile()) {
|
|
112
|
+
if (destination === DECLARED_PUBLIC_ASSET_DIRECTORY) {
|
|
113
|
+
throw new Error(
|
|
114
|
+
`${scope.label}[${asset.index}].to must name a file when ${scope.label}[${asset.index}].from is a file.`,
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
add(asset.index, destination);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (!stat.isDirectory()) {
|
|
121
|
+
throw new Error(
|
|
122
|
+
`${scope.label}[${asset.index}].from must contain only files and directories: ${path
|
|
123
|
+
.relative(appDirectory, sourcePath)
|
|
124
|
+
.replace(/\\/gu, '/')}`,
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
const entries = (await fs.readdir(sourcePath)).sort();
|
|
128
|
+
for (const entry of entries) {
|
|
129
|
+
await visit(
|
|
130
|
+
asset,
|
|
131
|
+
path.join(sourcePath, entry),
|
|
132
|
+
path.posix.join(destination, entry),
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
for (const asset of assets) {
|
|
138
|
+
const sourcePath = path.join(appDirectory, asset.from);
|
|
139
|
+
if (!(await pathExists(sourcePath))) {
|
|
140
|
+
throw new Error(
|
|
141
|
+
`${scope.label}[${asset.index}].from does not exist: ${asset.from}`,
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
await visit(
|
|
145
|
+
asset,
|
|
146
|
+
sourcePath,
|
|
147
|
+
path.posix.join(DECLARED_PUBLIC_ASSET_DIRECTORY, asset.to),
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
return files;
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Resolve the files declared public assets add to a deploy output that was
|
|
155
|
+
* copied from `generatedRoot`, relative to the deploy output root. Declared
|
|
156
|
+
* files that replace a generated file are not part of the result, matching
|
|
157
|
+
* {@link stageDeclaredPublicAssets}.
|
|
158
|
+
*/
|
|
159
|
+
export const resolveAddedDeclaredPublicAssetPaths = async ({
|
|
160
|
+
appDirectory,
|
|
161
|
+
assets,
|
|
162
|
+
generatedRoot,
|
|
163
|
+
scope,
|
|
164
|
+
}: {
|
|
165
|
+
appDirectory: string;
|
|
166
|
+
assets: readonly DeclaredPublicAsset[];
|
|
167
|
+
generatedRoot: string;
|
|
168
|
+
scope: DeclaredPublicAssetScope;
|
|
169
|
+
}): Promise<string[]> => {
|
|
170
|
+
const added: string[] = [];
|
|
171
|
+
for (const file of await listDeclaredPublicAssetFiles(
|
|
172
|
+
appDirectory,
|
|
173
|
+
assets,
|
|
174
|
+
scope,
|
|
175
|
+
)) {
|
|
176
|
+
if (!(await pathExists(path.join(generatedRoot, file.logicalPath)))) {
|
|
177
|
+
added.push(file.logicalPath);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return added.sort();
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Copy declared public assets into the deploy output after the framework has
|
|
185
|
+
* staged its own public files.
|
|
186
|
+
*
|
|
187
|
+
* A declared file may replace a generated public file, such as an app-owned
|
|
188
|
+
* `robots.txt`; the replaced file keeps its generated release classification.
|
|
189
|
+
*
|
|
190
|
+
* @returns the files the declarations added, relative to the deploy output
|
|
191
|
+
* root. The release envelope records these as declared public assets.
|
|
192
|
+
*/
|
|
193
|
+
export const stageDeclaredPublicAssets = async ({
|
|
194
|
+
appDirectory,
|
|
195
|
+
outputDirectory,
|
|
196
|
+
assets,
|
|
197
|
+
scope,
|
|
198
|
+
}: {
|
|
199
|
+
appDirectory: string;
|
|
200
|
+
outputDirectory: string;
|
|
201
|
+
assets: readonly DeclaredPublicAsset[];
|
|
202
|
+
scope: DeclaredPublicAssetScope;
|
|
203
|
+
}): Promise<string[]> => {
|
|
204
|
+
const files = await listDeclaredPublicAssetFiles(appDirectory, assets, scope);
|
|
205
|
+
const added: string[] = [];
|
|
206
|
+
for (const file of files) {
|
|
207
|
+
const asset = assets.find(candidate => candidate.index === file.index)!;
|
|
208
|
+
const relativePath = path.posix.relative(
|
|
209
|
+
path.posix.join(DECLARED_PUBLIC_ASSET_DIRECTORY, asset.to),
|
|
210
|
+
file.logicalPath,
|
|
211
|
+
);
|
|
212
|
+
const destination = path.join(outputDirectory, file.logicalPath);
|
|
213
|
+
const replacesGeneratedFile = await pathExists(destination);
|
|
214
|
+
await fs.mkdir(path.dirname(destination), { recursive: true });
|
|
215
|
+
await fs.copyFile(
|
|
216
|
+
path.join(appDirectory, asset.from, relativePath),
|
|
217
|
+
destination,
|
|
218
|
+
);
|
|
219
|
+
if (!replacesGeneratedFile) {
|
|
220
|
+
added.push(file.logicalPath);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return added.sort();
|
|
224
|
+
};
|
|
@@ -1,10 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { getArgv } from '@modern-js/utils';
|
|
2
|
+
import { provider as detectedProvider } from 'std-env';
|
|
2
3
|
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
export const DEPLOY_TARGETS = [
|
|
5
|
+
'node',
|
|
6
|
+
'vercel',
|
|
7
|
+
'netlify',
|
|
8
|
+
'ghPages',
|
|
9
|
+
'cloudflare',
|
|
10
|
+
] as const;
|
|
11
|
+
|
|
12
|
+
export type DeployTarget = (typeof DEPLOY_TARGETS)[number];
|
|
6
13
|
|
|
7
|
-
const
|
|
14
|
+
export const DEPLOY_TARGET_FLAG = '--deploy-target';
|
|
15
|
+
|
|
16
|
+
const providerDeployTargets: Partial<Record<string, DeployTarget>> = {
|
|
8
17
|
vercel: 'vercel',
|
|
9
18
|
netlify: 'netlify',
|
|
10
19
|
cloudflare: 'cloudflare',
|
|
@@ -12,12 +21,86 @@ const providerDeployTargets: Partial<Record<string, string>> = {
|
|
|
12
21
|
cloudflare_workers: 'cloudflare',
|
|
13
22
|
};
|
|
14
23
|
|
|
15
|
-
export
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
export interface ResolvedDeployTarget {
|
|
25
|
+
target: DeployTarget;
|
|
26
|
+
/** The flag, `deploy.target` or `MODERNJS_DEPLOY` chose the target. */
|
|
27
|
+
explicit: boolean;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface ResolveDeployTargetOptions {
|
|
31
|
+
argv?: readonly string[];
|
|
32
|
+
configTarget?: string;
|
|
33
|
+
env?: string;
|
|
34
|
+
provider?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const readDeployTargetFlag = (argv: readonly string[]) => {
|
|
38
|
+
for (const [index, arg] of argv.entries()) {
|
|
39
|
+
if (arg === DEPLOY_TARGET_FLAG) {
|
|
40
|
+
const value = argv[index + 1];
|
|
41
|
+
if (value === undefined || value.startsWith('-')) {
|
|
42
|
+
throw new Error(`${DEPLOY_TARGET_FLAG} needs a value.`);
|
|
43
|
+
}
|
|
44
|
+
return value;
|
|
45
|
+
}
|
|
46
|
+
if (arg.startsWith(`${DEPLOY_TARGET_FLAG}=`)) {
|
|
47
|
+
return arg.slice(DEPLOY_TARGET_FLAG.length + 1);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return undefined;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const assertDeployTarget = (target: string, source: string): DeployTarget => {
|
|
54
|
+
if (!(DEPLOY_TARGETS as readonly string[]).includes(target)) {
|
|
55
|
+
throw new Error(
|
|
56
|
+
`Unknown deploy target '${target}' from ${source}. Use one of: ${DEPLOY_TARGETS.join(', ')}.`,
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
return target as DeployTarget;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* The one deploy-target resolver: `--deploy-target` > `deploy.target` >
|
|
64
|
+
* `MODERNJS_DEPLOY` > detected CI provider > `node`. app-tools stores the
|
|
65
|
+
* result in the app context once; config files that branch on the target call
|
|
66
|
+
* it without `configTarget`.
|
|
67
|
+
*/
|
|
68
|
+
export const resolveDeployTarget = ({
|
|
69
|
+
argv = getArgv(),
|
|
70
|
+
configTarget,
|
|
71
|
+
env = process.env.MODERNJS_DEPLOY,
|
|
72
|
+
provider = detectedProvider,
|
|
73
|
+
}: ResolveDeployTargetOptions = {}): ResolvedDeployTarget => {
|
|
74
|
+
const explicit = (
|
|
75
|
+
[
|
|
76
|
+
[readDeployTargetFlag(argv), DEPLOY_TARGET_FLAG],
|
|
77
|
+
[configTarget, 'deploy.target'],
|
|
78
|
+
[env, 'MODERNJS_DEPLOY'],
|
|
79
|
+
] as const
|
|
80
|
+
).find(([target]) => target);
|
|
81
|
+
if (explicit?.[0]) {
|
|
82
|
+
return {
|
|
83
|
+
target: assertDeployTarget(explicit[0], explicit[1]),
|
|
84
|
+
explicit: true,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
target: (provider && providerDeployTargets[provider]) || 'node',
|
|
89
|
+
explicit: false,
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* The target `modern deploy` stages. Apps under another CLI meta name deploy
|
|
95
|
+
* only when a flag, config or env chose the target explicitly.
|
|
96
|
+
*/
|
|
97
|
+
export const getDeployingTarget = ({
|
|
98
|
+
deployTarget,
|
|
99
|
+
metaName,
|
|
100
|
+
}: {
|
|
101
|
+
deployTarget?: ResolvedDeployTarget;
|
|
102
|
+
metaName: string;
|
|
103
|
+
}): DeployTarget | undefined =>
|
|
104
|
+
deployTarget && (metaName === 'modern-js' || deployTarget.explicit)
|
|
105
|
+
? deployTarget.target
|
|
106
|
+
: undefined;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import os from 'node:os';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import type { NodeFileTraceResult } from '@vercel/nft';
|
|
4
|
+
|
|
5
|
+
// Directories owned by the build host rather than the app. nft statically
|
|
6
|
+
// evaluates `os.homedir()`, `os.tmpdir()` and literal system paths, so code
|
|
7
|
+
// like `fs.readFileSync(path.join(os.homedir(), name))` becomes a glob of the
|
|
8
|
+
// whole home directory. With `base: '/'` that glob walks the machine.
|
|
9
|
+
const SYSTEM_ROOTS =
|
|
10
|
+
process.platform === 'win32'
|
|
11
|
+
? []
|
|
12
|
+
: ['/dev', '/etc', '/proc', '/run', '/sys', '/var/run'];
|
|
13
|
+
|
|
14
|
+
const GLOB_SEGMENT = /[*?[{]/;
|
|
15
|
+
|
|
16
|
+
const isInside = (parent: string, child: string) =>
|
|
17
|
+
child === parent || child.startsWith(`${parent}${path.sep}`);
|
|
18
|
+
|
|
19
|
+
const globRoot = (absolutePattern: string) => {
|
|
20
|
+
const { root } = path.parse(absolutePattern);
|
|
21
|
+
const segments = absolutePattern.slice(root.length).split(path.sep);
|
|
22
|
+
const index = segments.findIndex(segment => GLOB_SEGMENT.test(segment));
|
|
23
|
+
return index === -1
|
|
24
|
+
? undefined
|
|
25
|
+
: path.join(root, ...segments.slice(0, index));
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Builds the nft `ignore` predicate for a trace rooted at `base`: skip every
|
|
30
|
+
* path under a system root, and skip globs that would enumerate the file
|
|
31
|
+
* system root, the home directory or the temp directory as a whole. Files
|
|
32
|
+
* inside home or temp are still traced, since package stores can live there.
|
|
33
|
+
*/
|
|
34
|
+
export const createBuildHostIgnore = (base: string) => {
|
|
35
|
+
const wholeDirectories = new Set([
|
|
36
|
+
path.parse(path.resolve(base)).root,
|
|
37
|
+
path.resolve(os.homedir()),
|
|
38
|
+
path.resolve(os.tmpdir()),
|
|
39
|
+
]);
|
|
40
|
+
return (relativePath: string) => {
|
|
41
|
+
const absolutePath = path.resolve(base, relativePath);
|
|
42
|
+
if (SYSTEM_ROOTS.some(root => isInside(root, absolutePath))) {
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
const root = globRoot(absolutePath);
|
|
46
|
+
return root !== undefined && wholeDirectories.has(root);
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* `traceFiles` implementation for ndepe's `nodeDepEmit`, tracing with the
|
|
52
|
+
* `@vercel/nft` release app-tools-extensions depends on instead of the one ndepe pins.
|
|
53
|
+
*/
|
|
54
|
+
export const traceDeployFiles = ({
|
|
55
|
+
entryFiles,
|
|
56
|
+
sourceDir,
|
|
57
|
+
base = '/',
|
|
58
|
+
}: {
|
|
59
|
+
entryFiles: string[];
|
|
60
|
+
sourceDir: string;
|
|
61
|
+
base?: string;
|
|
62
|
+
}): Promise<NodeFileTraceResult> => {
|
|
63
|
+
const resolvedBase = path.resolve(base);
|
|
64
|
+
const ignore = createBuildHostIgnore(resolvedBase);
|
|
65
|
+
try {
|
|
66
|
+
// NFT resolves roots and maps entries before its first await. Capture the
|
|
67
|
+
// same invocation here so loading it cannot observe a later cwd or edit.
|
|
68
|
+
const options = {
|
|
69
|
+
base: resolvedBase,
|
|
70
|
+
processCwd: path.resolve(sourceDir || resolvedBase),
|
|
71
|
+
ignore,
|
|
72
|
+
};
|
|
73
|
+
const entries = entryFiles.map(file => path.resolve(file));
|
|
74
|
+
return import('@vercel/nft').then(({ nodeFileTrace }) =>
|
|
75
|
+
nodeFileTrace(entries, options),
|
|
76
|
+
);
|
|
77
|
+
} catch (error) {
|
|
78
|
+
return Promise.reject(error);
|
|
79
|
+
}
|
|
80
|
+
};
|