Skip to content

Latest commit

 

History

2,192 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Static Site Importer

Import a static site or generated website artifact into WordPress pages and an intentional companion block or classic theme.

Try Static Site Importer in WordPress Playground

Static Site Importer is a WordPress plugin. It requires the Blocks Engine PHP transformer Composer package. Production compilation uses ArtifactCompiler::compile(); export still calls blocks_engine_php_transformer_convert_format().

Development packages

Build a production-shaped development ZIP using immutable Blocks Engine source without changing this checkout's Composer files:

npm run build:dev-package -- --blocks-engine-path ../blocks-engine --blocks-engine-ref origin/trunk

The command resolves the requested ref once, archives php-transformer/ and figma-transformer/ from that commit into an isolated temporary snapshot, installs production dependencies there, and delegates ZIP assembly to homeboy review build. The ZIP and adjacent provenance JSON are written to build/. Use --output-dir <path> to select another destination. The receipt records SSI HEAD, a dirty worktree identity when present, the Blocks Engine ref and SHA, Composer lock digest, ZIP digest, and schema version.

Runtime package profiles

runtime-package-manifest.json is the canonical package-composition contract for constrained or embedded WordPress runtimes. Consumers locate the manifest inside the normal plugin release, select a named profile, copy only matching relative paths, and fail if any declared required_files entry is absent. The contract is deployment-neutral: storage, archive transport, scheduling, and runtime infrastructure remain consumer concerns.

website-artifact-import remains the default release profile and provides the full artifact import, validation, WordPress site-plan materialization, and manifest-inspection abilities with all production Composer dependencies, including the Blocks Engine PHP and Figma transformers. html-site-import provides those same abilities for HTML string, file, URL, ZIP, and HTML website-artifact inputs while excluding Figma, optional Markdown conversion dependencies, and SSI's root Composer runtime. Select it for a development package with npm run build:dev-package -- --runtime-profile html-site-import; package consumers select the same name from runtime-package-manifest.json. Markdown and MDX inputs return an explicit unsupported-capability diagnostic in that reduced runtime. The immutable contract is also available from static-site-importer/get-runtime-package-manifest for runtime discovery.

Canonical Site Plans

static-site-importer/materialize-wordpress-site-plan is the generic plan-only boundary for a blocks-engine/wordpress-site-plan/v2 produced by Blocks Engine. SSI calls the package's canonical validator and resolver, then owns WordPress/filesystem preflight, materialization, reconciliation, and the static-site-importer/materialization-receipt/v2 response. Plan, report, classic handoff, and receipt bindings use the producer's blocks-engine/wordpress-site-plan-identity/v1; the materializer keeps its prepared_resolved_projection_hash separate for prepare-to-write TOCTOU detection. It accepts no source HTML or transformer result envelope.

For an isolated runtime matrix, invoke the ability with plan, slug, and optional overwrite, or use:

wp static-site-importer materialize-wordpress-site-plan --plan=/path/to/plan.json --slug=generated-site

Client Script Policy

Every artifact is passed through client_script_policy before Blocks Engine compilation and WordPress materialization. The default is inert: SSI removes executable inline, local, remote, module, telemetry, and data: script markup, removes bundled JavaScript assets, and records each disposition in import_report.client_script_policy. JSON data scripts are quarantined in the report and are not emitted into the generated site.

isolated_preview is the sole preservation opt-in. It requires an explicit client_script_provenance object with a non-empty ref and a runtime isolation assertion. It is intended only for an isolated disposable preview runtime. Preserved scripts remain untrusted_imported_code; artifact carriage, local paths, and source type never establish trust. Current-site REST imports forcibly use inert. Existing include_scripts URL collection callers no longer preserve scripts; callers must request script_policy: isolated_preview, supply provenance, and run only in an isolated preview environment.

Architecture Stack

Static Site Importer is the WordPress materialization layer for static website inputs. It accepts two related shapes:

  • Static source imports: an HTML entry file, pasted HTML document, public HTML URL, bounded public static-site collection, direct HTML upload, or ZIP source tree.
  • Generated website artifacts: a blocks-engine/php-transformer/site-artifact/v1 bundle emitted by website generation or browser runtimes.

The conversion stack is split by responsibility:

  • Static Site Importer owns WordPress intake, safety checks, page/theme creation, asset placement, import reports, quality gates, and intentional block or classic theme materialization.
  • Blocks Engine PHP transformer owns the generic ArtifactCompiler, its diagnostics, and the source_reports.wordpress_site_plan v2 output. SSI materializes that plan into WordPress and returns the receipt and import report.

Existing Companion Block Contracts

Companion refresh accepts new blocks, additive attributes, authoring defaults, styles and implementation updates. Removing a registration or changing/removing an existing attribute schema triggers a saved-usage lookup before files are written. It parses candidate WordPress posts (including revisions, reusable blocks and template posts), then active theme templates and parts. Text mentions and block name prefixes are not treated as actual instances.

If an instance still uses that contract, refresh returns static_site_importer_companion_saved_contract_changed with the block name, changed attributes and a post/template reference. An unused schema may change. An unavailable or capped lookup returns static_site_importer_companion_usage_unverified. overwrite is not a migration of every saved instance, including revisions.

This check targets schema-driven data loss. JavaScript/save-function compatibility, default-value behavior and switching to another companion plugin remain separate concerns. Run tests/companion-persistence.php in a disposable WordPress site for the real multi-request regression and legitimate-refresh proof.

Content-Only Security Boundary

All HTML, folders, ZIPs, URLs, and website artifact objects are untrusted static content. SSI accepts only explicit static asset extensions and rejects server-side source markers before compilation. Compiler-produced companion payloads are independently revalidated before any generated plugin file is written or activated. Companion block renders accept static HTML only; SSI emits its own fixed PHP wrapper to output that markup, so source PHP cannot be preserved or executed. Existing payloads that relied on PHP render templates or PHP companion assets must migrate their behavior to blocks, data bindings, or client-side JavaScript.

When a generated artifact contains full-document HTML, Static Site Importer routes document metadata, head content, styles, scripts, and page body fragments to the right WordPress destinations before calling the conversion stack. A core/html block in imported page content is therefore a materialization/conversion quality issue to fix in this stack, not a product-layer workaround to hide upstream.

What It Does

  • Accepts pasted HTML, one public HTML URL, a direct .html / .htm upload, or a ZIP containing a static-site folder with an index.html shell/chrome entry point.
  • Allows ZIP/CLI source-site imports to include nested .md / .markdown content documents; .mdx is skipped with explicit diagnostics because MDX runtime components are not supported.
  • Provides one WP-CLI importer, wp static-site-importer import, for pasted HTML, website files, ZIP archives, and public URLs through the canonical static-site-importer/import ability.
  • Discovers readable sibling *.html files beside the selected entry file and recursive Markdown content documents under the source tree, then imports them as WordPress pages.
  • Compiles static HTML fragments and Markdown content through Blocks Engine ArtifactCompiler.
  • Stores converted page bodies on the imported WordPress pages as post_content.
  • Generates a block theme with shared header/footer template parts, core/post-content templates, page patterns for reusable/reference artifacts, theme.json, style.css, and optional assets/site.js.
  • Rewrites local .html links to the imported WordPress page permalinks.
  • Creates deterministic wp_navigation posts for supported header/footer navigation and references them from generated template parts.
  • Keeps imported pages native and editor-visible; page content belongs to WordPress pages while the generated theme owns shared chrome, background decoration, styles, scripts, and template wrappers.
  • Optionally activates the generated theme and assigns the imported index.html page as the front page when that page exists.
  • Names the generated theme from the resolved imported site title unless the caller supplies an explicit name.
  • Removes untouched WordPress installation content (Hello world!, Sample Page, and the sample comment) from fresh sites by default.

Requirements

  • WordPress 6.6 or later.
  • PHP 8.2 or later.
  • Composer dependencies installed with composer install.
  • Node dependencies installed only when running the JavaScript block-validation smoke tests.

SSI requires automattic/blocks-engine-php-transformer:^0.4.3. Until the package is published on Packagist, composer.json includes an explicit package repository for the php-transformer-v0.4.3 tag with autoloading rooted at the Blocks Engine monorepo archive's php-transformer/src/ directory. Remove that repository override once Packagist serves the package metadata.

At runtime, SSI loads the transformer package from vendor/ and compiles with new ArtifactCompiler()->compile(). Theme export still calls blocks_engine_php_transformer_convert_format().

Runtime Interfaces

Static Site Importer is an infrastructure plugin. Its supported runtime surfaces are WordPress abilities, WP-CLI commands, REST endpoints, and PHP consumer APIs. It does not register an admin screen or ship an end-user Gutenberg block.

Browser Playground Demo

Open the separate demo interface in a disposable WordPress Playground site:

Try Static Site Importer in WordPress Playground

The release blueprint installs the infrastructure-only Static Site Importer package and a separate demo plugin built from demos/playground-importer/. The demo plugin owns the static-site-importer/importer block and consumes SSI's public REST contract to import into the disposable Playground site. The SSI plugin and its runtime package contain no demo UI or demo registration.

Testers can enter one public URL, upload static site files, choose a folder, upload a ZIP, or paste HTML. Figma upload is enabled only when the Playground runtime provides the optional zstd extension; all other source types remain available without it. Tagged blueprints and demo packages are published through the Playground publication contract, while the README uses the browser-verified playground/latest/blueprint.json alias.

Site Identity and Default Content

The imported site's resolved title is also the generated theme name, so a producer's generic package name does not replace the site identity. Callers can still pass name and slug. Developers can customize the final values with static_site_importer_theme_name and static_site_importer_theme_slug:

add_filter( 'static_site_importer_theme_name', static fn ( string $name ): string => $name . ' Theme' );
add_filter( 'static_site_importer_theme_slug', static fn ( string $slug ): string => 'custom-' . $slug );

Imports remove untouched core seed content on sites where WordPress still reports fresh_site. Records are fingerprinted before page materialization and checked again before deletion, so edited or replaced content is preserved. Set the canonical import argument remove_default_content to false, pass --keep-default-content to WP-CLI import commands, or use the static_site_importer_remove_default_content filter to disable cleanup.

REST Imports

POST /wp-json/static-site-importer/v1/imports normalizes source (url, html, files, archive, or artifact) and executes the canonical static-site-importer/import ability in the installed WordPress runtime. Product-specific interfaces and disposable preview orchestration belong in their owning product layers and consume the same ability contract.

URL intake rules:

  • Every built-in URL import collects the bounded site through the resumable batch engine. It reads the origin's /sitemap.xml, follows same-origin HTML links, and collects directly referenced page assets and nested CSS assets.
  • SSI owns collection, batch, deadline, asset, byte, pacing, and script-policy defaults. Hosts can adjust this policy with the static_site_importer_url_batch_import_args filter.
  • static-site-importer/import accepts { source: { type: "url", url, import_id? }, operation }. The first URL apply returns an opaque import_id; continuation supplies that ID in the same source envelope. SSI resolves the server-owned workspace and validates the URL, import options, and current user; no filesystem path is accepted or returned.
  • URL collection uses the frozen static artifact policy: executable and data scripts are omitted with reason-coded provenance because the server-rendered output does not require them.
  • All source HTML is preserved. On the block-theme path SSI does not classify a subtree as non-authored chrome; deciding what content is in scope belongs to the capture adapter and the Blocks Engine plan. Classic projection still extracts header/footer/background fragments from source HTML.
  • External assets must be directly referenced by fetched HTML or CSS and pass the same public-IP and redirect validation as page URLs.
  • Only http and https URLs are accepted.
  • Localhost, loopback, link-local, private, and otherwise reserved IP targets are rejected before connecting.
  • Address classification is owned by SSI and behaves identically on every supported PHP version. Addresses are compared as packed bytes against an explicit RFC 6890 block table, IPv4-in-IPv6 encodings such as ::ffff:127.0.0.1 are unmapped so the classified address is the address the transport connects to, and shared address space (100.64.0.0/10) is treated as non-public.
  • Redirect targets are revalidated with the same policy and capped.
  • Requests use a timeout and maximum response size, require an HTML-like content type, and do not forward cookies, authorization headers, or embedded URL credentials.
  • Import reports include source URL, final URL, status code, content type, fetch timestamps, response size, and redirect history.

ZIP intake rules:

  • A root-level index.html wins when present.
  • If there is no root-level index.html, the ZIP may contain exactly one nested index.html, such as site-export/index.html.
  • If there are multiple nested index.html files and no root index.html, the import fails so the entry point is not guessed.
  • Archive entries with absolute paths, ../ traversal segments, or server-side executable extensions are rejected before extraction when PHP's ZipArchive inspection is available.
  • .md and .markdown files under the selected source tree are imported as pages. .mdx files are not executed or parsed as Markdown; they are skipped and listed in import-report.json diagnostics.

Generated Store Contract

Static store generators can expose products directly in raw HTML. Product cards using .product-card with a visible heading and price are accepted as commerce context and do not require a separate manifest. Generators may also include an optional products.json file beside the selected entry HTML file. When present, Static Site Importer validates the manifest and records the contract result under commerce.products_manifest in import-report.json.

Minimal schema:

{
  "schema_version": 1,
  "products": [
    {
      "name": "Signal Hoodie",
      "slug": "signal-hoodie",
      "regular_price": "64.00"
    }
  ]
}

Required fields:

  • schema_version: integer 1.
  • products: array of product objects.
  • products[].name: non-empty string.
  • products[].slug: lowercase URL slug using letters, numbers, and hyphens.
  • products[].regular_price: decimal string such as 19.00.

Optional product fields:

  • sale_price: decimal string.
  • description and short_description: strings.
  • categories: array of non-empty category-name strings.
  • image: string path relative to the static site source.
  • status: string product post status metadata.
  • stock_status: string stock status metadata.
  • stock_quantity: integer stock quantity.
  • source_selectors: array of non-empty CSS selector strings for source-product cards.

Invalid manifests do not abort the import. The report marks the manifest invalid and records path-addressed errors such as $.products[0].slug. If raw HTML product cards supply product context, the optional manifest does not add a top-level products_manifest_invalid diagnostic.

WooCommerce Dependency

Commerce-bearing imports require WooCommerce. Commerce intent is detected when any of these signals are present:

  • a valid products.json manifest with at least one product, or
  • caller-supplied commerce_context with at least one product, or
  • inferred commerce context from JSON-LD Product data or visible product cards.

When intent is present and WooCommerce is not active, Static Site Importer first tries to materialize the dependency deterministically by installing and activating WooCommerce from WordPress.org inside the active WordPress runtime. This keeps product support in the WordPress/PHP materializer rather than relying on the generating agent to install plugins by prompt convention.

The dependency materialization result is recorded under plugin_materialization.plugins.woocommerce with the plugin slug, plugin file, source, attempted flag, install/activate actions, status, and any error. If WooCommerce is already loaded, the status is already_available; if the runtime installs and activates it, the status is installed_activated; if installation or activation is unavailable, the status is failed and the normal dependency gate still protects the import.

When WooCommerce remains unavailable after materialization, Static Site Importer hard-fails the import by default. Theme files are still written so the import report and generated artifacts can be inspected. The failure surfaces three ways:

  • commerce.dependencies.woocommerce block on the import report (required, active, waived, sources, product_count, missing_apis).
  • A woocommerce_missing error diagnostic in the report diagnostics[] list.
  • quality.failure_reasons[] contains woocommerce_missing, quality.commerce_dependency_failures is non-zero, and quality.fail_import is set regardless of --fail-on-quality.

Pass --allow-missing-woocommerce (CLI) or 'allow_missing_woocommerce' => true (PHP API) to import the theme without seeding products. The waiver records a woocommerce_waived warning diagnostic and clears the dependency failure. Pass --skip-dependency-materialization (CLI) or 'materialize_dependencies' => false (PHP API) only for tests or hosts that intentionally forbid plugin installation. Non-commerce imports (no manifest, no inferred context) are unaffected: no commerce.dependencies block is recorded and no dependency diagnostics are emitted.

This materializer is intentionally generic: WooCommerce is the first plugin-backed entity path, and the same pattern should be used for bbPress forums/topics, Jetpack-backed features, and other popular WordPress.org plugins. The source artifact declares or implies plugin-backed intent; SSI materializes the plugin in PHP; then a plugin-specific seeder creates native WordPress/plugin entities and records diagnostics.

CLI Usage

The canonical ability uses source.type (html, files, zip, url, or figma) and operation (plan or apply). Figma sources accept a raw figma_file, scenegraph, or artifact bundle and normalize through the Blocks Engine Figma transformer before canonical materialization. Planning returns the canonical WordPress site plan, diagnostics, quality evidence, and source provenance without writing to the destination. Reference-backed sources use opaque source.ref values resolved only by the server-side static_site_importer_resolve_source_reference filter; ability callers never provide filesystem paths. All source HTML is preserved: SSI has no provider or platform axis and offers no option to strip source markup.

static-site-importer import is the canonical host command. Its request file is the exact static-site-importer/import ability input, so every source type and option has one contract across PHP, REST, and WP-CLI. The command owns bounded continuation: it relaunches the same import step in fresh WordPress runtimes, passes SSI's opaque import_id, and prints only the terminal JSON result. A terminal failure prints the same machine-readable envelope and exits nonzero.

wp static-site-importer import --request=/absolute/path/to/import-request.json
{
  "operation": "apply",
  "source": {
    "type": "files",
    "entrypoint": "index.html",
    "files": [
      {
        "path": "index.html",
        "content": "<main><h1>Portable site</h1></main>"
      }
    ]
  },
  "slug": "portable-site",
  "name": "Portable Site",
  "activate": true,
  "overwrite": true
}

The host prints one static-site-importer/import-cli-receipt/v1 object. Use --report=/absolute/path/to/import-report.json for the operator-owned report destination and --max-steps=<count> to bound continuation (default 256). --single-step is the internal fresh-runtime seam; host integrations invoke the command without it. --url= is a minimal ergonomic source argument.

Use --theme-materialization=block|classic; block is the default. Use --operation=plan or "operation": "plan" in the request to emit a plan without writes. Apply a saved response with --plan=/absolute/path/to/plan-response.json. A classic plan response includes a versioned, hashed normalized artifact, projection, and complete normalized arguments bundle. Apply verifies every digest and requires the immutable theme_materialization=classic strategy before running the full classic lifecycle.

wp static-site-importer import --url=https://example.com/ \
  --operation=plan \
  --slug=example-site \
  > url-plan-receipt.json

jq '.response' url-plan-receipt.json > url-plan.json
wp static-site-importer import --plan=/absolute/path/to/url-plan.json

# Commerce-bearing import: put allow_missing_woocommerce in the request JSON.
wp static-site-importer import --request=/absolute/path/to/store-request.json

index.html has special front-page behavior: it becomes the home page slug and, when --activate is used, is assigned as the site's static front page. If the imported directory has no index.html, the pages are still imported, but the importer does not assign page_on_front automatically.

By default, source directories are deleted after a successful clean import so generated upload work directories do not accumulate. Sources are preserved when conversion quality checks report issues. Use --keep-source with CLI imports when you want to keep the original local source directory or fetched URL fixture after a successful clean import for debugging or development. Import reports include a source_documents summary with counts by format, skipped MDX count, unresolved local links, and Markdown parse-error diagnostics.

Generated Theme Shape

An import writes a conventional block theme directory under wp-content/themes/<slug>/:

<slug>/
  style.css
  functions.php
  theme.json
  assets/site.js          # only when the source has inline JS
  parts/header.html
  parts/footer.html
  templates/front-page.html
  templates/index.html
  templates/page.html
  templates/page-<page>.html
  patterns/page-<page>.php

Important behavior:

  • style.css and theme.json are plan-owned writes from Blocks Engine (ThemeJsonProjection and stylesheet assets). SSI materializes those files rather than re-deriving palettes from :root.
  • functions.php enqueues frontend styles, editor styles, and optional generated assets/site.js.
  • Shared chrome is stored in parts/header.html and, when present in the source, parts/footer.html.
  • Generated templates are lightweight block-theme wrappers: header template part, imported background decoration, core/post-content, and optional footer template part.
  • Imported WordPress page posts store the converted page body in post_content, so routing, titles, front-page assignment, editor visibility, and body edits stay native.
  • Page patterns are generated as reusable/reference copies of each converted page body; they are not the primary storage for imported page content.

Website Artifact Export

static-site-importer/export-theme exports an imported or active block theme as a Blocks Engine website artifact. SSI owns the WordPress import/export/materialization path; Blocks Engine PHP transformer owns generic website artifact compilation. Product callers should consume the exported website_artifact object instead of SSI-specific static-site wrappers.

The export envelope includes:

  • schema: "blocks-engine/php-transformer/site-artifact/v1", artifact_type: "website", version, id, generated_at, root, and entrypoint.
  • files[] entries with safe artifact-relative paths, role, kind, mime_type, encoding, bytes, sha256, and inline content.
  • UTF-8 text content by default; binary content is transported as Base64 with encoding: "base64".
  • source/materialization provenance under provenance.
  • import/validation summaries and reports[] references for repair loops.
  • import-report.json and source-documents.json metadata files when the exported theme has SSI import provenance.

The default root is website with entrypoint: "website/index.html". Callers can pass any safe single-segment root with a matching entrypoint, such as root: "artifact" and entrypoint: "artifact/index.html". The import ability accepts the same canonical website artifact through artifact.

Product Handoff Contract

The product handoff contract is defined in docs/product-handoff-contract.md and locked by tests/fixtures/product-handoff-contract/v1.json plus tests/smoke-product-handoff-contract.php.

Owner readiness after materialization uses static-site-importer/owner-handoff-evidence/v1. It binds the canonical plan identity and materialization receipt hashes, composes existing parity/editability/media/provider/metadata/runtime receipts, and renders the same envelope as a user-facing report card. Missing mandatory evidence is an evidence_gap; it is never treated as a pass. accepted_built_allowed is false when the report contains hard failures or evidence gaps. See docs/contracts/owner-handoff-evidence-v1.md.

The handoff path is:

  • product caller sends a blocks-engine/php-transformer/site-artifact/v1 input artifact;
  • Blocks Engine ArtifactCompiler returns blocks-engine/php-transformer/result/v1 with source_reports.wordpress_site_plan using blocks-engine/wordpress-site-plan/v2;
  • SSI consumes that v2 plan, writes WordPress state, and returns a materialization receipt plus static-site-importer/import-report/v1 with import validation and finding packet artifacts;
  • Codebox may validate the WordPress result and return wp-codebox/validation-artifact-envelope/v1 artifact references.

Blocks Engine does not know about Codebox. Products that need sandbox validation request it after SSI materializes WordPress.

Current-runtime validation

static-site-importer/validate-artifact validates a Blocks Engine website artifact in the current WordPress runtime and returns static-site-importer/import-validation-result/v1 importer diagnostics. The ability accepts artifact plus normal import options; it is exposed through the Abilities REST API when that API is available.

The matching CLI command reads an artifact JSON object, imports it with activation, overwrite, and dependency materialization enabled by default, and writes the result to stdout or --output:

wp static-site-importer validate-artifact \
  --artifact=/path/to/website-artifact.json \
  --slug=example-import \
  --name="Example Import" \
  --output=/path/to/validation-result.json

Validation

The repository has both WordPress-side fixture coverage and generated-artifact validation.

Full Validation Harness

Run the full local contract from the repository root:

npm install
npm run test:validation

The harness imports tests/fixtures/wordpress-is-dead/ into the configured WordPress site, then runs the PHP smokes and the JavaScript block-validation smoke in dependency order.

By default it uses:

studio wp --path /Users/chubes/Studio/intelligence-chubes4

Useful overrides:

STATIC_SITE_IMPORTER_SITE_PATH=/path/to/site npm run test:validation
STATIC_SITE_IMPORTER_WP_CLI="wp" npm run test:validation
npm run test:validation -- --skip-import /path/to/wp-content/themes/wordpress-is-dead
npm run test:validation -- --json

Test Inventory

test-manifest.json is the canonical repository-wide test inventory. It classifies every executable test as standalone PHP, WordPress runtime, Node, browser/WP Codebox, or operator-only acceptance. Explicit command values are arrays of executable and argument strings. npm test runs the fast standalone PHP and Node projection; it reports the environment-heavy lanes as skipped. npm run test:all is the complete CI/reviewer command and runs configured runtime lanes while reporting operator-only acceptance commands explicitly. npm run test:inventory verifies that every executable test is declared once and that homeboy-test-manifest.json remains the deterministic standalone-PHP projection used by Homeboy.

PHP Smokes

PHP smokes are declared in test-manifest.json; run the standalone lane with npm test or the complete configured lanes with npm run test:all.

php tests/smoke-wordpress-site-plan-materializer.php runs outside WordPress and verifies that Blocks Engine's direct ArtifactCompiler output is consumed through source_reports.wordpress_site_plan v2 and materialized into the stable receipt contract.

The wordpress-is-dead smoke verifies the multi-page fixture, generated block-theme artifacts, internal-link rewrites, persistent navigation entities, source CSS preservation, editor style support, plan-owned theme.json / style.css, and selector fidelity across stored/rendered paths. The mixed-source-site smoke verifies an Astro-like source tree with index.html, nested Markdown content documents, explicit skipped-MDX diagnostics, report source counts, and generated page block markup.

PHPUnit Fixture Test

tests/StaticSiteImporterFixtureTest.php mirrors the wordpress-is-dead fixture contract in PHPUnit form for the Homeboy WordPress test runner and CI.

homeboy test static-site-importer --path /path/to/static-site-importer

The GitHub workflow runs Extra-Chill/homeboy-action@v2 with the test command across PHP 8.2, 8.3, 8.4, and 8.5.

JavaScript Block Validation

The generated-theme JavaScript smoke runs Gutenberg's parser and block validator against generated theme artifacts:

npm install
npm run test:js-block-validation -- /path/to/wp-content/themes/wordpress-is-dead
npm run test:js-block-validation -- --json /path/to/wp-content/themes/wordpress-is-dead

If no path is passed, the smoke uses STATIC_SITE_IMPORTER_THEME_DIR, then WP_CONTENT_DIR/themes/wordpress-is-dead when WP_CONTENT_DIR is set, then a local wordpress-is-dead directory under the repository root.

It validates parts/header.html, parts/footer.html, patterns/*.php, and templates/*.html, and reports invalid blocks with the file, nested block path, block name, validation reason, and failure summaries grouped by block name and file.

Fixture Matrix Rig

The repo owns the Static Site Importer fixture matrix under bench/, tools/, lib/fixture-matrix/, fixtures/, and rigs/static-site-importer-fixture-matrix/. This is the product-level development gate for importer quality against generated static-site artifacts and the Blocks Engine fixture corpus.

npm run test:fixture-matrix
node bench/static-site-fixture-matrix.bench.mjs \
  --static-site-importer-path . \
  --blocks-engine-php-transformer-path /path/to/blocks-engine/php-transformer \
  --fixture-root /path/to/blocks-engine/fixtures/websites

See docs/fixture-matrix.md for the Homeboy/Lab/WP Codebox workflow, generated-artifact intake, visual parity, editor validation, and Blocks Engine corpus/override usage. SSI keeps only minimal matrix smoke fixtures under tests/fixtures/fixture-matrix; the canonical site corpus lives in Blocks Engine.

Release Workflow

This repo is Homeboy-managed:

  • homeboy.json declares the component ID, WordPress extension, version target in static-site-importer.php, and generated changelog target at docs/CHANGELOG.md.
  • Do not edit docs/CHANGELOG.md manually. Homeboy owns changelog generation from commits at release time.
  • Do not hand-bump plugin versions. Homeboy updates version targets during release.
  • Use conventional commits so release notes and changelog entries are meaningful.

Current Boundaries And Limitations

  • The importer is intentionally static-site/artifact-to-block-theme glue. Blocks Engine PHP transformer owns generic artifact compilation, format conversion, and conversion reports; SSI owns WordPress uploads, import workflows, media, route rewriting, page/product materialization, and theme assembly.
  • Local source imports discover flat sibling *.html files beside the selected entry file and recursive Markdown content documents. Bounded URL collection discovers sitemap and same-origin linked HTML routes but does not execute JavaScript or perform platform-specific API extraction.
  • Admin imports accept pasted HTML, one public URL, a direct .html / .htm file, or a ZIP with a root index.html or exactly one nested index.html; CLI imports take a direct HTML entry path or one public URL.
  • MDX, Astro, Eleventy, Hugo, and other runtime/build orchestration is out of scope. Build those projects to static HTML first, or provide plain .md / .markdown source content alongside the HTML shell.
  • Linked local stylesheets and inline styles are copied into style.css; inline scripts are copied into assets/site.js. Bounded URL collection packages directly referenced HTML/CSS assets, while local source intake does not independently crawl missing assets.
  • Navigation persistence is limited to supported header/footer shapes that can be converted into deterministic wp_navigation entities without guessing.
  • External live triage has exercised additional static sites; committed first-party fixtures include tests/fixtures/wordpress-is-dead/ and tests/fixtures/mixed-source-site/.

Boundary

This plugin owns static-site and website-artifact import workflows plus generated WordPress artifacts. Blocks Engine PHP transformer owns generic artifact compilation and emits source_reports.wordpress_site_plan using blocks-engine/wordpress-site-plan/v2; SSI materializes that plan and records its receipt.

The intended dependency direction is:

Static Site Importer -> Blocks Engine PHP transformer

SSI import reports record blocks_engine.transformer provenance and blocks_engine.wordpress_site_plan; they do not project compiled-site or materialization-plan v1 payloads. Blocks Engine schemas are the active wire contract; SSI should not call lower-level converter packages directly or re-derive semantic page-route intent when the transformer supplies it.

Imported pages remain WordPress pages for routing, titles, front-page assignment, editor visibility, and body content edits. Their imported body layouts live on the page posts as block markup in post_content. The generated block theme owns shared header/footer parts, optional background decoration, frontend/editor styles, scripts, and template wrappers that render page bodies through core/post-content; the generic templates/page.html stays the fallback for pages created after import.

About

Import static HTML sites into WordPress block themes

Resources

Stars

47 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages