UNPKG

@nuxthub/core

Version:

Build full-stack Nuxt applications on Cloudflare, with zero configuration.

160 lines (159 loc) • 5.31 kB
import { readFile } from "node:fs/promises"; import { createStorage } from "unstorage"; import fsDriver from "unstorage/drivers/fs"; import overlayDriver from "unstorage/drivers/overlay"; import { join } from "pathe"; export function useDatabaseMigrationsStorage(hub) { return createStorage({ driver: fsDriver({ base: join(hub.dir, "database/migrations") }) }); } export async function getDatabaseMigrationFiles(hub) { const fileKeys = await useDatabaseMigrationsStorage(hub).getKeys(); return fileKeys.filter((file) => file.endsWith(".sql")); } export async function copyDatabaseMigrationsToHubDir(hub) { const srcStorage = createStorage({ driver: overlayDriver({ layers: hub.databaseMigrationsDirs.map((dir) => fsDriver({ base: dir, ignore: [".DS_Store"] })) }) }); const destStorage = useDatabaseMigrationsStorage(hub); await destStorage.clear(); const migrationFiles = (await srcStorage.getKeys()).filter((file) => file.endsWith(".sql")); await Promise.all(migrationFiles.map(async (file) => { const sql = await srcStorage.getItem(file); await destStorage.setItem(file, sql); })); } export const CreateDatabaseMigrationsTableQuery = `CREATE TABLE IF NOT EXISTS _hub_migrations ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE, applied_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL );`; export const AppliedDatabaseMigrationsQuery = 'select "id", "name", "applied_at" from "_hub_migrations" order by "_hub_migrations"."id"'; export function useDatabaseQueriesStorage(hub) { return createStorage({ driver: fsDriver({ base: join(hub.dir, "database/queries") }) }); } export async function getDatabaseQueryFiles(hub) { const fileKeys = await useDatabaseQueriesStorage(hub).getKeys(); return fileKeys.filter((file) => file.endsWith(".sql")); } export async function copyDatabaseQueriesToHubDir(hub) { const destStorage = useDatabaseQueriesStorage(hub); await destStorage.clear(); await Promise.all(hub.databaseQueriesPaths.map(async (path) => { try { const filename = path.split("/").pop(); const sql = await readFile(path, "utf-8"); await destStorage.setItem(filename, sql); } catch (error) { console.error(`Failed to read database query file ${path}: ${error.message}`); } })); } export function splitSqlQueries(sqlFileContent) { const queries = []; let inString = false; let stringFence = ""; let result = ""; let currentGeneralWord = ""; let previousGeneralWord = ""; let inTrigger = false; let currentTriggerWord = ""; let triggerBlockNestingLevel = 0; for (let i = 0; i < sqlFileContent.length; i += 1) { const char = sqlFileContent[i]; const nextChar = sqlFileContent[i + 1]; if ((char === "'" || char === '"') && (i === 0 || sqlFileContent[i - 1] !== "\\")) { if (!inString) { inString = true; stringFence = char; } else if (char === stringFence) { inString = false; } } if (!inString) { if (char === "-" && nextChar === "-") { while (i < sqlFileContent.length && sqlFileContent[i] !== "\n") { i += 1; } continue; } if (char === "/" && nextChar === "*") { i += 2; while (i < sqlFileContent.length && !(sqlFileContent[i] === "*" && sqlFileContent[i + 1] === "/")) { i += 1; } i += 2; continue; } if (/\w/.test(char)) { currentGeneralWord += char.toLowerCase(); } else { if (previousGeneralWord === "create" && currentGeneralWord === "trigger") { inTrigger = true; } previousGeneralWord = currentGeneralWord; currentGeneralWord = ""; } if (inTrigger) { if (/\w/.test(char)) { currentTriggerWord += char.toLowerCase(); } else { if (currentTriggerWord === "begin") { triggerBlockNestingLevel++; } else if (currentTriggerWord === "end") { triggerBlockNestingLevel = Math.max(triggerBlockNestingLevel - 1, 0); } currentTriggerWord = ""; } } if (char === ";" && sqlFileContent[i - 1] !== "\\") { if (inTrigger) { if (triggerBlockNestingLevel === 0) { result += char; const trimmedResult = result.trim(); if (trimmedResult !== "") { queries.push(trimmedResult); } result = ""; inTrigger = false; triggerBlockNestingLevel = 0; continue; } else { result += char; } } else { result += char; const trimmedResult = result.trim(); if (trimmedResult !== "") { queries.push(trimmedResult); } result = ""; continue; } } } result += char; } const finalTrimmed = result.trim(); if (finalTrimmed !== "") { queries.push(finalTrimmed); } return queries.map((query) => { if (query.includes("TRIGGER") && query.includes("BEGIN")) { query = query.replace(/;+(?=\s+(?:END|\S|$))/g, ";"); } return query.replace(/;+$/, ";"); }).filter((query) => query !== ";" && query.trim() !== ""); }