Compare commits

...

3 Commits

Author SHA1 Message Date
GitHub Actions 84657e035a chore(release): v0.2.1 2023-07-16 15:14:44 +00:00
Aaron Klinker 6f5bf89645 feat: Support all content script options 2023-07-16 10:10:25 -05:00
Aaron Klinker 90fa6bfc40 fix: Remove HMR log
It was causing too many logs during development.
2023-07-15 14:11:22 -05:00
12 changed files with 154 additions and 37 deletions
+16
View File
@@ -1,5 +1,21 @@
# Changelog
## v0.2.1
[compare changes](https://github.com/aklinker1/wxt/compare/v0.2.0...v0.2.1)
### 🚀 Enhancements
- Support all content script options ([6f5bf89](https://github.com/aklinker1/wxt/commit/6f5bf89))
### 🩹 Fixes
- Remove HMR log ([90fa6bf](https://github.com/aklinker1/wxt/commit/90fa6bf))
### ❤️ Contributors
- Aaron Klinker
## v0.2.0
[compare changes](https://github.com/aklinker1/wxt/compare/v0.1.6...v0.2.0)
+4
View File
@@ -17,6 +17,10 @@ When a filename matches the pattern below, it is added as a content script in th
export default defineContentScript({
// Set manifest options
matches: ['*://google.com/*', '*://duckduckgo.com/*'],
excludeMatches: undefined | [],
includeGlobs: undefined | [],
excludeGlobs: undefined | [],
allFrames: undefined | [],
runAt: undefined | 'document_start' | 'document_end' | 'document_idle',
matchAboutBlank: undefined | true | false,
matchOriginAsFallback: undefined | true | false,
+3 -3
View File
@@ -149,7 +149,7 @@ describe('Manifest Content', () => {
`import "./style.css";
export default defineContentScript({
matches: ["*://google.com/*"],
run_at: "document_end",
runAt: "document_end",
main: () => {},
})`,
);
@@ -162,7 +162,7 @@ describe('Manifest Content', () => {
`import "./style.css";
export default defineContentScript({
matches: ["*://google.com/*"],
run_at: "document_end",
runAt: "document_end",
main: () => {},
})`,
);
@@ -175,7 +175,7 @@ describe('Manifest Content', () => {
`import "./style.css";
export default defineContentScript({
matches: ["*://duckduckgo.com/*"],
run_at: "document_end",
runAt: "document_end",
main: () => {},
})`,
);
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "wxt",
"type": "module",
"version": "0.2.0",
"version": "0.2.1",
"description": "Next gen framework for developing web extensions",
"engines": {
"node": ">=18.16.0",
+2 -1
View File
@@ -11,6 +11,7 @@ import { getEntrypointBundlePath } from './utils/entrypoints';
import { getContentScriptCssFiles } from './utils/manifest';
import { createWebExtRunner } from './runners/createWebExtRunner';
import { buildInternal } from './build';
import { mapWxtOptionsToContentScript } from './utils/content-scripts';
export async function getServerInfo(): Promise<ServerInfo> {
const port = await findOpenPort(3000, 3010);
@@ -102,9 +103,9 @@ export function reloadContentScripts(
const css = getContentScriptCssFiles([entry], server.currentOutput);
server.reloadContentScript({
...mapWxtOptionsToContentScript(entry.options),
js,
css,
...entry.options,
});
});
} else {
+42 -3
View File
@@ -255,11 +255,50 @@ export type Entrypoint =
export type OnContentScriptStopped = (cb: () => void) => void;
export interface ContentScriptDefinition {
matches: string[];
runAt?: 'document_start' | 'document_end' | 'document_idle';
matchAboutBlank?: boolean;
matches: Manifest.ContentScript['matches'];
/**
* See https://developer.chrome.com/docs/extensions/mv3/content_scripts/
* @default "documentIdle"
*/
runAt?: Manifest.ContentScript['run_at'];
/**
* See https://developer.chrome.com/docs/extensions/mv3/content_scripts/
* @default false
*/
matchAboutBlank?: Manifest.ContentScript['match_about_blank'];
/**
* See https://developer.chrome.com/docs/extensions/mv3/content_scripts/
* @default []
*/
excludeMatches?: Manifest.ContentScript['exclude_matches'];
/**
* See https://developer.chrome.com/docs/extensions/mv3/content_scripts/
* @default []
*/
includeGlobs?: Manifest.ContentScript['include_globs'];
/**
* See https://developer.chrome.com/docs/extensions/mv3/content_scripts/
* @default []
*/
excludeGlobs?: Manifest.ContentScript['exclude_globs'];
/**
* See https://developer.chrome.com/docs/extensions/mv3/content_scripts/
* @default false
*/
allFrames?: Manifest.ContentScript['all_frames'];
/**
* See https://developer.chrome.com/docs/extensions/mv3/content_scripts/
* @default false
*/
matchOriginAsFallback?: boolean;
/**
* See https://developer.chrome.com/docs/extensions/mv3/content_scripts/
* @default "ISOLATED"
*/
world?: 'ISOLATED' | 'MAIN';
/**
* Main function executed when the content script is loaded.
*/
main(): void | Promise<void>;
}
@@ -0,0 +1,28 @@
import { describe, expect, it } from 'vitest';
import { hashContentScriptOptions } from '../content-scripts';
describe('Content Script Utils', () => {
describe('hashContentScriptOptions', () => {
it('should return a string containing all the options with defaults applied', () => {
const hash = hashContentScriptOptions({ matches: [] });
expect(hash).toMatchInlineSnapshot(
'"[[\\"allFrames\\",false],[\\"excludeGlobs\\",[]],[\\"excludeMatches\\",[]],[\\"includeGlobs\\",[]],[\\"matchAboutBlank\\",false],[\\"matches\\",[]],[\\"matchOriginAsFallback\\",false],[\\"runAt\\",\\"document_idle\\"],[\\"world\\",\\"ISOLATED\\"]]"',
);
});
it('should be consistent regardless of the object ordering and default values', () => {
const hash1 = hashContentScriptOptions({
allFrames: true,
matches: ['*://google.com/*', '*://duckduckgo.com/*'],
matchAboutBlank: false,
});
const hash2 = hashContentScriptOptions({
matches: ['*://duckduckgo.com/*', '*://google.com/*'],
allFrames: true,
});
expect(hash1).toBe(hash2);
});
});
});
+52
View File
@@ -0,0 +1,52 @@
import { Manifest } from 'webextension-polyfill';
import { ContentScriptEntrypoint } from '../types';
/**
* Returns a unique and consistent string hash based on a content scripts options.
*
* It is able to recognize default values,
*/
export function hashContentScriptOptions(
options: ContentScriptEntrypoint['options'],
): string {
const withDefaults: ContentScriptEntrypoint['options'] = {
excludeGlobs: [],
excludeMatches: [],
includeGlobs: [],
matchAboutBlank: false,
matchOriginAsFallback: false,
runAt: 'document_idle',
allFrames: false,
world: 'ISOLATED',
// TODO: strip undefined fields from options object to improve content script grouping.
...options,
};
return JSON.stringify(
Object.entries(withDefaults)
// Sort any arrays so their values are consistent
.map<[string, unknown]>(([key, value]) => {
if (Array.isArray(value)) return [key, value.sort()];
else return [key, value];
})
// Sort all the fields alphabetically
.sort((l, r) => l[0].localeCompare(r[0])),
);
}
export function mapWxtOptionsToContentScript(
options: ContentScriptEntrypoint['options'],
): Omit<Manifest.ContentScript, 'js' | 'css'> {
return {
matches: options.matches,
all_frames: options.allFrames,
match_about_blank: options.matchAboutBlank,
exclude_globs: options.excludeGlobs,
exclude_matches: options.excludeMatches,
include_globs: options.includeGlobs,
run_at: options.runAt,
// @ts-expect-error: untyped chrome options
match_origin_as_fallback: options.matchOriginAsFallback,
world: options.world,
};
}
-1
View File
@@ -122,7 +122,6 @@ export async function getInternalConfig(
);
finalConfig.vite.plugins.push(plugins.devServerGlobals(finalConfig));
finalConfig.vite.plugins.push(plugins.tsconfigPaths(finalConfig));
finalConfig.vite.plugins.push(plugins.hmrLogger(finalConfig));
finalConfig.vite.define ??= {};
getGlobals(finalConfig).forEach((global) => {
+6 -2
View File
@@ -12,6 +12,10 @@ import fs from 'fs-extra';
import { resolve } from 'path';
import { getEntrypointBundlePath } from './entrypoints';
import { ContentSecurityPolicy } from './ContentSecurityPolicy';
import {
hashContentScriptOptions,
mapWxtOptionsToContentScript,
} from './content-scripts';
/**
* Writes the manifest to the output directory and the build output.
@@ -285,7 +289,7 @@ function addEntrypoints(
);
} else {
const hashToEntrypointsMap = contentScripts.reduce((map, script) => {
const hash = JSON.stringify(script.options);
const hash = hashContentScriptOptions(script.options);
if (map.has(hash)) map.get(hash)?.push(script);
else map.set(hash, [script]);
return map;
@@ -293,7 +297,7 @@ function addEntrypoints(
manifest.content_scripts = Array.from(hashToEntrypointsMap.entries()).map(
([, scripts]) => ({
...scripts[0].options,
...mapWxtOptionsToContentScript(scripts[0].options),
// TOOD: Sorting css and js arrays here so we get consistent test results... but we
// shouldn't have to. Where is the inconsistency coming from?
css: getContentScriptCssFiles(scripts, buildOutput)?.sort(),
-25
View File
@@ -1,25 +0,0 @@
import { Plugin } from 'vite';
import { InternalConfig } from '../types';
import { relative } from 'path';
import pc from 'picocolors';
/**
* Log when HMR changes are fired
*/
export function hmrLogger(config: InternalConfig): Plugin {
return {
name: 'wxt:hmr-logger',
apply: 'serve',
handleHotUpdate(ctx) {
if (
ctx.file.startsWith(config.srcDir) &&
!ctx.file.startsWith(config.wxtDir) &&
!ctx.file.endsWith('.html')
) {
config.logger.info(
'Hot reload: ' + pc.dim(relative(process.cwd(), ctx.file)),
);
}
},
};
}
-1
View File
@@ -5,4 +5,3 @@ export * from './multipageMove';
export * from './unimport';
export * from './virtualEntrypoint';
export * from './tsconfigPaths';
export * from './hmrLogger';