Skip to content

Conversation

@yamadashy
Copy link
Owner

Summary

Migrated the project from webextension-toolbox to WXT for improved developer experience and better maintenance support.

Key Changes

Dependencies:

  • ✅ Added wxt as the new build framework
  • ✅ Removed @webextension-toolbox/webextension-toolbox (last updated 4 years ago)

Project Structure:

app/                    →  entrypoints/ + public/
├── scripts/           →  entrypoints/
├── styles/            →  entrypoints/ (inlined into content.ts)
├── _locales/          →  public/_locales/
├── images/            →  public/images/
└── manifest.json      →  wxt.config.ts (auto-generated)

Code Migration:

  • Converted background.ts to WXT format using defineBackground()
  • Converted content.ts to WXT format using defineContentScript()
  • Inlined CSS styles into content script for better bundling
  • Updated TypeScript configuration to extend WXT's auto-generated types

Configuration:

  • Created wxt.config.ts with manifest settings
  • Updated tsconfig.json to use WXT's type definitions
  • Modified npm scripts for WXT commands
  • Added .wxt/ and .output/ to .gitignore
  • Fixed secretlint config path

Benefits

🚀 Better Developer Experience:

  • Hot Module Replacement (HMR) for instant UI updates
  • Vite-based build system (significantly faster than webpack)
  • Built-in TypeScript support with auto-generated types
  • File-based entrypoints with automatic manifest generation

🔧 Active Maintenance:

  • WXT is actively maintained (1.64k commits, 187 contributors)
  • webextension-toolbox hasn't been updated in 4 years
  • Modern tooling and regular updates

🌐 Better Cross-Browser Support:

  • Chrome: Manifest V3 ✅
  • Firefox: Manifest V2 ✅
  • Edge: Manifest V3 ✅
  • Automatic browser-specific optimizations

Testing

All builds have been tested and verified:

  • ✅ Chrome MV3 build successful (~102KB)
  • ✅ Firefox MV2 build successful (~102KB)
  • ✅ Edge MV3 build successful (~102KB)
  • ✅ All linters passing (biome, tsc, secretlint)
  • ✅ All 14 locales properly included
  • ✅ All icons and assets correctly bundled

New Commands

# Development
npm run dev              # Chrome (default)
npm run dev:firefox      # Firefox with hot reload

# Build
npm run build            # Chrome
npm run build:chrome     # Chrome
npm run build:firefox    # Firefox
npm run build:edge       # Edge
npm run build-all        # All browsers

# Package
npm run zip              # Chrome
npm run zip:chrome       # Chrome
npm run zip:firefox      # Firefox
npm run zip:edge         # Edge

Breaking Changes

None. The extension functionality remains exactly the same. Only the build tooling has changed.

Migration Notes

After merging, developers should:

  1. Run npm install to update dependencies
  2. Use new npm scripts (npm run dev instead of old commands)
  3. Build output is now in .output/ instead of dist/

Migrated the project from webextension-toolbox to wxt for improved
developer experience and better maintenance.

Changes:
- Replaced webextension-toolbox with wxt framework
- Restructured project: app/ → entrypoints/ + public/
- Updated build configuration in wxt.config.ts
- Converted scripts to WXT format:
  - background.ts: wrapped with defineBackground()
  - content.ts: wrapped with defineContentScript()
  - Inlined CSS into content.ts
- Updated tsconfig.json to extend WXT's config
- Updated npm scripts for wxt commands
- Added .wxt/ and .output/ to .gitignore
- Fixed secretlint config path in package.json

Benefits:
- HMR (Hot Module Replacement) for faster development
- Vite-based build system (faster than webpack)
- Active maintenance and modern tooling
- Built-in TypeScript support with auto-generated types
- Better cross-browser support (Chrome MV3, Firefox MV2, Edge MV3)

All builds (Chrome, Firefox, Edge) tested successfully.
@gemini-code-assist
Copy link

Summary of Changes

Hello @yamadashy, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request performs a comprehensive migration of the project's build system from the legacy webextension-toolbox to the modern WXT framework. The primary goal is to significantly enhance the developer experience, improve maintainability through active tooling support, and ensure robust cross-browser compatibility for the extension. This transition involves updating core dependencies, restructuring the project's file layout, adapting existing background and content scripts to WXT's API, and completely overhauling the build configuration. Crucially, all these changes are implemented without altering the extension's existing functionality or introducing any breaking changes to its end-user behavior.

Highlights

  • Dependency Update: Replaced the outdated webextension-toolbox with the actively maintained wxt framework, enhancing build stability and future compatibility.
  • Project Structure Refactor: Reorganized the project directory to align with WXT conventions, moving scripts, styles, _locales, and images into entrypoints/ and public/ respectively.
  • Codebase Modernization: Migrated background.ts and content.ts to WXT's defineBackground() and defineContentScript() APIs, and inlined CSS directly into the content script for better bundling.
  • Build Configuration Overhaul: Introduced wxt.config.ts for manifest generation, updated tsconfig.json to leverage WXT's type definitions, and revised npm scripts for WXT's development and build commands.
  • Developer Experience Enhancements: Integrated WXT to provide benefits such as Hot Module Replacement (HMR), significantly faster Vite-based builds, and automatic manifest/type generation.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request is a great initiative to modernize the project's build system by migrating from the unmaintained webextension-toolbox to WXT. The changes are well-structured and follow the migration path outlined in the description. I've identified a few areas for improvement:

  • A potentially redundant background script that can be removed to simplify the code and reduce permissions.
  • A race condition in the content script's MutationObserver setup that could affect reliability.
  • An opportunity to make the wxt.config.ts more maintainable.
    Overall, this is a solid migration, and addressing these points will make the extension more robust and secure.

Comment on lines +88 to +125
document.addEventListener('DOMContentLoaded', () => {
addCodeWikiButton();

// Handle GitHub SPA navigation
let lastUrl = location.href;
let isProcessing = false;

const observer = new MutationObserver((mutations: MutationRecord[]) => {
if (isProcessing) return;
isProcessing = true;

const url = location.href;
if (url !== lastUrl) {
lastUrl = url;
setTimeout(() => {
addCodeWikiButton();
isProcessing = false;
}, 500);
return;
}

// Monitor navigation element addition (only if button doesn't exist)
const navActions = document.querySelector<HTMLUListElement>('ul.pagehead-actions');
const codeWikiButton = document.querySelector('.codewiki-button');
if (navActions && !codeWikiButton) {
addCodeWikiButton();
}

isProcessing = false;
});

observer.observe(document.body, {
childList: true,
subtree: true,
attributes: false,
characterData: false,
});
});

Choose a reason for hiding this comment

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

high

The MutationObserver is currently initialized inside the DOMContentLoaded event listener. This can lead to a race condition where DOM mutations occur before the listener fires and the observer is attached, especially since the script is configured with runAt: 'document_start'. This could cause the button to not appear reliably.

To ensure all DOM changes are captured from the beginning, the MutationObserver should be initialized and started at the top level of the main function, and the DOMContentLoaded wrapper can be removed.

    // Handle GitHub SPA navigation
    let lastUrl = location.href;
    let isProcessing = false;

    const observer = new MutationObserver(() => {
      if (isProcessing) return;
      isProcessing = true;

      const url = location.href;
      if (url !== lastUrl) {
        lastUrl = url;
        setTimeout(() => {
          addCodeWikiButton();
          isProcessing = false;
        }, 500);
        return;
      }

      // Monitor navigation element addition (only if button doesn't exist)
      const navActions = document.querySelector<HTMLUListElement>('ul.pagehead-actions');
      const codeWikiButton = document.querySelector('.codewiki-button');
      if (navActions && !codeWikiButton) {
        addCodeWikiButton();
      }

      isProcessing = false;
    });

    observer.observe(document.body, {
      childList: true,
      subtree: true,
    });

Comment on lines 2 to 55
const injectContentToTab = async (tab: chrome.tabs.Tab): Promise<void> => {
// Skip if URL is undefined
if (!tab.url) {
return;
}

// Skip if tab is discarded
if (tab.discarded) {
return;
}

// Skip if tab ID is undefined
if (tab.id === undefined) {
return;
}

// Skip if not a GitHub URL
if (!tab.url.startsWith('https://github.com/')) {
return;
}

try {
const manifest = chrome.runtime.getManifest();

// Inject CSS
if (manifest.content_scripts?.[0]?.css) {
await chrome.scripting.insertCSS({
target: { tabId: tab.id },
files: manifest.content_scripts[0].css,
});
}

// Inject JavaScript
if (manifest.content_scripts?.[0]?.js) {
await chrome.scripting.executeScript({
target: { tabId: tab.id },
files: manifest.content_scripts[0].js,
});
}
} catch (error) {
console.error('Error injecting content script:', error);
}
};

// Update extension content for tabs
chrome.tabs.query({}, async (tabs: chrome.tabs.Tab[]) => {
for (const tab of tabs) {
try {
await injectContentToTab(tab);
} catch (e) {
console.error(e);
}
}
});

Choose a reason for hiding this comment

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

medium

This script for programmatically injecting the content script appears to be a holdover from the previous build system and is likely redundant. Modern browsers automatically inject manifest-defined content scripts into existing, matching tabs upon extension installation or update.

Furthermore, the current implementation is brittle as it relies on the specific structure of the auto-generated manifest (manifest.content_scripts[0]).

Recommendation:

  1. Remove the logic inside defineBackground.
  2. Remove the "scripting" permission from wxt.config.ts as it will no longer be needed. This reduces the extension's permission footprint, which is a security best practice.
  // This logic has been removed as it is redundant.
  // Browsers automatically inject content scripts on install/update.
  // The "scripting" permission in `wxt.config.ts` should also be removed.

Comment on lines +22 to +30
resources: [
'images/icon-16.png',
'images/icon-19.png',
'images/icon-32.png',
'images/icon-38.png',
'images/icon-48.png',
'images/icon-64.png',
'images/icon-128.png',
],

Choose a reason for hiding this comment

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

medium

The resources array for web_accessible_resources lists each icon file individually. This can be simplified and made more maintainable by using a glob pattern. This way, you won't need to update this list every time an icon is added or removed.

        resources: ['images/*.png'],

Added 'tabs' permission to enable the background script to inject
content scripts into existing GitHub tabs when the extension is
installed or updated. This ensures the extension works on already
open tabs without requiring a page reload.
Fixed permission error by filtering tabs to only GitHub URLs in
chrome.tabs.query(). Previously queried all tabs, causing permission
errors when trying to inject scripts into non-GitHub tabs.

Changes:
- Added url filter 'https://github.com/*' to chrome.tabs.query()
- No additional permissions needed (tabs permission not required with url filter)
- Maintains same permissions as original webextension-toolbox version
Added check for document.head existence before injecting CSS.
Since runAt is 'document_start', document.head may not be available
yet. Uses MutationObserver to wait for document.head if needed.

Fixes: Uncaught TypeError: Cannot read properties of null (reading 'appendChild')
@yamadashy yamadashy closed this Nov 16, 2025
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.

2 participants