Web part deployment and migration
Better Doctor publishes to the Better Doctor Markdown custom web part instead of the built-in SharePoint Markdown control. Deploy the matching SharePoint Framework (SPFx) solution before publishing pages with this renderer. Installing the Better Doctor npm package does not deploy the SharePoint app.
Deployment requirements
Section titled “Deployment requirements”- A SharePoint Online site and an administrator authorized to deploy apps to its tenant or site collection app catalog.
- The approved Better Doctor CLI and matching
bdoctor-markdown-webpartsolution from the same release. The solution uses SPFx 1.23.2 and Heft, not Gulp. - Node.js 22.14.0 or later within Node 22 when building both projects.
The SPFx child requires
>=22.14.0 <23.0.0; the CLI alone keeps its minimum of 22.13.0. - The existing certificate authentication setup for the CLI. App-catalog deployment is a separate administrative action.
- Page versioning and a reviewed retention policy for recovery. Migration data contains page content and must retain the page’s access restrictions.
SharePoint on-premises and Teams hosts are not supported. This web part is not a SharePoint file browser, remote Markdown loader, PDF exporter, or browser-based repository editor.
Get the solution package
Section titled “Get the solution package”Every full Better Doctor release carries the matching
bdoctor-markdown-webpart.sppkg as a release asset. Download the one attached to
the release of the CLI version you run: the CLI checks the contract version of
the web part it finds, so a package from a different release can be rejected.
Beta releases published under the next npm tag have no attached package. Build
those from source instead.
Build and package from source
Section titled “Build and package from source”Build from source when you run a beta, carry local changes, or would rather not take a prebuilt package.
The CLI and SPFx solution have separate dependency manifests and lockfiles. From the repository root, install each project’s dependencies:
npm cinpm --prefix bdoctor-markdown-webpart ciThen build and package:
npm run webpart:buildnpm run webpart:packagewebpart:build builds the CLI and shared renderer core first, then the child
SPFx project. webpart:package runs that build sequence again before packaging.
If you only need the package, run webpart:package once after installation.
Neither script installs the child’s dependencies or deploys anything.
Inside bdoctor-markdown-webpart, the production commands are:
npm run buildnpm run package-solutionThese invoke heft build --production and
heft package-solution --production, respectively. A standalone child build
requires the root CLI/core build first. The package is
bdoctor-markdown-webpart/sharepoint/solution/bdoctor-markdown-webpart.sppkg.
The package must include its client-side assets, including lazy-loaded chunks, Monaco workers, and KaTeX fonts/styles. Inspect the production artifact and verify loading from the deployed asset location; localhost or a public dependency CDN is not an acceptable production dependency.
Deploy the approved solution
Section titled “Deploy the approved solution”- Record the CLI version, solution version, package checksum, and source revision for the pilot.
- Upload the
.sppkgto the approved app catalog and review SharePoint’s deployment prompt. Approve only the intended deployment scope. - Make the app available to the target site using your catalog’s deployment model; install it on the site when required.
- Verify that Better Doctor Markdown is available on that site. Its component ID
is
26635cd4-b679-4f5e-8681-7390ff443616; a matching title alone is not proof of identity or compatibility. - Test the deployed page in read and edit modes before migrating production content. Verify chunks, fonts, workers, and browser network/CSP errors.
See Microsoft’s SPFx packaging and deployment guide for the app-catalog process.
Better Doctor checks renderer availability before cleanup, asset uploads, multilingual
configuration, or page mutation in a page-publishing run. Missing or incompatible
registration stops that run; there is no automatic deployment or silent fallback
to the built-in Markdown control. --skipPrecheck does not disable this guard.
Runs using --skipPages do not perform a renderer migration.
Prepare existing pages and templates
Section titled “Prepare existing pages and templates”Keep the repository as the authoritative content source. Inventory the pages
and templates that Better Doctor manages, the configured webPartTitle (default:
bdoctor-placeholder), and their current control placement.
Legacy migration selects the built-in Markdown component
1ef5ed11-ce7b-44be-bc5e-4abd55101d16 and the configured title. New controls
use the Better Doctor component ID and managed page identity. A title-only match is
not sufficient. Duplicate or ambiguous candidates must be resolved before
continuing; do not rename unrelated controls just to force a match.
Templates may contain a Better Doctor control or an explicitly selected legacy placeholder. Confirmed replacement is intended to preserve the selected control’s section, column, and order and leave other controls untouched. Unrecognized or unproven layouts must not be migrated by guessing placement. In particular, automatic legacy migration in a vertical section is blocked before migration writes until tenant validation supports that layout. Include your actual templates and layouts in the pilot.
A legacy migration must start from a page with a saved version and no pre-existing checkout. Resolve an existing checkout with its owner before starting; do not discard another editor’s draft to clear the prerequisite.
Run a read-only comparison first:
bdoctor statusThe render fingerprint includes source, resolved partials, relevant rendering
options, shortcode implementations, and renderer contract changes. Old state
can therefore report Modified even when Markdown text is unchanged.
status is not a live browser test or a substitute for migration preflight.
Serialize publishing
Section titled “Serialize publishing”Run only one publisher at a time for each target site, across pipelines, scheduled jobs, and local CLI runs. Queue later jobs rather than cancelling a run midway through migration. Coordinate with page editors so they do not edit the affected pages during the operation.
Migration acquires a page checkout and compares the canvas before staging the
replacement. It verifies the persisted identity/payload and compares the canvas
again before removing the legacy control from the draft. These safeguards are
not an atomic compare-and-swap: the available SharePoint commands use
If-Match: *, not a conditional write tied to the inspected version.
Concurrent writers using the same principal remain a risk, even with a
checkout. Do not treat a shared service account, --confirm, or retries as a
lock. Enforce serialization outside Better Doctor and exercise change detection,
checkout failure, and recovery in the tenant pilot.
Confirm a migration
Section titled “Confirm a migration”On the approved pilot site, with the usual credentials configured:
bdoctor publish --confirmReplacing a legacy control requires --confirm, even without cleanup flags.
--forceAll reprocesses content but does not authorize replacement.
--skipExistingPages leaves existing pages unmigrated.
Do not add --cleanStart, --cleanEnd, or --removeDeleted to a migration
trial: those authorize separate destructive actions. In particular, cleanup
is not a shortcut for in-place migration or its recovery safeguards.
The replacement stages a new custom control with the original control’s
recovery data in properties.migrationBackup. That backup stays on the
same page under the same page access controls; it is not written to the asset
library or an unrestricted file. Native SharePoint page versions provide a
second recovery source.
The backup contains:
version: 1: the recovery-data format.phase:stagedorcomplete.originalVersion: the saved page version before migration.legacyControl: the complete original legacy control, including its identity, properties, and placement.
This backup persists across later updates and is ignored by the renderer. A staged migration uses it to resume rather than treating the staged control as a fresh replacement.
The staged control must be re-read and its identity and payload verified before the selected legacy instance is removed from the draft. Final publish state must only record successful publishing. Multiple SharePoint calls are not an atomic transaction: an interruption may leave a staged control or a checkout requiring inspection and recovery. Do not assume a retry or rollback is tenant-proven until the pilot has exercised those failure paths.
Recover from an interrupted migration
Section titled “Recover from an interrupted migration”- Pause all publishers for the target site and coordinate with its editors.
- Inspect the page’s checkout status and owner, draft, current controls, and
version history. Preserve any staged
Better Doctor control containing
properties.migrationBackup; do not remove your recovery source before reviewing it. - Compare the original control, recorded placement, staged content, and current page edits. Resolve ambiguity or concurrent changes before retrying.
- If a failed run left a checkout, have its owner or an authorized administrator review the draft and check in the reviewed recovery state through SharePoint or your approved tenant procedure. Preserve the staged control and backup when resuming migration. Do not publish an unverified draft or assume check-in proves the migration succeeded.
- If reverting, have an authorized operator restore the affected control from the protected backup, or restore a reviewed native page version. A whole-page restore can discard unrelated edits made after that version.
- Confirm there is no remaining checkout and verify the intended page/recovery
state. Reconcile publishing state before resuming one serialized publisher;
a retained
stagedbackup enables the migration to resume. Retain the prior CLI artifact and approved solution package with the operational release record.
Do not blindly discard a checkout to make a retry pass. Discarding can remove the staged custom control, its backup, and unrelated draft edits. Only consider it after an authorized review confirms that the saved version provides the required recovery and no needed draft work will be lost.
There is no automatic rollback command or browser restore UI. Do not move page backups into the normal asset library, logs, or public build artifacts. Downgrading the CLI does not convert existing custom controls back to built-in ones. Keep the custom solution deployed while pages still reference it.
Pilot acceptance checklist
Section titled “Pilot acceptance checklist”These checks remain rollout gates, not claims of completed validation:
- Publish new pages and update existing pages with the deployed solution.
- Confirm that missing deployment or missing migration confirmation prevents the relevant mutations, including cleanup in a page-publishing run.
- Migrate template and non-template pages at their actual placements; preserve headers, metadata, navigation, and neighboring controls.
- Reject ambiguous identity, pre-existing checkout, missing saved version, and unsupported vertical-section migration before migration writes.
- Verify serialization across all publishing entry points, including jobs using the same principal. Exercise canvas-change detection without claiming atomic concurrency protection.
- Interrupt each migration stage, inspect/check in any retained checkout, and resume from a staged backup without losing content or creating duplicates.
- Restore a page from protected backup/version history, including a case with unrelated edits that must not be overwritten.
- Publish human
.lang.mdtranslations and generated translated HTML after their source pages; verify code, diagram, and TeX source preservation. - Verify exact code copy and clipboard-denial behavior, malformed Mermaid/math handling, and currency/code remaining literal when math is disabled.
- Test two web part instances, section themes, dark/light code themes, keyboard access, forced colors, narrow columns, and right-to-left content.
- Verify Monaco is absent in read mode, workers load in edit mode, and preview changes never persist to Better Doctor-managed content or repository files.
- Confirm all production assets load from approved origins under tenant CSP, then record the results and approve rollout separately.
Source ownership and compatibility
Section titled “Source ownership and compatibility”Monaco offers temporary preview and export in SharePoint edit mode. Prepared Markdown can contain expanded partials and rewritten links, so its export is not a reconstruction of the original repository files. HTML compatibility payloads, including machine-translated HTML, support read-only inspection and export rather than conversion back to editable Markdown.
Copy reviewed changes into the repository and run Better Doctor again to publish them.
Local include files and custom JavaScript shortcodes run in the CLI, not in the
browser. Sanitization removes active scripts, unsafe URLs, event handlers, and
arbitrary author styles; markdown.allowHtml is not permission to execute code.
Review custom shortcode output during the pilot.