Files
mokosh/vite.test.config.ts
Mark 49f087fe40 feat(01-10): wave-1 task-2 — welcome page bundle + Vite entries + web_accessible_resources
Plan 01-10 Wave 1: welcome page bundle staged with canonical Plan 01-12
tokens.css @import + chrome.i18n for D-08 tagline (Plan 01-12 path-B
contract).

Files created:
  - src/welcome/copy.ts (Russian non-tagline COPY map per D-03 Sober
    voice; WELCOME_HERO_RU_FALLBACK + WELCOME_HERO_EN_FALLBACK exported
    for the welcome.ts `|| <en-const>` fallback chain).
  - src/welcome/welcome.html (lang='ru'; data-mokosh-key + data-mokosh-
    i18n-key attribute conventions; SINGLE stylesheet link; D-02 Hero +
    body + footer structure; 10 keyed attrs total; <title> populated
    via populateCopy).
  - src/welcome/welcome.css (FIRST LINE `@import '../shared/tokens.css';`;
    ZERO hex literals in source; 65 var(--mks-*) refs; D-02 layout +
    --mks-welcome-max-w=720px; --mks-rec madder for recording-related
    accents per D-04 Loom palette).
  - src/welcome/welcome.ts (vanilla DOM; populateCopy + populateI18n
    filter-pipeline form per project rule "no `continue`"; no `as any`;
    Logger from src/shared; document.readyState guard; no event
    handlers per D-16-toolbar informational charter).

Files modified:
  - vite.config.ts: rollupOptions.input gains `welcome:
    'src/welcome/welcome.html'`; __VITE_DEV__ + __MOKOSH_UAT__ defines
    untouched (Plan 01-12 Wave 5 baseline preserved verbatim).
  - vite.test.config.ts: mirror entry in dist-test/; mergeConfig pattern
    untouched.
  - manifest.json: web_accessible_resources block added after
    host_permissions, before background; storage permission preserved
    in permissions array; default_locale='en' + __MSG_*__ placeholders
    from Plan 01-12 Wave 3 preserved verbatim.

NO src/welcome/welcome-tokens.css file is created — Plan 01-12 must_have
#9 path-B contract: Plan 01-12 landed FIRST (b909c37 → 865d394; SUMMARY
2026-05-20); canonical src/shared/tokens.css is import-ready
(Lora @font-face + IBM Plex Sans + D-04 Loom palette + --mks-rec=
var(--mks-madder-600) = #b2543d); welcome.css @imports it directly. No
placeholder transition needed.

Verify (all GREEN):
  - grep -F "@import '../shared/tokens.css'" src/welcome/welcome.css: exit 0
  - grep -E '#[0-9a-fA-F]{3,8}' src/welcome/welcome.css: exit 1 (zero hex)
  - grep -c 'var(--mks-' src/welcome/welcome.css: 65 (>= 5 required)
  - grep -oE 'data-mokosh-(i18n-)?key=' welcome.html | wc -l: 10 (>= 7)
  - npm run build: clean; dist/src/welcome/welcome.html present;
    dist/assets/welcome-D9oNz95l.css carries inlined tokens.css content
    (--mks-rec: var(--mks-madder-600); --mks-madder-600: #b2543d).
  - npm run build:test: clean; dist-test/src/welcome/welcome.html present;
    dist-test/assets/welcome-wB0e_R_n.js bundled.
  - npx tsc --noEmit: clean.
  - dist/manifest.json preserves "default_locale": "en" + __MSG_extName__
    + web_accessible_resources block present (Vite/crxjs propagated).
  - Vitest baseline preserved: Task 1's 3-test file unchanged
    (1 RED + 2 vacuous-GREEN; Task 3 flips Test A to GREEN).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 09:09:22 +02:00

102 lines
5.4 KiB
TypeScript

// vite.test.config.ts — Plan 01-11 two-bundle separation.
//
// Extends the production `./vite.config.ts` with the following delta knobs:
// 1. `mode: 'test'` — Vite statically replaces `import.meta.env.MODE`
// everywhere in the input source with the string literal `'test'`.
// 2. `define: { __MOKOSH_UAT__: 'true' }` — the dedicated build-time
// token gating the test-hook dynamic imports in
// src/background/index.ts + src/offscreen/recorder.ts (Plan 01-11
// Task 2). With this set to `true` the `if (__MOKOSH_UAT__)` branch
// becomes a live branch and Rollup KEEPS the dynamic imports;
// production builds (vite.config.ts sets it `false`) tree-shake
// them away (verified by the Tier-1 grep gate
// `tests/background/no-test-hooks-in-prod-bundle.test.ts`).
// 3. `build.outDir: 'dist-test'` + `emptyOutDir: true` — emit to a
// SEPARATE directory so a `npm run build` immediately after this
// build does not clobber. Puppeteer harness consumes this path via
// `puppeteer.launch({ enableExtensions: [<abs-path-to-dist-test>] })`.
// 4. `build.modulePreload: { polyfill: false }` — CRITICAL SW FIX.
// Vite's default module-preload polyfill calls
// `document.getElementsByTagName` + `document.querySelector` at
// module init in EVERY chunk that contains a dynamic import. The
// production bundle has no dynamic imports (the test-hook gate is
// dead code; tree-shaken). The test bundle HAS the dynamic
// `await import('../test-hooks/sw-hooks')` — so the preload
// polyfill gets included in the SW chunk. SWs have no DOM —
// `document` is undefined — and the polyfill throws on the very
// first await, killing the SW module init silently (no console
// output, just a dead worker). Disabling the polyfill removes the
// `document.*` references; modern Chrome (and our MV3 target ≥88)
// supports native dynamic import without the polyfill.
// Empirically verified: with the polyfill enabled, the test
// bundle's SW never reaches `Service Worker initializing` log;
// with it disabled, the SW initializes and chrome.runtime.onMessage
// handlers respond. See Plan 01-11 PROTOTYPE research session.
//
// Plan 01-13 Wave 1: the extension-internal harness page at
// `tests/uat/extension-page-harness.html` is added as a Rollup input
// so the test build emits it under that path in `dist-test/`. The
// Puppeteer driver navigates the in-browser tab to
// `chrome-extension://<id>/tests/uat/extension-page-harness.html` and
// invokes `window.__mokoshHarness.*` from the host side via CDP. The
// page itself has full chrome.* extension privileges (Approach B
// architectural anchor). Production builds (vite.config.ts) do NOT
// include this input — the page ships only in the test bundle.
//
// References:
// - Vite mergeConfig: https://vite.dev/guide/api-javascript.html#mergeconfig
// - Vite environment variables: https://vite.dev/guide/env-and-mode.html
// - Vite build.modulePreload: https://vite.dev/config/build-options.html#build-modulepreload
// - Rollup multi-entry inputs: https://rollupjs.org/configuration-options/#input
import { defineConfig, mergeConfig, type UserConfigExport } from 'vite';
import baseConfig from './vite.config';
const baseAsExport: UserConfigExport = baseConfig;
export default defineConfig(({ command, mode }) =>
mergeConfig(
typeof baseAsExport === 'function'
? baseAsExport({ command, mode, isPreview: false, isSsrBuild: false })
: baseAsExport,
{
mode: 'test',
// `define` performs a static text replacement at build time. We use a
// dedicated `__MOKOSH_UAT__` token (NOT `import.meta.env.MODE`) for the
// hook-import gate because vitest defaults its mode to 'test' too —
// gating on MODE would activate the hooks during unit tests and
// overwrite their vi.fn() mocks for chrome.notifications.create etc.
// The dedicated token is `false` everywhere except in this test bundle
// (Plan 01-11 RESEARCH §6 augmented — keeps Vite tree-shake semantics
// intact while sidestepping the vitest cross-contamination).
// Reference: https://vite.dev/config/shared-options.html#define
define: {
__MOKOSH_UAT__: 'true',
},
build: {
outDir: 'dist-test',
emptyOutDir: true,
// CRITICAL: see file header comment §4 — disables the
// document.*-using module preload polyfill that crashes SW init.
modulePreload: { polyfill: false },
rollupOptions: {
input: {
// Plan 01-13 Wave 1: emit the extension-internal harness
// page at its production path so it becomes reachable as
// chrome-extension://<id>/tests/uat/extension-page-harness.html
// The crxjs vite plugin will copy this HTML into dist-test/
// and rewrite the <script type="module" src> reference to
// the bundled chunk's hashed filename.
extension_page_harness: 'tests/uat/extension-page-harness.html',
// Plan 01-10 D-17-onboarding: mirror of vite.config.ts —
// the harness A15/A16/A17 assertions navigate against the
// test bundle and need welcome.html reachable under
// dist-test/src/welcome/welcome.html.
welcome: 'src/welcome/welcome.html',
},
},
},
},
),
);