├── .babelrc ├── .gitignore ├── .npmignore ├── README.md ├── lerna.json ├── package.json ├── packages ├── apollo-fragment-react-codegen │ ├── .npmignore │ ├── README.md │ ├── package.json │ ├── src │ │ ├── config.ts │ │ ├── index.ts │ │ └── visitor.ts │ ├── tsconfig.json │ └── tslint.json ├── apollo-fragment-react │ ├── .npmignore │ ├── example │ │ ├── .gitignore │ │ ├── package.json │ │ ├── public │ │ │ ├── favicon.ico │ │ │ └── index.html │ │ └── src │ │ │ ├── App.js │ │ │ ├── graphql │ │ │ ├── link.js │ │ │ └── schema.js │ │ │ ├── index.css │ │ │ └── index.js │ ├── package.json │ ├── rollup.config.js │ ├── src │ │ ├── index.tsx │ │ └── mocks │ │ │ ├── enzymeAdapter.ts │ │ │ ├── mockLink.ts │ │ │ └── mockSchema.ts │ ├── tsconfig.json │ └── tslint.json ├── apollo-fragment-utils │ ├── .npmignore │ ├── package.json │ ├── rollup.config.js │ ├── src │ │ ├── __tests__ │ │ │ └── index.test.ts │ │ └── index.ts │ ├── tsconfig.json │ └── tslint.json ├── apollo-fragment-vue │ ├── .npmignore │ ├── example │ │ ├── .gitignore │ │ ├── babel.config.js │ │ ├── package.json │ │ ├── public │ │ │ ├── favicon.ico │ │ │ └── index.html │ │ ├── src │ │ │ ├── App.vue │ │ │ ├── assets │ │ │ │ └── logo.png │ │ │ ├── components │ │ │ │ └── HelloWorld.vue │ │ │ ├── graphql │ │ │ │ ├── link.js │ │ │ │ ├── personFragment.js │ │ │ │ └── schema.js │ │ │ └── main.js │ │ └── vue.config.js │ ├── package.json │ ├── rollup.config.js │ ├── src │ │ ├── ApolloFragment.js │ │ └── index.ts │ ├── tsconfig.json │ └── tslint.json └── apollo-link-state-fragment │ ├── .npmignore │ ├── package.json │ ├── rollup.config.js │ ├── src │ ├── __tests__ │ │ └── index.ts │ ├── index.ts │ └── mocks │ │ ├── mockLink.ts │ │ └── mockSchema.ts │ ├── tsconfig.json │ └── tslint.json └── tsconfig.json /.babelrc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhiaiyer91/apollo-fragment/17e2f414fdcbc4d452538fd00f92c5957e3b9765/.babelrc -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | *.pid.lock 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # nyc test coverage 19 | .nyc_output 20 | 21 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 22 | .grunt 23 | 24 | # node-waf configuration 25 | .lock-wscript 26 | 27 | # Compiled binary addons (http://nodejs.org/api/addons.html) 28 | build/Release 29 | 30 | # Dependency directories 31 | node_modules/ 32 | jspm_packages 33 | 34 | # Optional npm cache directory 35 | .npm 36 | 37 | # Optional eslint cache 38 | .eslintcache 39 | 40 | # Optional REPL history 41 | .node_repl_history 42 | 43 | # Output of 'npm pack' 44 | *.tgz 45 | 46 | # Yarn Integrity file 47 | .yarn-integrity 48 | 49 | # lock files 50 | yarn.lock 51 | package-lock.json 52 | 53 | # Compiled 54 | dist 55 | lib 56 | 57 | .idea 58 | .DS_Store 59 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | dist/tests/ 2 | src/ 3 | tests/ 4 | .travis.yml 5 | tsconfig.json 6 | tslint.json 7 | typings.d.ts 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Apollo Fragment 2 | 3 | Apollo Fragment holds libraries aimed at connecting UI components to GraphQL 4 | fragments in the Apollo Cache. 5 | 6 | `apollo-link-state-fragment` exposes a `cacheRedirect` and `withClientState` 7 | configuration for querying fragments from the cache. 8 | 9 | `apollo-fragment-react` exposes an `ApolloFragment` query component that will 10 | connect your component to a fragment in cache and automatically watch all 11 | changes to it. 12 | 13 | `apollo-fragment-vue` exposes an `ApolloFragment` Vue component that will 14 | connect your component to a fragment in cache and automatically watch all 15 | changes to it. 16 | 17 | ## Background 18 | 19 | Read about this library here: https://medium.com/open-graphql/fragment-driven-uis-with-apollo-17d933fa1cbe 20 | 21 | ## React 22 | 23 |

24 | 25 | Npm download 26 | 27 |

28 | 29 | ## Vue 30 | 31 |

32 | 33 | Npm download 34 | 35 |

36 | 37 | ## Link State 38 | 39 |

40 | 41 | Npm download 42 | 43 |

44 | 45 | ## Installation 46 | 47 | It is simple to add to your current apollo client setup: 48 | 49 | ```bash 50 | # installing cache addons and react package 51 | yarn add apollo-link-state-fragment apollo-fragment-react -S 52 | ``` 53 | 54 | or 55 | 56 | ```bash 57 | # installing cache addons and react package 58 | yarn add apollo-link-state-fragment apollo-fragment-vue -S 59 | ``` 60 | 61 | ## Usage 62 | 63 | To get started you will want to add the `fragmentCacheRedirect` to your 64 | `InMemoryCache` cacheRedirect map. 65 | 66 | ```js 67 | import { InMemoryCache } from "apollo-cache-inmemory"; 68 | import { fragmentCacheRedirect } from "apollo-link-state-fragment"; 69 | 70 | const cache = new InMemoryCache({ 71 | cacheRedirects: { 72 | Query: { 73 | ...fragmentCacheRedirect(), 74 | }, 75 | }, 76 | }); 77 | ``` 78 | 79 | Next, import the `fragmentLinkState` and pass in the cache. 80 | 81 | ```js 82 | import { ApolloClient } from "apollo-client"; 83 | import { ApolloLink } from "apollo-link"; 84 | import { InMemoryCache } from "apollo-cache-inmemory"; 85 | import { 86 | fragmentCacheRedirect, 87 | fragmentLinkState, 88 | } from "apollo-link-state-fragment"; 89 | 90 | const cache = new InMemoryCache({ 91 | cacheRedirects: { 92 | Query: { 93 | ...fragmentCacheRedirect(), 94 | }, 95 | }, 96 | }); 97 | 98 | const client = new ApolloClient({ 99 | link: ApolloLink.from([fragmentLinkState(cache), new HttpLink()]), 100 | cache: new InMemoryCache(), 101 | }); 102 | ``` 103 | 104 | Once you have your client setup to make these kind of queries against the cache, 105 | we can now use the View layer integrations: All we have to do is pass the id of 106 | the fragment you're looking for, and the selection set in a named fragment. 107 | 108 | ## React 109 | 110 | ```jsx 111 | import { useApolloFragment } from "apollo-fragment-react"; 112 | 113 | const fragment = ` 114 | fragment fragmentFields on Person { 115 | idea 116 | name 117 | __typename 118 | } 119 | `; 120 | 121 | function App() { 122 | const { data } = useApolloFragment(fragment, "1"); 123 | 124 | return ( 125 |
126 |

127 | This component is "watching" a fragment in the cache, it will render the 128 | persons name once the data enters 129 |

130 |

{data && `Person Name: ${data.name || ""}`}

131 | 132 | 148 |
149 | ); 150 | } 151 | ``` 152 | 153 | ## Vue 154 | 155 | ```html 156 | 181 | 182 | 201 | ``` 202 | 203 | In our examples above, We have used the `ApolloFragment` query component to bind 204 | the current value of the fragment in cache. When a user clicks to load a list of 205 | people, our component subscribed to a user with id "1", will rerender and 206 | display the person's name. 207 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "lerna": "2.1.2", 3 | "packages": ["packages/*"], 4 | "version": "independent", 5 | "hoist": false 6 | } 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "license": "MIT", 4 | "scripts": { 5 | "bootstrap": "yarn install && lerna bootstrap", 6 | "postbootstrap": "npm run build", 7 | "build": "lerna run -- build", 8 | "test": "lerna run -- test", 9 | "prelint": "npm run lint-fix", 10 | "lint": "lerna run -- lint", 11 | "lint-fix": "prettier --trailing-comma all --single-quote --write \"packages/*/{src,tests,test,benchmark}/**/*.{j,t}s*\"", 12 | "lint-staged": "lint-staged", 13 | "filesize": "lerna run -- filesize && bundlesize", 14 | "type-check": "lerna run -- type-check", 15 | "coverage": "lerna run -- coverage", 16 | "coverage:upload": "codecov", 17 | "danger": "danger run --verbose", 18 | "predeploy": "npm run build", 19 | "deploy": "lerna publish -m \"chore: Publish\" --independent" 20 | }, 21 | "bundlesize": [ 22 | { 23 | "name": "apollo-link-state-fragment", 24 | "path": "./packages/apollo-link-state-fragment/lib/bundle.min.js", 25 | "maxSize": "3 Kb" 26 | } 27 | ], 28 | "jest": { 29 | "globals": { 30 | "ts-jest": { 31 | "useBabelrc": false, 32 | "mapCoverage": true 33 | } 34 | }, 35 | "mapCoverage": true, 36 | "transform": { 37 | ".(ts|tsx)": "/node_modules/ts-jest/preprocessor.js" 38 | } 39 | }, 40 | "lint-staged": { 41 | "*.ts*": [ 42 | "prettier --trailing-comma all --single-quote --write", 43 | "git add" 44 | ], 45 | "*.js*": [ 46 | "prettier --trailing-comma all --single-quote --write", 47 | "git add" 48 | ], 49 | "*.json*": [ 50 | "prettier --write", 51 | "git add" 52 | ] 53 | }, 54 | "pre-commit": "lint-staged", 55 | "dependencies": {}, 56 | "devDependencies": { 57 | "@types/enzyme": "^3.1.10", 58 | "@types/jest": "22.1.x", 59 | "@types/react": "^16.3.14", 60 | "@types/zen-observable": "0.5.3", 61 | "bundlesize": "0.15.3", 62 | "codecov": "3.0.0", 63 | "danger": "1.2.0", 64 | "lerna": "2.5.1", 65 | "lint-staged": "5.0.0", 66 | "pre-commit": "1.2.2", 67 | "prettier": "^1.19.1", 68 | "react-scripts": "^1.1.1", 69 | "ts-jest": "21.2.3", 70 | "typescript": "^4.1.2", 71 | "vue-apollo": "^3.0.0-beta.19" 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /packages/apollo-fragment-react-codegen/.npmignore: -------------------------------------------------------------------------------- 1 | dist/tests/ 2 | src/ 3 | tests/ 4 | .travis.yml 5 | tsconfig.json 6 | tslint.json 7 | typings.d.ts 8 | example/ 9 | __tests__/ 10 | -------------------------------------------------------------------------------- /packages/apollo-fragment-react-codegen/README.md: -------------------------------------------------------------------------------- 1 | # Apollo Fragment React Codegen 2 | This package contains a plugin for [GraphQL Code Generator](https://graphql-code-generator.com/) 3 | that allows to generate React hooks with corresponding TypeScript types 4 | based on `useApolloFragment` from `apollo-fragment-react` and GraphQL fragments defined in your app. 5 | 6 | ## Setup 7 | This package **requires** [TypeScript React Apollo](https://graphql-code-generator.com/docs/plugins/typescript-react-apollo) plugin 8 | to be installed and setup to generate fragment documents and types. 9 | 10 | First, install the package: 11 | ```bash 12 | yarn add -D apollo-fragment-react-codegen 13 | ``` 14 | Then register the plugin in GraphQL Code Generator config (`codegen.yml` by default): 15 | ```yml 16 | generates: 17 | path/to/file.ts: 18 | plugins: 19 | - typescript 20 | - typescript-operations 21 | - typescript-react-apollo 22 | - apollo-fragment-react-codegen 23 | config: 24 | withHooks: true 25 | ``` 26 | Now, whenever you run your codegen script, it will also generate React hooks based on existing fragment definitions 27 | which you then can use in your code instead of `useApolloFragment`. 28 | 29 | ## Usage 30 | Suppose you have some existing code to read user name and avatar from Apollo Client cache using `useApolloFragment`: 31 | ```typescript 32 | // UserAvatar.tsx 33 | import gql from 'graphql-tag' 34 | import { useApolloFragment } from 'apollo-fragment-react' 35 | import { User } from 'src/generated.ts' 36 | 37 | const userAvatarAndNameFragment = gql` 38 | fragment userAvatarAndNameFragment on User { 39 | name 40 | avatarUrl 41 | } 42 | ` 43 | 44 | type FragmentData = Pick 45 | 46 | export function UserAvatar(userId: string) { 47 | const { data: userData } = useApolloFragment(userId) 48 | 49 | if (!userData) { 50 | return null 51 | } 52 | 53 | return {`${userData.name} 54 | } 55 | ``` 56 | To leverage GraphQL codegen, let's move the fragment definiton into a `.graphql` file: 57 | ```graphql 58 | # fragments.graphql 59 | 60 | fragment userAvatarAndName on User { 61 | name 62 | avatarUrl 63 | } 64 | ``` 65 | and make sure that we include this file in the codegen configuration: 66 | ```yml 67 | documents: 68 | - "src/**/*.graphql" 69 | # OR 70 | # - "src/**/fragments.graphql" 71 | ``` 72 | Now, when we run the codegen script, the generated file should also include something like this: 73 | ```typescript 74 | export function useUserAvatarAndNameFragment(id: string) { 75 | return useApolloFragment( 76 | UserAvatarAndNameFragmentDoc, 77 | id 78 | ) 79 | } 80 | export type UserAvatarAndNameFragmentHookResult = ReturnType< 81 | typeof useUserAvatarAndNameFragment 82 | > 83 | ``` 84 | Next we can update our `UserAvatar` component: 85 | ```typescript 86 | // UserAvatar.tsx 87 | import { useUserAvatarAndNameFragment } from 'src/generated.ts' 88 | 89 | export function UserAvatar(userId: string) { 90 | const { data: userData } = useUserAvatarAndNameFragment(userId) 91 | 92 | if (!userData) { 93 | return null 94 | } 95 | 96 | return {`${userData.name} 97 | } 98 | ``` 99 | `useUserAvatarAndNameFragment` is just a wrapper around `useApolloFragment` which reduce the amount of boilerplate and also properly types the return value. -------------------------------------------------------------------------------- /packages/apollo-fragment-react-codegen/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "apollo-fragment-react-codegen", 3 | "version": "0.7.2", 4 | "description": "A plugin for graphql-code-generator that allows to generate React hooks based on fragment definitions", 5 | "author": "Aleksei Ustiuzhanin ", 6 | "license": "MIT", 7 | "main": "lib/index.js", 8 | "module": "lib/index.esm.js", 9 | "typings": "lib/index.d.ts", 10 | "typescript": { 11 | "definition": "lib/index.d.ts" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "git+https://github.com/abhiaiyer91/apollo-fragment.git" 16 | }, 17 | "bugs": { 18 | "url": "https://github.com/abhiaiyer91/apollo-fragment/issues" 19 | }, 20 | "homepage": "https://github.com/abhiaiyer91/apollo-fragment#readme", 21 | "scripts": { 22 | "build": "tsc -p .", 23 | "clean": "rimraf lib/* && rimraf coverage/*", 24 | "prelint": "npm run lint-fix", 25 | "lint-fix": "prettier --trailing-comma all --single-quote --write \"src/**/*.{j,t}s*\"", 26 | "lint": "tslint --type-check -p tsconfig.json -c tslint.json src/*.ts", 27 | "lint-staged": "lint-staged", 28 | "prebuild": "npm run clean", 29 | "prepublishOnly": "npm run clean && npm run build", 30 | "test": "jest", 31 | "coverage": "npm run lint && jest --coverage", 32 | "watch": "trap 'kill -9 %1' SIGINT; tsc -w -p ." 33 | }, 34 | "peerDependencies": { 35 | "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0", 36 | "graphql-tag": "^2.0.0" 37 | }, 38 | "devDependencies": { 39 | "@types/graphql": "^14.5.0", 40 | "@types/jest": "22.1.x", 41 | "codecov": "3.0.0", 42 | "danger": "1.2.0", 43 | "graphql": "^15.1.0", 44 | "graphql-tag": "^2.0.0", 45 | "jest": "21.2.1", 46 | "lint-staged": "4.3.0", 47 | "pre-commit": "1.2.2", 48 | "prettier": "1.7.4", 49 | "rimraf": "2.6.1", 50 | "ts-jest": "21.1.4", 51 | "tslint": "5.8.0", 52 | "typescript": "^4.1.2" 53 | }, 54 | "jest": { 55 | "mapCoverage": true, 56 | "transform": { 57 | ".(ts|tsx)": "/node_modules/ts-jest/preprocessor.js" 58 | }, 59 | "testRegex": "(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$", 60 | "moduleFileExtensions": [ 61 | "ts", 62 | "tsx", 63 | "js", 64 | "json" 65 | ] 66 | }, 67 | "dependencies": { 68 | "@graphql-codegen/plugin-helpers": "^1.15.4", 69 | "@graphql-codegen/visitor-plugin-common": "^1.15.4", 70 | "pascal-case": "^3.1.1" 71 | }, 72 | "lint-staged": { 73 | "*.ts*": [ 74 | "prettier --trailing-comma all --single-quote --write", 75 | "git add" 76 | ], 77 | "*.js*": [ 78 | "prettier --trailing-comma all --single-quote --write", 79 | "git add" 80 | ], 81 | "*.json*": [ 82 | "prettier --write", 83 | "git add" 84 | ] 85 | }, 86 | "pre-commit": "lint-staged" 87 | } 88 | -------------------------------------------------------------------------------- /packages/apollo-fragment-react-codegen/src/config.ts: -------------------------------------------------------------------------------- 1 | import { RawClientSideBasePluginConfig } from '@graphql-codegen/visitor-plugin-common'; 2 | 3 | /** 4 | * @description This plugin generates hooks based on apollo-fragment-react with TypeScript typings. 5 | * 6 | * It requires typescript-react-apollo to be setup to generate fragment documents. 7 | */ 8 | export interface ApolloFragmentReactRawPluginConfig 9 | extends RawClientSideBasePluginConfig { 10 | /** 11 | * @description Customize the package where apollo-fragment-react lib is loaded from. 12 | * @default "apollo-fragment-react" 13 | */ 14 | apolloFragmentReactImportFrom?: string; 15 | /** 16 | * @description Allows you to enable/disable the generation of docblocks in generated code. 17 | * Some IDE's (like VSCode) add extra inline information with docblocks, you can disable this feature if your preferred IDE does not. 18 | * @default true 19 | * 20 | * @exampleMarkdown 21 | * ```yml 22 | * generates: 23 | * path/to/file.ts: 24 | * plugins: 25 | * - typescript 26 | * - typescript-operations 27 | * - typescript-react-apollo 28 | * - apollo-fragment-react-codegen 29 | * config: 30 | * addDocBlocks: true 31 | * ``` 32 | */ 33 | addDocBlocks?: boolean; 34 | } 35 | -------------------------------------------------------------------------------- /packages/apollo-fragment-react-codegen/src/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Types, 3 | PluginValidateFn, 4 | PluginFunction, 5 | } from '@graphql-codegen/plugin-helpers'; 6 | import { 7 | GraphQLSchema, 8 | concatAST, 9 | Kind, 10 | FragmentDefinitionNode, 11 | } from 'graphql'; 12 | import { LoadedFragment } from '@graphql-codegen/visitor-plugin-common'; 13 | import { ApolloFragmentReactVisitor } from './visitor'; 14 | import { extname } from 'path'; 15 | import { ApolloFragmentReactRawPluginConfig } from './config'; 16 | 17 | export const plugin: PluginFunction< 18 | ApolloFragmentReactRawPluginConfig, 19 | Types.ComplexPluginOutput 20 | > = ( 21 | schema: GraphQLSchema, 22 | documents: Types.DocumentFile[], 23 | config: ApolloFragmentReactRawPluginConfig, 24 | ) => { 25 | const allAst = concatAST(documents.map(v => v.document)); 26 | 27 | const allFragments: LoadedFragment[] = [ 28 | ...(allAst.definitions.filter( 29 | d => d.kind === Kind.FRAGMENT_DEFINITION, 30 | ) as FragmentDefinitionNode[]).map(fragmentDef => ({ 31 | node: fragmentDef, 32 | name: fragmentDef.name.value, 33 | onType: fragmentDef.typeCondition.name.value, 34 | isExternal: false, 35 | })), 36 | ...(config.externalFragments || []), 37 | ]; 38 | 39 | const visitor = new ApolloFragmentReactVisitor( 40 | schema, 41 | allFragments, 42 | config, 43 | documents, 44 | ); 45 | 46 | return { 47 | prepend: visitor.getImports(), 48 | content: visitor.fragments, 49 | }; 50 | }; 51 | 52 | export const validate: PluginValidateFn = async ( 53 | _schema: GraphQLSchema, 54 | _documents: Types.DocumentFile[], 55 | _config: ApolloFragmentReactRawPluginConfig, 56 | outputFile: string, 57 | ) => { 58 | if (extname(outputFile) !== '.ts' && extname(outputFile) !== '.tsx') { 59 | throw new Error( 60 | `Plugin "apollo-fragment-react-codegen" requires extension to be ".ts" or ".tsx"!`, 61 | ); 62 | } 63 | }; 64 | 65 | export { ApolloFragmentReactVisitor }; 66 | -------------------------------------------------------------------------------- /packages/apollo-fragment-react-codegen/src/visitor.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ClientSideBaseVisitor, 3 | ClientSideBasePluginConfig, 4 | getConfigValue, 5 | LoadedFragment, 6 | } from '@graphql-codegen/visitor-plugin-common'; 7 | import { ApolloFragmentReactRawPluginConfig } from './config'; 8 | import autoBind from 'auto-bind'; 9 | import { GraphQLSchema, FragmentDefinitionNode } from 'graphql'; 10 | import { Types } from '@graphql-codegen/plugin-helpers'; 11 | import { pascalCase } from 'pascal-case'; 12 | 13 | export interface ReactApolloPluginConfig extends ClientSideBasePluginConfig { 14 | apolloFragmentReactImportFrom: string; 15 | withResultType: boolean; 16 | addDocBlocks: boolean; 17 | } 18 | 19 | export class ApolloFragmentReactVisitor extends ClientSideBaseVisitor< 20 | ApolloFragmentReactRawPluginConfig, 21 | ReactApolloPluginConfig 22 | > { 23 | imports: Set; 24 | 25 | constructor( 26 | schema: GraphQLSchema, 27 | fragments: LoadedFragment[], 28 | rawConfig: ApolloFragmentReactRawPluginConfig, 29 | documents: Types.DocumentFile[], 30 | ) { 31 | super(schema, fragments, rawConfig, { 32 | apolloFragmentReactImportFrom: getConfigValue( 33 | rawConfig.apolloFragmentReactImportFrom, 34 | `apollo-fragment-react`, 35 | ), 36 | addDocBlocks: getConfigValue(rawConfig.addDocBlocks, true), 37 | }); 38 | 39 | this._documents = documents; 40 | this.imports = new Set(); 41 | 42 | autoBind(this); 43 | } 44 | 45 | public getImports(): string[] { 46 | const baseImports = super.getImports(); 47 | 48 | if (!this.fragments) { 49 | return baseImports; 50 | } 51 | 52 | return [...baseImports, ...this.imports]; 53 | } 54 | 55 | protected _generateFragment(fragmentDocument: FragmentDefinitionNode) { 56 | this.imports.add(this.getApolloFragmentReactImport()); 57 | const hooks = this._buildHooks(fragmentDocument); 58 | 59 | return [hooks, ''].filter(a => a).join('\n'); 60 | } 61 | 62 | private getApolloFragmentReactImport(): string { 63 | return `import { useApolloFragment } from '${this.config.apolloFragmentReactImportFrom}';`; 64 | } 65 | 66 | private _buildHooksJSDoc(operationName: string): string { 67 | return ` 68 | /** 69 | * __use${operationName}__ 70 | * To read a fragment data from Apollo Cache, call \`use${operationName}\` and pass it the ID of the cached object. 71 | * When your component renders, \`use${operationName}\` returns an object from Apollo Client cache that contains data property 72 | * you can use to render your UI. 73 | * 74 | * @param id a string representing the ID of the cached object that will be passed into the useApolloFragment 75 | * 76 | * @example 77 | * const { data } = use${operationName}('fragment-id'); 78 | */`; 79 | } 80 | 81 | private _buildHooks(fragment: FragmentDefinitionNode): string { 82 | const suffix = this._getHookSuffix(fragment.name.value); 83 | const operationName: string = this.convertName(fragment.name.value, { 84 | suffix, 85 | useTypesPrefix: false, 86 | }); 87 | 88 | const hookFns = [ 89 | `export function use${operationName}(id: string) { 90 | return useApolloFragment<${this.getFragmentName( 91 | fragment, 92 | )}>(${this.getFragmentVariableName(fragment)}, id); 93 | }`, 94 | ]; 95 | 96 | if (this.config.addDocBlocks) { 97 | hookFns.unshift(this._buildHooksJSDoc(operationName)); 98 | } 99 | 100 | const hookResults = [ 101 | `export type ${operationName}HookResult = ReturnType;`, 102 | ]; 103 | 104 | return [...hookFns, ...hookResults].join('\n'); 105 | } 106 | 107 | private _getHookSuffix(name: string) { 108 | if (name.includes('Fragment')) { 109 | return ''; 110 | } 111 | return pascalCase(`fragment`); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /packages/apollo-fragment-react-codegen/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "./src", 4 | "outDir": "lib", 5 | "esModuleInterop": true, 6 | "allowSyntheticDefaultImports": true, 7 | "importHelpers": true, 8 | "experimentalDecorators": true, 9 | "module": "commonjs", 10 | "target": "esnext", 11 | "lib": ["es6", "esnext", "es2015", "dom"], 12 | "suppressImplicitAnyIndexErrors": true, 13 | "moduleResolution": "node", 14 | "emitDecoratorMetadata": true, 15 | "sourceMap": true, 16 | "declaration": true, 17 | "noImplicitThis": true, 18 | "alwaysStrict": true, 19 | "noImplicitReturns": true, 20 | "noUnusedLocals": true, 21 | "resolveJsonModule": true, 22 | "skipLibCheck": true 23 | }, 24 | "include": ["./src"] 25 | } 26 | -------------------------------------------------------------------------------- /packages/apollo-fragment-react-codegen/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "ban": false, 4 | "class-name": true, 5 | "eofline": true, 6 | "forin": true, 7 | "interface-name": [true, "never-prefix"], 8 | "jsdoc-format": true, 9 | "label-position": true, 10 | "member-access": true, 11 | "member-ordering": [ 12 | true, 13 | { 14 | "order": [ 15 | "static-field", 16 | "instance-field", 17 | "constructor", 18 | "public-instance-method", 19 | "protected-instance-method", 20 | "private-instance-method" 21 | ] 22 | } 23 | ], 24 | "no-any": false, 25 | "no-arg": true, 26 | "no-bitwise": true, 27 | "no-conditional-assignment": true, 28 | "no-consecutive-blank-lines": false, 29 | "no-console": [true, "log", "debug", "info", "time", "timeEnd", "trace"], 30 | "no-construct": true, 31 | "no-debugger": true, 32 | "no-duplicate-variable": true, 33 | "no-empty": true, 34 | "no-eval": true, 35 | "no-inferrable-types": false, 36 | "no-internal-module": true, 37 | "no-null-keyword": false, 38 | "no-require-imports": false, 39 | "no-shadowed-variable": true, 40 | "no-switch-case-fall-through": true, 41 | "no-trailing-whitespace": true, 42 | "no-unused-expression": true, 43 | "no-var-keyword": true, 44 | "no-var-requires": true, 45 | "object-literal-sort-keys": false, 46 | "radix": true, 47 | "switch-default": true, 48 | "triple-equals": [true, "allow-null-check"], 49 | "typedef": [ 50 | false, 51 | "call-signature", 52 | "parameter", 53 | "arrow-parameter", 54 | "property-declaration", 55 | "variable-declaration", 56 | "member-variable-declaration" 57 | ], 58 | "variable-name": [ 59 | true, 60 | "check-format", 61 | "allow-leading-underscore", 62 | "ban-keywords" 63 | ] 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /packages/apollo-fragment-react/.npmignore: -------------------------------------------------------------------------------- 1 | dist/tests/ 2 | src/ 3 | tests/ 4 | .travis.yml 5 | tsconfig.json 6 | tslint.json 7 | typings.d.ts 8 | example/ 9 | __tests__/ 10 | -------------------------------------------------------------------------------- /packages/apollo-fragment-react/example/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | build 4 | .DS_Store 5 | .env 6 | npm-debug.log 7 | -------------------------------------------------------------------------------- /packages/apollo-fragment-react/example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "apollo-client-error-template", 4 | "version": "0.1.0", 5 | "devDependencies": { 6 | "gh-pages": "^1.0.0", 7 | "react-scripts": "1.0.16" 8 | }, 9 | "dependencies": { 10 | "apollo-cache-inmemory": "^1.0.0", 11 | "apollo-client": "^2.3.1", 12 | "apollo-fragment-react": "^0.1.0", 13 | "apollo-link": "^1.0.0", 14 | "apollo-link-state": "^0.4.1", 15 | "apollo-link-state-fragment": "^0.1.0", 16 | "graphql": "^0.11.7", 17 | "graphql-tag": "^2.5.0", 18 | "react": "^16.0.0", 19 | "react-apollo": "^2.1.4", 20 | "react-dom": "^16.0.0" 21 | }, 22 | "scripts": { 23 | "start": "react-scripts start", 24 | "build": "react-scripts build", 25 | "test": "react-scripts test --env=jsdom", 26 | "deploy": 27 | "PUBLIC_URL=/react-apollo-error-template/$(git symbolic-ref --short HEAD) npm run build && gh-pages-clean && gh-pages -d build --dest $(git symbolic-ref --short HEAD) --remote" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/apollo-fragment-react/example/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhiaiyer91/apollo-fragment/17e2f414fdcbc4d452538fd00f92c5957e3b9765/packages/apollo-fragment-react/example/public/favicon.ico -------------------------------------------------------------------------------- /packages/apollo-fragment-react/example/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 16 | React App 17 | 18 | 19 |
20 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /packages/apollo-fragment-react/example/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { graphql } from 'react-apollo'; 3 | import gql from 'graphql-tag'; 4 | import { ApolloFragment } from 'apollo-fragment-react'; 5 | 6 | const fragment = ` 7 | fragment fragmentFields on Person { 8 | id 9 | name 10 | __typename 11 | } 12 | `; 13 | 14 | let ListView = function ListView({ data: { loading, people } }) { 15 | return ( 16 |
17 | {loading ? ( 18 |

Loading…

19 | ) : ( 20 |
    21 | {people && 22 | people.map(person =>
  • {person.name}
  • )} 23 |
24 | )} 25 |
26 | ); 27 | }; 28 | 29 | ListView = graphql( 30 | gql` 31 | query peeps { 32 | people { 33 | id 34 | name 35 | } 36 | } 37 | `, 38 | )(ListView); 39 | 40 | export default class App extends Component { 41 | constructor() { 42 | super(); 43 | this.state = { 44 | showView: false, 45 | }; 46 | } 47 | render() { 48 | const { showView } = this.state; 49 | 50 | return ( 51 |
52 |
53 |

ApolloFragment

54 |
55 | 56 | 57 | {({ data }) => { 58 | return ( 59 |
60 |

61 | This component is "watching" a fragment, it will render the 62 | persons name once the list view renders 63 |

64 |

{data && `Person Name: ${data.name || ''}`}

65 |
66 | ); 67 | }} 68 |
69 |
70 |
71 |
72 | 73 | {showView && } 74 | 75 | 82 |
83 | ); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /packages/apollo-fragment-react/example/src/graphql/link.js: -------------------------------------------------------------------------------- 1 | import { graphql, print } from 'graphql'; 2 | import { ApolloLink, Observable } from 'apollo-link'; 3 | import { schema } from './schema'; 4 | 5 | export const link = new ApolloLink(operation => { 6 | return new Observable(observer => { 7 | const { query, operationName, variables } = operation; 8 | delay(300) 9 | .then(() => 10 | graphql(schema, print(query), null, null, variables, operationName), 11 | ) 12 | .then(result => { 13 | observer.next(result); 14 | observer.complete(); 15 | }) 16 | .catch(observer.error.bind(observer)); 17 | }); 18 | }); 19 | 20 | function delay(ms) { 21 | return new Promise(resolve => { 22 | setTimeout(() => { 23 | resolve(); 24 | }, ms); 25 | }); 26 | } 27 | -------------------------------------------------------------------------------- /packages/apollo-fragment-react/example/src/graphql/schema.js: -------------------------------------------------------------------------------- 1 | import { 2 | GraphQLSchema, 3 | GraphQLObjectType, 4 | GraphQLID, 5 | GraphQLString, 6 | GraphQLList, 7 | } from 'graphql'; 8 | 9 | const PersonType = new GraphQLObjectType({ 10 | name: 'Person', 11 | fields: { 12 | id: { type: GraphQLID }, 13 | name: { type: GraphQLString }, 14 | }, 15 | }); 16 | 17 | const peopleData = [ 18 | { id: 1, name: 'John Smith' }, 19 | { id: 2, name: 'Sara Smith' }, 20 | { id: 3, name: 'Budd Deey' }, 21 | ]; 22 | 23 | const QueryType = new GraphQLObjectType({ 24 | name: 'Query', 25 | fields: { 26 | people: { 27 | type: new GraphQLList(PersonType), 28 | resolve: () => peopleData, 29 | }, 30 | }, 31 | }); 32 | 33 | export const schema = new GraphQLSchema({ query: QueryType }); 34 | -------------------------------------------------------------------------------- /packages/apollo-fragment-react/example/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: sans-serif; 3 | } 4 | -------------------------------------------------------------------------------- /packages/apollo-fragment-react/example/src/index.js: -------------------------------------------------------------------------------- 1 | import './index.css'; 2 | 3 | import React from 'react'; 4 | import { render } from 'react-dom'; 5 | import { ApolloLink } from 'apollo-link'; 6 | import { ApolloClient } from 'apollo-client'; 7 | import { ApolloProvider } from 'react-apollo'; 8 | import { InMemoryCache } from 'apollo-cache-inmemory'; 9 | import { 10 | fragmentCacheRedirect, 11 | fragmentLinkState, 12 | } from 'apollo-link-state-fragment'; 13 | import { link } from './graphql/link'; 14 | import App from './App'; 15 | 16 | const cache = new InMemoryCache({ 17 | cacheRedirects: { 18 | Query: { 19 | ...fragmentCacheRedirect(), 20 | }, 21 | }, 22 | }); 23 | 24 | const client = new ApolloClient({ 25 | cache, 26 | link: ApolloLink.from([fragmentLinkState(cache), link]), 27 | }); 28 | 29 | render( 30 | 31 | 32 | , 33 | document.getElementById('root'), 34 | ); 35 | -------------------------------------------------------------------------------- /packages/apollo-fragment-react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "apollo-fragment-react", 3 | "version": "0.7.1", 4 | "description": "A React hook to connect React components to GraphQL fragments in Apollo Client cache", 5 | "author": "Abhi Aiyer ", 6 | "license": "MIT", 7 | "main": "./lib/bundle.umd.js", 8 | "module": "./lib/index.js", 9 | "jsnext:main": "./lib/index.js", 10 | "typings": "./lib/index.d.ts", 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/abhiaiyer91/apollo-fragment.git" 14 | }, 15 | "bugs": { 16 | "url": "https://github.com/abhiaiyer91/apollo-fragment/issues" 17 | }, 18 | "homepage": "https://github.com/abhiaiyer91/apollo-fragment#readme", 19 | "scripts": { 20 | "build:browser": "browserify ./lib/bundle.umd.js -o=./lib/bundle.js --i apollo-link --i apollo-utilities --i graphql-anywhere && npm run minify:browser", 21 | "build": "tsc -p .", 22 | "bundle": "rollup -c", 23 | "clean": "rimraf lib/* && rimraf coverage/*", 24 | "filesize": "npm run build && npm run build:browser && bundlesize", 25 | "prelint": "npm run lint-fix", 26 | "lint-fix": "prettier --trailing-comma all --single-quote --write \"src/**/*.{j,t}s*\"", 27 | "lint": "tslint --type-check -p tsconfig.json -c tslint.json src/*.ts", 28 | "lint-staged": "lint-staged", 29 | "minify:browser": "uglifyjs -c -m -o ./lib/bundle.min.js -- ./lib/bundle.js", 30 | "postbuild": "npm run bundle", 31 | "prebuild": "npm run clean", 32 | "prepublishOnly": "npm run clean && npm run build", 33 | "test": "jest", 34 | "coverage": "npm run lint && jest --coverage", 35 | "watch": "trap 'kill -9 %1' SIGINT; tsc -w -p . & rollup -w -c" 36 | }, 37 | "bundlesize": [ 38 | { 39 | "name": "apollo-fragment-react", 40 | "path": "./lib/bundle.min.js", 41 | "threshold": "1 Kb" 42 | } 43 | ], 44 | "peerDependencies": { 45 | "@apollo/client": "^3.5.6" 46 | }, 47 | "devDependencies": { 48 | "@types/graphql": "0.11.5", 49 | "@types/jest": "22.1.x", 50 | "apollo-cache-inmemory": "^1.1.5", 51 | "@apollo/client": "^3.5.6", 52 | "apollo-link": "^1.0.0", 53 | "browserify": "14.5.0", 54 | "bundlesize": "0.15.3", 55 | "codecov": "3.0.0", 56 | "danger": "1.2.0", 57 | "enzyme": "^3.3.0", 58 | "enzyme-adapter-react-16": "^1.1.1", 59 | "graphql": "0.11.7", 60 | "graphql-tag": "2.5.0", 61 | "jest": "21.2.1", 62 | "lint-staged": "4.3.0", 63 | "pre-commit": "1.2.2", 64 | "prettier": "1.7.4", 65 | "react": "^16.8.6", 66 | "react-dom": "^16.8.6", 67 | "rimraf": "2.6.1", 68 | "rollup": "0.56.x", 69 | "rollup-plugin-local-resolve": "1.0.x", 70 | "rollup-plugin-sourcemaps": "0.4.x", 71 | "ts-jest": "21.1.4", 72 | "tslint": "5.8.0", 73 | "typescript": "^4.1.2", 74 | "uglify-js": "3.1.5" 75 | }, 76 | "jest": { 77 | "mapCoverage": true, 78 | "transform": { 79 | ".(ts|tsx)": "/node_modules/ts-jest/preprocessor.js" 80 | }, 81 | "testRegex": "(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$", 82 | "moduleFileExtensions": [ 83 | "ts", 84 | "tsx", 85 | "js", 86 | "json" 87 | ], 88 | "setupFiles": [ 89 | "/src/mocks/enzymeAdapter.ts" 90 | ] 91 | }, 92 | "dependencies": { 93 | "apollo-fragment-utils": "^0.2.1", 94 | "apollo-utilities": "^1.0.12", 95 | "compose-tiny": "^1.1.3" 96 | }, 97 | "lint-staged": { 98 | "*.ts*": [ 99 | "prettier --trailing-comma all --single-quote --write", 100 | "git add" 101 | ], 102 | "*.js*": [ 103 | "prettier --trailing-comma all --single-quote --write", 104 | "git add" 105 | ], 106 | "*.json*": [ 107 | "prettier --write", 108 | "git add" 109 | ] 110 | }, 111 | "pre-commit": "lint-staged" 112 | } 113 | -------------------------------------------------------------------------------- /packages/apollo-fragment-react/rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from 'rollup-plugin-local-resolve'; 2 | import sourcemaps from 'rollup-plugin-sourcemaps'; 3 | 4 | const globals = { 5 | // Apollo 6 | 'apollo-client': 'apollo.core', 7 | 'apollo-cache': 'apolloCache.core', 8 | 'apollo-link': 'apolloLink.core', 9 | 'apollo-utilities': 'apollo.utilities', 10 | 11 | 'graphql-anywhere/lib/async': 'graphqlAnywhere.async', 12 | }; 13 | 14 | export default { 15 | input: 'lib/index.js', 16 | output: { 17 | file: 'lib/bundle.umd.js', 18 | format: 'umd', 19 | name: 'apolloFragmentReact.state', 20 | exports: 'named', 21 | sourcemap: true, 22 | globals, 23 | }, 24 | external: Object.keys(globals), 25 | onwarn, 26 | plugins: [resolve(), sourcemaps()], 27 | }; 28 | 29 | function onwarn(message) { 30 | const suppressed = ['UNRESOLVED_IMPORT', 'THIS_IS_UNDEFINED']; 31 | 32 | if (!suppressed.find(code => message.code === code)) { 33 | return console.warn(message.message); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/apollo-fragment-react/src/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { ApolloClient, InMemoryCacheConfig } from '@apollo/client'; 3 | import { useQuery, QueryResult } from '@apollo/client/react'; 4 | import { DocumentNode, Location } from 'graphql'; 5 | import { getFragmentInfo, buildFragmentQuery } from 'apollo-fragment-utils'; 6 | 7 | type FragmentQueryData = { 8 | getFragment?: TData; 9 | }; 10 | 11 | export type SupportedFragment = DocumentNode | string; 12 | 13 | type Omit = Pick>; 14 | 15 | export type ApolloFragmentResult = Omit< 16 | QueryResult>, 17 | 'data' 18 | > & { 19 | data: TData | undefined; 20 | }; 21 | 22 | export function useApolloFragment( 23 | fragment: SupportedFragment, 24 | id: string, 25 | ): ApolloFragmentResult { 26 | const fragmentQuery = React.useMemo(() => createFragmentQuery(fragment), [ 27 | fragment, 28 | ]); 29 | 30 | const { data, client, ...rest } = useQuery>( 31 | fragmentQuery.query, 32 | { 33 | fetchPolicy: 'cache-only', 34 | variables: { 35 | id, 36 | __typename: fragmentQuery.fragmentTypeName, 37 | }, 38 | }, 39 | ); 40 | 41 | const fragmentData = data && data.getFragment; 42 | 43 | if (!fragmentData) { 44 | checkDataCompleteness({ 45 | fragmentQuery, 46 | id, 47 | client, 48 | }); 49 | } 50 | 51 | return { 52 | data: fragmentData, 53 | client, 54 | ...rest, 55 | }; 56 | } 57 | 58 | export const fragmentCacheConfig: Required> = { 62 | typePolicies: { 63 | Query: { 64 | fields: { 65 | getFragment(_, { args, toReference }) { 66 | return toReference({ 67 | id: args ? args.id : undefined, 68 | __typename: args ? args.__typename : undefined, 69 | }); 70 | }, 71 | }, 72 | }, 73 | }, 74 | }; 75 | 76 | type FragmentQuery = { 77 | query: DocumentNode; 78 | fragmentTypeName: string; 79 | fragmentSource: string; 80 | }; 81 | 82 | function createFragmentQuery(fragment: SupportedFragment): FragmentQuery { 83 | const { fragmentTypeName, fragmentName } = getFragmentInfo(fragment); 84 | 85 | return { 86 | query: buildFragmentQuery({ fragment, fragmentName }), 87 | fragmentTypeName: fragmentTypeName, 88 | fragmentSource: 89 | typeof fragment === `string` 90 | ? fragment 91 | : (fragment.loc as Location).source.body, 92 | }; 93 | } 94 | 95 | function checkDataCompleteness({ 96 | fragmentQuery, 97 | id, 98 | client, 99 | }: { 100 | fragmentQuery: FragmentQuery; 101 | id: string; 102 | client: ApolloClient; 103 | }): void { 104 | // Only perform completeness check for non-production code 105 | if (process.env.NODE_ENV === 'production') { 106 | return; 107 | } 108 | 109 | const diff = client.cache.diff({ 110 | query: fragmentQuery.query, 111 | variables: { 112 | id, 113 | __typename: fragmentQuery.fragmentTypeName, 114 | }, 115 | previousResult: undefined, 116 | optimistic: true, 117 | }); 118 | 119 | if (diff.complete === true) { 120 | return; 121 | } 122 | 123 | const fragmentData = diff.result && diff.result.getFragment; 124 | const noData = Object.keys(fragmentData || {}).length === 0; 125 | 126 | if (noData) { 127 | return; 128 | } 129 | const dataWithoutTypename = { ...fragmentData, __typename: undefined }; 130 | 131 | const errorMessage = ` 132 | Unable to resolve fragment fields for ${fragmentQuery.fragmentTypeName}:${id}: 133 | ${fragmentQuery.fragmentSource.trimRight()} 134 | 135 | Available data: 136 | ${JSON.stringify(dataWithoutTypename, null, 2)} 137 | 138 | Make sure that the fields requested in the fragment are fetched by some query`; 139 | 140 | console.error(errorMessage); 141 | } 142 | -------------------------------------------------------------------------------- /packages/apollo-fragment-react/src/mocks/enzymeAdapter.ts: -------------------------------------------------------------------------------- 1 | import * as Enzyme from 'enzyme'; 2 | const Adapter = require('enzyme-adapter-react-16'); 3 | 4 | Enzyme.configure({ adapter: new Adapter() }); 5 | -------------------------------------------------------------------------------- /packages/apollo-fragment-react/src/mocks/mockLink.ts: -------------------------------------------------------------------------------- 1 | import { graphql, print, ExecutionResult } from 'graphql'; 2 | import { ApolloLink, FetchResult, Observable } from '@apollo/client'; 3 | import { schema } from './mockSchema'; 4 | 5 | export default new ApolloLink(operation => { 6 | return new Observable(observer => { 7 | const { query, operationName, variables } = operation; 8 | delay(300) 9 | .then(() => 10 | graphql(schema, print(query), null, null, variables, operationName), 11 | ) 12 | .then((result: ExecutionResult) => { 13 | observer.next(result); 14 | observer.complete(); 15 | }) 16 | .catch(observer.error.bind(observer)); 17 | }); 18 | }); 19 | 20 | function delay(ms: number) { 21 | return new Promise(resolve => { 22 | setTimeout(() => { 23 | resolve(); 24 | }, ms); 25 | }); 26 | } 27 | -------------------------------------------------------------------------------- /packages/apollo-fragment-react/src/mocks/mockSchema.ts: -------------------------------------------------------------------------------- 1 | import { 2 | GraphQLSchema, 3 | GraphQLObjectType, 4 | GraphQLID, 5 | GraphQLString, 6 | GraphQLList, 7 | } from 'graphql'; 8 | 9 | const PersonType = new GraphQLObjectType({ 10 | name: 'Person', 11 | fields: { 12 | id: { type: GraphQLID }, 13 | name: { type: GraphQLString }, 14 | }, 15 | }); 16 | 17 | const peopleData = [ 18 | { id: 1, name: 'John Smith' }, 19 | { id: 2, name: 'Sara Smith' }, 20 | { id: 3, name: 'Budd Deey' }, 21 | ]; 22 | 23 | const QueryType = new GraphQLObjectType({ 24 | name: 'Query', 25 | fields: { 26 | people: { 27 | type: new GraphQLList(PersonType), 28 | resolve: () => peopleData, 29 | }, 30 | }, 31 | }); 32 | 33 | export const schema = new GraphQLSchema({ query: QueryType }); 34 | -------------------------------------------------------------------------------- /packages/apollo-fragment-react/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": false, 4 | "declaration": true, 5 | "experimentalDecorators": true, 6 | "jsx": "react", 7 | "lib": ["es2015", "dom"], 8 | "module": "es2015", 9 | "moduleResolution": "node", 10 | "noUnusedParameters": true, 11 | "outDir": "lib", 12 | "pretty": true, 13 | "removeComments": true, 14 | "skipLibCheck": true, 15 | "sourceMap": true, 16 | "strict": true, 17 | "target": "es5" 18 | }, 19 | "include": ["./typings/**/*", "./src/index.tsx", "./test/**/*"], 20 | "exclude": ["./node_modules", "./dist", "./lib", "./src/mocks"] 21 | } 22 | -------------------------------------------------------------------------------- /packages/apollo-fragment-react/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "ban": false, 4 | "class-name": true, 5 | "eofline": true, 6 | "forin": true, 7 | "interface-name": [true, "never-prefix"], 8 | "jsdoc-format": true, 9 | "label-position": true, 10 | "member-access": true, 11 | "member-ordering": [ 12 | true, 13 | { 14 | "order": [ 15 | "static-field", 16 | "instance-field", 17 | "constructor", 18 | "public-instance-method", 19 | "protected-instance-method", 20 | "private-instance-method" 21 | ] 22 | } 23 | ], 24 | "no-any": false, 25 | "no-arg": true, 26 | "no-bitwise": true, 27 | "no-conditional-assignment": true, 28 | "no-consecutive-blank-lines": false, 29 | "no-console": [true, "log", "debug", "info", "time", "timeEnd", "trace"], 30 | "no-construct": true, 31 | "no-debugger": true, 32 | "no-duplicate-variable": true, 33 | "no-empty": true, 34 | "no-eval": true, 35 | "no-inferrable-types": false, 36 | "no-internal-module": true, 37 | "no-null-keyword": false, 38 | "no-require-imports": false, 39 | "no-shadowed-variable": true, 40 | "no-switch-case-fall-through": true, 41 | "no-trailing-whitespace": true, 42 | "no-unused-expression": true, 43 | "no-var-keyword": true, 44 | "no-var-requires": true, 45 | "object-literal-sort-keys": false, 46 | "radix": true, 47 | "switch-default": true, 48 | "triple-equals": [true, "allow-null-check"], 49 | "typedef": [ 50 | false, 51 | "call-signature", 52 | "parameter", 53 | "arrow-parameter", 54 | "property-declaration", 55 | "variable-declaration", 56 | "member-variable-declaration" 57 | ], 58 | "variable-name": [ 59 | true, 60 | "check-format", 61 | "allow-leading-underscore", 62 | "ban-keywords" 63 | ] 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /packages/apollo-fragment-utils/.npmignore: -------------------------------------------------------------------------------- 1 | dist/tests/ 2 | src/ 3 | tests/ 4 | .travis.yml 5 | tsconfig.json 6 | tslint.json 7 | typings.d.ts 8 | example/ 9 | __tests__/ 10 | -------------------------------------------------------------------------------- /packages/apollo-fragment-utils/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "apollo-fragment-utils", 3 | "version": "0.2.1", 4 | "description": "A set of utils to help components to GraphQL fragments", 5 | "author": "Abhi Aiyer ", 6 | "license": "MIT", 7 | "main": "./lib/bundle.umd.js", 8 | "module": "./lib/index.js", 9 | "jsnext:main": "./lib/index.js", 10 | "typings": "./lib/index.d.ts", 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/abhiaiyer91/apollo-fragment.git" 14 | }, 15 | "bugs": { 16 | "url": "https://github.com/abhiaiyer91/apollo-fragment/issues" 17 | }, 18 | "homepage": "https://github.com/abhiaiyer91/apollo-fragment#readme", 19 | "scripts": { 20 | "build:browser": "browserify ./lib/bundle.umd.js -o=./lib/bundle.js --i apollo-link --i apollo-utilities --i graphql-anywhere && npm run minify:browser", 21 | "build": "tsc -p .", 22 | "bundle": "rollup -c", 23 | "clean": "rimraf lib/* && rimraf coverage/*", 24 | "filesize": "npm run build && npm run build:browser && bundlesize", 25 | "prelint": "npm run lint-fix", 26 | "lint-fix": "prettier --trailing-comma all --single-quote --write \"src/**/*.{j,t}s*\"", 27 | "lint": "tslint --type-check -p tsconfig.json -c tslint.json src/*.ts", 28 | "lint-staged": "lint-staged", 29 | "minify:browser": "uglifyjs -c -m -o ./lib/bundle.min.js -- ./lib/bundle.js", 30 | "postbuild": "npm run bundle", 31 | "prebuild": "npm run clean", 32 | "prepublishOnly": "npm run clean && npm run build", 33 | "test": "jest", 34 | "coverage": "npm run lint && jest --coverage", 35 | "watch": "trap 'kill -9 %1' SIGINT; tsc -w -p . & rollup -w -c" 36 | }, 37 | "bundlesize": [ 38 | { 39 | "name": "apollo-fragment-utils", 40 | "path": "./lib/bundle.min.js", 41 | "threshold": "1 Kb" 42 | } 43 | ], 44 | "devDependencies": { 45 | "@types/graphql": "0.11.5", 46 | "@types/jest": "22.1.x", 47 | "apollo-cache-inmemory": "^1.1.5", 48 | "apollo-client": "^2.2.0", 49 | "apollo-link": "^1.0.0", 50 | "browserify": "14.5.0", 51 | "bundlesize": "0.15.3", 52 | "codecov": "3.0.0", 53 | "danger": "1.2.0", 54 | "graphql": "0.11.7", 55 | "graphql-tag": "2.5.0", 56 | "jest": "21.2.1", 57 | "lint-staged": "4.3.0", 58 | "pre-commit": "1.2.2", 59 | "prettier": "1.7.4", 60 | "rimraf": "2.6.1", 61 | "rollup": "0.56.x", 62 | "rollup-plugin-local-resolve": "1.0.x", 63 | "rollup-plugin-sourcemaps": "0.4.x", 64 | "ts-jest": "21.1.4", 65 | "tslint": "5.8.0", 66 | "typescript": "^4.1.2", 67 | "uglify-js": "3.1.5" 68 | }, 69 | "dependencies": { 70 | "apollo-utilities": "^1.0.12" 71 | }, 72 | "jest": { 73 | "mapCoverage": true, 74 | "transform": { 75 | ".(ts|tsx)": "/node_modules/ts-jest/preprocessor.js" 76 | }, 77 | "testRegex": "(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$", 78 | "moduleFileExtensions": [ 79 | "ts", 80 | "tsx", 81 | "js", 82 | "json" 83 | ] 84 | }, 85 | "lint-staged": { 86 | "*.ts*": [ 87 | "prettier --trailing-comma all --single-quote --write", 88 | "git add" 89 | ], 90 | "*.js*": [ 91 | "prettier --trailing-comma all --single-quote --write", 92 | "git add" 93 | ], 94 | "*.json*": [ 95 | "prettier --write", 96 | "git add" 97 | ] 98 | }, 99 | "pre-commit": "lint-staged" 100 | } 101 | -------------------------------------------------------------------------------- /packages/apollo-fragment-utils/rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from 'rollup-plugin-local-resolve'; 2 | import sourcemaps from 'rollup-plugin-sourcemaps'; 3 | 4 | export default { 5 | input: 'lib/index.js', 6 | output: { 7 | file: 'lib/bundle.umd.js', 8 | format: 'umd', 9 | name: 'apolloFragmentUtils.state', 10 | exports: 'named', 11 | sourcemap: true, 12 | }, 13 | onwarn, 14 | plugins: [resolve(), sourcemaps()], 15 | }; 16 | 17 | function onwarn(message) { 18 | const suppressed = ['UNRESOLVED_IMPORT', 'THIS_IS_UNDEFINED']; 19 | 20 | if (!suppressed.find(code => message.code === code)) { 21 | return console.warn(message.message); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/apollo-fragment-utils/src/__tests__/index.test.ts: -------------------------------------------------------------------------------- 1 | import gql from 'graphql-tag'; 2 | import { getFragmentInfo, buildFragmentQuery } from '../'; 3 | 4 | describe('getFragmentInfo', () => { 5 | it('should return correct fragment name and typename for string fragments', () => { 6 | const fragment = ` 7 | fragment testFragment on Person { 8 | name 9 | avatar 10 | } 11 | `; 12 | 13 | const fragmentInfo = getFragmentInfo(fragment); 14 | 15 | expect(fragmentInfo.fragmentName).toBe('testFragment'); 16 | expect(fragmentInfo.fragmentTypeName).toBe('Person'); 17 | }); 18 | 19 | it('should return correct fragment name and typename for fragments parsed into GraphQL AST', () => { 20 | const fragment = gql` 21 | fragment testFragment on Person { 22 | name 23 | avatar 24 | } 25 | `; 26 | 27 | const fragmentInfo = getFragmentInfo(fragment); 28 | 29 | expect(fragmentInfo.fragmentName).toBe('testFragment'); 30 | expect(fragmentInfo.fragmentTypeName).toBe('Person'); 31 | }); 32 | }); 33 | 34 | describe('buildFragmentQuery', () => { 35 | const expectedQuery = gql` 36 | query getFragment($id: ID, $__typename: String) { 37 | getFragment(id: $id, __typename: $__typename) @client { 38 | ...testFragment 39 | } 40 | } 41 | 42 | fragment testFragment on Person { 43 | name 44 | avatar 45 | } 46 | `; 47 | 48 | it('should return a valid query based on a string fragment', () => { 49 | const fragment = ` 50 | fragment testFragment on Person { 51 | name 52 | avatar 53 | } 54 | `; 55 | 56 | const fragmentQuery = buildFragmentQuery({ 57 | fragment, 58 | fragmentName: `testFragment`, 59 | }); 60 | 61 | expect(fragmentQuery).toEqual(expectedQuery); 62 | }); 63 | 64 | it('should return a valid query based on a fragment parsed into GraphQL AST', () => { 65 | const fragment = gql` 66 | fragment testFragment on Person { 67 | name 68 | avatar 69 | } 70 | `; 71 | 72 | const fragmentQuery = buildFragmentQuery({ 73 | fragment, 74 | fragmentName: `testFragment`, 75 | }); 76 | 77 | expect(fragmentQuery).toEqual(expectedQuery); 78 | }); 79 | }); 80 | -------------------------------------------------------------------------------- /packages/apollo-fragment-utils/src/index.ts: -------------------------------------------------------------------------------- 1 | import gql from 'graphql-tag'; 2 | import { DocumentNode } from 'graphql'; 3 | 4 | export function getFragmentInfo(fragment: string | DocumentNode) { 5 | const fragmentAST = typeof fragment === `string` ? gql(fragment) : fragment; 6 | const fragmentDefinitions = 7 | fragmentAST.definitions && fragmentAST.definitions[0]; 8 | const fragmentName = fragmentDefinitions && fragmentDefinitions.name.value; 9 | const fragmentTypeName = 10 | fragmentDefinitions && fragmentDefinitions.typeCondition.name.value; 11 | 12 | return { 13 | fragmentName, 14 | fragmentTypeName, 15 | }; 16 | } 17 | 18 | export type buildFragmentQueryType = { 19 | fragment: string | DocumentNode; 20 | fragmentName: string; 21 | }; 22 | 23 | export function buildFragmentQuery({ 24 | fragment, 25 | fragmentName, 26 | }: buildFragmentQueryType): DocumentNode { 27 | return gql` 28 | query getFragment($id: ID, $__typename: String) { 29 | getFragment(id: $id, __typename: $__typename) @client { 30 | ...${fragmentName} 31 | } 32 | } 33 | ${fragment} 34 | `; 35 | } 36 | -------------------------------------------------------------------------------- /packages/apollo-fragment-utils/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowSyntheticDefaultImports": false, 4 | "declaration": true, 5 | "experimentalDecorators": true, 6 | "lib": ["es2015", "dom"], 7 | "module": "es2015", 8 | "moduleResolution": "node", 9 | "noUnusedParameters": true, 10 | "outDir": "lib", 11 | "pretty": true, 12 | "removeComments": true, 13 | "skipLibCheck": true, 14 | "sourceMap": true, 15 | "strict": true, 16 | "target": "es5" 17 | }, 18 | "include": ["./typings/**/*", "./src/index.ts", "./__tests__/**/*"], 19 | "exclude": ["./node_modules", "./dist", "./lib", "./src/mocks"] 20 | } 21 | -------------------------------------------------------------------------------- /packages/apollo-fragment-utils/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "ban": false, 4 | "class-name": true, 5 | "eofline": true, 6 | "forin": true, 7 | "interface-name": [true, "never-prefix"], 8 | "jsdoc-format": true, 9 | "label-position": true, 10 | "member-access": true, 11 | "member-ordering": [ 12 | true, 13 | { 14 | "order": [ 15 | "static-field", 16 | "instance-field", 17 | "constructor", 18 | "public-instance-method", 19 | "protected-instance-method", 20 | "private-instance-method" 21 | ] 22 | } 23 | ], 24 | "no-any": false, 25 | "no-arg": true, 26 | "no-bitwise": true, 27 | "no-conditional-assignment": true, 28 | "no-consecutive-blank-lines": false, 29 | "no-console": [true, "log", "debug", "info", "time", "timeEnd", "trace"], 30 | "no-construct": true, 31 | "no-debugger": true, 32 | "no-duplicate-variable": true, 33 | "no-empty": true, 34 | "no-eval": true, 35 | "no-inferrable-types": false, 36 | "no-internal-module": true, 37 | "no-null-keyword": false, 38 | "no-require-imports": false, 39 | "no-shadowed-variable": true, 40 | "no-switch-case-fall-through": true, 41 | "no-trailing-whitespace": true, 42 | "no-unused-expression": true, 43 | "no-var-keyword": true, 44 | "no-var-requires": true, 45 | "object-literal-sort-keys": false, 46 | "radix": true, 47 | "switch-default": true, 48 | "triple-equals": [true, "allow-null-check"], 49 | "typedef": [ 50 | false, 51 | "call-signature", 52 | "parameter", 53 | "arrow-parameter", 54 | "property-declaration", 55 | "variable-declaration", 56 | "member-variable-declaration" 57 | ], 58 | "variable-name": [ 59 | true, 60 | "check-format", 61 | "allow-leading-underscore", 62 | "ban-keywords" 63 | ] 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /packages/apollo-fragment-vue/.npmignore: -------------------------------------------------------------------------------- 1 | dist/tests/ 2 | src/ 3 | tests/ 4 | .travis.yml 5 | tsconfig.json 6 | tslint.json 7 | typings.d.ts 8 | example/ 9 | -------------------------------------------------------------------------------- /packages/apollo-fragment-vue/example/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw* 22 | -------------------------------------------------------------------------------- /packages/apollo-fragment-vue/example/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['@vue/app'], 3 | }; 4 | -------------------------------------------------------------------------------- /packages/apollo-fragment-vue/example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "apollo-fragment-utils": "^0.1.0" 12 | }, 13 | "devDependencies": { 14 | "@vue/cli-plugin-babel": "^3.0.0-beta.15", 15 | "@vue/cli-plugin-eslint": "^3.0.0-beta.15", 16 | "@vue/cli-service": "^3.0.0-beta.15", 17 | "ts-loader": "^4.4.2", 18 | "vue-template-compiler": "^2.5.16" 19 | }, 20 | "eslintConfig": { 21 | "root": true, 22 | "env": { 23 | "node": true 24 | }, 25 | "extends": ["plugin:vue/essential", "eslint:recommended"], 26 | "rules": {}, 27 | "parserOptions": { 28 | "parser": "babel-eslint" 29 | } 30 | }, 31 | "postcss": { 32 | "plugins": { 33 | "autoprefixer": {} 34 | } 35 | }, 36 | "browserslist": ["> 1%", "last 2 versions", "not ie <= 8"] 37 | } 38 | -------------------------------------------------------------------------------- /packages/apollo-fragment-vue/example/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhiaiyer91/apollo-fragment/17e2f414fdcbc4d452538fd00f92c5957e3b9765/packages/apollo-fragment-vue/example/public/favicon.ico -------------------------------------------------------------------------------- /packages/apollo-fragment-vue/example/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | example 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /packages/apollo-fragment-vue/example/src/App.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 19 | 20 | 30 | -------------------------------------------------------------------------------- /packages/apollo-fragment-vue/example/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abhiaiyer91/apollo-fragment/17e2f414fdcbc4d452538fd00f92c5957e3b9765/packages/apollo-fragment-vue/example/src/assets/logo.png -------------------------------------------------------------------------------- /packages/apollo-fragment-vue/example/src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 54 | 55 | 80 | 81 | 82 | 98 | -------------------------------------------------------------------------------- /packages/apollo-fragment-vue/example/src/graphql/link.js: -------------------------------------------------------------------------------- 1 | import { graphql, print } from 'graphql'; 2 | import { ApolloLink, Observable } from 'apollo-link'; 3 | import { schema } from './schema'; 4 | 5 | export const link = new ApolloLink(operation => { 6 | return new Observable(observer => { 7 | const { query, operationName, variables } = operation; 8 | delay(300) 9 | .then(() => 10 | graphql(schema, print(query), null, null, variables, operationName), 11 | ) 12 | .then(result => { 13 | observer.next(result); 14 | observer.complete(); 15 | }) 16 | .catch(observer.error.bind(observer)); 17 | }); 18 | }); 19 | 20 | function delay(ms) { 21 | return new Promise(resolve => { 22 | setTimeout(() => { 23 | resolve(); 24 | }, ms); 25 | }); 26 | } 27 | -------------------------------------------------------------------------------- /packages/apollo-fragment-vue/example/src/graphql/personFragment.js: -------------------------------------------------------------------------------- 1 | export default `fragment fragmentFields on Person { 2 | id 3 | name 4 | __typename 5 | }`; 6 | -------------------------------------------------------------------------------- /packages/apollo-fragment-vue/example/src/graphql/schema.js: -------------------------------------------------------------------------------- 1 | import { 2 | GraphQLSchema, 3 | GraphQLObjectType, 4 | GraphQLID, 5 | GraphQLString, 6 | GraphQLList, 7 | } from 'graphql'; 8 | 9 | const PersonType = new GraphQLObjectType({ 10 | name: 'Person', 11 | fields: { 12 | id: { type: GraphQLID }, 13 | name: { type: GraphQLString }, 14 | }, 15 | }); 16 | 17 | const peopleData = [ 18 | { id: '1', name: 'John Smith' }, 19 | { id: '2', name: 'Sara Smith' }, 20 | { id: '3', name: 'Budd Deey' }, 21 | ]; 22 | 23 | const QueryType = new GraphQLObjectType({ 24 | name: 'Query', 25 | fields: { 26 | people: { 27 | type: new GraphQLList(PersonType), 28 | resolve: () => peopleData, 29 | }, 30 | }, 31 | }); 32 | 33 | export const schema = new GraphQLSchema({ query: QueryType }); 34 | -------------------------------------------------------------------------------- /packages/apollo-fragment-vue/example/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import App from './App.vue'; 3 | import { ApolloClient } from 'apollo-client'; 4 | import { ApolloLink } from 'apollo-link'; 5 | import { InMemoryCache } from 'apollo-cache-inmemory'; 6 | import VueApollo from 'vue-apollo'; 7 | import { 8 | fragmentCacheRedirect, 9 | fragmentLinkState, 10 | } from 'apollo-link-state-fragment'; 11 | import ApolloFragment from '../../lib'; 12 | import { link } from './graphql/link'; 13 | 14 | Vue.config.productionTip = false; 15 | 16 | const cache = new InMemoryCache({ 17 | cacheRedirects: { 18 | Query: { 19 | ...fragmentCacheRedirect(), 20 | }, 21 | }, 22 | }); 23 | 24 | const client = new ApolloClient({ 25 | cache, 26 | link: ApolloLink.from([fragmentLinkState(cache), link]), 27 | }); 28 | 29 | // Install the vue plugin 30 | Vue.use(VueApollo); 31 | Vue.use(ApolloFragment); 32 | 33 | const apolloProvider = new VueApollo({ 34 | defaultClient: client, 35 | }); 36 | 37 | new Vue({ 38 | render: h => h(App), 39 | provide: apolloProvider.provide(), 40 | }).$mount('#app'); 41 | -------------------------------------------------------------------------------- /packages/apollo-fragment-vue/example/vue.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | chainWebpack: config => { 3 | const lintRule = config.module.rule('eslint'); 4 | 5 | lintRule.uses.clear(); 6 | config.resolve.extensions.prepend('.ts'); 7 | config.resolve.extensions.prepend('.mjs'); 8 | 9 | config.module 10 | .rule('mjs') 11 | .test(/\.mjs$/) 12 | .type('javascript/auto'); 13 | 14 | config.module 15 | .rule('ts') 16 | .test(/\.tsx?$/) 17 | .use('ts-loader') 18 | .loader('ts-loader'); 19 | }, 20 | }; 21 | -------------------------------------------------------------------------------- /packages/apollo-fragment-vue/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "apollo-fragment-vue", 3 | "version": "0.1.5", 4 | "description": "A Query component to connect Vue components to GraphQL fragments", 5 | "author": "Abhi Aiyer ", 6 | "license": "MIT", 7 | "main": "./lib/bundle.umd.js", 8 | "module": "./lib/index.js", 9 | "jsnext:main": "./lib/index.js", 10 | "typings": "./lib/index.d.ts", 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/abhiaiyer91/apollo-fragment.git" 14 | }, 15 | "bugs": { 16 | "url": "https://github.com/abhiaiyer91/apollo-fragment/issues" 17 | }, 18 | "homepage": "https://github.com/abhiaiyer91/apollo-fragment#readme", 19 | "scripts": { 20 | "build:browser": "browserify ./lib/bundle.umd.js -o=./lib/bundle.js --i apollo-link --i apollo-utilities --i graphql-anywhere && npm run minify:browser", 21 | "build": "tsc -p .", 22 | "bundle": "rollup -c", 23 | "clean": "rimraf lib/* && rimraf coverage/*", 24 | "filesize": "npm run build && npm run build:browser && bundlesize", 25 | "prelint": "npm run lint-fix", 26 | "lint-fix": "prettier --trailing-comma all --single-quote --write \"src/**/*.{j,t}s*\"", 27 | "lint": "tslint --type-check -p tsconfig.json -c tslint.json src/*.ts", 28 | "lint-staged": "lint-staged", 29 | "minify:browser": "uglifyjs -c -m -o ./lib/bundle.min.js -- ./lib/bundle.js", 30 | "postbuild": "npm run bundle", 31 | "prebuild": "npm run clean", 32 | "prepublishOnly": "npm run clean && npm run build", 33 | "test": "jest", 34 | "coverage": "npm run lint && jest --coverage", 35 | "watch": "trap 'kill -9 %1' SIGINT; tsc -w -p . & rollup -w -c" 36 | }, 37 | "bundlesize": [ 38 | { 39 | "name": "apollo-fragment-vue", 40 | "path": "./lib/bundle.min.js", 41 | "threshold": "1 Kb" 42 | } 43 | ], 44 | "peerDependencies": { 45 | "apollo-link-state": "0.4.1", 46 | "vue-apollo": "^3.0.0-beta.19" 47 | }, 48 | "devDependencies": { 49 | "@types/graphql": "0.11.5", 50 | "@types/jest": "22.1.x", 51 | "apollo-cache-inmemory": "^1.1.5", 52 | "apollo-client": "^2.2.0", 53 | "apollo-link": "^1.0.0", 54 | "browserify": "14.5.0", 55 | "bundlesize": "0.15.3", 56 | "codecov": "3.0.0", 57 | "danger": "1.2.0", 58 | "graphql": "0.11.7", 59 | "graphql-tag": "2.9.2", 60 | "lint-staged": "4.3.0", 61 | "pre-commit": "1.2.2", 62 | "prettier": "1.7.4", 63 | "rimraf": "2.6.1", 64 | "rollup": "0.56.x", 65 | "rollup-plugin-local-resolve": "1.0.x", 66 | "rollup-plugin-sourcemaps": "0.4.x", 67 | "ts-jest": "21.1.4", 68 | "tslint": "5.8.0", 69 | "typescript": "^4.1.2", 70 | "uglify-js": "3.1.5", 71 | "vue": "^2.5.16", 72 | "vue-apollo": "3.0.0-beta.19" 73 | }, 74 | "dependencies": { 75 | "apollo-fragment-utils": "^0.2.1", 76 | "apollo-utilities": "^1.0.12" 77 | }, 78 | "lint-staged": { 79 | "*.ts*": [ 80 | "prettier --trailing-comma all --single-quote --write", 81 | "git add" 82 | ], 83 | "*.js*": [ 84 | "prettier --trailing-comma all --single-quote --write", 85 | "git add" 86 | ], 87 | "*.json*": [ 88 | "prettier --write", 89 | "git add" 90 | ] 91 | }, 92 | "pre-commit": "lint-staged" 93 | } 94 | -------------------------------------------------------------------------------- /packages/apollo-fragment-vue/rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from 'rollup-plugin-local-resolve'; 2 | import sourcemaps from 'rollup-plugin-sourcemaps'; 3 | 4 | const globals = { 5 | // Apollo 6 | 'apollo-client': 'apollo.core', 7 | 'apollo-cache': 'apolloCache.core', 8 | 'apollo-link': 'apolloLink.core', 9 | 'apollo-utilities': 'apollo.utilities', 10 | 11 | 'graphql-anywhere/lib/async': 'graphqlAnywhere.async', 12 | }; 13 | 14 | export default { 15 | input: 'lib/index.js', 16 | output: { 17 | file: 'lib/bundle.umd.js', 18 | format: 'umd', 19 | name: 'apolloFragmentReact.state', 20 | exports: 'named', 21 | sourcemap: true, 22 | globals, 23 | }, 24 | external: Object.keys(globals), 25 | onwarn, 26 | plugins: [resolve(), sourcemaps()], 27 | }; 28 | 29 | function onwarn(message) { 30 | const suppressed = ['UNRESOLVED_IMPORT', 'THIS_IS_UNDEFINED']; 31 | 32 | if (!suppressed.find(code => message.code === code)) { 33 | return console.warn(message.message); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/apollo-fragment-vue/src/ApolloFragment.js: -------------------------------------------------------------------------------- 1 | import { ApolloQuery } from 'vue-apollo'; 2 | import { getFragmentInfo, buildFragmentQuery } from 'apollo-fragment-utils'; 3 | 4 | function isDataFilled(data) { 5 | return Object.keys(data).length > 0; 6 | } 7 | 8 | const CApolloFragment = { 9 | ...ApolloQuery, 10 | name: 'ApolloFragment', 11 | props: { 12 | fragment: { 13 | type: String, 14 | required: true, 15 | }, 16 | id: { 17 | type: String, 18 | required: true, 19 | }, 20 | tag: { 21 | type: String, 22 | default: 'div', 23 | }, 24 | }, 25 | data(props) { 26 | const { id, fragment } = this.$options.propsData; 27 | const { fragmentTypeName, fragmentName } = getFragmentInfo(fragment); 28 | const query = buildFragmentQuery({ fragment, fragmentName }); 29 | 30 | return { 31 | ...ApolloQuery.data(), 32 | query, 33 | variables: { 34 | id, 35 | __typename: fragmentTypeName, 36 | }, 37 | }; 38 | }, 39 | apollo: { 40 | $client() { 41 | return this.clientId; 42 | }, 43 | query() { 44 | return { 45 | ...ApolloQuery.apollo.query(), 46 | query() { 47 | return this.$data.query; 48 | }, 49 | variables() { 50 | return this.$data.variables; 51 | }, 52 | skip: false, 53 | fetchPolicy: 'cache-only', 54 | result(result) { 55 | const { errors, loading, networkStatus } = result; 56 | let { error } = result; 57 | result = Object.assign({}, result); 58 | 59 | if (errors && errors.length) { 60 | error = new Error(`Apollo errors occured (${errors.length})`); 61 | error.graphQLErrors = errors; 62 | } 63 | 64 | let data = {}; 65 | 66 | if (loading) { 67 | Object.assign(data, this.$_previousData, result.data); 68 | } else if (error) { 69 | Object.assign( 70 | data, 71 | this.$apollo.queries.query.observer.getLastResult() || {}, 72 | result.data, 73 | ); 74 | } else { 75 | data = result.data; 76 | this.$_previousData = result.data; 77 | } 78 | 79 | this.result = { 80 | data: isDataFilled(data) ? data.getFragment : undefined, 81 | loading, 82 | error, 83 | networkStatus, 84 | }; 85 | 86 | this.times = ++this.$_times; 87 | 88 | this.$emit('result', this.result); 89 | }, 90 | error(error) { 91 | this.result.loading = false; 92 | this.result.error = error; 93 | this.$emit('error', error); 94 | }, 95 | }; 96 | }, 97 | }, 98 | }; 99 | 100 | export default CApolloFragment; 101 | -------------------------------------------------------------------------------- /packages/apollo-fragment-vue/src/index.ts: -------------------------------------------------------------------------------- 1 | import CApolloFragment from './ApolloFragment'; 2 | 3 | function install(Vue) { 4 | Vue.component('apollo-fragment', CApolloFragment); 5 | Vue.component('ApolloFragment', CApolloFragment); 6 | } 7 | 8 | CApolloFragment.install = install; 9 | 10 | // Components 11 | export default CApolloFragment; 12 | -------------------------------------------------------------------------------- /packages/apollo-fragment-vue/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig", 3 | "compilerOptions": { 4 | "rootDir": "./src", 5 | "outDir": "lib", 6 | "allowJs": true 7 | }, 8 | "include": ["./src/*.ts"], 9 | "exclude": ["./node_modules", "./dist", "./lib"] 10 | } 11 | -------------------------------------------------------------------------------- /packages/apollo-fragment-vue/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "ban": false, 4 | "class-name": true, 5 | "eofline": true, 6 | "forin": true, 7 | "interface-name": [true, "never-prefix"], 8 | "jsdoc-format": true, 9 | "label-position": true, 10 | "member-access": true, 11 | "member-ordering": [ 12 | true, 13 | { 14 | "order": [ 15 | "static-field", 16 | "instance-field", 17 | "constructor", 18 | "public-instance-method", 19 | "protected-instance-method", 20 | "private-instance-method" 21 | ] 22 | } 23 | ], 24 | "no-any": false, 25 | "no-arg": true, 26 | "no-bitwise": true, 27 | "no-conditional-assignment": true, 28 | "no-consecutive-blank-lines": false, 29 | "no-console": [true, "log", "debug", "info", "time", "timeEnd", "trace"], 30 | "no-construct": true, 31 | "no-debugger": true, 32 | "no-duplicate-variable": true, 33 | "no-empty": true, 34 | "no-eval": true, 35 | "no-inferrable-types": false, 36 | "no-internal-module": true, 37 | "no-null-keyword": false, 38 | "no-require-imports": false, 39 | "no-shadowed-variable": true, 40 | "no-switch-case-fall-through": true, 41 | "no-trailing-whitespace": true, 42 | "no-unused-expression": true, 43 | "no-var-keyword": true, 44 | "no-var-requires": true, 45 | "object-literal-sort-keys": false, 46 | "radix": true, 47 | "switch-default": true, 48 | "triple-equals": [true, "allow-null-check"], 49 | "typedef": [ 50 | false, 51 | "call-signature", 52 | "parameter", 53 | "arrow-parameter", 54 | "property-declaration", 55 | "variable-declaration", 56 | "member-variable-declaration" 57 | ], 58 | "variable-name": [ 59 | true, 60 | "check-format", 61 | "allow-leading-underscore", 62 | "ban-keywords" 63 | ] 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /packages/apollo-link-state-fragment/.npmignore: -------------------------------------------------------------------------------- 1 | dist/tests/ 2 | src/ 3 | tests/ 4 | .travis.yml 5 | tsconfig.json 6 | tslint.json 7 | typings.d.ts 8 | -------------------------------------------------------------------------------- /packages/apollo-link-state-fragment/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "apollo-link-state-fragment", 3 | "version": "0.1.8", 4 | "description": "A library to read fragments data in the apollo cache", 5 | "author": "Abhi Aiyer ", 6 | "license": "MIT", 7 | "main": "./lib/bundle.umd.js", 8 | "module": "./lib/index.js", 9 | "jsnext:main": "./lib/index.js", 10 | "typings": "./lib/index.d.ts", 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/abhiaiyer91/apollo-fragment.git" 14 | }, 15 | "bugs": { 16 | "url": "https://github.com/abhiaiyer91/apollo-fragment/issues" 17 | }, 18 | "homepage": "https://github.com/abhiaiyer91/apollo-fragment#readme", 19 | "scripts": { 20 | "build:browser": "browserify ./lib/bundle.umd.js -o=./lib/bundle.js --i apollo-link --i apollo-utilities --i graphql-anywhere && npm run minify:browser", 21 | "build": "tsc -p .", 22 | "bundle": "rollup -c", 23 | "clean": "rimraf lib/* && rimraf coverage/*", 24 | "filesize": "npm run build && npm run build:browser && bundlesize", 25 | "prelint": "npm run lint-fix", 26 | "lint-fix": "prettier --trailing-comma all --single-quote --write \"src/**/*.{j,t}s*\"", 27 | "lint": "tslint --type-check -p tsconfig.json -c tslint.json src/*.ts", 28 | "lint-staged": "lint-staged", 29 | "minify:browser": "uglifyjs -c -m -o ./lib/bundle.min.js -- ./lib/bundle.js", 30 | "postbuild": "npm run bundle", 31 | "prebuild": "npm run clean", 32 | "prepublishOnly": "npm run clean && npm run build", 33 | "test": "jest", 34 | "coverage": "npm run lint && jest --coverage", 35 | "watch": "trap 'kill -9 %1' SIGINT; tsc -w -p . & rollup -w -c" 36 | }, 37 | "bundlesize": [ 38 | { 39 | "name": "apollo-link-state-fragment", 40 | "path": "./lib/bundle.min.js", 41 | "threshold": "1 Kb" 42 | } 43 | ], 44 | "peerDependencies": { 45 | "apollo-link-state": "0.4.1" 46 | }, 47 | "devDependencies": { 48 | "@types/graphql": "0.11.5", 49 | "@types/jest": "22.1.x", 50 | "apollo-cache-inmemory": "^1.1.5", 51 | "apollo-client": "^2.2.0", 52 | "apollo-link": "^1.0.0", 53 | "apollo-link-state": "^0.4.1", 54 | "browserify": "14.5.0", 55 | "bundlesize": "0.15.3", 56 | "codecov": "3.0.0", 57 | "danger": "1.2.0", 58 | "graphql": "0.11.7", 59 | "graphql-tag": "2.5.0", 60 | "jest": "21.2.1", 61 | "lint-staged": "4.3.0", 62 | "pre-commit": "1.2.2", 63 | "prettier": "1.7.4", 64 | "rimraf": "2.6.1", 65 | "rollup": "0.56.x", 66 | "rollup-plugin-local-resolve": "1.0.x", 67 | "rollup-plugin-sourcemaps": "0.4.x", 68 | "ts-jest": "21.1.4", 69 | "tslint": "5.8.0", 70 | "typescript": "^4.1.2", 71 | "uglify-js": "3.1.5" 72 | }, 73 | "jest": { 74 | "mapCoverage": true, 75 | "transform": { 76 | ".(ts|tsx)": "/node_modules/ts-jest/preprocessor.js" 77 | }, 78 | "testRegex": "(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$", 79 | "moduleFileExtensions": [ 80 | "ts", 81 | "tsx", 82 | "js", 83 | "json" 84 | ] 85 | }, 86 | "dependencies": { 87 | "apollo-utilities": "^1.0.12" 88 | }, 89 | "lint-staged": { 90 | "*.ts*": [ 91 | "prettier --trailing-comma all --single-quote --write", 92 | "git add" 93 | ], 94 | "*.js*": [ 95 | "prettier --trailing-comma all --single-quote --write", 96 | "git add" 97 | ], 98 | "*.json*": [ 99 | "prettier --write", 100 | "git add" 101 | ] 102 | }, 103 | "pre-commit": "lint-staged" 104 | } 105 | -------------------------------------------------------------------------------- /packages/apollo-link-state-fragment/rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from 'rollup-plugin-local-resolve'; 2 | import sourcemaps from 'rollup-plugin-sourcemaps'; 3 | 4 | const globals = { 5 | // Apollo 6 | 'apollo-client': 'apollo.core', 7 | 'apollo-cache': 'apolloCache.core', 8 | 'apollo-link': 'apolloLink.core', 9 | 'apollo-utilities': 'apollo.utilities', 10 | 11 | 'graphql-anywhere/lib/async': 'graphqlAnywhere.async', 12 | }; 13 | 14 | export default { 15 | input: 'lib/index.js', 16 | output: { 17 | file: 'lib/bundle.umd.js', 18 | format: 'umd', 19 | name: 'apolloLinkStateFragment.state', 20 | exports: 'named', 21 | sourcemap: true, 22 | globals, 23 | }, 24 | external: Object.keys(globals), 25 | onwarn, 26 | plugins: [resolve(), sourcemaps()], 27 | }; 28 | 29 | function onwarn(message) { 30 | const suppressed = ['UNRESOLVED_IMPORT', 'THIS_IS_UNDEFINED']; 31 | 32 | if (!suppressed.find(code => message.code === code)) { 33 | return console.warn(message.message); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/apollo-link-state-fragment/src/__tests__/index.ts: -------------------------------------------------------------------------------- 1 | import gql from 'graphql-tag'; 2 | import { ApolloLink, execute, Observable } from 'apollo-link'; 3 | import { ApolloClient } from 'apollo-client'; 4 | import { InMemoryCache } from 'apollo-cache-inmemory'; 5 | import mockLink from '../mocks/mockLink'; 6 | import { fragmentCacheRedirect, fragmentLinkState } from '../'; 7 | 8 | describe('Basic Usage', () => { 9 | it('Will return null when fragment data is missing from cache', () => { 10 | const query = gql` 11 | { 12 | getFragment(id: 1, __typename: "Person") @client { 13 | id 14 | name 15 | } 16 | } 17 | `; 18 | 19 | const cache = new InMemoryCache({ 20 | cacheRedirects: { 21 | Query: { 22 | ...fragmentCacheRedirect(cache), 23 | }, 24 | }, 25 | }); 26 | 27 | const local = fragmentLinkState(cache); 28 | 29 | const client = new ApolloClient({ 30 | cache, 31 | link: ApolloLink.from([local, mockLink]), 32 | }); 33 | 34 | return client.query({ query }).then(({ data }) => { 35 | expect(data).toEqual(null); 36 | }); 37 | }); 38 | 39 | it('Will return fragment data once in cache', function() { 40 | const query = gql` 41 | { 42 | getFragment(id: 1, __typename: "Person") @client { 43 | id 44 | name 45 | } 46 | } 47 | `; 48 | 49 | const cache = new InMemoryCache({ 50 | cacheRedirects: { 51 | Query: { 52 | ...fragmentCacheRedirect(), 53 | }, 54 | }, 55 | }); 56 | 57 | const local = fragmentLinkState(cache); 58 | 59 | const client = new ApolloClient({ 60 | cache, 61 | link: ApolloLink.from([local, mockLink]), 62 | }); 63 | 64 | return client 65 | .query({ 66 | query: gql` 67 | query peeps { 68 | people { 69 | id 70 | name 71 | } 72 | } 73 | `, 74 | }) 75 | .then(result => { 76 | return client.query({ query }).then(({ data }) => { 77 | expect(data.getFragment.id).toEqual('1'); 78 | }); 79 | }); 80 | }); 81 | }); 82 | -------------------------------------------------------------------------------- /packages/apollo-link-state-fragment/src/index.ts: -------------------------------------------------------------------------------- 1 | import { toIdValue, IdValue } from 'apollo-utilities'; 2 | import { ApolloLink } from 'apollo-link'; 3 | import { withClientState } from 'apollo-link-state'; 4 | import { ApolloCache } from 'apollo-cache'; 5 | import { defaultDataIdFromObject } from 'apollo-cache-inmemory'; 6 | 7 | type GetFragmentType = { 8 | __typename: string; 9 | id: string; 10 | }; 11 | 12 | export function fragmentCacheRedirect( 13 | dataIdFromObject = defaultDataIdFromObject, 14 | ) { 15 | return { 16 | getFragment: (_: any, { id, __typename }: GetFragmentType): IdValue => { 17 | return toIdValue(dataIdFromObject({ __typename, id })); 18 | }, 19 | }; 20 | } 21 | 22 | export function fragmentLinkState(apolloCache: ApolloCache): ApolloLink { 23 | return withClientState({ 24 | cache: apolloCache, 25 | resolvers: { 26 | Query: { 27 | getFragment: ( 28 | _: any, 29 | { __typename, id }: GetFragmentType, 30 | { cache }: { cache: any }, 31 | ) => { 32 | const fragmentId = cache.config.dataIdFromObject({ __typename, id }); 33 | return cache.data.data[fragmentId]; 34 | }, 35 | }, 36 | }, 37 | }); 38 | } 39 | -------------------------------------------------------------------------------- /packages/apollo-link-state-fragment/src/mocks/mockLink.ts: -------------------------------------------------------------------------------- 1 | import { graphql, print } from 'graphql'; 2 | import { 3 | Operation, 4 | GraphQLRequest, 5 | ApolloLink, 6 | FetchResult, 7 | Observable, 8 | // Observer, 9 | } from 'apollo-link'; 10 | import { schema } from './mockSchema'; 11 | 12 | export default new ApolloLink(operation => { 13 | return new Observable(observer => { 14 | const { query, operationName, variables } = operation; 15 | delay(300) 16 | .then(() => 17 | graphql(schema, print(query), null, null, variables, operationName), 18 | ) 19 | .then(result => { 20 | observer.next(result); 21 | observer.complete(); 22 | }) 23 | .catch(observer.error.bind(observer)); 24 | }); 25 | }); 26 | 27 | function delay(ms: number) { 28 | return new Promise(resolve => { 29 | setTimeout(() => { 30 | resolve(); 31 | }, ms); 32 | }); 33 | } 34 | -------------------------------------------------------------------------------- /packages/apollo-link-state-fragment/src/mocks/mockSchema.ts: -------------------------------------------------------------------------------- 1 | import { 2 | GraphQLSchema, 3 | GraphQLObjectType, 4 | GraphQLID, 5 | GraphQLString, 6 | GraphQLList, 7 | } from 'graphql'; 8 | 9 | const PersonType = new GraphQLObjectType({ 10 | name: 'Person', 11 | fields: { 12 | id: { type: GraphQLID }, 13 | name: { type: GraphQLString }, 14 | }, 15 | }); 16 | 17 | const peopleData = [ 18 | { id: 1, name: 'John Smith' }, 19 | { id: 2, name: 'Sara Smith' }, 20 | { id: 3, name: 'Budd Deey' }, 21 | ]; 22 | 23 | const QueryType = new GraphQLObjectType({ 24 | name: 'Query', 25 | fields: { 26 | people: { 27 | type: new GraphQLList(PersonType), 28 | resolve: () => peopleData, 29 | }, 30 | }, 31 | }); 32 | 33 | export const schema = new GraphQLSchema({ query: QueryType }); 34 | -------------------------------------------------------------------------------- /packages/apollo-link-state-fragment/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig", 3 | "compilerOptions": { 4 | "declaration": true, 5 | "rootDir": "./src", 6 | "outDir": "lib" 7 | }, 8 | "include": ["./src/index.ts"], 9 | "exclude": ["src/**/__tests__/*.ts", "./src/mocks"] 10 | } 11 | -------------------------------------------------------------------------------- /packages/apollo-link-state-fragment/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | "ban": false, 4 | "class-name": true, 5 | "eofline": true, 6 | "forin": true, 7 | "interface-name": [true, "never-prefix"], 8 | "jsdoc-format": true, 9 | "label-position": true, 10 | "member-access": true, 11 | "member-ordering": [ 12 | true, 13 | { 14 | "order": [ 15 | "static-field", 16 | "instance-field", 17 | "constructor", 18 | "public-instance-method", 19 | "protected-instance-method", 20 | "private-instance-method" 21 | ] 22 | } 23 | ], 24 | "no-any": false, 25 | "no-arg": true, 26 | "no-bitwise": true, 27 | "no-conditional-assignment": true, 28 | "no-consecutive-blank-lines": false, 29 | "no-console": [true, "log", "debug", "info", "time", "timeEnd", "trace"], 30 | "no-construct": true, 31 | "no-debugger": true, 32 | "no-duplicate-variable": true, 33 | "no-empty": true, 34 | "no-eval": true, 35 | "no-inferrable-types": false, 36 | "no-internal-module": true, 37 | "no-null-keyword": false, 38 | "no-require-imports": false, 39 | "no-shadowed-variable": true, 40 | "no-switch-case-fall-through": true, 41 | "no-trailing-whitespace": true, 42 | "no-unused-expression": true, 43 | "no-var-keyword": true, 44 | "no-var-requires": true, 45 | "object-literal-sort-keys": false, 46 | "radix": true, 47 | "switch-default": true, 48 | "triple-equals": [true, "allow-null-check"], 49 | "typedef": [ 50 | false, 51 | "call-signature", 52 | "parameter", 53 | "arrow-parameter", 54 | "property-declaration", 55 | "variable-declaration", 56 | "member-variable-declaration" 57 | ], 58 | "variable-name": [ 59 | true, 60 | "check-format", 61 | "allow-leading-underscore", 62 | "ban-keywords" 63 | ] 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["es6", "dom"], 5 | "module": "es2015", 6 | "moduleResolution": "node", 7 | "removeComments": true, 8 | "sourceMap": true, 9 | "declaration": false, 10 | "noImplicitAny": false, 11 | "noUnusedParameters": false, 12 | "noUnusedLocals": true, 13 | "skipLibCheck": true 14 | } 15 | } 16 | --------------------------------------------------------------------------------