102 lines
3.3 KiB
JavaScript
102 lines
3.3 KiB
JavaScript
#!/usr/bin/env node
|
|
import { promises as fs } from "fs";
|
|
import path from "path";
|
|
import { fileURLToPath } from "url";
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = path.dirname(__filename);
|
|
const projectRoot = path.resolve(__dirname, "..");
|
|
const funnelsDir = path.join(projectRoot, "public", "funnels");
|
|
const outputFile = path.join(projectRoot, "src", "lib", "funnel", "bakedFunnels.ts");
|
|
|
|
/**
|
|
* Нормализует данные воронки перед запеканием
|
|
* Удаляет поля которые не соответствуют типам TypeScript
|
|
*/
|
|
function normalizeFunnelData(funnelData) {
|
|
return {
|
|
...funnelData,
|
|
screens: funnelData.screens.map((screen) => {
|
|
const normalizedScreen = { ...screen };
|
|
|
|
// Удаляем variables из экранов, которые не поддерживают это поле
|
|
// variables поддерживается только в info экранах
|
|
if ('variables' in normalizedScreen && normalizedScreen.template !== 'info') {
|
|
delete normalizedScreen.variables;
|
|
}
|
|
|
|
return normalizedScreen;
|
|
}),
|
|
};
|
|
}
|
|
|
|
function formatFunnelRecord(funnels) {
|
|
const entries = Object.entries(funnels)
|
|
.map(([funnelId, definition]) => {
|
|
const serialized = JSON.stringify(definition, null, 2);
|
|
const indented = serialized
|
|
.split("\n")
|
|
.map((line, index) => (index === 0 ? line : ` ${line}`))
|
|
.join("\n");
|
|
return ` "${funnelId}": ${indented}`;
|
|
})
|
|
.join(",\n\n");
|
|
|
|
return `{
|
|
${entries}\n}`;
|
|
}
|
|
|
|
async function bakeFunnels() {
|
|
const dirExists = await fs
|
|
.access(funnelsDir)
|
|
.then(() => true)
|
|
.catch(() => false);
|
|
|
|
if (!dirExists) {
|
|
throw new Error(`Funnels directory not found: ${funnelsDir}`);
|
|
}
|
|
|
|
const files = (await fs.readdir(funnelsDir)).sort((a, b) => a.localeCompare(b));
|
|
const funnels = {};
|
|
|
|
for (const file of files) {
|
|
if (!file.endsWith(".json")) continue;
|
|
|
|
const filePath = path.join(funnelsDir, file);
|
|
const raw = await fs.readFile(filePath, "utf8");
|
|
|
|
let parsed;
|
|
try {
|
|
parsed = JSON.parse(raw);
|
|
} catch (error) {
|
|
throw new Error(`Failed to parse ${file}: ${error.message}`);
|
|
}
|
|
|
|
const funnelId = parsed?.meta?.id ?? parsed?.id ?? file.replace(/\.json$/, "");
|
|
|
|
if (!funnelId || typeof funnelId !== "string") {
|
|
throw new Error(
|
|
`Unable to determine funnel id for '${file}'. Ensure the file contains an 'id' or 'meta.id' field.`
|
|
);
|
|
}
|
|
|
|
// Нормализуем данные перед запеканием
|
|
funnels[funnelId] = normalizeFunnelData(parsed);
|
|
}
|
|
|
|
const headerComment = `/**\n * This file is auto-generated by scripts/bake-funnels.mjs.\n * Do not edit this file manually; update the source JSON files instead.\n */`;
|
|
|
|
const recordLiteral = formatFunnelRecord(funnels);
|
|
const contents = `${headerComment}\n\nimport type { FunnelDefinition } from "./types";\n\nexport const BAKED_FUNNELS: Record<string, FunnelDefinition> = ${recordLiteral};\n`;
|
|
|
|
await fs.mkdir(path.dirname(outputFile), { recursive: true });
|
|
await fs.writeFile(outputFile, contents, "utf8");
|
|
|
|
console.log(`Baked ${Object.keys(funnels).length} funnel(s) into ${path.relative(projectRoot, outputFile)}`);
|
|
}
|
|
|
|
bakeFunnels().catch((error) => {
|
|
console.error(error);
|
|
process.exit(1);
|
|
});
|