JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 53
  • Score
    100M100P100Q93087F
  • License MIT

Migrate anything text-shaped to WordPress — Jekyll, Hugo, Eleventy, Gatsby, Next, Astro, Hexo, Docusaurus, MkDocs, Ghost, WordPress WXR, Medium, Substack, Word docs, spreadsheets, PDFs, EPUBs, READMEs. Driven by GitHub Copilot CLI in a hybrid orchestration model and rendered as an Ink TUI.

Package Exports

    This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (to-wordpress) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

    Readme

    to-wordpress — migrate anything to WordPress

    Migrate anything text-shaped to WordPress — powered by GitHub Copilot CLI.

    Point it at a Jekyll / Hugo / Eleventy / Gatsby / Next / Astro / Hexo / Docusaurus / MkDocs site, a WordPress WXR export, a Medium or Substack export, a folder of Word documents, a spreadsheet of rows, a library of PDFs, an EPUB, a GitHub repo README, a pile of markdown notes, or plain .txt files. Walk away. Come back to a fully working, pixel-close WordPress under wp-env with a custom theme, a site plugin, imported content and media, preserved permalinks (where they existed), and a self-verified build.

    npm downloads license node

    Install · Quick start · How it works · Supported sources · CLI · FAQ


    Why

    Moving a content-heavy static site to WordPress by hand is a week of template translation, shortcode rewrites, data shuffling, and URL remapping. to-wordpress collapses that into one command: a deterministic TypeScript pipeline drives GitHub Copilot CLI (copilot -p) in a hybrid orchestration — the tool owns phase transitions, file I/O, and verification; Copilot handles the creative parts (Liquid → PHP, custom-block generation, edge-case normalization, auto-fix).

    What lands in wp-content/:

    • A bespoke classic WordPress theme with template-parts/… mirroring your source includes one-to-one.
    • A site plugin owning non-theme concerns — CPTs, newsletter, giscus, analytics, cookie banner, redirects, options page.
    • Posts, pages, terms, menus, users, featured images — imported via wp-cli with original permalinks preserved by default.
    • A self-written WORDPRESS_MIGRATION.md that documents every decision so you can audit or replay the run.

    Install

    to-wordpress is meant for one-shot runs, so use npx — no global install needed:

    npx to-wordpress ./path/to/your-site

    Or install globally if you iterate on the same source:

    npm install -g to-wordpress
    to-wordpress ./path/to/your-site

    Requirements

    • Node.js ≥ 20
    • Docker (for wp-env and the Jekyll prerender container)
    • GitHub Copilot CLI: brew install --cask github-copilot-cli then copilot login

    The tool is self-contained otherwise — no Ruby, no PHP, no wp-cli on the host. wp-env runs WordPress in Docker and the tool talks to wp-cli through it.

    Quick start

    # 1. Clone or cd into any static site
    git clone https://github.com/you/your-jekyll-site
    cd your-jekyll-site
    
    # 2. Run the migration (interactive TUI, asks a few URL questions)
    npx to-wordpress .
    
    # 3. Open the result
    open http://localhost:8888

    When it finishes you'll have:

    your-jekyll-site/
    ├── .wp-env.json              # theme + plugin + content mounted into WordPress
    ├── WORDPRESS_MIGRATION.md    # the plan + per-phase status + state block
    └── WORDPRESS_MIGRATION/
        ├── theme/                # your generated classic theme
        ├── plugin/               # your site plugin
        ├── content/              # canonical markdown for every post & page
        ├── media/                # collected image assets
        ├── rendered/             # Jekyll/Hugo/Eleventy build output (ground truth)
        ├── import-manifest.json  # exact payload fed to wp-cli
        ├── redirects.json        # old-path → new-path map
        └── verify-report.json    # url parity + count diff from the last run

    Want non-interactive? Add --yes (uses defaults for every prompt):

    npx to-wordpress ./my-site --yes

    How it works

    flowchart LR
        src[Source site] --> D[Detect]
        D --> P[Plan]
        P --> B[Boot wp-env]
        B --> T[Theme]
        T --> PL[Plugin]
        PL --> N[Normalize]
        N --> I[Import]
        I --> V[Verify]
        V -->|issues| F[Fix]
        F --> V
        V -->|clean| WP["Working WordPress @ localhost:8888"]

    Every phase is implemented in TypeScript with a deterministic fallback, then optionally enriched by copilot -p running in autopilot mode. Copilot streams JSONL events back to the tool's Ink TUI, which renders a live phase dashboard, activity log, and approval prompts.

    # Phase What it does
    1 Detect Probes the source with one detector per SSG. Falls back to a Copilot-driven freestyle detector that reads files and never skips a folder.
    2 Plan Produces WORDPRESS_MIGRATION.md with a template map, a feature-to-plugin map, the exact permalink structure, and acceptance criteria.
    3 Boot wp-env Writes .wp-env.json, mounts your theme + plugin + work dir into WordPress, and runs npx wp-env start.
    4 Theme Builds the source site to capture ground-truth HTML, mirrors _sass/, assets/, _data/, _layouts/, _includes/ into the theme dir, then drives Copilot to emit PHP templates whose DOM byte-matches the reference.
    5 Plugin Generates a site plugin covering CPTs, newsletter, comments (giscus/disqus/commento), analytics (GA/GTM/Plausible/Umami), cookie banner, dark mode, social meta, redirects, shortcodes, custom blocks, and an options page.
    6 Normalize Turns every post/page into canonical markdown with a fixed front-matter schema. Unusual cases are handed to Copilot with a strict edge-case prompt.
    7 Import Runs a generated PHP script via wp eval-file inside the container to upsert posts, terms, menus, users, and media; sets show_on_front; populates the primary menu from _data/menu.yml.
    8 Verify Counts posts per type with wp-cli, fetches a sample of source URLs from the live WP, and diffs titles, H1s, and HTTP status. Writes verify-report.json.
    9 Fix Feeds the verify report back to a scoped Copilot run — minimal surgical edits to theme/plugin/content, idempotent wp-cli calls for data fixes. Re-runs Verify until clean or N iterations.

    Supported sources

    to-wordpress ships detectors for every shape of content we've seen — from real SSGs down to "a folder of PDFs". Each detector emits a detector briefing that steers the theme, plugin, plan, and normalize phases; binary/tabular formats are first routed through a per-format conversion prompt (see src/prompts/convert-*.md) that turns the raw source into canonical markdown before the rest of the pipeline runs.

    Static-site generators

    Source Kind What ships
    Jekyll jekyll Posts (_posts/, _drafts/) + any collections/<x>/, pages, layouts, includes, sass, _data/*, feature-level detection (giscus, mailchimp, analytics, dark mode, OG/Twitter)
    Hugo hugo Content sections → collections/CPTs, layouts/**/*.html, data/**/*, static/
    Eleventy eleventy src/ or content/ posts, njk/liquid/hbs layouts
    Hexo hexo source/_posts/ posts + source/*.md pages, permalink preserved from _config.yml
    Astro astro src/content/<collection>/ collections + src/pages/*.{astro,md,mdx} pages
    Gatsby gatsby src/pages/*.{tsx,jsx} + content/**/*.md(x)
    Next.js next App/Pages router + content/ / posts/ / blog/ markdown

    Documentation frameworks

    Source Kind What ships
    Docusaurus docusaurus docs/ → docs collection, blog/ → posts, admonitions → Gutenberg groups
    MkDocs (+ Material) mkdocs docs/ collection, mkdocs.yml nav mirrored in WordPress menu

    CMS / platform exports

    Source Kind What ships
    WordPress WXR wp-wxr Full .xml dump: posts, pages, CPTs, categories, tags, authors, featured images
    Ghost export ghost-export Ghost JSON dump (posts, tags, users)
    Medium export medium-export posts/<date>_<slug>.html → posts, gists/tweets → Gutenberg embeds, canonical URL preserved
    Substack export substack-export posts.csv + posts/<id>.html → posts, paid-only → private, podcasts → podcast CPT

    Raw documents / text piles

    Source Kind What ships
    Word bundle docx-folder Folder of .docx / .doc / .rtf → one post per document (pandoc/mammoth)
    Spreadsheet xlsx-sheet .xlsx / .xls / .csv / .tsv → one post per row, column → front-matter field
    PDF library pdf-folder Folder of .pdf → one post per document, figures become Gutenberg image blocks
    EPUB books epub-book One .epub → one post per chapter, book metadata → site identity
    Plain text text-folder Folder of .txt / .rst → one post per file (first line = title)
    Markdown pile markdown-folder Obsidian / Notion / Zettelkasten exports, wiki-links preserved
    Plain HTML plain-html Every .html file as a page, every .md file as a post

    Code repos

    Source Kind What ships
    GitHub repo github-repo README → SaaS landing page (hero + feature grid + install CTA), docs/ → docs collection, LICENSE/CHANGELOG/CONTRIBUTING → separate pages

    Fallback

    Source Kind What ships
    Anything else unknown Deterministic file walker + Copilot-driven schema fill — never skips a folder

    Per-format conversion prompts

    Formats that aren't already markdown are routed through a Copilot prompt before normalize runs. Each prompt lives in src/prompts/ and is self-contained:

    Adding a new source type

    ~60 lines of code and one prompt:

    1. Drop a detector file in src/detectors/, implement match() + detect().
    2. Register it in src/detectors/index.ts.
    3. If the source isn't already markdown, declare rawSources[] on the DetectedContext, pick (or add) a RawSourceFormat in src/types.ts, and write a src/prompts/convert-<format>.md.

    CLI

    Usage: to-wordpress [options] [source]
    
    Migrate any codebase to WordPress. Hybrid orchestration with GitHub Copilot CLI.
    
    Arguments:
      source                    path to the source site to migrate (default: ".")
    
    Options:
      -v, --version             print version
      --skip-boot               skip wp-env start (assumes already running)
      --skip-copilot            use deterministic fallbacks only, don't invoke copilot
      -y, --yes                 auto-answer all prompts with defaults
      --only <phase>            run only this phase (advanced)
      --from <phase>            start from this phase (skip earlier ones)
      --until <phase>           stop after this phase (skip later ones)
      --max-fix-iterations <n>  max Verify→Fix iterations (default: 3)
      -h, --help                display help

    <phase> is one of: detect, plan, boot, theme, plugin, normalize, import, verify, fix.

    Re-running just the theme

    Iterating on theme fidelity? The state is persisted inside WORDPRESS_MIGRATION.md, so you can resume any later phase:

    npx to-wordpress ./my-site --yes --from theme --skip-boot

    Non-interactive in CI

    npx to-wordpress ./my-site --yes --skip-copilot --until normalize

    Skipping Copilot means every Copilot-driven step uses the deterministic fallback — you still get detect, plan, normalize, and import, just without the pixel-perfect theme transforms.

    The migration doc

    to-wordpress writes a live, human-readable WORDPRESS_MIGRATION.md inside your source repo. It contains:

    • The phase table with status + timestamps.
    • Overview of the migration and permalink strategy.
    • A template-mapping table (every source layout/include → WP file).
    • A feature-to-output mapping (theme vs plugin vs external plugin).
    • Risks & open questions.
    • Acceptance criteria the Verify phase checks against.
    • A WPIFY:STATE JSON block at the bottom the tool reads back on resume.

    You can commit this file — re-running to-wordpress updates it in place rather than re-planning from scratch.

    Prompts

    Every Copilot-driven phase runs with a rigorously structured prompt in src/prompts/. A shared preamble (_shared.md) gives every phase the same autonomy + fidelity contract; each phase adds:

    • Explicit Scope (which dirs may be written).
    • Explicit Required output (every file that must exist).
    • Non-negotiable rules (e.g. never esc_html(get_the_title()) in a template).
    • Banned anti-patterns (no TODOs, no Lorem ipsum, no hard-coded localhost URLs, no silenced PHP errors).
    • A self-check the model walks before stopping.

    The prompts are intentionally opinionated about WordPress best practices: escaping, text-domain consistency, activation hooks, idempotent wp-cli, options API, rewrite rules.

    FAQ

    Is my data safe? The tool writes everything under WORDPRESS_MIGRATION/ inside your source repo and to a local wp-env Docker volume. No network calls except to the GitHub Copilot API and Docker Hub. Nothing is sent to your live WordPress until you decide to deploy the generated theme/plugin.

    Why wp-env? It pins WordPress + PHP versions, runs wp-cli in-container, and tears down cleanly. You can export the database afterwards with npx wp-env run cli wp db export.

    What about pixel-perfect? The theme phase builds your Jekyll/Hugo site into HTML first (inside a Ruby 3.2 container so host Ruby version doesn't matter), then feeds that ground-truth DOM to Copilot as the exact target. Verify re-fetches your local WP and diffs structure + titles + status — any drift goes back to a scoped Fix loop.

    Can I use a live WordPress instead of wp-env? Not yet — the import uses wp eval-file inside the wp-env cli container. Remote WP-via-REST-API is a planned target.

    Does it migrate comments? Comments stay wherever they live (giscus/disqus/commento). The plugin re-attaches the same integration in WordPress so threads keep working.

    What's the Copilot bill? Expect 3–5 Copilot sessions per full run (plan, theme, plugin, per-post normalize edge cases, fix loop). A ~40-post Jekyll site runs in ~20 minutes end-to-end.

    Development

    git clone https://github.com/f/to-wordpress
    cd to-wordpress
    npm install
    npm run build
    node dist/cli.js ./fixtures/unknown --skip-boot --skip-copilot --yes --until normalize

    Run type checks and build in watch mode:

    npm run typecheck
    npm run dev

    The codebase is:

    PRs welcome — especially new detectors, new Copilot prompts for specific frameworks, and verify rules that catch more drift.

    License

    MIT © Fatih Kadir Akın