@crxjs/vite-plugin 2.5.0 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -38,6 +38,10 @@ npm create crxjs@latest
38
38
  > [!NOTE]
39
39
  > Looking for MV2 support? See [`rollup-plugin`](packages/rollup-plugin/README.md)
40
40
 
41
+ ## 💻 Contributing
42
+
43
+ See [CONTRIBUTING.md](./CONTRIBUTING.md) for local development workflows.
44
+
41
45
  ## 💝 Contributors
42
46
 
43
47
  This project exists thanks to all the people who contribute.
@@ -0,0 +1,448 @@
1
+ import { ConfigEnv, Plugin, PluginOption } from 'vite';
2
+ import { IsStringLiteral } from 'type-fest';
3
+ import { GlobOptions } from 'tinyglobby';
4
+ import { PluginContext, OutputBundle } from 'rollup';
5
+
6
+ interface DeclarativeNetRequestResource {
7
+ id: string;
8
+ enabled: boolean;
9
+ path: string;
10
+ }
11
+ interface WebAccessibleResourceByMatch {
12
+ matches: string[];
13
+ resources: string[];
14
+ use_dynamic_url?: boolean;
15
+ }
16
+ interface WebAccessibleResourceById {
17
+ extension_ids: string[];
18
+ resources: string[];
19
+ use_dynamic_url?: boolean;
20
+ }
21
+ interface ChromeManifestBackground {
22
+ service_worker: string;
23
+ type?: 'module' | (string & Record<never, never>);
24
+ }
25
+ interface FirefoxManifestBackground {
26
+ scripts: string[];
27
+ persistent?: false;
28
+ }
29
+ interface ManifestV3 {
30
+ manifest_version: number;
31
+ name: string;
32
+ version: string;
33
+ default_locale?: string | undefined;
34
+ description?: string | undefined;
35
+ icons?: chrome.runtime.ManifestIcons | undefined;
36
+ action?: chrome.runtime.ManifestAction | undefined;
37
+ /**
38
+ * @see https://developer.chrome.com/docs/extensions/reference/manifest/author
39
+ */
40
+ author?: {
41
+ email: string;
42
+ } | undefined;
43
+ background?: ChromeManifestBackground | FirefoxManifestBackground | undefined;
44
+ chrome_settings_overrides?: {
45
+ homepage?: string | undefined;
46
+ search_provider?: chrome.runtime.SearchProvider | undefined;
47
+ startup_pages?: string[] | undefined;
48
+ } | undefined;
49
+ chrome_ui_overrides?: {
50
+ bookmarks_ui?: {
51
+ remove_bookmark_shortcut?: boolean | undefined;
52
+ remove_button?: boolean | undefined;
53
+ } | undefined;
54
+ } | undefined;
55
+ chrome_url_overrides?: {
56
+ bookmarks?: string | undefined;
57
+ history?: string | undefined;
58
+ newtab?: string | undefined;
59
+ } | undefined;
60
+ commands?: {
61
+ [name: string]: {
62
+ suggested_key?: {
63
+ default?: string | undefined;
64
+ windows?: string | undefined;
65
+ mac?: string | undefined;
66
+ chromeos?: string | undefined;
67
+ linux?: string | undefined;
68
+ } | undefined;
69
+ description?: string | undefined;
70
+ global?: boolean | undefined;
71
+ };
72
+ } | undefined;
73
+ content_capabilities?: {
74
+ matches?: string[] | undefined;
75
+ permissions?: string[] | undefined;
76
+ } | undefined;
77
+ content_scripts?: {
78
+ matches?: string[] | undefined;
79
+ exclude_matches?: string[] | undefined;
80
+ css?: string[] | undefined;
81
+ js?: string[] | undefined;
82
+ run_at?: string | undefined;
83
+ all_frames?: boolean | undefined;
84
+ match_about_blank?: boolean | undefined;
85
+ include_globs?: string[] | undefined;
86
+ exclude_globs?: string[] | undefined;
87
+ world?: chrome.scripting.ExecutionWorld | string | undefined;
88
+ }[] | undefined;
89
+ content_security_policy?: {
90
+ extension_pages?: string;
91
+ sandbox?: string;
92
+ };
93
+ converted_from_user_script?: boolean | undefined;
94
+ current_locale?: string | undefined;
95
+ declarative_net_request?: {
96
+ rule_resources: DeclarativeNetRequestResource[];
97
+ };
98
+ devtools_page?: string | undefined;
99
+ event_rules?: {
100
+ event?: string | undefined;
101
+ actions?: {
102
+ type: string;
103
+ }[] | undefined;
104
+ conditions?: chrome.declarativeContent.PageStateMatcherProperties[] | undefined;
105
+ }[] | undefined;
106
+ externally_connectable?: {
107
+ ids?: string[] | undefined;
108
+ matches?: string[] | undefined;
109
+ accepts_tls_channel_id?: boolean | undefined;
110
+ } | undefined;
111
+ file_browser_handlers?: {
112
+ id?: string | undefined;
113
+ default_title?: string | undefined;
114
+ file_filters?: string[] | undefined;
115
+ }[] | undefined;
116
+ file_system_provider_capabilities?: {
117
+ configurable?: boolean | undefined;
118
+ watchable?: boolean | undefined;
119
+ multiple_mounts?: boolean | undefined;
120
+ source?: string | undefined;
121
+ } | undefined;
122
+ homepage_url?: string | undefined;
123
+ host_permissions?: string[] | undefined;
124
+ import?: {
125
+ id: string;
126
+ minimum_version?: string | undefined;
127
+ }[] | undefined;
128
+ export?: {
129
+ whitelist?: string[] | undefined;
130
+ } | undefined;
131
+ incognito?: string | undefined;
132
+ input_components?: {
133
+ name: string;
134
+ id?: string | undefined;
135
+ language?: string | string[] | undefined;
136
+ layouts?: string | string[] | undefined;
137
+ input_view?: string | undefined;
138
+ options_page?: string | undefined;
139
+ }[] | undefined;
140
+ key?: string | undefined;
141
+ minimum_chrome_version?: string | undefined;
142
+ nacl_modules?: {
143
+ path: string;
144
+ mime_type: string;
145
+ }[] | undefined;
146
+ oauth2?: {
147
+ client_id: string;
148
+ scopes?: string[] | undefined;
149
+ } | undefined;
150
+ offline_enabled?: boolean | undefined;
151
+ omnibox?: {
152
+ keyword: string;
153
+ } | undefined;
154
+ optional_host_permissions?: string[] | undefined;
155
+ optional_permissions?: chrome.runtime.ManifestPermissions[] | string[] | undefined;
156
+ options_page?: string | undefined;
157
+ options_ui?: {
158
+ page?: string | undefined;
159
+ chrome_style?: boolean | undefined;
160
+ open_in_tab?: boolean | undefined;
161
+ } | undefined;
162
+ permissions?: chrome.runtime.ManifestPermissions[] | string[] | undefined;
163
+ platforms?: {
164
+ nacl_arch?: string | undefined;
165
+ sub_package_path: string;
166
+ }[] | undefined;
167
+ plugins?: {
168
+ path: string;
169
+ }[] | undefined;
170
+ requirements?: {
171
+ '3D'?: {
172
+ features?: string[] | undefined;
173
+ } | undefined;
174
+ plugins?: {
175
+ npapi?: boolean | undefined;
176
+ } | undefined;
177
+ } | undefined;
178
+ sandbox?: {
179
+ pages: string[];
180
+ content_security_policy?: string | undefined;
181
+ } | undefined;
182
+ side_panel?: {
183
+ default_path?: string | undefined;
184
+ } | undefined;
185
+ short_name?: string | undefined;
186
+ spellcheck?: {
187
+ dictionary_language?: string | undefined;
188
+ dictionary_locale?: string | undefined;
189
+ dictionary_format?: string | undefined;
190
+ dictionary_path?: string | undefined;
191
+ } | undefined;
192
+ storage?: {
193
+ managed_schema: string;
194
+ } | undefined;
195
+ tts_engine?: {
196
+ voices: {
197
+ voice_name: string;
198
+ lang?: string | undefined;
199
+ gender?: string | undefined;
200
+ event_types?: string[] | undefined;
201
+ }[];
202
+ } | undefined;
203
+ update_url?: string | undefined;
204
+ version_name?: string | undefined;
205
+ web_accessible_resources?: (WebAccessibleResourceById | WebAccessibleResourceByMatch)[] | undefined;
206
+ browser_specific_settings?: {
207
+ gecko: {
208
+ id: string;
209
+ strict_min_version?: string | undefined;
210
+ strict_max_version?: string | undefined;
211
+ update_url?: string | undefined;
212
+ data_collection_permissions?: {
213
+ /**
214
+ * available value: "personallyIdentifyingInfo" | "healthInfo" | "financialAndPaymentInfo" | "authenticationInfo" | "personalCommunications" | "locationInfo" | "browsingActivity" | "websiteContent" | "websiteActivity" | "searchTerms" | "bookmarksInfo" | "none".
215
+ * see also: https://extensionworkshop.com/documentation/develop/firefox-builtin-data-consent/
216
+ */
217
+ required: GeckoPermissionsRequired[];
218
+ /**
219
+ * available value: "personallyIdentifyingInfo" | "healthInfo" | "financialAndPaymentInfo" | "authenticationInfo" | "personalCommunications" | "locationInfo" | "browsingActivity" | "websiteContent" | "websiteActivity" | "searchTerms" | "bookmarksInfo" | "technicalAndInteraction".
220
+ * see also: https://extensionworkshop.com/documentation/develop/firefox-builtin-data-consent/
221
+ */
222
+ optional?: GeckoPermissionsOptional[] | undefined;
223
+ };
224
+ };
225
+ } | undefined;
226
+ }
227
+ type GeckoPermissionsRequired = "personallyIdentifyingInfo" | "healthInfo" | "financialAndPaymentInfo" | "authenticationInfo" | "personalCommunications" | "locationInfo" | "browsingActivity" | "websiteContent" | "websiteActivity" | "searchTerms" | "bookmarksInfo" | "none";
228
+ type GeckoPermissionsOptional = "personallyIdentifyingInfo" | "healthInfo" | "financialAndPaymentInfo" | "authenticationInfo" | "personalCommunications" | "locationInfo" | "browsingActivity" | "websiteContent" | "websiteActivity" | "searchTerms" | "bookmarksInfo" | "technicalAndInteraction";
229
+
230
+ type ManifestV3Fn = (env: ConfigEnv) => ManifestV3 | Promise<ManifestV3>;
231
+ type ManifestV3Export = ManifestV3 | Promise<ManifestV3> | ManifestV3Fn;
232
+ type Code = '.' | '/' | '\\';
233
+ type LiteralManifestFilePath<T extends string> = T extends `${Code}${string}` ? never : T extends `${string}.${infer Ext}` ? Ext extends '' ? never : T : never;
234
+ type ManifestFilePath<T extends string> = IsStringLiteral<T> extends true ? LiteralManifestFilePath<T> : T;
235
+ interface ManifestIcons<T extends string> {
236
+ [size: number]: ManifestFilePath<T>;
237
+ }
238
+ type FilePathFields<T extends string> = {
239
+ icons?: ManifestIcons<T>;
240
+ action?: {
241
+ /**
242
+ * - Relative to Vite project root (where vite.config.js is)
243
+ * - Format: "subdir/icon.png" (no leading ./ or /)
244
+ *
245
+ * @example "assets/icon.png"
246
+ */
247
+ default_icon?: ManifestIcons<T>;
248
+ default_title?: string;
249
+ /**
250
+ * - Relative to Vite project root (where vite.config.js is)
251
+ * - Format: "subdir/index.html" (no leading ./ or /)
252
+ *
253
+ * @example "src/popup.html"
254
+ */
255
+ default_popup?: ManifestFilePath<T>;
256
+ };
257
+ background?: {
258
+ /**
259
+ * - Relative to Vite project root (where vite.config.js is)
260
+ * - Format: "subdir/index.js" (no leading ./ or /)
261
+ *
262
+ * @example "src/background.js"
263
+ */
264
+ service_worker: ManifestFilePath<T>;
265
+ type?: 'module' | (string & Record<never, never>);
266
+ } | FirefoxManifestBackground;
267
+ content_scripts?: {
268
+ matches?: string[];
269
+ exclude_matches?: string[];
270
+ /**
271
+ * - Relative to Vite project root (where vite.config.js is)
272
+ * - Format: "subdir/content.css" (no leading ./ or /)
273
+ *
274
+ * @example "src/content.css"
275
+ */
276
+ css?: ManifestFilePath<T>[];
277
+ /**
278
+ * - Relative to Vite project root (where vite.config.js is)
279
+ * - Format: "subdir/content.js" (no leading ./ or /)
280
+ *
281
+ * @example "src/content.js"
282
+ */
283
+ js?: ManifestFilePath<T>[];
284
+ run_at?: string;
285
+ all_frames?: boolean;
286
+ match_about_blank?: boolean;
287
+ include_globs?: string[];
288
+ exclude_globs?: string[];
289
+ /**
290
+ * - 'ISOLATED' (default): Content script runs in an isolated world.
291
+ * - 'MAIN': Content script runs in the main world.
292
+ * NOTE: MAIN currently does NOT support crxjs HMR
293
+ * @see https://developer.chrome.com/docs/extensions/mv3/content_scripts/#world
294
+ */
295
+ world?: 'ISOLATED' | 'MAIN';
296
+ }[];
297
+ input_components?: {
298
+ name: string;
299
+ id?: string;
300
+ language?: string | string[];
301
+ layouts?: string | string[];
302
+ input_view?: string;
303
+ /**
304
+ * - Relative to Vite project root (where vite.config.js is)
305
+ * - Format: "subdir/options.html" (no leading ./ or /)
306
+ *
307
+ * @example "src/options.html"
308
+ */
309
+ options_page?: ManifestFilePath<T>;
310
+ }[];
311
+ /**
312
+ * - Relative to Vite project root (where vite.config.js is)
313
+ * - Format: "subdir/options.html" (no leading ./ or /)
314
+ *
315
+ * @example "src/options.html"
316
+ */
317
+ options_page?: ManifestFilePath<T>;
318
+ /**
319
+ * - Relative to Vite project root (where vite.config.js is)
320
+ * - Format: "subdir/devtools.html" (no leading ./ or /)
321
+ *
322
+ * @example "src/devtools.html"
323
+ */
324
+ devtools_page?: ManifestFilePath<T>;
325
+ };
326
+ type ManifestOptions<T extends string> = Omit<ManifestV3, keyof FilePathFields<string>> & FilePathFields<T>;
327
+ type ManifestV3Options<T extends string = string> = ManifestOptions<T> | Promise<ManifestOptions<T>> | ManifestV3Define<T>;
328
+ type ManifestV3Define<T extends string> = (env: ConfigEnv) => ManifestOptions<T> | Promise<ManifestOptions<T>>;
329
+ declare const defineManifest: <T extends string>(manifest: ManifestV3Options<T>) => ManifestV3Export;
330
+ /**
331
+ * Content script resources like CSS and image files must be declared in the
332
+ * manifest under `web_accessible_resources`. Manifest V3 uses a match pattern
333
+ * to narrow the origins that can access a Chrome CRX resource.
334
+ *
335
+ * Content script resources use the same match pattern as the content script for
336
+ * web accessible resources.
337
+ *
338
+ * You don't need to define a match pattern for dynamic content script
339
+ * resources, but if you want to do so, you can use the helper function
340
+ * `defineDynamicResource` to define your web accessible resources in a
341
+ * TypeScript file:
342
+ *
343
+ * ```typescript
344
+ * import { crx, defineManifest, defineDynamicResource }
345
+ * const manifest = defineManifest({
346
+ * "web_accessible_resources": [
347
+ * defineDynamicResource({
348
+ * matches: ["https://example.com/*", "file:///*.mp3", "..."]
349
+ * use_dynamic_url?: true
350
+ * })
351
+ * ]
352
+ * })
353
+ * ```
354
+ */
355
+ declare const defineDynamicResource: ({ matches, use_dynamic_url, }: Omit<WebAccessibleResourceByMatch, 'resources'>) => WebAccessibleResourceByMatch;
356
+
357
+ type CrxDevAssetId = {
358
+ id: string;
359
+ type: 'asset';
360
+ source?: string | Uint8Array;
361
+ };
362
+ type CrxDevScriptId = {
363
+ id: string;
364
+ type: 'module' | 'iife';
365
+ };
366
+ interface CrxPlugin extends Plugin {
367
+ /**
368
+ * Runs during the transform hook for the manifest. Filenames use input
369
+ * filenames.
370
+ */
371
+ transformCrxManifest?: (this: PluginContext, manifest: ManifestV3) => Promise<ManifestV3 | null | undefined> | ManifestV3 | null | undefined;
372
+ /**
373
+ * Runs during generateBundle, before manifest output. Filenames use output
374
+ * filenames.
375
+ */
376
+ renderCrxManifest?: (this: PluginContext, manifest: ManifestV3, bundle: OutputBundle) => Promise<ManifestV3 | null | undefined> | ManifestV3 | null | undefined;
377
+ /**
378
+ * Runs in the file writer on content scripts during development. `script.id`
379
+ * is Vite URL format.
380
+ */
381
+ renderCrxDevScript?: (code: string, script: CrxDevScriptId) => Promise<string | null | undefined> | string | null | undefined;
382
+ }
383
+ interface CrxOptions {
384
+ contentScripts?: {
385
+ preambleCode?: string | false;
386
+ hmrTimeout?: number;
387
+ injectCss?: boolean;
388
+ /**
389
+ * List of content script files (relative to project root) that should be
390
+ * built as standalone IIFE bundles (self-contained, no loader, all imports
391
+ * inlined). This allows using normal filenames (without the `.iife.*`
392
+ * convention) for IIFE content scripts, e.g. for `world: 'MAIN'` or to
393
+ * avoid loader overhead.
394
+ *
395
+ * The files must still be listed in `manifest.content_scripts` (or used
396
+ * via `?script` / `?iife`).
397
+ *
398
+ * Example:
399
+ * crx({
400
+ * manifest,
401
+ * contentScripts: {
402
+ * standaloneFiles: ['src/injected.ts', 'src/another.js']
403
+ * }
404
+ * })
405
+ */
406
+ standaloneFiles?: string[];
407
+ };
408
+ globOptions?: GlobOptions;
409
+ /**
410
+ * The browser that this extension is targeting, can be "firefox" or "chrome".
411
+ * Default is "chrome".
412
+ */
413
+ browser?: Browser;
414
+ /**
415
+ * Enable automatic extension reload and HMR during development. When false:
416
+ *
417
+ * - The extension will not call `chrome.runtime.reload()` on background changes
418
+ * or dev server reconnection.
419
+ * - Content scripts will not receive HMR updates or reload their host pages.
420
+ * - Files are still rebuilt and written to the output directory on change.
421
+ *
422
+ * Use this when content scripts have side effects on injection and you want
423
+ * to manually reload the extension in the browser.
424
+ *
425
+ * Default is `true`.
426
+ */
427
+ liveReload?: boolean;
428
+ }
429
+ type Browser = 'firefox' | 'chrome';
430
+
431
+ /** Resolves when all existing files in scriptFiles are written. */
432
+ declare function allFilesReady(): Promise<void>;
433
+
434
+ type FileWriterId = {
435
+ type: CrxDevAssetId['type'] | CrxDevScriptId['type'] | 'loader';
436
+ id: string;
437
+ };
438
+ /** Resolves when file and dependencies are written. */
439
+ declare function fileReady(script: FileWriterId): Promise<void>;
440
+
441
+ declare const crx: (options: {
442
+ manifest: ManifestV3Export;
443
+ } & CrxOptions) => PluginOption[];
444
+ declare const chromeExtension: (options: {
445
+ manifest: ManifestV3Export;
446
+ } & CrxOptions) => PluginOption[];
447
+
448
+ export { CrxPlugin, ManifestV3Export, allFilesReady, chromeExtension, crx, defineDynamicResource, defineManifest, fileReady as filesReady };
package/dist/index.d.ts CHANGED
@@ -20,7 +20,7 @@ interface WebAccessibleResourceById {
20
20
  }
21
21
  interface ChromeManifestBackground {
22
22
  service_worker: string;
23
- type?: 'module' | (string & {});
23
+ type?: 'module' | (string & Record<never, never>);
24
24
  }
25
25
  interface FirefoxManifestBackground {
26
26
  scripts: string[];
@@ -262,7 +262,7 @@ type FilePathFields<T extends string> = {
262
262
  * @example "src/background.js"
263
263
  */
264
264
  service_worker: ManifestFilePath<T>;
265
- type?: 'module' | (string & {});
265
+ type?: 'module' | (string & Record<never, never>);
266
266
  } | FirefoxManifestBackground;
267
267
  content_scripts?: {
268
268
  matches?: string[];