Skip to content

Commit 792e303

Browse files
authored
feat: Support returning values from scripts executed with the scripting API (#624)
1 parent 38a5a2c commit 792e303

10 files changed

+88
-22
lines changed

Diff for: package.json

+2-7
Original file line numberDiff line numberDiff line change
@@ -45,16 +45,11 @@
4545
"*": "prettier --ignore-unknown --write"
4646
},
4747
"changelog": {
48-
"excludeAuthors": [
49-
50-
]
48+
"excludeAuthors": ["[email protected]"]
5149
},
5250
"pnpm": {
5351
"peerDependencyRules": {
54-
"ignoreMissing": [
55-
"@algolia/client-search",
56-
"search-insights"
57-
]
52+
"ignoreMissing": ["@algolia/client-search", "search-insights"]
5853
}
5954
}
6055
}

Diff for: packages/wxt/e2e/tests/output-structure.test.ts

+3
Original file line numberDiff line numberDiff line change
@@ -362,11 +362,13 @@ describe('Output Directory Structure', () => {
362362
function logHello(name) {
363363
console.log(\`Hello \${name}!\`);
364364
}
365+
_background;
365366
const definition = defineBackground({
366367
main() {
367368
logHello("background");
368369
}
369370
});
371+
_background;
370372
chrome;
371373
function print(method, ...args) {
372374
return;
@@ -389,6 +391,7 @@ describe('Output Directory Structure', () => {
389391
throw err;
390392
}
391393
})();
394+
_background;
392395
"
393396
`);
394397
});

Diff for: packages/wxt/src/core/builders/vite/index.ts

+10-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
} from '~/core/utils/virtual-modules';
2020
import { Hookable } from 'hookable';
2121
import { toArray } from '~/core/utils/arrays';
22+
import { safeVarName } from '~/core/utils/strings';
2223

2324
export async function createViteBuilder(
2425
wxtConfig: ResolvedConfig,
@@ -87,14 +88,22 @@ export async function createViteBuilder(
8788
plugins.push(wxtPlugins.cssEntrypoints(entrypoint, wxtConfig));
8889
}
8990

91+
const iifeReturnValueName = safeVarName(entrypoint.name);
9092
const libMode: vite.UserConfig = {
9193
mode: wxtConfig.mode,
9294
plugins,
95+
esbuild: {
96+
// Add a footer with the returned value so it can return values to `scripting.executeScript`
97+
// Footer is added apart of esbuild to make sure it's not minified. It
98+
// get's removed if added to `build.rollupOptions.output.footer`
99+
// See https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/scripting/executeScript#return_value
100+
footer: iifeReturnValueName + ';',
101+
},
93102
build: {
94103
lib: {
95104
entry,
96105
formats: ['iife'],
97-
name: '_',
106+
name: iifeReturnValueName,
98107
fileName: entrypoint.name,
99108
},
100109
rollupOptions: {

Diff for: packages/wxt/src/core/utils/__tests__/strings.test.ts

+20-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import { describe, expect, it } from 'vitest';
22
import {
33
kebabCaseAlphanumeric,
44
removeImportStatements,
5-
} from '~/core/utils/strings';
5+
safeVarName,
6+
} from '../strings';
67

78
describe('String utils', () => {
89
describe('kebabCaseAlphanumeric', () => {
@@ -18,6 +19,23 @@ describe('String utils', () => {
1819
});
1920
});
2021

22+
describe('safeVarName', () => {
23+
it.each([
24+
['Hello world!', '_hello_world'],
25+
['123', '_123'],
26+
['abc-123', '_abc_123'],
27+
['', '_'],
28+
[' ', '_'],
29+
['_', '_'],
30+
])(
31+
"should convert '%s' to '%s', which can be used for a variable name",
32+
(input, expected) => {
33+
const actual = safeVarName(input);
34+
expect(actual).toBe(expected);
35+
},
36+
);
37+
});
38+
2139
describe('removeImportStatements', () => {
2240
it('should remove all import formats', () => {
2341
const imports = `
@@ -30,7 +48,7 @@ import{ registerGithubService, createGithubApi }from "@/utils/github";
3048
import GitHub from "@/utils/github";
3149
import "@/utils/github";
3250
import '@/utils/github';
33-
import"@/utils/github"
51+
import"@/utils/github"
3452
import'@/utils/github';
3553
`;
3654
expect(removeImportStatements(imports).trim()).toEqual('');

Diff for: packages/wxt/src/core/utils/strings.ts

+8
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@ export function kebabCaseAlphanumeric(str: string): string {
55
.replace(/\s+/g, '-'); // Replace spaces with hyphens
66
}
77

8+
/**
9+
* Return a safe variable name for a given string.
10+
*/
11+
export function safeVarName(str: string): string {
12+
// _ prefix to ensure it doesn't start with a number
13+
return '_' + kebabCaseAlphanumeric(str.trim()).replace('-', '_');
14+
}
15+
816
/**
917
* Removes import statements from the top of a file. Keeps import.meta and inline, async `import()`
1018
* calls.

Diff for: packages/wxt/src/types/index.ts

+15-3
Original file line numberDiff line numberDiff line change
@@ -737,16 +737,24 @@ export interface IsolatedWorldContentScriptDefinition
737737
extends IsolatedWorldContentScriptEntrypointOptions {
738738
/**
739739
* Main function executed when the content script is loaded.
740+
*
741+
* When running a content script with `browser.scripting.executeScript`,
742+
* values returned from this function will be returned in the `executeScript`
743+
* result as well. Otherwise returning a value does nothing.
740744
*/
741-
main(ctx: ContentScriptContext): void | Promise<void>;
745+
main(ctx: ContentScriptContext): any | Promise<any>;
742746
}
743747

744748
export interface MainWorldContentScriptDefinition
745749
extends MainWorldContentScriptEntrypointOptions {
746750
/**
747751
* Main function executed when the content script is loaded.
752+
*
753+
* When running a content script with `browser.scripting.executeScript`,
754+
* values returned from this function will be returned in the `executeScript`
755+
* result as well. Otherwise returning a value does nothing.
748756
*/
749-
main(): void | Promise<void>;
757+
main(): any | Promise<any>;
750758
}
751759

752760
export type ContentScriptDefinition =
@@ -763,8 +771,12 @@ export interface BackgroundDefinition extends BackgroundEntrypointOptions {
763771
export interface UnlistedScriptDefinition extends BaseEntrypointOptions {
764772
/**
765773
* Main function executed when the unlisted script is ran.
774+
*
775+
* When running a content script with `browser.scripting.executeScript`,
776+
* values returned from this function will be returned in the `executeScript`
777+
* result as well. Otherwise returning a value does nothing.
766778
*/
767-
main(): void | Promise<void>;
779+
main(): any | Promise<any>;
768780
}
769781

770782
/**

Diff for: packages/wxt/src/virtual/content-script-isolated-world-entrypoint.ts

+9-2
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,23 @@ import definition from 'virtual:user-content-script-isolated-world-entrypoint';
22
import { logger } from '../sandbox/utils/logger';
33
import { ContentScriptContext } from 'wxt/client';
44

5-
(async () => {
5+
const result = (async () => {
66
try {
77
const { main, ...options } = definition;
88
const ctx = new ContentScriptContext(import.meta.env.ENTRYPOINT, options);
99

10-
await main(ctx);
10+
return await main(ctx);
1111
} catch (err) {
1212
logger.error(
1313
`The content script "${import.meta.env.ENTRYPOINT}" crashed on startup!`,
1414
err,
1515
);
16+
throw err;
1617
}
1718
})();
19+
20+
// Return the main function's result to the background when executed via the
21+
// scripting API. Default export causes the IIFE to return a value.
22+
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/scripting/executeScript#return_value
23+
// Tested on both Chrome and Firefox
24+
export default result;
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,21 @@
11
import definition from 'virtual:user-content-script-main-world-entrypoint';
22
import { logger } from '../sandbox/utils/logger';
33

4-
(async () => {
4+
const result = (async () => {
55
try {
66
const { main } = definition;
7-
await main();
7+
return await main();
88
} catch (err) {
99
logger.error(
1010
`The content script "${import.meta.env.ENTRYPOINT}" crashed on startup!`,
1111
err,
1212
);
13+
throw err;
1314
}
1415
})();
16+
17+
// Return the main function's result to the background when executed via the
18+
// scripting API. Default export causes the IIFE to return a value.
19+
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/scripting/executeScript#return_value
20+
// Tested on both Chrome and Firefox
21+
export default result;
+9-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
11
import definition from 'virtual:user-unlisted-script-entrypoint';
22
import { logger } from '../sandbox/utils/logger';
33

4-
(async () => {
4+
const result = (async () => {
55
try {
6-
await definition.main();
6+
return await definition.main();
77
} catch (err) {
88
logger.error(
99
`The unlisted script "${import.meta.env.ENTRYPOINT}" crashed on startup!`,
1010
err,
1111
);
12+
throw err;
1213
}
1314
})();
15+
16+
// Return the main function's result to the background when executed via the
17+
// scripting API. Default export causes the IIFE to return a value.
18+
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/scripting/executeScript#return_value
19+
// Tested on both Chrome and Firefox
20+
export default result;

Diff for: pnpm-lock.yaml

+3-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)