@nuxthub/core
Version:
Build full-stack Nuxt applications on Cloudflare, with zero configuration.
160 lines (159 loc) • 5.31 kB
JavaScript
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() !== "");
}