{"version":3,"file":"tempest-pwa-manifest.cjs","names":[],"sources":["../../src/vite/tempest-pwa-manifest.ts"],"sourcesContent":["import type { Plugin } from \"vite\";\nimport { basePrefix } from \"./base-url\";\n\n/**\n * A Vite plugin object. Typed loosely so the SDK's published declarations stay\n * free of `vite`'s internal types (which the `.d.ts` rollup can't analyze);\n * assign the result straight into a `plugins: [...]` array.\n */\nexport type TempestVitePlugin = { name: string } & Record<string, unknown>;\n\n/** Options for {@link tempestPwaManifest}. */\nexport interface TempestPwaManifestOptions {\n    /** Output file name (under the build root). Default `precache-manifest.json`. */\n    fileName?: string;\n    /**\n     * Extra URLs to precache that Vite doesn't emit into the bundle — typically\n     * `public/` assets like the web manifest and icons. Default `[]`.\n     *\n     * Write these relative to the site root (`/icon.svg`); the resolved Vite\n     * `base` is prefixed for you. An entry that already carries the base is\n     * left alone, so spelling it out by hand is still correct.\n     */\n    additionalUrls?: string[];\n    /** Emitted files matching this are skipped. Default `/\\.map$/` (source maps). */\n    exclude?: RegExp;\n    /** Include emitted `.html` documents (the app shell). Default `true`. */\n    includeHtml?: boolean;\n    /**\n     * App-shell document always added to the manifest, even if Vite emits it\n     * after this plugin runs. Must match `installPrecache`'s `navigateFallback`\n     * so offline navigations resolve. Pass `false` to disable. Default `/index.html`.\n     *\n     * Like {@link TempestPwaManifestOptions.additionalUrls}, this is prefixed\n     * with the resolved Vite `base`.\n     */\n    appShell?: string | false;\n}\n\nfunction joinBase(base: string, file: string): string {\n    return `${basePrefix(base)}${file}`.replace(/([^:]\\/)\\/+/g, \"$1\");\n}\n\n/**\n * Prefix a caller-supplied, root-absolute URL with the resolved Vite `base`.\n *\n * Idempotent: a URL that already starts with the base is returned untouched,\n * so configs written before this was applied — spelling the base out by hand —\n * keep producing the same manifest instead of gaining a doubled prefix.\n */\nfunction withBase(base: string, url: string): string {\n    const prefix = basePrefix(base);\n    if (prefix === \"/\") return url.startsWith(\"/\") ? url : `/${url}`;\n    if (url.startsWith(prefix)) return url;\n    return `${prefix}${url.replace(/^\\//, \"\")}`;\n}\n\n/** Deterministic djb2 hash → hex. Stable across rebuilds with the same assets. */\nfunction hash(input: string): string {\n    let h = 5381;\n    for (let i = 0; i < input.length; i++) {\n        h = ((h << 5) + h + input.charCodeAt(i)) >>> 0;\n    }\n    return h.toString(16);\n}\n\n/**\n * Vite build plugin that emits a `precache-manifest.json` listing every built\n * asset (plus any `additionalUrls`) as root-absolute URLs, with a content-based\n * `version`. It is the dependency-free counterpart to Workbox's `__WB_MANIFEST`:\n * `installPrecache` (from `tempest-react-sdk/sw`) reads this file at the service\n * worker's `install` event to cache the app shell for offline use.\n *\n * @example\n * // vite.config.ts\n * import { createViteConfig, tempestPwaManifest } from \"tempest-react-sdk/vite\";\n *\n * export default createViteConfig({\n *   plugins: [tempestPwaManifest({ additionalUrls: [\"/manifest.webmanifest\", \"/icon.svg\"] })],\n * });\n */\nexport function tempestPwaManifest(options: TempestPwaManifestOptions = {}): TempestVitePlugin {\n    const {\n        fileName = \"precache-manifest.json\",\n        additionalUrls = [],\n        exclude = /\\.map$/,\n        includeHtml = true,\n        appShell = \"/index.html\",\n    } = options;\n\n    let base = \"/\";\n\n    const plugin: Plugin = {\n        name: \"tempest-pwa-manifest\",\n        apply: \"build\",\n        configResolved(config) {\n            base = config.base ?? \"/\";\n        },\n        generateBundle(_outputOptions, bundle) {\n            const urls = new Set<string>(additionalUrls.map((url) => withBase(base, url)));\n            // Vite may emit index.html after this hook, so guarantee the shell.\n            if (appShell) urls.add(withBase(base, appShell));\n            for (const file of Object.keys(bundle)) {\n                if (file === fileName) continue;\n                if (exclude.test(file)) continue;\n                if (!includeHtml && file.endsWith(\".html\")) continue;\n                urls.add(joinBase(base, file));\n            }\n\n            const list = [...urls].sort();\n            const version = hash(list.join(\"\\n\"));\n            this.emitFile({\n                type: \"asset\",\n                fileName,\n                source: JSON.stringify({ version, urls: list }),\n            });\n        },\n    };\n\n    return plugin as TempestVitePlugin;\n}\n"],"mappings":"kCAsCA,SAAS,EAAS,EAAc,EAAsB,CAClD,MAAO,GAAG,EAAA,WAAW,CAAI,IAAI,IAAO,QAAQ,eAAgB,IAAI,CACpE,CASA,SAAS,EAAS,EAAc,EAAqB,CACjD,IAAM,EAAS,EAAA,WAAW,CAAI,EAG9B,OAFI,IAAW,IAAY,EAAI,WAAW,GAAG,EAAI,EAAM,IAAI,IACvD,EAAI,WAAW,CAAM,EAAU,EAC5B,GAAG,IAAS,EAAI,QAAQ,MAAO,EAAE,GAC5C,CAGA,SAAS,EAAK,EAAuB,CACjC,IAAI,EAAI,KACR,IAAK,IAAI,EAAI,EAAG,EAAI,EAAM,OAAQ,IAC9B,GAAM,GAAK,GAAK,EAAI,EAAM,WAAW,CAAC,IAAO,EAEjD,OAAO,EAAE,SAAS,EAAE,CACxB,CAiBA,SAAgB,EAAmB,EAAqC,CAAC,EAAsB,CAC3F,GAAM,CACF,WAAW,yBACX,iBAAiB,CAAC,EAClB,UAAU,SACV,cAAc,GACd,WAAW,eACX,EAEA,EAAO,IA6BX,MAAO,CA1BH,KAAM,uBACN,MAAO,QACP,eAAe,EAAQ,CACnB,EAAO,EAAO,MAAQ,GAC1B,EACA,eAAe,EAAgB,EAAQ,CACnC,IAAM,EAAO,IAAI,IAAY,EAAe,IAAK,GAAQ,EAAS,EAAM,CAAG,CAAC,CAAC,EAEzE,GAAU,EAAK,IAAI,EAAS,EAAM,CAAQ,CAAC,EAC/C,IAAK,IAAM,KAAQ,OAAO,KAAK,CAAM,EAC7B,IAAS,IACT,EAAQ,KAAK,CAAI,IAChB,GAAe,GAAK,SAAS,OAAO,IACzC,EAAK,IAAI,EAAS,EAAM,CAAI,CAAC,GAGjC,IAAM,EAAO,CAAC,GAAG,CAAI,CAAC,CAAC,KAAK,EACtB,EAAU,EAAK,EAAK,KAAK;CAAI,CAAC,EACpC,KAAK,SAAS,CACV,KAAM,QACN,WACA,OAAQ,KAAK,UAAU,CAAE,UAAS,KAAM,CAAK,CAAC,CAClD,CAAC,CACL,CAGG,CACX"}