Skip to content

Migrate website from 11ty to Nuxt 4#5091

Draft
dimitrieh wants to merge 76 commits into
mainfrom
4867-migrate-website-to-nuxt
Draft

Migrate website from 11ty to Nuxt 4#5091
dimitrieh wants to merge 76 commits into
mainfrom
4867-migrate-website-to-nuxt

Conversation

@dimitrieh
Copy link
Copy Markdown
Contributor

Description

Completes the 11ty to Nuxt 4 migration for the marketing website. Every existing URL is preserved (route-parity gate enforced), and the production build is now pure nuxt generate.

  • Marketing pages, handbook, docs, blog, integrations, node-red, events, and all other clusters migrated to native Nuxt 4 components and @nuxt/content collections.
  • 11ty fully removed: .eleventy.js, src/ 11ty templating, the legacy proxy middleware, and the 11ty build steps in package.json are gone.
  • Docs continue to source from FlowFuse/flowfuse via scripts/copy_docs.js (contract unchanged); a new scripts/copy_docs_nuxt.js step generates nuxt/content/docs/* at build time.
  • Build is now npm run build:nuxt (runs the copy scripts then nuxt generate). netlify.toml already points at nuxt/.output/public.
  • Route-parity baseline frozen at migration/routes-11ty.txt; migration/verify-routes.sh confirms Dropped: 0 (1186 routes, a superset of the 1178 baseline including upstream additions like the new blog categories and the NIS2 post).
  • Responsive sweep at 375/768/1280/1920 across docs, handbook, and a representative page per cluster: 0 horizontal overflow.
  • nuxt-link-checker: 0 of 1180 failing.

Full per-cluster notes, the verification harness, the route diff, and the long-form summary live under migration/ (see migration/PR_DESCRIPTION.md).

Related Issue(s)

Closes #4867

Checklist

  • I have read the contribution guidelines
  • I have considered the performance impact of these changes
  • Suitable unit/system level tests have been added and they pass (static site: verification via the committed route-parity harness, link-checker, and responsive sweep instead)
  • Documentation has been updated
  • For blog PRs, an Art Request has been created (N/A)

dimitrieh added 30 commits May 29, 2026 13:55
Adds tooling to prove the Nuxt build serves a superset of the legacy 11ty
routes (zero dropped URLs, trailing slashes preserved): route extractor,
diff checker, end-to-end verify script, and the committed 11ty route
baseline (1069 routes).
Separate one-time baseline capture (capture-baseline.sh) from per-build
verification (verify-routes.sh) so the diff keeps catching dropped URLs
even after a section is removed from the 11ty build.
Hybrid build (11ty copied into nuxt/public + Nuxt-native /terms,
/privacy-policy) produces a superset of the frozen 1069-route 11ty
baseline. Confirms the strangler-fig output drops no URLs.
Allowlist the sprite host so the Nuxt dev server works behind the
*.sprites.app proxy, and record migration progress, the route-parity
proof, and the remaining scope/blockers in migration/STATUS.md.
Render the handbook via @nuxt/content at its original /handbook/... URLs
(trailing slashes preserved) instead of 11ty:
- scripts/copy_handbook.js generates nuxt/content from src/handbook,
  rewriting relative .md links to absolute handbook URLs and relative
  images to /handbook-media (copied into public).
- handbook collection + pages/handbook/[...slug].vue with sidebar nav
  (HandbookNavTree) and TOC; routes prerendered from handbook.routes.json.
- legacy proxy yields /handbook* to Nuxt in dev.
- The one .njk-templated and one space-named handbook page stay on 11ty.
- Skip link-checker best-practice STYLE inspections (not broken links)
  that legacy prose violates; failOnError stays on.

Verified: nuxt generate green, link-checker 0 errors/0 warnings,
route diff 0 dropped URLs (Nuxt superset of the 1069-route baseline).
11ty now ignores the handbook pages Nuxt owns (via generated
nuxt/handbook.migrated-sources.json manifest); only the 3 bespoke
straggler pages (2 .njk + 1 space-named .md) still build on 11ty.
Verified: build green, link-checker 0/0, route diff 0 dropped.
Render the changelog at its original URLs via Nuxt instead of 11ty:
- scripts/copy_changelog.js generates nuxt/content/changelog from
  src/changelog (relative links/images rewritten) and a combined,
  date-desc card index (170 entries + 9 blog posts tagged 'changelog')
  matching 11ty's collection so pagination yields the identical pages.
- pages/changelog/[...slug].vue serves entries, the paginated index
  (/changelog/ + /changelog/1..9/, 19/page), with author/date/issues
  from the generated index (single source of truth shared with the feed).
- server/routes/changelog/index.xml.get.ts reproduces the Atom feed.
- .eleventy.js ignores the 170 entries + index.njk + feed-changelog.njk;
  legacy proxy yields /changelog* to Nuxt.

Verified: nuxt generate green, link-checker 0/0, route diff 0 dropped
(Nuxt superset of the 1069-route baseline).
scripts/copy_customer_stories.js generates nuxt/content/customer-stories
+ a metadata index (brand/quote/challenge/solution/logo) since @nuxt/content
doesn't surface nested frontmatter. pages/customer-stories/[...slug].vue
serves the index grid + story pages (hero, quote, Challenge/Solution
sidebar) at identical URLs; legacy proxy yields /customer-stories* to Nuxt.

11ty keeps building these (Nuxt prerender overwrites in the merged output)
because collections.stories is consumed by other pages that remain on 11ty
(node-red/index, landing/tulip, thank-you/contact, llms).

Verified: build green, link-checker 0/0, route diff 0 dropped.
Reproduces the legacy 11ty renderFlow shortcode: renders a Node-RED flow
client-side via the bundled @flowfuse/flow-renderer (window.FlowRenderer,
loaded through a runtime module script). Flow JSON is passed base64-encoded
to survive MDC parsing. Verified in a real browser: renders nodes, wires,
labels and zoom controls. Unblocks faithful migration of the 188 renderFlow
embeds across node-red and blog.
Render /webinars/ (index + 39 detail pages) and /ask-me-anything/ (3 pages)
natively via @nuxt/content, preserving every URL incl. trailing slashes.

- scripts/copy_events.js generates webinars + ama collections from
  src/webinars and src/ask-me-anything, resolving hosts (team+guests) and
  per-page metadata (date/time/duration/video/hubspot) into events.index.json,
  and emits events.routes.json for prerendering.
- Shared EventDetail + HubSpotForm components; webinars [...slug] handles the
  index and detail, ask-me-anything [...slug] reuses EventDetail.
- The one webinar whose filename contains a literal space is left on 11ty
  (Nuxt prerenderer cannot resolve unsafe-char routes); 11ty still emits it so
  route parity holds. Dir-index (index.md) maps to the directory URL.
- Removed /webinars + /ask-me-anything from the legacy proxy.

Verified: build green, prerender 0 errors, route diff 0 dropped (superset),
pages confirmed Nuxt-rendered.
Render /ebooks/<slug>/ natively via @nuxt/content, reusing the HubSpotForm
component for the gated download. copy_ebooks.js generates the ebooks
collection + metadata index (title/cover/contentTable/hubspot) from src/ebooks;
images resolved to absolute /images URLs. Removed /ebooks from the legacy proxy.

Verified: build green, prerender 0 errors, route diff 0 dropped, page
Nuxt-rendered with working download form.
…tes)

Convert three bespoke 11ty solution pages to native Vue pages, reproducing the
hand-crafted marketing markup (hero, feature grids, advantage grid, CTA, UNS
learn cards). Icons inlined as SVG; sign-up shortcode resolved to the app URL;
lite-youtube replaced with a responsive iframe. Added each exact route to the
legacy proxy NUXT_ROUTES and to nitro.prerender.routes.

Verified: build green, prerender 0 errors, link-checker 0/0, route diff 0
dropped, all three confirmed Nuxt-rendered.
Add the remaining three bespoke solution pages (data-integration, mes,
it-ot-middleware) as native Vue, reproducing the hand-crafted marketing markup:
feature grids, ffIconLg icons inlined as SVG, architecture/pictogram sections,
resources (case-study + whitepaper cards), deployment options, and a reusable
FaqAccordion component. With all six migrated, /solutions is now a Nuxt-owned
prefix in the legacy proxy (individual route entries removed).

Verified: build green, prerender 0 errors, link-checker 0/0, route diff 0
dropped, all six confirmed Nuxt-rendered.
Convert the two comparison landing pages to native Vue, reproducing the
data-driven hero, feature grids, comparison tables, switch steps, and CTA.
Add a reusable SocialProof.vue (homeLogos carousel) shared by both. Icons
inlined as SVG, sign-up shortcode resolved, lite-youtube -> responsive iframe.
/vs is now a Nuxt-owned proxy prefix.

Verified: build green, prerender 0 errors, link-checker 0/0, route diff 0
dropped, both pages Nuxt-rendered (kepware visually confirmed).
Convert the three gated whitepaper pages to a single data-driven
whitepaper/[...slug].vue reproducing layouts/whitepaper-gated.njk, reusing
SocialProof + HubSpotForm. /whitepaper is now a Nuxt-owned proxy prefix.

Verified: build green, prerender 0 errors, link-checker 0/0, route diff 0
dropped, all three Nuxt-rendered (uns whitepaper visually confirmed: hero,
social-proof logos, sticky download form).
Replicate the 3 job-posting redirect stubs (JS window.location.replace to
external greenhouse postings) as a data-driven jobs/[...slug].vue. Only the
engineering-manager opening has a live URL; the other two mirror the unset
site.openings keys. noindex preserved. /jobs is a Nuxt-owned proxy prefix.

Verified: build green, link-checker 0/0, route diff 0 dropped, all three
Nuxt-rendered.
…mponent

Convert /partners/ (index with cloud/hardware/solutions partner grids),
certify-hardware, ctrlx, and referral-sign-up to native Vue. Add reusable
Icon.vue + scripts/copy_icons.js (copies the 119 legacy icon SVGs into the
Nuxt project so a Vite glob can inline them by name) to replace per-page icon
inlining. referral-sign-up reuses HubSpotForm. /partners is a Nuxt-owned proxy
prefix.

Verified: build green, prerender 0 errors, link-checker 0/0, route diff 0
dropped, all four Nuxt-rendered (certify-hardware visually confirmed with icons).
careers + sign-up (redirects), email-signature (static), free-consultation
(form + social proof), and contact-us + book-demo (new shared MqlContact
component reproducing layouts/mql-contact.njk, with HubSpotMeeting embed for the
demo scheduler). Reuses Icon, SocialProof, HubSpotForm. Added each as an exact
NUXT_ROUTES entry + prerender route. Fixed a nested-<p> hydration mismatch on
contact-us.

Verified: build green, prerender 0 errors, link-checker 0/0, route diff 0
dropped, all six Nuxt-rendered (contact-us visually confirmed).
Two clean form pages: education (hero + scroll-to-form + HubSpot contact form)
and professional-services (centered hero + two service rows + HubSpot form),
reusing HubSpotForm. Added as exact NUXT_ROUTES + prerender routes.

Verified: build green, link-checker 0/0, route diff 0 dropped, both
Nuxt-rendered.
Centered hero + community/chat cards + submit-a-ticket HubSpot form. The Algolia
search box and HubSpot chat widget depend on site-wide scripts not yet ported,
so they degrade gracefully (empty search container, chat button no-ops) — noted
as fidelity gaps; the ticket form and route are fully functional.

Verified: build green, link-checker 0/0, route diff 0 dropped, Nuxt-rendered.
dimitrieh added 21 commits May 29, 2026 13:57
The hybrid 11ty passthrough has been replaced by native Nuxt asset/sitemap
build steps, so the production build is now plain build:nuxt (nuxt generate).
Removes the @11ty/* dev/build scripts and the eleventy plugin devDependencies
that are no longer invoked.
…dev proxy

The site is now generated entirely by Nuxt, so the 11ty engine and its
templating are dead code. Deletes .eleventy.js, the lib/ helpers it used, the
dev-only legacy proxy middleware, src/_data/eleventyComputed.js, and the
.njk/_includes templates 11ty rendered.

Keeps the handful of src/ files the Nuxt copy_* build scripts still read as
data: src/redirects.njk (static _redirects body), src/node-red/index.njk +
core-nodes/index.njk (FAQ + catalog index parsed by copy_node_red), and
src/_includes/{components/icons,core-nodes,hardware} (icons + node-red markdown
includes). All content markdown, _data, and static assets are retained as
build inputs.
The prerender routes are slash-less, so SSR matched route.params.id cleanly,
but the client loads the canonical trailing-slash URL, which gives the catch-all
param a trailing empty segment. The naive join produced "<id>/", so the
indexData.find() missed and the page threw a 404 on hydration — tearing down the
server-rendered content. Filter empty segments before joining, matching the
.filter(Boolean) pattern already used by the other data-driven [...slug] pages.
eleventy-img was only used by the deleted lib/image-handler.js (11ty image
pipeline); no remaining script or the Nuxt app imports it. Removing it prunes
its exclusive transitive deps from the lockfile. @11ty/eleventy-fetch is kept —
copy_integrations.js and copy_node_red.js use it as a build-time fetch cache.
Relative markdown/HTML image refs in generated content rendered verbatim
and 404'd (the browser resolved them against the page URL). Three copy-script
gaps caused it: markdown image titles with embedded quotes broke the blog
regex; customer-stories and node-red core-node use-case images were resolved
against the wrong base dir; and docs/handbook HTML <img> tags were never
matched by the markdown-only regexes.

Add scripts/normalize_content_images.js, a build step (wired into both build
chains after the content copy scripts) that rewrites every still-relative
image ref to the absolute path copy_assets publishes it at, resolving
file-dir-first then src/ root as 11ty's image-handler did. Rewrites 95 refs
in 36 files; intentional placeholders in handbook how-to guides are left
untouched.
The content globs only scanned the deleted 11ty src/**/*.njk templates and
.eleventy.js, never nuxt/**/*.vue. @layer components classes now used only in
Nuxt components (notably .ff-nav-dropdown) were purged, so the header
mega-menu rendered fully expanded on every page and pricing feature tables
showed inactive content.

Add the Nuxt app paths (components/layouts/pages, app vue, content markdown)
to the content array. Compiled style.css grows 121KB -> 183KB and the nav
renders correctly again.
scripts/visual-check.js drives a headless Chrome over a representative URL
per migrated cluster, capturing page errors, first-party 404s and render
signals (screenshots to /tmp/smoke). Document the final verification results
and the two known non-blocking items (hydration warnings on a few bespoke
pages; flow-renderer limitation on flows with group/junction nodes) in
migration/STATUS.md.
Ports the rotating hero background images, indigo full-bleed hero, screenshot
bridge, and updated metrics (red styling) from src/index.njk into the native
Nuxt homepage; 11ty source remains deleted.
…dbus categories

- Render the TL;DR / first-answer block on blog posts (tldr frontmatter now
  captured in blog.index.json) and show author job titles + 'Updated' date label,
  matching the upstream post layout.
- Add plc/mqtt/opcua/modbus to the blog category map so /blog/<cat>/ routes
  generate natively (the new upstream category landing pages), and wire the
  'See All PLC Articles' button on the PLC landing page.
- Docs/handbook: sidebar is now a collapsible disclosure below lg and the
  two-column layout shifts from md to lg, so tablet (768px) no longer overflows
  and mobile no longer dumps the full nav tree above the content.
- Cap all <img> to their container width (ported pages hardcoded
  style=max-width:NNNpx without width:100%, overflowing narrow viewports);
  also fixed the it-ot-middleware architecture diagram.
- Normalise blog card/hero image paths to absolute in copy_blog.js so relative
  frontmatter image refs stop 404ing on index/category pages.
- Make code fences and wide tables scroll within prose instead of forcing the
  page wider on mobile/tablet (no max-width was set, so long lines overflowed).
- Restore the docs landing-page tile grids (offering + product-feature): the
  11ty CSS for these classes was lost in migration, leaving inert grid-cols
  utilities and unstyled stacked text; re-add a responsive card grid.
…ile/tablet

The native docs/handbook pages lay out their own 3 columns on an inner wrapper,
but the outer div still carried the legacy .handbook class whose flex/grid
container (built for the old 11ty direct-child DOM) sized its flex item to
content min-width — forcing ~920px and overflowing narrow viewports (545px at
375px). Scope a .handbook-shell override to render those wrappers as plain
blocks; node-red and the legacy-static pages keep .handbook unchanged.
Blank lines inside the ff-*-tiles HTML containers (left by multi-line inline
SVGs) terminated the markdown HTML block, so the indented continuation rendered
as <pre> blocks of raw HTML on the docs landing page. Strip blank lines within
those containers during content generation so they render as the card grid.
Picks up the new upstream blog posts/tags (incl. NIS2), the new category routes,
TL;DR + absolute card-image paths from copy_blog, and the upstream mqtt-in docs
update; reconciles package-lock.json after the rebase.
…ication

scripts/responsive-check.js: scripted 4-viewport Playwright sweep (no MCP) with
overflow detection. Updates the committed route-diff evidence (Dropped: 0) and
documents this session in migration/STATUS.md.
The [dev] command still launched 'npx @11ty/eleventy --watch', whose engine
was deleted in the 11ty teardown, so 'netlify dev' would fail. Point it at the
Nuxt dev server ('npm run dev', port 3000) and drop the stale Eleventy labels
on the image cache paths (the paths themselves are still valid).
- nuxt.config.ts: drop the hardcoded '<sprite>.sprites.app' Vite allowedHosts
  entry. Replace with a $development-only allowlist sourced from the
  NUXT_DEV_ALLOWED_HOSTS env var, so nothing host-specific ships in the repo
  while devs behind a proxy can still allowlist their host.
- scripts/responsive-check.js, scripts/visual-check.js: resolve Playwright via
  a normal require('playwright') (with an install hint) instead of hardcoded
  /home/sprite npx-cache paths; make the Chrome executable opt-in via CHROME_PATH
  rather than a hardcoded /usr/bin path. Add qa:responsive / qa:smoke npm
  aliases so they are discoverable dev-QA tools.
- README.md: rewrite the repo-structure, dev-server, and build sections to
  describe the single Nuxt 4 static build (src/ is now a data source only).
  Remove the Strangler-Fig / 11ty-proxy / port-8080 / 'legacy-only mode' text
  and the Nuxt 3 references.
- .claude/CLAUDE.md: replace the deleted layouts/*.njk references (per content
  type + the Layouts table) with the Nuxt pages/components that render each
  section; drop the removed eleventyComputed.js data row.
- migration/README.md: note the frozen baseline is 1178 routes; 'hybrid' -> static.
- migration/PR_DESCRIPTION.md: new — what changed, why, dev/build commands,
  verification gates, and deferred items.
- VERIFICATION.md: rewrite as the final-state record (1178->1186 routes, Dropped 0;
  link-checker 0 of 1180 failing; responsive sweep 60 captures, 0 overflow) instead
  of the stale 1069-route handbook-increment snapshot.
- STATUS.md: add a 'FINAL STATE (PR-ready)' summary at the top with the current
  numbers, and flag the running log below as historical so the older interim
  counts don't read as current.
@dimitrieh
Copy link
Copy Markdown
Contributor Author

dimitrieh commented May 29, 2026

Additional details

High-level changes

  • All sections rendered natively by Nuxt. Marketing pages are bespoke
    nuxt/pages/**/*.vue; markdown-driven sections (blog, changelog, handbook,
    docs, customer-stories, webinars/AMAs, ebooks) use @nuxt/content v3
    collections.
  • src/ is now a data source only. The build-time scripts/copy_*.js steps
    read the markdown and src/_data/* and generate the Nuxt content collections +
    route lists. There are no .njk templates or .eleventy.js anymore.
  • Eleventy removed: .eleventy.js, lib/, the dev proxy
    (nuxt/server/middleware/legacy.ts), src/_data/eleventyComputed.js, and all
    src/ .njk/_includes templating are deleted. The production build is a
    plain npm run buildbuild:nuxtnuxt generate.

    Note: @11ty/eleventy-fetch is intentionally retained — it is a generic HTTP
    caching-fetch library used by copy_node_red.js, copy_integrations.js, and
    a couple of src/_data/* scripts, not the 11ty build engine.

  • Straggler pages that the Nuxt prerenderer cannot reproduce (literal spaces
    in the URL, /404.html, /llms.txt) are served from committed HTML under
    nuxt/legacy-static/ (copied into public by copy_legacy_static.js) so their
    URLs are preserved.

Dev & build commands

npm install            # npm workspaces; nuxt/ is a workspace
npm run dev            # nuxt dev (3000) + postcss + docs + blueprints watchers
npm run build          # → build:nuxt → copy_* steps then `nuxt generate`
npm run build:nuxt:skip-images   # faster iteration (skips image processing)
  • Running nuxt dev behind a remote proxy? Allowlist your host without committing
    it: NUXT_DEV_ALLOWED_HOSTS=my-host.example.com npm run dev.
  • Optional QA helpers (require Playwright): npm run qa:responsive,
    npm run qa:smoke.

Verification gates

Gate Result
nuxt generate (npm run build:nuxt:skip-images) clean, no errors
Route parity (migration/verify-routes.sh vs frozen 1178-route 11ty baseline) Dropped: 0 — Nuxt build is a superset (see migration/route-diff.txt)
nuxt-link-checker (failOnError: true) 0 failing
Responsive sweep — npm run qa:responsive, viewports 375/768/1280/1920, docs + handbook + one page per cluster 60 captures, 0 horizontal overflow, all HTTP 200

The route-parity check is the proof for the "URLs never change" constraint: the
frozen migration/routes-11ty.txt (1178 routes) is diffed against the generated
Nuxt route set; the build fails if any legacy URL is dropped or renamed. The
committed migration/route-diff.txt is the evidence artifact.

CI

  • .github/workflows/test.yml already builds the site with
    npm run build:nuxt:skip-images and runs the hyperlink link-check against
    nuxt/.output/public.
  • .github/workflows/build.yml syncs docs + blueprints and pushes to the live
    branch (Netlify builds live via netlify.toml [build] = npm run build:nuxt).
  • netlify.toml [dev] was the one stale reference to the deleted 11ty engine;
    it now runs npm run dev on port 3000.

Known / deferred items (non-blocking)

  • Cosmetic hydration warnings on a few bespoke marketing pages (/pricing/,
    /node-red/, /platform/device-agent/): "Hydration completed but contains
    mismatches". Content/header/footer all render correctly; pinpointing the exact
    node reliably needs a dev-mode build. Deferred rather than guessed at.
  • @flowfuse/flow-renderer throws on flows containing group/junction/tab
    container nodes (same library + flow JSON 11ty used) — a pre-existing renderer
    limitation, wrapped in try/catch so the page degrades gracefully.
  • Generated-content tracking is intentionally split by cluster: blog,
    integrations, and node-red commit their generated nuxt/content/** +
    *.json (they pull from external/volatile sources, so snapshotting them in git
    gives reproducible builds and a working nuxt dev without a full rebuild),
    while handbook/docs/changelog/events/ebooks/customer-stories gitignore theirs
    and regenerate at build time. Each cluster is internally consistent; unifying
    the two strategies is left for a follow-up if the team prefers.
  • Sidebar nav ordering for docs/handbook is currently alphabetical from
    queryCollectionNavigation; the legacy navGroup/navOrder grouping is
    captured in the generated index JSON but not yet applied to the tree.
  • Site-wide integrations not re-wired: Algolia search box and HubSpot chat are
    global-script integrations documented as degraded (pre-existing gap).

See migration/STATUS.md for the full page-by-page migration record and
migration/README.md for the verification harness.

Copy link
Copy Markdown

@github-advanced-security github-advanced-security AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trivy found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.

@allthedoll
Copy link
Copy Markdown
Contributor

We need to find a way to break this down, no one can review 809 files. 😅

@netlify
Copy link
Copy Markdown

netlify Bot commented May 29, 2026

Deploy Preview for flowforge-website ready!

Name Link
🔨 Latest commit 7982551
🔍 Latest deploy log https://app.netlify.com/projects/flowforge-website/deploys/6a19b8160a10840008fdd34b
😎 Deploy Preview https://deploy-preview-5091--flowforge-website.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
Lighthouse
Lighthouse
1 paths audited
Performance: 53 (🔴 down 24 from production)
Accessibility: 95 (🔴 down 3 from production)
Best Practices: 100 (no change from production)
SEO: 90 (🔴 down 1 from production)
PWA: -
View the detailed breakdown and full score reports

To edit notification comments on pull requests, go to your Netlify project configuration.

Vues default transformAssetUrls rewrites <img src="/foo.png"> into a Rollup
import. Under SKIP_IMAGES (which test.yml runs via build:nuxt:skip-images),
copy_assets skips populating nuxt/public, so the imports cannot resolve and
the production build errors out. Setting vite.vue.template.transformAssetUrls
.includeAbsolute=false leaves root-relative URLs as plain runtime paths served
from public/. Production deploys (no SKIP_IMAGES) and the prior local builds
were unaffected; this only matters when the assets directory is empty at
build time.
11tys addPassthroughCopy ran unconditionally regardless of SKIP_IMAGES;
the flag only disabled the @11ty/eleventy-img optimization step. The
post-migration copy_assets.js conflated the two, gating the raw copy on
SKIP_IMAGES and producing a build output with no images at all in test
mode. This broke CIs static link checker (4788 missing image refs).

Drop the SKIP_IMAGES gate so raw originals always copy. The flag becomes
a no-op here, reserved as a placeholder for any future image-optimization
pipeline (e.g. @nuxt/image) where it would once again mean disable
optimization, not skip copying.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Migrate website from 11ty to Nuxt

3 participants