diff --git a/base.tsconfig.json b/base.tsconfig.json new file mode 100644 index 00000000..3b06fcc2 --- /dev/null +++ b/base.tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "ES2017", + "module": "commonjs", + "lib": ["es2017"], + "noEmit": true, + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "moduleResolution": "node", + "esModuleInterop": true + } +} diff --git a/jest.config.js b/jest.config.js index f3598434..488db42a 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,16 +1,20 @@ -const commonSettings = { - globals: { - __DEV__: true, - }, -}; +function createPackageConfig(packageName) { + return { + transform: { + '^.+\\.tsx?$': 'ts-jest', + }, + globals: { + __DEV__: true, + 'ts-jest': { + tsConfig: `/packages/${packageName}/tsconfig.json`, + }, + }, + displayName: packageName, + roots: [`/packages/${packageName}`], + testMatch: [`/packages/${packageName}/__tests__/*{.,-}test.ts`], + }; +} module.exports = { - projects: [ - { - ...commonSettings, - displayName: 'core', - roots: ['/packages/core'], - testMatch: ['/packages/core/__tests__/*{.,-}test.ts'], - }, - ], + projects: [createPackageConfig('core'), createPackageConfig('storage-web')], }; diff --git a/package.json b/package.json index c0aeb48f..59e255df 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "clean": "lerna run clean", "test": "yarn test:lint && yarn test:unit && yarn test:types", "test:lint": "eslint packages/**/src/*.ts", - "test:types": "tsc --noEmit --declaration false -emitDeclarationOnly false", + "test:types": "lerna run test:types", "test:unit": "jest" }, "devDependencies": { @@ -41,6 +41,7 @@ "eslint": "5.16.0", "jest": "24.8.0", "lerna": "3.15.0", + "ts-jest": "24.1.0", "typescript": "3.5.2" }, "keywords": [ diff --git a/packages/core/package.json b/packages/core/package.json index afc2c512..22693d78 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -8,7 +8,9 @@ "scripts": { "build": "babel src --root-mode upward --out-dir build/ --extensions .ts --ignore build/**/* --ignore types/* --source-maps inline", "clean": "rm build -rf", - "start": "yarn build:lib --watch", - "prepublish": "yarn clean && yarn build" + "start": "yarn build --watch", + "prepublish": "yarn clean && yarn build", + "test:types": "tsc", + "test:lint": "eslint ./**/*.ts" } } diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json new file mode 100644 index 00000000..acf5c2d0 --- /dev/null +++ b/packages/core/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../base.tsconfig", + "include": [ + "./src", + "__tests__", + "./types" + ], + "exclude": [ + "node_modules" + ] +} diff --git a/packages/storage-legacy/package.json b/packages/storage-legacy/package.json index a130da8b..d0c9d28d 100644 --- a/packages/storage-legacy/package.json +++ b/packages/storage-legacy/package.json @@ -8,11 +8,12 @@ "scripts": { "build": "babel src --root-mode upward --out-dir build/ --extensions .ts --ignore build/**/* --ignore types/**/* --source-maps inline", "clean": "rm build -rf", - "generate:types": "tsc src/index.ts --noEmit false --lib es2015 --declarationDir types/ -d true -emitDeclarationOnly true", - "prepublish": "yarn clean && yarn build" + "prepublish": "yarn clean && yarn build", + "test:types": "tsc", + "test:lint": "eslint ./**/*.ts" }, "dependencies": { - "@react-native-community/async-storage": "^2.0" + "@react-native-community/async-storage": "^2.0.0" }, "peerDependencies": { "react": "^16.0", diff --git a/packages/storage-legacy/tsconfig.json b/packages/storage-legacy/tsconfig.json new file mode 100644 index 00000000..acf5c2d0 --- /dev/null +++ b/packages/storage-legacy/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../base.tsconfig", + "include": [ + "./src", + "__tests__", + "./types" + ], + "exclude": [ + "node_modules" + ] +} diff --git a/packages/storage-web/.npmignore b/packages/storage-web/.npmignore index b27a1f8b..39a1d2f9 100644 --- a/packages/storage-web/.npmignore +++ b/packages/storage-web/.npmignore @@ -1,4 +1,4 @@ -# Legacy storage backend +# Web storage backend node_modules yarn.lock # see https://github.com/yarnpkg/yarn/issues/7540 diff --git a/packages/storage-web/__tests__/index.test.ts b/packages/storage-web/__tests__/index.test.ts new file mode 100644 index 00000000..c72d1f75 --- /dev/null +++ b/packages/storage-web/__tests__/index.test.ts @@ -0,0 +1,69 @@ +import WebStorage from '../src'; +import 'jest-localstorage-mock'; + +describe.each([['sessionStorage', false], ['localStorage', true]])( + 'WebStorage using %s', + (storageName, storageBool) => { + const webStorage = new WebStorage(storageBool); + const storage: any = storageBool + ? window.sessionStorage + : window.localStorage; + + beforeEach(() => { + storage.clear(); + }); + describe(`main API with ${storageName}`, () => { + it(`gets single item from ${storage}`, async () => { + storage.setItem('key1', 'value1'); + expect(await webStorage.getSingle('key1')).toBe('value1'); + }); + + it(`saves single item to ${storageName}`, async () => { + await webStorage.setSingle('key1', 'value1'); + expect(storage.__STORE__.key1).toBe('value1'); + }); + + it(`gets multiple items from ${storageName}`, async () => { + storage.setItem('key1', 'value1'); + storage.setItem('key2', 'value2'); + expect(await webStorage.getMany(['key1', 'key2'])).toEqual({ + key1: 'value1', + key2: 'value2', + }); + }); + + it(`saves multiple items to ${storageName}`, async () => { + await webStorage.setMany([{key1: 'value1'}, {key2: 'value2'}]); + expect(storage.__STORE__).toEqual({key1: 'value1', key2: 'value2'}); + }); + + it(`removes single item from ${storageName}`, async () => { + storage.setItem('key1', 'value1'); + storage.setItem('key2', 'value2'); + await webStorage.removeSingle('key1'); + expect(storage.__STORE__).toEqual({key2: 'value2'}); + }); + + it(`removes multiple items from ${storageName}`, async () => { + storage.setItem('key1', 'value1'); + storage.setItem('key2', 'value2'); + storage.setItem('key3', 'value3'); + await webStorage.removeMany(['key1', 'key2']); + expect(storage.__STORE__).toEqual({key3: 'value3'}); + }); + + it(`gets keys from ${storageName}`, async () => { + storage.setItem('key1', 'value1'); + storage.setItem('key2', 'value2'); + expect(await webStorage.getKeys()).toEqual(['key1', 'key2']); + }); + + it(`removes all keys from ${storageName}`, async () => { + storage.setItem('key1', 'value1'); + storage.setItem('key2', 'value2'); + await webStorage.dropStorage(); + expect(storage.__STORE__).toEqual({}); + }); + }); + }, +); diff --git a/packages/storage-web/package.json b/packages/storage-web/package.json index 893a8a0d..24e9dee5 100644 --- a/packages/storage-web/package.json +++ b/packages/storage-web/package.json @@ -8,8 +8,8 @@ "scripts": { "build": "babel src --root-mode upward --out-dir build/ --extensions .ts --ignore build/**/* --ignore types/**/* --source-maps inline", "clean": "rm build -rf", - "generate:types": "tsc src/index.ts --noEmit false --lib es2015 --declarationDir types/ -d true -emitDeclarationOnly true", - "prepublish": "yarn clean && yarn build" + "prepublish": "yarn clean && yarn build", + "test:types": "tsc" }, "dependencies": { "@react-native-community/async-storage": "^2.0" @@ -19,6 +19,7 @@ "react-native": ">=0.58" }, "devDependencies": { + "jest-localstorage-mock": "^2.4.0", "react": "^16.0", "react-native": ">=0.58" } diff --git a/packages/storage-web/src/index.ts b/packages/storage-web/src/index.ts index 74118de4..4aceb520 100644 --- a/packages/storage-web/src/index.ts +++ b/packages/storage-web/src/index.ts @@ -5,3 +5,102 @@ * LICENSE file in the root directory of this source tree. * */ +import { + IStorageBackend, + EmptyStorageModel, + StorageOptions, +} from '@react-native-community/async-storage'; + +class WebStorage + implements IStorageBackend { + storage: any; + constructor(sessionStorage: boolean | string = false) { + this.storage = sessionStorage ? window.sessionStorage : window.localStorage; + } + + async getSingle( + key: K, + opts?: StorageOptions, + ): Promise { + if (opts) { + // noop + } + return this.storage.getItem(key); + } + + async setSingle( + key: K, + value: T[K], + opts?: StorageOptions, + ): Promise { + if (opts) { + // noop + } + return this.storage.setItem(key, value); + } + + async getMany( + keys: Array, + opts?: StorageOptions, + ): Promise<{[k in K]: T[k] | null}> { + if (opts) { + // noop + } + return keys.reduce( + (storageValues, key) => { + return { + ...storageValues, + [key]: this.storage.getItem(key), + }; + }, + {} as {[k in K]: T[k] | null}, + ); + } + + async setMany( + values: Array>, + opts?: StorageOptions, + ): Promise { + if (opts) { + // noop + } + for (let keyValue of values) { + const key = Object.getOwnPropertyNames(keyValue)[0] as K; + if (!key) { + continue; + } + this.storage.setItem(key, keyValue[key]); + } + } + + async removeSingle(key: keyof T, opts?: StorageOptions): Promise { + if (opts) { + // noop + } + return this.storage.removeItem(key); + } + + async removeMany(keys: Array, opts?: StorageOptions): Promise { + if (opts) { + // noop + } + Promise.all(keys.map(k => this.storage.removeItem(k))); + } + + async getKeys(opts?: StorageOptions): Promise> { + if (opts) { + // noop + } + return Object.keys(this.storage); + } + + async dropStorage(opts?: StorageOptions): Promise { + if (opts) { + // noop + } + const keys = await this.getKeys(); + await this.removeMany(keys); + } +} + +export default WebStorage; diff --git a/packages/storage-web/tsconfig.json b/packages/storage-web/tsconfig.json new file mode 100644 index 00000000..f3edcac6 --- /dev/null +++ b/packages/storage-web/tsconfig.json @@ -0,0 +1,15 @@ +{ + "extends": "../../base.tsconfig", + "compilerOptions": { + "lib": ["es2017", "dom"], + "types": ["@types/jest"] + }, + "include": [ + "./src", + "./__tests__", + "./types" + ], + "exclude": [ + "node_modules" + ] +} diff --git a/packages/storage-web/types/index.d.ts b/packages/storage-web/types/index.d.ts index 74118de4..c13161f0 100644 --- a/packages/storage-web/types/index.d.ts +++ b/packages/storage-web/types/index.d.ts @@ -5,3 +5,42 @@ * LICENSE file in the root directory of this source tree. * */ + +import { + EmptyStorageModel, + IStorageBackend, + StorageOptions, +} from '@react-native-community/async-storage'; +export default class WebStorage + implements IStorageBackend { + private readonly _asyncStorageNativeModule; + storage: Function; + getSingle( + key: K, + opts?: StorageOptions, + ): Promise; + + setSingle( + key: K, + value: T[K], + opts?: StorageOptions, + ): Promise; + + getMany( + keys: Array, + opts?: StorageOptions, + ): Promise<{[k in K]: T[k] | null}>; + + setMany( + values: Array<{[k in K]: T[k]}>, + opts?: StorageOptions, + ): Promise; + + removeSingle(key: keyof T, opts?: StorageOptions): Promise; + + removeMany(keys: Array, opts?: StorageOptions): Promise; + + getKeys(opts?: StorageOptions): Promise>; + + dropStorage(opts?: StorageOptions): Promise; +} diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index fc591fe6..00000000 --- a/tsconfig.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "compilerOptions": { - "target": "ES2017", - "module": "commonjs", - "lib": ["es2017"], - - // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ - "noEmit": true, - "strict": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true, - "moduleResolution": "node", - - "esModuleInterop": true - }, - "include": [ - "@types/jest", - "packages/**/src/", - "packages/**/types/", - "packages/**/__tests__/", - ] -} diff --git a/yarn.lock b/yarn.lock index c9704f64..9a4aac85 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2588,6 +2588,13 @@ browserslist@^4.6.0, browserslist@^4.6.2: electron-to-chromium "^1.3.164" node-releases "^1.1.23" +bs-logger@0.x: + version "0.2.6" + resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" + integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== + dependencies: + fast-json-stable-stringify "2.x" + bser@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/bser/-/bser-2.0.0.tgz#9ac78d3ed5d915804fd87acb158bc797147a1719" @@ -2605,7 +2612,7 @@ buffer-crc32@^0.2.13: resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= -buffer-from@^1.0.0: +buffer-from@1.x, buffer-from@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== @@ -3974,7 +3981,7 @@ fast-glob@^2.0.2: merge2 "^1.2.3" micromatch "^3.1.10" -fast-json-stable-stringify@^2.0.0: +fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I= @@ -5329,6 +5336,11 @@ jest-leak-detector@^24.8.0: dependencies: pretty-format "^24.8.0" +jest-localstorage-mock@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/jest-localstorage-mock/-/jest-localstorage-mock-2.4.0.tgz#c6073810735dd3af74020ea6c3885ec1cc6d0d13" + integrity sha512-/mC1JxnMeuIlAaQBsDMilskC/x/BicsQ/BXQxEOw+5b1aGZkkOAqAF3nu8yq449CpzGtp5jJ5wCmDNxLgA2m6A== + jest-matcher-utils@^24.8.0: version "24.8.0" resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-24.8.0.tgz#2bce42204c9af12bde46f83dc839efe8be832495" @@ -5635,6 +5647,13 @@ json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= +json5@2.x: + version "2.1.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.1.tgz#81b6cb04e9ba496f1c7005d07b4368a2638f90b6" + integrity sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ== + dependencies: + minimist "^1.2.0" + json5@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.0.tgz#e7a0c62c48285c628d20a10b85c89bb807c32850" @@ -5846,6 +5865,11 @@ lodash.ismatch@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz#756cb5150ca3ba6f11085a78849645f188f85f37" integrity sha1-dWy1FQyjum8RCFp4hJZF8Yj4Xzc= +lodash.memoize@4.x: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= + lodash.pad@^4.1.0: version "4.5.1" resolved "https://registry.yarnpkg.com/lodash.pad/-/lodash.pad-4.5.1.tgz#4330949a833a7c8da22cc20f6a26c4d59debba70" @@ -5956,6 +5980,11 @@ make-dir@^2.0.0, make-dir@^2.1.0: pify "^4.0.1" semver "^5.6.0" +make-error@1.x: + version "1.3.5" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.5.tgz#efe4e81f6db28cadd605c70f29c831b58ef776c8" + integrity sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g== + make-fetch-happen@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-4.0.1.tgz#141497cb878f243ba93136c83d8aba12c216c083" @@ -6493,7 +6522,7 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0: +mkdirp@0.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= @@ -7976,6 +8005,13 @@ resolve@1.1.7: resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs= +resolve@1.x: + version "1.12.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.12.0.tgz#3fc644a35c84a48554609ff26ec52b66fa577df6" + integrity sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w== + dependencies: + path-parse "^1.0.6" + resolve@^1.10.0, resolve@^1.3.2, resolve@^1.5.0, resolve@^1.8.1, resolve@^1.9.0: version "1.11.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.11.0.tgz#4014870ba296176b86343d50b60f3b50609ce232" @@ -8133,6 +8169,11 @@ semver@5.5.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" integrity sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA== +semver@^5.5: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + semver@^6.0.0: version "6.1.1" resolved "https://registry.yarnpkg.com/semver/-/semver-6.1.1.tgz#53f53da9b30b2103cd4f15eab3a18ecbcb210c9b" @@ -8825,6 +8866,22 @@ trim-right@^1.0.1: resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM= +ts-jest@24.1.0: + version "24.1.0" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-24.1.0.tgz#2eaa813271a2987b7e6c3fefbda196301c131734" + integrity sha512-HEGfrIEAZKfu1pkaxB9au17b1d9b56YZSqz5eCVE8mX68+5reOvlM93xGOzzCREIov9mdH7JBG+s0UyNAqr0tQ== + dependencies: + bs-logger "0.x" + buffer-from "1.x" + fast-json-stable-stringify "2.x" + json5 "2.x" + lodash.memoize "4.x" + make-error "1.x" + mkdirp "0.x" + resolve "1.x" + semver "^5.5" + yargs-parser "10.x" + tslib@^1.8.1, tslib@^1.9.0: version "1.10.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" @@ -9319,6 +9376,13 @@ yallist@^3.0.0, yallist@^3.0.2, yallist@^3.0.3: resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9" integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A== +yargs-parser@10.x: + version "10.1.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-10.1.0.tgz#7202265b89f7e9e9f2e5765e0fe735a905edbaa8" + integrity sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ== + dependencies: + camelcase "^4.1.0" + yargs-parser@^11.1.1: version "11.1.1" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-11.1.1.tgz#879a0865973bca9f6bab5cbdf3b1c67ec7d3bcf4"