initial commit
This commit is contained in:
274
packages/db/scripts/patch-prisma-imports.ts
Executable file
274
packages/db/scripts/patch-prisma-imports.ts
Executable file
@@ -0,0 +1,274 @@
|
||||
#!/usr/bin/env ts-node
|
||||
/**
|
||||
* patch-prisma-imports.ts (SAFE)
|
||||
*
|
||||
* - Converts value-level imports/exports of `Prisma` -> type-only imports/exports
|
||||
* (splits mixed imports).
|
||||
* - Replaces runtime usages of `Prisma.Decimal` -> `Decimal`.
|
||||
* - Ensures exactly one `import Decimal from "decimal.js";` per file.
|
||||
* - DEDICATED: only modifies TypeScript source files (.ts/.tsx).
|
||||
* - SKIPS: files under packages/db/generated/prisma (the Prisma runtime package).
|
||||
*
|
||||
* Usage:
|
||||
* npx ts-node packages/db/scripts/patch-prisma-imports.ts
|
||||
*
|
||||
* Run after `prisma generate` (and make sure generated runtime .js are restored
|
||||
* if they were modified — see notes below).
|
||||
*/
|
||||
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import fg from "fast-glob";
|
||||
|
||||
const repoRoot = process.cwd();
|
||||
const GENERATED_FRAGMENT = path.join("packages", "db", "generated", "prisma");
|
||||
|
||||
// Only operate on TS sources (do NOT touch .js)
|
||||
const GLOBS = [
|
||||
"packages/db/shared/**/*.ts",
|
||||
"packages/db/shared/**/*.tsx",
|
||||
"packages/db/generated/**/*.ts",
|
||||
"packages/db/generated/**/*.tsx",
|
||||
];
|
||||
|
||||
// -------------------- helpers --------------------
|
||||
|
||||
function isFromGeneratedPrisma(fromPath: string) {
|
||||
// match relative imports that include generated/prisma
|
||||
return (
|
||||
fromPath.includes("generated/prisma") ||
|
||||
fromPath.includes("/generated/prisma") ||
|
||||
fromPath.includes("\\generated\\prisma")
|
||||
);
|
||||
}
|
||||
|
||||
function splitSpecifiers(list: string) {
|
||||
return list
|
||||
.split(",")
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
function buildNamedImport(specs: string[]) {
|
||||
return `{ ${specs.join(", ")} }`;
|
||||
}
|
||||
|
||||
function extractDecimalLines(src: string) {
|
||||
const lines = src.split(/\r?\n/);
|
||||
const matches: number[] = [];
|
||||
|
||||
const regexes = [
|
||||
/^import\s+Decimal\s+from\s+['"]decimal\.js['"]\s*;?/,
|
||||
/^import\s+\{\s*Decimal\s*\}\s+from\s+['"]decimal\.js['"]\s*;?/,
|
||||
/^import\s+\*\s+as\s+Decimal\s+from\s+['"]decimal\.js['"]\s*;?/,
|
||||
/^(const|let|var)\s+Decimal\s*=\s*require\(\s*['"]decimal\.js['"]\s*\)\s*;?/,
|
||||
/^(const|let|var)\s+Decimal\s*=\s*require\(\s*['"]decimal\.js['"]\s*\)\.default\s*;?/,
|
||||
];
|
||||
|
||||
lines.forEach((line, i) => {
|
||||
for (const re of regexes) {
|
||||
if (re.test(line)) {
|
||||
matches.push(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return { lines, matches };
|
||||
}
|
||||
|
||||
function ensureSingleDecimalImport(src: string) {
|
||||
const { lines, matches } = extractDecimalLines(src);
|
||||
|
||||
if (matches.length === 0) return src;
|
||||
|
||||
// remove all matched import/require lines
|
||||
// do in reverse index order to keep indices valid
|
||||
matches
|
||||
.slice()
|
||||
.sort((a, b) => b - a)
|
||||
.forEach((idx) => lines.splice(idx, 1));
|
||||
|
||||
let result = lines.join("\n");
|
||||
|
||||
// insert single canonical import if missing
|
||||
if (!/import\s+Decimal\s+from\s+['"]decimal\.js['"]/.test(result)) {
|
||||
const importBlockMatch = result.match(/^(?:\s*import[\s\S]*?;\r?\n)+/);
|
||||
if (importBlockMatch && importBlockMatch.index !== undefined) {
|
||||
const idx = importBlockMatch[0].length;
|
||||
result =
|
||||
result.slice(0, idx) +
|
||||
`\nimport Decimal from "decimal.js";\n` +
|
||||
result.slice(idx);
|
||||
} else {
|
||||
result = `import Decimal from "decimal.js";\n` + result;
|
||||
}
|
||||
}
|
||||
|
||||
// collapse excessive blank lines
|
||||
result = result.replace(/\n{3,}/g, "\n\n");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function replacePrismaDecimalRuntime(src: string) {
|
||||
if (!/\bPrisma\.Decimal\b/.test(src)) return { out: src, changed: false };
|
||||
|
||||
// mask import/export-from lines so we don't accidentally change them
|
||||
const placeholder =
|
||||
"__MASK_IMPORT_EXPORT__" + Math.random().toString(36).slice(2);
|
||||
const saved: string[] = [];
|
||||
|
||||
const masked = src.replace(
|
||||
/(^\s*(?:import|export)\s+[\s\S]*?from\s+['"][^'"]+['"]\s*;?)/gm,
|
||||
(m) => {
|
||||
saved.push(m);
|
||||
return `${placeholder}${saved.length - 1}__\n`;
|
||||
}
|
||||
);
|
||||
|
||||
const replaced = masked.replace(/\bPrisma\.Decimal\b/g, "Decimal");
|
||||
|
||||
const restored = replaced.replace(
|
||||
new RegExp(`${placeholder}(\\d+)__\\n`, "g"),
|
||||
(_m, i) => saved[Number(i)] || ""
|
||||
);
|
||||
|
||||
return { out: restored, changed: true };
|
||||
}
|
||||
|
||||
// -------------------- patching logic --------------------
|
||||
|
||||
function patchFileContent(src: string, filePath: string) {
|
||||
// safety: do not edit runtime prisma package files
|
||||
const normalized = path.normalize(filePath);
|
||||
if (normalized.includes(path.normalize(GENERATED_FRAGMENT))) {
|
||||
// skip any files inside packages/db/generated/prisma
|
||||
return { out: src, changed: false, skipped: true };
|
||||
}
|
||||
|
||||
let out = src;
|
||||
let changed = false;
|
||||
|
||||
// 1) Named imports
|
||||
out = out.replace(
|
||||
/import\s+(?!type)(\{[^}]+\})\s+from\s+(['"])([^'"]+)\2\s*;?/gm,
|
||||
(match, specBlock: string, q: string, fromPath: string) => {
|
||||
if (!isFromGeneratedPrisma(fromPath)) return match;
|
||||
|
||||
const specList = specBlock.replace(/^\{|\}$/g, "").trim();
|
||||
const specs = splitSpecifiers(specList);
|
||||
|
||||
const prismaEntries = specs.filter((s) =>
|
||||
/^\s*Prisma(\s+as\s+\w+)?\s*$/.test(s)
|
||||
);
|
||||
const otherEntries = specs.filter(
|
||||
(s) => !/^\s*Prisma(\s+as\s+\w+)?\s*$/.test(s)
|
||||
);
|
||||
|
||||
if (prismaEntries.length === 0) return match;
|
||||
|
||||
changed = true;
|
||||
let replacement = `import type ${buildNamedImport(prismaEntries)} from ${q}${fromPath}${q};`;
|
||||
if (otherEntries.length > 0) {
|
||||
replacement += `\nimport ${buildNamedImport(otherEntries)} from ${q}${fromPath}${q};`;
|
||||
}
|
||||
return replacement;
|
||||
}
|
||||
);
|
||||
|
||||
// 2) Named exports
|
||||
out = out.replace(
|
||||
/export\s+(?!type)(\{[^}]+\})\s+from\s+(['"])([^'"]+)\2\s*;?/gm,
|
||||
(match, specBlock: string, q: string, fromPath: string) => {
|
||||
if (!isFromGeneratedPrisma(fromPath)) return match;
|
||||
|
||||
const specList = specBlock.replace(/^\{|\}$/g, "").trim();
|
||||
const specs = splitSpecifiers(specList);
|
||||
|
||||
const prismaEntries = specs.filter((s) =>
|
||||
/^\s*Prisma(\s+as\s+\w+)?\s*$/.test(s)
|
||||
);
|
||||
const otherEntries = specs.filter(
|
||||
(s) => !/^\s*Prisma(\s+as\s+\w+)?\s*$/.test(s)
|
||||
);
|
||||
|
||||
if (prismaEntries.length === 0) return match;
|
||||
|
||||
changed = true;
|
||||
let replacement = `export type ${buildNamedImport(prismaEntries)} from ${q}${fromPath}${q};`;
|
||||
if (otherEntries.length > 0) {
|
||||
replacement += `\nexport ${buildNamedImport(otherEntries)} from ${q}${fromPath}${q};`;
|
||||
}
|
||||
return replacement;
|
||||
}
|
||||
);
|
||||
|
||||
// 3) Namespace imports
|
||||
out = out.replace(
|
||||
/import\s+\*\s+as\s+([A-Za-z0-9_$]+)\s+from\s+(['"])([^'"]+)\2\s*;?/gm,
|
||||
(match, ns: string, q: string, fromPath: string) => {
|
||||
if (!isFromGeneratedPrisma(fromPath)) return match;
|
||||
changed = true;
|
||||
return `import type * as ${ns} from ${q}${fromPath}${q};`;
|
||||
}
|
||||
);
|
||||
|
||||
// 4) Default imports
|
||||
out = out.replace(
|
||||
/import\s+(?!type)([A-Za-z0-9_$]+)\s+from\s+(['"])([^'"]+)\2\s*;?/gm,
|
||||
(match, binding: string, q: string, fromPath: string) => {
|
||||
if (!isFromGeneratedPrisma(fromPath)) return match;
|
||||
changed = true;
|
||||
return `import type ${binding} from ${q}${fromPath}${q};`;
|
||||
}
|
||||
);
|
||||
|
||||
// 5) Replace Prisma.Decimal -> Decimal safely
|
||||
if (/\bPrisma\.Decimal\b/.test(out)) {
|
||||
const { out: decimalOut, changed: decimalChanged } =
|
||||
replacePrismaDecimalRuntime(out);
|
||||
out = decimalOut;
|
||||
if (decimalChanged) changed = true;
|
||||
// Ensure a single Decimal import exists
|
||||
out = ensureSingleDecimalImport(out);
|
||||
}
|
||||
|
||||
return { out, changed, skipped: false };
|
||||
}
|
||||
|
||||
// -------------------- runner --------------------
|
||||
|
||||
async function run() {
|
||||
const files = await fg(GLOBS, { absolute: true, cwd: repoRoot, dot: true });
|
||||
if (!files || files.length === 0) {
|
||||
console.warn(
|
||||
"No files matched. Check the GLOBS patterns and run from repo root."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const file of files) {
|
||||
try {
|
||||
const src = fs.readFileSync(file, "utf8");
|
||||
const { out, changed, skipped } = patchFileContent(src, file);
|
||||
if (skipped) {
|
||||
// intentionally skipped runtime-prisma files
|
||||
continue;
|
||||
}
|
||||
if (changed && out !== src) {
|
||||
fs.writeFileSync(file, out, "utf8");
|
||||
console.log("patched:", path.relative(repoRoot, file));
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("failed patching", file, err);
|
||||
}
|
||||
}
|
||||
|
||||
console.log("done.");
|
||||
}
|
||||
|
||||
run().catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
19
packages/db/scripts/patch-zod-buffer.ts
Executable file
19
packages/db/scripts/patch-zod-buffer.ts
Executable file
@@ -0,0 +1,19 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
|
||||
const dir = path.resolve(__dirname, '../shared/schemas/objects');
|
||||
|
||||
fs.readdirSync(dir).forEach(file => {
|
||||
if (!file.endsWith('.schema.ts')) return;
|
||||
const full = path.join(dir, file);
|
||||
let content = fs.readFileSync(full, 'utf8');
|
||||
if (content.includes('z.instanceof(Buffer)') && !content.includes("import { Buffer")) {
|
||||
content = `import { Buffer } from 'buffer';\n` + content;
|
||||
fs.writeFileSync(full, content);
|
||||
console.log('Patched:', file);
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user