diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 9f25247..24d86ac 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -77,6 +77,9 @@ jobs:
- name: Build
run: npm run build
+ - name: Consumer typecheck
+ run: npm run test:consumer-types
+
- name: Upload coverage artifact
if: always()
uses: actions/upload-artifact@v4
diff --git a/package-lock.json b/package-lock.json
index d9d8088..c6dd9d4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,12 +8,14 @@
"name": "@addon-core/browser",
"version": "0.7.1",
"license": "MIT",
+ "dependencies": {
+ "@types/chrome": "^0.2.2"
+ },
"devDependencies": {
"@biomejs/biome": "^2.2.4",
"@commitlint/cli": "^20.0.0",
"@commitlint/config-conventional": "^20.0.0",
"@release-it/conventional-changelog": "^10.0.1",
- "@types/chrome": "^0.1.36",
"@types/jest": "^30.0.0",
"husky": "^9.1.7",
"jest": "^30.1.3",
@@ -21,9 +23,6 @@
"ts-jest": "^29.4.6",
"tsup": "^8.5.0",
"typescript": "^5.9.2"
- },
- "peerDependencies": {
- "@types/chrome": "*"
}
},
"node_modules/@babel/code-frame": {
@@ -3092,10 +3091,9 @@
}
},
"node_modules/@types/chrome": {
- "version": "0.1.36",
- "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.1.36.tgz",
- "integrity": "sha512-BvHbuyGttYXnGt5Gpwa4769KIinKHY1iLjlAPrrMBS2GI9m/XNMPtdsq0NgQalyuUdxvlMN/0OyGw0shFVIoUQ==",
- "dev": true,
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.2.2.tgz",
+ "integrity": "sha512-8rSMZ4cvo2xmaSyQg0sN5yRL7oiDkntLoiHxUhfwQnv1mvnkrdoZ25SlNrKWmYKaeP50WvrfWj1pmc02+U9KKw==",
"license": "MIT",
"dependencies": {
"@types/filesystem": "*",
@@ -3113,7 +3111,6 @@
"version": "0.0.36",
"resolved": "https://registry.npmjs.org/@types/filesystem/-/filesystem-0.0.36.tgz",
"integrity": "sha512-vPDXOZuannb9FZdxgHnqSwAG/jvdGM8Wq+6N4D/d80z+D4HWH+bItqsZaVRQykAn6WEVeEkLm2oQigyHtgb0RA==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@types/filewriter": "*"
@@ -3123,14 +3120,12 @@
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/@types/filewriter/-/filewriter-0.0.33.tgz",
"integrity": "sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g==",
- "dev": true,
"license": "MIT"
},
"node_modules/@types/har-format": {
"version": "1.2.16",
"resolved": "https://registry.npmjs.org/@types/har-format/-/har-format-1.2.16.tgz",
"integrity": "sha512-fluxdy7ryD3MV6h8pTfTYpy/xQzCFC7m89nOH9y94cNqJ1mDIDPut7MnRHI3F6qRmh/cT2fUjG1MLdCNb4hE9A==",
- "dev": true,
"license": "MIT"
},
"node_modules/@types/istanbul-lib-coverage": {
diff --git a/package.json b/package.json
index 9c1e3b4..84ab8be 100644
--- a/package.json
+++ b/package.json
@@ -48,15 +48,19 @@
"dist"
],
"sideEffects": false,
+ "dependencies": {
+ "@types/chrome": "^0.2.2"
+ },
"scripts": {
"prepare": "husky",
- "build": "tsup",
+ "build": "tsup && node ./scripts/copy-api-types.mjs",
"prepublishOnly": "npm run build",
"dev": "tsup --watch",
"lint": "biome check .",
"fix": "biome check --write --unsafe .",
"test": "jest",
"test:ci": "jest --ci --passWithNoTests --coverage",
+ "test:consumer-types": "npm run build && node ./tests/consumer-types/check.mjs",
"test:related": "jest --bail --passWithNoTests",
"typecheck": "tsc -p tsconfig.json --noEmit",
"release": "release-it",
@@ -67,7 +71,6 @@
"@commitlint/cli": "^20.0.0",
"@commitlint/config-conventional": "^20.0.0",
"@release-it/conventional-changelog": "^10.0.1",
- "@types/chrome": "^0.1.36",
"@types/jest": "^30.0.0",
"husky": "^9.1.7",
"jest": "^30.1.3",
@@ -76,9 +79,6 @@
"tsup": "^8.5.0",
"typescript": "^5.9.2"
},
- "peerDependencies": {
- "@types/chrome": "*"
- },
"overrides": {
"test-exclude": {
"glob": "^9.3.5"
diff --git a/scripts/copy-api-types.mjs b/scripts/copy-api-types.mjs
new file mode 100644
index 0000000..7246c69
--- /dev/null
+++ b/scripts/copy-api-types.mjs
@@ -0,0 +1,7 @@
+import {copyFileSync} from "node:fs";
+import {dirname, join} from "node:path";
+import {fileURLToPath} from "node:url";
+
+const rootDirectory = join(dirname(fileURLToPath(import.meta.url)), "..");
+
+copyFileSync(join(rootDirectory, "src/api.d.ts"), join(rootDirectory, "dist/api.d.ts"));
diff --git a/tests/consumer-types/check.mjs b/tests/consumer-types/check.mjs
new file mode 100644
index 0000000..e3705c3
--- /dev/null
+++ b/tests/consumer-types/check.mjs
@@ -0,0 +1,51 @@
+import assert from "node:assert/strict";
+import {execFileSync} from "node:child_process";
+import {cpSync, existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync} from "node:fs";
+import {tmpdir} from "node:os";
+import {dirname, join} from "node:path";
+import {fileURLToPath} from "node:url";
+
+const fixtureDirectory = dirname(fileURLToPath(import.meta.url));
+const packageDirectory = join(fixtureDirectory, "../..");
+const temporaryDirectory = mkdtempSync(join(tmpdir(), "addon-core-browser-consumer-"));
+const npm = process.platform === "win32" ? "npm.cmd" : "npm";
+
+try {
+ const [{filename}] = JSON.parse(
+ execFileSync(npm, ["pack", "--ignore-scripts", "--json", "--pack-destination", temporaryDirectory], {
+ cwd: packageDirectory,
+ encoding: "utf8",
+ })
+ );
+ const archive = join(temporaryDirectory, filename);
+ const consumerDirectory = join(temporaryDirectory, "consumer");
+
+ cpSync(fixtureDirectory, consumerDirectory, {recursive: true});
+ writeFileSync(join(consumerDirectory, "package.json"), '{"private":true,"type":"module"}\n');
+ execFileSync(npm, ["install", "--ignore-scripts", "--no-package-lock", "--no-save", archive], {
+ cwd: consumerDirectory,
+ stdio: "inherit",
+ });
+
+ const installedPackageDirectory = join(consumerDirectory, "node_modules/@addon-core/browser");
+ const installedPackage = JSON.parse(readFileSync(join(installedPackageDirectory, "package.json"), "utf8"));
+
+ assert.equal(installedPackage.dependencies?.["@types/chrome"], "^0.2.2");
+ assert.equal(installedPackage.peerDependencies?.["@types/chrome"], undefined);
+ assert.equal(installedPackage.types, "dist/index.d.ts");
+
+ const declarations = readFileSync(join(installedPackageDirectory, installedPackage.types), "utf8");
+
+ assert.match(declarations, /^\/\/\/ /);
+ assert.equal(existsSync(join(consumerDirectory, "node_modules/@types/chrome")), true);
+
+ execFileSync(
+ process.execPath,
+ [join(packageDirectory, "node_modules/typescript/bin/tsc"), "--project", consumerDirectory],
+ {
+ stdio: "inherit",
+ }
+ );
+} finally {
+ rmSync(temporaryDirectory, {force: true, recursive: true});
+}
diff --git a/tests/consumer-types/index.ts b/tests/consumer-types/index.ts
new file mode 100644
index 0000000..3f30071
--- /dev/null
+++ b/tests/consumer-types/index.ts
@@ -0,0 +1,11 @@
+import {onTabUpdated} from "@addon-core/browser";
+
+onTabUpdated((tabId, changeInfo, tab) => {
+ const id: number = tabId;
+ const info: chrome.tabs.OnUpdatedInfo = changeInfo;
+ const currentTab: chrome.tabs.Tab = tab;
+
+ void id;
+ void info;
+ void currentTab;
+});
diff --git a/tests/consumer-types/tsconfig.json b/tests/consumer-types/tsconfig.json
new file mode 100644
index 0000000..f55336a
--- /dev/null
+++ b/tests/consumer-types/tsconfig.json
@@ -0,0 +1,9 @@
+{
+ "compilerOptions": {
+ "module": "NodeNext",
+ "moduleResolution": "NodeNext",
+ "noEmit": true,
+ "strict": true,
+ "target": "ESNext"
+ }
+}
diff --git a/tsup.config.ts b/tsup.config.ts
index c8f38ed..0af8e15 100644
--- a/tsup.config.ts
+++ b/tsup.config.ts
@@ -11,7 +11,9 @@ export default defineConfig([
{
...common,
format: ["esm"],
- dts: true,
+ dts: {
+ banner: '/// \n/// ',
+ },
outExtension() {
return {js: ".js"};
},