Last active
March 24, 2022 20:21
-
-
Save irgendwr/09bfa9af38d6ef42572c0bbddd813242 to your computer and use it in GitHub Desktop.
A patch that enables Manifest v3 support in @parcel/transformer-webextension
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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); |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.