diff --git a/.babelrc b/.babelrc index d2b9ce12..490e80aa 100644 --- a/.babelrc +++ b/.babelrc @@ -6,6 +6,7 @@ "env": { "cjs": { "plugins": [ + "transform-class-properties", ["transform-runtime", { "polyfill": false }] ], "presets": [ @@ -17,6 +18,7 @@ ] }, "mjs": { + "plugins": ["transform-class-properties"], "presets": [ [ "env", @@ -31,9 +33,11 @@ ] }, "es": { + "plugins": ["transform-class-properties"], }, "node8": { "plugins": [ + "transform-class-properties", ["transform-runtime", { "polyfill": false }] ], "presets": [ @@ -47,6 +51,9 @@ ] }, "test": { + "plugins": [ + "transform-class-properties", + ], "presets": [ ["env", { "targets": { diff --git a/.travis.yml b/.travis.yml index f515d165..f5663cda 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,9 +10,6 @@ notifications: node_js: - "9" - "8" -before_install: yarn global add greenkeeper-lockfile@1 -before_script: greenkeeper-lockfile-update -after_script: greenkeeper-lockfile-upload script: - yarn run test - yarn run build diff --git a/README.md b/README.md index 50468931..69608740 100644 --- a/README.md +++ b/README.md @@ -92,9 +92,9 @@ const User = mongoose.model('User', UserSchema); const customizationOptions = {}; // left it empty for simplicity, described below const UserTC = composeWithMongoose(User, customizationOptions); -// STEP 3: CREATE CRAZY GraphQL SCHEMA WITH ALL CRUD USER OPERATIONS +// STEP 3: Add needed CRUD User operations to the GraphQL Schema // via graphql-compose it will be much much easier, with less typing -schemaComposer.rootQuery().addFields({ +schemaComposer.Query.addFields({ userById: UserTC.getResolver('findById'), userByIds: UserTC.getResolver('findByIds'), userOne: UserTC.getResolver('findOne'), @@ -104,7 +104,7 @@ schemaComposer.rootQuery().addFields({ userPagination: UserTC.getResolver('pagination'), }); -schemaComposer.rootMutation().addFields({ +schemaComposer.Mutation.addFields({ userCreate: UserTC.getResolver('createOne'), userUpdateById: UserTC.getResolver('updateById'), userUpdateOne: UserTC.getResolver('updateOne'), @@ -122,6 +122,126 @@ You think that is to much code? I don't think so, because by default internally was created about 55 graphql types (for input, sorting, filtering). So you will need much much more lines of code to implement all these CRUD operations by hands. +### Working with Mongoose Collection Level Discriminators +Variable Namings +* `...DTC` - Suffix for a `DiscriminatorTypeComposer` instance, which is also an instance of `TypeComposer`. All fields and Relations manipulations on this instance affects all registered discriminators and the Discriminator Interface. + +```js + import mongoose from 'mongoose'; + import { schemaComposer } from 'graphql-compose'; + import { composeWithMongooseDiscriminators } from 'graphql-compose-mongoose'; + + // pick a discriminatorKey + const DKey = 'type'; + + const enumCharacterType = { + PERSON: 'Person', + DROID: 'Droid', + }; + + // DEFINE BASE SCHEMA + const CharacterSchema = new mongoose.Schema({ + // _id: field... + type: { + type: String, + require: true, + enum: (Object.keys(enumCharacterType): Array), + description: 'Character type Droid or Person', + }, + + name: String, + height: Number, + mass: Number, + films: [String], + }); + + // DEFINE DISCRIMINATOR SCHEMAS + const DroidSchema = new mongoose.Schema({ + makeDate: String, + primaryFunction: [String], + }); + + const PersonSchema = new mongoose.Schema({ + gender: String, + hairColor: String, + starships: [String], + }); + + // set discriminator Key + CharacterSchema.set('discriminatorKey', DKey); + + // create base Model + const CharacterModel = mongoose.model('Character', CharacterSchema); + + // create mongoose discriminator models + const DroidModel = CharacterModel.discriminator(enumCharacterType.DROID, DroidSchema); + const PersonModel = CharacterModel.discriminator(enumCharacterType.PERSON, PersonSchema); + + // create DiscriminatorTypeComposer + const baseOptions = { // regular TypeConverterOptions, passed to composeWithMongoose + fields: { + remove: ['friends'], + } + } + const CharacterDTC = composeWithMongooseDiscriminators(CharacterModel, baseOptions); + + // create Discriminator Types + const droidTypeConverterOptions = { // this options will be merged with baseOptions -> customisationsOptions + fields: { + remove: ['makeDate'], + } + }; + const DroidTC = CharacterDTC.discriminator(DroidModel, droidTypeConverterOptions); + const PersonTC = CharacterDTC.discriminator(PersonModel); // baseOptions -> customisationsOptions applied + + // You may now use CharacterDTC to add fields to all Discriminators + // Use DroidTC, `PersonTC as any other TypeComposer. + schemaComposer.Mutation.addFields({ + droidCreate: DroidTC.getResolver('createOne'), + personCreate: PersonTC.getResolver('createOne'), + }); + + const schema = schemaComposer.buildSchema(); + + describe('createOne', () => { + it('should create child document without specifying DKey', async () => { + const res = await graphql.graphql( + schema, + `mutation CreateCharacters { + droidCreate(record: {name: "Queue XL", modelNumber: 360 }) { + record { + __typename + type + name + modelNumber + } + } + + personCreate(record: {name: "mernxl", dob: 57275272}) { + record { + __typename + type + name + dob + } + } + }` + ); + + expect(res).toEqual({ + data: { + droidCreate: { + record: { __typename: 'Droid', type: 'Droid', name: 'Queue XL', modelNumber: 360 }, + }, + personCreate: { + record: { __typename: 'Person', type: 'Person', name: 'mernxl', dob: 57275272 }, + }, + }, + }); + }); + }); +``` + ## FAQ ### Can I get generated vanilla GraphQL types? @@ -273,7 +393,7 @@ The typical implementation may be like this: // extend resolve params with hook rp.beforeRecordMutate = async function(doc, rp) { doc.userTouchedAt = new Date(); - + const canMakeUpdate = await performAsyncTask( ...provide data from doc... ) if (!canMakeUpdate) { throw new Error('Forbidden!'); @@ -304,7 +424,7 @@ function adminAccess(resolvers) { // extend resolve params with hook rp.beforeRecordMutate = async function(doc, rp) { ... } - + return next(rp) }) }) @@ -465,4 +585,3 @@ This plugin adds `pagination` resolver. ## License [MIT](https://github.com/graphql-compose/graphql-compose-mongoose/blob/master/LICENSE.md) - diff --git a/flow-typed/npm/babel-cli_vx.x.x.js b/flow-typed/npm/babel-cli_vx.x.x.js index 2e574359..896a72cf 100644 --- a/flow-typed/npm/babel-cli_vx.x.x.js +++ b/flow-typed/npm/babel-cli_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: adfc416d3944bc9e0c30c54aa3ad632b -// flow-typed version: <>/babel-cli_v^6.26.0/flow_v0.54.1 +// flow-typed signature: 40ac71f9403783e7957989cfd3c8695f +// flow-typed version: <>/babel-cli_v^6.26.0/flow_v0.76.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/babel-core_vx.x.x.js b/flow-typed/npm/babel-core_vx.x.x.js new file mode 100644 index 00000000..0248523b --- /dev/null +++ b/flow-typed/npm/babel-core_vx.x.x.js @@ -0,0 +1,234 @@ +// flow-typed signature: dd6ca158cab6edd188477098518fa97e +// flow-typed version: <>/babel-core_v^6.26.3/flow_v0.76.0 + +/** + * This is an autogenerated libdef stub for: + * + * 'babel-core' + * + * Fill this stub out by replacing all the `any` types. + * + * Once filled out, we encourage you to share your work with the + * community by sending a pull request to: + * https://github.com/flowtype/flow-typed + */ + +declare module 'babel-core' { + declare module.exports: any; +} + +/** + * We include stubs for each file inside this npm package in case you need to + * require those files directly. Feel free to delete any files that aren't + * needed. + */ +declare module 'babel-core/lib/api/browser' { + declare module.exports: any; +} + +declare module 'babel-core/lib/api/node' { + declare module.exports: any; +} + +declare module 'babel-core/lib/helpers/get-possible-plugin-names' { + declare module.exports: any; +} + +declare module 'babel-core/lib/helpers/get-possible-preset-names' { + declare module.exports: any; +} + +declare module 'babel-core/lib/helpers/merge' { + declare module.exports: any; +} + +declare module 'babel-core/lib/helpers/normalize-ast' { + declare module.exports: any; +} + +declare module 'babel-core/lib/helpers/resolve-from-possible-names' { + declare module.exports: any; +} + +declare module 'babel-core/lib/helpers/resolve-plugin' { + declare module.exports: any; +} + +declare module 'babel-core/lib/helpers/resolve-preset' { + declare module.exports: any; +} + +declare module 'babel-core/lib/helpers/resolve' { + declare module.exports: any; +} + +declare module 'babel-core/lib/store' { + declare module.exports: any; +} + +declare module 'babel-core/lib/tools/build-external-helpers' { + declare module.exports: any; +} + +declare module 'babel-core/lib/transformation/file/index' { + declare module.exports: any; +} + +declare module 'babel-core/lib/transformation/file/logger' { + declare module.exports: any; +} + +declare module 'babel-core/lib/transformation/file/merge-map' { + declare module.exports: any; +} + +declare module 'babel-core/lib/transformation/file/metadata' { + declare module.exports: any; +} + +declare module 'babel-core/lib/transformation/file/options/build-config-chain' { + declare module.exports: any; +} + +declare module 'babel-core/lib/transformation/file/options/config' { + declare module.exports: any; +} + +declare module 'babel-core/lib/transformation/file/options/index' { + declare module.exports: any; +} + +declare module 'babel-core/lib/transformation/file/options/option-manager' { + declare module.exports: any; +} + +declare module 'babel-core/lib/transformation/file/options/parsers' { + declare module.exports: any; +} + +declare module 'babel-core/lib/transformation/file/options/removed' { + declare module.exports: any; +} + +declare module 'babel-core/lib/transformation/internal-plugins/block-hoist' { + declare module.exports: any; +} + +declare module 'babel-core/lib/transformation/internal-plugins/shadow-functions' { + declare module.exports: any; +} + +declare module 'babel-core/lib/transformation/pipeline' { + declare module.exports: any; +} + +declare module 'babel-core/lib/transformation/plugin-pass' { + declare module.exports: any; +} + +declare module 'babel-core/lib/transformation/plugin' { + declare module.exports: any; +} + +declare module 'babel-core/lib/util' { + declare module.exports: any; +} + +declare module 'babel-core/register' { + declare module.exports: any; +} + +// Filename aliases +declare module 'babel-core/index' { + declare module.exports: $Exports<'babel-core'>; +} +declare module 'babel-core/index.js' { + declare module.exports: $Exports<'babel-core'>; +} +declare module 'babel-core/lib/api/browser.js' { + declare module.exports: $Exports<'babel-core/lib/api/browser'>; +} +declare module 'babel-core/lib/api/node.js' { + declare module.exports: $Exports<'babel-core/lib/api/node'>; +} +declare module 'babel-core/lib/helpers/get-possible-plugin-names.js' { + declare module.exports: $Exports<'babel-core/lib/helpers/get-possible-plugin-names'>; +} +declare module 'babel-core/lib/helpers/get-possible-preset-names.js' { + declare module.exports: $Exports<'babel-core/lib/helpers/get-possible-preset-names'>; +} +declare module 'babel-core/lib/helpers/merge.js' { + declare module.exports: $Exports<'babel-core/lib/helpers/merge'>; +} +declare module 'babel-core/lib/helpers/normalize-ast.js' { + declare module.exports: $Exports<'babel-core/lib/helpers/normalize-ast'>; +} +declare module 'babel-core/lib/helpers/resolve-from-possible-names.js' { + declare module.exports: $Exports<'babel-core/lib/helpers/resolve-from-possible-names'>; +} +declare module 'babel-core/lib/helpers/resolve-plugin.js' { + declare module.exports: $Exports<'babel-core/lib/helpers/resolve-plugin'>; +} +declare module 'babel-core/lib/helpers/resolve-preset.js' { + declare module.exports: $Exports<'babel-core/lib/helpers/resolve-preset'>; +} +declare module 'babel-core/lib/helpers/resolve.js' { + declare module.exports: $Exports<'babel-core/lib/helpers/resolve'>; +} +declare module 'babel-core/lib/store.js' { + declare module.exports: $Exports<'babel-core/lib/store'>; +} +declare module 'babel-core/lib/tools/build-external-helpers.js' { + declare module.exports: $Exports<'babel-core/lib/tools/build-external-helpers'>; +} +declare module 'babel-core/lib/transformation/file/index.js' { + declare module.exports: $Exports<'babel-core/lib/transformation/file/index'>; +} +declare module 'babel-core/lib/transformation/file/logger.js' { + declare module.exports: $Exports<'babel-core/lib/transformation/file/logger'>; +} +declare module 'babel-core/lib/transformation/file/merge-map.js' { + declare module.exports: $Exports<'babel-core/lib/transformation/file/merge-map'>; +} +declare module 'babel-core/lib/transformation/file/metadata.js' { + declare module.exports: $Exports<'babel-core/lib/transformation/file/metadata'>; +} +declare module 'babel-core/lib/transformation/file/options/build-config-chain.js' { + declare module.exports: $Exports<'babel-core/lib/transformation/file/options/build-config-chain'>; +} +declare module 'babel-core/lib/transformation/file/options/config.js' { + declare module.exports: $Exports<'babel-core/lib/transformation/file/options/config'>; +} +declare module 'babel-core/lib/transformation/file/options/index.js' { + declare module.exports: $Exports<'babel-core/lib/transformation/file/options/index'>; +} +declare module 'babel-core/lib/transformation/file/options/option-manager.js' { + declare module.exports: $Exports<'babel-core/lib/transformation/file/options/option-manager'>; +} +declare module 'babel-core/lib/transformation/file/options/parsers.js' { + declare module.exports: $Exports<'babel-core/lib/transformation/file/options/parsers'>; +} +declare module 'babel-core/lib/transformation/file/options/removed.js' { + declare module.exports: $Exports<'babel-core/lib/transformation/file/options/removed'>; +} +declare module 'babel-core/lib/transformation/internal-plugins/block-hoist.js' { + declare module.exports: $Exports<'babel-core/lib/transformation/internal-plugins/block-hoist'>; +} +declare module 'babel-core/lib/transformation/internal-plugins/shadow-functions.js' { + declare module.exports: $Exports<'babel-core/lib/transformation/internal-plugins/shadow-functions'>; +} +declare module 'babel-core/lib/transformation/pipeline.js' { + declare module.exports: $Exports<'babel-core/lib/transformation/pipeline'>; +} +declare module 'babel-core/lib/transformation/plugin-pass.js' { + declare module.exports: $Exports<'babel-core/lib/transformation/plugin-pass'>; +} +declare module 'babel-core/lib/transformation/plugin.js' { + declare module.exports: $Exports<'babel-core/lib/transformation/plugin'>; +} +declare module 'babel-core/lib/util.js' { + declare module.exports: $Exports<'babel-core/lib/util'>; +} +declare module 'babel-core/register.js' { + declare module.exports: $Exports<'babel-core/register'>; +} diff --git a/flow-typed/npm/babel-eslint_vx.x.x.js b/flow-typed/npm/babel-eslint_vx.x.x.js index 1dcd5e9c..c35485f1 100644 --- a/flow-typed/npm/babel-eslint_vx.x.x.js +++ b/flow-typed/npm/babel-eslint_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: a339a1bc763a819934b7920cfda8cf01 -// flow-typed version: <>/babel-eslint_v^7.2.3/flow_v0.54.1 +// flow-typed signature: ef9cea9278aed0477e7e368ee69651ce +// flow-typed version: <>/babel-eslint_v^8.2.6/flow_v0.76.0 /** * This is an autogenerated libdef stub for: @@ -22,59 +22,102 @@ declare module 'babel-eslint' { * require those files directly. Feel free to delete any files that aren't * needed. */ -declare module 'babel-eslint/babylon-to-espree/attachComments' { +declare module 'babel-eslint/lib/analyze-scope' { declare module.exports: any; } -declare module 'babel-eslint/babylon-to-espree/convertComments' { +declare module 'babel-eslint/lib/babylon-to-espree/attachComments' { declare module.exports: any; } -declare module 'babel-eslint/babylon-to-espree/convertTemplateType' { +declare module 'babel-eslint/lib/babylon-to-espree/convertComments' { declare module.exports: any; } -declare module 'babel-eslint/babylon-to-espree/index' { +declare module 'babel-eslint/lib/babylon-to-espree/convertTemplateType' { declare module.exports: any; } -declare module 'babel-eslint/babylon-to-espree/toAST' { +declare module 'babel-eslint/lib/babylon-to-espree/index' { declare module.exports: any; } -declare module 'babel-eslint/babylon-to-espree/toToken' { +declare module 'babel-eslint/lib/babylon-to-espree/toAST' { declare module.exports: any; } -declare module 'babel-eslint/babylon-to-espree/toTokens' { +declare module 'babel-eslint/lib/babylon-to-espree/toToken' { + declare module.exports: any; +} + +declare module 'babel-eslint/lib/babylon-to-espree/toTokens' { + declare module.exports: any; +} + +declare module 'babel-eslint/lib/index' { + declare module.exports: any; +} + +declare module 'babel-eslint/lib/parse-with-patch' { + declare module.exports: any; +} + +declare module 'babel-eslint/lib/parse-with-scope' { + declare module.exports: any; +} + +declare module 'babel-eslint/lib/parse' { + declare module.exports: any; +} + +declare module 'babel-eslint/lib/patch-eslint-scope' { + declare module.exports: any; +} + +declare module 'babel-eslint/lib/visitor-keys' { declare module.exports: any; } // Filename aliases -declare module 'babel-eslint/babylon-to-espree/attachComments.js' { - declare module.exports: $Exports<'babel-eslint/babylon-to-espree/attachComments'>; +declare module 'babel-eslint/lib/analyze-scope.js' { + declare module.exports: $Exports<'babel-eslint/lib/analyze-scope'>; +} +declare module 'babel-eslint/lib/babylon-to-espree/attachComments.js' { + declare module.exports: $Exports<'babel-eslint/lib/babylon-to-espree/attachComments'>; +} +declare module 'babel-eslint/lib/babylon-to-espree/convertComments.js' { + declare module.exports: $Exports<'babel-eslint/lib/babylon-to-espree/convertComments'>; +} +declare module 'babel-eslint/lib/babylon-to-espree/convertTemplateType.js' { + declare module.exports: $Exports<'babel-eslint/lib/babylon-to-espree/convertTemplateType'>; +} +declare module 'babel-eslint/lib/babylon-to-espree/index.js' { + declare module.exports: $Exports<'babel-eslint/lib/babylon-to-espree/index'>; +} +declare module 'babel-eslint/lib/babylon-to-espree/toAST.js' { + declare module.exports: $Exports<'babel-eslint/lib/babylon-to-espree/toAST'>; } -declare module 'babel-eslint/babylon-to-espree/convertComments.js' { - declare module.exports: $Exports<'babel-eslint/babylon-to-espree/convertComments'>; +declare module 'babel-eslint/lib/babylon-to-espree/toToken.js' { + declare module.exports: $Exports<'babel-eslint/lib/babylon-to-espree/toToken'>; } -declare module 'babel-eslint/babylon-to-espree/convertTemplateType.js' { - declare module.exports: $Exports<'babel-eslint/babylon-to-espree/convertTemplateType'>; +declare module 'babel-eslint/lib/babylon-to-espree/toTokens.js' { + declare module.exports: $Exports<'babel-eslint/lib/babylon-to-espree/toTokens'>; } -declare module 'babel-eslint/babylon-to-espree/index.js' { - declare module.exports: $Exports<'babel-eslint/babylon-to-espree/index'>; +declare module 'babel-eslint/lib/index.js' { + declare module.exports: $Exports<'babel-eslint/lib/index'>; } -declare module 'babel-eslint/babylon-to-espree/toAST.js' { - declare module.exports: $Exports<'babel-eslint/babylon-to-espree/toAST'>; +declare module 'babel-eslint/lib/parse-with-patch.js' { + declare module.exports: $Exports<'babel-eslint/lib/parse-with-patch'>; } -declare module 'babel-eslint/babylon-to-espree/toToken.js' { - declare module.exports: $Exports<'babel-eslint/babylon-to-espree/toToken'>; +declare module 'babel-eslint/lib/parse-with-scope.js' { + declare module.exports: $Exports<'babel-eslint/lib/parse-with-scope'>; } -declare module 'babel-eslint/babylon-to-espree/toTokens.js' { - declare module.exports: $Exports<'babel-eslint/babylon-to-espree/toTokens'>; +declare module 'babel-eslint/lib/parse.js' { + declare module.exports: $Exports<'babel-eslint/lib/parse'>; } -declare module 'babel-eslint/index' { - declare module.exports: $Exports<'babel-eslint'>; +declare module 'babel-eslint/lib/patch-eslint-scope.js' { + declare module.exports: $Exports<'babel-eslint/lib/patch-eslint-scope'>; } -declare module 'babel-eslint/index.js' { - declare module.exports: $Exports<'babel-eslint'>; +declare module 'babel-eslint/lib/visitor-keys.js' { + declare module.exports: $Exports<'babel-eslint/lib/visitor-keys'>; } diff --git a/flow-typed/npm/babel-jest_vx.x.x.js b/flow-typed/npm/babel-jest_vx.x.x.js index f0a1a3dc..e2844ac0 100644 --- a/flow-typed/npm/babel-jest_vx.x.x.js +++ b/flow-typed/npm/babel-jest_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 140e378926218ceed9402f883b2debc2 -// flow-typed version: <>/babel-jest_v^21.0.0/flow_v0.54.1 +// flow-typed signature: fd955224b4ff20689118b3df4ec748c1 +// flow-typed version: <>/babel-jest_v^23.4.0/flow_v0.76.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/jest_vx.x.x.js b/flow-typed/npm/babel-plugin-transform-class-properties_vx.x.x.js similarity index 52% rename from flow-typed/npm/jest_vx.x.x.js rename to flow-typed/npm/babel-plugin-transform-class-properties_vx.x.x.js index baeee98b..7ecf3f27 100644 --- a/flow-typed/npm/jest_vx.x.x.js +++ b/flow-typed/npm/babel-plugin-transform-class-properties_vx.x.x.js @@ -1,10 +1,10 @@ -// flow-typed signature: e3d2cb4f62a81c60200a3d062b033a30 -// flow-typed version: <>/jest_v^21.0.1/flow_v0.54.1 +// flow-typed signature: eebd75aac9101b8de7b9d665f7d1cbc0 +// flow-typed version: <>/babel-plugin-transform-class-properties_v^6.24.1/flow_v0.76.0 /** * This is an autogenerated libdef stub for: * - * 'jest' + * 'babel-plugin-transform-class-properties' * * Fill this stub out by replacing all the `any` types. * @@ -13,7 +13,7 @@ * https://github.com/flowtype/flow-typed */ -declare module 'jest' { +declare module 'babel-plugin-transform-class-properties' { declare module.exports: any; } @@ -22,18 +22,11 @@ declare module 'jest' { * require those files directly. Feel free to delete any files that aren't * needed. */ -declare module 'jest/bin/jest' { - declare module.exports: any; -} - -declare module 'jest/build/jest' { +declare module 'babel-plugin-transform-class-properties/lib/index' { declare module.exports: any; } // Filename aliases -declare module 'jest/bin/jest.js' { - declare module.exports: $Exports<'jest/bin/jest'>; -} -declare module 'jest/build/jest.js' { - declare module.exports: $Exports<'jest/build/jest'>; +declare module 'babel-plugin-transform-class-properties/lib/index.js' { + declare module.exports: $Exports<'babel-plugin-transform-class-properties/lib/index'>; } diff --git a/flow-typed/npm/babel-plugin-transform-flow-strip-types_vx.x.x.js b/flow-typed/npm/babel-plugin-transform-flow-strip-types_vx.x.x.js index aef23cbb..9f27bc93 100644 --- a/flow-typed/npm/babel-plugin-transform-flow-strip-types_vx.x.x.js +++ b/flow-typed/npm/babel-plugin-transform-flow-strip-types_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 9d65b0c9d809b07ed1065eb8981071bb -// flow-typed version: <>/babel-plugin-transform-flow-strip-types_v^6.22.0/flow_v0.54.1 +// flow-typed signature: 7e06d1262f0f379fdffc8b4b1302b1ec +// flow-typed version: <>/babel-plugin-transform-flow-strip-types_v^6.22.0/flow_v0.76.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/babel-plugin-transform-object-rest-spread_vx.x.x.js b/flow-typed/npm/babel-plugin-transform-object-rest-spread_vx.x.x.js index 32f96a41..f5b4300a 100644 --- a/flow-typed/npm/babel-plugin-transform-object-rest-spread_vx.x.x.js +++ b/flow-typed/npm/babel-plugin-transform-object-rest-spread_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 89dfc917947ca47a4c5b9e1992cec48a -// flow-typed version: <>/babel-plugin-transform-object-rest-spread_v^6.26.0/flow_v0.54.1 +// flow-typed signature: e9becb73a43b8cce0d0ecaecc0656b4f +// flow-typed version: <>/babel-plugin-transform-object-rest-spread_v^6.13.0/flow_v0.76.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/babel-plugin-transform-runtime_vx.x.x.js b/flow-typed/npm/babel-plugin-transform-runtime_vx.x.x.js index bd06592d..8d57b18e 100644 --- a/flow-typed/npm/babel-plugin-transform-runtime_vx.x.x.js +++ b/flow-typed/npm/babel-plugin-transform-runtime_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 3c0181a4c0caf2048ffeb2f4d65d65a4 -// flow-typed version: <>/babel-plugin-transform-runtime_v^6.23.0/flow_v0.54.1 +// flow-typed signature: 40c36e02c35c1af81d97565d84ab4f4a +// flow-typed version: <>/babel-plugin-transform-runtime_v^6.23.0/flow_v0.76.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/babel-preset-env_vx.x.x.js b/flow-typed/npm/babel-preset-env_vx.x.x.js index 39902fd9..0663850f 100644 --- a/flow-typed/npm/babel-preset-env_vx.x.x.js +++ b/flow-typed/npm/babel-preset-env_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 74d80f7769b6753de780fadfe23183ab -// flow-typed version: <>/babel-preset-env_v^1.5.2/flow_v0.54.1 +// flow-typed signature: 8771aedbbbbaebd92bf5dd5d2b88e200 +// flow-typed version: <>/babel-preset-env_v^1.7.0/flow_v0.76.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/babel-runtime_vx.x.x.js b/flow-typed/npm/babel-runtime_vx.x.x.js index 5dbe5b7f..ce5b5b8a 100644 --- a/flow-typed/npm/babel-runtime_vx.x.x.js +++ b/flow-typed/npm/babel-runtime_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: d034cd70bf4b0e5fcb160731f5ba83e4 -// flow-typed version: <>/babel-runtime_v^6.26.0/flow_v0.54.1 +// flow-typed signature: 28d37768c644ac8df6745b78e648f2a4 +// flow-typed version: <>/babel-runtime_v^6.26.0/flow_v0.76.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/cz-conventional-changelog_vx.x.x.js b/flow-typed/npm/cz-conventional-changelog_vx.x.x.js index 9dbe62c2..e5f8b3c9 100644 --- a/flow-typed/npm/cz-conventional-changelog_vx.x.x.js +++ b/flow-typed/npm/cz-conventional-changelog_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 14f08f6fb00b23789863dc2dfb573350 -// flow-typed version: <>/cz-conventional-changelog_v^2.0.0/flow_v0.54.1 +// flow-typed signature: a8e2b37e9c218fa5800022dca4f0e1e4 +// flow-typed version: <>/cz-conventional-changelog_v^2.1.0/flow_v0.76.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/eslint-config-airbnb-base_vx.x.x.js b/flow-typed/npm/eslint-config-airbnb-base_vx.x.x.js index da4309e1..6293356f 100644 --- a/flow-typed/npm/eslint-config-airbnb-base_vx.x.x.js +++ b/flow-typed/npm/eslint-config-airbnb-base_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 865f24e3b831228744b25759c2f008a0 -// flow-typed version: <>/eslint-config-airbnb-base_v^12.0.0/flow_v0.54.1 +// flow-typed signature: 3bffbca5c29c1b94644a5ea56e39ada2 +// flow-typed version: <>/eslint-config-airbnb-base_v^13.0.0/flow_v0.76.0 /** * This is an autogenerated libdef stub for: @@ -58,10 +58,18 @@ declare module 'eslint-config-airbnb-base/rules/variables' { declare module.exports: any; } +declare module 'eslint-config-airbnb-base/test/requires' { + declare module.exports: any; +} + declare module 'eslint-config-airbnb-base/test/test-base' { declare module.exports: any; } +declare module 'eslint-config-airbnb-base/whitespace' { + declare module.exports: any; +} + // Filename aliases declare module 'eslint-config-airbnb-base/index' { declare module.exports: $Exports<'eslint-config-airbnb-base'>; @@ -96,6 +104,12 @@ declare module 'eslint-config-airbnb-base/rules/style.js' { declare module 'eslint-config-airbnb-base/rules/variables.js' { declare module.exports: $Exports<'eslint-config-airbnb-base/rules/variables'>; } +declare module 'eslint-config-airbnb-base/test/requires.js' { + declare module.exports: $Exports<'eslint-config-airbnb-base/test/requires'>; +} declare module 'eslint-config-airbnb-base/test/test-base.js' { declare module.exports: $Exports<'eslint-config-airbnb-base/test/test-base'>; } +declare module 'eslint-config-airbnb-base/whitespace.js' { + declare module.exports: $Exports<'eslint-config-airbnb-base/whitespace'>; +} diff --git a/flow-typed/npm/eslint-config-prettier_vx.x.x.js b/flow-typed/npm/eslint-config-prettier_vx.x.x.js index 8644ee7e..143769d2 100644 --- a/flow-typed/npm/eslint-config-prettier_vx.x.x.js +++ b/flow-typed/npm/eslint-config-prettier_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 69ef6d4efe092588f3a851dc30d6d869 -// flow-typed version: <>/eslint-config-prettier_v^2.4.0/flow_v0.54.1 +// flow-typed signature: 47396938aaa9046dffff96a17d07c4a5 +// flow-typed version: <>/eslint-config-prettier_v^2.9.0/flow_v0.76.0 /** * This is an autogenerated libdef stub for: @@ -38,6 +38,10 @@ declare module 'eslint-config-prettier/react' { declare module.exports: any; } +declare module 'eslint-config-prettier/standard' { + declare module.exports: any; +} + // Filename aliases declare module 'eslint-config-prettier/bin/cli.js' { declare module.exports: $Exports<'eslint-config-prettier/bin/cli'>; @@ -57,3 +61,6 @@ declare module 'eslint-config-prettier/index.js' { declare module 'eslint-config-prettier/react.js' { declare module.exports: $Exports<'eslint-config-prettier/react'>; } +declare module 'eslint-config-prettier/standard.js' { + declare module.exports: $Exports<'eslint-config-prettier/standard'>; +} diff --git a/flow-typed/npm/eslint-plugin-flowtype_vx.x.x.js b/flow-typed/npm/eslint-plugin-flowtype_vx.x.x.js index 7e098f7b..f6283561 100644 --- a/flow-typed/npm/eslint-plugin-flowtype_vx.x.x.js +++ b/flow-typed/npm/eslint-plugin-flowtype_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: b337881bcd475ba74e4f385f5284d792 -// flow-typed version: <>/eslint-plugin-flowtype_v^2.35.1/flow_v0.54.1 +// flow-typed signature: 974989caf115566e5faac9a6f74c8c32 +// flow-typed version: <>/eslint-plugin-flowtype_v^2.50.0/flow_v0.76.0 /** * This is an autogenerated libdef stub for: @@ -30,6 +30,26 @@ declare module 'eslint-plugin-flowtype/dist/index' { declare module.exports: any; } +declare module 'eslint-plugin-flowtype/dist/rules/arrayStyle/index' { + declare module.exports: any; +} + +declare module 'eslint-plugin-flowtype/dist/rules/arrayStyle/isSimpleType' { + declare module.exports: any; +} + +declare module 'eslint-plugin-flowtype/dist/rules/arrayStyle/needWrap' { + declare module.exports: any; +} + +declare module 'eslint-plugin-flowtype/dist/rules/arrayStyleComplexType' { + declare module.exports: any; +} + +declare module 'eslint-plugin-flowtype/dist/rules/arrayStyleSimpleType' { + declare module.exports: any; +} + declare module 'eslint-plugin-flowtype/dist/rules/booleanStyle' { declare module.exports: any; } @@ -46,10 +66,26 @@ declare module 'eslint-plugin-flowtype/dist/rules/genericSpacing' { declare module.exports: any; } +declare module 'eslint-plugin-flowtype/dist/rules/newlineAfterFlowAnnotation' { + declare module.exports: any; +} + declare module 'eslint-plugin-flowtype/dist/rules/noDupeKeys' { declare module.exports: any; } +declare module 'eslint-plugin-flowtype/dist/rules/noExistentialType' { + declare module.exports: any; +} + +declare module 'eslint-plugin-flowtype/dist/rules/noFlowFixMeComments' { + declare module.exports: any; +} + +declare module 'eslint-plugin-flowtype/dist/rules/noMutableArray' { + declare module.exports: any; +} + declare module 'eslint-plugin-flowtype/dist/rules/noPrimitiveConstructorTypes' { declare module.exports: any; } @@ -58,6 +94,10 @@ declare module 'eslint-plugin-flowtype/dist/rules/noTypesMissingFileAnnotation' declare module.exports: any; } +declare module 'eslint-plugin-flowtype/dist/rules/noUnusedExpressions' { + declare module.exports: any; +} + declare module 'eslint-plugin-flowtype/dist/rules/noWeakTypes' { declare module.exports: any; } @@ -66,6 +106,10 @@ declare module 'eslint-plugin-flowtype/dist/rules/objectTypeDelimiter' { declare module.exports: any; } +declare module 'eslint-plugin-flowtype/dist/rules/requireExactType' { + declare module.exports: any; +} + declare module 'eslint-plugin-flowtype/dist/rules/requireParameterType' { declare module.exports: any; } @@ -74,6 +118,10 @@ declare module 'eslint-plugin-flowtype/dist/rules/requireReturnType' { declare module.exports: any; } +declare module 'eslint-plugin-flowtype/dist/rules/requireTypesAtTop' { + declare module.exports: any; +} + declare module 'eslint-plugin-flowtype/dist/rules/requireValidFileAnnotation' { declare module.exports: any; } @@ -126,6 +174,10 @@ declare module 'eslint-plugin-flowtype/dist/rules/typeColonSpacing/evaluateTypic declare module.exports: any; } +declare module 'eslint-plugin-flowtype/dist/rules/typeColonSpacing/evaluateVariables' { + declare module.exports: any; +} + declare module 'eslint-plugin-flowtype/dist/rules/typeColonSpacing/index' { declare module.exports: any; } @@ -138,6 +190,10 @@ declare module 'eslint-plugin-flowtype/dist/rules/typeIdMatch' { declare module.exports: any; } +declare module 'eslint-plugin-flowtype/dist/rules/typeImportStyle' { + declare module.exports: any; +} + declare module 'eslint-plugin-flowtype/dist/rules/unionIntersectionSpacing' { declare module.exports: any; } @@ -201,6 +257,21 @@ declare module 'eslint-plugin-flowtype/bin/readmeAssertions.js' { declare module 'eslint-plugin-flowtype/dist/index.js' { declare module.exports: $Exports<'eslint-plugin-flowtype/dist/index'>; } +declare module 'eslint-plugin-flowtype/dist/rules/arrayStyle/index.js' { + declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/arrayStyle/index'>; +} +declare module 'eslint-plugin-flowtype/dist/rules/arrayStyle/isSimpleType.js' { + declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/arrayStyle/isSimpleType'>; +} +declare module 'eslint-plugin-flowtype/dist/rules/arrayStyle/needWrap.js' { + declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/arrayStyle/needWrap'>; +} +declare module 'eslint-plugin-flowtype/dist/rules/arrayStyleComplexType.js' { + declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/arrayStyleComplexType'>; +} +declare module 'eslint-plugin-flowtype/dist/rules/arrayStyleSimpleType.js' { + declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/arrayStyleSimpleType'>; +} declare module 'eslint-plugin-flowtype/dist/rules/booleanStyle.js' { declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/booleanStyle'>; } @@ -213,27 +284,48 @@ declare module 'eslint-plugin-flowtype/dist/rules/delimiterDangle.js' { declare module 'eslint-plugin-flowtype/dist/rules/genericSpacing.js' { declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/genericSpacing'>; } +declare module 'eslint-plugin-flowtype/dist/rules/newlineAfterFlowAnnotation.js' { + declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/newlineAfterFlowAnnotation'>; +} declare module 'eslint-plugin-flowtype/dist/rules/noDupeKeys.js' { declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/noDupeKeys'>; } +declare module 'eslint-plugin-flowtype/dist/rules/noExistentialType.js' { + declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/noExistentialType'>; +} +declare module 'eslint-plugin-flowtype/dist/rules/noFlowFixMeComments.js' { + declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/noFlowFixMeComments'>; +} +declare module 'eslint-plugin-flowtype/dist/rules/noMutableArray.js' { + declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/noMutableArray'>; +} declare module 'eslint-plugin-flowtype/dist/rules/noPrimitiveConstructorTypes.js' { declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/noPrimitiveConstructorTypes'>; } declare module 'eslint-plugin-flowtype/dist/rules/noTypesMissingFileAnnotation.js' { declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/noTypesMissingFileAnnotation'>; } +declare module 'eslint-plugin-flowtype/dist/rules/noUnusedExpressions.js' { + declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/noUnusedExpressions'>; +} declare module 'eslint-plugin-flowtype/dist/rules/noWeakTypes.js' { declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/noWeakTypes'>; } declare module 'eslint-plugin-flowtype/dist/rules/objectTypeDelimiter.js' { declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/objectTypeDelimiter'>; } +declare module 'eslint-plugin-flowtype/dist/rules/requireExactType.js' { + declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/requireExactType'>; +} declare module 'eslint-plugin-flowtype/dist/rules/requireParameterType.js' { declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/requireParameterType'>; } declare module 'eslint-plugin-flowtype/dist/rules/requireReturnType.js' { declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/requireReturnType'>; } +declare module 'eslint-plugin-flowtype/dist/rules/requireTypesAtTop.js' { + declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/requireTypesAtTop'>; +} declare module 'eslint-plugin-flowtype/dist/rules/requireValidFileAnnotation.js' { declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/requireValidFileAnnotation'>; } @@ -273,6 +365,9 @@ declare module 'eslint-plugin-flowtype/dist/rules/typeColonSpacing/evaluateTypeC declare module 'eslint-plugin-flowtype/dist/rules/typeColonSpacing/evaluateTypical.js' { declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/typeColonSpacing/evaluateTypical'>; } +declare module 'eslint-plugin-flowtype/dist/rules/typeColonSpacing/evaluateVariables.js' { + declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/typeColonSpacing/evaluateVariables'>; +} declare module 'eslint-plugin-flowtype/dist/rules/typeColonSpacing/index.js' { declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/typeColonSpacing/index'>; } @@ -282,6 +377,9 @@ declare module 'eslint-plugin-flowtype/dist/rules/typeColonSpacing/reporter.js' declare module 'eslint-plugin-flowtype/dist/rules/typeIdMatch.js' { declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/typeIdMatch'>; } +declare module 'eslint-plugin-flowtype/dist/rules/typeImportStyle.js' { + declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/typeImportStyle'>; +} declare module 'eslint-plugin-flowtype/dist/rules/unionIntersectionSpacing.js' { declare module.exports: $Exports<'eslint-plugin-flowtype/dist/rules/unionIntersectionSpacing'>; } diff --git a/flow-typed/npm/eslint-plugin-import_vx.x.x.js b/flow-typed/npm/eslint-plugin-import_vx.x.x.js index b085cb9a..8ba450ba 100644 --- a/flow-typed/npm/eslint-plugin-import_vx.x.x.js +++ b/flow-typed/npm/eslint-plugin-import_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: 6fbf610c7f983a335def5ef026c6096a -// flow-typed version: <>/eslint-plugin-import_v^2.3.0/flow_v0.54.1 +// flow-typed signature: f8d6fbc6d273624ced514c6631ff38ef +// flow-typed version: <>/eslint-plugin-import_v^2.13.0/flow_v0.76.0 /** * This is an autogenerated libdef stub for: @@ -58,6 +58,10 @@ declare module 'eslint-plugin-import/lib/core/staticRequire' { declare module.exports: any; } +declare module 'eslint-plugin-import/lib/docsUrl' { + declare module.exports: any; +} + declare module 'eslint-plugin-import/lib/ExportMap' { declare module.exports: any; } @@ -74,10 +78,18 @@ declare module 'eslint-plugin-import/lib/rules/default' { declare module.exports: any; } +declare module 'eslint-plugin-import/lib/rules/dynamic-import-chunkname' { + declare module.exports: any; +} + declare module 'eslint-plugin-import/lib/rules/export' { declare module.exports: any; } +declare module 'eslint-plugin-import/lib/rules/exports-last' { + declare module.exports: any; +} + declare module 'eslint-plugin-import/lib/rules/extensions' { declare module.exports: any; } @@ -86,6 +98,10 @@ declare module 'eslint-plugin-import/lib/rules/first' { declare module.exports: any; } +declare module 'eslint-plugin-import/lib/rules/group-exports' { + declare module.exports: any; +} + declare module 'eslint-plugin-import/lib/rules/imports-first' { declare module.exports: any; } @@ -122,6 +138,14 @@ declare module 'eslint-plugin-import/lib/rules/no-commonjs' { declare module.exports: any; } +declare module 'eslint-plugin-import/lib/rules/no-cycle' { + declare module.exports: any; +} + +declare module 'eslint-plugin-import/lib/rules/no-default-export' { + declare module.exports: any; +} + declare module 'eslint-plugin-import/lib/rules/no-deprecated' { declare module.exports: any; } @@ -166,10 +190,18 @@ declare module 'eslint-plugin-import/lib/rules/no-nodejs-modules' { declare module.exports: any; } +declare module 'eslint-plugin-import/lib/rules/no-relative-parent-imports' { + declare module.exports: any; +} + declare module 'eslint-plugin-import/lib/rules/no-restricted-paths' { declare module.exports: any; } +declare module 'eslint-plugin-import/lib/rules/no-self-import' { + declare module.exports: any; +} + declare module 'eslint-plugin-import/lib/rules/no-unassigned-import' { declare module.exports: any; } @@ -178,6 +210,10 @@ declare module 'eslint-plugin-import/lib/rules/no-unresolved' { declare module.exports: any; } +declare module 'eslint-plugin-import/lib/rules/no-useless-path-segments' { + declare module.exports: any; +} + declare module 'eslint-plugin-import/lib/rules/no-webpack-loader-syntax' { declare module.exports: any; } @@ -226,6 +262,9 @@ declare module 'eslint-plugin-import/lib/core/importType.js' { declare module 'eslint-plugin-import/lib/core/staticRequire.js' { declare module.exports: $Exports<'eslint-plugin-import/lib/core/staticRequire'>; } +declare module 'eslint-plugin-import/lib/docsUrl.js' { + declare module.exports: $Exports<'eslint-plugin-import/lib/docsUrl'>; +} declare module 'eslint-plugin-import/lib/ExportMap.js' { declare module.exports: $Exports<'eslint-plugin-import/lib/ExportMap'>; } @@ -238,15 +277,24 @@ declare module 'eslint-plugin-import/lib/index.js' { declare module 'eslint-plugin-import/lib/rules/default.js' { declare module.exports: $Exports<'eslint-plugin-import/lib/rules/default'>; } +declare module 'eslint-plugin-import/lib/rules/dynamic-import-chunkname.js' { + declare module.exports: $Exports<'eslint-plugin-import/lib/rules/dynamic-import-chunkname'>; +} declare module 'eslint-plugin-import/lib/rules/export.js' { declare module.exports: $Exports<'eslint-plugin-import/lib/rules/export'>; } +declare module 'eslint-plugin-import/lib/rules/exports-last.js' { + declare module.exports: $Exports<'eslint-plugin-import/lib/rules/exports-last'>; +} declare module 'eslint-plugin-import/lib/rules/extensions.js' { declare module.exports: $Exports<'eslint-plugin-import/lib/rules/extensions'>; } declare module 'eslint-plugin-import/lib/rules/first.js' { declare module.exports: $Exports<'eslint-plugin-import/lib/rules/first'>; } +declare module 'eslint-plugin-import/lib/rules/group-exports.js' { + declare module.exports: $Exports<'eslint-plugin-import/lib/rules/group-exports'>; +} declare module 'eslint-plugin-import/lib/rules/imports-first.js' { declare module.exports: $Exports<'eslint-plugin-import/lib/rules/imports-first'>; } @@ -274,6 +322,12 @@ declare module 'eslint-plugin-import/lib/rules/no-anonymous-default-export.js' { declare module 'eslint-plugin-import/lib/rules/no-commonjs.js' { declare module.exports: $Exports<'eslint-plugin-import/lib/rules/no-commonjs'>; } +declare module 'eslint-plugin-import/lib/rules/no-cycle.js' { + declare module.exports: $Exports<'eslint-plugin-import/lib/rules/no-cycle'>; +} +declare module 'eslint-plugin-import/lib/rules/no-default-export.js' { + declare module.exports: $Exports<'eslint-plugin-import/lib/rules/no-default-export'>; +} declare module 'eslint-plugin-import/lib/rules/no-deprecated.js' { declare module.exports: $Exports<'eslint-plugin-import/lib/rules/no-deprecated'>; } @@ -307,15 +361,24 @@ declare module 'eslint-plugin-import/lib/rules/no-namespace.js' { declare module 'eslint-plugin-import/lib/rules/no-nodejs-modules.js' { declare module.exports: $Exports<'eslint-plugin-import/lib/rules/no-nodejs-modules'>; } +declare module 'eslint-plugin-import/lib/rules/no-relative-parent-imports.js' { + declare module.exports: $Exports<'eslint-plugin-import/lib/rules/no-relative-parent-imports'>; +} declare module 'eslint-plugin-import/lib/rules/no-restricted-paths.js' { declare module.exports: $Exports<'eslint-plugin-import/lib/rules/no-restricted-paths'>; } +declare module 'eslint-plugin-import/lib/rules/no-self-import.js' { + declare module.exports: $Exports<'eslint-plugin-import/lib/rules/no-self-import'>; +} declare module 'eslint-plugin-import/lib/rules/no-unassigned-import.js' { declare module.exports: $Exports<'eslint-plugin-import/lib/rules/no-unassigned-import'>; } declare module 'eslint-plugin-import/lib/rules/no-unresolved.js' { declare module.exports: $Exports<'eslint-plugin-import/lib/rules/no-unresolved'>; } +declare module 'eslint-plugin-import/lib/rules/no-useless-path-segments.js' { + declare module.exports: $Exports<'eslint-plugin-import/lib/rules/no-useless-path-segments'>; +} declare module 'eslint-plugin-import/lib/rules/no-webpack-loader-syntax.js' { declare module.exports: $Exports<'eslint-plugin-import/lib/rules/no-webpack-loader-syntax'>; } diff --git a/flow-typed/npm/eslint-plugin-prettier_vx.x.x.js b/flow-typed/npm/eslint-plugin-prettier_vx.x.x.js index d25f4b6d..89c7ec0c 100644 --- a/flow-typed/npm/eslint-plugin-prettier_vx.x.x.js +++ b/flow-typed/npm/eslint-plugin-prettier_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: b1370f19e3bbb4a40a87a527f06a977f -// flow-typed version: <>/eslint-plugin-prettier_v^2.2.0/flow_v0.54.1 +// flow-typed signature: a6c1f1668d6268e92e257ce12811053b +// flow-typed version: <>/eslint-plugin-prettier_v^2.6.2/flow_v0.76.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/eslint_vx.x.x.js b/flow-typed/npm/eslint_vx.x.x.js index f4e72b38..2d85c871 100644 --- a/flow-typed/npm/eslint_vx.x.x.js +++ b/flow-typed/npm/eslint_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: b4d0a131a8866156c23d02ea64f4f7fa -// flow-typed version: <>/eslint_v^4.6.1/flow_v0.54.1 +// flow-typed signature: ba08f3dee57234feb6d7af4470bc84e4 +// flow-typed version: <>/eslint_v^5.1.0/flow_v0.76.0 /** * This is an autogenerated libdef stub for: @@ -34,10 +34,6 @@ declare module 'eslint/conf/default-cli-options' { declare module.exports: any; } -declare module 'eslint/conf/default-config-options' { - declare module.exports: any; -} - declare module 'eslint/conf/environments' { declare module.exports: any; } @@ -190,14 +186,6 @@ declare module 'eslint/lib/ignored-paths' { declare module.exports: any; } -declare module 'eslint/lib/internal-rules/internal-consistent-docs-description' { - declare module.exports: any; -} - -declare module 'eslint/lib/internal-rules/internal-no-invalid-meta' { - declare module.exports: any; -} - declare module 'eslint/lib/linter' { declare module.exports: any; } @@ -394,6 +382,10 @@ declare module 'eslint/lib/rules/id-match' { declare module.exports: any; } +declare module 'eslint/lib/rules/implicit-arrow-linebreak' { + declare module.exports: any; +} + declare module 'eslint/lib/rules/indent-legacy' { declare module.exports: any; } @@ -434,6 +426,14 @@ declare module 'eslint/lib/rules/lines-around-directive' { declare module.exports: any; } +declare module 'eslint/lib/rules/lines-between-class-members' { + declare module.exports: any; +} + +declare module 'eslint/lib/rules/max-classes-per-file' { + declare module.exports: any; +} + declare module 'eslint/lib/rules/max-depth' { declare module.exports: any; } @@ -442,6 +442,10 @@ declare module 'eslint/lib/rules/max-len' { declare module.exports: any; } +declare module 'eslint/lib/rules/max-lines-per-function' { + declare module.exports: any; +} + declare module 'eslint/lib/rules/max-lines' { declare module.exports: any; } @@ -462,6 +466,10 @@ declare module 'eslint/lib/rules/max-statements' { declare module.exports: any; } +declare module 'eslint/lib/rules/multiline-comment-style' { + declare module.exports: any; +} + declare module 'eslint/lib/rules/multiline-ternary' { declare module.exports: any; } @@ -1086,6 +1094,10 @@ declare module 'eslint/lib/rules/prefer-numeric-literals' { declare module.exports: any; } +declare module 'eslint/lib/rules/prefer-object-spread' { + declare module.exports: any; +} + declare module 'eslint/lib/rules/prefer-promise-reject-errors' { declare module.exports: any; } @@ -1302,6 +1314,10 @@ declare module 'eslint/lib/util/ajv' { declare module.exports: any; } +declare module 'eslint/lib/util/apply-disable-directives' { + declare module.exports: any; +} + declare module 'eslint/lib/util/fix-tracker' { declare module.exports: any; } @@ -1318,6 +1334,10 @@ declare module 'eslint/lib/util/hash' { declare module.exports: any; } +declare module 'eslint/lib/util/interpolate' { + declare module.exports: any; +} + declare module 'eslint/lib/util/keywords' { declare module.exports: any; } @@ -1326,6 +1346,10 @@ declare module 'eslint/lib/util/module-resolver' { declare module.exports: any; } +declare module 'eslint/lib/util/naming' { + declare module.exports: any; +} + declare module 'eslint/lib/util/node-event-generator' { declare module.exports: any; } @@ -1346,6 +1370,10 @@ declare module 'eslint/lib/util/rule-fixer' { declare module.exports: any; } +declare module 'eslint/lib/util/safe-emitter' { + declare module.exports: any; +} + declare module 'eslint/lib/util/source-code-fixer' { declare module.exports: any; } @@ -1376,9 +1404,6 @@ declare module 'eslint/conf/config-schema.js' { declare module 'eslint/conf/default-cli-options.js' { declare module.exports: $Exports<'eslint/conf/default-cli-options'>; } -declare module 'eslint/conf/default-config-options.js' { - declare module.exports: $Exports<'eslint/conf/default-config-options'>; -} declare module 'eslint/conf/environments.js' { declare module.exports: $Exports<'eslint/conf/environments'>; } @@ -1493,12 +1518,6 @@ declare module 'eslint/lib/formatters/visualstudio.js' { declare module 'eslint/lib/ignored-paths.js' { declare module.exports: $Exports<'eslint/lib/ignored-paths'>; } -declare module 'eslint/lib/internal-rules/internal-consistent-docs-description.js' { - declare module.exports: $Exports<'eslint/lib/internal-rules/internal-consistent-docs-description'>; -} -declare module 'eslint/lib/internal-rules/internal-no-invalid-meta.js' { - declare module.exports: $Exports<'eslint/lib/internal-rules/internal-no-invalid-meta'>; -} declare module 'eslint/lib/linter.js' { declare module.exports: $Exports<'eslint/lib/linter'>; } @@ -1646,6 +1665,9 @@ declare module 'eslint/lib/rules/id-length.js' { declare module 'eslint/lib/rules/id-match.js' { declare module.exports: $Exports<'eslint/lib/rules/id-match'>; } +declare module 'eslint/lib/rules/implicit-arrow-linebreak.js' { + declare module.exports: $Exports<'eslint/lib/rules/implicit-arrow-linebreak'>; +} declare module 'eslint/lib/rules/indent-legacy.js' { declare module.exports: $Exports<'eslint/lib/rules/indent-legacy'>; } @@ -1676,12 +1698,21 @@ declare module 'eslint/lib/rules/lines-around-comment.js' { declare module 'eslint/lib/rules/lines-around-directive.js' { declare module.exports: $Exports<'eslint/lib/rules/lines-around-directive'>; } +declare module 'eslint/lib/rules/lines-between-class-members.js' { + declare module.exports: $Exports<'eslint/lib/rules/lines-between-class-members'>; +} +declare module 'eslint/lib/rules/max-classes-per-file.js' { + declare module.exports: $Exports<'eslint/lib/rules/max-classes-per-file'>; +} declare module 'eslint/lib/rules/max-depth.js' { declare module.exports: $Exports<'eslint/lib/rules/max-depth'>; } declare module 'eslint/lib/rules/max-len.js' { declare module.exports: $Exports<'eslint/lib/rules/max-len'>; } +declare module 'eslint/lib/rules/max-lines-per-function.js' { + declare module.exports: $Exports<'eslint/lib/rules/max-lines-per-function'>; +} declare module 'eslint/lib/rules/max-lines.js' { declare module.exports: $Exports<'eslint/lib/rules/max-lines'>; } @@ -1697,6 +1728,9 @@ declare module 'eslint/lib/rules/max-statements-per-line.js' { declare module 'eslint/lib/rules/max-statements.js' { declare module.exports: $Exports<'eslint/lib/rules/max-statements'>; } +declare module 'eslint/lib/rules/multiline-comment-style.js' { + declare module.exports: $Exports<'eslint/lib/rules/multiline-comment-style'>; +} declare module 'eslint/lib/rules/multiline-ternary.js' { declare module.exports: $Exports<'eslint/lib/rules/multiline-ternary'>; } @@ -2165,6 +2199,9 @@ declare module 'eslint/lib/rules/prefer-destructuring.js' { declare module 'eslint/lib/rules/prefer-numeric-literals.js' { declare module.exports: $Exports<'eslint/lib/rules/prefer-numeric-literals'>; } +declare module 'eslint/lib/rules/prefer-object-spread.js' { + declare module.exports: $Exports<'eslint/lib/rules/prefer-object-spread'>; +} declare module 'eslint/lib/rules/prefer-promise-reject-errors.js' { declare module.exports: $Exports<'eslint/lib/rules/prefer-promise-reject-errors'>; } @@ -2327,6 +2364,9 @@ declare module 'eslint/lib/token-store/utils.js' { declare module 'eslint/lib/util/ajv.js' { declare module.exports: $Exports<'eslint/lib/util/ajv'>; } +declare module 'eslint/lib/util/apply-disable-directives.js' { + declare module.exports: $Exports<'eslint/lib/util/apply-disable-directives'>; +} declare module 'eslint/lib/util/fix-tracker.js' { declare module.exports: $Exports<'eslint/lib/util/fix-tracker'>; } @@ -2339,12 +2379,18 @@ declare module 'eslint/lib/util/glob.js' { declare module 'eslint/lib/util/hash.js' { declare module.exports: $Exports<'eslint/lib/util/hash'>; } +declare module 'eslint/lib/util/interpolate.js' { + declare module.exports: $Exports<'eslint/lib/util/interpolate'>; +} declare module 'eslint/lib/util/keywords.js' { declare module.exports: $Exports<'eslint/lib/util/keywords'>; } declare module 'eslint/lib/util/module-resolver.js' { declare module.exports: $Exports<'eslint/lib/util/module-resolver'>; } +declare module 'eslint/lib/util/naming.js' { + declare module.exports: $Exports<'eslint/lib/util/naming'>; +} declare module 'eslint/lib/util/node-event-generator.js' { declare module.exports: $Exports<'eslint/lib/util/node-event-generator'>; } @@ -2360,6 +2406,9 @@ declare module 'eslint/lib/util/patterns/letters.js' { declare module 'eslint/lib/util/rule-fixer.js' { declare module.exports: $Exports<'eslint/lib/util/rule-fixer'>; } +declare module 'eslint/lib/util/safe-emitter.js' { + declare module.exports: $Exports<'eslint/lib/util/safe-emitter'>; +} declare module 'eslint/lib/util/source-code-fixer.js' { declare module.exports: $Exports<'eslint/lib/util/source-code-fixer'>; } diff --git a/flow-typed/npm/jest_v20.x.x.js b/flow-typed/npm/jest_v20.x.x.js deleted file mode 100644 index 25fbe318..00000000 --- a/flow-typed/npm/jest_v20.x.x.js +++ /dev/null @@ -1,483 +0,0 @@ -// flow-typed signature: 336a37cc59a5628d581d11f98d1d94ab -// flow-typed version: ef52b40a4e/jest_v20.x.x/flow_>=v0.33.x - -type JestMockFn = { - (...args: Array): any, - /** - * An object for introspecting mock calls - */ - mock: { - /** - * An array that represents all calls that have been made into this mock - * function. Each call is represented by an array of arguments that were - * passed during the call. - */ - calls: Array>, - /** - * An array that contains all the object instances that have been - * instantiated from this mock function. - */ - instances: mixed - }, - /** - * Resets all information stored in the mockFn.mock.calls and - * mockFn.mock.instances arrays. Often this is useful when you want to clean - * up a mock's usage data between two assertions. - */ - mockClear(): Function, - /** - * Resets all information stored in the mock. This is useful when you want to - * completely restore a mock back to its initial state. - */ - mockReset(): Function, - /** - * Removes the mock and restores the initial implementation. This is useful - * when you want to mock functions in certain test cases and restore the - * original implementation in others. Beware that mockFn.mockRestore only - * works when mock was created with jest.spyOn. Thus you have to take care of - * restoration yourself when manually assigning jest.fn(). - */ - mockRestore(): Function, - /** - * Accepts a function that should be used as the implementation of the mock. - * The mock itself will still record all calls that go into and instances - * that come from itself -- the only difference is that the implementation - * will also be executed when the mock is called. - */ - mockImplementation(fn: Function): JestMockFn, - /** - * Accepts a function that will be used as an implementation of the mock for - * one call to the mocked function. Can be chained so that multiple function - * calls produce different results. - */ - mockImplementationOnce(fn: Function): JestMockFn, - /** - * Just a simple sugar function for returning `this` - */ - mockReturnThis(): void, - /** - * Deprecated: use jest.fn(() => value) instead - */ - mockReturnValue(value: any): JestMockFn, - /** - * Sugar for only returning a value once inside your mock - */ - mockReturnValueOnce(value: any): JestMockFn -}; - -type JestAsymmetricEqualityType = { - /** - * A custom Jasmine equality tester - */ - asymmetricMatch(value: mixed): boolean -}; - -type JestCallsType = { - allArgs(): mixed, - all(): mixed, - any(): boolean, - count(): number, - first(): mixed, - mostRecent(): mixed, - reset(): void -}; - -type JestClockType = { - install(): void, - mockDate(date: Date): void, - tick(milliseconds?: number): void, - uninstall(): void -}; - -type JestMatcherResult = { - message?: string | (() => string), - pass: boolean -}; - -type JestMatcher = (actual: any, expected: any) => JestMatcherResult; - -type JestPromiseType = { - /** - * Use rejects to unwrap the reason of a rejected promise so any other - * matcher can be chained. If the promise is fulfilled the assertion fails. - */ - rejects: JestExpectType, - /** - * Use resolves to unwrap the value of a fulfilled promise so any other - * matcher can be chained. If the promise is rejected the assertion fails. - */ - resolves: JestExpectType -}; - -type JestExpectType = { - not: JestExpectType, - /** - * If you have a mock function, you can use .lastCalledWith to test what - * arguments it was last called with. - */ - lastCalledWith(...args: Array): void, - /** - * toBe just checks that a value is what you expect. It uses === to check - * strict equality. - */ - toBe(value: any): void, - /** - * Use .toHaveBeenCalled to ensure that a mock function got called. - */ - toBeCalled(): void, - /** - * Use .toBeCalledWith to ensure that a mock function was called with - * specific arguments. - */ - toBeCalledWith(...args: Array): void, - /** - * Using exact equality with floating point numbers is a bad idea. Rounding - * means that intuitive things fail. - */ - toBeCloseTo(num: number, delta: any): void, - /** - * Use .toBeDefined to check that a variable is not undefined. - */ - toBeDefined(): void, - /** - * Use .toBeFalsy when you don't care what a value is, you just want to - * ensure a value is false in a boolean context. - */ - toBeFalsy(): void, - /** - * To compare floating point numbers, you can use toBeGreaterThan. - */ - toBeGreaterThan(number: number): void, - /** - * To compare floating point numbers, you can use toBeGreaterThanOrEqual. - */ - toBeGreaterThanOrEqual(number: number): void, - /** - * To compare floating point numbers, you can use toBeLessThan. - */ - toBeLessThan(number: number): void, - /** - * To compare floating point numbers, you can use toBeLessThanOrEqual. - */ - toBeLessThanOrEqual(number: number): void, - /** - * Use .toBeInstanceOf(Class) to check that an object is an instance of a - * class. - */ - toBeInstanceOf(cls: Class<*>): void, - /** - * .toBeNull() is the same as .toBe(null) but the error messages are a bit - * nicer. - */ - toBeNull(): void, - /** - * Use .toBeTruthy when you don't care what a value is, you just want to - * ensure a value is true in a boolean context. - */ - toBeTruthy(): void, - /** - * Use .toBeUndefined to check that a variable is undefined. - */ - toBeUndefined(): void, - /** - * Use .toContain when you want to check that an item is in a list. For - * testing the items in the list, this uses ===, a strict equality check. - */ - toContain(item: any): void, - /** - * Use .toContainEqual when you want to check that an item is in a list. For - * testing the items in the list, this matcher recursively checks the - * equality of all fields, rather than checking for object identity. - */ - toContainEqual(item: any): void, - /** - * Use .toEqual when you want to check that two objects have the same value. - * This matcher recursively checks the equality of all fields, rather than - * checking for object identity. - */ - toEqual(value: any): void, - /** - * Use .toHaveBeenCalled to ensure that a mock function got called. - */ - toHaveBeenCalled(): void, - /** - * Use .toHaveBeenCalledTimes to ensure that a mock function got called exact - * number of times. - */ - toHaveBeenCalledTimes(number: number): void, - /** - * Use .toHaveBeenCalledWith to ensure that a mock function was called with - * specific arguments. - */ - toHaveBeenCalledWith(...args: Array): void, - /** - * Check that an object has a .length property and it is set to a certain - * numeric value. - */ - toHaveLength(number: number): void, - /** - * - */ - toHaveProperty(propPath: string, value?: any): void, - /** - * Use .toMatch to check that a string matches a regular expression. - */ - toMatch(regexp: RegExp): void, - /** - * Use .toMatchObject to check that a javascript object matches a subset of the properties of an object. - */ - toMatchObject(object: Object): void, - /** - * This ensures that a React component matches the most recent snapshot. - */ - toMatchSnapshot(name?: string): void, - /** - * Use .toThrow to test that a function throws when it is called. - * If you want to test that a specific error gets thrown, you can provide an - * argument to toThrow. The argument can be a string for the error message, - * a class for the error, or a regex that should match the error. - * - * Alias: .toThrowError - */ - toThrow(message?: string | Error | RegExp): void, - toThrowError(message?: string | Error | RegExp): void, - /** - * Use .toThrowErrorMatchingSnapshot to test that a function throws a error - * matching the most recent snapshot when it is called. - */ - toThrowErrorMatchingSnapshot(): void -}; - -type JestObjectType = { - /** - * Disables automatic mocking in the module loader. - * - * After this method is called, all `require()`s will return the real - * versions of each module (rather than a mocked version). - */ - disableAutomock(): JestObjectType, - /** - * An un-hoisted version of disableAutomock - */ - autoMockOff(): JestObjectType, - /** - * Enables automatic mocking in the module loader. - */ - enableAutomock(): JestObjectType, - /** - * An un-hoisted version of enableAutomock - */ - autoMockOn(): JestObjectType, - /** - * Clears the mock.calls and mock.instances properties of all mocks. - * Equivalent to calling .mockClear() on every mocked function. - */ - clearAllMocks(): JestObjectType, - /** - * Resets the state of all mocks. Equivalent to calling .mockReset() on every - * mocked function. - */ - resetAllMocks(): JestObjectType, - /** - * Removes any pending timers from the timer system. - */ - clearAllTimers(): void, - /** - * The same as `mock` but not moved to the top of the expectation by - * babel-jest. - */ - doMock(moduleName: string, moduleFactory?: any): JestObjectType, - /** - * The same as `unmock` but not moved to the top of the expectation by - * babel-jest. - */ - dontMock(moduleName: string): JestObjectType, - /** - * Returns a new, unused mock function. Optionally takes a mock - * implementation. - */ - fn(implementation?: Function): JestMockFn, - /** - * Determines if the given function is a mocked function. - */ - isMockFunction(fn: Function): boolean, - /** - * Given the name of a module, use the automatic mocking system to generate a - * mocked version of the module for you. - */ - genMockFromModule(moduleName: string): any, - /** - * Mocks a module with an auto-mocked version when it is being required. - * - * The second argument can be used to specify an explicit module factory that - * is being run instead of using Jest's automocking feature. - * - * The third argument can be used to create virtual mocks -- mocks of modules - * that don't exist anywhere in the system. - */ - mock( - moduleName: string, - moduleFactory?: any, - options?: Object - ): JestObjectType, - /** - * Resets the module registry - the cache of all required modules. This is - * useful to isolate modules where local state might conflict between tests. - */ - resetModules(): JestObjectType, - /** - * Exhausts the micro-task queue (usually interfaced in node via - * process.nextTick). - */ - runAllTicks(): void, - /** - * Exhausts the macro-task queue (i.e., all tasks queued by setTimeout(), - * setInterval(), and setImmediate()). - */ - runAllTimers(): void, - /** - * Exhausts all tasks queued by setImmediate(). - */ - runAllImmediates(): void, - /** - * Executes only the macro task queue (i.e. all tasks queued by setTimeout() - * or setInterval() and setImmediate()). - */ - runTimersToTime(msToRun: number): void, - /** - * Executes only the macro-tasks that are currently pending (i.e., only the - * tasks that have been queued by setTimeout() or setInterval() up to this - * point) - */ - runOnlyPendingTimers(): void, - /** - * Explicitly supplies the mock object that the module system should return - * for the specified module. Note: It is recommended to use jest.mock() - * instead. - */ - setMock(moduleName: string, moduleExports: any): JestObjectType, - /** - * Indicates that the module system should never return a mocked version of - * the specified module from require() (e.g. that it should always return the - * real module). - */ - unmock(moduleName: string): JestObjectType, - /** - * Instructs Jest to use fake versions of the standard timer functions - * (setTimeout, setInterval, clearTimeout, clearInterval, nextTick, - * setImmediate and clearImmediate). - */ - useFakeTimers(): JestObjectType, - /** - * Instructs Jest to use the real versions of the standard timer functions. - */ - useRealTimers(): JestObjectType, - /** - * Creates a mock function similar to jest.fn but also tracks calls to - * object[methodName]. - */ - spyOn(object: Object, methodName: string): JestMockFn -}; - -type JestSpyType = { - calls: JestCallsType -}; - -/** Runs this function after every test inside this context */ -declare function afterEach(fn: Function): void; -/** Runs this function before every test inside this context */ -declare function beforeEach(fn: Function): void; -/** Runs this function after all tests have finished inside this context */ -declare function afterAll(fn: Function): void; -/** Runs this function before any tests have started inside this context */ -declare function beforeAll(fn: Function): void; -/** A context for grouping tests together */ -declare function describe(name: string, fn: Function): void; - -/** An individual test unit */ -declare var it: { - /** - * An individual test unit - * - * @param {string} Name of Test - * @param {Function} Test - */ - (name: string, fn?: Function): ?Promise, - /** - * Only run this test - * - * @param {string} Name of Test - * @param {Function} Test - */ - only(name: string, fn?: Function): ?Promise, - /** - * Skip running this test - * - * @param {string} Name of Test - * @param {Function} Test - */ - skip(name: string, fn?: Function): ?Promise, - /** - * Run the test concurrently - * - * @param {string} Name of Test - * @param {Function} Test - */ - concurrent(name: string, fn?: Function): ?Promise -}; -declare function fit(name: string, fn: Function): ?Promise; -/** An individual test unit */ -declare var test: typeof it; -/** A disabled group of tests */ -declare var xdescribe: typeof describe; -/** A focused group of tests */ -declare var fdescribe: typeof describe; -/** A disabled individual test */ -declare var xit: typeof it; -/** A disabled individual test */ -declare var xtest: typeof it; - -/** The expect function is used every time you want to test a value */ -declare var expect: { - /** The object that you want to make assertions against */ - (value: any): JestExpectType & JestPromiseType, - /** Add additional Jasmine matchers to Jest's roster */ - extend(matchers: { [name: string]: JestMatcher }): void, - /** Add a module that formats application-specific data structures. */ - addSnapshotSerializer(serializer: (input: Object) => string): void, - assertions(expectedAssertions: number): void, - hasAssertions(): void, - any(value: mixed): JestAsymmetricEqualityType, - anything(): void, - arrayContaining(value: Array): void, - objectContaining(value: Object): void, - /** Matches any received string that contains the exact expected string. */ - stringContaining(value: string): void, - stringMatching(value: string | RegExp): void -}; - -// TODO handle return type -// http://jasmine.github.io/2.4/introduction.html#section-Spies -declare function spyOn(value: mixed, method: string): Object; - -/** Holds all functions related to manipulating test runner */ -declare var jest: JestObjectType; - -/** - * The global Jamine object, this is generally not exposed as the public API, - * using features inside here could break in later versions of Jest. - */ -declare var jasmine: { - DEFAULT_TIMEOUT_INTERVAL: number, - any(value: mixed): JestAsymmetricEqualityType, - anything(): void, - arrayContaining(value: Array): void, - clock(): JestClockType, - createSpy(name: string): JestSpyType, - createSpyObj( - baseName: string, - methodNames: Array - ): { [methodName: string]: JestSpyType }, - objectContaining(value: Object): void, - stringMatching(value: string): void -}; diff --git a/flow-typed/npm/jest_v23.x.x.js b/flow-typed/npm/jest_v23.x.x.js new file mode 100644 index 00000000..43faf9cf --- /dev/null +++ b/flow-typed/npm/jest_v23.x.x.js @@ -0,0 +1,1104 @@ +// flow-typed signature: ad251f3a3446f6ab4e6691a94e701cad +// flow-typed version: caa120caaa/jest_v23.x.x/flow_>=v0.39.x + +type JestMockFn, TReturn> = { + (...args: TArguments): TReturn, + /** + * An object for introspecting mock calls + */ + mock: { + /** + * An array that represents all calls that have been made into this mock + * function. Each call is represented by an array of arguments that were + * passed during the call. + */ + calls: Array, + /** + * An array that contains all the object instances that have been + * instantiated from this mock function. + */ + instances: Array + }, + /** + * Resets all information stored in the mockFn.mock.calls and + * mockFn.mock.instances arrays. Often this is useful when you want to clean + * up a mock's usage data between two assertions. + */ + mockClear(): void, + /** + * Resets all information stored in the mock. This is useful when you want to + * completely restore a mock back to its initial state. + */ + mockReset(): void, + /** + * Removes the mock and restores the initial implementation. This is useful + * when you want to mock functions in certain test cases and restore the + * original implementation in others. Beware that mockFn.mockRestore only + * works when mock was created with jest.spyOn. Thus you have to take care of + * restoration yourself when manually assigning jest.fn(). + */ + mockRestore(): void, + /** + * Accepts a function that should be used as the implementation of the mock. + * The mock itself will still record all calls that go into and instances + * that come from itself -- the only difference is that the implementation + * will also be executed when the mock is called. + */ + mockImplementation( + fn: (...args: TArguments) => TReturn + ): JestMockFn, + /** + * Accepts a function that will be used as an implementation of the mock for + * one call to the mocked function. Can be chained so that multiple function + * calls produce different results. + */ + mockImplementationOnce( + fn: (...args: TArguments) => TReturn + ): JestMockFn, + /** + * Accepts a string to use in test result output in place of "jest.fn()" to + * indicate which mock function is being referenced. + */ + mockName(name: string): JestMockFn, + /** + * Just a simple sugar function for returning `this` + */ + mockReturnThis(): void, + /** + * Accepts a value that will be returned whenever the mock function is called. + */ + mockReturnValue(value: TReturn): JestMockFn, + /** + * Sugar for only returning a value once inside your mock + */ + mockReturnValueOnce(value: TReturn): JestMockFn, + /** + * Sugar for jest.fn().mockImplementation(() => Promise.resolve(value)) + */ + mockResolvedValue(value: TReturn): JestMockFn>, + /** + * Sugar for jest.fn().mockImplementationOnce(() => Promise.resolve(value)) + */ + mockResolvedValueOnce(value: TReturn): JestMockFn>, + /** + * Sugar for jest.fn().mockImplementation(() => Promise.reject(value)) + */ + mockRejectedValue(value: TReturn): JestMockFn>, + /** + * Sugar for jest.fn().mockImplementationOnce(() => Promise.reject(value)) + */ + mockRejectedValueOnce(value: TReturn): JestMockFn> +}; + +type JestAsymmetricEqualityType = { + /** + * A custom Jasmine equality tester + */ + asymmetricMatch(value: mixed): boolean +}; + +type JestCallsType = { + allArgs(): mixed, + all(): mixed, + any(): boolean, + count(): number, + first(): mixed, + mostRecent(): mixed, + reset(): void +}; + +type JestClockType = { + install(): void, + mockDate(date: Date): void, + tick(milliseconds?: number): void, + uninstall(): void +}; + +type JestMatcherResult = { + message?: string | (() => string), + pass: boolean +}; + +type JestMatcher = (actual: any, expected: any) => JestMatcherResult; + +type JestPromiseType = { + /** + * Use rejects to unwrap the reason of a rejected promise so any other + * matcher can be chained. If the promise is fulfilled the assertion fails. + */ + rejects: JestExpectType, + /** + * Use resolves to unwrap the value of a fulfilled promise so any other + * matcher can be chained. If the promise is rejected the assertion fails. + */ + resolves: JestExpectType +}; + +/** + * Jest allows functions and classes to be used as test names in test() and + * describe() + */ +type JestTestName = string | Function; + +/** + * Plugin: jest-styled-components + */ + +type JestStyledComponentsMatcherValue = + | string + | JestAsymmetricEqualityType + | RegExp + | typeof undefined; + +type JestStyledComponentsMatcherOptions = { + media?: string; + modifier?: string; + supports?: string; +} + +type JestStyledComponentsMatchersType = { + toHaveStyleRule( + property: string, + value: JestStyledComponentsMatcherValue, + options?: JestStyledComponentsMatcherOptions + ): void, +}; + +/** + * Plugin: jest-enzyme + */ +type EnzymeMatchersType = { + toBeChecked(): void, + toBeDisabled(): void, + toBeEmpty(): void, + toBeEmptyRender(): void, + toBePresent(): void, + toContainReact(element: React$Element): void, + toExist(): void, + toHaveClassName(className: string): void, + toHaveHTML(html: string): void, + toHaveProp: ((propKey: string, propValue?: any) => void) & ((props: Object) => void), + toHaveRef(refName: string): void, + toHaveState: ((stateKey: string, stateValue?: any) => void) & ((state: Object) => void), + toHaveStyle: ((styleKey: string, styleValue?: any) => void) & ((style: Object) => void), + toHaveTagName(tagName: string): void, + toHaveText(text: string): void, + toIncludeText(text: string): void, + toHaveValue(value: any): void, + toMatchElement(element: React$Element): void, + toMatchSelector(selector: string): void +}; + +// DOM testing library extensions https://github.com/kentcdodds/dom-testing-library#custom-jest-matchers +type DomTestingLibraryType = { + toBeInTheDOM(): void, + toHaveTextContent(content: string): void, + toHaveAttribute(name: string, expectedValue?: string): void +}; + +// Jest JQuery Matchers: https://github.com/unindented/custom-jquery-matchers +type JestJQueryMatchersType = { + toExist(): void, + toHaveLength(len: number): void, + toHaveId(id: string): void, + toHaveClass(className: string): void, + toHaveTag(tag: string): void, + toHaveAttr(key: string, val?: any): void, + toHaveProp(key: string, val?: any): void, + toHaveText(text: string | RegExp): void, + toHaveData(key: string, val?: any): void, + toHaveValue(val: any): void, + toHaveCss(css: {[key: string]: any}): void, + toBeChecked(): void, + toBeDisabled(): void, + toBeEmpty(): void, + toBeHidden(): void, + toBeSelected(): void, + toBeVisible(): void, + toBeFocused(): void, + toBeInDom(): void, + toBeMatchedBy(sel: string): void, + toHaveDescendant(sel: string): void, + toHaveDescendantWithText(sel: string, text: string | RegExp): void +}; + + +// Jest Extended Matchers: https://github.com/jest-community/jest-extended +type JestExtendedMatchersType = { + /** + * Note: Currently unimplemented + * Passing assertion + * + * @param {String} message + */ + // pass(message: string): void; + + /** + * Note: Currently unimplemented + * Failing assertion + * + * @param {String} message + */ + // fail(message: string): void; + + /** + * Use .toBeEmpty when checking if a String '', Array [] or Object {} is empty. + */ + toBeEmpty(): void; + + /** + * Use .toBeOneOf when checking if a value is a member of a given Array. + * @param {Array.<*>} members + */ + toBeOneOf(members: any[]): void; + + /** + * Use `.toBeNil` when checking a value is `null` or `undefined`. + */ + toBeNil(): void; + + /** + * Use `.toSatisfy` when you want to use a custom matcher by supplying a predicate function that returns a `Boolean`. + * @param {Function} predicate + */ + toSatisfy(predicate: (n: any) => boolean): void; + + /** + * Use `.toBeArray` when checking if a value is an `Array`. + */ + toBeArray(): void; + + /** + * Use `.toBeArrayOfSize` when checking if a value is an `Array` of size x. + * @param {Number} x + */ + toBeArrayOfSize(x: number): void; + + /** + * Use `.toIncludeAllMembers` when checking if an `Array` contains all of the same members of a given set. + * @param {Array.<*>} members + */ + toIncludeAllMembers(members: any[]): void; + + /** + * Use `.toIncludeAnyMembers` when checking if an `Array` contains any of the members of a given set. + * @param {Array.<*>} members + */ + toIncludeAnyMembers(members: any[]): void; + + /** + * Use `.toSatisfyAll` when you want to use a custom matcher by supplying a predicate function that returns a `Boolean` for all values in an array. + * @param {Function} predicate + */ + toSatisfyAll(predicate: (n: any) => boolean): void; + + /** + * Use `.toBeBoolean` when checking if a value is a `Boolean`. + */ + toBeBoolean(): void; + + /** + * Use `.toBeTrue` when checking a value is equal (===) to `true`. + */ + toBeTrue(): void; + + /** + * Use `.toBeFalse` when checking a value is equal (===) to `false`. + */ + toBeFalse(): void; + + /** + * Use .toBeDate when checking if a value is a Date. + */ + toBeDate(): void; + + /** + * Use `.toBeFunction` when checking if a value is a `Function`. + */ + toBeFunction(): void; + + /** + * Use `.toHaveBeenCalledBefore` when checking if a `Mock` was called before another `Mock`. + * + * Note: Required Jest version >22 + * Note: Your mock functions will have to be asynchronous to cause the timestamps inside of Jest to occur in a differentJS event loop, otherwise the mock timestamps will all be the same + * + * @param {Mock} mock + */ + toHaveBeenCalledBefore(mock: JestMockFn): void; + + /** + * Use `.toBeNumber` when checking if a value is a `Number`. + */ + toBeNumber(): void; + + /** + * Use `.toBeNaN` when checking a value is `NaN`. + */ + toBeNaN(): void; + + /** + * Use `.toBeFinite` when checking if a value is a `Number`, not `NaN` or `Infinity`. + */ + toBeFinite(): void; + + /** + * Use `.toBePositive` when checking if a value is a positive `Number`. + */ + toBePositive(): void; + + /** + * Use `.toBeNegative` when checking if a value is a negative `Number`. + */ + toBeNegative(): void; + + /** + * Use `.toBeEven` when checking if a value is an even `Number`. + */ + toBeEven(): void; + + /** + * Use `.toBeOdd` when checking if a value is an odd `Number`. + */ + toBeOdd(): void; + + /** + * Use `.toBeWithin` when checking if a number is in between the given bounds of: start (inclusive) and end (exclusive). + * + * @param {Number} start + * @param {Number} end + */ + toBeWithin(start: number, end: number): void; + + /** + * Use `.toBeObject` when checking if a value is an `Object`. + */ + toBeObject(): void; + + /** + * Use `.toContainKey` when checking if an object contains the provided key. + * + * @param {String} key + */ + toContainKey(key: string): void; + + /** + * Use `.toContainKeys` when checking if an object has all of the provided keys. + * + * @param {Array.} keys + */ + toContainKeys(keys: string[]): void; + + /** + * Use `.toContainAllKeys` when checking if an object only contains all of the provided keys. + * + * @param {Array.} keys + */ + toContainAllKeys(keys: string[]): void; + + /** + * Use `.toContainAnyKeys` when checking if an object contains at least one of the provided keys. + * + * @param {Array.} keys + */ + toContainAnyKeys(keys: string[]): void; + + /** + * Use `.toContainValue` when checking if an object contains the provided value. + * + * @param {*} value + */ + toContainValue(value: any): void; + + /** + * Use `.toContainValues` when checking if an object contains all of the provided values. + * + * @param {Array.<*>} values + */ + toContainValues(values: any[]): void; + + /** + * Use `.toContainAllValues` when checking if an object only contains all of the provided values. + * + * @param {Array.<*>} values + */ + toContainAllValues(values: any[]): void; + + /** + * Use `.toContainAnyValues` when checking if an object contains at least one of the provided values. + * + * @param {Array.<*>} values + */ + toContainAnyValues(values: any[]): void; + + /** + * Use `.toContainEntry` when checking if an object contains the provided entry. + * + * @param {Array.} entry + */ + toContainEntry(entry: [string, string]): void; + + /** + * Use `.toContainEntries` when checking if an object contains all of the provided entries. + * + * @param {Array.>} entries + */ + toContainEntries(entries: [string, string][]): void; + + /** + * Use `.toContainAllEntries` when checking if an object only contains all of the provided entries. + * + * @param {Array.>} entries + */ + toContainAllEntries(entries: [string, string][]): void; + + /** + * Use `.toContainAnyEntries` when checking if an object contains at least one of the provided entries. + * + * @param {Array.>} entries + */ + toContainAnyEntries(entries: [string, string][]): void; + + /** + * Use `.toBeExtensible` when checking if an object is extensible. + */ + toBeExtensible(): void; + + /** + * Use `.toBeFrozen` when checking if an object is frozen. + */ + toBeFrozen(): void; + + /** + * Use `.toBeSealed` when checking if an object is sealed. + */ + toBeSealed(): void; + + /** + * Use `.toBeString` when checking if a value is a `String`. + */ + toBeString(): void; + + /** + * Use `.toEqualCaseInsensitive` when checking if a string is equal (===) to another ignoring the casing of both strings. + * + * @param {String} string + */ + toEqualCaseInsensitive(string: string): void; + + /** + * Use `.toStartWith` when checking if a `String` starts with a given `String` prefix. + * + * @param {String} prefix + */ + toStartWith(prefix: string): void; + + /** + * Use `.toEndWith` when checking if a `String` ends with a given `String` suffix. + * + * @param {String} suffix + */ + toEndWith(suffix: string): void; + + /** + * Use `.toInclude` when checking if a `String` includes the given `String` substring. + * + * @param {String} substring + */ + toInclude(substring: string): void; + + /** + * Use `.toIncludeRepeated` when checking if a `String` includes the given `String` substring the correct number of times. + * + * @param {String} substring + * @param {Number} times + */ + toIncludeRepeated(substring: string, times: number): void; + + /** + * Use `.toIncludeMultiple` when checking if a `String` includes all of the given substrings. + * + * @param {Array.} substring + */ + toIncludeMultiple(substring: string[]): void; +}; + +interface JestExpectType { + not: + & JestExpectType + & EnzymeMatchersType + & DomTestingLibraryType + & JestJQueryMatchersType + & JestStyledComponentsMatchersType + & JestExtendedMatchersType, + /** + * If you have a mock function, you can use .lastCalledWith to test what + * arguments it was last called with. + */ + lastCalledWith(...args: Array): void, + /** + * toBe just checks that a value is what you expect. It uses === to check + * strict equality. + */ + toBe(value: any): void, + /** + * Use .toBeCalledWith to ensure that a mock function was called with + * specific arguments. + */ + toBeCalledWith(...args: Array): void, + /** + * Using exact equality with floating point numbers is a bad idea. Rounding + * means that intuitive things fail. + */ + toBeCloseTo(num: number, delta: any): void, + /** + * Use .toBeDefined to check that a variable is not undefined. + */ + toBeDefined(): void, + /** + * Use .toBeFalsy when you don't care what a value is, you just want to + * ensure a value is false in a boolean context. + */ + toBeFalsy(): void, + /** + * To compare floating point numbers, you can use toBeGreaterThan. + */ + toBeGreaterThan(number: number): void, + /** + * To compare floating point numbers, you can use toBeGreaterThanOrEqual. + */ + toBeGreaterThanOrEqual(number: number): void, + /** + * To compare floating point numbers, you can use toBeLessThan. + */ + toBeLessThan(number: number): void, + /** + * To compare floating point numbers, you can use toBeLessThanOrEqual. + */ + toBeLessThanOrEqual(number: number): void, + /** + * Use .toBeInstanceOf(Class) to check that an object is an instance of a + * class. + */ + toBeInstanceOf(cls: Class<*>): void, + /** + * .toBeNull() is the same as .toBe(null) but the error messages are a bit + * nicer. + */ + toBeNull(): void, + /** + * Use .toBeTruthy when you don't care what a value is, you just want to + * ensure a value is true in a boolean context. + */ + toBeTruthy(): void, + /** + * Use .toBeUndefined to check that a variable is undefined. + */ + toBeUndefined(): void, + /** + * Use .toContain when you want to check that an item is in a list. For + * testing the items in the list, this uses ===, a strict equality check. + */ + toContain(item: any): void, + /** + * Use .toContainEqual when you want to check that an item is in a list. For + * testing the items in the list, this matcher recursively checks the + * equality of all fields, rather than checking for object identity. + */ + toContainEqual(item: any): void, + /** + * Use .toEqual when you want to check that two objects have the same value. + * This matcher recursively checks the equality of all fields, rather than + * checking for object identity. + */ + toEqual(value: any): void, + /** + * Use .toHaveBeenCalled to ensure that a mock function got called. + */ + toHaveBeenCalled(): void, + toBeCalled(): void; + /** + * Use .toHaveBeenCalledTimes to ensure that a mock function got called exact + * number of times. + */ + toHaveBeenCalledTimes(number: number): void, + toBeCalledTimes(number: number): void; + /** + * + */ + toHaveBeenNthCalledWith(nthCall: number, ...args: Array): void; + nthCalledWith(nthCall: number, ...args: Array): void; + /** + * + */ + toHaveReturned(): void; + toReturn(): void; + /** + * + */ + toHaveReturnedTimes(number: number): void; + toReturnTimes(number: number): void; + /** + * + */ + toHaveReturnedWith(value: any): void; + toReturnWith(value: any): void; + /** + * + */ + toHaveLastReturnedWith(value: any): void; + lastReturnedWith(value: any): void; + /** + * + */ + toHaveNthReturnedWith(nthCall: number, value: any): void; + nthReturnedWith(nthCall: number, value: any): void; + /** + * Use .toHaveBeenCalledWith to ensure that a mock function was called with + * specific arguments. + */ + toHaveBeenCalledWith(...args: Array): void, + toBeCalledWith(...args: Array): void, + /** + * Use .toHaveBeenLastCalledWith to ensure that a mock function was last called + * with specific arguments. + */ + toHaveBeenLastCalledWith(...args: Array): void, + lastCalledWith(...args: Array): void, + /** + * Check that an object has a .length property and it is set to a certain + * numeric value. + */ + toHaveLength(number: number): void, + /** + * + */ + toHaveProperty(propPath: string, value?: any): void, + /** + * Use .toMatch to check that a string matches a regular expression or string. + */ + toMatch(regexpOrString: RegExp | string): void, + /** + * Use .toMatchObject to check that a javascript object matches a subset of the properties of an object. + */ + toMatchObject(object: Object | Array): void, + /** + * Use .toStrictEqual to check that a javascript object matches a subset of the properties of an object. + */ + toStrictEqual(value: any): void, + /** + * This ensures that an Object matches the most recent snapshot. + */ + toMatchSnapshot(propertyMatchers?: {[key: string]: JestAsymmetricEqualityType}, name?: string): void, + /** + * This ensures that an Object matches the most recent snapshot. + */ + toMatchSnapshot(name: string): void, + /** + * Use .toThrow to test that a function throws when it is called. + * If you want to test that a specific error gets thrown, you can provide an + * argument to toThrow. The argument can be a string for the error message, + * a class for the error, or a regex that should match the error. + * + * Alias: .toThrowError + */ + toThrow(message?: string | Error | Class | RegExp): void, + toThrowError(message?: string | Error | Class | RegExp): void, + /** + * Use .toThrowErrorMatchingSnapshot to test that a function throws a error + * matching the most recent snapshot when it is called. + */ + toThrowErrorMatchingSnapshot(): void +} + +type JestObjectType = { + /** + * Disables automatic mocking in the module loader. + * + * After this method is called, all `require()`s will return the real + * versions of each module (rather than a mocked version). + */ + disableAutomock(): JestObjectType, + /** + * An un-hoisted version of disableAutomock + */ + autoMockOff(): JestObjectType, + /** + * Enables automatic mocking in the module loader. + */ + enableAutomock(): JestObjectType, + /** + * An un-hoisted version of enableAutomock + */ + autoMockOn(): JestObjectType, + /** + * Clears the mock.calls and mock.instances properties of all mocks. + * Equivalent to calling .mockClear() on every mocked function. + */ + clearAllMocks(): JestObjectType, + /** + * Resets the state of all mocks. Equivalent to calling .mockReset() on every + * mocked function. + */ + resetAllMocks(): JestObjectType, + /** + * Restores all mocks back to their original value. + */ + restoreAllMocks(): JestObjectType, + /** + * Removes any pending timers from the timer system. + */ + clearAllTimers(): void, + /** + * The same as `mock` but not moved to the top of the expectation by + * babel-jest. + */ + doMock(moduleName: string, moduleFactory?: any): JestObjectType, + /** + * The same as `unmock` but not moved to the top of the expectation by + * babel-jest. + */ + dontMock(moduleName: string): JestObjectType, + /** + * Returns a new, unused mock function. Optionally takes a mock + * implementation. + */ + fn, TReturn>( + implementation?: (...args: TArguments) => TReturn + ): JestMockFn, + /** + * Determines if the given function is a mocked function. + */ + isMockFunction(fn: Function): boolean, + /** + * Given the name of a module, use the automatic mocking system to generate a + * mocked version of the module for you. + */ + genMockFromModule(moduleName: string): any, + /** + * Mocks a module with an auto-mocked version when it is being required. + * + * The second argument can be used to specify an explicit module factory that + * is being run instead of using Jest's automocking feature. + * + * The third argument can be used to create virtual mocks -- mocks of modules + * that don't exist anywhere in the system. + */ + mock( + moduleName: string, + moduleFactory?: any, + options?: Object + ): JestObjectType, + /** + * Returns the actual module instead of a mock, bypassing all checks on + * whether the module should receive a mock implementation or not. + */ + requireActual(moduleName: string): any, + /** + * Returns a mock module instead of the actual module, bypassing all checks + * on whether the module should be required normally or not. + */ + requireMock(moduleName: string): any, + /** + * Resets the module registry - the cache of all required modules. This is + * useful to isolate modules where local state might conflict between tests. + */ + resetModules(): JestObjectType, + /** + * Exhausts the micro-task queue (usually interfaced in node via + * process.nextTick). + */ + runAllTicks(): void, + /** + * Exhausts the macro-task queue (i.e., all tasks queued by setTimeout(), + * setInterval(), and setImmediate()). + */ + runAllTimers(): void, + /** + * Exhausts all tasks queued by setImmediate(). + */ + runAllImmediates(): void, + /** + * Executes only the macro task queue (i.e. all tasks queued by setTimeout() + * or setInterval() and setImmediate()). + */ + advanceTimersByTime(msToRun: number): void, + /** + * Executes only the macro task queue (i.e. all tasks queued by setTimeout() + * or setInterval() and setImmediate()). + * + * Renamed to `advanceTimersByTime`. + */ + runTimersToTime(msToRun: number): void, + /** + * Executes only the macro-tasks that are currently pending (i.e., only the + * tasks that have been queued by setTimeout() or setInterval() up to this + * point) + */ + runOnlyPendingTimers(): void, + /** + * Explicitly supplies the mock object that the module system should return + * for the specified module. Note: It is recommended to use jest.mock() + * instead. + */ + setMock(moduleName: string, moduleExports: any): JestObjectType, + /** + * Indicates that the module system should never return a mocked version of + * the specified module from require() (e.g. that it should always return the + * real module). + */ + unmock(moduleName: string): JestObjectType, + /** + * Instructs Jest to use fake versions of the standard timer functions + * (setTimeout, setInterval, clearTimeout, clearInterval, nextTick, + * setImmediate and clearImmediate). + */ + useFakeTimers(): JestObjectType, + /** + * Instructs Jest to use the real versions of the standard timer functions. + */ + useRealTimers(): JestObjectType, + /** + * Creates a mock function similar to jest.fn but also tracks calls to + * object[methodName]. + */ + spyOn(object: Object, methodName: string, accessType?: "get" | "set"): JestMockFn, + /** + * Set the default timeout interval for tests and before/after hooks in milliseconds. + * Note: The default timeout interval is 5 seconds if this method is not called. + */ + setTimeout(timeout: number): JestObjectType +}; + +type JestSpyType = { + calls: JestCallsType +}; + +/** Runs this function after every test inside this context */ +declare function afterEach( + fn: (done: () => void) => ?Promise, + timeout?: number +): void; +/** Runs this function before every test inside this context */ +declare function beforeEach( + fn: (done: () => void) => ?Promise, + timeout?: number +): void; +/** Runs this function after all tests have finished inside this context */ +declare function afterAll( + fn: (done: () => void) => ?Promise, + timeout?: number +): void; +/** Runs this function before any tests have started inside this context */ +declare function beforeAll( + fn: (done: () => void) => ?Promise, + timeout?: number +): void; + +/** A context for grouping tests together */ +declare var describe: { + /** + * Creates a block that groups together several related tests in one "test suite" + */ + (name: JestTestName, fn: () => void): void, + + /** + * Only run this describe block + */ + only(name: JestTestName, fn: () => void): void, + + /** + * Skip running this describe block + */ + skip(name: JestTestName, fn: () => void): void +}; + +/** An individual test unit */ +declare var it: { + /** + * An individual test unit + * + * @param {JestTestName} Name of Test + * @param {Function} Test + * @param {number} Timeout for the test, in milliseconds. + */ + ( + name: JestTestName, + fn?: (done: () => void) => ?Promise, + timeout?: number + ): void, + /** + * each runs this test against array of argument arrays per each run + * + * @param {table} table of Test + */ + each( + table: Array> + ): ( + name: JestTestName, + fn?: (...args: Array) => ?Promise + ) => void, + /** + * Only run this test + * + * @param {JestTestName} Name of Test + * @param {Function} Test + * @param {number} Timeout for the test, in milliseconds. + */ + only( + name: JestTestName, + fn?: (done: () => void) => ?Promise, + timeout?: number + ): { + each( + table: Array> + ): ( + name: JestTestName, + fn?: (...args: Array) => ?Promise + ) => void, + }, + /** + * Skip running this test + * + * @param {JestTestName} Name of Test + * @param {Function} Test + * @param {number} Timeout for the test, in milliseconds. + */ + skip( + name: JestTestName, + fn?: (done: () => void) => ?Promise, + timeout?: number + ): void, + /** + * Run the test concurrently + * + * @param {JestTestName} Name of Test + * @param {Function} Test + * @param {number} Timeout for the test, in milliseconds. + */ + concurrent( + name: JestTestName, + fn?: (done: () => void) => ?Promise, + timeout?: number + ): void +}; +declare function fit( + name: JestTestName, + fn: (done: () => void) => ?Promise, + timeout?: number +): void; +/** An individual test unit */ +declare var test: typeof it; +/** A disabled group of tests */ +declare var xdescribe: typeof describe; +/** A focused group of tests */ +declare var fdescribe: typeof describe; +/** A disabled individual test */ +declare var xit: typeof it; +/** A disabled individual test */ +declare var xtest: typeof it; + +type JestPrettyFormatColors = { + comment: { close: string, open: string }, + content: { close: string, open: string }, + prop: { close: string, open: string }, + tag: { close: string, open: string }, + value: { close: string, open: string }, +}; + +type JestPrettyFormatIndent = string => string; +type JestPrettyFormatRefs = Array; +type JestPrettyFormatPrint = any => string; +type JestPrettyFormatStringOrNull = string | null; + +type JestPrettyFormatOptions = {| + callToJSON: boolean, + edgeSpacing: string, + escapeRegex: boolean, + highlight: boolean, + indent: number, + maxDepth: number, + min: boolean, + plugins: JestPrettyFormatPlugins, + printFunctionName: boolean, + spacing: string, + theme: {| + comment: string, + content: string, + prop: string, + tag: string, + value: string, + |}, +|}; + +type JestPrettyFormatPlugin = { + print: ( + val: any, + serialize: JestPrettyFormatPrint, + indent: JestPrettyFormatIndent, + opts: JestPrettyFormatOptions, + colors: JestPrettyFormatColors, + ) => string, + test: any => boolean, +}; + +type JestPrettyFormatPlugins = Array; + +/** The expect function is used every time you want to test a value */ +declare var expect: { + /** The object that you want to make assertions against */ + (value: any): + & JestExpectType + & JestPromiseType + & EnzymeMatchersType + & DomTestingLibraryType + & JestJQueryMatchersType + & JestStyledComponentsMatchersType + & JestExtendedMatchersType, + + /** Add additional Jasmine matchers to Jest's roster */ + extend(matchers: { [name: string]: JestMatcher }): void, + /** Add a module that formats application-specific data structures. */ + addSnapshotSerializer(pluginModule: JestPrettyFormatPlugin): void, + assertions(expectedAssertions: number): void, + hasAssertions(): void, + any(value: mixed): JestAsymmetricEqualityType, + anything(): any, + arrayContaining(value: Array): Array, + objectContaining(value: Object): Object, + /** Matches any received string that contains the exact expected string. */ + stringContaining(value: string): string, + stringMatching(value: string | RegExp): string, + not: { + arrayContaining: (value: $ReadOnlyArray) => Array, + objectContaining: (value: {}) => Object, + stringContaining: (value: string) => string, + stringMatching: (value: string | RegExp) => string, + }, +}; + +// TODO handle return type +// http://jasmine.github.io/2.4/introduction.html#section-Spies +declare function spyOn(value: mixed, method: string): Object; + +/** Holds all functions related to manipulating test runner */ +declare var jest: JestObjectType; + +/** + * The global Jasmine object, this is generally not exposed as the public API, + * using features inside here could break in later versions of Jest. + */ +declare var jasmine: { + DEFAULT_TIMEOUT_INTERVAL: number, + any(value: mixed): JestAsymmetricEqualityType, + anything(): any, + arrayContaining(value: Array): Array, + clock(): JestClockType, + createSpy(name: string): JestSpyType, + createSpyObj( + baseName: string, + methodNames: Array + ): { [methodName: string]: JestSpyType }, + objectContaining(value: Object): Object, + stringMatching(value: string): string +}; diff --git a/flow-typed/npm/mongoose_v4.x.x.js b/flow-typed/npm/mongoose_v4.x.x.js index df125819..30e64eaa 100644 --- a/flow-typed/npm/mongoose_v4.x.x.js +++ b/flow-typed/npm/mongoose_v4.x.x.js @@ -109,9 +109,12 @@ type Mongoose$SchemaHookTypes = | "save" | "validate" | "find" + | "findOne" + | "count" | "update" | "remove" | "findOneAndRemove" + | "findOneAndUpdate" | "init"; type Mongoose$SchemaPlugin = ( @@ -252,7 +255,7 @@ declare class Mongoose$Document { ): Promise & { exec(): Promise }; static create(doc: $Shape | Array<$Shape>): Promise; static where(criteria?: Object): Mongoose$Query; - static aggregate(pipeline: Object[]): Promise; + static aggregate(pipeline: Object[]): Aggregate$Query; static bulkWrite(ops: Object[]): Promise; static deleteMany(criteria: Object): Promise; static deleteOne(criteria: Object): Promise; @@ -420,6 +423,38 @@ declare class Mongoose$Query extends Promise { toConstructor(): Class>; } +declare class Aggregate$Query extends Promise { + exec(): Promise; + allowDiskUse(bool: boolean): Aggregate$Query; + addCursorFlag(str: string, bool: boolean): Aggregate$Query; + addFields(opts?: Object): Aggregate$Query; + append(opts?: Object): Aggregate$Query; + collation(opts?: Object): Aggregate$Query; + count(str: string): Promise; + cursor(opts?: Object): Mongoose$QueryCursor; + exec(cb?:Function): Promise; + explain(cb?:Function): Aggregate$Query; + facet(opts?: Object): Aggregate$Query; + graphLookup(opts?: Object): Aggregate$Query; + group(opts?: Object): Aggregate$Query; + hint(opts?: Object): Aggregate$Query; + limit(n: number): Aggregate$Query; + lookup(opts?: Object): Aggregate$Query; + match(opts?: Object): Aggregate$Query; + model(opts?: Object): Aggregate$Query; + near(opts?: Object): Aggregate$Query; + option(opts?: Object): Aggregate$Query; + pipeline(): Object[]; + project(opts?: Object): Aggregate$Query; + read(str: string): Aggregate$Query; + replaceRoot(opts?: Object): Aggregate$Query; + sample(n: number): Aggregate$Query; + skip(n: number): Aggregate$Query; + sort(options: {} | string): Aggregate$Query; + sortByCount(options: {} | string): Aggregate$Query; + unwind(str: string): Aggregate$Query; +} + declare class Mongoose$QueryCursor { on(type: "data" | "end" | string, cb: Function): void; next(cb?: (err: Error, doc: Doc) => void): Promise; @@ -498,15 +533,15 @@ declare class Mongoose$Connection { } declare module "mongoose" { - declare type MongooseConnection = Mongoose$Connection; - declare type MongoId = MongoId; - declare type BSONObjectId = bson$ObjectId; - declare type ObjectId = bson$ObjectId; - declare type MongooseQuery = Mongoose$Query; - declare type MongooseDocument = Mongoose$Document; - declare type MongooseModel = typeof Mongoose$Document; - declare type MongooseSchema = Mongoose$Schema; - declare type MongooseSchemaField = Mongoose$SchemaField< + declare export type MongooseConnection = Mongoose$Connection; + declare export type MongoId = MongoId; + declare export type BSONObjectId = bson$ObjectId; + declare export type ObjectId = bson$ObjectId; + declare export type MongooseQuery = Mongoose$Query; + declare export type MongooseDocument = Mongoose$Document; + declare export type MongooseModel = typeof Mongoose$Document; + declare export type MongooseSchema = Mongoose$Schema; + declare export type MongooseSchemaField = Mongoose$SchemaField< Schema >; @@ -515,9 +550,10 @@ declare module "mongoose" { Types: Mongoose$Types, Promise: any, model: $PropertyType, + models: { [name: string]: typeof Mongoose$Document }, createConnection(uri?: string, options?: Object): Mongoose$Connection, set: (key: string, value: string | Function | boolean) => void, - connect: (uri: string, options?: Object) => void, + connect: (uri?: string, options?: Object) => void, connection: Mongoose$Connection, connections: Mongoose$Connection[], Query: typeof Mongoose$Query, diff --git a/flow-typed/npm/object-path_vx.x.x.js b/flow-typed/npm/object-path_vx.x.x.js index fdac30ea..d5eb2b87 100644 --- a/flow-typed/npm/object-path_vx.x.x.js +++ b/flow-typed/npm/object-path_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: fb09d0c62c84a90007779c4ec5a07157 -// flow-typed version: <>/object-path_v^0.11.4/flow_v0.54.1 +// flow-typed signature: 92fa967d75d9acc3a1466183192eebc1 +// flow-typed version: <>/object-path_v^0.11.4/flow_v0.76.0 /** * This is an autogenerated libdef stub for: diff --git a/flow-typed/npm/prettier_v1.x.x.js b/flow-typed/npm/prettier_v1.x.x.js new file mode 100644 index 00000000..0c244915 --- /dev/null +++ b/flow-typed/npm/prettier_v1.x.x.js @@ -0,0 +1,178 @@ +// flow-typed signature: 4eed8da2dc730dc33e7710b465eaa44b +// flow-typed version: cc7a557b34/prettier_v1.x.x/flow_>=v0.56.x + +declare module "prettier" { + declare type AST = Object; + declare type Doc = Object; + declare type FastPath = Object; + + declare type PrettierParserName = + | "babylon" + | "flow" + | "typescript" + | "postcss" + | "css" + | "less" + | "scss" + | "json" + | "graphql" + | "markdown" + | "vue"; + + declare type PrettierParser = { + [name: PrettierParserName]: (text: string, options?: Object) => AST + }; + + declare type CustomParser = ( + text: string, + parsers: PrettierParser, + options: Options + ) => AST; + + declare type Options = {| + printWidth?: number, + tabWidth?: number, + useTabs?: boolean, + semi?: boolean, + singleQuote?: boolean, + trailingComma?: "none" | "es5" | "all", + bracketSpacing?: boolean, + jsxBracketSameLine?: boolean, + arrowParens?: "avoid" | "always", + rangeStart?: number, + rangeEnd?: number, + parser?: PrettierParserName | CustomParser, + filepath?: string, + requirePragma?: boolean, + insertPragma?: boolean, + proseWrap?: "always" | "never" | "preserve", + plugins?: Array + |}; + + declare type Plugin = { + languages: SupportLanguage, + parsers: { [parserName: string]: Parser }, + printers: { [astFormat: string]: Printer } + }; + + declare type Parser = { + parse: ( + text: string, + parsers: { [parserName: string]: Parser }, + options: Object + ) => AST, + astFormat: string + }; + + declare type Printer = { + print: ( + path: FastPath, + options: Object, + print: (path: FastPath) => Doc + ) => Doc, + embed: ( + path: FastPath, + print: (path: FastPath) => Doc, + textToDoc: (text: string, options: Object) => Doc, + options: Object + ) => ?Doc + }; + + declare type CursorOptions = {| + cursorOffset: number, + printWidth?: $PropertyType, + tabWidth?: $PropertyType, + useTabs?: $PropertyType, + semi?: $PropertyType, + singleQuote?: $PropertyType, + trailingComma?: $PropertyType, + bracketSpacing?: $PropertyType, + jsxBracketSameLine?: $PropertyType, + arrowParens?: $PropertyType, + parser?: $PropertyType, + filepath?: $PropertyType, + requirePragma?: $PropertyType, + insertPragma?: $PropertyType, + proseWrap?: $PropertyType, + plugins?: $PropertyType + |}; + + declare type CursorResult = {| + formatted: string, + cursorOffset: number + |}; + + declare type ResolveConfigOptions = {| + useCache?: boolean, + config?: string, + editorconfig?: boolean + |}; + + declare type SupportLanguage = { + name: string, + since: string, + parsers: Array, + group?: string, + tmScope: string, + aceMode: string, + codemirrorMode: string, + codemirrorMimeType: string, + aliases?: Array, + extensions: Array, + filenames?: Array, + linguistLanguageId: number, + vscodeLanguageIds: Array + }; + + declare type SupportOption = {| + since: string, + type: "int" | "boolean" | "choice" | "path", + deprecated?: string, + redirect?: SupportOptionRedirect, + description: string, + oppositeDescription?: string, + default: SupportOptionValue, + range?: SupportOptionRange, + choices?: SupportOptionChoice + |}; + + declare type SupportOptionRedirect = {| + options: string, + value: SupportOptionValue + |}; + + declare type SupportOptionRange = {| + start: number, + end: number, + step: number + |}; + + declare type SupportOptionChoice = {| + value: boolean | string, + description?: string, + since?: string, + deprecated?: string, + redirect?: SupportOptionValue + |}; + + declare type SupportOptionValue = number | boolean | string; + + declare type SupportInfo = {| + languages: Array, + options: Array + |}; + + declare type Prettier = {| + format: (source: string, options?: Options) => string, + check: (source: string, options?: Options) => boolean, + formatWithCursor: (source: string, options: CursorOptions) => CursorResult, + resolveConfig: { + (filePath: string, options?: ResolveConfigOptions): Promise, + sync(filePath: string, options?: ResolveConfigOptions): Promise + }, + clearConfigCache: () => void, + getSupportInfo: (version?: string) => SupportInfo + |}; + + declare export default Prettier; +} diff --git a/flow-typed/npm/prettier_vx.x.x.js b/flow-typed/npm/prettier_vx.x.x.js deleted file mode 100644 index d1f6980b..00000000 --- a/flow-typed/npm/prettier_vx.x.x.js +++ /dev/null @@ -1,80 +0,0 @@ -// flow-typed signature: d003fe8fa6770c8af843a5f64e9641de -// flow-typed version: <>/prettier_v^1.6.1/flow_v0.54.1 - -/** - * This is an autogenerated libdef stub for: - * - * 'prettier' - * - * Fill this stub out by replacing all the `any` types. - * - * Once filled out, we encourage you to share your work with the - * community by sending a pull request to: - * https://github.com/flowtype/flow-typed - */ - -declare module 'prettier' { - declare module.exports: any; -} - -/** - * We include stubs for each file inside this npm package in case you need to - * require those files directly. Feel free to delete any files that aren't - * needed. - */ -declare module 'prettier/bin/prettier' { - declare module.exports: any; -} - -declare module 'prettier/parser-babylon' { - declare module.exports: any; -} - -declare module 'prettier/parser-flow' { - declare module.exports: any; -} - -declare module 'prettier/parser-graphql' { - declare module.exports: any; -} - -declare module 'prettier/parser-parse5' { - declare module.exports: any; -} - -declare module 'prettier/parser-postcss' { - declare module.exports: any; -} - -declare module 'prettier/parser-typescript' { - declare module.exports: any; -} - -// Filename aliases -declare module 'prettier/bin/prettier.js' { - declare module.exports: $Exports<'prettier/bin/prettier'>; -} -declare module 'prettier/index' { - declare module.exports: $Exports<'prettier'>; -} -declare module 'prettier/index.js' { - declare module.exports: $Exports<'prettier'>; -} -declare module 'prettier/parser-babylon.js' { - declare module.exports: $Exports<'prettier/parser-babylon'>; -} -declare module 'prettier/parser-flow.js' { - declare module.exports: $Exports<'prettier/parser-flow'>; -} -declare module 'prettier/parser-graphql.js' { - declare module.exports: $Exports<'prettier/parser-graphql'>; -} -declare module 'prettier/parser-parse5.js' { - declare module.exports: $Exports<'prettier/parser-parse5'>; -} -declare module 'prettier/parser-postcss.js' { - declare module.exports: $Exports<'prettier/parser-postcss'>; -} -declare module 'prettier/parser-typescript.js' { - declare module.exports: $Exports<'prettier/parser-typescript'>; -} diff --git a/flow-typed/npm/request_vx.x.x.js b/flow-typed/npm/request_vx.x.x.js new file mode 100644 index 00000000..1a0dccf1 --- /dev/null +++ b/flow-typed/npm/request_vx.x.x.js @@ -0,0 +1,115 @@ +// flow-typed signature: f934c25f22ac154ceb51b3723113c69c +// flow-typed version: <>/request_v^2.87.0/flow_v0.76.0 + +/** + * This is an autogenerated libdef stub for: + * + * 'request' + * + * Fill this stub out by replacing all the `any` types. + * + * Once filled out, we encourage you to share your work with the + * community by sending a pull request to: + * https://github.com/flowtype/flow-typed + */ + +declare module 'request' { + declare module.exports: any; +} + +/** + * We include stubs for each file inside this npm package in case you need to + * require those files directly. Feel free to delete any files that aren't + * needed. + */ +declare module 'request/lib/auth' { + declare module.exports: any; +} + +declare module 'request/lib/cookies' { + declare module.exports: any; +} + +declare module 'request/lib/getProxyFromURI' { + declare module.exports: any; +} + +declare module 'request/lib/har' { + declare module.exports: any; +} + +declare module 'request/lib/hawk' { + declare module.exports: any; +} + +declare module 'request/lib/helpers' { + declare module.exports: any; +} + +declare module 'request/lib/multipart' { + declare module.exports: any; +} + +declare module 'request/lib/oauth' { + declare module.exports: any; +} + +declare module 'request/lib/querystring' { + declare module.exports: any; +} + +declare module 'request/lib/redirect' { + declare module.exports: any; +} + +declare module 'request/lib/tunnel' { + declare module.exports: any; +} + +declare module 'request/request' { + declare module.exports: any; +} + +// Filename aliases +declare module 'request/index' { + declare module.exports: $Exports<'request'>; +} +declare module 'request/index.js' { + declare module.exports: $Exports<'request'>; +} +declare module 'request/lib/auth.js' { + declare module.exports: $Exports<'request/lib/auth'>; +} +declare module 'request/lib/cookies.js' { + declare module.exports: $Exports<'request/lib/cookies'>; +} +declare module 'request/lib/getProxyFromURI.js' { + declare module.exports: $Exports<'request/lib/getProxyFromURI'>; +} +declare module 'request/lib/har.js' { + declare module.exports: $Exports<'request/lib/har'>; +} +declare module 'request/lib/hawk.js' { + declare module.exports: $Exports<'request/lib/hawk'>; +} +declare module 'request/lib/helpers.js' { + declare module.exports: $Exports<'request/lib/helpers'>; +} +declare module 'request/lib/multipart.js' { + declare module.exports: $Exports<'request/lib/multipart'>; +} +declare module 'request/lib/oauth.js' { + declare module.exports: $Exports<'request/lib/oauth'>; +} +declare module 'request/lib/querystring.js' { + declare module.exports: $Exports<'request/lib/querystring'>; +} +declare module 'request/lib/redirect.js' { + declare module.exports: $Exports<'request/lib/redirect'>; +} +declare module 'request/lib/tunnel.js' { + declare module.exports: $Exports<'request/lib/tunnel'>; +} +declare module 'request/request.js' { + declare module.exports: $Exports<'request/request'>; +} diff --git a/flow-typed/npm/rimraf_v2.x.x.js b/flow-typed/npm/rimraf_v2.x.x.js new file mode 100644 index 00000000..13b85249 --- /dev/null +++ b/flow-typed/npm/rimraf_v2.x.x.js @@ -0,0 +1,18 @@ +// flow-typed signature: 1dff23447d5e18f5ac2b05aaec7cfb74 +// flow-typed version: a453e98ea2/rimraf_v2.x.x/flow_>=v0.25.0 + +declare module 'rimraf' { + declare type Options = { + maxBusyTries?: number, + emfileWait?: number, + glob?: boolean, + disableGlob?: boolean + }; + + declare type Callback = (err: ?Error, path: ?string) => void; + + declare module.exports: { + (f: string, opts?: Options | Callback, callback?: Callback): void; + sync(path: string, opts?: Options): void; + }; +} diff --git a/flow-typed/npm/rimraf_vx.x.x.js b/flow-typed/npm/rimraf_vx.x.x.js deleted file mode 100644 index 8972caec..00000000 --- a/flow-typed/npm/rimraf_vx.x.x.js +++ /dev/null @@ -1,39 +0,0 @@ -// flow-typed signature: 4b7bccb2735580438711ebecb7134886 -// flow-typed version: <>/rimraf_v^2.6.1/flow_v0.54.1 - -/** - * This is an autogenerated libdef stub for: - * - * 'rimraf' - * - * Fill this stub out by replacing all the `any` types. - * - * Once filled out, we encourage you to share your work with the - * community by sending a pull request to: - * https://github.com/flowtype/flow-typed - */ - -declare module 'rimraf' { - declare module.exports: any; -} - -/** - * We include stubs for each file inside this npm package in case you need to - * require those files directly. Feel free to delete any files that aren't - * needed. - */ -declare module 'rimraf/bin' { - declare module.exports: any; -} - -declare module 'rimraf/rimraf' { - declare module.exports: any; -} - -// Filename aliases -declare module 'rimraf/bin.js' { - declare module.exports: $Exports<'rimraf/bin'>; -} -declare module 'rimraf/rimraf.js' { - declare module.exports: $Exports<'rimraf/rimraf'>; -} diff --git a/flow-typed/npm/semantic-release_vx.x.x.js b/flow-typed/npm/semantic-release_vx.x.x.js index 92ac3fbb..5236de9f 100644 --- a/flow-typed/npm/semantic-release_vx.x.x.js +++ b/flow-typed/npm/semantic-release_vx.x.x.js @@ -1,5 +1,5 @@ -// flow-typed signature: c01fd07cc4d7da4eab76e96afbb75fdd -// flow-typed version: <>/semantic-release_v^7.0.2/flow_v0.54.1 +// flow-typed signature: 5f5994118cebf48610791b12832e286c +// flow-typed version: <>/semantic-release_v^15.7.2/flow_v0.76.0 /** * This is an autogenerated libdef stub for: @@ -26,35 +26,75 @@ declare module 'semantic-release/bin/semantic-release' { declare module.exports: any; } -declare module 'semantic-release/src/lib/commits' { +declare module 'semantic-release/cli' { declare module.exports: any; } -declare module 'semantic-release/src/lib/get-registry' { +declare module 'semantic-release/lib/definitions/constants' { declare module.exports: any; } -declare module 'semantic-release/src/lib/plugin-noop' { +declare module 'semantic-release/lib/definitions/errors' { declare module.exports: any; } -declare module 'semantic-release/src/lib/plugins' { +declare module 'semantic-release/lib/definitions/plugins' { declare module.exports: any; } -declare module 'semantic-release/src/lib/type' { +declare module 'semantic-release/lib/get-commits' { declare module.exports: any; } -declare module 'semantic-release/src/lib/verify' { +declare module 'semantic-release/lib/get-config' { declare module.exports: any; } -declare module 'semantic-release/src/post' { +declare module 'semantic-release/lib/get-error' { declare module.exports: any; } -declare module 'semantic-release/src/pre' { +declare module 'semantic-release/lib/get-git-auth-url' { + declare module.exports: any; +} + +declare module 'semantic-release/lib/get-last-release' { + declare module.exports: any; +} + +declare module 'semantic-release/lib/get-next-version' { + declare module.exports: any; +} + +declare module 'semantic-release/lib/git' { + declare module.exports: any; +} + +declare module 'semantic-release/lib/hide-sensitive' { + declare module.exports: any; +} + +declare module 'semantic-release/lib/logger' { + declare module.exports: any; +} + +declare module 'semantic-release/lib/plugins/index' { + declare module.exports: any; +} + +declare module 'semantic-release/lib/plugins/normalize' { + declare module.exports: any; +} + +declare module 'semantic-release/lib/plugins/pipeline' { + declare module.exports: any; +} + +declare module 'semantic-release/lib/utils' { + declare module.exports: any; +} + +declare module 'semantic-release/lib/verify' { declare module.exports: any; } @@ -62,27 +102,63 @@ declare module 'semantic-release/src/pre' { declare module 'semantic-release/bin/semantic-release.js' { declare module.exports: $Exports<'semantic-release/bin/semantic-release'>; } -declare module 'semantic-release/src/lib/commits.js' { - declare module.exports: $Exports<'semantic-release/src/lib/commits'>; +declare module 'semantic-release/cli.js' { + declare module.exports: $Exports<'semantic-release/cli'>; +} +declare module 'semantic-release/index' { + declare module.exports: $Exports<'semantic-release'>; +} +declare module 'semantic-release/index.js' { + declare module.exports: $Exports<'semantic-release'>; +} +declare module 'semantic-release/lib/definitions/constants.js' { + declare module.exports: $Exports<'semantic-release/lib/definitions/constants'>; +} +declare module 'semantic-release/lib/definitions/errors.js' { + declare module.exports: $Exports<'semantic-release/lib/definitions/errors'>; +} +declare module 'semantic-release/lib/definitions/plugins.js' { + declare module.exports: $Exports<'semantic-release/lib/definitions/plugins'>; +} +declare module 'semantic-release/lib/get-commits.js' { + declare module.exports: $Exports<'semantic-release/lib/get-commits'>; +} +declare module 'semantic-release/lib/get-config.js' { + declare module.exports: $Exports<'semantic-release/lib/get-config'>; +} +declare module 'semantic-release/lib/get-error.js' { + declare module.exports: $Exports<'semantic-release/lib/get-error'>; +} +declare module 'semantic-release/lib/get-git-auth-url.js' { + declare module.exports: $Exports<'semantic-release/lib/get-git-auth-url'>; +} +declare module 'semantic-release/lib/get-last-release.js' { + declare module.exports: $Exports<'semantic-release/lib/get-last-release'>; +} +declare module 'semantic-release/lib/get-next-version.js' { + declare module.exports: $Exports<'semantic-release/lib/get-next-version'>; +} +declare module 'semantic-release/lib/git.js' { + declare module.exports: $Exports<'semantic-release/lib/git'>; } -declare module 'semantic-release/src/lib/get-registry.js' { - declare module.exports: $Exports<'semantic-release/src/lib/get-registry'>; +declare module 'semantic-release/lib/hide-sensitive.js' { + declare module.exports: $Exports<'semantic-release/lib/hide-sensitive'>; } -declare module 'semantic-release/src/lib/plugin-noop.js' { - declare module.exports: $Exports<'semantic-release/src/lib/plugin-noop'>; +declare module 'semantic-release/lib/logger.js' { + declare module.exports: $Exports<'semantic-release/lib/logger'>; } -declare module 'semantic-release/src/lib/plugins.js' { - declare module.exports: $Exports<'semantic-release/src/lib/plugins'>; +declare module 'semantic-release/lib/plugins/index.js' { + declare module.exports: $Exports<'semantic-release/lib/plugins/index'>; } -declare module 'semantic-release/src/lib/type.js' { - declare module.exports: $Exports<'semantic-release/src/lib/type'>; +declare module 'semantic-release/lib/plugins/normalize.js' { + declare module.exports: $Exports<'semantic-release/lib/plugins/normalize'>; } -declare module 'semantic-release/src/lib/verify.js' { - declare module.exports: $Exports<'semantic-release/src/lib/verify'>; +declare module 'semantic-release/lib/plugins/pipeline.js' { + declare module.exports: $Exports<'semantic-release/lib/plugins/pipeline'>; } -declare module 'semantic-release/src/post.js' { - declare module.exports: $Exports<'semantic-release/src/post'>; +declare module 'semantic-release/lib/utils.js' { + declare module.exports: $Exports<'semantic-release/lib/utils'>; } -declare module 'semantic-release/src/pre.js' { - declare module.exports: $Exports<'semantic-release/src/pre'>; +declare module 'semantic-release/lib/verify.js' { + declare module.exports: $Exports<'semantic-release/lib/verify'>; } diff --git a/package.json b/package.json index 5745db4f..3fff007e 100644 --- a/package.json +++ b/package.json @@ -35,14 +35,15 @@ "graphql-compose-pagination": ">=3.3.0" }, "peerDependencies": { - "graphql-compose": ">=4.0.0", + "graphql-compose": ">=4.7.1", "mongoose": ">=4.0.0 || >=5.0.0" }, "devDependencies": { "babel-cli": "^6.26.0", "babel-core": "^6.26.3", - "babel-eslint": "^8.2.5", - "babel-jest": "^23.2.0", + "babel-eslint": "^8.2.6", + "babel-jest": "^23.4.0", + "babel-plugin-transform-class-properties": "^6.24.1", "babel-plugin-transform-flow-strip-types": "^6.22.0", "babel-plugin-transform-object-rest-spread": "^6.13.0", "babel-plugin-transform-runtime": "^6.23.0", @@ -56,16 +57,16 @@ "eslint-plugin-prettier": "^2.6.2", "flow-bin": "^0.76.0", "graphql": "0.13.2", - "graphql-compose": "^4.4.1", + "graphql-compose": "^4.7.1", "graphql-compose-connection": ">=3.2.0", "graphql-compose-pagination": ">=3.3.0", - "jest": "^23.3.0", + "jest": "^23.4.1", "mongodb-memory-server": "^1.9.0", - "mongoose": "^5.2.2", + "mongoose": "^5.2.3", "prettier": "^1.13.7", "request": "^2.87.0", "rimraf": "^2.6.2", - "semantic-release": "^15.6.3" + "semantic-release": "^15.7.2" }, "config": { "commitizen": { diff --git a/src/__mocks__/mongooseCommon.js b/src/__mocks__/mongooseCommon.js index 16b5b577..3aea193a 100644 --- a/src/__mocks__/mongooseCommon.js +++ b/src/__mocks__/mongooseCommon.js @@ -1,7 +1,7 @@ /* @flow */ /* eslint-disable no-param-reassign, no-console */ -import mongoose, { Schema } from 'mongoose'; +import mongoose, { Schema, Types } from 'mongoose'; import MongodbMemoryServer from 'mongodb-memory-server'; mongoose.Promise = Promise; @@ -35,4 +35,4 @@ mongoose.connect = (async () => { }); }: any); -export { mongoose, Schema }; +export { mongoose, Schema, Types }; diff --git a/src/__tests__/composeWithMongooseDiscriminators-test.js b/src/__tests__/composeWithMongooseDiscriminators-test.js new file mode 100644 index 00000000..8d3e2363 --- /dev/null +++ b/src/__tests__/composeWithMongooseDiscriminators-test.js @@ -0,0 +1,60 @@ +/* @flow */ + +import { InputTypeComposer, schemaComposer, TypeComposer } from 'graphql-compose'; +import { getCharacterModels } from '../discriminators/__mocks__/characterModels'; +import { MovieModel } from '../discriminators/__mocks__/movieModel'; +import { composeWithMongooseDiscriminators } from '../composeWithMongooseDiscriminators'; +import { DiscriminatorTypeComposer } from '../discriminators'; + +beforeAll(() => MovieModel.base.connect()); +afterAll(() => MovieModel.base.disconnect()); + +const { CharacterModel, PersonModel } = getCharacterModels('type'); + +describe('composeWithMongooseDiscriminators ->', () => { + beforeEach(() => { + schemaComposer.clear(); + }); + + describe('basics', () => { + it('should create and return a DiscriminatorTypeComposer', () => { + expect(composeWithMongooseDiscriminators(CharacterModel)).toBeInstanceOf( + DiscriminatorTypeComposer + ); + }); + + it('should return a TypeComposer as childTC on discriminator() call', () => { + expect( + composeWithMongooseDiscriminators(CharacterModel).discriminator(PersonModel) + ).toBeInstanceOf(TypeComposer); + }); + }); + + describe('composeWithMongoose customisationOptions', () => { + it('required input fields, should be passed down to resolvers', () => { + const typeComposer = composeWithMongooseDiscriminators(CharacterModel, { + inputType: { + fields: { + required: ['kind'], + }, + }, + }); + const filterArgInFindOne: any = typeComposer.getResolver('findOne').getArg('filter'); + const inputComposer = new InputTypeComposer(filterArgInFindOne.type); + expect(inputComposer.isRequired('kind')).toBe(true); + }); + + it('should proceed customizationOptions.inputType.fields.required', () => { + const itc = composeWithMongooseDiscriminators(CharacterModel, { + inputType: { + fields: { + required: ['name', 'friends'], + }, + }, + }).getInputTypeComposer(); + + expect(itc.isRequired('name')).toBe(true); + expect(itc.isRequired('friends')).toBe(true); + }); + }); +}); diff --git a/src/__tests__/fieldConverter-test.js b/src/__tests__/fieldConverter-test.js index 45ac0061..39e97386 100644 --- a/src/__tests__/fieldConverter-test.js +++ b/src/__tests__/fieldConverter-test.js @@ -17,9 +17,6 @@ import { } from '../fieldsConverter'; import GraphQLMongoID from '../types/mongoid'; -// beforeAll(() => UserModel.base.connect()); -// afterAll(() => UserModel.base.disconnect()); - describe('fieldConverter', () => { const fields: { [key: string]: any } = getFieldsFromModel(UserModel); const fieldNames = Object.keys(fields); diff --git a/src/__tests__/github-issues-test.js b/src/__tests__/github-issues-test.js index 9a325000..b4f69da8 100644 --- a/src/__tests__/github-issues-test.js +++ b/src/__tests__/github-issues-test.js @@ -89,7 +89,7 @@ describe('github issues checks', () => { }); }); - describe('#92 How to verify the fields?', async () => { + describe('#92 How to verify the fields?', () => { UserTC.wrapResolverResolve('createOne', next => rp => { if (rp.args.record.age < 21) throw new Error('You are too young'); if (rp.args.record.age > 60) throw new Error('You are too old'); diff --git a/src/__tests__/integration-discriminators-test.js b/src/__tests__/integration-discriminators-test.js new file mode 100644 index 00000000..0cb40b40 --- /dev/null +++ b/src/__tests__/integration-discriminators-test.js @@ -0,0 +1,220 @@ +/* @flow */ + +import { graphql, schemaComposer } from 'graphql-compose/lib/index'; +import { mongoose } from '../__mocks__/mongooseCommon'; +import { getCharacterModels } from '../discriminators/__mocks__/characterModels'; +import { composeWithMongooseDiscriminators } from '../composeWithMongooseDiscriminators'; + +const { CharacterModel, PersonModel, DroidModel } = getCharacterModels('type'); + +beforeAll(() => mongoose.connect()); +afterAll(() => mongoose.disconnect()); + +describe('#78 Mongoose and Discriminators', () => { + const options = { discriminatorKey: 'kind' }; + + const eventSchema = new mongoose.Schema({ refId: String }, options); + const Event = mongoose.model('Event', eventSchema); + + const clickedLinkSchema = new mongoose.Schema({ url: String }); + const ClickedLinkEvent = Event.discriminator('ClickedLinkEvent', clickedLinkSchema); + + const EventTC = composeWithMongooseDiscriminators(Event); + const ClickedLinkEventTC = EventTC.discriminator(ClickedLinkEvent); + + afterAll(() => Event.remove({})); + + it('creating Types from models', () => { + expect(EventTC.getFieldNames()).toEqual(['_id', 'kind', 'refId']); + expect(ClickedLinkEventTC.getFieldNames()).toEqual(['_id', 'kind', 'refId', 'url']); + }); + + it('manually override resolver output type for findMany', async () => { + // let's check graphql response + + await Event.create({ refId: 'aaa' }); + await Event.create({ refId: 'bbb' }); + await ClickedLinkEvent.create({ refId: 'ccc', url: 'url1' }); + await ClickedLinkEvent.create({ refId: 'ddd', url: 'url2' }); + + schemaComposer.rootQuery().addFields({ + eventFindMany: EventTC.getResolver('findMany'), + }); + + const schema = schemaComposer.buildSchema(); + + const res = await graphql.graphql( + schema, + `{ + eventFindMany { + __typename + ... on Event { + refId + } + ... on ClickedLinkEvent { + kind + refId + url + } + } + }` + ); + + expect(res).toEqual({ + data: { + eventFindMany: [ + { __typename: 'Event', refId: 'aaa' }, + { __typename: 'Event', refId: 'bbb' }, + { __typename: 'ClickedLinkEvent', kind: 'ClickedLinkEvent', refId: 'ccc', url: 'url1' }, + { __typename: 'ClickedLinkEvent', kind: 'ClickedLinkEvent', refId: 'ddd', url: 'url2' }, + ], + }, + }); + }); +}); + +describe('depicting other Enhancements to resolvers', () => { + const CharacterDTC = composeWithMongooseDiscriminators(CharacterModel); + const DroidCTC = CharacterDTC.discriminator(DroidModel); + const PersonCTC = CharacterDTC.discriminator(PersonModel); + + schemaComposer.rootQuery().addFields({ + characterById: CharacterDTC.getResolver('findById'), + characterByIds: CharacterDTC.getResolver('findByIds'), + characterOne: CharacterDTC.getResolver('findOne'), + characterMany: CharacterDTC.getResolver('findMany'), + characterCount: CharacterDTC.getResolver('count'), + characterConnection: CharacterDTC.getResolver('connection'), + characterPagination: CharacterDTC.getResolver('pagination'), + }); + + schemaComposer.rootMutation().addFields({ + characterCreate: CharacterDTC.getResolver('createOne'), + characterUpdateById: CharacterDTC.getResolver('updateById'), + characterUpdateOne: CharacterDTC.getResolver('updateOne'), + characterUpdateMany: CharacterDTC.getResolver('updateMany'), + characterRemoveById: CharacterDTC.getResolver('removeById'), + characterRemoveOne: CharacterDTC.getResolver('removeOne'), + characterRemoveMany: CharacterDTC.getResolver('removeMany'), + }); + + schemaComposer.rootQuery().addFields({ + droidById: DroidCTC.getResolver('findById'), + droidByIds: DroidCTC.getResolver('findByIds'), + // droidOne: DroidCTC.getResolver('findOne'), + droidMany: DroidCTC.getResolver('findMany'), + droidCount: DroidCTC.getResolver('count'), + droidConnection: DroidCTC.getResolver('connection'), + droidPagination: DroidCTC.getResolver('pagination'), + }); + + schemaComposer.rootMutation().addFields({ + droidCreate: DroidCTC.getResolver('createOne'), + droidUpdateById: DroidCTC.getResolver('updateById'), + droidUpdateOne: DroidCTC.getResolver('updateOne'), + droidUpdateMany: DroidCTC.getResolver('updateMany'), + droidRemoveById: DroidCTC.getResolver('removeById'), + droidRemoveOne: DroidCTC.getResolver('removeOne'), + droidRemoveMany: DroidCTC.getResolver('removeMany'), + }); + + schemaComposer.rootQuery().addFields({ + personById: PersonCTC.getResolver('findById'), + personByIds: PersonCTC.getResolver('findByIds'), + // personOne: PersonCTC.getResolver('findOne'), + personMany: PersonCTC.getResolver('findMany'), + personCount: PersonCTC.getResolver('count'), + personConnection: PersonCTC.getResolver('connection'), + personPagination: PersonCTC.getResolver('pagination'), + }); + + schemaComposer.rootMutation().addFields({ + personCreate: PersonCTC.getResolver('createOne'), + personUpdateById: PersonCTC.getResolver('updateById'), + personUpdateOne: PersonCTC.getResolver('updateOne'), + personUpdateMany: PersonCTC.getResolver('updateMany'), + personRemoveById: PersonCTC.getResolver('removeById'), + personRemoveOne: PersonCTC.getResolver('removeOne'), + personRemoveMany: PersonCTC.getResolver('removeMany'), + }); + + const schema = schemaComposer.buildSchema(); + + describe('baseResolvers Enhancements', () => { + describe('createOne', () => { + it('should create base document with DKey provided, generic fields', async () => { + const res = await graphql.graphql( + schema, + `mutation CreateCharacters { + droidCreate: characterCreate(record: {name: "Queue XL", type: Droid }) { + record { + __typename + type + name + } + } + + personCreate: characterCreate(record: {name: "mernxl", type: Person }) { + record { + __typename + type + name + } + } + }` + ); + + expect(res).toEqual({ + data: { + droidCreate: { + record: { __typename: 'Droid', type: 'Droid', name: 'Queue XL' }, + }, + personCreate: { + record: { __typename: 'Person', type: 'Person', name: 'mernxl' }, + }, + }, + }); + }); + }); + }); + + describe('childResolvers Enhancements', () => { + describe('createOne', () => { + it('should create child document without specifying DKey', async () => { + const res = await graphql.graphql( + schema, + `mutation CreateCharacters { + droidCreate(record: {name: "Queue XL", modelNumber: 360 }) { + record { + __typename + type + name + modelNumber + } + } + + personCreate(record: {name: "mernxl", dob: 57275272}) { + record { + __typename + type + name + dob + } + } + }` + ); + + expect(res).toEqual({ + data: { + droidCreate: { + record: { __typename: 'Droid', type: 'Droid', name: 'Queue XL', modelNumber: 360 }, + }, + personCreate: { + record: { __typename: 'Person', type: 'Person', name: 'mernxl', dob: 57275272 }, + }, + }, + }); + }); + }); + }); +}); diff --git a/src/__tests__/integration-test.js b/src/__tests__/integration-test.js index 0add8edd..3239cbb1 100644 --- a/src/__tests__/integration-test.js +++ b/src/__tests__/integration-test.js @@ -89,7 +89,7 @@ describe('integration tests', () => { }); }); - describe('check mixed field', async () => { + describe('check mixed field', () => { it('should properly return data via graphql query', async () => { const UserTC = composeWithMongoose(UserModel, { schemaComposer }); const user = new UserModel({ @@ -123,7 +123,7 @@ describe('integration tests', () => { }); }); - describe('projection', async () => { + describe('projection', () => { let schema; let UserTC; beforeAll(async () => { diff --git a/src/__tests__/prepareConnectionResolver-test.js b/src/__tests__/prepareConnectionResolver-test.js deleted file mode 100644 index 093e55e8..00000000 --- a/src/__tests__/prepareConnectionResolver-test.js +++ /dev/null @@ -1,99 +0,0 @@ -/* @flow */ - -import { prepareCursorQuery } from '../prepareConnectionResolver'; - -let rawQuery; - -describe('prepareConnectionResolver', () => { - describe('prepareCursorQuery()', () => { - describe('single index', () => { - const cursorData = { a: 10 }; - const indexKeys = Object.keys(cursorData); - - it('asc order', () => { - const indexData = { a: 1 }; - - // for beforeCursorQuery - rawQuery = {}; - prepareCursorQuery(rawQuery, cursorData, indexKeys, indexData, '$lt', '$gt'); - expect(rawQuery).toEqual({ a: { $lt: 10 } }); - - // for afterCursorQuery - rawQuery = {}; - prepareCursorQuery(rawQuery, cursorData, indexKeys, indexData, '$gt', '$lt'); - expect(rawQuery).toEqual({ a: { $gt: 10 } }); - }); - - it('desc order', () => { - const indexData = { a: -1 }; - - // for beforeCursorQuery - rawQuery = {}; - prepareCursorQuery(rawQuery, cursorData, indexKeys, indexData, '$lt', '$gt'); - expect(rawQuery).toEqual({ a: { $gt: 10 } }); - - // for afterCursorQuery - rawQuery = {}; - prepareCursorQuery(rawQuery, cursorData, indexKeys, indexData, '$gt', '$lt'); - expect(rawQuery).toEqual({ a: { $lt: 10 } }); - }); - }); - - describe('compound index', () => { - const cursorData = { a: 10, b: 100, c: 1000 }; - const indexKeys = Object.keys(cursorData); - - it('asc order', () => { - const indexData = { a: 1, b: -1, c: 1 }; - - // for beforeCursorQuery - rawQuery = {}; - prepareCursorQuery(rawQuery, cursorData, indexKeys, indexData, '$lt', '$gt'); - expect(rawQuery).toEqual({ - $or: [ - { a: 10, b: 100, c: { $lt: 1000 } }, - { a: 10, b: { $gt: 100 } }, - { a: { $lt: 10 } }, - ], - }); - - // for afterCursorQuery - rawQuery = {}; - prepareCursorQuery(rawQuery, cursorData, indexKeys, indexData, '$gt', '$lt'); - expect(rawQuery).toEqual({ - $or: [ - { a: 10, b: 100, c: { $gt: 1000 } }, - { a: 10, b: { $lt: 100 } }, - { a: { $gt: 10 } }, - ], - }); - }); - - it('desc order', () => { - const indexData = { a: -1, b: 1, c: -1 }; - - // for beforeCursorQuery - rawQuery = {}; - prepareCursorQuery(rawQuery, cursorData, indexKeys, indexData, '$lt', '$gt'); - expect(rawQuery).toEqual({ - $or: [ - { a: 10, b: 100, c: { $gt: 1000 } }, - { a: 10, b: { $lt: 100 } }, - { a: { $gt: 10 } }, - ], - }); - - // for afterCursorQuery - rawQuery = {}; - prepareCursorQuery(rawQuery, cursorData, indexKeys, indexData, '$gt', '$lt'); - expect(rawQuery).toEqual({ - $or: [ - { a: 10, b: 100, c: { $lt: 1000 } }, - { a: 10, b: { $gt: 100 } }, - { a: { $lt: 10 } }, - ], - }); - }); - }); - }); -}); diff --git a/src/composeWithMongoose.js b/src/composeWithMongoose.js index aba57b51..159a792f 100644 --- a/src/composeWithMongoose.js +++ b/src/composeWithMongoose.js @@ -4,10 +4,8 @@ import type { TypeComposer, InputTypeComposer, SchemaComposer } from 'graphql-compose'; import { schemaComposer } from 'graphql-compose'; import type { MongooseModel } from 'mongoose'; -import type { ConnectionSortMapOpts } from 'graphql-compose-connection'; import { convertModelToGraphQL } from './fieldsConverter'; import * as resolvers from './resolvers'; -import { prepareConnectionResolver } from './prepareConnectionResolver'; import type { FilterHelperArgsOpts, LimitHelperArgsOpts, @@ -15,8 +13,10 @@ import type { RecordHelperArgsOpts, } from './resolvers/helpers'; import MongoID from './types/mongoid'; +import type { PaginationResolverOpts } from './resolvers/pagination'; +import type { ConnectionSortMapOpts } from './resolvers/connection'; -export type TypeConverterOpts = { +export type TypeConverterOpts = {| schemaComposer?: SchemaComposer, name?: string, description?: string, @@ -27,7 +27,7 @@ export type TypeConverterOpts = { }, inputType?: TypeConverterInputTypeOpts, resolvers?: false | TypeConverterResolversOpts, -}; +|}; export type TypeConverterInputTypeOpts = { name?: string, @@ -110,13 +110,9 @@ export type TypeConverterResolversOpts = { pagination?: PaginationResolverOpts | false, }; -export type PaginationResolverOpts = { - perPage?: number, -}; - export function composeWithMongoose( model: Object, // MongooseModel, TODO use Model from mongoose_v4.x.x definition when it will be public - opts: TypeConverterOpts = {} + opts: TypeConverterOpts = ({}: any) ): TypeComposer { const name: string = (opts && opts.name) || model.modelName; @@ -217,30 +213,10 @@ export function createResolvers( const createResolverFn = resolvers[resolverName]; if (createResolverFn) { const resolver = createResolverFn(model, tc, opts[resolverName] || {}); - tc.setResolver(resolverName, resolver); + if (resolver) { + tc.setResolver(resolverName, resolver); + } } } }); - - if (!{}.hasOwnProperty.call(opts, 'connection') || opts.connection !== false) { - prepareConnectionResolver(model, tc, opts.connection ? opts.connection : {}); - } - - if (!{}.hasOwnProperty.call(opts, 'pagination') || opts.pagination !== false) { - preparePaginationResolver(tc, opts.pagination || {}); - } -} - -export function preparePaginationResolver(tc: TypeComposer, opts: PaginationResolverOpts) { - try { - require.resolve('graphql-compose-pagination'); - } catch (e) { - return; - } - const composeWithPagination = require('graphql-compose-pagination').default; - composeWithPagination(tc, { - findResolverName: 'findMany', - countResolverName: 'count', - ...opts, - }); } diff --git a/src/composeWithMongooseDiscriminators.js b/src/composeWithMongooseDiscriminators.js new file mode 100644 index 00000000..753b219f --- /dev/null +++ b/src/composeWithMongooseDiscriminators.js @@ -0,0 +1,11 @@ +/* @flow */ + +import type { Model } from 'mongoose'; +import { type DiscriminatorOptions, DiscriminatorTypeComposer } from './discriminators'; + +export function composeWithMongooseDiscriminators( + baseModel: Class, + opts?: DiscriminatorOptions +): DiscriminatorTypeComposer { + return DiscriminatorTypeComposer.createFromModel(baseModel, opts); +} diff --git a/src/discriminators/DiscriminatorTypeComposer.js b/src/discriminators/DiscriminatorTypeComposer.js new file mode 100644 index 00000000..59ed2bbc --- /dev/null +++ b/src/discriminators/DiscriminatorTypeComposer.js @@ -0,0 +1,344 @@ +/* @flow */ + +import type { ComposeFieldConfigMap } from 'graphql-compose'; +import { + EnumTypeComposer, + schemaComposer, + SchemaComposer, + TypeComposerClass, + type InterfaceTypeComposerClass, + type ComposeFieldConfig, + type RelationOpts, + type GetRecordIdFn, +} from 'graphql-compose'; +import type { ComposePartialFieldConfigAsObject } from 'graphql-compose/lib/TypeComposer'; +import type { Model } from 'mongoose'; +import { composeWithMongoose, type TypeConverterOpts } from '../composeWithMongoose'; +import { composeChildTC } from './composeChildTC'; +import { mergeCustomizationOptions } from './utils/mergeCustomizationOptions'; +import { prepareBaseResolvers } from './prepareBaseResolvers'; +import { reorderFields } from './utils/reorderFields'; + +export type DiscriminatorOptions = {| + reorderFields?: boolean | string[], // true order: _id, DKey, DInterfaceFields, DiscriminatorFields + ...TypeConverterOpts, +|}; + +type Discriminators = { + [DName: string]: any, +}; + +// sets the values on DKey enum TC +function setDKeyETCValues(discriminators: Discriminators): any { + const values: { [propName: string]: { value: string } } = {}; + + for (const DName in discriminators) { + if (discriminators.hasOwnProperty(DName)) { + values[DName] = { + value: DName, + }; + } + } + + return values; +} + +// creates an enum from discriminator names +// then sets this enum type as the discriminator key field type +function createAndSetDKeyETC(dTC: DiscriminatorTypeComposer, discriminators: Discriminators) { + const DKeyETC = EnumTypeComposer.create({ + name: `EnumDKey${dTC.getTypeName()}${dTC.getDKey()[0].toUpperCase() + dTC.getDKey().substr(1)}`, + values: setDKeyETCValues(discriminators), + }); + + // set on Output + dTC.extendField(dTC.getDKey(), { + type: () => DKeyETC, + }); + + // set on Input + dTC.getInputTypeComposer().extendField(dTC.getDKey(), { + type: () => DKeyETC, + }); + + return DKeyETC; +} + +export class DiscriminatorTypeComposer extends TypeComposerClass { + discriminatorKey: string; + + DKeyETC: EnumTypeComposer; + + opts: DiscriminatorOptions; + + DInterface: InterfaceTypeComposerClass; + + childTCs: TypeComposerClass[]; + + static _getClassConnectedWithSchemaComposer( + sc?: SchemaComposer + ): Class> { + class _DiscriminatorTypeComposer extends DiscriminatorTypeComposer { + static schemaComposer = sc || schemaComposer; + } + + return _DiscriminatorTypeComposer; + } + + /* :: + constructor(gqType: any): DiscriminatorTypeComposer { + super(gqType); + return this; + } + */ + + static createFromModel(baseModel: Class, opts?: any): DiscriminatorTypeComposer { + if (!baseModel || !(baseModel: any).discriminators) { + throw Error('Discriminator Key not Set, Use composeWithMongoose for Normal Collections'); + } + + opts = { // eslint-disable-line + reorderFields: true, + schemaComposer, + ...opts, + }; + + const baseTC = composeWithMongoose(baseModel, opts); + + const _DiscriminatorTypeComposer = this._getClassConnectedWithSchemaComposer( + opts.schemaComposer + ); + const baseDTC = new _DiscriminatorTypeComposer(baseTC.getType()); + + baseDTC.opts = opts; + baseDTC.childTCs = []; + baseDTC.discriminatorKey = (baseModel: any).schema.get('discriminatorKey') || '__t'; + + // discriminators an object containing all discriminators with key being DNames + baseDTC.DKeyETC = createAndSetDKeyETC(baseDTC, (baseModel: any).discriminators); + + reorderFields(baseDTC, (baseDTC.opts: any).reorderFields, baseDTC.discriminatorKey); + + baseDTC.DInterface = baseDTC._createDInterface(baseDTC); + baseDTC.setInterfaces([baseDTC.DInterface]); + + baseDTC.schemaComposer.addSchemaMustHaveType(baseDTC); + + // prepare Base Resolvers + prepareBaseResolvers(baseDTC); + + return baseDTC; + } + + _createDInterface(baseTC: DiscriminatorTypeComposer): InterfaceTypeComposerClass { + return this.schemaComposer.InterfaceTypeComposer.create({ + name: `${baseTC.getTypeName()}Interface`, + + resolveType: (value: any) => { + const childDName = value[baseTC.getDKey()]; + + if (childDName) { + return baseTC.schemaComposer.getTC(childDName).getType(); + } + + // as fallback return BaseModelTC + return baseTC.schemaComposer.getTC(baseTC.getTypeName()).getType(); + }, + fields: (): ComposeFieldConfigMap => { + const baseFields = baseTC.getFieldNames(); + const interfaceFields = {}; + for (const field of baseFields) { + interfaceFields[field] = baseTC.getFieldConfig(field); + } + return interfaceFields; + }, + }); + } + + getDKey(): string { + return this.discriminatorKey; + } + + getDKeyETC(): EnumTypeComposer { + return this.DKeyETC; + } + + getDInterface(): InterfaceTypeComposerClass { + return this.DInterface; + } + + hasChildTC(DName: string): boolean { + return !!this.childTCs.find(ch => ch.getTypeName() === DName); + } + + setFields(fields: ComposeFieldConfigMap): DiscriminatorTypeComposer { + const oldFieldNames = super.getFieldNames(); + super.setFields(fields); + + for (const childTC of this.childTCs) { + childTC.removeField(oldFieldNames); + childTC.addFields(fields); + reorderFields(childTC, (this.opts: any).reorderFields, this.getDKey(), super.getFieldNames()); + } + + return this; + } + + setField( + fieldName: string, + fieldConfig: ComposeFieldConfig + ): DiscriminatorTypeComposer { + super.setField(fieldName, fieldConfig); + + for (const childTC of this.childTCs) { + childTC.setField(fieldName, fieldConfig); + } + + return this; + } + + // discriminators must have all interface fields + addFields(newFields: ComposeFieldConfigMap): DiscriminatorTypeComposer { + super.addFields(newFields); + + for (const childTC of this.childTCs) { + childTC.addFields(newFields); + } + + return this; + } + + addNestedFields(newFields: ComposeFieldConfigMap): DiscriminatorTypeComposer { + super.addNestedFields(newFields); + + for (const childTC of this.childTCs) { + childTC.addNestedFields(newFields); + } + + return this; + } + + removeField(fieldNameOrArray: string | Array): DiscriminatorTypeComposer { + super.removeField(fieldNameOrArray); + + for (const childTC of this.childTCs) { + childTC.removeField(fieldNameOrArray); + } + + return this; + } + + removeOtherFields(fieldNameOrArray: string | Array): DiscriminatorTypeComposer { + const oldFieldNames = super.getFieldNames(); + super.removeOtherFields(fieldNameOrArray); + + for (const childTC of this.childTCs) { + const specificFields = childTC + .getFieldNames() + .filter( + childFieldName => + !oldFieldNames.find(oldBaseFieldName => oldBaseFieldName === childFieldName) + ); + childTC.removeOtherFields(super.getFieldNames().concat(specificFields)); + reorderFields(childTC, (this.opts: any).reorderFields, this.getDKey(), super.getFieldNames()); + } + + return this; + } + + extendField( + fieldName: string, + partialFieldConfig: ComposePartialFieldConfigAsObject + ): this { + super.extendField(fieldName, partialFieldConfig); + + for (const childTC of this.childTCs) { + childTC.extendField(fieldName, partialFieldConfig); + } + + return this; + } + + reorderFields(names: string[]): DiscriminatorTypeComposer { + super.reorderFields(names); + + for (const childTC of this.childTCs) { + childTC.reorderFields(names); + } + + return this; + } + + makeFieldNonNull(fieldNameOrArray: string | Array): DiscriminatorTypeComposer { + super.makeFieldNonNull(fieldNameOrArray); + + for (const childTC of this.childTCs) { + childTC.makeFieldNonNull(fieldNameOrArray); + } + + return this; + } + + makeFieldNullable(fieldNameOrArray: string | Array): DiscriminatorTypeComposer { + super.makeFieldNullable(fieldNameOrArray); + + for (const childTC of this.childTCs) { + childTC.makeFieldNullable(fieldNameOrArray); + } + + return this; + } + + deprecateFields( + fields: { [fieldName: string]: string } | string[] | string + ): DiscriminatorTypeComposer { + super.deprecateFields(fields); + + for (const childTC of this.childTCs) { + childTC.deprecateFields(fields); + } + + return this; + } + + // relations with args are a bit hard to manage as interfaces i believe as of now do not + // support field args. Well if one wants to have use args, you setType for resolver as this + // this = this DiscriminantTypeComposer + // NOTE, those relations will be propagated to the childTypeComposers and you can use normally. + addRelation( + fieldName: string, + relationOpts: RelationOpts + ): DiscriminatorTypeComposer { + super.addRelation(fieldName, relationOpts); + + for (const childTC of this.childTCs) { + childTC.addRelation(fieldName, relationOpts); + } + + return this; + } + + setRecordIdFn(fn: GetRecordIdFn): DiscriminatorTypeComposer { + super.setRecordIdFn(fn); + + for (const childTC of this.childTCs) { + childTC.setRecordIdFn(fn); + } + + return this; + } + + /* eslint no-use-before-define: 0 */ + discriminator(childModel: Class, opts?: TypeConverterOpts): TypeComposerClass { + const customizationOpts = mergeCustomizationOptions((this.opts: any), opts); + + let childTC = composeWithMongoose(childModel, customizationOpts); + + childTC = composeChildTC(this, childTC, this.opts); + + this.schemaComposer.addSchemaMustHaveType(childTC); + this.childTCs.push(childTC); + + return childTC; + } +} diff --git a/src/discriminators/__mocks__/characterModels.js b/src/discriminators/__mocks__/characterModels.js new file mode 100644 index 00000000..b9605492 --- /dev/null +++ b/src/discriminators/__mocks__/characterModels.js @@ -0,0 +1,65 @@ +/* @flow */ + +import { mongoose, Schema, Types } from '../../__mocks__/mongooseCommon'; +import { DroidSchema } from './droidSchema'; +import { PersonSchema } from './personSchema'; + +const enumCharacterType = { + PERSON: 'Person', + DROID: 'Droid', +}; + +export const CharacterObject = { + _id: { + type: String, + default: () => new Types.ObjectId(), + }, + name: String, + + type: { + type: String, + require: true, + enum: (Object.keys(enumCharacterType): Array), + }, + kind: { + type: String, + require: true, + enum: (Object.keys(enumCharacterType): Array), + }, + + friends: [String], // another Character + appearsIn: [String], // movie +}; + +const CharacterSchema = new Schema(CharacterObject); +const ACharacterSchema = new Schema(Object.assign({}, CharacterObject)); + +export function getCharacterModels(DKey: string) { + CharacterSchema.set('discriminatorKey', DKey); + + const CharacterModel = mongoose.models.Character + ? mongoose.models.Character + : mongoose.model('Character', CharacterSchema); + + const PersonModel = mongoose.models[enumCharacterType.PERSON] + ? mongoose.models[enumCharacterType.PERSON] + : CharacterModel.discriminator(enumCharacterType.PERSON, PersonSchema); + + const DroidModel = mongoose.models[enumCharacterType.DROID] + ? mongoose.models[enumCharacterType.DROID] + : CharacterModel.discriminator(enumCharacterType.DROID, DroidSchema); + + return { CharacterModel, PersonModel, DroidModel }; +} + +export function getCharacterModelClone() { + const NoDKeyCharacterModel = mongoose.model('NoDKeyCharacter', ACharacterSchema); + + /* + const APersonModel = ACharacterModel.discriminator('A' + enumCharacterType.PERSON, PersonSchema.clone()); + + const ADroidModel = ACharacterModel.discriminator('A' + enumCharacterType.DROID, DroidSchema.clone()); + */ + + return { NoDKeyCharacterModel }; // APersonModel, ADroidModel }; +} diff --git a/src/discriminators/__mocks__/droidSchema.js b/src/discriminators/__mocks__/droidSchema.js new file mode 100644 index 00000000..31b6a41f --- /dev/null +++ b/src/discriminators/__mocks__/droidSchema.js @@ -0,0 +1,9 @@ +/* @flow */ + +import { Schema } from '../../__mocks__/mongooseCommon'; + +export const DroidSchema = new Schema({ + makeDate: Date, + modelNumber: Number, + primaryFunction: [String], +}); diff --git a/src/discriminators/__mocks__/movieModel.js b/src/discriminators/__mocks__/movieModel.js new file mode 100644 index 00000000..06247cde --- /dev/null +++ b/src/discriminators/__mocks__/movieModel.js @@ -0,0 +1,22 @@ +/* @flow */ + +import { mongoose, Schema } from '../../__mocks__/mongooseCommon'; + +const MovieSchema = new Schema({ + _id: String, + + characters: { + type: [String], // redundant but i need it. + description: 'A character in the Movie, Person or Droid.', + }, + + director: { + type: String, // id of director + description: 'Directed the movie.', + }, + + imdbRatings: String, + releaseDate: String, +}); + +export const MovieModel = mongoose.model('Movie', MovieSchema); diff --git a/src/discriminators/__mocks__/personSchema.js b/src/discriminators/__mocks__/personSchema.js new file mode 100644 index 00000000..afe2890d --- /dev/null +++ b/src/discriminators/__mocks__/personSchema.js @@ -0,0 +1,9 @@ +/* @flow */ + +import { Schema } from '../../__mocks__/mongooseCommon'; + +export const PersonSchema = new Schema({ + dob: Number, + starShips: [String], + totalCredits: Number, +}); diff --git a/src/discriminators/__tests__/DiscriminatorTypeComposer-test.js b/src/discriminators/__tests__/DiscriminatorTypeComposer-test.js new file mode 100644 index 00000000..907d7b92 --- /dev/null +++ b/src/discriminators/__tests__/DiscriminatorTypeComposer-test.js @@ -0,0 +1,502 @@ +/* @flow */ + +import { schemaComposer, graphql, TypeComposer, InterfaceTypeComposer } from 'graphql-compose'; +import { getCharacterModels } from '../__mocks__/characterModels'; +import { MovieModel } from '../__mocks__/movieModel'; +import { composeWithMongoose } from '../../composeWithMongoose'; +import { composeWithMongooseDiscriminators } from '../../composeWithMongooseDiscriminators'; + +const { CharacterModel, PersonModel, DroidModel } = getCharacterModels('type'); + +describe('DiscriminatorTypeComposer', () => { + it('should have as interface DInterface', () => { + const baseDTC = composeWithMongooseDiscriminators(CharacterModel); + expect(baseDTC.hasInterface(baseDTC.getDInterface())).toBeTruthy(); + }); + + describe('DInterface', () => { + afterAll(() => schemaComposer.clear()); + const baseDTC = composeWithMongooseDiscriminators(CharacterModel); + + it('should have same field names as baseModel used to create it', () => { + expect(baseDTC.getFieldNames()).toEqual( + expect.arrayContaining(Object.keys(baseDTC.getDInterface().getFields())) + ); + }); + + it('should be accessed with getDInterface', () => { + expect(baseDTC.getDInterface()).toBeInstanceOf(InterfaceTypeComposer); + }); + + it('should have field names synced with the baseTC', () => { + expect(baseDTC.getFieldNames()).toEqual(Object.keys(baseDTC.getDInterface().getFields())); + + beforeAll(() => { + baseDTC.addFields({ + field1: 'String', + field2: 'String', + }); + }); + + expect(baseDTC.getFieldNames()).toEqual(Object.keys(baseDTC.getDInterface().getFields())); + }); + }); + + describe('class methods', () => { + let characterDTC; + let personTC; + let droidTC; + + describe('hasChildTC(DName)', () => { + beforeAll(() => { + schemaComposer.clear(); + + characterDTC = composeWithMongooseDiscriminators(CharacterModel); + personTC = characterDTC.discriminator(PersonModel); + }); + + it('should check and return true if childTC is available', () => { + expect(characterDTC.hasChildTC(personTC.getTypeName())).toBeTruthy(); + }); + + it('should be falsified as childTC not found', () => { + expect(characterDTC.hasChildTC('NOT_AVAILABLE')).toBeFalsy(); + }); + }); + + describe('setFields(fields)', () => { + let personSpecificFields; + let droidSpecificFields; + + beforeAll(() => { + schemaComposer.clear(); + + characterDTC = composeWithMongooseDiscriminators(CharacterModel); + droidTC = characterDTC.discriminator(DroidModel); + personTC = characterDTC.discriminator(PersonModel); + + personSpecificFields = personTC.getFieldNames().filter(v => !characterDTC.hasField(v)); + droidSpecificFields = droidTC.getFieldNames().filter(v => !characterDTC.hasField(v)); + }); + + const fieldsToSet = { + kind: 'String', + appearsIn: 'String', + field1: 'String', + field2: 'String', + }; + + beforeAll(() => { + characterDTC.setFields(fieldsToSet); + }); + + afterAll(() => schemaComposer.clear()); + + it('should set fields to baseTC', () => { + expect(characterDTC.getFieldNames()).toEqual(Object.keys(fieldsToSet)); + }); + + it('should sets fields to DInterface', () => { + expect(Object.keys(characterDTC.getDInterface().getFields())).toEqual( + Object.keys(fieldsToSet) + ); + }); + + it('should set fields to childTC', () => { + expect(personTC.getFieldNames()).toEqual(expect.arrayContaining(Object.keys(fieldsToSet))); + expect(droidTC.getFieldNames()).toEqual(expect.arrayContaining(Object.keys(fieldsToSet))); + }); + + it('should keep child specific fields', () => { + expect(droidTC.getFieldNames()).toEqual(expect.arrayContaining(droidSpecificFields)); + expect(personTC.getFieldNames()).toEqual(expect.arrayContaining(personSpecificFields)); + }); + + it('should contain total of fieldsToSet and child specific fields', () => { + expect(droidTC.getFieldNames().length).toBe(droidSpecificFields.length + 4); + expect(personTC.getFieldNames().length).toBe(personSpecificFields.length + 4); + }); + }); + + describe('setField(fieldName, fieldConfig)', () => { + beforeAll(() => { + schemaComposer.clear(); + + characterDTC = composeWithMongooseDiscriminators(CharacterModel); + droidTC = characterDTC.discriminator(DroidModel); + personTC = characterDTC.discriminator(PersonModel); + }); + const fieldName = 'myField'; + const fieldConfig = { + type: 'String', + }; + + beforeAll(() => { + characterDTC.setField(fieldName, fieldConfig); + }); + + afterAll(() => schemaComposer.clear()); + + it('should set field on baseTC', () => { + expect(characterDTC.getFieldType(fieldName)).toEqual(graphql.GraphQLString); + }); + + it('should set field on DInterface', () => { + expect(characterDTC.getDInterface().getFieldType(fieldName)).toEqual(graphql.GraphQLString); + }); + + it('should set field on childTC', () => { + expect(droidTC.getFieldType(fieldName)).toEqual(graphql.GraphQLString); + expect(personTC.getFieldType(fieldName)).toEqual(graphql.GraphQLString); + }); + }); + + describe('addFields(newFields)', () => { + let personSpecificFields; + let droidSpecificFields; + + beforeAll(() => { + schemaComposer.clear(); + + characterDTC = composeWithMongooseDiscriminators(CharacterModel); + droidTC = characterDTC.discriminator(DroidModel); + personTC = characterDTC.discriminator(PersonModel); + + personSpecificFields = personTC.getFieldNames().filter(v => !characterDTC.hasField(v)); + droidSpecificFields = droidTC.getFieldNames().filter(v => !characterDTC.hasField(v)); + }); + + const newFields = { + field1: 'String', + field2: 'String', + }; + + beforeAll(() => { + characterDTC.addFields(newFields); + }); + + afterAll(() => schemaComposer.clear()); + + it('should add fields to baseTC', () => { + expect(characterDTC.getFieldNames()).toEqual( + expect.arrayContaining(Object.keys(newFields)) + ); + }); + + it('should add fields to DInterface', () => { + expect(Object.keys(characterDTC.getDInterface().getFields())).toEqual( + expect.arrayContaining(Object.keys(newFields)) + ); + }); + + it('should have exactly plus two fields added to childTC fields', () => { + expect(droidTC.getFieldNames()).toEqual([ + ...characterDTC.getFieldNames(), + ...droidSpecificFields, + ]); + expect(personTC.getFieldNames()).toEqual([ + ...characterDTC.getFieldNames(), + ...personSpecificFields, + ]); + }); + }); + + describe('addNestedFields(newFields)', () => { + let personFields; + let droidFields; + + beforeAll(() => { + schemaComposer.clear(); + + characterDTC = composeWithMongooseDiscriminators(CharacterModel); + droidTC = characterDTC.discriminator(DroidModel); + personTC = characterDTC.discriminator(PersonModel); + + personFields = personTC.getFieldNames(); + droidFields = droidTC.getFieldNames(); + }); + + const newFields = { + 'field1.nested': 'String', + 'field2.nested': 'String', + }; + + beforeAll(() => { + characterDTC.addNestedFields(newFields); + }); + + afterAll(() => schemaComposer.clear()); + + it('should add field to baseTC', () => { + expect(characterDTC.getFieldTC('field1').getFieldType('nested')).toEqual( + graphql.GraphQLString + ); + }); + + it('should add field to DInterface', () => { + expect( + characterDTC + .getDInterface() + .getFieldTC('field1') + .getFieldType('nested') + ).toEqual(graphql.GraphQLString); + }); + + it('should have exactly plus two fields added to childTC fields', () => { + expect(droidTC.getFieldTC('field1').getFieldType('nested')).toEqual(graphql.GraphQLString); + expect(personTC.getFieldTC('field2').getFieldType('nested')).toEqual(graphql.GraphQLString); + }); + + it('should have plus 2 field length on childTC', () => { + expect(droidTC.getFieldNames().length).toBe(droidFields.length + 2); + expect(personTC.getFieldNames().length).toBe(personFields.length + 2); + }); + }); + + describe('removeField(fieldName)', () => { + let personFields; + let droidFields; + + beforeAll(() => { + schemaComposer.clear(); + + characterDTC = composeWithMongooseDiscriminators(CharacterModel); + droidTC = characterDTC.discriminator(DroidModel); + personTC = characterDTC.discriminator(PersonModel); + + personFields = personTC.getFieldNames().filter(v => v !== 'friends'); + droidFields = droidTC.getFieldNames().filter(v => v !== 'friends'); + }); + + const field = 'friends'; + + beforeAll(() => { + characterDTC.removeField(field); + }); + + afterAll(() => schemaComposer.clear()); + + it('should remove fields from baseTC', () => { + expect(characterDTC.hasField(field)).toBeFalsy(); + }); + + it('should remove fields from DInterface', () => { + expect(characterDTC.getDInterface().hasField(field)).toBeFalsy(); + }); + + it('should remove fields from childTC', () => { + expect(personTC.hasField(field)).toBeFalsy(); + expect(droidTC.hasField(field)).toBeFalsy(); + }); + + it('should remove only DFields fields', () => { + expect(droidTC.getFieldNames()).toEqual(droidFields); + expect(personTC.getFieldNames()).toEqual(personFields); + }); + }); + + describe('removeOtherFields(fieldNames)', () => { + let personSpecificFields; + let droidSpecificFields; + + beforeAll(() => { + schemaComposer.clear(); + + characterDTC = composeWithMongooseDiscriminators(CharacterModel); + droidTC = characterDTC.discriminator(DroidModel); + personTC = characterDTC.discriminator(PersonModel); + + personSpecificFields = personTC.getFieldNames().filter(v => !characterDTC.hasField(v)); + droidSpecificFields = droidTC.getFieldNames().filter(v => !characterDTC.hasField(v)); + }); + const fields = ['type', 'friends']; + + beforeAll(() => { + characterDTC.removeOtherFields(fields); + }); + + afterAll(() => schemaComposer.clear()); + + it('should remove fields from baseTC', () => { + expect(characterDTC.getFieldNames()).toEqual(fields); + }); + + it('should remove fields from DInterface', () => { + expect(characterDTC.getDInterface().getFieldNames()).toEqual(fields); + }); + + it('should remove only DFields from childTC', () => { + expect(personTC.getFieldNames()).toEqual([...fields, ...personSpecificFields]); + expect(droidTC.getFieldNames()).toEqual([...fields, ...droidSpecificFields]); + }); + }); + + describe('extendFields(fieldName, extensionField)', () => { + let personFields; + let droidFields; + + beforeAll(() => { + schemaComposer.clear(); + + characterDTC = composeWithMongooseDiscriminators(CharacterModel); + droidTC = characterDTC.discriminator(DroidModel); + personTC = characterDTC.discriminator(PersonModel); + + personFields = personTC.getFieldNames(); + droidFields = droidTC.getFieldNames(); + }); + + const fieldName = 'kind'; + const fieldExtension = { + type: 'String', + description: 'Hello I am changed', + }; + + beforeAll(() => { + characterDTC.extendField(fieldName, fieldExtension); + }); + + it('should extend field on baseTC', () => { + expect(characterDTC.getFieldType(fieldName).toString()).toEqual(graphql.GraphQLString.name); + + expect((characterDTC.getField(fieldName): any).description).toEqual( + fieldExtension.description + ); + }); + + it('should extend field type on DInterface', () => { + expect(characterDTC.getDInterface().getFields()[fieldName]).toBeTruthy(); + expect( + characterDTC + .getDInterface() + .getFieldType(fieldName) + .toString() + ).toEqual(fieldExtension.type); + }); + + it('should extend field on childTC', () => { + expect(personTC.getFieldType(fieldName)).toEqual(graphql.GraphQLString); + + expect((personTC.getField(fieldName): any).description).toEqual(fieldExtension.description); + + expect(droidTC.getFieldType(fieldName)).toEqual(graphql.GraphQLString); + + expect((droidTC.getField(fieldName): any).description).toEqual(fieldExtension.description); + }); + + it('should have same field length on childTC an others', () => { + expect(droidTC.getFieldNames().length).toBe(droidFields.length); + expect(personTC.getFieldNames().length).toBe(personFields.length); + }); + }); + + describe('makeFieldNonNull(fieldName), makeFieldNullable(fieldName), deprecateFields(fieldName)', () => { + beforeAll(() => { + schemaComposer.clear(); + + characterDTC = composeWithMongooseDiscriminators(CharacterModel); + droidTC = characterDTC.discriminator(DroidModel); + personTC = characterDTC.discriminator(PersonModel); + }); + + const fieldNonNull = 'type'; + const fieldNullable = 'friends'; + const fieldDeprecated = 'kind'; + const deprecateMessage = 'Property was to be use for tests only'; + + beforeAll(() => { + characterDTC.makeFieldNonNull(fieldNonNull); + characterDTC.makeFieldNonNull(fieldNullable); + characterDTC.makeFieldNullable(fieldNullable); + characterDTC.deprecateFields({ [fieldDeprecated]: deprecateMessage }); + }); + + it('should effect fields on baseTC', () => { + expect(characterDTC.isFieldNonNull(fieldNonNull)).toBeTruthy(); + expect(characterDTC.isFieldNonNull(fieldNullable)).toBeFalsy(); + expect(characterDTC.getFieldConfig(fieldDeprecated).deprecationReason).toBe( + deprecateMessage + ); + }); + + it('should effect fields on DInterface', () => { + const dInterface = characterDTC.getDInterface(); + + expect(dInterface.isFieldNonNull(fieldNonNull)).toBeTruthy(); + expect(dInterface.isFieldNonNull(fieldNullable)).toBeFalsy(); + expect(dInterface.getFieldConfig(fieldDeprecated).deprecationReason).toBe(deprecateMessage); + }); + + it('should effect fields on childTC', () => { + expect(personTC.isFieldNonNull(fieldNonNull)).toBeTruthy(); + expect(personTC.isFieldNonNull(fieldNullable)).toBeFalsy(); + expect(personTC.getFieldConfig(fieldDeprecated).deprecationReason).toBe(deprecateMessage); + expect(droidTC.isFieldNonNull(fieldNonNull)).toBeTruthy(); + expect(droidTC.isFieldNonNull(fieldNullable)).toBeFalsy(); + expect(droidTC.getFieldConfig(fieldDeprecated).deprecationReason).toBe(deprecateMessage); + }); + }); + + describe('addRelation(fieldName, relationOpts)', () => { + beforeAll(() => { + schemaComposer.clear(); + + characterDTC = composeWithMongooseDiscriminators(CharacterModel); + droidTC = characterDTC.discriminator(DroidModel); + personTC = characterDTC.discriminator(PersonModel); + }); + + const relationField = 'movies'; + const relationResolver = composeWithMongoose(MovieModel).getResolver('findMany'); + + beforeAll(() => { + characterDTC.addRelation(relationField, { + resolver: relationResolver, + }); + }); + + it('should create relation on baseTC', () => { + expect(characterDTC.getRelations()[relationField].resolver).toEqual(relationResolver); + }); + + it('should create field with type Movie on DInterface', () => { + const dInterface = characterDTC.getDInterface(); + + expect(dInterface.getFieldType(relationField)).toEqual(relationResolver.getType()); + }); + + it('should create Movie relation on childTC', () => { + expect(personTC.getRelations()[relationField].resolver).toEqual(relationResolver); + expect(droidTC.getRelations()[relationField].resolver).toEqual(relationResolver); + }); + }); + }); + + describe('discriminator()', () => { + let characterDTC; + + beforeEach(() => { + characterDTC = composeWithMongooseDiscriminators(CharacterModel); + }); + + it('should return an instance of TypeComposer as childTC', () => { + expect(characterDTC.discriminator(PersonModel)).toBeInstanceOf(TypeComposer); + expect(characterDTC.discriminator(DroidModel)).toBeInstanceOf(TypeComposer); + }); + + it('should register childTC in childTC(childTCs) array', () => { + const childTC = characterDTC.discriminator(DroidModel); + expect(characterDTC.hasChildTC(childTC.getTypeName())).toBeTruthy(); + }); + + it('should apply filters passed', () => { + const tc = characterDTC.discriminator(PersonModel, { + fields: { + remove: ['dob', 'starShips'], + }, + }); + + expect(tc.getFieldNames()).not.toEqual(expect.arrayContaining(['dob', 'starShips'])); + }); + }); +}); diff --git a/src/discriminators/__tests__/composeChildTC-test.js b/src/discriminators/__tests__/composeChildTC-test.js new file mode 100644 index 00000000..99d06487 --- /dev/null +++ b/src/discriminators/__tests__/composeChildTC-test.js @@ -0,0 +1,47 @@ +/* @flow */ + +import { schemaComposer } from 'graphql-compose'; +import { getCharacterModels } from '../__mocks__/characterModels'; +import { composeWithMongooseDiscriminators } from '../../composeWithMongooseDiscriminators'; + +const { CharacterModel, PersonModel, DroidModel } = getCharacterModels('type'); + +beforeAll(() => schemaComposer.clear()); + +describe('composeChildTC ->', () => { + const CharacterDTC = composeWithMongooseDiscriminators(CharacterModel); + const PersonTC = CharacterDTC.discriminator(PersonModel); + const DroidTC = CharacterDTC.discriminator(DroidModel); + + it('should set DInterface to childTC', () => { + expect(DroidTC.hasInterface(CharacterDTC.getDInterface())).toBeTruthy(); + expect(PersonTC.hasInterface(CharacterDTC.getDInterface())).toBeTruthy(); + }); + + it('should copy all baseFields from BaseDTC to ChildTCs', () => { + expect(DroidTC.getFieldNames()).toEqual(expect.arrayContaining(CharacterDTC.getFieldNames())); + expect(PersonTC.getFieldNames()).toEqual(expect.arrayContaining(CharacterDTC.getFieldNames())); + }); + + it('should make childTC have same fieldTypes as baseTC', () => { + const characterFields = CharacterDTC.getFieldNames(); + + for (const field of characterFields) { + expect(DroidTC.getFieldType(field)).toEqual(CharacterDTC.getFieldType(field)); + expect(PersonTC.getFieldType(field)).toEqual(CharacterDTC.getFieldType(field)); + } + }); + + it('should operate normally like any other TypeComposer', () => { + const fields = PersonTC.getFieldNames(); + + PersonTC.addFields({ + field: { type: 'String' }, + }); + + expect(PersonTC.getFieldNames()).toEqual(fields.concat(['field'])); + + PersonTC.removeField('field'); + expect(PersonTC.getFieldNames()).toEqual(fields); + }); +}); diff --git a/src/discriminators/__tests__/prepareBaseResolvers-test.js b/src/discriminators/__tests__/prepareBaseResolvers-test.js new file mode 100644 index 00000000..87e1ddfa --- /dev/null +++ b/src/discriminators/__tests__/prepareBaseResolvers-test.js @@ -0,0 +1,161 @@ +/* @flow */ + +import { graphql } from 'graphql-compose'; +import { getCharacterModels } from '../__mocks__/characterModels'; +import { composeWithMongooseDiscriminators } from '../../composeWithMongooseDiscriminators'; + +const { CharacterModel } = getCharacterModels('type'); + +const CharacterDTC = composeWithMongooseDiscriminators(CharacterModel); + +describe('prepareBaseResolvers()', () => { + describe('setDKeyEnumOnITCArgs()', () => { + const resolversWithFilterAndRecordArgs = []; + const resolversWithFilterArgsOnly = []; + const resolversWithRecordArgsOnly = []; + const resolversWithNoInterestArgs = []; + const interestArgs = ['filter', 'record']; + + beforeAll(() => { + const resolvers = CharacterDTC.getResolvers(); // map + + resolvers.forEach(resolver => { + if (resolver.hasArg(interestArgs[0]) && resolver.hasArg(interestArgs[1])) { + resolversWithFilterAndRecordArgs.push(resolver); + } else if (!(resolver.hasArg(interestArgs[0]) && resolver.hasArg(interestArgs[1]))) { + resolversWithNoInterestArgs.push(resolver); + } else if (resolver.hasArg(interestArgs[0]) && !resolver.hasArg(interestArgs[1])) { + resolversWithFilterArgsOnly.push(resolver); + } else if (!resolver.hasArg(interestArgs[0]) && resolver.hasArg(interestArgs[1])) { + resolversWithRecordArgsOnly.push(resolver); + } + }); + }); + + it('should set type to DKeyEnum on DKey field on filter and record args', () => { + for (const resolver of resolversWithFilterAndRecordArgs) { + for (const arg of interestArgs) { + expect(resolver.getArgTC(arg).getFieldType(CharacterDTC.getDKey())).toEqual( + CharacterDTC.getDKeyETC().getType() + ); + } + } + }); + + it('should set type to DKeyEnum on DKey field only on filter args', () => { + for (const resolver of resolversWithFilterArgsOnly) { + expect(interestArgs[0]).toEqual('filter'); + expect(resolver.getArgTC(interestArgs[0]).getFieldType(CharacterDTC.getDKey())).toEqual( + CharacterDTC.getDKeyETC().getType() + ); + expect(resolver.getArgTC(interestArgs[1]).getFieldType(CharacterDTC.getDKey())).not.toEqual( + CharacterDTC.getDKeyETC().getType() + ); + } + }); + + it('should set type to DKeyEnum on DKey field only on record args', () => { + for (const resolver of resolversWithFilterArgsOnly) { + expect(interestArgs[1]).toEqual('record'); + expect(resolver.getArgTC(interestArgs[1]).getFieldType(CharacterDTC.getDKey())).toEqual( + CharacterDTC.getDKeyETC().getType() + ); + expect(resolver.getArgTC(interestArgs[0]).getFieldType(CharacterDTC.getDKey())).not.toEqual( + CharacterDTC.getDKeyETC().getType() + ); + } + }); + + it('should NOT set type to DKeyEnum on DKey as filter and record not found args', () => { + for (const resolver of resolversWithFilterArgsOnly) { + for (const arg of interestArgs) { + expect(resolver.hasArg(arg)).toBeFalsy(); + } + } + }); + }); + + it('should set resolver type to DInterface List, findMany', () => { + expect(CharacterDTC.getResolver('findMany').getType()).toEqual( + graphql.GraphQLList(CharacterDTC.getDInterface().getType()) + ); + }); + + it('should set resolver type to DInterface List, findByIds', () => { + expect(CharacterDTC.getResolver('findByIds').getType()).toEqual( + graphql.GraphQLList(CharacterDTC.getDInterface().getType()) + ); + }); + + it('should set resolver type to DInterface, findOne', () => { + expect(CharacterDTC.getResolver('findOne').getType()).toEqual( + CharacterDTC.getDInterface().getType() + ); + }); + + it('should set resolver type to DInterface, findById', () => { + expect(CharacterDTC.getResolver('findById').getType()).toEqual( + CharacterDTC.getDInterface().getType() + ); + }); + + it('should set resolver record field type to DInterface, createOne', () => { + expect( + CharacterDTC.getResolver('createOne') + .getTypeComposer() + .getFieldType('record') + ).toEqual(CharacterDTC.getDInterface().getType()); + }); + + it('should set resolver record field type to DInterface, updateOne', () => { + expect( + CharacterDTC.getResolver('updateOne') + .getTypeComposer() + .getFieldType('record') + ).toEqual(CharacterDTC.getDInterface().getType()); + }); + + it('should set resolver record field type to DInterface, updateById', () => { + expect( + CharacterDTC.getResolver('updateById') + .getTypeComposer() + .getFieldType('record') + ).toEqual(CharacterDTC.getDInterface().getType()); + }); + + it('should set resolver record field type to DInterface, ', () => { + expect( + CharacterDTC.getResolver('removeById') + .getTypeComposer() + .getFieldType('record') + ).toEqual(CharacterDTC.getDInterface().getType()); + }); + + it('should set resolver record arg field, DKey to NonNull DKeyETC type, createOne', () => { + expect( + CharacterDTC.getResolver('createOne') + .getArgTC('record') + .getFieldType(CharacterDTC.getDKey()) + ).toEqual(graphql.GraphQLNonNull(CharacterDTC.getDKeyETC().getType())); + }); + + it('should set type on items in pagination resolver to DInterface List, pagination', () => { + expect( + CharacterDTC.getResolver('pagination') + .getTypeComposer() + .getFieldType('items') + ).toEqual(graphql.GraphQLList(CharacterDTC.getDInterface().getType())); + }); + + it('should clone, rename edges field on connection resolver, connection', () => { + const newName = `${CharacterDTC.getTypeName()}Edge`; + const connectionRS = CharacterDTC.getResolver('connection'); + + expect( + connectionRS + .getTypeComposer() + .getFieldTC('edges') + .getTypeName() + ).toEqual(newName); + }); +}); diff --git a/src/discriminators/composeChildTC.js b/src/discriminators/composeChildTC.js new file mode 100644 index 00000000..014b28f1 --- /dev/null +++ b/src/discriminators/composeChildTC.js @@ -0,0 +1,48 @@ +/* @flow */ + +import { TypeComposer } from 'graphql-compose'; +import type { DiscriminatorTypeComposer, DiscriminatorOptions } from './DiscriminatorTypeComposer'; +import { prepareChildResolvers } from './prepareChildResolvers'; +import { reorderFields } from './utils/reorderFields'; + +// copy all baseTypeComposer fields to childTC +// these are the fields before calling discriminator +function copyBaseTCFieldsToChildTC(baseDTC: TypeComposer, childTC: TypeComposer) { + const baseFields = baseDTC.getFieldNames(); + const childFields = childTC.getFieldNames(); + + for (const field of baseFields) { + const childFieldName = childFields.find(fld => fld === field); + + if (childFieldName) { + childTC.extendField(field, { + type: baseDTC.getFieldType(field), + }); + } else { + childTC.setField(field, baseDTC.getField(field)); + } + } + + return childTC; +} + +export function composeChildTC( + baseDTC: DiscriminatorTypeComposer, + childTC: TypeComposer, + opts: DiscriminatorOptions +): TypeComposer { + const composedChildTC = copyBaseTCFieldsToChildTC(baseDTC, childTC); + + composedChildTC.setInterfaces([baseDTC.getDInterface()]); + + prepareChildResolvers(baseDTC, composedChildTC, opts); + + reorderFields( + composedChildTC, + (opts: any).reorderFields, + baseDTC.getDKey(), + baseDTC.getFieldNames() + ); + + return composedChildTC; +} diff --git a/src/discriminators/index.js b/src/discriminators/index.js new file mode 100644 index 00000000..1591367b --- /dev/null +++ b/src/discriminators/index.js @@ -0,0 +1,4 @@ +/* @flow */ + +export { DiscriminatorTypeComposer, DiscriminatorOptions } from './DiscriminatorTypeComposer'; +export { mergeCustomizationOptions } from './utils/mergeCustomizationOptions'; diff --git a/src/discriminators/prepareBaseResolvers.js b/src/discriminators/prepareBaseResolvers.js new file mode 100644 index 00000000..28253a7e --- /dev/null +++ b/src/discriminators/prepareBaseResolvers.js @@ -0,0 +1,98 @@ +/* @flow */ + +import { graphql } from 'graphql-compose'; +import { DiscriminatorTypeComposer } from './DiscriminatorTypeComposer'; +import { EMCResolvers } from '../resolvers'; + +const { GraphQLList, GraphQLNonNull } = graphql; + +// change type on DKey generated by composeWithMongoose +// set it to created enum TypeComposer for DKey DKeyETC +// only sets on filter and record typeComposers, since they contain our DKey +function setDKeyEnumOnITCArgs(resolver, baseTC: DiscriminatorTypeComposer) { + // setDKeyEnum for filter types, and on record types + if (resolver) { + const argNames = resolver.getArgNames(); + + for (const argName of argNames) { + if (argName === 'filter' || argName === 'record') { + const filterArgTC = resolver.getArgTC(argName); + + if (filterArgTC) { + filterArgTC.extendField(baseTC.getDKey(), { + type: baseTC.getDKeyETC(), + }); + } + } + } + } +} + +// recomposing sets up the DInterface as the return types for +// Also sets up DKey enum as type for DKey field on composers with filter and/or record args +// composeWithMongoose composers +export function prepareBaseResolvers(baseTC: DiscriminatorTypeComposer) { + for (const resolverName in EMCResolvers) { + if (EMCResolvers.hasOwnProperty(resolverName) && baseTC.hasResolver(resolverName)) { + const resolver = baseTC.getResolver(resolverName); + + switch (resolverName) { + case EMCResolvers.findMany: + case EMCResolvers.findByIds: + resolver.setType(new GraphQLList(baseTC.getDInterface().getType())); + break; + + case EMCResolvers.findById: + case EMCResolvers.findOne: + resolver.setType(baseTC.getDInterface()); + break; + + case EMCResolvers.createOne: + case EMCResolvers.updateOne: + case EMCResolvers.updateById: + case EMCResolvers.removeOne: + case EMCResolvers.removeById: + resolver.getTypeComposer().extendField('record', { + type: baseTC.getDInterface(), + }); + break; + + case EMCResolvers.pagination: + resolver.getTypeComposer().extendField('items', { + type: new GraphQLList(baseTC.getDInterface().getType()), + }); + break; + + case EMCResolvers.connection: + const edgesTC = resolver // eslint-disable-line no-case-declarations + .getTypeComposer() + .getFieldTC('edges') + .clone(`${baseTC.getTypeName()}Edge`); + + edgesTC.extendField('node', { + type: new GraphQLNonNull(baseTC.getDInterface().getType()), + }); + + resolver + .getTypeComposer() + .setField( + 'edges', + new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(edgesTC.getType()))) + ); + break; + + default: + } + + setDKeyEnumOnITCArgs(resolver, baseTC); + + // set DKey as required field to create from base + // must be done after setting DKeyEnum + if (resolverName === EMCResolvers.createOne) { + resolver.getArgTC('record').extendField(baseTC.getDKey(), { + type: new GraphQLNonNull(baseTC.getDKeyETC().getType()), + }); + } + } + } +} diff --git a/src/discriminators/prepareChildResolvers.js b/src/discriminators/prepareChildResolvers.js new file mode 100644 index 00000000..992db0e2 --- /dev/null +++ b/src/discriminators/prepareChildResolvers.js @@ -0,0 +1,174 @@ +/* @flow */ + +import type { ResolveParams } from 'graphql-compose'; +import { ResolverClass, TypeComposerClass } from 'graphql-compose'; +import { type DiscriminatorOptions, DiscriminatorTypeComposer } from './DiscriminatorTypeComposer'; +import { EMCResolvers } from '../resolvers'; + +// set the DKey as a query on filter, also project it +// Also look at it like setting for filters, makes sure to limit +// query to child type +function setQueryDKey( + resolver: ResolverClass, + childTC: TypeComposerClass, + DKey: string, + fromField: string +) { + if (resolver) { + resolver.wrapResolve(next => (resolve: ResolveParams) => { + const DName = childTC.getTypeName(); + + /* eslint no-param-reassign: 0 */ + resolve.args = resolve.args ? resolve.args : {}; + resolve.projection = resolve.projection ? resolve.projection : {}; + + if (fromField) { + resolve.args[fromField] = resolve.args[fromField] ? resolve.args[fromField] : {}; + resolve.args[fromField][DKey] = DName; + } else { + resolve.args[DKey] = DName; + } + + resolve.projection[DKey] = 1; + /* eslint no-param-reassign: 1 */ + + return next(resolve); + }); + } +} + +// hide the DKey on the filter or record +function hideDKey( + resolver: ResolverClass, + childTC: TypeComposerClass, + DKey: string, + fromField: string[] | string +) { + if (Array.isArray(fromField)) { + for (const field of fromField) { + hideDKey(resolver, childTC, DKey, field); + } + } else if (fromField && resolver.hasArg(fromField)) { + const fieldTC = resolver.getArgTC(fromField); + + if (fieldTC) { + fieldTC.removeField(DKey); + } + } else { + resolver.removeArg(DKey); + } +} + +// makes sure that all input fields are same as that on Interface, +// that is all should be same as base typeComposer types +// only changes for common properties, executed only once, on discriminator creation +function setBaseInputTypesOnChildInputTypes( + resolver: ResolverClass, + baseDTC: DiscriminatorTypeComposer, + fromField: string[] | string +) { + // set sharedField types on input types + if (resolver && baseDTC.hasInputTypeComposer()) { + if (Array.isArray(fromField)) { + for (const field of fromField) { + setBaseInputTypesOnChildInputTypes(resolver, baseDTC, field); + } + } else if (fromField && resolver.hasArg(fromField)) { + const argTc = resolver.getArgTC(fromField); + + const baseITCFields = baseDTC.getInputTypeComposer().getFieldNames(); + + for (const baseField of baseITCFields) { + if (argTc.hasField(baseField) && baseField !== '_id') { + argTc.extendField(baseField, { + type: baseDTC.getInputTypeComposer().getFieldType(baseField), + }); + } + } + } + } +} + +// reorder input fields resolvers, based on reorderFields opts +function reorderFieldsRecordFilter( + resolver: ResolverClass, + baseDTC: DiscriminatorTypeComposer, + order: string[] | boolean | void | null, + fromField: string[] | string +) { + if (order) { + if (Array.isArray(fromField)) { + for (const field of fromField) { + reorderFieldsRecordFilter(resolver, baseDTC, order, field); + } + } else if (fromField && resolver.hasArg(fromField)) { + const argTC = resolver.getArgTC(fromField); + + if (Array.isArray(order)) { + argTC.reorderFields(order); + } else { + const newOrder = []; + + // is CDTC + if (baseDTC.hasInputTypeComposer()) { + newOrder.push(...baseDTC.getInputTypeComposer().getFieldNames()); + + newOrder.filter(value => value === '_id' || value === baseDTC.getDKey()); + + newOrder.unshift('_id', baseDTC.getDKey()); + } + + argTC.reorderFields(newOrder); + } + } + } +} + +export function prepareChildResolvers( + baseDTC: DiscriminatorTypeComposer, + childTC: TypeComposerClass, + opts: DiscriminatorOptions +) { + for (const resolverName in EMCResolvers) { + if (EMCResolvers.hasOwnProperty(resolverName) && childTC.hasResolver(resolverName)) { + const resolver = childTC.getResolver(resolverName); + + switch (resolverName) { + case EMCResolvers.createOne: + setQueryDKey(resolver, childTC, baseDTC.getDKey(), 'record'); + + hideDKey(resolver, childTC, baseDTC.getDKey(), 'record'); + break; + + case EMCResolvers.updateById: + hideDKey(resolver, childTC, baseDTC.getDKey(), 'record'); + break; + + case EMCResolvers.updateOne: + case EMCResolvers.updateMany: + setQueryDKey(resolver, childTC, baseDTC.getDKey(), 'filter'); + + hideDKey(resolver, childTC, baseDTC.getDKey(), ['record', 'filter']); + break; + + case EMCResolvers.findOne: + case EMCResolvers.findMany: + case EMCResolvers.removeOne: + case EMCResolvers.removeMany: + case EMCResolvers.count: + case EMCResolvers.pagination: + case EMCResolvers.connection: + // limit remove scope to DKey + setQueryDKey(resolver, childTC, baseDTC.getDKey(), 'filter'); + + // remove DKey Field, remove from filter + hideDKey(resolver, childTC, baseDTC.getDKey(), 'filter'); + break; + default: + } + + setBaseInputTypesOnChildInputTypes(resolver, baseDTC, ['filter', 'record']); + reorderFieldsRecordFilter(resolver, baseDTC, opts.reorderFields, ['filter', 'record']); + } + } +} diff --git a/src/discriminators/utils/__test__/mergeCustomizationOptions-test.js b/src/discriminators/utils/__test__/mergeCustomizationOptions-test.js new file mode 100644 index 00000000..8d2dbc8b --- /dev/null +++ b/src/discriminators/utils/__test__/mergeCustomizationOptions-test.js @@ -0,0 +1,236 @@ +/* @flow */ + +import { SchemaComposer } from 'graphql-compose'; +import type { TypeConverterOpts } from '../../../composeWithMongoose'; +import { + mergeCustomizationOptions, + mergeFieldMaps, + mergeStringAndStringArraysFields, +} from '../mergeCustomizationOptions'; + +const baseFields = { + remove: ['id', 'friends', 'health', 'appearsIn'], + only: ['id'], +}; + +const childFields = { + remove: ['id', 'appearsIn', 'dob', 'health'], + only: ['id'], +}; + +const expectedFields = { + remove: ['id', 'friends', 'health', 'appearsIn', 'dob'], + only: ['id'], +}; + +const baseInputTypeFields = { + only: ['id', 'appearsIn'], + remove: ['dob', 'gender'], +}; + +const childInputTypeFields = { + only: ['id', 'friends', 'appearsIn'], + remove: ['dob'], + required: ['id', 'dob', 'gender'], +}; + +const expectedInputTypes = { + only: ['id', 'appearsIn', 'friends'], + remove: ['dob', 'gender'], + required: ['id', 'dob', 'gender'], +}; + +const optsTypes = ['string', 'string[]']; + +describe('mergeStringAndStringArraysFields()', () => { + it('should concat two Arrays', () => { + expect( + mergeStringAndStringArraysFields(baseInputTypeFields.remove, childFields.only, optsTypes[0]) + ).toEqual([...baseInputTypeFields.remove, ...childFields.only]); + }); + + it('should combine two input strings into an array', () => { + expect( + mergeStringAndStringArraysFields( + baseInputTypeFields.remove[0], + baseInputTypeFields.remove[1], + optsTypes[1] + ) + ).toEqual(baseInputTypeFields.remove); + }); + + it('should combine an array and a string into an array', () => { + expect( + mergeStringAndStringArraysFields( + childInputTypeFields.required[0], + baseInputTypeFields.remove, + optsTypes[0] + ) + ).toEqual(childInputTypeFields.required); + + expect( + mergeStringAndStringArraysFields( + baseInputTypeFields.only, + childInputTypeFields.only[1], + optsTypes[0] + ) + ).toEqual(expectedInputTypes.only); + }); + + it('should remove repeated fields from the array', () => { + expect( + mergeStringAndStringArraysFields(baseFields.remove, childFields.remove, optsTypes[0]) + ).toEqual(expectedFields.remove); + + expect( + mergeStringAndStringArraysFields(undefined, childInputTypeFields.required, optsTypes[0]) + ).toEqual(expectedInputTypes.required); + + expect( + mergeStringAndStringArraysFields( + baseInputTypeFields.only, + childInputTypeFields.only, + optsTypes[0] + ) + ).toEqual(expectedInputTypes.only); + }); + + it('should return an ARRAY of the defined if other one is undefined', () => { + expect(mergeStringAndStringArraysFields(baseFields.remove, undefined, optsTypes[0])).toEqual( + baseFields.remove + ); + + expect(mergeStringAndStringArraysFields(undefined, childFields.only, optsTypes[0])).toEqual( + childFields.only + ); + }); + + it('should operate normally with ARRAY of optsTypes', () => { + expect(mergeStringAndStringArraysFields(baseFields.remove, undefined, optsTypes)).toEqual( + baseFields.remove + ); + + expect(mergeStringAndStringArraysFields(undefined, childFields.only, optsTypes)).toEqual( + childFields.only + ); + }); + + it('should return child field if not amongst opsType', () => { + expect(mergeStringAndStringArraysFields(baseFields.remove, undefined, 'boolean')).toEqual( + undefined + ); + + expect(mergeStringAndStringArraysFields(undefined, childFields.only, 'number')).toEqual( + childFields.only + ); + }); +}); + +describe('mergeFieldMaps()', () => { + it('should merge fields', () => { + expect(mergeFieldMaps(baseFields, childFields)).toEqual(expectedFields); + expect(mergeFieldMaps(baseInputTypeFields, childInputTypeFields)).toEqual(expectedInputTypes); + }); +}); + +describe('mergeCustomizationOptions()', () => { + const baseCustomOptions: TypeConverterOpts = { + fields: baseFields, + inputType: { + name: 'BaseInput', + description: 'Hello Base', + fields: baseInputTypeFields, + }, + resolvers: { + findMany: { + limit: { defaultValue: 20 }, + // sort: false, + skip: false, + filter: { + isRequired: true, + removeFields: ['id', 'dob'], + operators: { + one: ['gt', 'gte', 'lt'], + two: ['gt', 'gte', 'lt', 'in[]', 'nin[]'], + }, + }, + }, + findById: false, + }, + }; + + const childCustomOptions: TypeConverterOpts = { + fields: childFields, + inputType: { + name: 'ChildInputs', + description: 'Hello Child', + fields: childInputTypeFields, + }, + resolvers: { + findMany: { + limit: { defaultValue: 50 }, + sort: false, + // skip: false, + filter: { + removeFields: ['gender', 'dob', 'age'], + operators: { + one: ['gt', 'lte', 'ne', 'in[]', 'nin[]'], + two: ['gt', 'gte', 'lt', 'lte', 'ne'], + three: ['gte', 'lt'], + }, + }, + }, + updateById: { + input: { + removeFields: ['one', 'two', 'five'], + requiredFields: ['eight', 'two', 'five'], + }, + }, + }, + }; + + const expected: TypeConverterOpts = { + fields: expectedFields, + inputType: { + name: 'ChildInputs', + description: 'Hello Child', + fields: expectedInputTypes, + }, + resolvers: { + findMany: { + limit: { defaultValue: 50 }, + sort: false, + skip: false, + filter: { + isRequired: true, + removeFields: ['id', 'dob', 'gender', 'age'], + operators: { + one: ['gt', 'gte', 'lt', 'lte', 'ne', 'in[]', 'nin[]'], + two: ['gt', 'gte', 'lt', 'in[]', 'nin[]', 'lte', 'ne'], + three: ['gte', 'lt'], + }, + }, + }, + findById: false, + updateById: { + input: { + removeFields: ['one', 'two', 'five'], + requiredFields: ['eight', 'two', 'five'], + }, + }, + }, + }; + + it('should merge customisation Options', () => { + expect(mergeCustomizationOptions(baseCustomOptions, childCustomOptions)).toEqual(expected); + }); + + it('should produce error if using different schema composers', () => { + expect(() => { + mergeCustomizationOptions( + { schemaComposer: new SchemaComposer() }, + { schemaComposer: new SchemaComposer() } + ); + }).toThrow('[Discriminators] ChildModels should have same schemaComposer as its BaseModel'); + }); +}); diff --git a/src/discriminators/utils/__test__/mergeTypeConverterResolverOpts-test.js b/src/discriminators/utils/__test__/mergeTypeConverterResolverOpts-test.js new file mode 100644 index 00000000..a955a608 --- /dev/null +++ b/src/discriminators/utils/__test__/mergeTypeConverterResolverOpts-test.js @@ -0,0 +1,131 @@ +/* @flow */ + +import type { TypeConverterResolversOpts } from '../../../composeWithMongoose'; +import { + mergeTypeConverterResolverOpts, + mergePrimitiveTypeFields, + mergeMapTypeFields, + mergeFilterOperatorsOptsMap, +} from '../mergeTypeConverterResolversOpts'; + +const baseConverterResolverOpts: TypeConverterResolversOpts = { + findMany: { + limit: { defaultValue: 20 }, + // sort: false, + skip: false, + filter: { + isRequired: true, + removeFields: ['id', 'dob'], + operators: { + one: ['gt', 'gte', 'lt'], + two: ['gt', 'gte', 'lt', 'in[]', 'nin[]'], + }, + }, + }, + findById: false, +}; + +const childConverterResolverOpts: TypeConverterResolversOpts = { + findMany: { + limit: { defaultValue: 50 }, + sort: false, + // skip: false, + filter: { + removeFields: ['gender', 'dob', 'age'], + operators: { + one: ['gt', 'lte', 'ne', 'in[]', 'nin[]'], + two: ['gt', 'gte', 'lt', 'lte', 'ne'], + three: ['gte', 'lt'], + }, + }, + }, +}; + +const expectedConverterResolverOpts: TypeConverterResolversOpts = { + findMany: { + limit: { defaultValue: 50 }, + sort: false, + skip: false, + filter: { + isRequired: true, + removeFields: ['id', 'dob', 'gender', 'age'], + operators: { + one: ['gt', 'gte', 'lt', 'lte', 'ne', 'in[]', 'nin[]'], + two: ['gt', 'gte', 'lt', 'in[]', 'nin[]', 'lte', 'ne'], + three: ['gte', 'lt'], + }, + }, + }, + findById: false, +}; + +describe('mergeTypeConverterResolverOpts()', () => { + it('should merge TypeConverterResolverOpts', () => { + expect( + mergeTypeConverterResolverOpts(baseConverterResolverOpts, childConverterResolverOpts) + ).toEqual(expectedConverterResolverOpts); + }); + + describe('mergeFilterOperatorsOptsMap()', () => { + it('should merge FilterOperatorsOptsMap', () => { + expect( + mergeFilterOperatorsOptsMap( + ((baseConverterResolverOpts.findMany: any).filter.operators: any), + ((childConverterResolverOpts.findMany: any).filter.operators: any) + ) + ).toEqual((expectedConverterResolverOpts.findMany: any).filter.operators); + }); + }); + + describe('mergePrimitiveTypeFields()', () => { + const opsTypes = ['string', 'boolean']; + + it('should merge [base false] and [child undefined] to [false]', () => { + expect(mergePrimitiveTypeFields(false, undefined, opsTypes[1])).toEqual(false); + }); + + it('should merge [base true] and [child undefined] to [true]', () => { + expect(mergePrimitiveTypeFields(true, undefined, opsTypes[1])).toEqual(true); + }); + + it('should merge [base undefined] and [child true] to [true]', () => { + expect(mergePrimitiveTypeFields(undefined, true, opsTypes[1])).toEqual(true); + }); + + it('should merge [base undefined] and [child false] to [false]', () => { + expect(mergePrimitiveTypeFields(undefined, false, opsTypes[1])).toEqual(false); + }); + + it('should merge [base undefined] and [child undefined] to [undefined]', () => { + expect(mergePrimitiveTypeFields(undefined, undefined, opsTypes[1])).toEqual(undefined); + }); + + it('should merge with correct results if opsTypes is an array containing "boolean"', () => { + expect(mergePrimitiveTypeFields(false, undefined, opsTypes)).toEqual(false); + expect(mergePrimitiveTypeFields(undefined, false, opsTypes)).toEqual(false); + expect(mergePrimitiveTypeFields(undefined, undefined, opsTypes)).toEqual(undefined); + }); + + it('should return input child field if optsTypes does not have "boolean"', () => { + expect(mergePrimitiveTypeFields(undefined, undefined, opsTypes[0])).toEqual(undefined); + expect(mergePrimitiveTypeFields(undefined, false, opsTypes[0])).toEqual(false); + expect(mergePrimitiveTypeFields(undefined, true, opsTypes)).toEqual(true); + }); + }); + + describe('mergeMapTypeFields()', () => { + const optsTypes = { + isRequired: 'boolean', + }; + + it('should merge Map type fields', () => { + expect( + mergeMapTypeFields( + (baseConverterResolverOpts.findMany: any).filter, + (childConverterResolverOpts.findMany: any).filter, + optsTypes + ) + ).toEqual((expectedConverterResolverOpts.findMany: any).filter); + }); + }); +}); diff --git a/src/discriminators/utils/mergeCustomizationOptions.js b/src/discriminators/utils/mergeCustomizationOptions.js new file mode 100644 index 00000000..fbad481f --- /dev/null +++ b/src/discriminators/utils/mergeCustomizationOptions.js @@ -0,0 +1,131 @@ +/* @flow */ + +import type { TypeConverterOpts } from '../../composeWithMongoose'; +import { mergeTypeConverterResolverOpts } from './mergeTypeConverterResolversOpts'; + +type FieldMap = { + [fieldName: string]: string[] | typeof undefined, +}; + +export function mergeStringAndStringArraysFields( + baseField?: string[] | string, + childField?: string[] | string, + argOptsTypes: string[] | string +): string[] | typeof undefined { + if (Array.isArray(argOptsTypes)) { + if (argOptsTypes.find(v => v === 'string' || v === 'string[]')) { + return mergeStringAndStringArraysFields(baseField, childField, 'string'); + } + } + + let merged = childField; + + if (argOptsTypes === 'string' || argOptsTypes === 'string[]') { + if (!baseField) { + if (childField) { + return Array.isArray(childField) ? childField : [childField]; + } + return undefined; + } + + if (!childField) { + if (baseField) { + return Array.isArray(baseField) ? baseField : [baseField]; + } + return undefined; + } + + merged = Array.of( + ...(Array.isArray(baseField) ? baseField : [baseField]), + ...(Array.isArray(childField) ? childField : [childField]) + ); + + let length = merged.length; + + for (let i = 0; i <= length; i++) { + for (let j = i + 1; j < length; j++) { + if (merged[i] === merged[j]) { + merged.splice(j, 1); + length--; + } + } + } + } + + return (merged: any); +} + +export function mergeFieldMaps( + baseFieldMap?: FieldMap, + childFieldMap?: FieldMap +): FieldMap | typeof undefined { + if (!baseFieldMap) { + return childFieldMap; + } + + const mergedFieldMap = childFieldMap || {}; + + for (const key in baseFieldMap) { + if (baseFieldMap.hasOwnProperty(key)) { + mergedFieldMap[key] = mergeStringAndStringArraysFields( + baseFieldMap[key], + mergedFieldMap[key], + 'string' + ); + } + } + + return mergedFieldMap; +} + +export function mergeCustomizationOptions( + baseCOptions: TypeConverterOpts, + childCOptions?: TypeConverterOpts +): TypeConverterOpts | void { + if (!baseCOptions) { + return childCOptions; + } + + const mergedOptions: TypeConverterOpts = childCOptions || ({}: any); + + if ( + baseCOptions.schemaComposer !== mergedOptions.schemaComposer && + mergedOptions.schemaComposer + ) { + throw new Error( + '[Discriminators] ChildModels should have same schemaComposer as its BaseModel' + ); + } + + // use base schemaComposer + mergedOptions.schemaComposer = baseCOptions.schemaComposer; + + // merge fields map + if (baseCOptions.fields) { + mergedOptions.fields = (mergeFieldMaps( + (baseCOptions.fields: any), + (mergedOptions.fields: any) + ): any); + } + + // merge inputType fields map + if (baseCOptions.inputType && baseCOptions.inputType.fields) { + if (mergedOptions.inputType) { + (mergedOptions.inputType: any).fields = (mergeFieldMaps( + (baseCOptions.inputType.fields: any), + (mergedOptions.inputType.fields: any) + ): any); + } else { + mergedOptions.inputType = { + fields: (mergeFieldMaps((baseCOptions.inputType.fields: any), undefined): any), + }; + } + } + + mergedOptions.resolvers = mergeTypeConverterResolverOpts( + baseCOptions.resolvers, + mergedOptions.resolvers + ); + + return mergedOptions; +} diff --git a/src/discriminators/utils/mergeTypeConverterResolversOpts.js b/src/discriminators/utils/mergeTypeConverterResolversOpts.js new file mode 100644 index 00000000..a952b308 --- /dev/null +++ b/src/discriminators/utils/mergeTypeConverterResolversOpts.js @@ -0,0 +1,193 @@ +/* @flow */ + +import type { TypeConverterResolversOpts } from '../../composeWithMongoose'; +import { MergeAbleHelperArgsOpts } from '../../resolvers/helpers'; +import { mergeStringAndStringArraysFields } from './mergeCustomizationOptions'; + +type TypeFieldMap = { + [fieldName: string]: any, +}; + +export function mergePrimitiveTypeFields( + baseField?: any, + childField?: any, + argOptsTypes: string[] | string +) { + if (Array.isArray(argOptsTypes)) { + if (argOptsTypes.find(v => v === 'boolean' || v === 'number')) { + return mergePrimitiveTypeFields(baseField, childField, 'boolean'); + } + } + + if (argOptsTypes === 'boolean' || argOptsTypes === 'number') { + if (childField === undefined) { + return baseField; + } else { + return childField; + } + } + + return childField; +} + +export function mergeFilterOperatorsOptsMap( + baseFilterOperatorField: TypeFieldMap, + childFilterOperatorField?: TypeFieldMap +) { + const baseOptsKeys = Object.keys(baseFilterOperatorField); + const baseOptsTypes = {}; + for (const key of baseOptsKeys) { + baseOptsTypes[key] = 'string[]'; + } + + /* eslint-disable */ + childFilterOperatorField = mergeMapTypeFields( + baseFilterOperatorField, + childFilterOperatorField, + baseOptsTypes + ); + /* eslint-enable */ + + return childFilterOperatorField; +} + +export function mergeArraysTypeFields(baseField: any, childField: any, argOptsType: TypeFieldMap) { + let merged = childField !== undefined ? childField : {}; + if (Array.isArray(argOptsType)) { + for (const argType of argOptsType) { + if (argType === 'FilterOperatorsOptsMap') { + merged = mergeFilterOperatorsOptsMap(baseField, (merged: any)); + + continue; // eslint-disable-line no-continue + } + + merged = mergePrimitiveTypeFields(baseField, childField, argType); + + merged = mergeStringAndStringArraysFields(baseField, merged, argType); + } + } + + return merged; +} + +export function mergeMapTypeFields(baseField: any, childField: any, argOptsTypes: TypeFieldMap) { + const merged = childField === undefined ? {} : childField; + + if (argOptsTypes !== null && typeof argOptsTypes === 'object') { + for (const argOptType in argOptsTypes) { + if (argOptsTypes.hasOwnProperty(argOptType)) { + if (baseField[argOptType] === undefined) { + continue; // eslint-disable-line no-continue + } + + if (childField === undefined) { + childField = {}; // eslint-disable-line no-param-reassign + } + + if (argOptType === 'FilterOperatorsOptsMap') { + merged[argOptType] = mergeFilterOperatorsOptsMap( + baseField[argOptType], + merged[argOptType] + ); + continue; // eslint-disable-line no-continue + } + + merged[argOptType] = mergePrimitiveTypeFields( + baseField[argOptType], + childField[argOptType], + argOptsTypes[argOptType] + ); + + merged[argOptType] = mergeStringAndStringArraysFields( + baseField[argOptType], + merged[argOptType], + argOptsTypes[argOptType] + ); + + merged[argOptType] = mergeArraysTypeFields( + baseField[argOptType], + merged[argOptType], + argOptsTypes[argOptType] + ); + } + } + } + + return merged; +} + +export function mergeTypeConverterResolverOpts( + baseTypeConverterResolverOpts?: TypeConverterResolversOpts | false, + childTypeConverterResolverOpts?: TypeConverterResolversOpts | false +): TypeConverterResolversOpts | false | void { + if (!baseTypeConverterResolverOpts) { + return childTypeConverterResolverOpts; + } + + if (!childTypeConverterResolverOpts) { + return baseTypeConverterResolverOpts; + } + + const mergedTypeConverterResolverOpts = + JSON.parse(JSON.stringify(childTypeConverterResolverOpts)) || {}; + + for (const baseResolverOpt in baseTypeConverterResolverOpts) { + if (baseTypeConverterResolverOpts.hasOwnProperty(baseResolverOpt)) { + // e.g. baseResolverArgs = [ limit, filter ] + const baseResolverArgs = baseTypeConverterResolverOpts[baseResolverOpt]; + let childResolverArgs = childTypeConverterResolverOpts[baseResolverOpt]; + + // e.g. { findMany: ... findById: ... } baseResolverOpt = findById + if (baseResolverArgs === undefined) { + continue; // eslint-disable-line no-continue + } + + // if nothing set for child resolver set base + if (baseResolverArgs === false && childResolverArgs === undefined) { + mergedTypeConverterResolverOpts[baseResolverOpt] = false; + continue; // eslint-disable-line no-continue + } + + // set to empty object in-order to reference + if (childResolverArgs === undefined) { + childResolverArgs = {}; + } + + // create path on merged if not available + const mergedResolverArgs = mergedTypeConverterResolverOpts[baseResolverOpt] || {}; + + // e.g. { limit: ..., filter: ... } + for (const baseResolverArg in baseResolverArgs) { + if (baseResolverArgs.hasOwnProperty(baseResolverArg)) { + const argOptsType = MergeAbleHelperArgsOpts[baseResolverArg]; + + // e.g. {limit: ...} baseResolverArg = limit + if (baseResolverArgs[baseResolverArg] === undefined) { + continue; // eslint-disable-line no-continue + } + + mergedResolverArgs[baseResolverArg] = mergePrimitiveTypeFields( + baseResolverArgs[baseResolverArg], + childResolverArgs[baseResolverArg], + argOptsType + ); + + mergedResolverArgs[baseResolverArg] = mergeMapTypeFields( + baseResolverArgs[baseResolverArg], + mergedResolverArgs[baseResolverArg], + argOptsType + ); + + mergedResolverArgs[baseResolverArg] = mergeArraysTypeFields( + baseResolverArgs[baseResolverArg], + mergedResolverArgs[baseResolverArg], + argOptsType + ); + } + } + mergedTypeConverterResolverOpts[baseResolverOpt] = mergedResolverArgs; + } + } + + return mergedTypeConverterResolverOpts; +} diff --git a/src/discriminators/utils/reorderFields.js b/src/discriminators/utils/reorderFields.js new file mode 100644 index 00000000..789d8c0e --- /dev/null +++ b/src/discriminators/utils/reorderFields.js @@ -0,0 +1,37 @@ +/* @flow */ + +import { TypeComposerClass } from 'graphql-compose'; +import { DiscriminatorTypeComposer } from '../DiscriminatorTypeComposer'; + +export function reorderFields( + modelTC: DiscriminatorTypeComposer | TypeComposerClass, + order: string[] | boolean, + DKey: string, + commonFieldKeys?: string[] +) { + if (order) { + if (Array.isArray(order)) { + modelTC.reorderFields(order); + } else { + const newOrder = []; + + // is child discriminator + if (modelTC instanceof TypeComposerClass && commonFieldKeys) { + newOrder.push(...commonFieldKeys); + + newOrder.filter(value => value === '_id' || value === DKey); + + newOrder.unshift('_id', DKey); + } else { + if (modelTC.getField('_id')) { + newOrder.push('_id'); + } + newOrder.push(DKey); + } + + modelTC.reorderFields(newOrder); + } + } + + return modelTC; +} diff --git a/src/index.js b/src/index.js index bd893cea..d08bc755 100644 --- a/src/index.js +++ b/src/index.js @@ -1,9 +1,11 @@ /* @flow */ import { composeWithMongoose } from './composeWithMongoose'; +import { composeWithMongooseDiscriminators } from './composeWithMongooseDiscriminators'; import GraphQLMongoID from './types/mongoid'; export default composeWithMongoose; export * from './fieldsConverter'; -export { composeWithMongoose, GraphQLMongoID }; +export * from './discriminators'; +export { composeWithMongoose, composeWithMongooseDiscriminators, GraphQLMongoID }; diff --git a/src/resolvers/__tests__/connection-test.js b/src/resolvers/__tests__/connection-test.js new file mode 100644 index 00000000..5e8c4a23 --- /dev/null +++ b/src/resolvers/__tests__/connection-test.js @@ -0,0 +1,221 @@ +/* @flow */ + +import { Resolver, schemaComposer } from 'graphql-compose'; +import { UserModel } from '../../__mocks__/userModel'; +import connection, { prepareCursorQuery } from '../connection'; +import findMany from '../findMany'; +import count from '../count'; +import { convertModelToGraphQL } from '../../fieldsConverter'; + +beforeAll(() => UserModel.base.connect()); +afterAll(() => UserModel.base.disconnect()); + +describe('connection() resolver', () => { + describe('prepareCursorQuery()', () => { + let rawQuery; + + describe('single index', () => { + const cursorData = { a: 10 }; + const indexKeys = Object.keys(cursorData); + + it('asc order', () => { + const indexData = { a: 1 }; + + // for beforeCursorQuery + rawQuery = {}; + prepareCursorQuery(rawQuery, cursorData, indexKeys, indexData, '$lt', '$gt'); + expect(rawQuery).toEqual({ a: { $lt: 10 } }); + + // for afterCursorQuery + rawQuery = {}; + prepareCursorQuery(rawQuery, cursorData, indexKeys, indexData, '$gt', '$lt'); + expect(rawQuery).toEqual({ a: { $gt: 10 } }); + }); + + it('desc order', () => { + const indexData = { a: -1 }; + + // for beforeCursorQuery + rawQuery = {}; + prepareCursorQuery(rawQuery, cursorData, indexKeys, indexData, '$lt', '$gt'); + expect(rawQuery).toEqual({ a: { $gt: 10 } }); + + // for afterCursorQuery + rawQuery = {}; + prepareCursorQuery(rawQuery, cursorData, indexKeys, indexData, '$gt', '$lt'); + expect(rawQuery).toEqual({ a: { $lt: 10 } }); + }); + }); + + describe('compound index', () => { + const cursorData = { a: 10, b: 100, c: 1000 }; + const indexKeys = Object.keys(cursorData); + + it('asc order', () => { + const indexData = { a: 1, b: -1, c: 1 }; + + // for beforeCursorQuery + rawQuery = {}; + prepareCursorQuery(rawQuery, cursorData, indexKeys, indexData, '$lt', '$gt'); + expect(rawQuery).toEqual({ + $or: [ + { a: 10, b: 100, c: { $lt: 1000 } }, + { a: 10, b: { $gt: 100 } }, + { a: { $lt: 10 } }, + ], + }); + + // for afterCursorQuery + rawQuery = {}; + prepareCursorQuery(rawQuery, cursorData, indexKeys, indexData, '$gt', '$lt'); + expect(rawQuery).toEqual({ + $or: [ + { a: 10, b: 100, c: { $gt: 1000 } }, + { a: 10, b: { $lt: 100 } }, + { a: { $gt: 10 } }, + ], + }); + }); + + it('desc order', () => { + const indexData = { a: -1, b: 1, c: -1 }; + + // for beforeCursorQuery + rawQuery = {}; + prepareCursorQuery(rawQuery, cursorData, indexKeys, indexData, '$lt', '$gt'); + expect(rawQuery).toEqual({ + $or: [ + { a: 10, b: 100, c: { $gt: 1000 } }, + { a: 10, b: { $lt: 100 } }, + { a: { $gt: 10 } }, + ], + }); + + // for afterCursorQuery + rawQuery = {}; + prepareCursorQuery(rawQuery, cursorData, indexKeys, indexData, '$gt', '$lt'); + expect(rawQuery).toEqual({ + $or: [ + { a: 10, b: 100, c: { $lt: 1000 } }, + { a: 10, b: { $gt: 100 } }, + { a: { $lt: 10 } }, + ], + }); + }); + }); + }); + + describe('connection() -> ', () => { + let UserTC; + + beforeEach(() => { + schemaComposer.clear(); + UserTC = convertModelToGraphQL(UserModel, 'User', schemaComposer); + UserTC.setResolver('findMany', findMany(UserModel, UserTC)); + UserTC.setResolver('count', count(UserModel, UserTC)); + }); + + let user1; + let user2; + + beforeEach(async () => { + await UserModel.remove({}); + + user1 = new UserModel({ + name: 'userName1', + skills: ['js', 'ruby', 'php', 'python'], + gender: 'male', + relocation: true, + }); + + user2 = new UserModel({ + name: 'userName2', + skills: ['go', 'erlang'], + gender: 'female', + relocation: false, + }); + + await Promise.all([user1.save(), user2.save()]); + }); + + it('should return Resolver object', () => { + const resolver = connection(UserModel, UserTC); + expect(resolver).toBeInstanceOf(Resolver); + }); + + it('Resolver object should have `filter` arg', () => { + const resolver = connection(UserModel, UserTC); + if (!resolver) throw new Error('Connection resolveris undefined'); + expect(resolver.hasArg('filter')).toBe(true); + }); + + it('Resolver object should have `sort` arg', () => { + const resolver = connection(UserModel, UserTC); + if (!resolver) throw new Error('Connection resolveris undefined'); + expect(resolver.hasArg('sort')).toBe(true); + }); + + it('Resolver object should have `connection args', () => { + const resolver = connection(UserModel, UserTC); + if (!resolver) throw new Error('Connection resolveris undefined'); + expect(resolver.hasArg('first')).toBe(true); + expect(resolver.hasArg('last')).toBe(true); + expect(resolver.hasArg('before')).toBe(true); + expect(resolver.hasArg('after')).toBe(true); + }); + + describe('Resolver.resolve():Promise', () => { + it('should be fulfilled Promise', async () => { + const resolver = connection(UserModel, UserTC); + if (!resolver) throw new Error('Connection resolveris undefined'); + const result = resolver.resolve({ args: { first: 20 } }); + await expect(result).resolves.toBeDefined(); + }); + + it('should return array of documents in `edges`', async () => { + const resolver = connection(UserModel, UserTC); + if (!resolver) throw new Error('Connection resolveris undefined'); + const result = await resolver.resolve({ args: { first: 20 } }); + + expect(result.edges).toBeInstanceOf(Array); + expect(result.edges).toHaveLength(2); + expect(result.edges.map(d => d.node.name)).toEqual( + expect.arrayContaining([user1.name, user2.name]) + ); + }); + + it('should limit records', async () => { + const resolver = connection(UserModel, UserTC); + if (!resolver) throw new Error('Connection resolveris undefined'); + const result = await resolver.resolve({ args: { first: 1 } }); + + expect(result.edges).toBeInstanceOf(Array); + expect(result.edges).toHaveLength(1); + }); + + it('should sort records', async () => { + const resolver = connection(UserModel, UserTC); + if (!resolver) throw new Error('Connection resolveris undefined'); + + const result1 = await resolver.resolve({ + args: { sort: { _id: 1 }, first: 1 }, + }); + + const result2 = await resolver.resolve({ + args: { sort: { _id: -1 }, first: 1 }, + }); + + expect(`${result1.edges[0].node._id}`).not.toBe(`${result2.edges[0].node._id}`); + }); + + it('should return mongoose documents', async () => { + const resolver = connection(UserModel, UserTC); + if (!resolver) throw new Error('Connection resolveris undefined'); + + const result = await resolver.resolve({ args: { first: 20 } }); + expect(result.edges[0].node).toBeInstanceOf(UserModel); + expect(result.edges[1].node).toBeInstanceOf(UserModel); + }); + }); + }); +}); diff --git a/src/resolvers/__tests__/findOne-test.js b/src/resolvers/__tests__/findOne-test.js index d00a206a..6918afa7 100644 --- a/src/resolvers/__tests__/findOne-test.js +++ b/src/resolvers/__tests__/findOne-test.js @@ -10,16 +10,13 @@ beforeAll(() => UserModel.base.connect()); afterAll(() => UserModel.base.disconnect()); let UserTC; - -beforeEach(() => { - schemaComposer.clear(); - UserTC = convertModelToGraphQL(UserModel, 'User', schemaComposer); -}); - let user1; let user2; beforeEach(async () => { + schemaComposer.clear(); + UserTC = convertModelToGraphQL(UserModel, 'User', schemaComposer); + await UserModel.remove({}); user1 = new UserModel({ diff --git a/src/resolvers/__tests__/pagination-test.js b/src/resolvers/__tests__/pagination-test.js new file mode 100644 index 00000000..bd403529 --- /dev/null +++ b/src/resolvers/__tests__/pagination-test.js @@ -0,0 +1,147 @@ +/* @flow */ + +import { Resolver, schemaComposer } from 'graphql-compose'; +import { UserModel } from '../../__mocks__/userModel'; +import pagination from '../pagination'; +import findMany from '../findMany'; +import count from '../count'; +import { convertModelToGraphQL } from '../../fieldsConverter'; + +beforeAll(() => UserModel.base.connect()); +afterAll(() => UserModel.base.disconnect()); + +describe('pagination() ->', () => { + let UserTC; + + beforeEach(() => { + schemaComposer.clear(); + UserTC = convertModelToGraphQL(UserModel, 'User', schemaComposer); + UserTC.setResolver('findMany', findMany(UserModel, UserTC)); + UserTC.setResolver('count', count(UserModel, UserTC)); + }); + + let user1; + let user2; + + beforeEach(async () => { + await UserModel.remove({}); + + user1 = new UserModel({ + name: 'userName1', + skills: ['js', 'ruby', 'php', 'python'], + gender: 'male', + relocation: true, + }); + + user2 = new UserModel({ + name: 'userName2', + skills: ['go', 'erlang'], + gender: 'female', + relocation: false, + }); + + await Promise.all([user1.save(), user2.save()]); + }); + + it('should return Resolver object', () => { + const resolver = pagination(UserModel, UserTC); + expect(resolver).toBeInstanceOf(Resolver); + }); + + it('Resolver object should have `filter` arg', () => { + const resolver = pagination(UserModel, UserTC); + if (!resolver) throw new Error('Pagination resolver is undefined'); + expect(resolver.hasArg('filter')).toBe(true); + }); + + it('Resolver object should have `page` arg', () => { + const resolver = pagination(UserModel, UserTC); + if (!resolver) throw new Error('Pagination resolver is undefined'); + expect(resolver.hasArg('page')).toBe(true); + }); + + it('Resolver object should have `perPage` arg', () => { + const resolver = pagination(UserModel, UserTC); + if (!resolver) throw new Error('Pagination resolver is undefined'); + expect(resolver.hasArg('perPage')).toBe(true); + expect(resolver.getArgConfig('perPage').defaultValue).toBe(20); + }); + + it('Resolver object should have `perPage` arg with custom default value', () => { + const resolver = pagination(UserModel, UserTC, { + perPage: 33, + }); + if (!resolver) throw new Error('Pagination resolver is undefined'); + expect(resolver.hasArg('perPage')).toBe(true); + expect(resolver.getArgConfig('perPage').defaultValue).toBe(33); + }); + + it('Resolver object should have `sort` arg', () => { + const resolver = pagination(UserModel, UserTC); + if (!resolver) throw new Error('Pagination resolver is undefined'); + expect(resolver.hasArg('sort')).toBe(true); + }); + + describe('Resolver.resolve():Promise', () => { + it('should be fulfilled Promise', async () => { + const resolver = pagination(UserModel, UserTC); + if (!resolver) throw new Error('Pagination resolver is undefined'); + const result = resolver.resolve({ args: { page: 1, perPage: 20 } }); + await expect(result).resolves.toBeDefined(); + }); + + it('should return array of documents in `items`', async () => { + const resolver = pagination(UserModel, UserTC); + if (!resolver) throw new Error('Pagination resolver is undefined'); + const result = await resolver.resolve({ args: { page: 1, perPage: 20 } }); + + expect(result.items).toBeInstanceOf(Array); + expect(result.items).toHaveLength(2); + expect(result.items.map(d => d.name)).toEqual( + expect.arrayContaining([user1.name, user2.name]) + ); + }); + + it('should limit records', async () => { + const resolver = pagination(UserModel, UserTC); + if (!resolver) throw new Error('Pagination resolver is undefined'); + const result = await resolver.resolve({ args: { page: 1, perPage: 1 } }); + + expect(result.items).toBeInstanceOf(Array); + expect(result.items).toHaveLength(1); + }); + + it('should skip records', async () => { + const resolver = pagination(UserModel, UserTC); + if (!resolver) throw new Error('Pagination resolver is undefined'); + const result = await resolver.resolve({ args: { page: 999, perPage: 10 } }); + + expect(result.items).toBeInstanceOf(Array); + expect(result.items).toHaveLength(0); + }); + + it('should sort records', async () => { + const resolver = pagination(UserModel, UserTC); + if (!resolver) throw new Error('Pagination resolver is undefined'); + + const result1 = await resolver.resolve({ + args: { sort: { _id: 1 }, page: 1, perPage: 20 }, + }); + + const result2 = await resolver.resolve({ + args: { sort: { _id: -1 }, page: 1, perPage: 20 }, + }); + + expect(`${result1.items[0]._id}`).not.toBe(`${result2.items[0]._id}`); + }); + + it('should return mongoose documents', async () => { + const resolver = pagination(UserModel, UserTC); + if (!resolver) throw new Error('Pagination resolver is undefined'); + + const result = await resolver.resolve({ args: { page: 1, perPage: 20 } }); + expect(result.items[0]).toBeInstanceOf(UserModel); + expect(result.items[1]).toBeInstanceOf(UserModel); + }); + }); +}); diff --git a/src/prepareConnectionResolver.js b/src/resolvers/connection.js similarity index 80% rename from src/prepareConnectionResolver.js rename to src/resolvers/connection.js index 56dd3461..0a679071 100644 --- a/src/prepareConnectionResolver.js +++ b/src/resolvers/connection.js @@ -2,25 +2,33 @@ /* eslint-disable no-use-before-define, no-param-reassign, global-require */ import type { MongooseModel } from 'mongoose'; -import type { ConnectionSortMapOpts } from 'graphql-compose-connection'; -import type { TypeComposer } from 'graphql-compose'; +import type { ConnectionSortMapOpts as _ConnectionSortMapOpts } from 'graphql-compose-connection'; +import type { Resolver, TypeComposer } from 'graphql-compose'; import { getUniqueIndexes, extendByReversedIndexes, type IndexT, -} from './utils/getIndexesFromModel'; +} from '../utils/getIndexesFromModel'; -export function prepareConnectionResolver( +export type ConnectionSortMapOpts = _ConnectionSortMapOpts; + +export default function connection( model: MongooseModel, tc: TypeComposer, - opts: ConnectionSortMapOpts -) { + opts?: ConnectionSortMapOpts +): ?Resolver { try { require.resolve('graphql-compose-connection'); } catch (e) { - return; + return undefined; + } + const prepareConnectionResolver = require('graphql-compose-connection').prepareConnectionResolver; + + if (!prepareConnectionResolver) { + throw new Error( + 'You should update `graphql-compose-connection` package till 3.2.0 version or above' + ); } - const composeWithConnection = require('graphql-compose-connection').default; const uniqueIndexes = extendByReversedIndexes(getUniqueIndexes(model), { reversedFirst: true, @@ -49,7 +57,7 @@ export function prepareConnectionResolver( }; }); - composeWithConnection(tc, { + return prepareConnectionResolver(tc, { findResolverName: 'findMany', countResolverName: 'count', sort: { diff --git a/src/resolvers/helpers/filter.js b/src/resolvers/helpers/filter.js index 298c4166..b37a32c9 100644 --- a/src/resolvers/helpers/filter.js +++ b/src/resolvers/helpers/filter.js @@ -21,6 +21,16 @@ export type FilterHelperArgsOpts = { removeFields?: string | string[], }; +// for merging, discriminators merge-able only +export const getFilterHelperArgOptsMap = () => ({ + // filterTypeName? : 'string' + isRequired: 'boolean', + onlyIndexed: 'boolean', + requiredFields: ['string', 'string[]'], + operators: ['FilterOperatorsOptsMap', 'boolean'], + removeFields: ['string', 'string[]'], +}); + export const filterHelperArgs = ( typeComposer: TypeComposer, model: MongooseModel, diff --git a/src/resolvers/helpers/index.js b/src/resolvers/helpers/index.js index b8c9ac85..6fb4859b 100644 --- a/src/resolvers/helpers/index.js +++ b/src/resolvers/helpers/index.js @@ -1,8 +1,20 @@ /* @flow */ +import { getFilterHelperArgOptsMap } from './filter'; +import { getLimitHelperArgsOptsMap } from './limit'; +import { getRecordHelperArgsOptsMap } from './record'; + export * from './filter'; export * from './limit'; export * from './projection'; export * from './record'; export * from './skip'; export * from './sort'; + +export const MergeAbleHelperArgsOpts = { + sort: 'boolean', + skip: 'boolean', + limit: getLimitHelperArgsOptsMap(), + filter: getFilterHelperArgOptsMap(), + input: getRecordHelperArgsOptsMap(), +}; diff --git a/src/resolvers/helpers/limit.js b/src/resolvers/helpers/limit.js index 2d5dcc67..69c63871 100644 --- a/src/resolvers/helpers/limit.js +++ b/src/resolvers/helpers/limit.js @@ -7,6 +7,9 @@ export type LimitHelperArgsOpts = { defaultValue?: number, }; +// for merging, discriminators merge-able only +export const getLimitHelperArgsOptsMap = () => ({ defaultValue: 'number' }); + export const limitHelperArgs = (opts?: LimitHelperArgsOpts): ComposeFieldConfigArgumentMap => { return { limit: { diff --git a/src/resolvers/helpers/record.js b/src/resolvers/helpers/record.js index ee8e0944..f49180e2 100644 --- a/src/resolvers/helpers/record.js +++ b/src/resolvers/helpers/record.js @@ -9,6 +9,13 @@ export type RecordHelperArgsOpts = { requiredFields?: string[], }; +// for merging, discriminators merge-able only +export const getRecordHelperArgsOptsMap = () => ({ + isRequired: 'boolean', + removeFields: 'string[]', + requiredFields: 'string[]', +}); + export const recordHelperArgs = ( tc: TypeComposer, opts?: RecordHelperArgsOpts diff --git a/src/resolvers/index.js b/src/resolvers/index.js index 68772b32..1487ba8e 100644 --- a/src/resolvers/index.js +++ b/src/resolvers/index.js @@ -19,6 +19,9 @@ import removeMany from './removeMany'; import createOne from './createOne'; import count from './count'; +import pagination from './pagination'; +import connection from './connection'; + import type { FilterHelperArgsOpts, SortHelperArgsOpts, @@ -53,6 +56,8 @@ export { removeMany, createOne, count, + pagination, + connection, }; export function getAvailableNames(): string[] { @@ -69,5 +74,25 @@ export function getAvailableNames(): string[] { 'removeMany', 'createOne', 'count', + 'pagination', // should be defined after `findMany` and `count` resolvers + 'connection', // should be defined after `findMany` and `count` resolvers ]; } + +// Enum MongooseComposeResolvers +export const EMCResolvers = { + findById: 'findById', + findByIds: 'findByIds', + findOne: 'findOne', + findMany: 'findMany', + updateById: 'updateById', + updateOne: 'updateOne', + updateMany: 'updateMany', + removeById: 'removeById', + removeOne: 'removeOne', + removeMany: 'removeMany', + createOne: 'createOne', + count: 'count', + connection: 'connection', + pagination: 'pagination', +}; diff --git a/src/resolvers/pagination.js b/src/resolvers/pagination.js new file mode 100644 index 00000000..8e3ec942 --- /dev/null +++ b/src/resolvers/pagination.js @@ -0,0 +1,36 @@ +/* @flow */ +/* eslint-disable global-require */ + +import type { Resolver, TypeComposer } from 'graphql-compose'; +import type { MongooseModel } from 'mongoose'; + +export type PaginationResolverOpts = { + perPage?: number, +}; + +export default function pagination( + model: MongooseModel, + tc: TypeComposer, + opts?: PaginationResolverOpts +): ?Resolver { + try { + require.resolve('graphql-compose-pagination'); + } catch (e) { + return undefined; + } + const preparePaginationResolver = require('graphql-compose-pagination').preparePaginationResolver; + + if (!preparePaginationResolver) { + throw new Error( + 'You should update `graphql-compose-pagination` package till 3.3.0 version or above' + ); + } + + const resolver = preparePaginationResolver(tc, { + findResolverName: 'findMany', + countResolverName: 'count', + ...opts, + }); + + return resolver; +} diff --git a/yarn.lock b/yarn.lock index 44e86d6a..2cfe2c03 100644 --- a/yarn.lock +++ b/yarn.lock @@ -544,15 +544,15 @@ babel-core@^6.26.3: slash "^1.0.0" source-map "^0.5.7" -babel-eslint@^8.2.5: - version "8.2.5" - resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-8.2.5.tgz#dc2331c259d36782aa189da510c43dedd5adc7a3" +babel-eslint@^8.2.6: + version "8.2.6" + resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-8.2.6.tgz#6270d0c73205628067c0f7ae1693a9e797acefd9" dependencies: "@babel/code-frame" "7.0.0-beta.44" "@babel/traverse" "7.0.0-beta.44" "@babel/types" "7.0.0-beta.44" babylon "7.0.0-beta.44" - eslint-scope "~3.7.1" + eslint-scope "3.7.1" eslint-visitor-keys "^1.0.0" babel-generator@^6.18.0, babel-generator@^6.25.0: @@ -682,9 +682,9 @@ babel-helpers@^6.24.1: babel-runtime "^6.22.0" babel-template "^6.24.1" -babel-jest@^23.2.0: - version "23.2.0" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-23.2.0.tgz#14a9d6a3f4122dfea6069d37085adf26a53a4dba" +babel-jest@^23.4.0: + version "23.4.0" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-23.4.0.tgz#22c34c392e2176f6a4c367992a7fcff69d2e8557" dependencies: babel-plugin-istanbul "^4.1.6" babel-preset-jest "^23.2.0" @@ -718,6 +718,10 @@ babel-plugin-syntax-async-functions@^6.8.0: version "6.13.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz#cad9cad1191b5ad634bf30ae0872391e0647be95" +babel-plugin-syntax-class-properties@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz#d7eb23b79a317f8543962c505b827c7d6cac27de" + babel-plugin-syntax-exponentiation-operator@^6.8.0: version "6.13.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz#9ee7e8337290da95288201a6a57f4170317830de" @@ -742,6 +746,15 @@ babel-plugin-transform-async-to-generator@^6.22.0: babel-plugin-syntax-async-functions "^6.8.0" babel-runtime "^6.22.0" +babel-plugin-transform-class-properties@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz#6a79763ea61d33d36f37b611aa9def81a81b46ac" + dependencies: + babel-helper-function-name "^6.24.1" + babel-plugin-syntax-class-properties "^6.8.0" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-plugin-transform-es2015-arrow-functions@^6.22.0: version "6.22.0" resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz#452692cb711d5f79dc7f85e440ce41b9f244d221" @@ -2004,16 +2017,16 @@ eslint-restricted-globals@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/eslint-restricted-globals/-/eslint-restricted-globals-0.1.1.tgz#35f0d5cbc64c2e3ed62e93b4b1a7af05ba7ed4d7" -eslint-scope@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.0.tgz#50bf3071e9338bcdc43331794a0cb533f0136172" +eslint-scope@3.7.1: + version "3.7.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8" dependencies: esrecurse "^4.1.0" estraverse "^4.1.1" -eslint-scope@~3.7.1: - version "3.7.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8" +eslint-scope@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.0.tgz#50bf3071e9338bcdc43331794a0cb533f0136172" dependencies: esrecurse "^4.1.0" estraverse "^4.1.1" @@ -2168,15 +2181,15 @@ expand-range@^1.8.1: dependencies: fill-range "^2.1.0" -expect@^23.3.0: - version "23.3.0" - resolved "https://registry.yarnpkg.com/expect/-/expect-23.3.0.tgz#ecb051adcbdc40ac4db576c16067f12fdb13cc61" +expect@^23.4.0: + version "23.4.0" + resolved "https://registry.yarnpkg.com/expect/-/expect-23.4.0.tgz#6da4ecc99c1471253e7288338983ad1ebadb60c3" dependencies: ansi-styles "^3.2.0" jest-diff "^23.2.0" jest-get-type "^22.1.0" jest-matcher-utils "^23.2.0" - jest-message-util "^23.3.0" + jest-message-util "^23.4.0" jest-regex-util "^23.3.0" extend-shallow@^2.0.1: @@ -2540,18 +2553,17 @@ git-up@^2.0.0: is-ssh "^1.3.0" parse-url "^1.3.0" -git-url-parse@^8.0.0: - version "8.0.1" - resolved "https://registry.yarnpkg.com/git-url-parse/-/git-url-parse-8.0.1.tgz#eb3fa427e294d214d9abbeb59637cc8646279c00" +git-url-parse@^10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/git-url-parse/-/git-url-parse-10.0.1.tgz#75f153b24ac7297447fc583cf9fac23a5ae687c1" dependencies: git-up "^2.0.0" -git-url-parse@^9.0.0: - version "9.0.0" - resolved "https://registry.yarnpkg.com/git-url-parse/-/git-url-parse-9.0.0.tgz#a82a36acc3544c77ed0984d6488b37fbcfbec24d" +git-url-parse@^8.0.0: + version "8.0.1" + resolved "https://registry.yarnpkg.com/git-url-parse/-/git-url-parse-8.0.1.tgz#eb3fa427e294d214d9abbeb59637cc8646279c00" dependencies: git-up "^2.0.0" - parse-domain "^2.0.0" glob-base@^0.3.0: version "0.3.0" @@ -2631,21 +2643,21 @@ graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.4 version "1.0.1" resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" -graphql-compose-connection@^3.2.0: +graphql-compose-connection@>=3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/graphql-compose-connection/-/graphql-compose-connection-3.2.0.tgz#87bb068564b76bfdf613c48134b1e47ed4a2f232" dependencies: babel-runtime "^6.26.0" -graphql-compose-pagination@^3.3.0: +graphql-compose-pagination@>=3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/graphql-compose-pagination/-/graphql-compose-pagination-3.3.0.tgz#9977bf416985e8c512b3dfba373e6a90f8d316cf" dependencies: babel-runtime "^6.26.0" -graphql-compose@^4.4.1: - version "4.4.1" - resolved "https://registry.yarnpkg.com/graphql-compose/-/graphql-compose-4.4.1.tgz#2f801bb8e70e8601c21a2e52b629a21c6aba35db" +graphql-compose@^4.7.1: + version "4.7.1" + resolved "https://registry.yarnpkg.com/graphql-compose/-/graphql-compose-4.7.1.tgz#b69a08f674ba882ef62f3e7c52351ef8854c8c55" dependencies: babel-runtime "^6.26.0" graphql-type-json "^0.2.1" @@ -2803,9 +2815,9 @@ hosted-git-info@^2.1.4: version "2.5.0" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.5.0.tgz#6d60e34b3abbc8313062c3b798ef8d901a07af3c" -hosted-git-info@^2.6.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.6.0.tgz#23235b29ab230c576aab0d4f13fc046b0b038222" +hosted-git-info@^2.7.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047" html-encoding-sniffer@^1.0.2: version "1.0.2" @@ -3298,15 +3310,15 @@ java-properties@^0.2.9: version "0.2.10" resolved "https://registry.yarnpkg.com/java-properties/-/java-properties-0.2.10.tgz#2551560c25fa1ad94d998218178f233ad9b18f60" -jest-changed-files@^23.2.0: - version "23.2.0" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-23.2.0.tgz#a145a6e4b66d0129fc7c99cee134dc937a643d9c" +jest-changed-files@^23.4.0: + version "23.4.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-23.4.0.tgz#f1b304f98c235af5d9a31ec524262c5e4de3c6ff" dependencies: throat "^4.0.0" -jest-cli@^23.3.0: - version "23.3.0" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-23.3.0.tgz#307e9be7733443b789a8279d694054d051a9e5e2" +jest-cli@^23.4.1: + version "23.4.1" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-23.4.1.tgz#c1ffd33254caee376990aa2abe2963e0de4ca76b" dependencies: ansi-escapes "^3.0.0" chalk "^2.0.1" @@ -3319,22 +3331,22 @@ jest-cli@^23.3.0: istanbul-lib-coverage "^1.2.0" istanbul-lib-instrument "^1.10.1" istanbul-lib-source-maps "^1.2.4" - jest-changed-files "^23.2.0" - jest-config "^23.3.0" - jest-environment-jsdom "^23.3.0" + jest-changed-files "^23.4.0" + jest-config "^23.4.1" + jest-environment-jsdom "^23.4.0" jest-get-type "^22.1.0" - jest-haste-map "^23.2.0" - jest-message-util "^23.3.0" + jest-haste-map "^23.4.1" + jest-message-util "^23.4.0" jest-regex-util "^23.3.0" - jest-resolve-dependencies "^23.3.0" - jest-runner "^23.3.0" - jest-runtime "^23.3.0" - jest-snapshot "^23.3.0" - jest-util "^23.3.0" - jest-validate "^23.3.0" - jest-watcher "^23.2.0" + jest-resolve-dependencies "^23.4.1" + jest-runner "^23.4.1" + jest-runtime "^23.4.1" + jest-snapshot "^23.4.1" + jest-util "^23.4.0" + jest-validate "^23.4.0" + jest-watcher "^23.4.0" jest-worker "^23.2.0" - micromatch "^3.1.10" + micromatch "^2.3.11" node-notifier "^5.2.1" prompts "^0.1.9" realpath-native "^1.0.0" @@ -3345,22 +3357,22 @@ jest-cli@^23.3.0: which "^1.2.12" yargs "^11.0.0" -jest-config@^23.3.0: - version "23.3.0" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-23.3.0.tgz#bb4d53b70f9500fafddf718d226abb53b13b8323" +jest-config@^23.4.1: + version "23.4.1" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-23.4.1.tgz#3172fa21f0507d7f8a088ed1dbe4157057f201e9" dependencies: babel-core "^6.0.0" - babel-jest "^23.2.0" + babel-jest "^23.4.0" chalk "^2.0.1" glob "^7.1.1" - jest-environment-jsdom "^23.3.0" - jest-environment-node "^23.3.0" + jest-environment-jsdom "^23.4.0" + jest-environment-node "^23.4.0" jest-get-type "^22.1.0" - jest-jasmine2 "^23.3.0" + jest-jasmine2 "^23.4.1" jest-regex-util "^23.3.0" - jest-resolve "^23.2.0" - jest-util "^23.3.0" - jest-validate "^23.3.0" + jest-resolve "^23.4.1" + jest-util "^23.4.0" + jest-validate "^23.4.0" pretty-format "^23.2.0" jest-diff@^23.2.0: @@ -3382,58 +3394,58 @@ jest-docblock@^23.2.0: dependencies: detect-newline "^2.1.0" -jest-each@^23.2.0: - version "23.2.0" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-23.2.0.tgz#a400f81c857083f50c4f53399b109f12023fb19d" +jest-each@^23.4.0: + version "23.4.0" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-23.4.0.tgz#2fa9edd89daa1a4edc9ff9bf6062a36b71345143" dependencies: chalk "^2.0.1" pretty-format "^23.2.0" -jest-environment-jsdom@^23.3.0: - version "23.3.0" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-23.3.0.tgz#190457f91c9e615454c4186056065db6ed7a4e2a" +jest-environment-jsdom@^23.4.0: + version "23.4.0" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-23.4.0.tgz#056a7952b3fea513ac62a140a2c368c79d9e6023" dependencies: jest-mock "^23.2.0" - jest-util "^23.3.0" + jest-util "^23.4.0" jsdom "^11.5.1" -jest-environment-node@^23.3.0: - version "23.3.0" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-23.3.0.tgz#1e8df21c847aa5d03b76573f0dc16fcde5034c32" +jest-environment-node@^23.4.0: + version "23.4.0" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-23.4.0.tgz#57e80ed0841dea303167cce8cd79521debafde10" dependencies: jest-mock "^23.2.0" - jest-util "^23.3.0" + jest-util "^23.4.0" jest-get-type@^22.1.0: version "22.4.3" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-22.4.3.tgz#e3a8504d8479342dd4420236b322869f18900ce4" -jest-haste-map@^23.2.0: - version "23.2.0" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-23.2.0.tgz#d10cbac007c695948c8ef1821a2b2ed2d4f2d4d8" +jest-haste-map@^23.4.1: + version "23.4.1" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-23.4.1.tgz#43a174ba7ac079ae1dd74eaf5a5fe78989474dd2" dependencies: fb-watchman "^2.0.0" graceful-fs "^4.1.11" jest-docblock "^23.2.0" jest-serializer "^23.0.1" jest-worker "^23.2.0" - micromatch "^3.1.10" + micromatch "^2.3.11" sane "^2.0.0" -jest-jasmine2@^23.3.0: - version "23.3.0" - resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-23.3.0.tgz#a8706baac23c8a130d5aa8ef5464a9d49096d1b5" +jest-jasmine2@^23.4.1: + version "23.4.1" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-23.4.1.tgz#fa192262430d418e827636e4a98423e5e7ff0fce" dependencies: chalk "^2.0.1" co "^4.6.0" - expect "^23.3.0" + expect "^23.4.0" is-generator-fn "^1.0.0" jest-diff "^23.2.0" - jest-each "^23.2.0" + jest-each "^23.4.0" jest-matcher-utils "^23.2.0" - jest-message-util "^23.3.0" - jest-snapshot "^23.3.0" - jest-util "^23.3.0" + jest-message-util "^23.4.0" + jest-snapshot "^23.4.1" + jest-util "^23.4.0" pretty-format "^23.2.0" jest-leak-detector@^23.2.0: @@ -3450,13 +3462,13 @@ jest-matcher-utils@^23.2.0: jest-get-type "^22.1.0" pretty-format "^23.2.0" -jest-message-util@^23.3.0: - version "23.3.0" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-23.3.0.tgz#bc07b11cec6971fb5dd9de2dfb60ebc22150c160" +jest-message-util@^23.4.0: + version "23.4.0" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-23.4.0.tgz#17610c50942349508d01a3d1e0bda2c079086a9f" dependencies: "@babel/code-frame" "^7.0.0-beta.35" chalk "^2.0.1" - micromatch "^3.1.10" + micromatch "^2.3.11" slash "^1.0.0" stack-utils "^1.0.1" @@ -3468,42 +3480,42 @@ jest-regex-util@^23.3.0: version "23.3.0" resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-23.3.0.tgz#5f86729547c2785c4002ceaa8f849fe8ca471bc5" -jest-resolve-dependencies@^23.3.0: - version "23.3.0" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-23.3.0.tgz#8444d3b0b1288b80864d8801ff50b44a4d695d1d" +jest-resolve-dependencies@^23.4.1: + version "23.4.1" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-23.4.1.tgz#a1d85247e2963f8b3859f6b0ec61b741b359378e" dependencies: jest-regex-util "^23.3.0" - jest-snapshot "^23.3.0" + jest-snapshot "^23.4.1" -jest-resolve@^23.2.0: - version "23.2.0" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-23.2.0.tgz#a0790ad5a3b99002ab4dbfcbf8d9e2d6a69b3d99" +jest-resolve@^23.4.1: + version "23.4.1" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-23.4.1.tgz#7f3c17104732a2c0c940a01256025ed745814982" dependencies: browser-resolve "^1.11.3" chalk "^2.0.1" realpath-native "^1.0.0" -jest-runner@^23.3.0: - version "23.3.0" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-23.3.0.tgz#04c7e458a617501a4875db0d7ffbe0e3cbd43bfb" +jest-runner@^23.4.1: + version "23.4.1" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-23.4.1.tgz#d41fd1459b95d35d6df685f1468c09e617c8c260" dependencies: exit "^0.1.2" graceful-fs "^4.1.11" - jest-config "^23.3.0" + jest-config "^23.4.1" jest-docblock "^23.2.0" - jest-haste-map "^23.2.0" - jest-jasmine2 "^23.3.0" + jest-haste-map "^23.4.1" + jest-jasmine2 "^23.4.1" jest-leak-detector "^23.2.0" - jest-message-util "^23.3.0" - jest-runtime "^23.3.0" - jest-util "^23.3.0" + jest-message-util "^23.4.0" + jest-runtime "^23.4.1" + jest-util "^23.4.0" jest-worker "^23.2.0" source-map-support "^0.5.6" throat "^4.0.0" -jest-runtime@^23.3.0: - version "23.3.0" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-23.3.0.tgz#4865aab4ceff82f9cec6335fd7ae1422cc1de7df" +jest-runtime@^23.4.1: + version "23.4.1" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-23.4.1.tgz#c1822eba5eb19294debe6b25b2760d0a8c532fd1" dependencies: babel-core "^6.0.0" babel-plugin-istanbul "^4.1.6" @@ -3512,15 +3524,15 @@ jest-runtime@^23.3.0: exit "^0.1.2" fast-json-stable-stringify "^2.0.0" graceful-fs "^4.1.11" - jest-config "^23.3.0" - jest-haste-map "^23.2.0" - jest-message-util "^23.3.0" + jest-config "^23.4.1" + jest-haste-map "^23.4.1" + jest-message-util "^23.4.0" jest-regex-util "^23.3.0" - jest-resolve "^23.2.0" - jest-snapshot "^23.3.0" - jest-util "^23.3.0" - jest-validate "^23.3.0" - micromatch "^3.1.10" + jest-resolve "^23.4.1" + jest-snapshot "^23.4.1" + jest-util "^23.4.0" + jest-validate "^23.4.0" + micromatch "^2.3.11" realpath-native "^1.0.0" slash "^1.0.0" strip-bom "3.0.0" @@ -3531,47 +3543,47 @@ jest-serializer@^23.0.1: version "23.0.1" resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-23.0.1.tgz#a3776aeb311e90fe83fab9e533e85102bd164165" -jest-snapshot@^23.3.0: - version "23.3.0" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-23.3.0.tgz#fc4e9f81e45432d10507e27f50bce60f44d81424" +jest-snapshot@^23.4.1: + version "23.4.1" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-23.4.1.tgz#090de9acae927f6a3af3005bda40d912b83e9c96" dependencies: babel-traverse "^6.0.0" babel-types "^6.0.0" chalk "^2.0.1" jest-diff "^23.2.0" jest-matcher-utils "^23.2.0" - jest-message-util "^23.3.0" - jest-resolve "^23.2.0" + jest-message-util "^23.4.0" + jest-resolve "^23.4.1" mkdirp "^0.5.1" natural-compare "^1.4.0" pretty-format "^23.2.0" semver "^5.5.0" -jest-util@^23.3.0: - version "23.3.0" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-23.3.0.tgz#79f35bb0c30100ef611d963ee6b88f8ed873a81d" +jest-util@^23.4.0: + version "23.4.0" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-23.4.0.tgz#4d063cb927baf0a23831ff61bec2cbbf49793561" dependencies: callsites "^2.0.0" chalk "^2.0.1" graceful-fs "^4.1.11" is-ci "^1.0.10" - jest-message-util "^23.3.0" + jest-message-util "^23.4.0" mkdirp "^0.5.1" slash "^1.0.0" source-map "^0.6.0" -jest-validate@^23.3.0: - version "23.3.0" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-23.3.0.tgz#d49bea6aad98c30acd2cbb542434798a0cc13f76" +jest-validate@^23.4.0: + version "23.4.0" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-23.4.0.tgz#d96eede01ef03ac909c009e9c8e455197d48c201" dependencies: chalk "^2.0.1" jest-get-type "^22.1.0" leven "^2.1.0" pretty-format "^23.2.0" -jest-watcher@^23.2.0: - version "23.2.0" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-23.2.0.tgz#678e852896e919e9d9a0eb4b8baf1ae279620ea9" +jest-watcher@^23.4.0: + version "23.4.0" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-23.4.0.tgz#d2e28ce74f8dad6c6afc922b92cabef6ed05c91c" dependencies: ansi-escapes "^3.0.0" chalk "^2.0.1" @@ -3583,12 +3595,12 @@ jest-worker@^23.2.0: dependencies: merge-stream "^1.0.1" -jest@^23.3.0: - version "23.3.0" - resolved "https://registry.yarnpkg.com/jest/-/jest-23.3.0.tgz#1355cd792f38cf20fba4da02dddb7ca14d9484b5" +jest@^23.4.1: + version "23.4.1" + resolved "https://registry.yarnpkg.com/jest/-/jest-23.4.1.tgz#39550c72f3237f63ae1b434d8d122cdf6fa007b6" dependencies: import-local "^1.0.0" - jest-cli "^23.3.0" + jest-cli "^23.4.1" js-tokens@^3.0.0, js-tokens@^3.0.2: version "3.0.2" @@ -3947,7 +3959,7 @@ merge@^1.1.3: version "1.2.0" resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.0.tgz#7531e39d4949c281a66b8c5a6e0265e8b05894da" -micromatch@^2.1.5: +micromatch@^2.1.5, micromatch@^2.3.11: version "2.3.11" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" dependencies: @@ -3965,7 +3977,7 @@ micromatch@^2.1.5: parse-glob "^3.0.4" regex-cache "^0.4.2" -micromatch@^3.1.10, micromatch@^3.1.8: +micromatch@^3.1.8: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" dependencies: @@ -4072,9 +4084,9 @@ mongoose-legacy-pluralize@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz#3ba9f91fa507b5186d399fb40854bff18fb563e4" -mongoose@^5.2.2: - version "5.2.2" - resolved "https://registry.yarnpkg.com/mongoose/-/mongoose-5.2.2.tgz#3310e71e7e271c1134915c2d7fdf815eb2fa7072" +mongoose@^5.2.3: + version "5.2.3" + resolved "https://registry.yarnpkg.com/mongoose/-/mongoose-5.2.3.tgz#667b92c394a6aa81c3c9f2f45f3a8f28cea28e9c" dependencies: async "2.6.1" bson "~1.0.5" @@ -4410,10 +4422,6 @@ p-try@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.0.0.tgz#85080bb87c64688fa47996fe8f7dfbe8211760b1" -parse-domain@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/parse-domain/-/parse-domain-2.0.0.tgz#e9f42f697c30f7c2051dc5c55ff4d8a80da7943c" - parse-github-url@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/parse-github-url/-/parse-github-url-1.0.2.tgz#242d3b65cbcdda14bb50439e3242acf6971db395" @@ -5086,9 +5094,9 @@ seek-bzip@^1.0.5: dependencies: commander "~2.8.1" -semantic-release@^15.6.3: - version "15.6.3" - resolved "https://registry.yarnpkg.com/semantic-release/-/semantic-release-15.6.3.tgz#3e336f421b7f7595c991609d6de3ee7f53918f2a" +semantic-release@^15.7.2: + version "15.7.2" + resolved "https://registry.yarnpkg.com/semantic-release/-/semantic-release-15.7.2.tgz#e79949d6dffd07a80bb3cefa1601df48ed08e8c7" dependencies: "@semantic-release/commit-analyzer" "^5.0.0" "@semantic-release/error" "^2.2.0" @@ -5104,9 +5112,9 @@ semantic-release@^15.6.3: find-versions "^2.0.0" get-stream "^3.0.0" git-log-parser "^1.2.0" - git-url-parse "^9.0.0" + git-url-parse "^10.0.1" hook-std "^1.0.1" - hosted-git-info "^2.6.0" + hosted-git-info "^2.7.1" lodash "^4.17.4" marked "^0.4.0" marked-terminal "^3.0.0"