From e31d46bac681fa765581f81236cf61dbbb16f3fe Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Tue, 13 Oct 2020 21:20:08 -0700 Subject: [PATCH] feat: add typedef generation --- .eslintrc | 3 ++ .github/workflows/typecheck.yml | 27 ++++++++++++ package.json | 20 ++++++--- src/index.js | 75 ++++++++++++++++++++++++--------- test/index.spec.js | 4 +- tsconfig.json | 42 ++++++++++++++++++ 6 files changed, 143 insertions(+), 28 deletions(-) create mode 100644 .eslintrc create mode 100644 .github/workflows/typecheck.yml create mode 100644 tsconfig.json diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..ea565dd --- /dev/null +++ b/.eslintrc @@ -0,0 +1,3 @@ +{ + "extends": "ipfs" +} diff --git a/.github/workflows/typecheck.yml b/.github/workflows/typecheck.yml new file mode 100644 index 0000000..e618c21 --- /dev/null +++ b/.github/workflows/typecheck.yml @@ -0,0 +1,27 @@ +on: + push: + branches: + - master + - main + - default + pull_request: + branches: + - '**' + +name: Typecheck +jobs: + check: + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [12.x] + steps: + - uses: actions/checkout@v1 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v1 + with: + node-version: ${{ matrix.node-version }} + - name: Install dependencies + run: npm install + - name: Typecheck + uses: gozala/typescript-error-reporter-action@v1.0.2 diff --git a/package.json b/package.json index 7fd9020..42e08f5 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,10 @@ "main": "src/index.js", "scripts": { "lint": "aegir lint", - "build": "aegir build", + "check": "tsc --noEmit --noErrorTruncation", + "build": "npm run build:js && npm run build:types", + "build:js": "aegir build", + "build:types": "tsc --emitDeclarationOnly --declarationDir dist", "test": "aegir test", "test:node": "aegir test --target node", "test:browser": "aegir test --target browser", @@ -34,17 +37,24 @@ }, "homepage": "https://github.com/ipld/js-ipld-block#readme", "devDependencies": { - "aegir": "^25.0.0", - "uint8arrays": "^1.0.0" + "aegir": "^27.0.0", + "uint8arrays": "^1.0.0", + "typescript": "^4.0.3" }, "dependencies": { - "cids": "^1.0.0", - "class-is": "^1.1.0" + "cids": "^1.0.0" }, "engines": { "node": ">=6.0.0", "npm": ">=3.0.0" }, + "typesVersions": { + "*": { + "*": [ + "dist/types/*" + ] + } + }, "contributors": [ "David Dias ", "Volker Mische ", diff --git a/src/index.js b/src/index.js index 07e1cbd..5d10c5c 100644 --- a/src/index.js +++ b/src/index.js @@ -1,7 +1,10 @@ 'use strict' const CID = require('cids') -const withIs = require('class-is') + +const { version } = require('../package.json') +const blockSymbol = Symbol.for('@ipld/js-ipld-block/block') +const readonly = { writable: false, configurable: false, enumerable: true } /** * Represents an immutable block of data that is uniquely referenced with a cid. @@ -9,9 +12,8 @@ const withIs = require('class-is') * @example * const block = new Block(Uint8Array.from([0, 1, 2, 3]), new CID('...')) */ -module.exports = class Block { +class Block { /** - * @constructor * @param {Uint8Array} data - The data to be stored in the block as a Uint8Array. * @param {CID} cid - The cid of the data */ @@ -24,46 +26,77 @@ module.exports = class Block { throw new Error('second argument must be a CID') } - this._data = data - this._cid = cid + this.data = data + this.cid = cid + + Object.defineProperties(this, { + data: readonly, + cid: readonly + }) } /** * The data of this block. * + * @deprecated * @type {Uint8Array} */ - get data () { - return this._data - } - - set data (val) { - throw new Error('Tried to change an immutable block') + get _data () { + deprecateData() + return this.data } /** * The cid of the data this block represents. * + * @deprecated * @type {CID} */ - get cid () { - return this._cid + get _cid () { + deprecateCID() + return this.cid } - set cid (val) { - throw new Error('Tried to change an immutable block') + get [Symbol.toStringTag] () { + return 'Block' + } + + get [blockSymbol] () { + return true } - // eslint-disable-next-line valid-jsdoc /** * Check if the given value is a Block. + * + * @param {any} other * @returns {other is Block} */ - static isBlock (other) { // eslint-disable-line no-unused-vars - // implemented by class-is module + static isBlock (other) { + return Boolean(other && other[blockSymbol]) } } -// to trick the typings engine -// https://github.com/ipld/js-ipld-block/pull/55#discussion_r478845002 -module.exports = withIs(module.exports, { className: 'Block', symbolName: '@ipld/js-ipld-block/block' }) +/** + * @param {RegExp} range + * @param {string} message + * @returns {() => void} + */ +const deprecate = (range, message) => { + let warned = false + return () => { + if (range.test(version)) { + if (!warned) { + warned = true + // eslint-disable-next-line no-console + console.warn(message) + } + } else { + throw new Error(message) + } + } +} + +const deprecateCID = deprecate(/^0\.10/, 'block._cid is deprecated and will be removed in the next major release. Use block.cid instead') +const deprecateData = deprecate(/^0\.10/, 'block._data is deprecated and will be removed in the next major release. Use block.data instead') + +module.exports = Block diff --git a/test/index.spec.js b/test/index.spec.js index 15e0dee..7691154 100644 --- a/test/index.spec.js +++ b/test/index.spec.js @@ -34,13 +34,13 @@ describe('block', () => { expect( () => { b.data = 'fail' } ).to.throw( - /immutable/ + /read only/ ) expect( () => { b.cid = 'fail' } ).to.throw( - /immutable/ + /read only/ ) }) }) diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..4ce54b0 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,42 @@ +{ + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "forceConsistentCasingInFileNames": true, + "noImplicitReturns": false, + "noImplicitAny": true, + "noImplicitThis": true, + "noFallthroughCasesInSwitch": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "strictFunctionTypes": false, + "strictNullChecks": true, + "strictPropertyInitialization": true, + "strictBindCallApply": true, + "strict": true, + "alwaysStrict": true, + "esModuleInterop": true, + "target": "ES2018", + "moduleResolution": "node", + "declaration": true, + "declarationMap": true, + "outDir": "dist", + "skipLibCheck": true, + "stripInternal": true, + "resolveJsonModule": true, + "paths": { + "multiformats": [ + "src" + ] + }, + "baseUrl": "." + }, + "include": [ + "src" + ], + "exclude": [ + "vendor", + "node_modules" + ], + "compileOnSave": false +}