diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5d181bb..2132400 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,8 +13,8 @@ jobs: Lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2 + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 with: node-version: 16 - name: install @@ -25,7 +25,7 @@ jobs: Test: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: install run: npm ci || npm install - name: build @@ -36,7 +36,7 @@ jobs: Build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: install run: npm ci || npm install - name: build diff --git a/.github/workflows/esm-lint.yml b/.github/workflows/esm-lint.yml index 0205cd9..f5802fd 100644 --- a/.github/workflows/esm-lint.yml +++ b/.github/workflows/esm-lint.yml @@ -4,6 +4,7 @@ env: # FILE GENERATED WITH: npx ghat fregante/ghatemplates/esm-lint # SOURCE: https://github.com/fregante/ghatemplates +# OPTIONS: {"exclude":["jobs.Rollup"]} name: ESM on: @@ -18,19 +19,19 @@ jobs: Pack: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - run: npm install - run: npm run build --if-present - run: npm pack --dry-run - run: npm pack | tail -1 | xargs -n1 tar -xzf - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: path: package Webpack: runs-on: ubuntu-latest needs: Pack steps: - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@v3 - run: npm install ./artifact - run: echo "${{ env.IMPORT_TEXT }} '${{ env.NPM_MODULE_NAME }}'" > index.js - run: webpack --entry ./index.js @@ -39,35 +40,37 @@ jobs: runs-on: ubuntu-latest needs: Pack steps: - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@v3 - run: npm install ./artifact - run: echo "${{ env.IMPORT_TEXT }} '${{ env.NPM_MODULE_NAME }}'" > index.js - - run: npx parcel build index.js + - run: npx parcel@2 build index.js - run: cat dist/index.js - Rollup: + Vite: runs-on: ubuntu-latest needs: Pack steps: - - uses: actions/download-artifact@v2 - - run: npm install ./artifact rollup@2 @rollup/plugin-node-resolve - - run: echo "${{ env.IMPORT_TEXT }} '${{ env.NPM_MODULE_NAME }}'" > index.js - - run: npx rollup -p node-resolve index.js - Snowpack: + - uses: actions/download-artifact@v3 + - run: npm install ./artifact + - run: >- + echo '' > index.html + - run: npx vite build + - run: cat dist/assets/* + esbuild: runs-on: ubuntu-latest needs: Pack steps: - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@v3 - run: echo '{}' > package.json - run: echo "${{ env.IMPORT_TEXT }} '${{ env.NPM_MODULE_NAME }}'" > index.js - run: npm install ./artifact - - run: npx snowpack@2 build - - run: cat build/web_modules/$NPM_MODULE_NAME.js + - run: npx esbuild --bundle index.js TypeScript: runs-on: ubuntu-latest needs: Pack steps: - - uses: actions/download-artifact@v2 - - run: npm install ./artifact + - uses: actions/download-artifact@v3 + - run: npm install ./artifact && npm install @types/estree - run: echo "${{ env.IMPORT_TEXT }} '${{ env.NPM_MODULE_NAME }}'" > index.ts - run: tsc index.ts - run: cat index.js @@ -75,8 +78,8 @@ jobs: runs-on: ubuntu-latest needs: Pack steps: - - uses: actions/download-artifact@v2 - - uses: actions/setup-node@v1 + - uses: actions/download-artifact@v3 + - uses: actions/setup-node@v3 with: node-version: 14.x - run: echo "${{ env.IMPORT_TEXT }} '${{ env.NPM_MODULE_NAME }}'" > index.mjs diff --git a/globals.d.ts b/globals.d.ts index 5db4dca..6b388a1 100644 --- a/globals.d.ts +++ b/globals.d.ts @@ -1,18 +1,14 @@ -declare module 'dom-form-serializer/lib/serialize' { +declare module 'dom-form-serializer/dist/dom-form-serializer.mjs' { import {JsonObject} from 'type-fest'; - export default function serialize( + export function serialize( element: HTMLFormElement, options: { include?: string[]; } ): JSONValue; -} - -declare module 'dom-form-serializer/lib/deserialize' { - import {JsonObject} from 'type-fest'; - export default function deserialize( + export function deserialize( element: HTMLFormElement, serializedData: JsonObject, options?: { diff --git a/index.ts b/index.ts index 3d1d18d..3ae4dcf 100644 --- a/index.ts +++ b/index.ts @@ -1,34 +1,28 @@ import {debounce} from 'throttle-debounce'; -import serialize from 'dom-form-serializer/lib/serialize'; -import deserialize from 'dom-form-serializer/lib/deserialize'; +import chromeP from 'webext-polyfill-kinda'; import {isBackground} from 'webext-detect-page'; -import {compressToEncodedURIComponent, decompressFromEncodedURIComponent} from 'lz-string'; +import {serialize, deserialize} from 'dom-form-serializer/dist/dom-form-serializer.mjs'; +import LZString from 'lz-string'; + +// eslint-disable-next-line @typescript-eslint/naming-convention -- CJS in ESM imports +const {compressToEncodedURIComponent, decompressFromEncodedURIComponent} = LZString; async function shouldRunMigrations(): Promise { + const self = await chromeP.management?.getSelf(); + + // Always run migrations during development #25 + if (self?.installType === 'development') { + return true; + } + return new Promise(resolve => { - const callback = (installType: string): void => { - // Always run migrations during development #25 - if (installType === 'development') { - resolve(true); - return; - } + // Run migrations when the extension is installed or updated + chrome.runtime.onInstalled.addListener(() => { + resolve(true); + }); - // Run migrations when the extension is installed or updated - chrome.runtime.onInstalled.addListener(() => { - resolve(true); - }); - - // If `onInstalled` isn't fired, then migrations should not be run - setTimeout(resolve, 500, false); - }; - - if (chrome.management?.getSelf) { - chrome.management.getSelf(({installType}) => { - callback(installType); - }); - } else { - callback('unknown'); - } + // If `onInstalled` isn't fired, then migrations should not be run + setTimeout(resolve, 500, false); }); } @@ -113,8 +107,6 @@ class OptionsSync { this.storageName = storageName; this.defaults = defaults; this.storageType = storageType; - this._handleFormInput = debounce(300, this._handleFormInput.bind(this)); - this._handleStorageChangeOnForm = this._handleStorageChangeOnForm.bind(this); if (!logging) { this._log = () => {}; @@ -123,8 +115,8 @@ class OptionsSync { this._migrations = this._runMigrations(migrations); } - get storage(): chrome.storage.StorageArea { - return chrome.storage[this.storageType]; + get storage(): chromeP.storage.StorageArea { + return chromeP.storage[this.storageType]; } /** @@ -198,29 +190,14 @@ class OptionsSync { } private async _getAll(): Promise { - return new Promise((resolve, reject) => { - this.storage.get(this.storageName, result => { - if (chrome.runtime.lastError) { - reject(chrome.runtime.lastError); - } else { - resolve(this._decode(result[this.storageName])); - } - }); - }); + const result = await this.storage.get(this.storageName); + return this._decode(result[this.storageName]); } private async _setAll(newOptions: UserOptions): Promise { this._log('log', 'Saving options', newOptions); - return new Promise((resolve, reject) => { - this.storage.set({ - [this.storageName]: this._encode(newOptions), - }, () => { - if (chrome.runtime.lastError) { - reject(chrome.runtime.lastError); - } else { - resolve(); - } - }); + await this.storage.set({ + [this.storageName]: this._encode(newOptions), }); } @@ -266,7 +243,8 @@ class OptionsSync { } } - private async _handleFormInput({target}: Event): Promise { + // eslint-disable-next-line @typescript-eslint/member-ordering -- Needs to be near _handleFormSubmit + private readonly _handleFormInput = debounce(300, async ({target}: Event): Promise => { const field = target as HTMLInputElement; if (!field.name) { return; @@ -276,7 +254,7 @@ class OptionsSync { field.form!.dispatchEvent(new CustomEvent('options-sync:form-synced', { bubbles: true, })); - } + }); private _handleFormSubmit(event: Event): void { event.preventDefault(); @@ -312,7 +290,7 @@ class OptionsSync { return serialize(form, {include}); } - private _handleStorageChangeOnForm(changes: Record, areaName: string): void { + private readonly _handleStorageChangeOnForm = (changes: Record, areaName: string): void => { if ( areaName === this.storageType && changes[this.storageName] @@ -320,7 +298,7 @@ class OptionsSync { ) { this._updateForm(this._form!, this._decode(changes[this.storageName].newValue)); } - } + }; } export default OptionsSync; diff --git a/package.json b/package.json index 0bd4910..308d324 100644 --- a/package.json +++ b/package.json @@ -16,16 +16,15 @@ "author": "Federico Brigante (https://fregante.com)", "type": "module", "main": "index.js", - "module": "index.js", "files": [ "index.js", "index.d.ts" ], "scripts": { - "build": "rollup -c", - "prepack": "rollup -c", + "build": "tsc", + "prepack": "tsc", "test": "run-p build ava xo", - "watch": "rollup -c --watch", + "watch": "tsc --watch", "ava": "ava", "xo": "xo" }, @@ -35,39 +34,35 @@ "webextensions" ], "rules": { - "@typescript-eslint/no-unsafe-call": "off", "@typescript-eslint/no-unsafe-return": "off", - "@typescript-eslint/no-unsafe-member-access": "off", - "@typescript-eslint/no-duplicate-imports": "off", "@typescript-eslint/no-dynamic-delete": "off", - "@typescript-eslint/no-empty-function": "off", - "import/no-duplicates": "off", - "import/no-unassigned-import": "off" + "@typescript-eslint/no-empty-function": "off" } }, + "ava": { + "require": [ + "./test/_env.js" + ] + }, "dependencies": { - "webext-detect-page": "^4.0.0" + "dom-form-serializer": "^2.0.0", + "lz-string": "^1.4.4", + "throttle-debounce": "^5.0.0", + "webext-detect-page": "^4.0.1", + "webext-polyfill-kinda": "^0.10.0" }, "devDependencies": { - "@rollup/plugin-commonjs": "^21.0.1", - "@rollup/plugin-node-resolve": "^13.1.3", - "@rollup/plugin-typescript": "^8.3.0", - "@sindresorhus/tsconfig": "^2.0.0", - "@types/chrome": "0.0.176", - "@types/estree": "^0.0.50", + "@sindresorhus/tsconfig": "^3.0.1", + "@types/chrome": "0.0.193", + "@types/firefox-webext-browser": "^94.0.1", "@types/lz-string": "^1.3.34", - "@types/throttle-debounce": "^2.1.0", - "ava": "^4.0.1", - "dom-form-serializer": "^2.0.0", - "lz-string": "^1.4.4", + "@types/throttle-debounce": "^5.0.0", + "ava": "^4.3.1", "npm-run-all": "^4.1.5", - "rollup": "^2.63.0", - "rollup-plugin-terser": "^7.0.2", - "sinon": "^12.0.1", + "sinon": "^14.0.0", "sinon-chrome": "^3.0.1", - "throttle-debounce": "^3.0.1", - "type-fest": "^2.9.0", - "typescript": "^4.5.4", - "xo": "^0.47.0" + "type-fest": "^2.17.0", + "typescript": "^4.7.4", + "xo": "^0.51.0" } } diff --git a/rollup.config.js b/rollup.config.js deleted file mode 100644 index e1238a2..0000000 --- a/rollup.config.js +++ /dev/null @@ -1,39 +0,0 @@ -import typescript from '@rollup/plugin-typescript'; -import {terser} from 'rollup-plugin-terser'; -import commonjs from '@rollup/plugin-commonjs'; -import resolve from '@rollup/plugin-node-resolve'; - -const config = { - input: 'index.ts', - output: { - format: 'esm', - dir: '.', - }, - external: [ - // These are `type: module` packages so they don't need to be bundled - 'webext-detect-page', - ], - plugins: [ - resolve(), - commonjs(), - typescript({ - outDir: '.', - }), - terser({ - toplevel: true, - output: { - comments: false, - beautify: true, - }, - mangle: false, - compress: { - join_vars: false, // eslint-disable-line camelcase - booleans: false, - expression: false, - sequences: false, - }, - }), - ], -}; - -export default config; diff --git a/test/_fixtures.js b/test/_env.js similarity index 100% rename from test/_fixtures.js rename to test/_env.js diff --git a/test/index.js b/test/index.js index 50d5750..c8315e8 100644 --- a/test/index.js +++ b/test/index.js @@ -1,4 +1,3 @@ -import './_fixtures.js'; import test from 'ava'; import lzString from 'lz-string'; import OptionsSync from '../index.js'; diff --git a/tsconfig.json b/tsconfig.json index 47a7f4d..6680c65 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,8 @@ { "extends": "@sindresorhus/tsconfig/tsconfig.json", "compilerOptions": { - "target": "es2020" + "target": "es2020", + "outDir": "." }, "files": [ "globals.d.ts",