Skip to content

Commit 568c31d

Browse files
committed
Merge branch 'release/v0.2.0'
2 parents 0f4d278 + 9e3aa0d commit 568c31d

File tree

7 files changed

+80
-85
lines changed

7 files changed

+80
-85
lines changed

README.md

Lines changed: 36 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ Automatically detects Chrome Extension Manifest V2 and V3 and delegates to the a
1010

1111
- [Installation](#installation)
1212
- [Usage](#usage)
13-
- [Executing Functions](#executing-functions)
14-
- [Injecting Script Files](#injecting-script-files)
15-
- [Updating Options](#updating-options)
13+
- [Executing Functions](#executing-functions)
14+
- [Injecting Script Files](#injecting-script-files)
15+
- [Updating Options](#updating-options)
1616
- [API](#api)
1717
- [Options](#options)
1818
- [Examples](#examples)
@@ -41,23 +41,26 @@ import injectScript, {InjectScriptOptions} from "@adnbn/inject-script";
4141

4242
// Initialize an injector for a specific tab (Manifest V2 or V3)
4343
const injector = injectScript({
44-
tabId: 123,
45-
frameId: false, // inject into the top frame only
46-
matchAboutBlank: true, // include about:blank frames
47-
injectImmediately: false, // inject at `document_idle`
48-
// V2 only: timeFallback: 5000, // ms before timing out (default: 4000)
49-
// V3 only: world: 'ISOLATED', documentId: 'abc123'
44+
tabId: 123,
45+
frameId: false, // inject into the top frame only
46+
matchAboutBlank: true, // include about:blank frames
47+
runAt: "document_idle", // inject at `document_idle`
48+
// V2 only: timeFallback: 5000, // ms before timing out (default: 4000)
49+
// V3 only: world: 'ISOLATED', documentId: 'abc123'
5050
});
5151

5252
// Execute a function in the page context
53-
await injector.run((msg: string) => {
54-
console.log(msg);
55-
return 'Done';
56-
}, ['Hello from extension!']);
53+
await injector.run(
54+
(msg: string) => {
55+
console.log(msg);
56+
return "Done";
57+
},
58+
["Hello from extension!"]
59+
);
5760

5861
// Inject one or more external script files
59-
await injector.file('scripts/content.js');
60-
await injector.file(['scripts/lib.js', 'scripts/util.js']);
62+
await injector.file("scripts/content.js");
63+
await injector.file(["scripts/lib.js", "scripts/util.js"]);
6164
```
6265

6366
### Executing Functions
@@ -74,7 +77,7 @@ Use the `options(opts: Partial<InjectScriptOptions>)` method to merge or overrid
7477

7578
## API
7679

77-
### `injectScript(options: InjectScriptUnionOptions): InjectScriptContract`
80+
### `injectScript(options: InjectScriptOptions): InjectScriptContract`
7881

7982
Creates and returns a new script injector. Detects your manifest version via `@adnbn/browser` and delegates to the appropriate implementation.
8083

@@ -86,40 +89,41 @@ Creates and returns a new script injector. Detects your manifest version via `@a
8689

8790
## Options
8891

89-
| Option | Type | Description |
90-
| ------------------ | --------------------------------------- | ----------------------------------------------------------------------------------------------- |
91-
| `tabId` | `number` (required) | Target browser tab ID. |
92-
| `frameId` | `boolean \| number \| number[]` | `true` for all frames, number or list for specific frame IDs. |
93-
| `matchAboutBlank` | `boolean` | Include `about:blank` and similar frames (V2 and V3). Default: `true`. |
94-
| `injectImmediately` | `boolean` | Inject at `document_start` (`true`) or `document_idle` (`false`). |
95-
| `timeFallback` | `number` | (V2 only) ms before timing out. Default: `4000`. |
96-
| `world` | `'MAIN' \| 'ISOLATED'` | (V3 only) Execution world for script injection. |
97-
| `documentId` | `string \| string[]` | (V3 only) Document IDs for injection target. |
92+
| Option | Type | Description |
93+
| ----------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
94+
| `tabId` | `number` (required) | Target browser tab ID. |
95+
| `frameId` | `boolean \| number \| number[]` | `true` for all frames, number or list for specific frame IDs. |
96+
| `matchAboutBlank` | `boolean` | Include `about:blank` and similar frames (V2 and V3). Default: `true`. |
97+
| `runAt` | `'document_start' \| 'document_end' \| 'document_idle'` | Script injection timing. Use `document_start`, `document_end`, or `document_idle`. Default: `document_idle`. |
98+
| `timeFallback` | `number` | (V2 only) ms before timing out. Default: `4000`. |
99+
| `world` | `'MAIN' \| 'ISOLATED'` | (V3 only) Execution world for script injection. |
100+
| `documentId` | `string \| string[]` | (V3 only) Document IDs for injection target. |
98101

99102
## Examples
100103

101104
```ts
102105
import injectScript from "@adnbn/inject-script";
103106

104107
const injector = injectScript({
105-
tabId: 123,
106-
frameId: [0, 1],
107-
injectImmediately: true,
108-
world: 'MAIN',
109-
documentId: ['doc1', 'doc2']
108+
tabId: 123,
109+
frameId: [0, 1],
110+
runAt: "document_start",
111+
world: "MAIN",
112+
documentId: ["doc1", "doc2"],
110113
});
111114

112115
// Execute code and handle results
113116
const results = await injector.run(() => location.href);
114117
console.log(results);
115118

116119
// Inject scripts
117-
await injector.file(['content.js', 'helper.js']);
120+
await injector.file(["content.js", "helper.js"]);
118121
```
119122

120123
## Development
121124

122125
Build files
126+
123127
```bash
124128
npm run build
125129
```
@@ -142,4 +146,4 @@ See [CONTRIBUTING.md](CONTRIBUTING.md) and [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.
142146

143147
## License
144148

145-
MIT © Addon Bone
149+
MIT © Addon Bone

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@adnbn/inject-script",
3-
"version": "0.1.1",
3+
"version": "0.2.0",
44
"description": "A lightweight, TypeScript-ready library for injecting JavaScript functions or external scripts into Chrome extension tabs and frames (Manifest V2 & V3).",
55
"keywords": [
66
"browser",

src/AbstractInjectScript.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {InjectScriptOptions, InjectScriptContract} from "./types";
1+
import {InjectScriptContract, InjectScriptOptions} from "./types";
22

33
type Awaited<T> = chrome.scripting.Awaited<T>;
44
type InjectionResult<T> = chrome.scripting.InjectionResult<T>;

src/InjectScriptV2.ts

Lines changed: 8 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -6,29 +6,22 @@ import AbstractInjectScript from "./AbstractInjectScript";
66

77
import {InjectScriptOptions} from "./types";
88

9-
type RunAt = chrome.extensionTypes.RunAt;
10-
type InjectDetails = chrome.extensionTypes.InjectDetails;
11-
12-
type MessageSender = chrome.runtime.MessageSender;
13-
149
type Awaited<T> = chrome.scripting.Awaited<T>;
10+
type MessageSender = chrome.runtime.MessageSender;
11+
type InjectDetails = chrome.extensionTypes.InjectDetails;
1512
type InjectionResult<T> = chrome.scripting.InjectionResult<T>;
1613

17-
export interface InjectScriptV2Options extends InjectScriptOptions {
18-
timeFallback?: number;
19-
}
20-
2114
export default class extends AbstractInjectScript {
22-
public constructor(protected _options: InjectScriptV2Options) {
23-
super(_options);
15+
public constructor(options: InjectScriptOptions) {
16+
super(options);
2417
}
2518

2619
public async run<A extends any[], R extends any>(
2720
func: (...args: A) => R,
2821
args?: A
2922
): Promise<InjectionResult<Awaited<R>>[]> {
3023
return new Promise<InjectionResult<Awaited<R>>[]>(async (resolve, reject) => {
31-
const {tabId} = this._options;
24+
const {tabId, runAt} = this._options;
3225

3326
const type = `inject-script-${nanoid()}`;
3427
const injectResults: InjectionResult<Awaited<R>>[] = [];
@@ -71,8 +64,8 @@ export default class extends AbstractInjectScript {
7164
}, this._options.timeFallback || 4000);
7265

7366
const details: InjectDetails = {
67+
runAt,
7468
code: this.getCode(type, func, args),
75-
runAt: this.runAt,
7669
matchAboutBlank: this.matchAboutBlank,
7770
};
7871

@@ -93,18 +86,14 @@ export default class extends AbstractInjectScript {
9386
}
9487

9588
public async file(files: string | string[]): Promise<void> {
96-
const {tabId} = this._options;
89+
const {tabId, runAt} = this._options;
9790

9891
const fileList = typeof files === "string" ? [files] : files;
9992

10093
const injectTasks: Promise<any>[] = [];
10194

10295
for (const file of fileList) {
103-
const details: InjectDetails = {
104-
file,
105-
runAt: this.runAt,
106-
matchAboutBlank: this.matchAboutBlank,
107-
};
96+
const details: InjectDetails = {file, runAt, matchAboutBlank: this.matchAboutBlank};
10897

10998
if (this.allFrames) {
11099
injectTasks.push(executeScriptTab(tabId, {...details, allFrames: true}));
@@ -148,8 +137,4 @@ export default class extends AbstractInjectScript {
148137
});
149138
};
150139
}
151-
152-
protected get runAt(): RunAt {
153-
return this._options.injectImmediately ? "document_start" : "document_idle";
154-
}
155140
}

src/InjectScriptV3.ts

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,39 +7,37 @@ import {InjectScriptOptions} from "./types";
77
type Awaited<T> = chrome.scripting.Awaited<T>;
88
type InjectionTarget = chrome.scripting.InjectionTarget;
99
type InjectionResult<T> = chrome.scripting.InjectionResult<T>;
10-
type ExecutionWorld = chrome.scripting.ExecutionWorld;
11-
12-
export interface InjectScriptV3Options extends InjectScriptOptions {
13-
world?: ExecutionWorld;
14-
documentId?: string | string[];
15-
}
1610

1711
export default class extends AbstractInjectScript {
18-
constructor(protected _options: InjectScriptV3Options) {
19-
super(_options);
12+
constructor(options: InjectScriptOptions) {
13+
super(options);
2014
}
2115

2216
public async run<A extends any[], R extends any>(
2317
func: (...args: A) => R,
2418
args?: A
2519
): Promise<InjectionResult<Awaited<R>>[]> {
26-
const {world, injectImmediately} = this._options;
27-
28-
return executeScript({target: this.target, func, args, world, injectImmediately});
20+
return executeScript({
21+
target: this.target,
22+
world: this._options.world,
23+
injectImmediately: this.injectImmediately,
24+
func,
25+
args,
26+
});
2927
}
3028

3129
public async file(fileList: string | string[]): Promise<void> {
32-
const {world, injectImmediately} = this._options;
33-
const files = typeof fileList === "string" ? [fileList] : fileList;
34-
35-
await executeScript({target: this.target, files, world, injectImmediately});
30+
await executeScript({
31+
target: this.target,
32+
world: this._options.world,
33+
injectImmediately: this.injectImmediately,
34+
files: typeof fileList === "string" ? [fileList] : fileList,
35+
});
3636
}
3737

3838
protected get target(): InjectionTarget {
39-
const {tabId} = this._options;
40-
4139
return {
42-
tabId,
40+
tabId: this._options.tabId,
4341
allFrames: this.allFrames,
4442
frameIds: this.frameIds,
4543
documentIds: this.documentIds,
@@ -51,4 +49,8 @@ export default class extends AbstractInjectScript {
5149

5250
return typeof documentId === "string" ? [documentId] : documentId;
5351
}
52+
53+
protected get injectImmediately(): boolean {
54+
return this._options.runAt === "document_start";
55+
}
5456
}

src/index.ts

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,12 @@
11
import {isManifestVersion3} from "@adnbn/browser";
22

3-
import InjectScriptV2, {InjectScriptV2Options} from "./InjectScriptV2";
4-
import InjectScriptV3, {InjectScriptV3Options} from "./InjectScriptV3";
3+
import InjectScriptV2 from "./InjectScriptV2";
4+
import InjectScriptV3 from "./InjectScriptV3";
55

66
import {InjectScriptOptions, InjectScriptContract} from "./types";
77

88
export {type InjectScriptContract, type InjectScriptOptions};
99

10-
export type InjectScriptUnionOptions = InjectScriptV2Options & InjectScriptV3Options;
11-
12-
export default (options: InjectScriptUnionOptions): InjectScriptContract => {
13-
const {timeFallback, ...optionsV3} = options;
14-
const {documentId, world, ...optionsV2} = options;
15-
16-
return isManifestVersion3() ? new InjectScriptV3(optionsV3) : new InjectScriptV2(optionsV2);
10+
export default (options: InjectScriptOptions): InjectScriptContract => {
11+
return isManifestVersion3() ? new InjectScriptV3(options) : new InjectScriptV2(options);
1712
};

src/types.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
type RunAt = chrome.extensionTypes.RunAt;
12
type Awaited<T> = chrome.scripting.Awaited<T>;
3+
type ExecutionWorld = chrome.scripting.ExecutionWorld;
24
type InjectionResult<T> = chrome.scripting.InjectionResult<T>;
35

46
export interface InjectScriptContract {
@@ -13,5 +15,12 @@ export interface InjectScriptOptions {
1315
tabId: number;
1416
frameId?: boolean | number | number[];
1517
matchAboutBlank?: boolean;
16-
injectImmediately?: boolean;
18+
19+
// Options for MV2
20+
runAt?: RunAt;
21+
timeFallback?: number;
22+
23+
// Options for MV3
24+
world?: ExecutionWorld;
25+
documentId?: string | string[];
1726
}

0 commit comments

Comments
 (0)