Skip to content

Instantly share code, notes, and snippets.

@JosXa
Created February 19, 2025 17:51
Show Gist options
  • Select an option

  • Save JosXa/1849862bc12f2efc38abcc857f11e60c to your computer and use it in GitHub Desktop.

Select an option

Save JosXa/1849862bc12f2efc38abcc857f11e60c to your computer and use it in GitHub Desktop.

Revisions

  1. JosXa created this gist Feb 19, 2025.
    102 changes: 102 additions & 0 deletions migrate-snippets-to-scriptlets.ts
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,102 @@
    import "@johnlindquist/kit"
    import { parseMetadata } from "@johnlindquist/kit"

    metadata = {
    name: "Migrate Snippets to Scriptlets",
    description: "Reads your legacy snippets folder and migrates them to the new markdown-based scriptlets syntax",
    author: "JosXa",
    }

    const snippetsDir = kenvPath("snippets")

    const snippetFiles = await readdir(snippetsDir, { encoding: "utf8", recursive: false })

    const scriptletParts = ["# Migrated Snippets"]

    const skippedFiles = [] as { fileName: string; reason: string }[]
    const filesToDelete = [] as string[]

    for (const fileName of snippetFiles) {
    try {
    if (!fileName.endsWith(".txt")) {
    skippedFiles.push({ fileName, reason: "it is not a .txt file" })
    continue
    }
    const fileContent = await readFile(path.join(snippetsDir, fileName), { encoding: "utf8" })

    const contentRegex = /(?:\/\/.*\r?\n)*([\s\S]+)$/
    const text = fileContent.match(contentRegex)?.[1]?.trim()

    if (!text) {
    skippedFiles.push({ fileName, reason: "no expand text was found" })
    continue
    }

    // @ts-expect-error Typings bug
    const { name, snippet, expand, type: _, ...rest } = parseMetadata(fileContent)

    const expandMetadata = expand ?? snippet

    if (!expandMetadata) {
    skippedFiles.push({ fileName, reason: `no "// Expand" or "// Snippet" metadata was found` })
    continue
    }

    scriptletParts.push(`## ${name}`)

    const metadataStr = [] as string[]
    metadataStr.push(`Expand: ${expandMetadata}`)
    for (const [key, value] of Object.entries(rest)) {
    metadataStr.push(`${titleCase(key)}: ${value}`)
    }

    scriptletParts.push(`<!--\n${metadataStr.join("\n")}\n-->`)

    const sanitizedText = text.replaceAll("~~~", "```")
    scriptletParts.push(`~~~paste\n${sanitizedText}\n\~~~\n`)

    filesToDelete.push(path.join(snippetsDir, fileName))
    } catch (err) {
    skippedFiles.push({ fileName, reason: `an error occurred: ${err}` })
    }
    }

    const finalScriptlet = scriptletParts.join("\n\n")

    const outFile = kenvPath("scriptlets", "migrated-snippets.md")

    if (await pathExists(outFile)) {
    const choice = await select<"Abort" | "Yes">(
    { hint: `File at ${outFile} already exists! Overwrite it?`, strict: true, multiple: false },
    ["[A]bort", "[Y]es"],
    )
    if (choice === "Abort") {
    exit()
    }
    await unlink(outFile)
    }

    await writeFile(outFile, finalScriptlet)
    await edit(outFile)

    await rm(filesToDelete)

    function titleCase(str: string) {
    return str.slice(0, 1).toUpperCase() + str.slice(1)
    }

    if (skippedFiles.length > 0) {
    await div(
    md(
    `Skipped ${skippedFiles.length} files:\n\n${skippedFiles.map((x) => `- <b>${x.fileName}</b> because ${x.reason}`).join("\n")}`,
    ),
    )
    } else if (skippedFiles.length === 0) {
    await div(md("All files migrated successfully, yay!"))

    try {
    await rmdir(snippetsDir, { recursive: true })
    } catch (err) {
    console.error(err)
    }
    }