Skip to content

feat: R package that emits WordBuilderSwift scripts from R analysis results #88

Description

@kiki830621

Problem

Original text:
「我記得我有一個想要用 swift 腳本來做成 word 的想法,我在想是不是可以寫一個 R 的 package 可以直接從分析結果整理成 swift 的 docx 腳本?」
— Source: Claude Code session 2026-04-19

word-builder-swift 0.9.0 (#71) shipped a fluent Swift API for programmatically building .docx — a Swift-side equivalent of docx.js. R users doing statistical analysis still generate Word reports through older toolchains (officer, flextable, rmarkdown → pandoc) that have their own quirks and limited styling control.

Proposal: an R package that serialises R analysis artefacts (data frames, ggplot2 plots, model summaries, character vectors, lists) into a runnable .swift script that imports WordBuilderSwift and emits a .docx. The R function call shells out to swift run, captures the produced .docx, and returns its path to R.

Type

feature

Motivation

  • Unifies the "analysis in R, formatted report in Swift" pipeline. R stays the analytical engine; Swift owns layout/styling.
  • Keeps word-builder-swift as the single canonical .docx writer across the macdoc ecosystem — R integration becomes "just another caller," not a parallel DOCX generator.
  • The produced .swift file is a first-class artefact — committable, re-runnable, reviewable, diffable against last week's report. Beats "black-box PDF" reproducibility by a wide margin.
  • Forces a clean R ↔ Swift type mapping (data.frame → Table, ggplot → ImageRun when that lands in Phase 2, character heading → Paragraph(heading:), etc.) — exercising word-builder-swift's API surface and surfacing Phase 2/3 needs.

Expected

R user writes:

library(wordBuilder)  # or whatever the package ends up named

doc <- word_document()
doc <- add_heading(doc, "Q1 Report", level = 1)
doc <- add_paragraph(doc, paste("Revenue grew", scales::percent(0.15)))
doc <- add_table(doc, mtcars[1:5, ])
doc <- add_plot(doc, ggplot(mtcars, aes(mpg, hp)) + geom_point())

render(doc, output = "q1-report.docx")
# Internally:
#   1. Emits q1-report.swift (calls WordBuilderSwift)
#   2. Emits any plot as media/{png}
#   3. `swift run --package-path <tempdir> q1-report.swift`
#   4. Returns path to q1-report.docx

The intermediate q1-report.swift is retained (not deleted) so users can:

  • Read / edit it for fine-grained control
  • Check it into git alongside R analysis code
  • Re-run Swift-side without re-running R

Actual

No such package exists. R users wanting .docx output today use:

  • officer + flextable — most common, but styling API is idiosyncratic, Windows-biased, and has long-standing table-inside-body quirks
  • rmarkdown → pandoc — converts markdown to docx, but loses fine control over run-level formatting
  • Manual copy-paste from R → Word (still the most common "pipeline" in practice)

None of these produce a reviewable intermediate artefact like the Swift script approach would.

Impact

Positive:

  • New R-side audience for word-builder-swift (feat: 新增 word-builder-swift — Swift fluent API 直接寫 .docx(類 docx.js) #71) — forces the API to be good enough for scientific/statistical reporting, which often exposes gaps early
  • APA-style reporting becomes easier — R already has great citation/bibliography tooling (bibtex, citr); combined with bib-apa-*-swift the Swift-side emission can consume that
  • Reports become rerunnable single-file artefacts (the .swift) — superior reproducibility story vs. black-box .docx
  • Potentially drives Phase 2 priorities for word-builder-swift (headers/footers, numbering, images — all high-value for scientific reports)

Negative / risk:

  • Platform: Swift toolchain is required on the R user's machine. macOS native; Linux requires swift.org toolchain; Windows is painful. Scope is macOS-first, explicitly.
  • Dependency surface: R package pulls in nothing extra (pure R + shelling to Swift), but runtime needs swift binary on $PATH. Document prominently.
  • Performance: each render() call compiles the generated Swift file (~5–20s for a fresh compile, cached after). Acceptable for "run once per report" workflow; bad for "iterate 50 times" — mitigation: provide a persistent compile cache or a long-lived SPM project under ~/.wordBuilder/.

Scope (Phase 1 MVP)

  • R package (proposed name: wordBuilder, rswiftdocx, or docx.swift — open for bikeshed) — shipped as rwordbuilder at PsychQuant/r-wordbuilder
  • API mirrors word-builder-swift idioms where sensible: word_document(), add_heading(), add_paragraph(), add_text_run(), add_table() — re-scoped to lens-model per 2026-06-01 re-diagnosis: shipped as wb_document() / wb_paragraph() / wb_run_bold() / wb_table() pipeline
  • Code generator: R state → .swift file using WordBuilderSwift — shipped: R/edit_planner.R + R/emit.R + wb_export()
  • [~] Shell-out runner: swift run the generated file, return .docx path — deferred: Phase 1 is emit-only (user runs swift themselves, documented in README + tests/integration/run-smoke.sh); in-R runner is Phase 2
  • [~] Type mapping Phase 1: character → heading/paragraph, data.frame → Table (no styling), list of character → mixed runs — partial: character → paragraph shipped; table/cell constructors accept input but emit try?-wrapped pending ooxml-swift#71 Phase 2c reducer cases
  • Test suite: R-side unit tests (generated .swift matches golden files) + integration test (runs Swift, reads-back .docx via officer to assert content) — shipped: 153 testthat tests incl. golden-file (§6.4) + swiftc -parse roundtrip (§6.6) + end-to-end smoke (§8.4, content asserted via unzip/document.xml instead of officer)
  • [~] Vignette: "Your first report" translating an existing R markdown example — deferred to Phase 2: README Usage section covers the minimum + report examples; full vignette pending RMarkdown/Quarto integration story (out of Phase 1 scope per diagnosis Residue)

Scope (later phases, explicitly out)

  • ggplot2 → image embedding (needs word-builder-swift Phase 2 ImageRun)
  • Table styling / APA format (needs word-builder-swift Phase 2 Styles)
  • Cross-references / citations (needs Phase 2 hyperlinks + bookmarks)
  • Windows support (revisit when Swift/Windows matures)

Open design questions (for diagnosis)

  1. Repo location: PsychQuant/r-word-builder? Separate org? CRAN vs GitHub-only?
  2. Package name: any opinion? Short + searchable matters more than clever.
  3. Runner abstraction: should render() call macdoc's convert subcommand, or swift run directly on the generated file? (Former keeps macdoc as the single binary surface; latter is more direct.)
  4. Intermediate .swift retention: default retain vs default cleanup? Users likely want both modes.
  5. ggplot → image: wait for word-builder-swift Phase 2 ImageRun, or inline image embedding as raw OOXML in Phase 1 R-side? Probably wait — scope creep.
  6. Relationship to existing R → docx packages: compete with officer, or position as "officer for Swift users who want reviewable intermediates"?
  7. Testing strategy: how to assert generated .swift correctness without running Swift in every R CI (Swift toolchain install is slow)? Golden-file comparison + periodic end-to-end runs?

Related

Key Decisions

Scope Changes

Blocking

  • Awaiting /spectra-discuss to lock: R package location, emit target API (direct vs helper-Swift), step-type coverage given Reducer gaps, code-injection security model.
  • PR Propose R wordbuilder MVP #96 review/merge — superseded (closed without merge 2026-05-25).

Commits

  • (none for this issue yet)

Current Status

Phase: closed
Last updated: 2026-06-12 by /idd-close (closing summary: #88 (comment))

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions