From 7c7fb8545092e0440f99d5a8ad62909bbc086a6c Mon Sep 17 00:00:00 2001 From: Jared Scott Date: Sat, 7 Feb 2026 13:22:28 +0800 Subject: [PATCH] build(tooling): migrate from Webpack to Vite Consolidate the build/test toolchain on Vite, which is already in the dependency tree via Vitest. This removes 4 packages (webpack, webpack-cli, ts-loader, copy-webpack-plugin), adds 1 explicit dependency (vite), and simplifies the build config from CJS to a native ESM vite.config.ts. - Move static assets (css, html, images) from src/ to public/ - Output ESM bundles instead of IIFE for MV3 compatibility - Add "type": "module" to manifest background service worker - Update all documentation to reflect new paths and tooling Co-Authored-By: Claude Opus 4.6 Signed-off-by: Jared Scott --- AGENTS.md | 25 +- CHANGELOG.md | 14 + CLAUDE.md | 4 +- CONTRIBUTING.md | 2 +- README.md | 4 +- app.zip | Bin 266724 -> 258282 bytes biome.json | 5 +- docs/architecture.md | 19 +- docs/development.md | 20 +- docs/troubleshooting.md | 2 +- package.json | 19 +- pnpm-lock.yaml | 1169 +++------------------------ {src => public}/css/main.css | 0 {src => public}/html/index.html | 2 +- {src => public}/images/icon-128.png | Bin {src => public}/images/icon-16.png | Bin {src => public}/images/icon-48.png | Bin {src => public}/images/icon-512.png | Bin {src => public}/images/icon-64.png | Bin {src => public}/images/icon.png | Bin public/manifest.json | 3 +- src/main.spec.ts | 2 +- tsconfig.build.json | 2 +- vite.config.ts | 23 + webpack/webpack.config.cjs | 68 -- 25 files changed, 180 insertions(+), 1203 deletions(-) rename {src => public}/css/main.css (100%) rename {src => public}/html/index.html (95%) rename {src => public}/images/icon-128.png (100%) rename {src => public}/images/icon-16.png (100%) rename {src => public}/images/icon-48.png (100%) rename {src => public}/images/icon-512.png (100%) rename {src => public}/images/icon-64.png (100%) rename {src => public}/images/icon.png (100%) create mode 100644 vite.config.ts delete mode 100644 webpack/webpack.config.cjs diff --git a/AGENTS.md b/AGENTS.md index a4873f2..7782b10 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,11 +1,11 @@ # AGENTS ## Project Overview -Write Wall is a Chrome Extension (Manifest V3) that provides a synced text pad backed by `chrome.storage.sync` so text is shared across the signed-in Chrome account. The UI is a single page (`src/html/index.html`) with a textarea and a byte counter; logic lives in `src/main.ts` and background logic in `src/service_worker.ts`. +Write Wall is a Chrome Extension (Manifest V3) that provides a synced text pad backed by `chrome.storage.sync` so text is shared across the signed-in Chrome account. The UI is a single page (`public/html/index.html`) with a textarea and a byte counter; logic lives in `src/main.ts` and background logic in `src/service_worker.ts`. ## Tech Stack - TypeScript (ES2024 target, strict mode) -- Webpack + ts-loader (build) +- Vite (build) - Vitest (tests, `*.spec.ts` naming) - Biome (lint + format) - Husky (pre-commit: lint + test) @@ -16,10 +16,11 @@ Write Wall is a Chrome Extension (Manifest V3) that provides a synced text pad b - `src/main.ts`: UI logic, reads/writes synced text, throttles writes to respect sync quotas. - `src/service_worker.ts`: MV3 service worker, opens `html/index.html` when the action icon is clicked. - `src/utils.ts`: Shared utilities (throttle function). -- `src/html/index.html`: Extension UI page. -- `src/css/main.css`: UI styles (dark theme, CSS custom properties). +- `public/html/index.html`: Extension UI page. +- `public/css/main.css`: UI styles (dark theme, CSS custom properties). +- `public/images/`: Extension icons (copied verbatim to `dist/` by Vite). - `public/manifest.json`: MV3 manifest (source of truth, copied to `dist/` on build). -- `webpack/webpack.config.cjs`: Build config, emits bundles to `dist/` and copies static assets. +- `vite.config.ts`: Build config, emits bundles to `dist/` and copies `public/` assets. - `build.cjs`: Packages `dist/` into `app.zip` for release. - `dist/`: Build output (generated, do not edit). @@ -30,11 +31,11 @@ Write Wall is a Chrome Extension (Manifest V3) that provides a synced text pad b | UI behavior / event handlers | `src/main.ts` | | Background / tab management | `src/service_worker.ts` | | Shared utilities | `src/utils.ts` | -| Styles | `src/css/main.css` | -| HTML structure | `src/html/index.html` | -| Static assets / icons | `src/images/` | +| Styles | `public/css/main.css` | +| HTML structure | `public/html/index.html` | +| Static assets / icons | `public/images/` | | Manifest changes | `public/manifest.json` | -| Build config | `webpack/webpack.config.cjs` | +| Build config | `vite.config.ts` | | New test | `src/.spec.ts` | ## Development Workflow @@ -48,8 +49,8 @@ Write Wall is a Chrome Extension (Manifest V3) that provides a synced text pad b - `pnpm test`: Run Vitest tests. - `pnpm lint`: Run Biome checks. - `pnpm lint:fix`: Run Biome with auto-fix. -- `pnpm develop`: Webpack build in watch mode. -- `pnpm build`: Webpack build + package `dist/` into `app.zip`. +- `pnpm develop`: Vite build in watch mode. +- `pnpm build`: Vite build + package `dist/` into `app.zip`. - `pnpm type:check`: Run TypeScript type checking. - `pnpm prepare`: Install Husky hooks. - `pnpm check-updates`: Run npm-check-updates in interactive mode. @@ -63,7 +64,7 @@ Write Wall is a Chrome Extension (Manifest V3) that provides a synced text pad b ## Common Pitfalls - Sync quota is 8,192 bytes total. Test near-limit behavior. -- Webpack uses `tsconfig.build.json`, not `tsconfig.json`. Build errors may differ from editor errors. +- Vite uses esbuild for transpilation (not `tsconfig.build.json`). Type checking is separate (`pnpm type:check`). - Pre-commit hook runs lint + test. Fix with `pnpm lint:fix` before retrying. - Test environment is Node (not browser). Chrome APIs must be mocked in tests. diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d32aec..2280616 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/). +## [Unreleased] + +### Changed + +- Replace Webpack with Vite for build tooling +- Move static assets (`css/`, `html/`, `images/`) from `src/` to `public/` +- Output ESM bundles instead of IIFE (required for Vite multi-entry builds) +- Add `"type": "module"` to manifest background service worker + +### Removed + +- Remove `webpack`, `webpack-cli`, `ts-loader`, `copy-webpack-plugin` dependencies +- Remove `webpack/` directory + ## [2.5.0] - 2026-01-27 ### Added diff --git a/CLAUDE.md b/CLAUDE.md index 20afb14..c88c655 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ ## Architecture Quick Reference -- UI page: `src/html/index.html` with logic in `src/main.ts` +- UI page: `public/html/index.html` with logic in `src/main.ts` - Service worker: `src/service_worker.ts` (tab management only) - Shared utilities: `src/utils.ts` (throttle function) - Sync storage key: `v2` in `chrome.storage.sync` (8,192 byte limit) @@ -33,7 +33,7 @@ if (copyButtonEl) { 1. **Tests fail**: Run `pnpm test` locally. Tests mock Chrome APIs; check mock setup in spec files. 2. **Lint errors**: Run `pnpm lint:fix`. Biome config is in `biome.json`. -3. **Build issues**: Check `webpack/webpack.config.cjs`. Uses `tsconfig.build.json` (not `tsconfig.json`). +3. **Build issues**: Check `vite.config.ts`. Vite uses esbuild for transpilation; type checking is separate (`pnpm type:check`). 4. **Version mismatch**: Both `package.json` and `public/manifest.json` must match. Use `pnpm verify-version`. ## CI/CD Details diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 640b132..6445c46 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -56,7 +56,7 @@ pnpm verify-version - Avoid editing `dist/` directly (generated output). - `chrome.storage.sync` is quota-limited; preserve write throttling. -- The UI is `src/html/index.html`; logic is in `src/main.ts`. +- The UI is `public/html/index.html`; logic is in `src/main.ts`. ## Communication diff --git a/README.md b/README.md index f8e6234..e2e5698 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ signing in to Chrome. ## How It Works -- The UI lives in `src/html/index.html` with logic in `src/main.ts` +- The UI lives in `public/html/index.html` with logic in `src/main.ts` - Text is saved to `chrome.storage.sync` under the `v2` key - Writes are throttled to respect Chrome sync quotas - The byte counter uses `chrome.storage.sync.getBytesInUse` @@ -55,7 +55,7 @@ signing in to Chrome. ### Common Scripts -- `pnpm develop`: Webpack build in watch mode +- `pnpm develop`: Vite build in watch mode - `pnpm build`: Production build + `app.zip` packaging - `pnpm lint`: Biome checks - `pnpm lint:fix`: Biome auto-fix diff --git a/app.zip b/app.zip index 9390d46a7660caa952fa01ccc1feebb25a19f054..7a8a33d7ab73efe4aa7b32be62f70bb7b99e5397 100644 GIT binary patch delta 3130 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 PzMByYgG8)=$h-dmWlUfy delta 11628 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 diff --git a/biome.json b/biome.json index f1fb825..5e7788d 100644 --- a/biome.json +++ b/biome.json @@ -1,5 +1,5 @@ { - "$schema": "https://biomejs.dev/schemas/2.3.12/schema.json", + "$schema": "https://biomejs.dev/schemas/2.3.14/schema.json", "vcs": { "enabled": true, "clientKind": "git", "useIgnoreFile": true }, "files": { "includes": ["**", "!!**/dist"] }, "formatter": { @@ -122,8 +122,7 @@ "!.idea/*", "!dist/*", "!node_modules/*", - "!*.cjs", - "!webpack/*" + "!*.cjs" ] }, "javascript": { diff --git a/docs/architecture.md b/docs/architecture.md index 3ebd985..c18019a 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -6,7 +6,7 @@ Write Wall is a Chrome Extension built on Manifest V3. It provides a synced text ## Extension Components -### UI Page (`src/html/index.html`) +### UI Page (`public/html/index.html`) Single HTML page rendered when the extension action icon is clicked. Contains: - ` - + diff --git a/src/images/icon-128.png b/public/images/icon-128.png similarity index 100% rename from src/images/icon-128.png rename to public/images/icon-128.png diff --git a/src/images/icon-16.png b/public/images/icon-16.png similarity index 100% rename from src/images/icon-16.png rename to public/images/icon-16.png diff --git a/src/images/icon-48.png b/public/images/icon-48.png similarity index 100% rename from src/images/icon-48.png rename to public/images/icon-48.png diff --git a/src/images/icon-512.png b/public/images/icon-512.png similarity index 100% rename from src/images/icon-512.png rename to public/images/icon-512.png diff --git a/src/images/icon-64.png b/public/images/icon-64.png similarity index 100% rename from src/images/icon-64.png rename to public/images/icon-64.png diff --git a/src/images/icon.png b/public/images/icon.png similarity index 100% rename from src/images/icon.png rename to public/images/icon.png diff --git a/public/manifest.json b/public/manifest.json index ed33712..c1a1470 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -20,7 +20,8 @@ } }, "background": { - "service_worker": "service_worker.bundle.js" + "service_worker": "service_worker.bundle.js", + "type": "module" }, "permissions": ["storage"] } diff --git a/src/main.spec.ts b/src/main.spec.ts index b83281d..4ff89bb 100644 --- a/src/main.spec.ts +++ b/src/main.spec.ts @@ -669,7 +669,7 @@ describe('main UI bootstrap', () => { }); it('ships a placeholder hint in the UI', () => { - const html = readFileSync(new URL('./html/index.html', import.meta.url), 'utf8'); + const html = readFileSync(new URL('../public/html/index.html', import.meta.url), 'utf8'); expect(html).toContain('placeholder="Type here... auto-syncs across Chrome."'); }); }); diff --git a/tsconfig.build.json b/tsconfig.build.json index 41e8a1d..4b554e4 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -2,12 +2,12 @@ "extends": "./tsconfig.json", "include": ["src/**/*"], "exclude": [ - "webpack/**/*", "dist/**/*", ".github/**/*", ".husky/**/*", "build.cjs", "src/**/*.spec.ts", + "vite.config.ts", "vitest.config.ts" ] } diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..c61d9cc --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,23 @@ +import { dirname, resolve } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { defineConfig } from 'vite'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); + +export default defineConfig({ + build: { + outDir: 'dist', + emptyOutDir: true, + sourcemap: false, + target: 'es2024', + rollupOptions: { + input: { + main: resolve(__dirname, 'src/main.ts'), + service_worker: resolve(__dirname, 'src/service_worker.ts'), + }, + output: { + entryFileNames: '[name].bundle.js', + }, + }, + }, +}); diff --git a/webpack/webpack.config.cjs b/webpack/webpack.config.cjs deleted file mode 100644 index edebf8e..0000000 --- a/webpack/webpack.config.cjs +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2023-2026 Jared M. Scott. This work is licensed under the Creative - * Commons Attribution 3.0 Un-ported License. To view a copy of this license, - * visit http://creativecommons.org/licenses/by/3.0/ or send a letter to - * Creative Commons, - * 444 Castro Street, Suite 900, - * Mountain View, California, 94041, USA. - */ - -const path = require('path'); -const CopyPlugin = require('copy-webpack-plugin'); -module.exports = { - mode: 'production', - devtool: 'inline-source-map', - entry: { - main: path.resolve(__dirname, '..', 'src', 'main.ts'), - service_worker: path.resolve(__dirname, '..', 'src', 'service_worker.ts'), - }, - output: { - path: path.join(__dirname, '../dist'), - filename: '[name].bundle.js', - clean: true, - }, - resolve: { - extensionAlias: { - '.js': ['.ts', '.tsx', '.js', '.jsx'], - }, - extensions: ['.tsx', '.ts', '.js'], - }, - module: { - rules: [ - { - test: /\.tsx?$/, - use: [ - { - loader: 'ts-loader', - options: { - configFile: path.resolve(__dirname, '..', 'tsconfig.build.json'), - }, - }, - ], - exclude: /node_modules/, - }, - ], - }, - plugins: [ - new CopyPlugin({ - patterns: [ - { from: '.', to: '.', context: 'public' }, - { - from: path.resolve(__dirname, '..', 'src', 'css'), - to: path.resolve(__dirname, '..', 'dist', 'css'), - context: 'public', - }, - { - from: path.resolve(__dirname, '..', 'src', 'html'), - to: path.resolve(__dirname, '..', 'dist', 'html'), - context: 'public', - }, - { - from: path.resolve(__dirname, '..', 'src', 'images'), - to: path.resolve(__dirname, '..', 'dist', 'images'), - context: 'public', - }, - ], - }), - ], -};