--- name: fix-paths-for-subpath-hosting description: Make a static site's asset paths relative so it works wherever Simple Host serves it — the root of its own host, or the short-lived // address a new owner's site may have at first. First detects the framework — for any framework with a base-path build setting (Vite, Next.js, CRA, SvelteKit, Astro, Nuxt, Angular, Gatsby, Vue CLI), routes back to the simple-host skill's framework-specific build instructions. Mechanically rewrites root-relative paths to relative paths only for genuinely raw HTML projects with no build system. Use before deploying a plain HTML site, when its assets 404, or when the deploy skill references this as a pre-deploy step. --- # Fix Paths for Subpath Hosting A Simple Host site is served at the root of its own host, `./`, but for a short time after an owner's first site is created it may be at `//`. A root-relative path like `src="/assets/app.js"` 404s at the second; a path with the site name baked in 404s at the first. Relative paths (`assets/app.js`, `./`) work at both. There are two ways to make paths relative, and **picking the right one is more important than how well you execute either**: 1. **For framework projects (Vite, Next, React, Svelte, Astro, Nuxt, Angular, Gatsby, etc.)**: set the framework's base path at build time. The build tool then bakes the correct paths into the output. **Do not mechanically rewrite the build output** — minified variable names shift build-to-build, dynamic-import chunk loaders prepend a configured base to every chunk, and string-replacement is fragile. 2. **For raw HTML/CSS/JS projects with no build step**: mechanically rewrite root-relative paths to relative paths in source. This is the only option for plain HTML, but it is a fallback, not a default. ## Step 1: Detect the framework first Before doing anything else, check for these signals at the project root: | Signal | Framework | Action | |---|---|---| | `package.json` has `vite` (and a `vite.config.*`) | Vite — also covers Vue/React/Svelte/Preact/Lit Vite templates | Use simple-host skill's "Vite" section | | `package.json` has `@slidev/cli` | Slidev | Use simple-host skill's "Slidev" section | | `package.json` has `next` (or `next.config.*`) | Next.js | Use simple-host skill's "Next.js" section | | `package.json` has `react-scripts` | Create React App | Use simple-host skill's "CRA" section | | `package.json` has `@sveltejs/kit` | SvelteKit | Use simple-host skill's "SvelteKit" section | | `package.json` has `astro` (or `astro.config.*`) | Astro | Use simple-host skill's "Astro" section | | `package.json` has `nuxt` (or `nuxt.config.*`) | Nuxt | Use simple-host skill's "Nuxt" section | | `angular.json` exists | Angular | Use simple-host skill's "Angular" section | | `package.json` has `gatsby` | Gatsby | Use simple-host skill's "Gatsby" section | | `package.json` has `@vue/cli-service` | Vue CLI (legacy) | Use simple-host skill's "Vue CLI" section | | Anything else with a `package.json` and a build script | Unrecognized framework | Search that framework's docs for a relative base (`./`) setting ("base", "public path", "relative URLs") — apply it, rebuild, re-upload. If it only supports an absolute base, use `/`, never `//`. **Do not** mechanically rewrite output. | | **No `package.json`, OR `package.json` without a recognized framework dep AND without a build script** | Plain HTML | Continue with the mechanical rewrite below | If a framework is detected, **stop here** and tell the user (or the calling agent) to use the simple-host skill's framework-specific section. That skill has the correct build flag, output directory, and pre-flight checks for every framework above. Trying to mechanically fix paths in a built bundle will silently break dynamic imports, code splitting, and asset loaders even if the static asset references look right at first glance. ## Step 2: Mechanical rewrite — only for plain HTML Use this section ONLY if Step 1 concluded "Plain HTML." Convert root-relative paths to relative paths based on each file's directory depth within the site. ### Depth calculation The **depth** of a file is the number of directory levels from the site root: | File path | Depth | Prefix to use | |-----------|-------|---------------| | `index.html` | 0 | `./` (or just strip the leading `/`) | | `about/index.html` | 1 | `../` | | `tests/easy/index.html` | 2 | `../../` | **Root-level files (depth 0):** Simply remove the leading `/`. - `/css/style.css` becomes `css/style.css` - `/favicon.svg` becomes `favicon.svg` **Subdirectory files (depth 1+):** Prepend `../` for each level of depth. - At depth 1: `/css/style.css` becomes `../css/style.css` - At depth 2: `/css/style.css` becomes `../../css/style.css` ### 1. HTML Files Find all root-relative `src`, `href`, `action`, and `content` attributes. **Root-level file (`index.html`, depth 0):** ```html ``` **Subdirectory file (`about/index.html`, depth 1):** ```html Home Home ``` ### 2. JavaScript Files **CRITICAL:** Different JavaScript APIs resolve paths relative to different base URLs. You MUST identify which resolution rule applies to each path: | API / Context | Resolves relative to | Use depth of | |---------------|---------------------|--------------| | `fetch()`, `new Image()`, `element.href=`, `element.src=`, `navigator.serviceWorker.register()`, `new Worker()` | The **HTML page** that loaded the script | The loading HTML page | | ES module `import()` and static `import` | The **module file** itself | The JS file's own depth | | Service worker `importScripts()`, `caches.match()`, `self.registration` | The **worker file** itself | The worker file's own depth | | Web worker `importScripts()`, `fetch()` inside worker | The **worker file** itself | The worker file's own depth | **This means a SINGLE JS file may need MIXED depth treatments:** ```javascript // assets/app-bundle.js (filesystem depth 1), loaded by index.html (depth 0) // DOM/browser APIs → use PAGE depth (0): var sw = "sw.js"; // NOT "../sw.js" var icon = "favicon.svg"; // NOT "../favicon.svg" navigator.serviceWorker.register(sw, {scope: "./"}); fetch("api/config.json"); new Worker("workers/compute.js"); document.querySelector("link").href = icon; // ES module import() → use MODULE depth (1): const mod = await import("./page-home.js"); // relative to the module itself // or equivalently: import("../assets/page-home.js") ``` **How to determine which rule applies:** Look at what USES the path value, not where the string is declared. A `var url = "..."` might be used by `fetch(url)` (page-relative) or `import(url)` (module-relative) — check the usage. #### Scripts loaded by HTML pages (bundled or not) Any JS file loaded via `