Import a static site or generated website artifact into WordPress pages and an intentional companion block or classic theme.
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().
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/trunkThe 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-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.
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-siteEvery 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.
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/v1bundle 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 thesource_reports.wordpress_site_planv2 output. SSI materializes that plan into WordPress and returns the receipt and import report.
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.
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.
- Accepts pasted HTML, one public HTML URL, a direct
.html/.htmupload, or a ZIP containing a static-site folder with anindex.htmlshell/chrome entry point. - Allows ZIP/CLI source-site imports to include nested
.md/.markdowncontent documents;.mdxis 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 canonicalstatic-site-importer/importability. - Discovers readable sibling
*.htmlfiles 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-contenttemplates, page patterns for reusable/reference artifacts,theme.json,style.css, and optionalassets/site.js. - Rewrites local
.htmllinks to the imported WordPress page permalinks. - Creates deterministic
wp_navigationposts 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.htmlpage 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.
- 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().
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.
Open the separate demo interface in a disposable WordPress Playground site:
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.
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.
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_argsfilter. static-site-importer/importaccepts{ source: { type: "url", url, import_id? }, operation }. The first URL apply returns an opaqueimport_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
httpandhttpsURLs 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.1are 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.htmlwins when present. - If there is no root-level
index.html, the ZIP may contain exactly one nestedindex.html, such assite-export/index.html. - If there are multiple nested
index.htmlfiles and no rootindex.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'sZipArchiveinspection is available. .mdand.markdownfiles under the selected source tree are imported as pages..mdxfiles are not executed or parsed as Markdown; they are skipped and listed inimport-report.jsondiagnostics.
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: integer1.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 as19.00.
Optional product fields:
sale_price: decimal string.descriptionandshort_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.
Commerce-bearing imports require WooCommerce. Commerce intent is detected when any of these signals are present:
- a valid
products.jsonmanifest with at least one product, or - caller-supplied
commerce_contextwith at least one product, or - inferred commerce context from JSON-LD
Productdata 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.woocommerceblock on the import report (required,active,waived,sources,product_count,missing_apis).- A
woocommerce_missingerror diagnostic in the reportdiagnostics[]list. quality.failure_reasons[]containswoocommerce_missing,quality.commerce_dependency_failuresis non-zero, andquality.fail_importis 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.
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.jsonindex.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.
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.cssandtheme.jsonare plan-owned writes from Blocks Engine (ThemeJsonProjectionand stylesheet assets). SSI materializes those files rather than re-deriving palettes from:root.functions.phpenqueues frontend styles, editor styles, and optional generatedassets/site.js.- Shared chrome is stored in
parts/header.htmland, 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.
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, andentrypoint.files[]entries with safe artifact-relative paths,role,kind,mime_type,encoding,bytes,sha256, and inlinecontent.- 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.jsonandsource-documents.jsonmetadata 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.
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/v1input artifact; - Blocks Engine
ArtifactCompilerreturnsblocks-engine/php-transformer/result/v1withsource_reports.wordpress_site_planusingblocks-engine/wordpress-site-plan/v2; - SSI consumes that v2 plan, writes WordPress state, and returns a materialization receipt plus
static-site-importer/import-report/v1with import validation and finding packet artifacts; - Codebox may validate the WordPress result and return
wp-codebox/validation-artifact-envelope/v1artifact references.
Blocks Engine does not know about Codebox. Products that need sandbox validation request it after SSI materializes WordPress.
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.jsonThe repository has both WordPress-side fixture coverage and generated-artifact validation.
Run the full local contract from the repository root:
npm install
npm run test:validationThe 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 -- --jsontest-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 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.
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-importerThe GitHub workflow runs Extra-Chill/homeboy-action@v2 with the test command across PHP 8.2, 8.3, 8.4, and 8.5.
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-deadIf 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.
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/websitesSee 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.
This repo is Homeboy-managed:
homeboy.jsondeclares the component ID, WordPress extension, version target instatic-site-importer.php, and generated changelog target atdocs/CHANGELOG.md.- Do not edit
docs/CHANGELOG.mdmanually. 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.
- 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
*.htmlfiles 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/.htmfile, or a ZIP with a rootindex.htmlor exactly one nestedindex.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/.markdownsource content alongside the HTML shell. - Linked local stylesheets and inline styles are copied into
style.css; inline scripts are copied intoassets/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_navigationentities without guessing. - External live triage has exercised additional static sites; committed first-party fixtures include
tests/fixtures/wordpress-is-dead/andtests/fixtures/mixed-source-site/.
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.