Skip to content

Instantly share code, notes, and snippets.

@irgendwr
Last active March 24, 2022 20:21
Show Gist options
  • Save irgendwr/09bfa9af38d6ef42572c0bbddd813242 to your computer and use it in GitHub Desktop.
Save irgendwr/09bfa9af38d6ef42572c0bbddd813242 to your computer and use it in GitHub Desktop.
A patch that enables Manifest v3 support in @parcel/transformer-webextension
diff --git a/node_modules/@parcel/transformer-webextension/webextension/lib/WebExtensionTransformer.js b/node_modules/@parcel/transformer-webextension/webextension/lib/WebExtensionTransformer.js
new file mode 100644
index 0000000..156e583
--- /dev/null
+++ b/node_modules/@parcel/transformer-webextension/webextension/lib/WebExtensionTransformer.js
@@ -0,0 +1,401 @@
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.default = void 0;
+
+function _plugin() {
+ const data = require("@parcel/plugin");
+
+ _plugin = function () {
+ return data;
+ };
+
+ return data;
+}
+
+function _path() {
+ const data = _interopRequireDefault(require("path"));
+
+ _path = function () {
+ return data;
+ };
+
+ return data;
+}
+
+function _jsonSourceMap() {
+ const data = _interopRequireDefault(require("json-source-map"));
+
+ _jsonSourceMap = function () {
+ return data;
+ };
+
+ return data;
+}
+
+function _contentSecurityPolicyParser() {
+ const data = _interopRequireDefault(require("content-security-policy-parser"));
+
+ _contentSecurityPolicyParser = function () {
+ return data;
+ };
+
+ return data;
+}
+
+function _utils() {
+ const data = require("@parcel/utils");
+
+ _utils = function () {
+ return data;
+ };
+
+ return data;
+}
+
+function _diagnostic() {
+ const data = _interopRequireWildcard(require("@parcel/diagnostic"));
+
+ _diagnostic = function () {
+ return data;
+ };
+
+ return data;
+}
+
+var _schema = require("./schema");
+
+function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
+
+function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+const DEP_LOCS = [['icons'], ['browser_action', 'default_icon'], ['browser_action', 'default_popup'], ['page_action', 'default_icon'], ['page_action', 'default_popup'], ['action', 'default_icon'], ['action', 'default_popup'], ['background', 'scripts'], ['chrome_url_overrides'], ['devtools_page'], ['options_ui', 'page'], ['sidebar_action', 'default_icon'], ['sidebar_action', 'default_panel'], ['storage', 'managed_schema'], ['theme', 'images', 'theme_frame'], ['theme', 'images', 'additional_backgrounds'], ['user_scripts', 'api_script']];
+
+async function collectDependencies(asset, program, ptrs, hot) {
+ var _program$browserActio;
+
+ const fs = asset.fs;
+ const filePath = asset.filePath;
+
+ const assetDir = _path().default.dirname(filePath);
+
+ const isMV2 = program.manifest_version == 2;
+
+ if (program.default_locale) {
+ const locales = _path().default.join(assetDir, '_locales');
+
+ let err = !(await fs.exists(locales)) ? 'key' : !(await fs.exists(_path().default.join(locales, program.default_locale))) ? 'value' : null;
+
+ if (err) {
+ throw new (_diagnostic().default)({
+ diagnostic: [{
+ message: 'Invalid Web Extension manifest',
+ origin: '@parcel/transformer-webextension',
+ codeFrames: [{
+ filePath,
+ codeHighlights: [{ ...(0, _diagnostic().getJSONSourceLocation)(ptrs['/default_locale'], err),
+ message: (0, _diagnostic().md)`Localization directory${err == 'value' ? ' for ' + program.default_locale : ''} does not exist: ${_path().default.relative(assetDir, _path().default.join(locales, program.default_locale))}`
+ }]
+ }]
+ }]
+ });
+ }
+
+ for (const locale of await fs.readdir(locales)) {
+ if (await fs.exists(_path().default.join(locales, locale))) {
+ asset.addURLDependency(`_locales/${locale}/messages.json`, {
+ needsStableName: true,
+ pipeline: 'raw'
+ });
+ }
+ }
+ }
+
+ let needRuntimeBG = false;
+
+ if (program.content_scripts) {
+ for (let i = 0; i < program.content_scripts.length; ++i) {
+ const sc = program.content_scripts[i];
+
+ for (const k of ['css', 'js']) {
+ const assets = sc[k] || [];
+
+ for (let j = 0; j < assets.length; ++j) {
+ if (k == 'js') {
+ asset.invalidateOnFileChange(_path().default.join(assetDir, assets[j]));
+ }
+
+ assets[j] = asset.addURLDependency(assets[j], {
+ loc: {
+ filePath,
+ ...(0, _diagnostic().getJSONSourceLocation)(ptrs[`/content_scripts/${i}/${k}/${j}`], 'value')
+ }
+ });
+ }
+ }
+
+ if (hot && sc.js && sc.js.length) {
+ needRuntimeBG = true;
+ sc.js.push(asset.addURLDependency('./runtime/autoreload.js', {
+ resolveFrom: __filename
+ }));
+ }
+ }
+ }
+
+ if (program.dictionaries) {
+ for (const dict in program.dictionaries) {
+ const sourceLoc = (0, _diagnostic().getJSONSourceLocation)(ptrs[`/dictionaries/${dict}`], 'value');
+ const loc = {
+ filePath,
+ ...sourceLoc
+ };
+ const dictFile = program.dictionaries[dict];
+
+ if (_path().default.extname(dictFile) != '.dic') {
+ throw new (_diagnostic().default)({
+ diagnostic: [{
+ message: 'Invalid Web Extension manifest',
+ origin: '@parcel/transformer-webextension',
+ codeFrames: [{
+ filePath,
+ codeHighlights: [{ ...sourceLoc,
+ message: 'Dictionaries must be .dic files'
+ }]
+ }]
+ }]
+ });
+ }
+
+ program.dictionaries[dict] = asset.addURLDependency(dictFile, {
+ needsStableName: true,
+ loc
+ });
+ asset.addURLDependency(dictFile.slice(0, -4) + '.aff', {
+ needsStableName: true,
+ loc
+ });
+ }
+ }
+
+ const browserActionName = isMV2 ? 'browser_action' : 'action';
+
+ if ((_program$browserActio = program[browserActionName]) !== null && _program$browserActio !== void 0 && _program$browserActio.theme_icons) {
+ for (let i = 0; i < program[browserActionName].theme_icons.length; ++i) {
+ const themeIcon = program[browserActionName].theme_icons[i];
+
+ for (const k of ['light', 'dark']) {
+ const loc = (0, _diagnostic().getJSONSourceLocation)(ptrs[`/${browserActionName}/theme_icons/${i}/${k}`], 'value');
+ themeIcon[k] = asset.addURLDependency(themeIcon[k], {
+ loc: { ...loc,
+ filePath
+ }
+ });
+ }
+ }
+ }
+
+ if (program.web_accessible_resources) {
+ let war = [];
+
+ for (let i = 0; i < program.web_accessible_resources.length; ++i) {
+ // TODO: this doesn't support Parcel resolution
+ const currentEntry = program.web_accessible_resources[i];
+ const files = isMV2 ? [currentEntry] : currentEntry.resources;
+
+ for (let j = 0; j < files.length; ++j) {
+ const globFiles = (await (0, _utils().glob)(_path().default.join(assetDir, files[j]), fs, {})).map(fp => asset.addURLDependency(_path().default.relative(assetDir, fp), {
+ needsStableName: true,
+ loc: {
+ filePath,
+ ...(0, _diagnostic().getJSONSourceLocation)(ptrs[`/web_accessible_resources/${i}${isMV2 ? '' : `/resources/${j}`}`])
+ }
+ }));
+
+ if (isMV2) {
+ war = war.concat(globFiles);
+ } else {
+ currentEntry.resources = globFiles;
+ war.push(currentEntry);
+ }
+ }
+ }
+
+ program.web_accessible_resources = war;
+ }
+
+ for (const loc of DEP_LOCS) {
+ const location = '/' + loc.join('/');
+ if (!ptrs[location]) continue;
+ let parent = program;
+
+ for (let i = 0; i < loc.length - 1; ++i) {
+ parent = parent[loc[i]];
+ }
+
+ const lastLoc = loc[loc.length - 1];
+ const obj = parent[lastLoc];
+ if (typeof obj == 'string') parent[lastLoc] = asset.addURLDependency(obj, {
+ loc: {
+ filePath,
+ ...(0, _diagnostic().getJSONSourceLocation)(ptrs[location], 'value')
+ },
+ pipeline: _path().default.extname(obj) == '.json' ? 'raw' : undefined
+ });else {
+ for (const k of Object.keys(obj)) {
+ obj[k] = asset.addURLDependency(obj[k], {
+ loc: {
+ filePath,
+ ...(0, _diagnostic().getJSONSourceLocation)(ptrs[location + '/' + k], 'value')
+ },
+ pipeline: _path().default.extname(obj[k]) == '.json' ? 'raw' : undefined
+ });
+ }
+ }
+ }
+
+ if (isMV2) {
+ var _program$background;
+
+ if ((_program$background = program.background) !== null && _program$background !== void 0 && _program$background.page) {
+ program.background.page = asset.addURLDependency(program.background.page, {
+ loc: {
+ filePath,
+ ...(0, _diagnostic().getJSONSourceLocation)(ptrs['/background/page'], 'value')
+ }
+ });
+
+ if (needRuntimeBG) {
+ asset.meta.webextBGInsert = program.background.page;
+ }
+ }
+
+ if (hot) {
+ var _program$background2;
+
+ // To enable HMR, we must override the CSP to allow 'unsafe-eval'
+ program.content_security_policy = cspPatchHMR(program.content_security_policy);
+
+ if (needRuntimeBG && !((_program$background2 = program.background) !== null && _program$background2 !== void 0 && _program$background2.page)) {
+ if (!program.background) {
+ program.background = {};
+ }
+
+ if (!program.background.scripts) {
+ program.background.scripts = [];
+ }
+
+ if (program.background.scripts.length == 0) {
+ program.background.scripts.push(asset.addURLDependency('./runtime/default-bg.js', {
+ resolveFrom: __filename
+ }));
+ }
+
+ asset.meta.webextBGInsert = program.background.scripts[0];
+ }
+ }
+ } else {
+ var _program$background3;
+
+ if ((_program$background3 = program.background) !== null && _program$background3 !== void 0 && _program$background3.service_worker) {
+ program.background.service_worker = asset.addURLDependency(program.background.service_worker, {
+ loc: {
+ filePath,
+ ...(0, _diagnostic().getJSONSourceLocation)(ptrs['/background/service_worker'], 'value')
+ },
+ env: {
+ context: 'service-worker',
+ sourceType: program.background.type == 'module' ? 'module' : 'script'
+ }
+ });
+ }
+
+ if (needRuntimeBG) {
+ if (!program.background) {
+ program.background = {};
+ }
+
+ if (!program.background.service_worker) {
+ program.background.service_worker = asset.addURLDependency('./runtime/default-bg.js', {
+ resolveFrom: __filename,
+ env: {
+ context: 'service-worker'
+ }
+ });
+ }
+
+ asset.meta.webextBGInsert = program.background.service_worker;
+ }
+ }
+}
+
+function cspPatchHMR(policy) {
+ if (policy) {
+ const csp = (0, _contentSecurityPolicyParser().default)(policy);
+ policy = '';
+
+ if (!csp['script-src']) {
+ csp['script-src'] = ["'self' 'unsafe-eval' blob: filesystem:"];
+ }
+
+ if (!csp['script-src'].includes("'unsafe-eval'")) {
+ csp['script-src'].push("'unsafe-eval'");
+ }
+
+ for (const k in csp) {
+ policy += `${k} ${csp[k].join(' ')};`;
+ }
+
+ return policy;
+ } else {
+ return "script-src 'self' 'unsafe-eval' blob: filesystem:;" + "object-src 'self' blob: filesystem:;";
+ }
+}
+
+var _default = new (_plugin().Transformer)({
+ async transform({
+ asset,
+ options
+ }) {
+ const code = await asset.getCode();
+
+ const parsed = _jsonSourceMap().default.parse(code);
+
+ const data = parsed.data; // Not using a unified schema dramatically improves error messages
+
+ let schema = _schema.VersionSchema;
+
+ if (data.manifest_version === 3) {
+ schema = _schema.MV3Schema;
+ } else if (data.manifest_version === 2) {
+ schema = _schema.MV2Schema;
+ }
+
+ _utils().validateSchema.diagnostic(schema, {
+ data: data,
+ source: code,
+ filePath: asset.filePath
+ }, '@parcel/transformer-webextension', 'Invalid Web Extension manifest');
+
+ asset.setEnvironment({
+ context: 'browser',
+ engines: asset.env.engines,
+ shouldOptimize: asset.env.shouldOptimize,
+ sourceMap: { ...asset.env.sourceMap,
+ inline: true,
+ inlineSources: true
+ }
+ });
+ await collectDependencies(asset, data, parsed.pointers, Boolean(options.hmrOptions));
+ asset.setCode(JSON.stringify(data, null, 2));
+ asset.meta.webextEntry = true;
+ return [asset];
+ }
+
+});
+
+exports.default = _default;
\ No newline at end of file
diff --git a/node_modules/@parcel/transformer-webextension/webextension/lib/runtime/autoreload.js b/node_modules/@parcel/transformer-webextension/webextension/lib/runtime/autoreload.js
new file mode 100644
index 0000000..61ef27e
--- /dev/null
+++ b/node_modules/@parcel/transformer-webextension/webextension/lib/runtime/autoreload.js
@@ -0,0 +1,12 @@
+"use strict";
+
+/* global chrome, browser, addEventListener */
+var env = typeof chrome == 'undefined' ? browser : chrome;
+addEventListener('beforeunload', function () {
+ try {
+ env.runtime.sendMessage({
+ __parcel_hmr_reload__: true
+ });
+ } catch (err) {// ignore throwing if extension context invalidated
+ }
+});
\ No newline at end of file
diff --git a/node_modules/@parcel/transformer-webextension/webextension/lib/runtime/default-bg.js b/node_modules/@parcel/transformer-webextension/webextension/lib/runtime/default-bg.js
new file mode 100644
index 0000000..9a390c3
--- /dev/null
+++ b/node_modules/@parcel/transformer-webextension/webextension/lib/runtime/default-bg.js
@@ -0,0 +1 @@
+"use strict";
\ No newline at end of file
diff --git a/node_modules/@parcel/transformer-webextension/webextension/lib/schema.js b/node_modules/@parcel/transformer-webextension/webextension/lib/schema.js
new file mode 100644
index 0000000..1cac21d
--- /dev/null
+++ b/node_modules/@parcel/transformer-webextension/webextension/lib/schema.js
@@ -0,0 +1,486 @@
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.VersionSchema = exports.MV3Schema = exports.MV2Schema = void 0;
+
+const validateVersion = ver => {
+ const parts = ver.split('.', 5);
+ if (parts.length == 5) return 'Extension versions to have at most three dots';
+ if (parts.every(part => part.length != 0 && Number(part[0]) >= 0 && Number(part) < 65536)) return;
+ return 'Extension versions must be dot-separated integers between 0 and 65535';
+};
+
+const string = {
+ type: 'string'
+};
+const boolean = {
+ type: 'boolean'
+};
+const icons = {
+ type: 'object',
+ properties: {},
+ additionalProperties: string
+};
+const actionProps = {
+ // FF only
+ browser_style: boolean,
+ // You can also have a raw string, but not in Edge, apparently...
+ default_icon: {
+ oneOf: [icons, string]
+ },
+ default_popup: string,
+ default_title: string
+};
+const arrStr = {
+ type: 'array',
+ items: string
+};
+const browserAction = {
+ type: 'object',
+ properties: { ...actionProps,
+ // rest are FF only
+ default_area: {
+ type: 'string',
+ enum: ['navbar', 'menupanel', 'tabstrip', 'personaltoolbar']
+ },
+ theme_icons: {
+ type: 'array',
+ items: {
+ type: 'object',
+ properties: {
+ light: string,
+ dark: string,
+ size: {
+ type: 'number'
+ }
+ },
+ additionalProperties: false,
+ required: ['light', 'dark', 'size']
+ }
+ }
+ },
+ additionalProperties: false
+};
+const warBase = {
+ type: 'object',
+ properties: {
+ resources: arrStr,
+ matches: arrStr,
+ extension_ids: arrStr,
+ use_dynamic_url: boolean
+ },
+ additionalProperties: false
+};
+const commonProps = {
+ name: string,
+ version: {
+ type: 'string',
+ __validate: validateVersion
+ },
+ default_locale: string,
+ description: string,
+ icons,
+ author: string,
+ chrome_settings_overrides: {
+ type: 'object',
+ properties: {
+ homepage: string,
+ search_provider: {
+ type: 'object',
+ properties: {
+ name: string,
+ keyword: string,
+ favicon_url: string,
+ search_url: string,
+ encoding: string,
+ suggest_url: string,
+ image_url: string,
+ instant_url: string,
+ search_url_post_params: string,
+ suggest_url_post_params: string,
+ image_url_post_params: string,
+ instant_url_post_params: string,
+ alternate_urls: arrStr,
+ prepopulated_id: {
+ type: 'number'
+ },
+ is_default: boolean
+ },
+ additionalProperties: false,
+ required: ['name', 'search_url']
+ },
+ startup_pages: arrStr
+ },
+ additionalProperties: false
+ },
+ chrome_url_overrides: {
+ type: 'object',
+ properties: {
+ bookmarks: string,
+ history: string,
+ newtab: string
+ },
+ additionalProperties: false
+ },
+ commands: {
+ type: 'object',
+ properties: {},
+ additionalProperties: {
+ type: 'object',
+ properties: {
+ suggested_key: {
+ type: 'object',
+ properties: {
+ default: string,
+ mac: string,
+ linux: string,
+ windows: string,
+ chromeos: string,
+ android: string,
+ ios: string
+ },
+ additionalProperties: false
+ },
+ description: string
+ },
+ additionalProperties: false
+ }
+ },
+ content_scripts: {
+ type: 'array',
+ items: {
+ type: 'object',
+ properties: {
+ matches: arrStr,
+ css: arrStr,
+ js: arrStr,
+ match_about_blank: boolean,
+ exclude_matches: arrStr,
+ include_globs: arrStr,
+ exclude_globs: arrStr,
+ run_at: {
+ type: 'string',
+ enum: ['document_idle', 'document_start', 'document_end']
+ },
+ all_frames: boolean
+ },
+ additionalProperties: false,
+ required: ['matches']
+ }
+ },
+ devtools_page: string,
+ // looks to be FF only
+ dictionaries: {
+ type: 'object',
+ properties: {},
+ additionalProperties: string
+ },
+ externally_connectable: {
+ type: 'object',
+ properties: {
+ ids: arrStr,
+ matches: arrStr,
+ accept_tls_channel_id: boolean
+ },
+ additionalProperties: false
+ },
+ // These next two are where it gets a bit Chrome-y
+ // (we don't include all because some have next to no actual use)
+ file_browser_handlers: {
+ type: 'array',
+ items: {
+ type: 'object',
+ properties: {
+ id: string,
+ default_title: string,
+ file_filters: arrStr
+ },
+ additionalProperties: false,
+ required: ['id', 'default_title', 'file_filters']
+ }
+ },
+ file_system_provider_capabilities: {
+ type: 'object',
+ properties: {
+ configurable: boolean,
+ multiple_mounts: boolean,
+ watchable: boolean,
+ source: {
+ type: 'string',
+ enum: ['file', 'device', 'network']
+ }
+ },
+ additionalProperties: false,
+ required: ['source']
+ },
+ homepage_url: string,
+ incognito: {
+ type: 'string',
+ enum: ['spanning', 'split', 'not_allowed']
+ },
+ minimum_chrome_version: {
+ type: 'string',
+ __validate: validateVersion
+ },
+ // No NaCl modules because deprecated
+ offline_enabled: boolean,
+ omnibox: {
+ type: 'object',
+ properties: {},
+ additionalProperties: string
+ },
+ optional_permissions: arrStr,
+ // options_page is deprecated
+ options_ui: {
+ type: 'object',
+ properties: {
+ browser_style: boolean,
+ open_in_tab: boolean,
+ page: string
+ },
+ additionalProperties: false,
+ required: ['page']
+ },
+ permissions: arrStr,
+ // FF only, but has some use
+ protocol_handlers: {
+ type: 'array',
+ items: {
+ type: 'object',
+ properties: {
+ protocol: string,
+ name: string,
+ uriTemplate: string
+ },
+ additionalProperties: false,
+ required: ['protocol', 'name', 'uriTemplate']
+ }
+ },
+ // Chrome only
+ requirements: {
+ type: 'object',
+ properties: {
+ '3D': {
+ type: 'object',
+ properties: {
+ features: arrStr
+ },
+ additionalProperties: false
+ }
+ }
+ },
+ // sandbox is deprecated
+ short_name: string,
+ // FF only, but has some use
+ sidebar_action: {
+ type: 'object',
+ properties: {
+ browser_style: actionProps.browser_style,
+ default_icon: actionProps.default_icon,
+ default_panel: string,
+ default_title: string,
+ open_at_install: boolean
+ },
+ additionalProperties: false,
+ required: ['default_panel']
+ },
+ storage: {
+ type: 'object',
+ properties: {
+ managed_schema: string
+ },
+ additionalProperties: false
+ },
+ theme: {
+ type: 'object',
+ properties: {
+ images: {
+ type: 'object',
+ properties: {
+ theme_frame: string,
+ additional_backgrounds: arrStr
+ },
+ additionalProperties: false
+ },
+ colors: {
+ type: 'object',
+ properties: {
+ bookmark_text: string,
+ button_background_active: string,
+ button_background_hover: string,
+ icons: string,
+ icons_attention: string,
+ frame: string,
+ frame_inactive: string,
+ ntp_background: string,
+ ntp_text: string,
+ popup: string,
+ popup_border: string,
+ popup_highlight: string,
+ popup_highlight_text: string,
+ popup_text: string,
+ sidebar: string,
+ sidebar_border: string,
+ sidebar_highlight: string,
+ sidebar_highlight_text: string,
+ sidebar_text: string,
+ tab_background_separator: string,
+ tab_background_text: string,
+ tab_line: string,
+ tab_loading: string,
+ tab_selected: string,
+ tab_text: string,
+ toolbar: string,
+ toolbar_bottom_separator: string,
+ toolbar_field: string,
+ toolbar_field_border: string,
+ toolbar_field_border_focus: string,
+ toolbar_field_focus: string,
+ toolbar_field_highlight: string,
+ toolbar_field_highlight_text: string,
+ toolbar_field_separator: string,
+ toolbar_field_text: string,
+ toolbar_field_text_focus: string,
+ toolbar_text: string,
+ toolbar_top_separator: string,
+ toolbar_vertical_separator: string
+ },
+ additionalProperties: false
+ },
+ properties: {
+ type: 'object',
+ properties: {
+ additional_backgrounds_alignment: arrStr,
+ additional_backgrounds_tiling: {
+ type: 'array',
+ items: {
+ type: 'string',
+ enum: ['no-repeat', 'repeat', 'repeat-x', 'repeat-y']
+ }
+ }
+ },
+ additionalProperties: false
+ }
+ },
+ additionalProperties: false,
+ required: ['colors']
+ },
+ tts_engine: {
+ type: 'object',
+ properties: {
+ voices: {
+ type: 'array',
+ items: {
+ type: 'object',
+ properties: {
+ voice_name: string,
+ lang: string,
+ event_type: {
+ type: 'string',
+ enum: ['start', 'word', 'sentence', 'marker', 'end', 'error']
+ }
+ },
+ additionalProperties: false,
+ required: ['voice_name', 'event_type']
+ }
+ }
+ },
+ additionalProperties: false
+ },
+ user_scripts: {
+ type: 'object',
+ properties: {
+ api_script: string
+ },
+ additionalProperties: false
+ },
+ version_name: string
+};
+const MV3Schema = {
+ type: 'object',
+ properties: { ...commonProps,
+ manifest_version: {
+ type: 'number',
+ enum: [3]
+ },
+ action: browserAction,
+ background: {
+ type: 'object',
+ properties: {
+ service_worker: string,
+ type: {
+ type: 'string',
+ enum: ['classic', 'module']
+ }
+ },
+ additionalProperties: false,
+ required: ['service_worker']
+ },
+ content_security_policy: {
+ type: 'object',
+ properties: {
+ extension_pages: string,
+ sandbox: string
+ },
+ additionalProperties: false
+ },
+ host_permissions: arrStr,
+ web_accessible_resources: {
+ type: 'array',
+ items: {
+ oneOf: [{ ...warBase,
+ required: ['resources', 'matches']
+ }, { ...warBase,
+ required: ['resources', 'extension_ids']
+ }]
+ }
+ }
+ },
+ additionalProperties: false
+};
+exports.MV3Schema = MV3Schema;
+const MV2Schema = {
+ type: 'object',
+ properties: { ...commonProps,
+ manifest_version: {
+ type: 'number',
+ enum: [2]
+ },
+ background: {
+ type: 'object',
+ properties: {
+ scripts: arrStr,
+ page: string,
+ persistent: boolean
+ },
+ additionalProperties: false
+ },
+ browser_action: browserAction,
+ page_action: {
+ type: 'object',
+ properties: { ...actionProps,
+ // rest are FF only
+ hide_matches: arrStr,
+ show_matches: arrStr,
+ pinned: boolean
+ },
+ additionalProperties: false
+ },
+ content_security_policy: string,
+ web_accessible_resources: arrStr
+ },
+ additionalProperties: false
+};
+exports.MV2Schema = MV2Schema;
+const VersionSchema = {
+ type: 'object',
+ properties: {
+ manifest_version: {
+ type: 'number',
+ enum: [2, 3]
+ }
+ }
+};
+exports.VersionSchema = VersionSchema;
\ No newline at end of file
diff --git a/node_modules/@parcel/transformer-webextension/webextension/src/WebExtensionTransformer.js b/node_modules/@parcel/transformer-webextension/webextension/src/WebExtensionTransformer.js
new file mode 100644
index 0000000..f42ac1d
--- /dev/null
+++ b/node_modules/@parcel/transformer-webextension/webextension/src/WebExtensionTransformer.js
@@ -0,0 +1,384 @@
+// @flow
+import type {MutableAsset} from '@parcel/types';
+
+import {Transformer} from '@parcel/plugin';
+import path from 'path';
+import jsm from 'json-source-map';
+import parseCSP from 'content-security-policy-parser';
+import {validateSchema, relativePath} from '@parcel/utils';
+import ThrowableDiagnostic, {
+ getJSONSourceLocation,
+ md,
+} from '@parcel/diagnostic';
+import {glob} from '@parcel/utils';
+import {MV3Schema, MV2Schema, VersionSchema} from './schema';
+
+const DEP_LOCS = [
+ ['icons'],
+ ['browser_action', 'default_icon'],
+ ['browser_action', 'default_popup'],
+ ['page_action', 'default_icon'],
+ ['page_action', 'default_popup'],
+ ['action', 'default_icon'],
+ ['action', 'default_popup'],
+ ['background', 'scripts'],
+ ['chrome_url_overrides'],
+ ['devtools_page'],
+ ['options_ui', 'page'],
+ ['sidebar_action', 'default_icon'],
+ ['sidebar_action', 'default_panel'],
+ ['storage', 'managed_schema'],
+ ['theme', 'images', 'theme_frame'],
+ ['theme', 'images', 'additional_backgrounds'],
+ ['user_scripts', 'api_script'],
+];
+
+async function collectDependencies(
+ asset: MutableAsset,
+ program: any,
+ ptrs: {[key: string]: any, ...},
+ hot: boolean,
+) {
+ const fs = asset.fs;
+ const filePath = asset.filePath;
+ const assetDir = path.dirname(filePath);
+ const isMV2 = program.manifest_version == 2;
+ if (program.default_locale) {
+ const locales = path.join(assetDir, '_locales');
+ let err = !(await fs.exists(locales))
+ ? 'key'
+ : !(await fs.exists(path.join(locales, program.default_locale)))
+ ? 'value'
+ : null;
+ if (err) {
+ throw new ThrowableDiagnostic({
+ diagnostic: [
+ {
+ message: 'Invalid Web Extension manifest',
+ origin: '@parcel/transformer-webextension',
+ codeFrames: [
+ {
+ filePath,
+ codeHighlights: [
+ {
+ ...getJSONSourceLocation(ptrs['/default_locale'], err),
+ message: md`Localization directory${
+ err == 'value' ? ' for ' + program.default_locale : ''
+ } does not exist: ${path.relative(
+ assetDir,
+ path.join(locales, program.default_locale),
+ )}`,
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ });
+ }
+ for (const locale of await fs.readdir(locales)) {
+ if (await fs.exists(path.join(locales, locale))) {
+ asset.addURLDependency(`_locales/${locale}/messages.json`, {
+ needsStableName: true,
+ pipeline: 'raw',
+ });
+ }
+ }
+ }
+ let needRuntimeBG = false;
+ if (program.content_scripts) {
+ for (let i = 0; i < program.content_scripts.length; ++i) {
+ const sc = program.content_scripts[i];
+ for (const k of ['css', 'js']) {
+ const assets = sc[k] || [];
+ for (let j = 0; j < assets.length; ++j) {
+ if (k == 'js') {
+ asset.invalidateOnFileChange(path.join(assetDir, assets[j]));
+ }
+ assets[j] = asset.addURLDependency(assets[j], {
+ loc: {
+ filePath,
+ ...getJSONSourceLocation(
+ ptrs[`/content_scripts/${i}/${k}/${j}`],
+ 'value',
+ ),
+ },
+ });
+ }
+ }
+ if (hot && sc.js && sc.js.length) {
+ needRuntimeBG = true;
+ sc.js.push(
+ asset.addURLDependency('./runtime/autoreload.js', {
+ resolveFrom: __filename,
+ }),
+ );
+ }
+ }
+ }
+ if (program.dictionaries) {
+ for (const dict in program.dictionaries) {
+ const sourceLoc = getJSONSourceLocation(
+ ptrs[`/dictionaries/${dict}`],
+ 'value',
+ );
+ const loc = {
+ filePath,
+ ...sourceLoc,
+ };
+ const dictFile = program.dictionaries[dict];
+ if (path.extname(dictFile) != '.dic') {
+ throw new ThrowableDiagnostic({
+ diagnostic: [
+ {
+ message: 'Invalid Web Extension manifest',
+ origin: '@parcel/transformer-webextension',
+ codeFrames: [
+ {
+ filePath,
+ codeHighlights: [
+ {
+ ...sourceLoc,
+ message: 'Dictionaries must be .dic files',
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ });
+ }
+ program.dictionaries[dict] = asset.addURLDependency(dictFile, {
+ needsStableName: true,
+ loc,
+ });
+ asset.addURLDependency(dictFile.slice(0, -4) + '.aff', {
+ needsStableName: true,
+ loc,
+ });
+ }
+ }
+ const browserActionName = isMV2 ? 'browser_action' : 'action';
+ if (program[browserActionName]?.theme_icons) {
+ for (let i = 0; i < program[browserActionName].theme_icons.length; ++i) {
+ const themeIcon = program[browserActionName].theme_icons[i];
+ for (const k of ['light', 'dark']) {
+ const loc = getJSONSourceLocation(
+ ptrs[`/${browserActionName}/theme_icons/${i}/${k}`],
+ 'value',
+ );
+ themeIcon[k] = asset.addURLDependency(themeIcon[k], {
+ loc: {
+ ...loc,
+ filePath,
+ },
+ });
+ }
+ }
+ }
+ if (program.web_accessible_resources) {
+ let war = [];
+ for (let i = 0; i < program.web_accessible_resources.length; ++i) {
+ // TODO: this doesn't support Parcel resolution
+ const currentEntry = program.web_accessible_resources[i];
+ const files = isMV2 ? [currentEntry] : currentEntry.resources;
+ for (let j = 0; j < files.length; ++j) {
+ const globFiles = (
+ await glob(path.join(assetDir, files[j]), fs, {})
+ ).map(fp =>
+ asset.addURLDependency(path.relative(assetDir, fp), {
+ needsStableName: true,
+ loc: {
+ filePath,
+ ...getJSONSourceLocation(
+ ptrs[
+ `/web_accessible_resources/${i}${
+ isMV2 ? '' : `/resources/${j}`
+ }`
+ ],
+ ),
+ },
+ }),
+ );
+ if (isMV2) {
+ war = war.concat(globFiles);
+ } else {
+ currentEntry.resources = globFiles;
+ war.push(currentEntry);
+ }
+ }
+ }
+ program.web_accessible_resources = war;
+ }
+ for (const loc of DEP_LOCS) {
+ const location = '/' + loc.join('/');
+ if (!ptrs[location]) continue;
+ let parent: any = program;
+ for (let i = 0; i < loc.length - 1; ++i) {
+ parent = parent[loc[i]];
+ }
+ const lastLoc = loc[loc.length - 1];
+ const obj = parent[lastLoc];
+ if (typeof obj == 'string')
+ parent[lastLoc] = asset.addURLDependency(obj, {
+ loc: {
+ filePath,
+ ...getJSONSourceLocation(ptrs[location], 'value'),
+ },
+ pipeline: path.extname(obj) == '.json' ? 'raw' : undefined,
+ });
+ else {
+ for (const k of Object.keys(obj)) {
+ obj[k] = asset.addURLDependency(obj[k], {
+ loc: {
+ filePath,
+ ...getJSONSourceLocation(ptrs[location + '/' + k], 'value'),
+ },
+ pipeline: path.extname(obj[k]) == '.json' ? 'raw' : undefined,
+ });
+ }
+ }
+ }
+ if (isMV2) {
+ if (program.background?.page) {
+ program.background.page = asset.addURLDependency(
+ program.background.page,
+ {
+ loc: {
+ filePath,
+ ...getJSONSourceLocation(ptrs['/background/page'], 'value'),
+ },
+ },
+ );
+ if (needRuntimeBG) {
+ asset.meta.webextBGInsert = program.background.page;
+ }
+ }
+ if (hot) {
+ // To enable HMR, we must override the CSP to allow 'unsafe-eval'
+ program.content_security_policy = cspPatchHMR(
+ program.content_security_policy,
+ );
+
+ if (needRuntimeBG && !program.background?.page) {
+ if (!program.background) {
+ program.background = {};
+ }
+ if (!program.background.scripts) {
+ program.background.scripts = [];
+ }
+ if (program.background.scripts.length == 0) {
+ program.background.scripts.push(
+ asset.addURLDependency('./runtime/default-bg.js', {
+ resolveFrom: __filename,
+ }),
+ );
+ }
+ asset.meta.webextBGInsert = program.background.scripts[0];
+ }
+ }
+ } else {
+ if (program.background?.service_worker) {
+ program.background.service_worker = asset.addURLDependency(
+ program.background.service_worker,
+ {
+ loc: {
+ filePath,
+ ...getJSONSourceLocation(
+ ptrs['/background/service_worker'],
+ 'value',
+ ),
+ },
+ env: {
+ context: 'service-worker',
+ sourceType:
+ program.background.type == 'module' ? 'module' : 'script',
+ },
+ },
+ );
+ }
+ if (needRuntimeBG) {
+ if (!program.background) {
+ program.background = {};
+ }
+ if (!program.background.service_worker) {
+ program.background.service_worker = asset.addURLDependency(
+ './runtime/default-bg.js',
+ {
+ resolveFrom: __filename,
+ env: {context: 'service-worker'},
+ },
+ );
+ }
+ asset.meta.webextBGInsert = program.background.service_worker;
+ }
+ }
+}
+
+function cspPatchHMR(policy: ?string) {
+ if (policy) {
+ const csp = parseCSP(policy);
+ policy = '';
+ if (!csp['script-src']) {
+ csp['script-src'] = ["'self' 'unsafe-eval' blob: filesystem:"];
+ }
+ if (!csp['script-src'].includes("'unsafe-eval'")) {
+ csp['script-src'].push("'unsafe-eval'");
+ }
+ for (const k in csp) {
+ policy += `${k} ${csp[k].join(' ')};`;
+ }
+ return policy;
+ } else {
+ return (
+ "script-src 'self' 'unsafe-eval' blob: filesystem:;" +
+ "object-src 'self' blob: filesystem:;"
+ );
+ }
+}
+
+export default (new Transformer({
+ async transform({asset, options}) {
+ const code = await asset.getCode();
+ const parsed = jsm.parse(code);
+ const data: any = parsed.data;
+
+ // Not using a unified schema dramatically improves error messages
+ let schema = VersionSchema;
+ if (data.manifest_version === 3) {
+ schema = MV3Schema;
+ } else if (data.manifest_version === 2) {
+ schema = MV2Schema;
+ }
+
+ validateSchema.diagnostic(
+ schema,
+ {
+ data: data,
+ source: code,
+ filePath: asset.filePath,
+ },
+ '@parcel/transformer-webextension',
+ 'Invalid Web Extension manifest',
+ );
+ asset.setEnvironment({
+ context: 'browser',
+ engines: asset.env.engines,
+ shouldOptimize: asset.env.shouldOptimize,
+ sourceMap: {
+ ...asset.env.sourceMap,
+ inline: true,
+ inlineSources: true,
+ },
+ });
+ await collectDependencies(
+ asset,
+ data,
+ parsed.pointers,
+ Boolean(options.hmrOptions),
+ );
+ asset.setCode(JSON.stringify(data, null, 2));
+ asset.meta.webextEntry = true;
+ return [asset];
+ },
+}): Transformer);
diff --git a/node_modules/@parcel/transformer-webextension/webextension/src/runtime/autoreload.js b/node_modules/@parcel/transformer-webextension/webextension/src/runtime/autoreload.js
new file mode 100644
index 0000000..df9da7a
--- /dev/null
+++ b/node_modules/@parcel/transformer-webextension/webextension/src/runtime/autoreload.js
@@ -0,0 +1,11 @@
+/* global chrome, browser, addEventListener */
+var env = typeof chrome == 'undefined' ? browser : chrome;
+addEventListener('beforeunload', function () {
+ try {
+ env.runtime.sendMessage({
+ __parcel_hmr_reload__: true,
+ });
+ } catch (err) {
+ // ignore throwing if extension context invalidated
+ }
+});
diff --git a/node_modules/@parcel/transformer-webextension/webextension/src/runtime/default-bg.js b/node_modules/@parcel/transformer-webextension/webextension/src/runtime/default-bg.js
new file mode 100644
index 0000000..e69de29
diff --git a/node_modules/@parcel/transformer-webextension/webextension/src/schema.js b/node_modules/@parcel/transformer-webextension/webextension/src/schema.js
new file mode 100644
index 0000000..8c06dc6
--- /dev/null
+++ b/node_modules/@parcel/transformer-webextension/webextension/src/schema.js
@@ -0,0 +1,494 @@
+// @flow strict-local
+import type {SchemaEntity} from '@parcel/utils';
+
+const validateVersion = (ver: string): ?string => {
+ const parts = ver.split('.', 5);
+ if (parts.length == 5) return 'Extension versions to have at most three dots';
+ if (
+ parts.every(
+ part => part.length != 0 && Number(part[0]) >= 0 && Number(part) < 65536,
+ )
+ )
+ return;
+ return 'Extension versions must be dot-separated integers between 0 and 65535';
+};
+
+const string: SchemaEntity = {type: 'string'};
+const boolean: SchemaEntity = {type: 'boolean'};
+
+const icons: SchemaEntity = {
+ type: 'object',
+ properties: {},
+ additionalProperties: string,
+};
+
+const actionProps = {
+ // FF only
+ browser_style: boolean,
+ // You can also have a raw string, but not in Edge, apparently...
+ default_icon: {
+ oneOf: [icons, string],
+ },
+ default_popup: string,
+ default_title: string,
+};
+
+const arrStr = {
+ type: 'array',
+ items: string,
+};
+
+const browserAction = {
+ type: 'object',
+ properties: {
+ ...actionProps,
+ // rest are FF only
+ default_area: {
+ type: 'string',
+ enum: ['navbar', 'menupanel', 'tabstrip', 'personaltoolbar'],
+ },
+ theme_icons: {
+ type: 'array',
+ items: {
+ type: 'object',
+ properties: {
+ light: string,
+ dark: string,
+ size: {type: 'number'},
+ },
+ additionalProperties: false,
+ required: ['light', 'dark', 'size'],
+ },
+ },
+ },
+ additionalProperties: false,
+};
+
+const warBase = {
+ type: 'object',
+ properties: {
+ resources: arrStr,
+ matches: arrStr,
+ extension_ids: arrStr,
+ use_dynamic_url: boolean,
+ },
+ additionalProperties: false,
+};
+
+const commonProps = {
+ name: string,
+ version: {
+ type: 'string',
+ __validate: validateVersion,
+ },
+ default_locale: string,
+ description: string,
+ icons,
+ author: string,
+ chrome_settings_overrides: {
+ type: 'object',
+ properties: {
+ homepage: string,
+ search_provider: {
+ type: 'object',
+ properties: {
+ name: string,
+ keyword: string,
+ favicon_url: string,
+ search_url: string,
+ encoding: string,
+ suggest_url: string,
+ image_url: string,
+ instant_url: string,
+ search_url_post_params: string,
+ suggest_url_post_params: string,
+ image_url_post_params: string,
+ instant_url_post_params: string,
+ alternate_urls: arrStr,
+ prepopulated_id: {type: 'number'},
+ is_default: boolean,
+ },
+ additionalProperties: false,
+ required: ['name', 'search_url'],
+ },
+ startup_pages: arrStr,
+ },
+ additionalProperties: false,
+ },
+ chrome_url_overrides: {
+ type: 'object',
+ properties: {
+ bookmarks: string,
+ history: string,
+ newtab: string,
+ },
+ additionalProperties: false,
+ },
+ commands: ({
+ type: 'object',
+ properties: {},
+ additionalProperties: {
+ type: 'object',
+ properties: {
+ suggested_key: {
+ type: 'object',
+ properties: {
+ default: string,
+ mac: string,
+ linux: string,
+ windows: string,
+ chromeos: string,
+ android: string,
+ ios: string,
+ },
+ additionalProperties: false,
+ },
+ description: string,
+ },
+ additionalProperties: false,
+ },
+ }: SchemaEntity),
+ content_scripts: {
+ type: 'array',
+ items: {
+ type: 'object',
+ properties: {
+ matches: arrStr,
+ css: arrStr,
+ js: arrStr,
+ match_about_blank: boolean,
+ exclude_matches: arrStr,
+ include_globs: arrStr,
+ exclude_globs: arrStr,
+ run_at: {
+ type: 'string',
+ enum: ['document_idle', 'document_start', 'document_end'],
+ },
+ all_frames: boolean,
+ },
+ additionalProperties: false,
+ required: ['matches'],
+ },
+ },
+ devtools_page: string,
+ // looks to be FF only
+ dictionaries: ({
+ type: 'object',
+ properties: {},
+ additionalProperties: string,
+ }: SchemaEntity),
+ externally_connectable: {
+ type: 'object',
+ properties: {
+ ids: arrStr,
+ matches: arrStr,
+ accept_tls_channel_id: boolean,
+ },
+ additionalProperties: false,
+ },
+ // These next two are where it gets a bit Chrome-y
+ // (we don't include all because some have next to no actual use)
+ file_browser_handlers: {
+ type: 'array',
+ items: {
+ type: 'object',
+ properties: {
+ id: string,
+ default_title: string,
+ file_filters: arrStr,
+ },
+ additionalProperties: false,
+ required: ['id', 'default_title', 'file_filters'],
+ },
+ },
+ file_system_provider_capabilities: {
+ type: 'object',
+ properties: {
+ configurable: boolean,
+ multiple_mounts: boolean,
+ watchable: boolean,
+ source: {
+ type: 'string',
+ enum: ['file', 'device', 'network'],
+ },
+ },
+ additionalProperties: false,
+ required: ['source'],
+ },
+ homepage_url: string,
+ incognito: {
+ type: 'string',
+ enum: ['spanning', 'split', 'not_allowed'],
+ },
+ minimum_chrome_version: {
+ type: 'string',
+ __validate: validateVersion,
+ },
+ // No NaCl modules because deprecated
+ offline_enabled: boolean,
+ omnibox: ({
+ type: 'object',
+ properties: {},
+ additionalProperties: string,
+ }: SchemaEntity),
+ optional_permissions: arrStr,
+ // options_page is deprecated
+ options_ui: {
+ type: 'object',
+ properties: {
+ browser_style: boolean,
+ open_in_tab: boolean,
+ page: string,
+ },
+ additionalProperties: false,
+ required: ['page'],
+ },
+ permissions: arrStr,
+ // FF only, but has some use
+ protocol_handlers: {
+ type: 'array',
+ items: {
+ type: 'object',
+ properties: {
+ protocol: string,
+ name: string,
+ uriTemplate: string,
+ },
+ additionalProperties: false,
+ required: ['protocol', 'name', 'uriTemplate'],
+ },
+ },
+ // Chrome only
+ requirements: {
+ type: 'object',
+ properties: {
+ '3D': {
+ type: 'object',
+ properties: {
+ features: arrStr,
+ },
+ additionalProperties: false,
+ },
+ },
+ },
+ // sandbox is deprecated
+ short_name: string,
+ // FF only, but has some use
+ sidebar_action: {
+ type: 'object',
+ properties: {
+ browser_style: actionProps.browser_style,
+ default_icon: actionProps.default_icon,
+ default_panel: string,
+ default_title: string,
+ open_at_install: boolean,
+ },
+ additionalProperties: false,
+ required: ['default_panel'],
+ },
+ storage: {
+ type: 'object',
+ properties: {
+ managed_schema: string,
+ },
+ additionalProperties: false,
+ },
+ theme: {
+ type: 'object',
+ properties: {
+ images: {
+ type: 'object',
+ properties: {
+ theme_frame: string,
+ additional_backgrounds: arrStr,
+ },
+ additionalProperties: false,
+ },
+ colors: {
+ type: 'object',
+ properties: {
+ bookmark_text: string,
+ button_background_active: string,
+ button_background_hover: string,
+ icons: string,
+ icons_attention: string,
+ frame: string,
+ frame_inactive: string,
+ ntp_background: string,
+ ntp_text: string,
+ popup: string,
+ popup_border: string,
+ popup_highlight: string,
+ popup_highlight_text: string,
+ popup_text: string,
+ sidebar: string,
+ sidebar_border: string,
+ sidebar_highlight: string,
+ sidebar_highlight_text: string,
+ sidebar_text: string,
+ tab_background_separator: string,
+ tab_background_text: string,
+ tab_line: string,
+ tab_loading: string,
+ tab_selected: string,
+ tab_text: string,
+ toolbar: string,
+ toolbar_bottom_separator: string,
+ toolbar_field: string,
+ toolbar_field_border: string,
+ toolbar_field_border_focus: string,
+ toolbar_field_focus: string,
+ toolbar_field_highlight: string,
+ toolbar_field_highlight_text: string,
+ toolbar_field_separator: string,
+ toolbar_field_text: string,
+ toolbar_field_text_focus: string,
+ toolbar_text: string,
+ toolbar_top_separator: string,
+ toolbar_vertical_separator: string,
+ },
+ additionalProperties: false,
+ },
+ properties: {
+ type: 'object',
+ properties: {
+ additional_backgrounds_alignment: arrStr,
+ additional_backgrounds_tiling: {
+ type: 'array',
+ items: {
+ type: 'string',
+ enum: ['no-repeat', 'repeat', 'repeat-x', 'repeat-y'],
+ },
+ },
+ },
+ additionalProperties: false,
+ },
+ },
+ additionalProperties: false,
+ required: ['colors'],
+ },
+ tts_engine: {
+ type: 'object',
+ properties: {
+ voices: {
+ type: 'array',
+ items: {
+ type: 'object',
+ properties: {
+ voice_name: string,
+ lang: string,
+ event_type: {
+ type: 'string',
+ enum: ['start', 'word', 'sentence', 'marker', 'end', 'error'],
+ },
+ },
+ additionalProperties: false,
+ required: ['voice_name', 'event_type'],
+ },
+ },
+ },
+ additionalProperties: false,
+ },
+ user_scripts: {
+ type: 'object',
+ properties: {
+ api_script: string,
+ },
+ additionalProperties: false,
+ },
+ version_name: string,
+};
+
+export const MV3Schema = ({
+ type: 'object',
+ properties: {
+ ...commonProps,
+ manifest_version: {
+ type: 'number',
+ enum: [3],
+ },
+ action: browserAction,
+ background: {
+ type: 'object',
+ properties: {
+ service_worker: string,
+ type: {
+ type: 'string',
+ enum: ['classic', 'module'],
+ },
+ },
+ additionalProperties: false,
+ required: ['service_worker'],
+ },
+ content_security_policy: {
+ type: 'object',
+ properties: {
+ extension_pages: string,
+ sandbox: string,
+ },
+ additionalProperties: false,
+ },
+ host_permissions: arrStr,
+ web_accessible_resources: {
+ type: 'array',
+ items: {
+ oneOf: [
+ {
+ ...warBase,
+ required: ['resources', 'matches'],
+ },
+ {
+ ...warBase,
+ required: ['resources', 'extension_ids'],
+ },
+ ],
+ },
+ },
+ },
+ additionalProperties: false,
+}: SchemaEntity);
+
+export const MV2Schema = ({
+ type: 'object',
+ properties: {
+ ...commonProps,
+ manifest_version: {
+ type: 'number',
+ enum: [2],
+ },
+ background: {
+ type: 'object',
+ properties: {
+ scripts: arrStr,
+ page: string,
+ persistent: boolean,
+ },
+ additionalProperties: false,
+ },
+ browser_action: browserAction,
+ page_action: {
+ type: 'object',
+ properties: {
+ ...actionProps,
+ // rest are FF only
+ hide_matches: arrStr,
+ show_matches: arrStr,
+ pinned: boolean,
+ },
+ additionalProperties: false,
+ },
+ content_security_policy: string,
+ web_accessible_resources: arrStr,
+ },
+ additionalProperties: false,
+}: SchemaEntity);
+
+export const VersionSchema = ({
+ type: 'object',
+ properties: {
+ manifest_version: {
+ type: 'number',
+ enum: [2, 3],
+ },
+ },
+}: SchemaEntity);
@irgendwr
Copy link
Author

Credit for this goes to 101arrowz. The code originates from the pull-request here.

An explanation on how to apply this can be found here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment