@reliverse/dler 1.7.152 → 1.7.153
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/bin/impl/auth/impl/init.d.ts +2 -2
- package/bin/impl/build/impl.d.ts +7 -1
- package/bin/impl/build/impl.js +161 -1
- package/bin/impl/config/constants.d.ts +1 -1
- package/bin/impl/config/constants.js +1 -1
- package/bin/impl/providers/better-t-stack/types.d.ts +7 -7
- package/bin/impl/pub/impl.d.ts +6 -1
- package/bin/impl/pub/impl.js +176 -2
- package/bin/impl/schema/mod.d.ts +140 -0
- package/bin/impl/schema/mod.js +22 -0
- package/bin/impl/utils/workspace-prompt.d.ts +9 -0
- package/bin/impl/utils/workspace-prompt.js +46 -0
- package/bin/impl/utils/workspace-utils.d.ts +28 -0
- package/bin/impl/utils/workspace-utils.js +127 -0
- package/bin/mod.d.ts +2 -9
- package/bin/mod.js +9 -23
- package/package.json +2 -1
- package/bin/impl/migrate/codemods/anything-bun.d.ts +0 -5
- package/bin/impl/migrate/codemods/anything-bun.js +0 -577
- package/bin/impl/migrate/codemods/commander-rempts.d.ts +0 -4
- package/bin/impl/migrate/codemods/commander-rempts.js +0 -250
- package/bin/impl/migrate/codemods/console-relinka.d.ts +0 -3
- package/bin/impl/migrate/codemods/console-relinka.js +0 -142
- package/bin/impl/migrate/codemods/fs-relifso.d.ts +0 -8
- package/bin/impl/migrate/codemods/fs-relifso.js +0 -156
- package/bin/impl/migrate/codemods/monorepo-catalog.d.ts +0 -96
- package/bin/impl/migrate/codemods/monorepo-catalog.js +0 -517
- package/bin/impl/migrate/codemods/nodenext-bundler.d.ts +0 -10
- package/bin/impl/migrate/codemods/nodenext-bundler.js +0 -222
- package/bin/impl/migrate/codemods/path-pathkit.d.ts +0 -8
- package/bin/impl/migrate/codemods/path-pathkit.js +0 -143
- package/bin/impl/migrate/codemods/readdir-glob.d.ts +0 -8
- package/bin/impl/migrate/codemods/readdir-glob.js +0 -133
|
@@ -1,577 +0,0 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
2
|
-
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
|
-
import { join, resolve } from "@reliverse/pathkit";
|
|
4
|
-
import { relinka } from "@reliverse/relinka";
|
|
5
|
-
import { readPackageJSON } from "pkg-types";
|
|
6
|
-
import { glob } from "tinyglobby";
|
|
7
|
-
const NODE_MODULES = [
|
|
8
|
-
"assert",
|
|
9
|
-
"async_hooks",
|
|
10
|
-
"buffer",
|
|
11
|
-
"child_process",
|
|
12
|
-
"cluster",
|
|
13
|
-
"crypto",
|
|
14
|
-
"dgram",
|
|
15
|
-
"diagnostics_channel",
|
|
16
|
-
"dns",
|
|
17
|
-
"dns/promises",
|
|
18
|
-
"events",
|
|
19
|
-
"fs",
|
|
20
|
-
"fs/promises",
|
|
21
|
-
"http",
|
|
22
|
-
"http2",
|
|
23
|
-
"https",
|
|
24
|
-
"module",
|
|
25
|
-
"net",
|
|
26
|
-
"os",
|
|
27
|
-
"path",
|
|
28
|
-
"perf_hooks",
|
|
29
|
-
"punycode",
|
|
30
|
-
"querystring",
|
|
31
|
-
"readline",
|
|
32
|
-
"readline/promises",
|
|
33
|
-
"stream",
|
|
34
|
-
"stream/consumers",
|
|
35
|
-
"stream/promises",
|
|
36
|
-
"stream/web",
|
|
37
|
-
"string_decoder",
|
|
38
|
-
"test",
|
|
39
|
-
"test/reporters",
|
|
40
|
-
"timers/promises",
|
|
41
|
-
"tls",
|
|
42
|
-
"trace_events",
|
|
43
|
-
"tty",
|
|
44
|
-
"url",
|
|
45
|
-
"util",
|
|
46
|
-
"util/types",
|
|
47
|
-
"v8",
|
|
48
|
-
"vm",
|
|
49
|
-
"wasi",
|
|
50
|
-
"worker_threads",
|
|
51
|
-
"zlib"
|
|
52
|
-
];
|
|
53
|
-
const BUN_API_REPLACEMENTS = {
|
|
54
|
-
// Database drivers to Bun alternatives
|
|
55
|
-
pg: 'import { sql } from "bun";',
|
|
56
|
-
postgres: 'import { sql } from "bun";',
|
|
57
|
-
sqlite3: 'import { Database } from "bun:sqlite";',
|
|
58
|
-
"better-sqlite3": 'import { Database } from "bun:sqlite";',
|
|
59
|
-
// Redis clients
|
|
60
|
-
redis: 'import { redis } from "bun";',
|
|
61
|
-
ioredis: 'import { redis } from "bun";',
|
|
62
|
-
// Utilities
|
|
63
|
-
glob: 'import { Glob } from "bun";',
|
|
64
|
-
semver: 'import { semver } from "bun";',
|
|
65
|
-
bcrypt: 'import { password } from "bun";',
|
|
66
|
-
argon2: 'import { password } from "bun";',
|
|
67
|
-
// Test runners
|
|
68
|
-
jest: 'import { describe, it, expect, beforeAll, afterAll, beforeEach, afterEach } from "bun:test";',
|
|
69
|
-
"@jest/globals": 'import { describe, it, expect, beforeAll, afterAll, beforeEach, afterEach } from "bun:test";',
|
|
70
|
-
vitest: 'import { describe, it, expect, beforeAll, afterAll, beforeEach, afterEach } from "bun:test";',
|
|
71
|
-
// FFI libraries
|
|
72
|
-
"node-ffi": 'import { dlopen, FFIType } from "bun:ffi";',
|
|
73
|
-
"node-ffi-napi": 'import { dlopen, FFIType } from "bun:ffi";',
|
|
74
|
-
ffi: 'import { dlopen, FFIType } from "bun:ffi";'
|
|
75
|
-
};
|
|
76
|
-
const analyzeProject = async (projectRoot) => {
|
|
77
|
-
const packageJsonPath = join(projectRoot, "package.json");
|
|
78
|
-
if (!existsSync(packageJsonPath)) {
|
|
79
|
-
throw new Error("No package.json found in project root");
|
|
80
|
-
}
|
|
81
|
-
const packageJson = await readPackageJSON(projectRoot);
|
|
82
|
-
const dependencies = Object.keys(packageJson.dependencies || {});
|
|
83
|
-
const devDependencies = Object.keys(packageJson.devDependencies || {});
|
|
84
|
-
const allDeps = [...dependencies, ...devDependencies];
|
|
85
|
-
const framework = detectFramework(allDeps);
|
|
86
|
-
const sourceFiles = await glob("**/*.{ts,tsx,js,jsx}", {
|
|
87
|
-
cwd: projectRoot,
|
|
88
|
-
ignore: ["node_modules/**", "dist/**", "build/**", ".next/**"]
|
|
89
|
-
});
|
|
90
|
-
const testFiles = await glob("**/*.{test,spec}.{ts,tsx,js,jsx}", {
|
|
91
|
-
cwd: projectRoot,
|
|
92
|
-
ignore: ["node_modules/**"]
|
|
93
|
-
});
|
|
94
|
-
const configFiles = await glob("{*.config.{js,ts,json},.*rc*,tsconfig.json}", {
|
|
95
|
-
cwd: projectRoot
|
|
96
|
-
});
|
|
97
|
-
return {
|
|
98
|
-
packageJson,
|
|
99
|
-
hasTypeScript: allDeps.includes("typescript") || sourceFiles.some((f) => f.endsWith(".ts") || f.endsWith(".tsx")),
|
|
100
|
-
hasTests: testFiles.length > 0,
|
|
101
|
-
framework,
|
|
102
|
-
dependencies,
|
|
103
|
-
devDependencies,
|
|
104
|
-
scripts: Object.fromEntries(
|
|
105
|
-
Object.entries(packageJson.scripts || {}).filter(([, v]) => typeof v === "string")
|
|
106
|
-
),
|
|
107
|
-
configFiles,
|
|
108
|
-
sourceFiles,
|
|
109
|
-
testFiles
|
|
110
|
-
};
|
|
111
|
-
};
|
|
112
|
-
const detectFramework = (dependencies) => {
|
|
113
|
-
if (dependencies.includes("next")) return "next";
|
|
114
|
-
if (dependencies.includes("react")) return "react";
|
|
115
|
-
if (dependencies.includes("vue")) return "vue";
|
|
116
|
-
if (dependencies.includes("svelte")) return "svelte";
|
|
117
|
-
if (dependencies.includes("express")) return "express";
|
|
118
|
-
if (dependencies.includes("fastify")) return "fastify";
|
|
119
|
-
return null;
|
|
120
|
-
};
|
|
121
|
-
const transformPackageJson = (analysis) => {
|
|
122
|
-
const { packageJson } = analysis;
|
|
123
|
-
const changes = [];
|
|
124
|
-
const newScripts = {};
|
|
125
|
-
for (const [name, script] of Object.entries(packageJson.scripts || {})) {
|
|
126
|
-
let newScript = script;
|
|
127
|
-
newScript = newScript.replace(/\bnpm run\b/g, "bun run");
|
|
128
|
-
newScript = newScript.replace(/\bnpm install\b/g, "bun install");
|
|
129
|
-
newScript = newScript.replace(/\byarn\b/g, "bun");
|
|
130
|
-
newScript = newScript.replace(/\bnpx\b/g, "bunx");
|
|
131
|
-
if (newScript.includes("jest")) {
|
|
132
|
-
newScript = newScript.replace(/jest/g, "bun test");
|
|
133
|
-
changes.push(`Replaced Jest with bun test in script "${name}"`);
|
|
134
|
-
}
|
|
135
|
-
if (newScript.includes("vitest")) {
|
|
136
|
-
newScript = newScript.replace(/vitest/g, "bun test");
|
|
137
|
-
changes.push(`Replaced Vitest with bun test in script "${name}"`);
|
|
138
|
-
}
|
|
139
|
-
if (newScript.includes("node ")) {
|
|
140
|
-
newScript = newScript.replace(/\bnode\s+/g, "bun ");
|
|
141
|
-
changes.push(`Replaced Node.js with Bun runtime in script "${name}"`);
|
|
142
|
-
}
|
|
143
|
-
newScripts[name] = newScript;
|
|
144
|
-
if (newScript !== script) {
|
|
145
|
-
changes.push(`Updated script "${name}": ${script} \u2192 ${newScript}`);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
const newDependencies = { ...packageJson.dependencies };
|
|
149
|
-
const newDevDependencies = { ...packageJson.devDependencies };
|
|
150
|
-
const dependencyReplacements = {
|
|
151
|
-
// Database drivers
|
|
152
|
-
pg: null,
|
|
153
|
-
// Will use Bun.sql
|
|
154
|
-
postgres: null,
|
|
155
|
-
sqlite3: null,
|
|
156
|
-
// Will use bun:sqlite
|
|
157
|
-
"better-sqlite3": null,
|
|
158
|
-
redis: null,
|
|
159
|
-
// Will use Bun.redis
|
|
160
|
-
ioredis: null,
|
|
161
|
-
// Utilities that have Bun alternatives
|
|
162
|
-
glob: null,
|
|
163
|
-
// Will use Bun.Glob
|
|
164
|
-
semver: null,
|
|
165
|
-
// Will use Bun.semver
|
|
166
|
-
bcrypt: null,
|
|
167
|
-
// Will use Bun.password
|
|
168
|
-
argon2: null,
|
|
169
|
-
// Test runners
|
|
170
|
-
jest: null,
|
|
171
|
-
"@types/jest": null,
|
|
172
|
-
vitest: null,
|
|
173
|
-
"@vitest/ui": null,
|
|
174
|
-
"ts-jest": null,
|
|
175
|
-
// Build tools that Bun replaces
|
|
176
|
-
webpack: null,
|
|
177
|
-
rollup: null,
|
|
178
|
-
esbuild: null,
|
|
179
|
-
// Bun uses esbuild internally
|
|
180
|
-
// FFI libraries
|
|
181
|
-
"node-ffi": null,
|
|
182
|
-
"node-ffi-napi": null,
|
|
183
|
-
ffi: null
|
|
184
|
-
};
|
|
185
|
-
for (const [oldDep] of Object.entries(dependencyReplacements)) {
|
|
186
|
-
if (newDependencies[oldDep]) {
|
|
187
|
-
delete newDependencies[oldDep];
|
|
188
|
-
changes.push(`Removed dependency: ${oldDep} (replaced by Bun built-in)`);
|
|
189
|
-
}
|
|
190
|
-
if (newDevDependencies[oldDep]) {
|
|
191
|
-
delete newDevDependencies[oldDep];
|
|
192
|
-
changes.push(`Removed dev dependency: ${oldDep} (replaced by Bun built-in)`);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
const newPackageJson = {
|
|
196
|
-
...packageJson,
|
|
197
|
-
scripts: newScripts,
|
|
198
|
-
dependencies: newDependencies,
|
|
199
|
-
devDependencies: newDevDependencies
|
|
200
|
-
};
|
|
201
|
-
return {
|
|
202
|
-
filePath: "package.json",
|
|
203
|
-
originalContent: JSON.stringify(packageJson, null, 2),
|
|
204
|
-
transformedContent: JSON.stringify(newPackageJson, null, 2),
|
|
205
|
-
changes
|
|
206
|
-
};
|
|
207
|
-
};
|
|
208
|
-
const transformSourceFile = (filePath, content) => {
|
|
209
|
-
let transformedContent = content;
|
|
210
|
-
const changes = [];
|
|
211
|
-
for (const nodeModule of NODE_MODULES) {
|
|
212
|
-
const patterns = [
|
|
213
|
-
// import ... from "module"
|
|
214
|
-
new RegExp(`import\\s+([^"']*?)from\\s+['"]${nodeModule}['"];?`, "g"),
|
|
215
|
-
// import("module")
|
|
216
|
-
new RegExp(`import\\s*\\(\\s*['"]${nodeModule}['"]\\s*\\)`, "g"),
|
|
217
|
-
// require("module")
|
|
218
|
-
new RegExp(`require\\s*\\(\\s*['"]${nodeModule}['"]\\s*\\)`, "g")
|
|
219
|
-
];
|
|
220
|
-
for (const pattern of patterns) {
|
|
221
|
-
if (pattern.test(transformedContent)) {
|
|
222
|
-
transformedContent = transformedContent.replace(
|
|
223
|
-
pattern,
|
|
224
|
-
(match) => match.replace(`"${nodeModule}"`, `"node:${nodeModule}"`).replace(`'${nodeModule}'`, `'node:${nodeModule}'`)
|
|
225
|
-
);
|
|
226
|
-
if (!changes.includes(`Added node: prefix to ${nodeModule} imports`)) {
|
|
227
|
-
changes.push(`Added node: prefix to ${nodeModule} imports`);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
for (const [oldModule, newImport] of Object.entries(BUN_API_REPLACEMENTS)) {
|
|
233
|
-
if (content.includes(`from "${oldModule}"`) || content.includes(`from '${oldModule}'`)) {
|
|
234
|
-
const importPattern = new RegExp(
|
|
235
|
-
`import\\s+.*?from\\s+['"]${oldModule.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}['"];?\\s*`,
|
|
236
|
-
"g"
|
|
237
|
-
);
|
|
238
|
-
if (importPattern.test(transformedContent)) {
|
|
239
|
-
transformedContent = transformedContent.replace(importPattern, `${newImport}
|
|
240
|
-
`);
|
|
241
|
-
changes.push(`Replaced ${oldModule} import with Bun alternative`);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
transformedContent = transformedContent.replace(
|
|
246
|
-
/fs\.readFileSync\((.*?)\)/g,
|
|
247
|
-
"await Bun.file($1).text()"
|
|
248
|
-
);
|
|
249
|
-
transformedContent = transformedContent.replace(
|
|
250
|
-
/fs\.writeFileSync\((.*?),\s*(.*?)\)/g,
|
|
251
|
-
"await Bun.write($1, $2)"
|
|
252
|
-
);
|
|
253
|
-
transformedContent = transformedContent.replace(
|
|
254
|
-
/fs\.existsSync\((.*?)\)/g,
|
|
255
|
-
"await Bun.file($1).exists()"
|
|
256
|
-
);
|
|
257
|
-
if (transformedContent.includes("Bun.file") || transformedContent.includes("Bun.write")) {
|
|
258
|
-
changes.push("Converted fs operations to Bun file API");
|
|
259
|
-
}
|
|
260
|
-
if (content.includes("bcrypt.hash") || content.includes("argon2.hash")) {
|
|
261
|
-
transformedContent = transformedContent.replace(
|
|
262
|
-
/bcrypt\.hash\((.*?),\s*(.*?)\)/g,
|
|
263
|
-
"await Bun.password.hash($1, { algorithm: 'bcrypt', cost: $2 })"
|
|
264
|
-
);
|
|
265
|
-
transformedContent = transformedContent.replace(
|
|
266
|
-
/argon2\.hash\((.*?)\)/g,
|
|
267
|
-
"await Bun.password.hash($1, { algorithm: 'argon2id' })"
|
|
268
|
-
);
|
|
269
|
-
changes.push("Converted password hashing to Bun.password");
|
|
270
|
-
}
|
|
271
|
-
if (content.includes("glob.sync") || content.includes("glob(")) {
|
|
272
|
-
transformedContent = transformedContent.replace(
|
|
273
|
-
/glob\.sync\((.*?)\)/g,
|
|
274
|
-
"new Glob($1).scanSync('.')"
|
|
275
|
-
);
|
|
276
|
-
transformedContent = transformedContent.replace(/glob\((.*?)\)/g, "new Glob($1).scan('.')");
|
|
277
|
-
changes.push("Converted glob operations to Bun.Glob");
|
|
278
|
-
}
|
|
279
|
-
if (content.includes("semver.")) {
|
|
280
|
-
transformedContent = transformedContent.replace(/semver\./g, "Bun.semver.");
|
|
281
|
-
changes.push("Converted semver operations to Bun.semver");
|
|
282
|
-
}
|
|
283
|
-
if (content.includes("new Database") && content.includes("sqlite3")) {
|
|
284
|
-
transformedContent = transformedContent.replace(
|
|
285
|
-
/new sqlite3\.Database\((.*?)\)/g,
|
|
286
|
-
"new Database($1)"
|
|
287
|
-
);
|
|
288
|
-
changes.push("Converted SQLite3 to bun:sqlite");
|
|
289
|
-
}
|
|
290
|
-
if (content.includes("createClient") && (content.includes("redis") || content.includes("ioredis"))) {
|
|
291
|
-
transformedContent = transformedContent.replace(
|
|
292
|
-
/redis\.createClient\((.*?)\)/g,
|
|
293
|
-
"Bun.redis($1)"
|
|
294
|
-
);
|
|
295
|
-
transformedContent = transformedContent.replace(/new Redis\((.*?)\)/g, "Bun.redis($1)");
|
|
296
|
-
changes.push("Converted Redis client to Bun.redis");
|
|
297
|
-
}
|
|
298
|
-
if (content.includes("ffi.Library") || content.includes("dlopen")) {
|
|
299
|
-
transformedContent = transformedContent.replace(
|
|
300
|
-
/ffi\.Library\((.*?),\s*(.*?)\)/g,
|
|
301
|
-
"dlopen($1, $2)"
|
|
302
|
-
);
|
|
303
|
-
changes.push("Converted FFI usage to bun:ffi");
|
|
304
|
-
}
|
|
305
|
-
if (content.includes("express()")) {
|
|
306
|
-
const expressReplacement = `
|
|
307
|
-
import { serve } from "bun";
|
|
308
|
-
const server = serve({
|
|
309
|
-
port: 3000,
|
|
310
|
-
fetch(req) {
|
|
311
|
-
const url = new URL(req.url);
|
|
312
|
-
// Add your routes here
|
|
313
|
-
if (url.pathname === "/") {
|
|
314
|
-
return new Response("Hello from Bun!");
|
|
315
|
-
}
|
|
316
|
-
return new Response("Not Found", { status: 404 });
|
|
317
|
-
},
|
|
318
|
-
});
|
|
319
|
-
relinka("verbose", \`Server running on localhost:\${server.port}\`);
|
|
320
|
-
`;
|
|
321
|
-
if (content.includes("const app = express()")) {
|
|
322
|
-
transformedContent = transformedContent.replace(
|
|
323
|
-
/const app = express\(\);[\s\S]*?app\.listen\(.*?\);?/,
|
|
324
|
-
expressReplacement
|
|
325
|
-
);
|
|
326
|
-
changes.push("Converted Express app to Bun.serve (manual route migration needed)");
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
if (content.includes("exec") || content.includes("spawn")) {
|
|
330
|
-
transformedContent = transformedContent.replace(/exec\((.*?)\)/g, "await Bun.$`$1`");
|
|
331
|
-
transformedContent = transformedContent.replace(
|
|
332
|
-
/spawn\((.*?),\s*(.*?)\)/g,
|
|
333
|
-
"await Bun.$`$1 ${$2.join(' ')}`"
|
|
334
|
-
);
|
|
335
|
-
changes.push("Converted child_process operations to Bun.$");
|
|
336
|
-
}
|
|
337
|
-
return {
|
|
338
|
-
filePath,
|
|
339
|
-
originalContent: content,
|
|
340
|
-
transformedContent,
|
|
341
|
-
changes
|
|
342
|
-
};
|
|
343
|
-
};
|
|
344
|
-
const transformTestFile = (filePath, content) => {
|
|
345
|
-
let transformedContent = content;
|
|
346
|
-
const changes = [];
|
|
347
|
-
if (content.includes("@jest/globals") || content.includes("vitest")) {
|
|
348
|
-
transformedContent = transformedContent.replace(
|
|
349
|
-
/import.*?from\s+['"]@jest\/globals['"];?\s*/g,
|
|
350
|
-
'import { describe, it, expect, beforeAll, afterAll, beforeEach, afterEach } from "bun:test";\n'
|
|
351
|
-
);
|
|
352
|
-
transformedContent = transformedContent.replace(
|
|
353
|
-
/import.*?from\s+['"]vitest['"];?\s*/g,
|
|
354
|
-
'import { describe, it, expect, beforeAll, afterAll, beforeEach, afterEach } from "bun:test";\n'
|
|
355
|
-
);
|
|
356
|
-
changes.push("Replaced Jest/Vitest imports with bun:test");
|
|
357
|
-
}
|
|
358
|
-
if (content.includes("jest.")) {
|
|
359
|
-
transformedContent = transformedContent.replace(/jest\.mock/g, "mock");
|
|
360
|
-
transformedContent = transformedContent.replace(/jest\.spyOn/g, "spyOn");
|
|
361
|
-
transformedContent = transformedContent.replace(/jest\.fn/g, "mock");
|
|
362
|
-
changes.push("Updated Jest APIs to bun:test equivalents");
|
|
363
|
-
}
|
|
364
|
-
if ((content.includes("describe(") || content.includes("it(") || content.includes("test(")) && !content.includes('from "bun:test"') && !content.includes("from 'bun:test'")) {
|
|
365
|
-
transformedContent = `import { describe, it, test, expect, beforeAll, afterAll, beforeEach, afterEach } from "bun:test";
|
|
366
|
-
${transformedContent}`;
|
|
367
|
-
changes.push("Added bun:test import for test functions");
|
|
368
|
-
}
|
|
369
|
-
return {
|
|
370
|
-
filePath,
|
|
371
|
-
originalContent: content,
|
|
372
|
-
transformedContent,
|
|
373
|
-
changes
|
|
374
|
-
};
|
|
375
|
-
};
|
|
376
|
-
const generateBunConfig = (analysis) => `# Bun configuration file
|
|
377
|
-
[install]
|
|
378
|
-
cache = true
|
|
379
|
-
auto = "bun"
|
|
380
|
-
production = false
|
|
381
|
-
[test]
|
|
382
|
-
preload = ["./test/setup.ts"]
|
|
383
|
-
timeout = 5000
|
|
384
|
-
[run]
|
|
385
|
-
shell = "bun"
|
|
386
|
-
[build]
|
|
387
|
-
target = "bun"
|
|
388
|
-
outdir = "./dist"
|
|
389
|
-
sourcemap = "external"
|
|
390
|
-
${analysis.hasTypeScript ? `
|
|
391
|
-
[typescript]
|
|
392
|
-
compilerOptions = "tsconfig.json"
|
|
393
|
-
` : ""}
|
|
394
|
-
`;
|
|
395
|
-
const generateDockerfile = (analysis) => `# Use Bun's official image
|
|
396
|
-
FROM oven/bun:1 as base
|
|
397
|
-
WORKDIR /usr/src/app
|
|
398
|
-
# Install dependencies
|
|
399
|
-
COPY package.json bun.lock* ./
|
|
400
|
-
RUN bun install --frozen-lockfile
|
|
401
|
-
# Copy source code
|
|
402
|
-
COPY . .
|
|
403
|
-
# Build the app (if needed)
|
|
404
|
-
${analysis.scripts.build ? "RUN bun run build" : ""}
|
|
405
|
-
# Start the app
|
|
406
|
-
EXPOSE 3000
|
|
407
|
-
CMD ["bun", "start"]
|
|
408
|
-
`;
|
|
409
|
-
const createBackup = async (projectRoot) => {
|
|
410
|
-
const backupDir = join(projectRoot, ".bun-migration-backup");
|
|
411
|
-
if (!existsSync(backupDir)) {
|
|
412
|
-
await mkdir(backupDir, { recursive: true });
|
|
413
|
-
}
|
|
414
|
-
const packageJsonPath = join(projectRoot, "package.json");
|
|
415
|
-
if (existsSync(packageJsonPath)) {
|
|
416
|
-
const backupPath = join(backupDir, "package.json");
|
|
417
|
-
const packageJson = await readPackageJSON(projectRoot);
|
|
418
|
-
await writeFile(backupPath, JSON.stringify(packageJson, null, 2));
|
|
419
|
-
}
|
|
420
|
-
};
|
|
421
|
-
const writeTransformedFile = async (projectRoot, result, dryRun) => {
|
|
422
|
-
if (dryRun) {
|
|
423
|
-
relinka("verbose", `[DRY RUN] Would transform ${result.filePath}`);
|
|
424
|
-
for (const change of result.changes) {
|
|
425
|
-
relinka("verbose", ` - ${change}`);
|
|
426
|
-
}
|
|
427
|
-
return;
|
|
428
|
-
}
|
|
429
|
-
const fullPath = join(projectRoot, result.filePath);
|
|
430
|
-
await writeFile(fullPath, result.transformedContent);
|
|
431
|
-
relinka("verbose", `\u2713 Transformed ${result.filePath}`);
|
|
432
|
-
for (const change of result.changes) {
|
|
433
|
-
relinka("verbose", ` - ${change}`);
|
|
434
|
-
}
|
|
435
|
-
};
|
|
436
|
-
const migrateProject = async (config) => {
|
|
437
|
-
const { projectRoot, dryRun, backup } = config;
|
|
438
|
-
relinka("verbose", "\u{1F50D} Analyzing project...");
|
|
439
|
-
const analysis = await analyzeProject(projectRoot);
|
|
440
|
-
relinka(
|
|
441
|
-
"log",
|
|
442
|
-
`\u{1F4CA} Analysis complete:
|
|
443
|
-
- Framework: ${analysis.framework || "None detected"}
|
|
444
|
-
- TypeScript: ${analysis.hasTypeScript ? "Yes" : "No"}
|
|
445
|
-
- Tests: ${analysis.hasTests ? "Yes" : "No"}
|
|
446
|
-
- Source files: ${analysis.sourceFiles.length}
|
|
447
|
-
- Test files: ${analysis.testFiles.length}
|
|
448
|
-
`
|
|
449
|
-
);
|
|
450
|
-
if (backup && !dryRun) {
|
|
451
|
-
relinka("verbose", "\u{1F4BE} Creating backup...");
|
|
452
|
-
await createBackup(projectRoot);
|
|
453
|
-
}
|
|
454
|
-
const transformResults = [];
|
|
455
|
-
const manualSteps = [];
|
|
456
|
-
const errors = [];
|
|
457
|
-
const warnings = [];
|
|
458
|
-
try {
|
|
459
|
-
relinka("verbose", "\u{1F4E6} Transforming package.json...");
|
|
460
|
-
const packageJsonResult = transformPackageJson(analysis);
|
|
461
|
-
transformResults.push(packageJsonResult);
|
|
462
|
-
await writeTransformedFile(projectRoot, packageJsonResult, dryRun);
|
|
463
|
-
relinka("verbose", "\u{1F527} Transforming source files...");
|
|
464
|
-
for (const sourceFile of analysis.sourceFiles.slice(0, 50)) {
|
|
465
|
-
try {
|
|
466
|
-
const fullPath = join(projectRoot, sourceFile);
|
|
467
|
-
const content = await readFile(fullPath, "utf8");
|
|
468
|
-
const result = transformSourceFile(sourceFile, content);
|
|
469
|
-
if (result.changes.length > 0) {
|
|
470
|
-
transformResults.push(result);
|
|
471
|
-
await writeTransformedFile(projectRoot, result, dryRun);
|
|
472
|
-
}
|
|
473
|
-
} catch (error) {
|
|
474
|
-
errors.push(`Failed to transform ${sourceFile}: ${error}`);
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
if (analysis.hasTests) {
|
|
478
|
-
relinka("verbose", "\u{1F9EA} Transforming test files...");
|
|
479
|
-
for (const testFile of analysis.testFiles.slice(0, 20)) {
|
|
480
|
-
try {
|
|
481
|
-
const fullPath = join(projectRoot, testFile);
|
|
482
|
-
const content = await readFile(fullPath, "utf8");
|
|
483
|
-
const result = transformTestFile(testFile, content);
|
|
484
|
-
if (result.changes.length > 0) {
|
|
485
|
-
transformResults.push(result);
|
|
486
|
-
await writeTransformedFile(projectRoot, result, dryRun);
|
|
487
|
-
}
|
|
488
|
-
} catch (error) {
|
|
489
|
-
errors.push(`Failed to transform test ${testFile}: ${error}`);
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
if (!dryRun) {
|
|
494
|
-
relinka("verbose", "\u2699\uFE0F Generating Bun configuration...");
|
|
495
|
-
const bunConfig = generateBunConfig(analysis);
|
|
496
|
-
await writeFile(join(projectRoot, "bunfig.toml"), bunConfig);
|
|
497
|
-
const dockerfile = generateDockerfile(analysis);
|
|
498
|
-
await writeFile(join(projectRoot, "Dockerfile.bun"), dockerfile);
|
|
499
|
-
}
|
|
500
|
-
manualSteps.push("Run 'bun install' to install dependencies with Bun");
|
|
501
|
-
manualSteps.push("Update your CI/CD scripts to use 'bun' instead of npm/yarn");
|
|
502
|
-
manualSteps.push("Test your application thoroughly after migration");
|
|
503
|
-
manualSteps.push("Review async/await usage in converted file operations");
|
|
504
|
-
if (analysis.framework === "express") {
|
|
505
|
-
manualSteps.push("Manual Express route migration to Bun.serve required");
|
|
506
|
-
manualSteps.push("Review middleware conversions");
|
|
507
|
-
}
|
|
508
|
-
if (analysis.dependencies.includes("pg") || analysis.dependencies.includes("postgres")) {
|
|
509
|
-
manualSteps.push("Update database queries to use Bun.sql syntax");
|
|
510
|
-
}
|
|
511
|
-
if (analysis.dependencies.includes("sqlite3") || analysis.dependencies.includes("better-sqlite3")) {
|
|
512
|
-
manualSteps.push("Update SQLite usage to bun:sqlite API");
|
|
513
|
-
}
|
|
514
|
-
if (analysis.dependencies.includes("redis") || analysis.dependencies.includes("ioredis")) {
|
|
515
|
-
manualSteps.push("Update Redis client usage to Bun.redis API");
|
|
516
|
-
}
|
|
517
|
-
if (analysis.dependencies.includes("bcrypt") || analysis.dependencies.includes("argon2")) {
|
|
518
|
-
manualSteps.push("Update password hashing to Bun.password API");
|
|
519
|
-
}
|
|
520
|
-
} catch (error) {
|
|
521
|
-
errors.push(`Migration failed: ${error}`);
|
|
522
|
-
}
|
|
523
|
-
return {
|
|
524
|
-
filesTransformed: transformResults.length,
|
|
525
|
-
transformResults,
|
|
526
|
-
manualSteps,
|
|
527
|
-
errors,
|
|
528
|
-
warnings
|
|
529
|
-
};
|
|
530
|
-
};
|
|
531
|
-
export async function migrateAnythingToBun({
|
|
532
|
-
project = ".",
|
|
533
|
-
dryRun = false,
|
|
534
|
-
noBackup = false
|
|
535
|
-
}) {
|
|
536
|
-
const config = {
|
|
537
|
-
projectRoot: resolve(project),
|
|
538
|
-
dryRun,
|
|
539
|
-
backup: !noBackup,
|
|
540
|
-
selective: [],
|
|
541
|
-
skipFrameworks: []
|
|
542
|
-
};
|
|
543
|
-
relinka("verbose", "\u{1F680} Starting Bun migration...");
|
|
544
|
-
relinka("verbose", `\u{1F4C1} Project: ${config.projectRoot}`);
|
|
545
|
-
relinka("verbose", `\u{1F50D} Mode: ${config.dryRun ? "DRY RUN" : "LIVE MIGRATION"}`);
|
|
546
|
-
relinka("verbose", "");
|
|
547
|
-
try {
|
|
548
|
-
const report = await migrateProject(config);
|
|
549
|
-
relinka("verbose", "\n\u2705 Migration complete!");
|
|
550
|
-
relinka("verbose", `\u{1F4CA} Files transformed: ${report.filesTransformed}`);
|
|
551
|
-
if (report.manualSteps.length > 0) {
|
|
552
|
-
relinka("verbose", "\n\u{1F4CB} Manual steps required:");
|
|
553
|
-
for (const step of report.manualSteps) {
|
|
554
|
-
relinka("verbose", ` \u2022 ${step}`);
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
if (report.warnings.length > 0) {
|
|
558
|
-
relinka("verbose", "\n\u26A0\uFE0F Warnings:");
|
|
559
|
-
for (const warning of report.warnings) {
|
|
560
|
-
relinka("verbose", ` \u2022 ${warning}`);
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
if (report.errors.length > 0) {
|
|
564
|
-
relinka("verbose", "\n\u274C Errors:");
|
|
565
|
-
for (const error of report.errors) {
|
|
566
|
-
relinka("verbose", ` \u2022 ${error}`);
|
|
567
|
-
}
|
|
568
|
-
}
|
|
569
|
-
if (!config.dryRun) {
|
|
570
|
-
relinka("verbose", "\n\u2705 Your project has been migrated to Bun!");
|
|
571
|
-
relinka("verbose", "Run 'bun install' to get started.");
|
|
572
|
-
}
|
|
573
|
-
} catch (error) {
|
|
574
|
-
relinka("error", "\u274C Migration failed:", error);
|
|
575
|
-
process.exit(1);
|
|
576
|
-
}
|
|
577
|
-
}
|