@edge-base/cli 0.2.7 → 0.2.9
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/commands/build-app.d.ts +3 -0
- package/dist/commands/build-app.d.ts.map +1 -0
- package/dist/commands/build-app.js +45 -0
- package/dist/commands/build-app.js.map +1 -0
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +14 -14
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +43 -36
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/docker.d.ts +5 -0
- package/dist/commands/docker.d.ts.map +1 -1
- package/dist/commands/docker.js +83 -5
- package/dist/commands/docker.js.map +1 -1
- package/dist/commands/pack.d.ts +3 -0
- package/dist/commands/pack.d.ts.map +1 -0
- package/dist/commands/pack.js +117 -0
- package/dist/commands/pack.js.map +1 -0
- package/dist/index.js +6 -2
- package/dist/index.js.map +1 -1
- package/dist/lib/app-bundle.d.ts +48 -0
- package/dist/lib/app-bundle.d.ts.map +1 -0
- package/dist/lib/app-bundle.js +240 -0
- package/dist/lib/app-bundle.js.map +1 -0
- package/dist/lib/deploy-shared.d.ts +4 -0
- package/dist/lib/deploy-shared.d.ts.map +1 -1
- package/dist/lib/deploy-shared.js +40 -8
- package/dist/lib/deploy-shared.js.map +1 -1
- package/dist/lib/frontend-config.d.ts +7 -0
- package/dist/lib/frontend-config.d.ts.map +1 -0
- package/dist/lib/frontend-config.js +8 -0
- package/dist/lib/frontend-config.js.map +1 -0
- package/dist/lib/function-registry.d.ts +1 -0
- package/dist/lib/function-registry.d.ts.map +1 -1
- package/dist/lib/function-registry.js +3 -1
- package/dist/lib/function-registry.js.map +1 -1
- package/dist/lib/managed-resource-names.d.ts +2 -0
- package/dist/lib/managed-resource-names.d.ts.map +1 -1
- package/dist/lib/managed-resource-names.js +19 -1
- package/dist/lib/managed-resource-names.js.map +1 -1
- package/dist/lib/node-tools.d.ts +3 -1
- package/dist/lib/node-tools.d.ts.map +1 -1
- package/dist/lib/node-tools.js +2 -2
- package/dist/lib/node-tools.js.map +1 -1
- package/dist/lib/pack.d.ts +102 -0
- package/dist/lib/pack.d.ts.map +1 -0
- package/dist/lib/pack.js +1047 -0
- package/dist/lib/pack.js.map +1 -0
- package/dist/lib/runtime-scaffold.d.ts +43 -3
- package/dist/lib/runtime-scaffold.d.ts.map +1 -1
- package/dist/lib/runtime-scaffold.js +455 -38
- package/dist/lib/runtime-scaffold.js.map +1 -1
- package/package.json +7 -4
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { cpSync, existsSync, lstatSync, mkdirSync, readFileSync, readlinkSync, rmSync, unlinkSync, symlinkSync, writeFileSync, } from 'node:fs';
|
|
2
|
-
import {
|
|
1
|
+
import { copyFileSync, cpSync, existsSync, lstatSync, mkdirSync, realpathSync, readdirSync, readFileSync, readlinkSync, rmSync, unlinkSync, symlinkSync, writeFileSync, } from 'node:fs';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { basename, dirname, join, posix, resolve } from 'node:path';
|
|
3
4
|
import { fileURLToPath } from 'node:url';
|
|
4
|
-
import {
|
|
5
|
+
import { normalizeFrontendMountPath } from './frontend-config.js';
|
|
6
|
+
import { buildManagedD1DatabaseName, buildManagedR2BucketName, buildManagedWorkerName, } from './managed-resource-names.js';
|
|
5
7
|
import { deriveProjectSlug, INTERNAL_D1_BINDINGS } from './project-runtime.js';
|
|
6
8
|
import { resolveWranglerTool } from './wrangler.js';
|
|
7
9
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
@@ -14,7 +16,9 @@ const MONOREPO_ADMIN_BUILD_SOURCES = [
|
|
|
14
16
|
const CLI_NODE_MODULES_SOURCE = resolve(__dirname, '../../node_modules');
|
|
15
17
|
const WORKSPACE_NODE_MODULES_SOURCE = resolve(WORKSPACE_ROOT, 'node_modules');
|
|
16
18
|
const SERVER_NODE_MODULES_SOURCE = resolve(__dirname, '../../../server/node_modules');
|
|
19
|
+
const MONOREPO_SERVER_PACKAGE_MANIFEST_SOURCE = resolve(__dirname, '../../../server/package.json');
|
|
17
20
|
const MONOREPO_SHARED_SOURCE = resolve(__dirname, '../../../shared');
|
|
21
|
+
const EDGEBASE_ASSETS_DIRNAME = 'app-assets';
|
|
18
22
|
export { deriveProjectSlug, INTERNAL_D1_BINDINGS } from './project-runtime.js';
|
|
19
23
|
export function getRuntimeRoot(projectDir) {
|
|
20
24
|
return join(projectDir, '.edgebase', 'runtime', 'server');
|
|
@@ -25,17 +29,21 @@ export function getRuntimeServerSrcDir(projectDir) {
|
|
|
25
29
|
export function getRuntimeAdminBuildDir(projectDir) {
|
|
26
30
|
return join(getRuntimeRoot(projectDir), 'admin-build');
|
|
27
31
|
}
|
|
28
|
-
export function
|
|
32
|
+
export function getRuntimeAssetsDir(projectDir) {
|
|
33
|
+
return join(getRuntimeRoot(projectDir), EDGEBASE_ASSETS_DIRNAME);
|
|
34
|
+
}
|
|
35
|
+
export function ensureRuntimeScaffold(projectDir, options = {}) {
|
|
29
36
|
const runtimeRoot = getRuntimeRoot(projectDir);
|
|
30
37
|
mkdirSync(runtimeRoot, { recursive: true });
|
|
31
38
|
copyRuntimeDir(resolveServerRuntimeSource(projectDir), getRuntimeServerSrcDir(projectDir));
|
|
32
39
|
const adminBuildDir = getRuntimeAdminBuildDir(projectDir);
|
|
33
40
|
copyRuntimeDir(resolveAdminBuildSource(projectDir), adminBuildDir);
|
|
34
41
|
normalizeAdminBuildBase(adminBuildDir);
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
42
|
+
prepareRuntimeAssets(projectDir, options.frontendProjectDir ?? projectDir, adminBuildDir, options.frontend ?? undefined);
|
|
43
|
+
ensureRuntimeNodeModules(projectDir, runtimeRoot, options.dependencyMode ?? 'symlink', options.dependencyProfile ?? 'portable');
|
|
44
|
+
ensureProjectSharedPackageLink(projectDir, options.dependencyMode ?? 'symlink');
|
|
45
|
+
writeRuntimeConfigShim(projectDir, undefined, { importPath: options.configImportPath });
|
|
46
|
+
writeRuntimeTestConfigShim(projectDir, { importPath: options.testConfigImportPath });
|
|
39
47
|
}
|
|
40
48
|
export function ensureLocalWranglerToml(projectDir) {
|
|
41
49
|
const wranglerPath = join(projectDir, 'wrangler.toml');
|
|
@@ -45,14 +53,15 @@ export function ensureLocalWranglerToml(projectDir) {
|
|
|
45
53
|
return wranglerPath;
|
|
46
54
|
}
|
|
47
55
|
export function buildDefaultWranglerToml(accountId, projectName = 'edgebase') {
|
|
56
|
+
const workerName = buildManagedWorkerName(projectName);
|
|
48
57
|
const accountLine = accountId ? `account_id = "${accountId}"\n` : '';
|
|
49
58
|
const internalD1Blocks = INTERNAL_D1_BINDINGS.map(({ name, binding }) => `[[d1_databases]]
|
|
50
59
|
binding = "${binding}"
|
|
51
|
-
database_name = "${buildManagedD1DatabaseName(
|
|
60
|
+
database_name = "${buildManagedD1DatabaseName(workerName, name)}"
|
|
52
61
|
database_id = "local"
|
|
53
62
|
`).join('\n');
|
|
54
63
|
return `# Auto-generated by edgebase
|
|
55
|
-
name = "${
|
|
64
|
+
name = "${workerName}"
|
|
56
65
|
${accountLine}main = ".edgebase/runtime/server/src/index.ts"
|
|
57
66
|
compatibility_date = "2025-02-10"
|
|
58
67
|
compatibility_flags = ["nodejs_compat"]
|
|
@@ -76,7 +85,7 @@ new_sqlite_classes = ["LogsDO"]
|
|
|
76
85
|
|
|
77
86
|
[[r2_buckets]]
|
|
78
87
|
binding = "STORAGE"
|
|
79
|
-
bucket_name = "${
|
|
88
|
+
bucket_name = "${buildManagedR2BucketName(workerName)}"
|
|
80
89
|
|
|
81
90
|
[[kv_namespaces]]
|
|
82
91
|
binding = "KV"
|
|
@@ -85,7 +94,7 @@ id = "local"
|
|
|
85
94
|
${internalD1Blocks}
|
|
86
95
|
|
|
87
96
|
[assets]
|
|
88
|
-
directory = ".edgebase/runtime/server/
|
|
97
|
+
directory = ".edgebase/runtime/server/app-assets"
|
|
89
98
|
binding = "ASSETS"
|
|
90
99
|
run_worker_first = true
|
|
91
100
|
`;
|
|
@@ -106,10 +115,10 @@ function copyRuntimeDir(source, target) {
|
|
|
106
115
|
}
|
|
107
116
|
function resolveServerRuntimeSource(projectDir) {
|
|
108
117
|
const candidates = dedupeCandidates([
|
|
118
|
+
MONOREPO_SERVER_RUNTIME_SOURCE,
|
|
109
119
|
join(projectDir, 'node_modules', '@edge-base', 'server', 'src'),
|
|
110
120
|
resolve(CLI_NODE_MODULES_SOURCE, '@edge-base/server', 'src'),
|
|
111
121
|
resolve(WORKSPACE_NODE_MODULES_SOURCE, '@edge-base/server', 'src'),
|
|
112
|
-
MONOREPO_SERVER_RUNTIME_SOURCE,
|
|
113
122
|
]);
|
|
114
123
|
for (const candidate of candidates) {
|
|
115
124
|
if (existsSync(join(candidate, 'index.ts'))) {
|
|
@@ -120,10 +129,10 @@ function resolveServerRuntimeSource(projectDir) {
|
|
|
120
129
|
}
|
|
121
130
|
function resolveAdminBuildSource(projectDir) {
|
|
122
131
|
const candidates = dedupeCandidates([
|
|
132
|
+
...MONOREPO_ADMIN_BUILD_SOURCES,
|
|
123
133
|
join(projectDir, 'node_modules', '@edge-base', 'server', 'admin-build'),
|
|
124
134
|
resolve(CLI_NODE_MODULES_SOURCE, '@edge-base/server', 'admin-build'),
|
|
125
135
|
resolve(WORKSPACE_NODE_MODULES_SOURCE, '@edge-base/server', 'admin-build'),
|
|
126
|
-
...MONOREPO_ADMIN_BUILD_SOURCES,
|
|
127
136
|
]);
|
|
128
137
|
for (const candidate of candidates) {
|
|
129
138
|
if (existsSync(join(candidate, 'index.html'))) {
|
|
@@ -132,6 +141,31 @@ function resolveAdminBuildSource(projectDir) {
|
|
|
132
141
|
}
|
|
133
142
|
throw new Error(`Admin build source is missing. Checked: ${candidates.join(', ')}`);
|
|
134
143
|
}
|
|
144
|
+
function prepareRuntimeAssets(projectDir, sourceProjectDir, adminBuildDir, frontend) {
|
|
145
|
+
const assetsDir = getRuntimeAssetsDir(projectDir);
|
|
146
|
+
rmSync(assetsDir, { recursive: true, force: true });
|
|
147
|
+
mkdirSync(assetsDir, { recursive: true });
|
|
148
|
+
copyRuntimeDir(adminBuildDir, join(assetsDir, 'admin'));
|
|
149
|
+
copyFrontendBuild(sourceProjectDir, assetsDir, frontend);
|
|
150
|
+
}
|
|
151
|
+
function copyFrontendBuild(projectDir, assetsDir, frontend) {
|
|
152
|
+
if (!frontend?.directory)
|
|
153
|
+
return;
|
|
154
|
+
const source = resolve(projectDir, frontend.directory);
|
|
155
|
+
if (!existsSync(source)) {
|
|
156
|
+
throw new Error(`Frontend build directory is missing: ${source}`);
|
|
157
|
+
}
|
|
158
|
+
const mountPath = normalizeFrontendMountPath(frontend.mountPath);
|
|
159
|
+
if (mountPath === '/') {
|
|
160
|
+
const conflicts = readdirSync(source).filter((entry) => entry === 'admin');
|
|
161
|
+
if (conflicts.length > 0) {
|
|
162
|
+
throw new Error(`Frontend build directory '${frontend.directory}' contains reserved top-level entries: ${conflicts.join(', ')}.`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
const target = mountPath === '/' ? assetsDir : join(assetsDir, mountPath.slice(1));
|
|
166
|
+
mkdirSync(target, { recursive: true });
|
|
167
|
+
copyRuntimeDir(source, target);
|
|
168
|
+
}
|
|
135
169
|
function normalizeAdminBuildBase(adminBuildDir) {
|
|
136
170
|
const indexPath = join(adminBuildDir, 'index.html');
|
|
137
171
|
if (!existsSync(indexPath))
|
|
@@ -146,11 +180,15 @@ function normalizeAdminBuildBase(adminBuildDir) {
|
|
|
146
180
|
writeFileSync(indexPath, normalized, 'utf-8');
|
|
147
181
|
}
|
|
148
182
|
}
|
|
149
|
-
function
|
|
150
|
-
const source = resolveRuntimeNodeModulesSource(projectDir);
|
|
183
|
+
function ensureRuntimeNodeModules(projectDir, runtimeRoot, mode, profile) {
|
|
184
|
+
const source = resolveRuntimeNodeModulesSource(projectDir, mode);
|
|
151
185
|
if (!source)
|
|
152
186
|
return;
|
|
153
187
|
const target = join(runtimeRoot, 'node_modules');
|
|
188
|
+
if (mode === 'copy') {
|
|
189
|
+
materializeNodeModulesTree(source, target, getRuntimeNodeModulesCandidates(projectDir, mode), resolveRuntimeDependencySelection(projectDir, profile));
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
154
192
|
const currentLink = readExistingSymlinkTarget(target);
|
|
155
193
|
if (currentLink === source)
|
|
156
194
|
return;
|
|
@@ -159,16 +197,16 @@ function ensureRuntimeNodeModulesLink(projectDir, runtimeRoot) {
|
|
|
159
197
|
}
|
|
160
198
|
symlinkSync(source, target, process.platform === 'win32' ? 'junction' : 'dir');
|
|
161
199
|
}
|
|
162
|
-
export function ensureProjectSharedPackageLink(projectDir) {
|
|
200
|
+
export function ensureProjectSharedPackageLink(projectDir, mode = 'symlink') {
|
|
163
201
|
const source = resolveSharedPackageSource(projectDir);
|
|
164
202
|
if (!source)
|
|
165
203
|
return;
|
|
166
204
|
for (const root of resolveSharedPackageLinkRoots(projectDir)) {
|
|
167
|
-
ensureSharedPackageLinkAtRoot(root, source);
|
|
205
|
+
ensureSharedPackageLinkAtRoot(root, source, mode);
|
|
168
206
|
}
|
|
169
207
|
}
|
|
170
|
-
function resolveRuntimeNodeModulesSource(projectDir) {
|
|
171
|
-
return resolveRuntimeNodeModulesSourceFromCandidates(getRuntimeNodeModulesCandidates(projectDir));
|
|
208
|
+
function resolveRuntimeNodeModulesSource(projectDir, mode = 'symlink') {
|
|
209
|
+
return resolveRuntimeNodeModulesSourceFromCandidates(getRuntimeNodeModulesCandidates(projectDir, mode));
|
|
172
210
|
}
|
|
173
211
|
export function resolveRuntimeNodeModulesSourceFromCandidates(candidates) {
|
|
174
212
|
for (const candidate of candidates) {
|
|
@@ -189,7 +227,15 @@ export function resolveSharedPackageSourceFromCandidates(candidates) {
|
|
|
189
227
|
}
|
|
190
228
|
return null;
|
|
191
229
|
}
|
|
192
|
-
function getRuntimeNodeModulesCandidates(projectDir) {
|
|
230
|
+
function getRuntimeNodeModulesCandidates(projectDir, mode = 'symlink') {
|
|
231
|
+
if (mode === 'copy') {
|
|
232
|
+
return dedupeCandidates([
|
|
233
|
+
SERVER_NODE_MODULES_SOURCE,
|
|
234
|
+
CLI_NODE_MODULES_SOURCE,
|
|
235
|
+
join(projectDir, 'node_modules'),
|
|
236
|
+
WORKSPACE_NODE_MODULES_SOURCE,
|
|
237
|
+
]);
|
|
238
|
+
}
|
|
193
239
|
return dedupeCandidates([
|
|
194
240
|
SERVER_NODE_MODULES_SOURCE,
|
|
195
241
|
CLI_NODE_MODULES_SOURCE,
|
|
@@ -197,6 +243,174 @@ function getRuntimeNodeModulesCandidates(projectDir) {
|
|
|
197
243
|
join(projectDir, 'node_modules'),
|
|
198
244
|
]);
|
|
199
245
|
}
|
|
246
|
+
function materializeNodeModulesTree(sourceRoot, targetRoot, candidateRoots, selectedPackages) {
|
|
247
|
+
rmSync(targetRoot, { recursive: true, force: true });
|
|
248
|
+
mkdirSync(targetRoot, { recursive: true });
|
|
249
|
+
const normalizedRoots = dedupeCandidates(candidateRoots)
|
|
250
|
+
.filter((candidate) => existsSync(candidate))
|
|
251
|
+
.map((candidate) => resolve(candidate))
|
|
252
|
+
.sort((left, right) => right.length - left.length);
|
|
253
|
+
const copiedTargets = new Set();
|
|
254
|
+
if (selectedPackages && selectedPackages.length > 0) {
|
|
255
|
+
for (const selection of selectedPackages) {
|
|
256
|
+
materializeNodeModulesEntry(selection.packageDir, join(targetRoot, ...selection.packageName.split('/')), targetRoot, normalizedRoots, copiedTargets);
|
|
257
|
+
}
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
for (const entry of readdirSync(sourceRoot)) {
|
|
261
|
+
materializeNodeModulesEntry(join(sourceRoot, entry), join(targetRoot, entry), targetRoot, normalizedRoots, copiedTargets);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
function resolvePackageDirectoryPath(packageName, candidateRoots) {
|
|
265
|
+
const topLevelEntry = resolveTopLevelPackageEntry(packageName, candidateRoots);
|
|
266
|
+
if (topLevelEntry) {
|
|
267
|
+
return topLevelEntry;
|
|
268
|
+
}
|
|
269
|
+
const packageSegments = packageName.split('/');
|
|
270
|
+
for (const candidateRoot of dedupeCandidates(candidateRoots)) {
|
|
271
|
+
const pnpmDir = join(candidateRoot, '.pnpm');
|
|
272
|
+
if (!existsSync(pnpmDir))
|
|
273
|
+
continue;
|
|
274
|
+
for (const entry of readdirSync(pnpmDir)) {
|
|
275
|
+
const candidatePath = join(pnpmDir, entry, 'node_modules', ...packageSegments);
|
|
276
|
+
if (existsSync(candidatePath)) {
|
|
277
|
+
return candidatePath;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return null;
|
|
282
|
+
}
|
|
283
|
+
function resolveTopLevelPackageEntry(packageName, candidateRoots) {
|
|
284
|
+
const packagePath = packageName.split('/');
|
|
285
|
+
for (const candidateRoot of candidateRoots) {
|
|
286
|
+
const candidatePath = join(candidateRoot, ...packagePath);
|
|
287
|
+
if (existsSync(candidatePath)) {
|
|
288
|
+
return candidatePath;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
function materializeNodeModulesEntry(sourceEntry, targetEntry, targetRoot, candidateRoots, copiedTargets, skipNestedNodeModules = false) {
|
|
294
|
+
const stat = lstatSync(sourceEntry);
|
|
295
|
+
if (stat.isSymbolicLink()) {
|
|
296
|
+
const sourceLinkTarget = readlinkSync(sourceEntry);
|
|
297
|
+
const resolvedSourceTarget = resolve(dirname(sourceEntry), sourceLinkTarget);
|
|
298
|
+
const sourceContainerRoot = findContainingRoot(resolvedSourceTarget, candidateRoots);
|
|
299
|
+
if (!existsSync(resolvedSourceTarget)) {
|
|
300
|
+
rmSync(targetEntry, { recursive: true, force: true });
|
|
301
|
+
mkdirSync(dirname(targetEntry), { recursive: true });
|
|
302
|
+
symlinkSync(buildDirectoryLinkTarget(targetEntry, resolvedSourceTarget), targetEntry, process.platform === 'win32' ? 'junction' : 'dir');
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
if (!sourceContainerRoot) {
|
|
306
|
+
const targetKey = `${resolvedSourceTarget}=>${targetEntry}`;
|
|
307
|
+
if (copiedTargets.has(targetKey)) {
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
copiedTargets.add(targetKey);
|
|
311
|
+
materializeNodeModulesEntry(resolvedSourceTarget, targetEntry, targetRoot, candidateRoots, copiedTargets, true);
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
const materialization = getNodeModulesMaterialization(resolvedSourceTarget, sourceContainerRoot, targetRoot);
|
|
315
|
+
const targetKey = `${materialization.sourceRoot}=>${materialization.targetRoot}`;
|
|
316
|
+
if (!copiedTargets.has(targetKey)) {
|
|
317
|
+
copiedTargets.add(targetKey);
|
|
318
|
+
materializeNodeModulesEntry(materialization.sourceRoot, materialization.targetRoot, targetRoot, candidateRoots, copiedTargets, skipNestedNodeModules);
|
|
319
|
+
}
|
|
320
|
+
rmSync(targetEntry, { recursive: true, force: true });
|
|
321
|
+
mkdirSync(dirname(targetEntry), { recursive: true });
|
|
322
|
+
symlinkSync(buildDirectoryLinkTarget(targetEntry, materialization.targetPath), targetEntry, process.platform === 'win32' ? 'junction' : 'dir');
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
if (stat.isDirectory()) {
|
|
326
|
+
mkdirSync(targetEntry, { recursive: true });
|
|
327
|
+
for (const entry of readdirSync(sourceEntry)) {
|
|
328
|
+
if (skipNestedNodeModules && entry === 'node_modules') {
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
materializeNodeModulesEntry(join(sourceEntry, entry), join(targetEntry, entry), targetRoot, candidateRoots, copiedTargets, skipNestedNodeModules);
|
|
332
|
+
}
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
mkdirSync(dirname(targetEntry), { recursive: true });
|
|
336
|
+
copyFileSync(sourceEntry, targetEntry);
|
|
337
|
+
}
|
|
338
|
+
function findContainingRoot(targetPath, candidateRoots) {
|
|
339
|
+
for (const candidateRoot of candidateRoots) {
|
|
340
|
+
if (getRelativePathSegmentsWithinRoot(candidateRoot, targetPath) !== null) {
|
|
341
|
+
return candidateRoot;
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
return null;
|
|
345
|
+
}
|
|
346
|
+
function getNodeModulesMaterialization(sourcePath, sourceContainerRoot, targetRoot) {
|
|
347
|
+
const relativeSegments = getRelativePathSegmentsWithinRoot(sourceContainerRoot, sourcePath);
|
|
348
|
+
if (!relativeSegments) {
|
|
349
|
+
throw new Error(`Could not resolve '${sourcePath}' inside runtime dependency root '${sourceContainerRoot}'.`);
|
|
350
|
+
}
|
|
351
|
+
const targetPath = join(targetRoot, ...relativeSegments);
|
|
352
|
+
if (relativeSegments[0] === '.pnpm' && relativeSegments.length >= 2) {
|
|
353
|
+
const packageRoot = join(sourceContainerRoot, '.pnpm', relativeSegments[1]);
|
|
354
|
+
return {
|
|
355
|
+
sourceRoot: packageRoot,
|
|
356
|
+
targetRoot: join(targetRoot, '.pnpm', relativeSegments[1]),
|
|
357
|
+
targetPath,
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
return {
|
|
361
|
+
sourceRoot: sourcePath,
|
|
362
|
+
targetRoot: targetPath,
|
|
363
|
+
targetPath,
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
function getRelativePathSegmentsWithinRoot(rootPath, targetPath) {
|
|
367
|
+
const rootSegments = splitCrossPlatformPathSegments(rootPath);
|
|
368
|
+
const targetSegments = splitCrossPlatformPathSegments(targetPath);
|
|
369
|
+
if (rootSegments.length > targetSegments.length) {
|
|
370
|
+
return null;
|
|
371
|
+
}
|
|
372
|
+
for (let index = 0; index < rootSegments.length; index += 1) {
|
|
373
|
+
if (rootSegments[index] !== targetSegments[index]) {
|
|
374
|
+
return null;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
return targetSegments.slice(rootSegments.length);
|
|
378
|
+
}
|
|
379
|
+
function splitCrossPlatformPathSegments(pathValue) {
|
|
380
|
+
const normalized = normalizeCrossPlatformPath(pathValue);
|
|
381
|
+
return normalized.split('/').filter(Boolean);
|
|
382
|
+
}
|
|
383
|
+
function normalizeCrossPlatformPath(pathValue) {
|
|
384
|
+
let normalized = pathValue.replace(/\\/g, '/').replace(/\/+/g, '/');
|
|
385
|
+
if (normalized.length > 1 && normalized.endsWith('/')) {
|
|
386
|
+
normalized = normalized.slice(0, -1);
|
|
387
|
+
}
|
|
388
|
+
if (/^[A-Z]:($|\/)/.test(normalized)) {
|
|
389
|
+
normalized = `${normalized[0].toLowerCase()}${normalized.slice(1)}`;
|
|
390
|
+
}
|
|
391
|
+
return normalized;
|
|
392
|
+
}
|
|
393
|
+
function buildDirectoryLinkTarget(linkPath, destinationPath, platform = process.platform) {
|
|
394
|
+
if (platform === 'win32') {
|
|
395
|
+
if (/^[A-Za-z]:[\\/]/.test(destinationPath)) {
|
|
396
|
+
return destinationPath.replace(/\//g, '\\');
|
|
397
|
+
}
|
|
398
|
+
return resolve(destinationPath);
|
|
399
|
+
}
|
|
400
|
+
const sourceDir = normalizeCrossPlatformPath(dirname(linkPath));
|
|
401
|
+
const targetPath = normalizeCrossPlatformPath(destinationPath);
|
|
402
|
+
return posix.relative(sourceDir, targetPath) || '.';
|
|
403
|
+
}
|
|
404
|
+
export const __runtimeScaffoldTestUtils = {
|
|
405
|
+
buildDirectoryLinkTarget,
|
|
406
|
+
findContainingRoot,
|
|
407
|
+
getNodeModulesMaterialization,
|
|
408
|
+
getRelativePathSegmentsWithinRoot,
|
|
409
|
+
resolvePackageSelectionFromManifestCandidates,
|
|
410
|
+
resolvePackageDirectoryPath,
|
|
411
|
+
resolveRuntimePackageSelections,
|
|
412
|
+
resolveRuntimePackageSelectionsFromInitialSelections,
|
|
413
|
+
};
|
|
200
414
|
function getSharedPackageSourceCandidates(projectDir) {
|
|
201
415
|
return dedupeCandidates([
|
|
202
416
|
join(projectDir, 'node_modules', '@edge-base', 'shared'),
|
|
@@ -206,6 +420,169 @@ function getSharedPackageSourceCandidates(projectDir) {
|
|
|
206
420
|
MONOREPO_SHARED_SOURCE,
|
|
207
421
|
]);
|
|
208
422
|
}
|
|
423
|
+
function resolveRuntimeDependencySelection(projectDir, profile) {
|
|
424
|
+
const candidateRoots = getRuntimeNodeModulesCandidates(projectDir, 'copy');
|
|
425
|
+
const serverDependencies = readPackageDependencyNamesFromCandidates(getServerPackageManifestCandidates(projectDir));
|
|
426
|
+
if (!serverDependencies) {
|
|
427
|
+
return null;
|
|
428
|
+
}
|
|
429
|
+
const initialSelections = [];
|
|
430
|
+
for (const packageName of dedupeCandidates(serverDependencies)) {
|
|
431
|
+
const selection = resolveRuntimePackageSelection(packageName, candidateRoots);
|
|
432
|
+
if (selection) {
|
|
433
|
+
initialSelections.push(selection);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
if (profile === 'portable') {
|
|
437
|
+
const wranglerSelection = resolvePackageSelectionFromManifestCandidates('wrangler', getPortableWranglerManifestCandidates(projectDir)) ?? resolveRuntimePackageSelection('wrangler', candidateRoots);
|
|
438
|
+
if (wranglerSelection) {
|
|
439
|
+
initialSelections.push(wranglerSelection);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
return resolveRuntimePackageSelectionsFromInitialSelections(initialSelections, candidateRoots);
|
|
443
|
+
}
|
|
444
|
+
function getPortableWranglerManifestCandidates(projectDir) {
|
|
445
|
+
return dedupeCandidates([
|
|
446
|
+
join(projectDir, 'node_modules', 'wrangler', 'package.json'),
|
|
447
|
+
resolve(CLI_NODE_MODULES_SOURCE, 'wrangler', 'package.json'),
|
|
448
|
+
resolve(WORKSPACE_NODE_MODULES_SOURCE, 'wrangler', 'package.json'),
|
|
449
|
+
resolve(SERVER_NODE_MODULES_SOURCE, 'wrangler', 'package.json'),
|
|
450
|
+
]);
|
|
451
|
+
}
|
|
452
|
+
function getServerPackageManifestCandidates(projectDir) {
|
|
453
|
+
return dedupeCandidates([
|
|
454
|
+
MONOREPO_SERVER_PACKAGE_MANIFEST_SOURCE,
|
|
455
|
+
join(projectDir, 'node_modules', '@edge-base', 'server', 'package.json'),
|
|
456
|
+
resolve(CLI_NODE_MODULES_SOURCE, '@edge-base', 'server', 'package.json'),
|
|
457
|
+
resolve(WORKSPACE_NODE_MODULES_SOURCE, '@edge-base', 'server', 'package.json'),
|
|
458
|
+
]);
|
|
459
|
+
}
|
|
460
|
+
function readPackageDependencyNamesFromCandidates(candidates) {
|
|
461
|
+
for (const candidate of dedupeCandidates(candidates)) {
|
|
462
|
+
if (!existsSync(candidate))
|
|
463
|
+
continue;
|
|
464
|
+
try {
|
|
465
|
+
return readPackageDependencyNames(candidate);
|
|
466
|
+
}
|
|
467
|
+
catch {
|
|
468
|
+
continue;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
return null;
|
|
472
|
+
}
|
|
473
|
+
function resolveRuntimePackageSelections(initialPackages, candidateRoots) {
|
|
474
|
+
const initialSelections = [];
|
|
475
|
+
for (const packageName of dedupeCandidates(initialPackages)) {
|
|
476
|
+
const selection = resolveRuntimePackageSelection(packageName, candidateRoots);
|
|
477
|
+
if (!selection) {
|
|
478
|
+
continue;
|
|
479
|
+
}
|
|
480
|
+
initialSelections.push(selection);
|
|
481
|
+
}
|
|
482
|
+
return resolveRuntimePackageSelectionsFromInitialSelections(initialSelections, candidateRoots);
|
|
483
|
+
}
|
|
484
|
+
function resolveRuntimePackageSelectionsFromInitialSelections(initialSelections, candidateRoots) {
|
|
485
|
+
const selections = new Map();
|
|
486
|
+
const visitedManifestPaths = new Set();
|
|
487
|
+
const queue = [];
|
|
488
|
+
for (const selection of initialSelections) {
|
|
489
|
+
if (selections.has(selection.packageName)) {
|
|
490
|
+
continue;
|
|
491
|
+
}
|
|
492
|
+
selections.set(selection.packageName, selection);
|
|
493
|
+
queue.push(selection);
|
|
494
|
+
}
|
|
495
|
+
while (queue.length > 0) {
|
|
496
|
+
const selection = queue.shift();
|
|
497
|
+
if (!selection || visitedManifestPaths.has(selection.manifestPath)) {
|
|
498
|
+
continue;
|
|
499
|
+
}
|
|
500
|
+
visitedManifestPaths.add(selection.manifestPath);
|
|
501
|
+
const packageRequire = createRequire(selection.manifestPath);
|
|
502
|
+
for (const dependencyName of readPackageDependencyNames(selection.manifestPath)) {
|
|
503
|
+
const dependencySelection = resolveRuntimePackageSelection(dependencyName, candidateRoots, packageRequire);
|
|
504
|
+
if (!dependencySelection) {
|
|
505
|
+
continue;
|
|
506
|
+
}
|
|
507
|
+
queue.push(dependencySelection);
|
|
508
|
+
if (!selections.has(dependencyName)) {
|
|
509
|
+
selections.set(dependencyName, dependencySelection);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
return Array.from(selections.values());
|
|
514
|
+
}
|
|
515
|
+
function resolvePackageSelectionFromManifestCandidates(packageName, manifestCandidates) {
|
|
516
|
+
for (const manifestCandidate of dedupeCandidates(manifestCandidates)) {
|
|
517
|
+
if (!existsSync(manifestCandidate)) {
|
|
518
|
+
continue;
|
|
519
|
+
}
|
|
520
|
+
try {
|
|
521
|
+
const rawManifestPath = resolve(manifestCandidate);
|
|
522
|
+
const resolvedManifestPath = realpathSync(manifestCandidate);
|
|
523
|
+
const packageJson = JSON.parse(readFileSync(resolvedManifestPath, 'utf-8'));
|
|
524
|
+
if (packageJson.name !== packageName) {
|
|
525
|
+
continue;
|
|
526
|
+
}
|
|
527
|
+
return {
|
|
528
|
+
packageName,
|
|
529
|
+
packageDir: dirname(rawManifestPath),
|
|
530
|
+
manifestPath: resolvedManifestPath,
|
|
531
|
+
};
|
|
532
|
+
}
|
|
533
|
+
catch {
|
|
534
|
+
continue;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
return null;
|
|
538
|
+
}
|
|
539
|
+
function resolveRuntimePackageSelection(packageName, candidateRoots, packageRequire) {
|
|
540
|
+
const location = resolvePackageManifestLocation(packageName, candidateRoots, packageRequire);
|
|
541
|
+
if (!location) {
|
|
542
|
+
return null;
|
|
543
|
+
}
|
|
544
|
+
return {
|
|
545
|
+
packageName,
|
|
546
|
+
packageDir: location.packageDir,
|
|
547
|
+
manifestPath: location.manifestPath,
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
function resolvePackageManifestLocation(packageName, candidateRoots, packageRequire) {
|
|
551
|
+
if (packageRequire) {
|
|
552
|
+
try {
|
|
553
|
+
const rawManifestPath = packageRequire.resolve(`${packageName}/package.json`);
|
|
554
|
+
if (existsSync(rawManifestPath)) {
|
|
555
|
+
return {
|
|
556
|
+
packageDir: dirname(resolve(rawManifestPath)),
|
|
557
|
+
manifestPath: realpathSync(rawManifestPath),
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
catch {
|
|
562
|
+
// Fall back to the candidate-root scan below so workspace shims and
|
|
563
|
+
// fixture tests can still resolve packages outside the package graph.
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
const packageDir = resolvePackageDirectoryPath(packageName, candidateRoots);
|
|
567
|
+
if (!packageDir) {
|
|
568
|
+
return null;
|
|
569
|
+
}
|
|
570
|
+
const rawManifestPath = join(packageDir, 'package.json');
|
|
571
|
+
if (!existsSync(rawManifestPath)) {
|
|
572
|
+
return null;
|
|
573
|
+
}
|
|
574
|
+
return {
|
|
575
|
+
packageDir,
|
|
576
|
+
manifestPath: realpathSync(rawManifestPath),
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
function readPackageDependencyNames(packageJsonPath) {
|
|
580
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
581
|
+
return [
|
|
582
|
+
...Object.keys(packageJson.dependencies ?? {}),
|
|
583
|
+
...Object.keys(packageJson.optionalDependencies ?? {}),
|
|
584
|
+
];
|
|
585
|
+
}
|
|
209
586
|
function dedupeCandidates(candidates) {
|
|
210
587
|
return Array.from(new Set(candidates));
|
|
211
588
|
}
|
|
@@ -217,7 +594,7 @@ export function resolveSharedPackageLinkRoots(projectDir) {
|
|
|
217
594
|
}
|
|
218
595
|
return Array.from(roots);
|
|
219
596
|
}
|
|
220
|
-
function ensureSharedPackageLinkAtRoot(rootDir, source) {
|
|
597
|
+
function ensureSharedPackageLinkAtRoot(rootDir, source, mode) {
|
|
221
598
|
const target = join(rootDir, 'node_modules', '@edge-base', 'shared');
|
|
222
599
|
const markerPath = join(target, '.edgebase-shim');
|
|
223
600
|
const normalizedSource = resolve(source);
|
|
@@ -242,7 +619,12 @@ function ensureSharedPackageLinkAtRoot(rootDir, source) {
|
|
|
242
619
|
mkdirSync(target, { recursive: true });
|
|
243
620
|
writeFileSync(join(target, 'package.json'), buildSharedShimPackageJson(), 'utf-8');
|
|
244
621
|
writeFileSync(markerPath, 'edgebase-shared-shim\n', 'utf-8');
|
|
245
|
-
|
|
622
|
+
if (mode === 'copy') {
|
|
623
|
+
replaceDirectoryContents(join(source, 'src'), join(target, 'src'));
|
|
624
|
+
}
|
|
625
|
+
else {
|
|
626
|
+
ensureDirectorySymlink(join(source, 'src'), join(target, 'src'));
|
|
627
|
+
}
|
|
246
628
|
}
|
|
247
629
|
function findWorkspaceRoot(startDir) {
|
|
248
630
|
let currentDir = resolve(startDir);
|
|
@@ -288,6 +670,39 @@ function ensureDirectorySymlink(source, target) {
|
|
|
288
670
|
symlinkSync(normalizedSource, target, process.platform === 'win32' ? 'junction' : 'dir');
|
|
289
671
|
}
|
|
290
672
|
}
|
|
673
|
+
function replaceDirectoryContents(source, target, options = {}) {
|
|
674
|
+
rmSync(target, { recursive: true, force: true });
|
|
675
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
676
|
+
if ((options.dereference ?? true) === false && (options.verbatimSymlinks ?? false) === true) {
|
|
677
|
+
copyDirectoryTreePreservingSymlinks(source, target);
|
|
678
|
+
return;
|
|
679
|
+
}
|
|
680
|
+
cpSync(source, target, {
|
|
681
|
+
recursive: true,
|
|
682
|
+
force: true,
|
|
683
|
+
dereference: options.dereference ?? true,
|
|
684
|
+
verbatimSymlinks: options.verbatimSymlinks ?? false,
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
function copyDirectoryTreePreservingSymlinks(source, target) {
|
|
688
|
+
const stat = lstatSync(source);
|
|
689
|
+
if (stat.isSymbolicLink()) {
|
|
690
|
+
rmSync(target, { recursive: true, force: true });
|
|
691
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
692
|
+
const linkTarget = readlinkSync(source);
|
|
693
|
+
symlinkSync(linkTarget, target, process.platform === 'win32' ? 'junction' : 'dir');
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
if (stat.isDirectory()) {
|
|
697
|
+
mkdirSync(target, { recursive: true });
|
|
698
|
+
for (const entry of readdirSync(source)) {
|
|
699
|
+
copyDirectoryTreePreservingSymlinks(join(source, entry), join(target, entry));
|
|
700
|
+
}
|
|
701
|
+
return;
|
|
702
|
+
}
|
|
703
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
704
|
+
copyFileSync(source, target);
|
|
705
|
+
}
|
|
291
706
|
function readExistingSymlinkTarget(target) {
|
|
292
707
|
try {
|
|
293
708
|
if (!lstatSync(target).isSymbolicLink()) {
|
|
@@ -316,16 +731,16 @@ function buildSharedShimPackageJson() {
|
|
|
316
731
|
},
|
|
317
732
|
}, null, 2)}\n`;
|
|
318
733
|
}
|
|
319
|
-
function buildRuntimeConfigShimContents(injectedEnv) {
|
|
734
|
+
function buildRuntimeConfigShimContents(injectedEnv, importPath = '../../../../edgebase.config.ts') {
|
|
320
735
|
const normalizedEnv = Object.fromEntries(Object.entries(injectedEnv ?? {}).filter(([, value]) => typeof value === 'string'));
|
|
321
736
|
if (Object.keys(normalizedEnv).length === 0) {
|
|
322
|
-
return `// Auto-generated by edgebase CLI. Re-export the
|
|
323
|
-
import config from '
|
|
737
|
+
return `// Auto-generated by edgebase CLI. Re-export the runtime config module with a static path so Wrangler can bundle it.
|
|
738
|
+
import config from '${importPath}';
|
|
324
739
|
|
|
325
740
|
export default config;
|
|
326
741
|
`;
|
|
327
742
|
}
|
|
328
|
-
return `// Auto-generated by edgebase CLI. Inject local config env before evaluating the
|
|
743
|
+
return `// Auto-generated by edgebase CLI. Inject local config env before evaluating the runtime config module.
|
|
329
744
|
const injectedEnv = ${JSON.stringify(normalizedEnv, null, 2)} as Record<string, string>;
|
|
330
745
|
|
|
331
746
|
if (typeof process !== 'undefined' && process?.env) {
|
|
@@ -336,25 +751,27 @@ if (typeof process !== 'undefined' && process?.env) {
|
|
|
336
751
|
}
|
|
337
752
|
}
|
|
338
753
|
|
|
339
|
-
const mod = await import('
|
|
754
|
+
const mod = await import('${importPath}');
|
|
340
755
|
const config = (mod as { default?: unknown }).default ?? mod;
|
|
341
756
|
|
|
342
757
|
export default config;
|
|
343
758
|
`;
|
|
344
759
|
}
|
|
345
|
-
export function writeRuntimeConfigShim(projectDir, injectedEnv) {
|
|
760
|
+
export function writeRuntimeConfigShim(projectDir, injectedEnv, options) {
|
|
346
761
|
const shimPath = join(getRuntimeServerSrcDir(projectDir), 'generated-config.ts');
|
|
347
|
-
writeFileSync(shimPath, buildRuntimeConfigShimContents(injectedEnv), 'utf-8');
|
|
762
|
+
writeFileSync(shimPath, buildRuntimeConfigShimContents(injectedEnv, options?.importPath), 'utf-8');
|
|
348
763
|
}
|
|
349
|
-
function writeRuntimeTestConfigShim(projectDir) {
|
|
764
|
+
function writeRuntimeTestConfigShim(projectDir, options) {
|
|
350
765
|
const shimPath = join(getRuntimeRoot(projectDir), 'edgebase.test.config.ts');
|
|
351
|
-
const
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
766
|
+
const importPath = options?.importPath ?? (() => {
|
|
767
|
+
const tsConfigPath = join(projectDir, 'edgebase.test.config.ts');
|
|
768
|
+
const jsConfigPath = join(projectDir, 'edgebase.test.config.js');
|
|
769
|
+
return existsSync(tsConfigPath)
|
|
770
|
+
? '../../../edgebase.test.config.ts'
|
|
771
|
+
: existsSync(jsConfigPath)
|
|
772
|
+
? '../../../edgebase.test.config.js'
|
|
773
|
+
: './src/generated-config.ts';
|
|
774
|
+
})();
|
|
358
775
|
writeFileSync(shimPath, `// Auto-generated by edgebase CLI. Keep the runtime test-config import resolvable in scaffolded dev environments.
|
|
359
776
|
// If the project does not define a dedicated test config, fall back to the generated runtime config shim.
|
|
360
777
|
import config from '${importPath}';
|