// Name: Split JSON Array into Files // Description: Splits a JSON array file into individual JSON files saved in a subfolder. // Author: dallascrilley // GitHub: dallascrilley // Shortcut: cmd shift j import "@johnlindquist/kit" // Utility types for JSON type JsonValue = string | number | boolean | null | JsonObject | JsonValue[] type JsonObject = { [key: string]: JsonValue } // Kebab-case conversion for filenames function kebabCase(input: string): string { const ascii = input .normalize("NFKD") .replace(/[\u0300-\u036f]/g, "") // remove diacritics return ascii .toLowerCase() .replace(/['’"]/g, "") .replace(/[^a-z0-9]+/g, "-") .replace(/^-+|-+$/g, "") .trim() } // Ensure unique filename by appending -2, -3, ... async function uniqueFileName( dir: string, base: string, used: Set<string> ): Promise<string> { let name = `${base}.json` let counter = 2 // Avoid collisions with previously written files in the same run and existing files on disk // Check both the set and the filesystem while (used.has(name) || (await pathExists(path.join(dir, name)))) { name = `${base}-${counter}.json` counter++ } used.add(name) return name } try { // Prompt: select a .json file const inputPath = await selectFile("Select a JSON array file") if (!inputPath) { await notify("No file selected. Exiting.") exit() } if (!inputPath.toLowerCase().endsWith(".json")) { await div( md( `# Invalid File Please select a file with the .json extension. Selected: ${path.basename(inputPath)}` ) ) exit(1) } // Read file contents let raw: string try { raw = await readFile(inputPath, "utf8") } catch (err: any) { await div( md( `# Read Error Failed to read the file. - Path: \`${inputPath}\` - Error: \`${String(err?.message || err)}\`` ) ) exit(1) } // Parse and validate JSON let parsed: unknown try { parsed = JSON.parse(raw) } catch (err: any) { await div( md( `# Invalid JSON The selected file is not valid JSON. - Path: \`${inputPath}\` - Error: \`${String(err?.message || err)}\`` ) ) exit(1) } if (!Array.isArray(parsed)) { await div( md( `# Invalid Format The JSON must be an array of objects.` ) ) exit(1) } const items = parsed as unknown[] if (items.length === 0) { await div( md( `# Empty Array The JSON array is empty. Nothing to split.` ) ) exit() } // Prepare output directory const { dir, name: baseName } = path.parse(inputPath) const outDir = path.join(dir, `${baseName}-split`) try { await ensureDir(outDir) } catch (err: any) { await div( md( `# Write Error Failed to create output directory. - Directory: \`${outDir}\` - Error: \`${String(err?.message || err)}\`` ) ) exit(1) } // Split and write files const createdFiles: string[] = [] const errors: string[] = [] const usedNames = new Set<string>() await setProgress?.(0, items.length) for (let i = 0; i < items.length; i++) { const item = items[i] // Validate object shape if (typeof item !== "object" || item === null || Array.isArray(item)) { errors.push(`Index ${i}: Item is not a JSON object.`) continue } const obj = item as JsonObject const rawName = typeof obj["name"] === "string" && obj["name"].trim().length > 0 ? (obj["name"] as string).trim() : `item-${i + 1}` const slug = kebabCase(rawName) || `item-${i + 1}` const fileName = await uniqueFileName(outDir, slug, usedNames) const outPath = path.join(outDir, fileName) try { await writeFile(outPath, JSON.stringify(obj, null, 2), "utf8") createdFiles.push(fileName) } catch (err: any) { errors.push(`Index ${i} (${fileName}): ${String(err?.message || err)}`) } await setProgress?.(i + 1, items.length) } // Feedback summary const summaryLines = [ `# Split Complete`, ``, `- Input File: \`${path.basename(inputPath)}\``, `- Output Directory: \`${outDir}\``, `- Files Created: **${createdFiles.length}**`, ] if (createdFiles.length > 0) { summaryLines.push( ``, `## Created Files`, ...createdFiles.map(f => `- \`${f}\``) ) } if (errors.length > 0) { summaryLines.push( ``, `## Errors (${errors.length})`, ...errors.map(e => `- ${e}`) ) } await notify(`Created ${createdFiles.length} file${createdFiles.length === 1 ? "" : "s"}`) await div(md(summaryLines.join("\n"))) } catch (err: any) { await div( md( `# Unexpected Error An unexpected error occurred. - Error: \`${String(err?.message || err)}\`` ) ) exit(1) }