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"}; },