├── .npmignore ├── dist ├── WebpackLogger.js ├── getRelayCompilerPluginHooks.js ├── getFilepathsFromGlob.js ├── getSchemaSource.js ├── createRaiseErrorsReporter.js ├── getWriter.js └── index.js ├── .gitignore ├── .eslintrc ├── .flowconfig ├── src ├── WebpackLogger.js ├── getFilepathsFromGlob.js ├── getSchemaSource.js ├── getRelayCompilerPluginHooks.js ├── createRaiseErrorsReporter.js ├── getWriter.js └── index.js ├── test ├── fixtureProject │ ├── src │ │ ├── entry.js │ │ ├── components │ │ │ ├── HomeItem.js │ │ │ ├── Home.js │ │ │ ├── App.js │ │ │ └── About.js │ │ └── mutations │ │ │ └── updateFirstNameMutation.js │ ├── .babelrc.js │ ├── createWebpackConfig.js │ └── schema.json ├── support │ ├── createTempFixtureProject.js │ └── normaliseConfigForWebpackVersion.js ├── hooks.test.js ├── invalidArgs.test.js ├── normalCase.test.js └── __snapshots__ │ └── normalCase.test.js.snap ├── .github └── workflows │ └── build.yml ├── LICENSE ├── package.json └── README.md /.npmignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.log 3 | -------------------------------------------------------------------------------- /dist/WebpackLogger.js: -------------------------------------------------------------------------------- 1 | "use strict"; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | /test/temp 3 | /reports 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": [ 4 | "standard", 5 | "airbnb-base" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | .*/test/.* 3 | 4 | [include] 5 | 6 | [libs] 7 | 8 | [options] 9 | esproposal.optional_chaining=enable 10 | 11 | [version] 12 | ^0.120.0 13 | -------------------------------------------------------------------------------- /src/WebpackLogger.js: -------------------------------------------------------------------------------- 1 | export interface WebpackLogger { 2 | log(any): void; 3 | info(any): void; 4 | warn(any): void; 5 | error(any): void; 6 | debug(any): void; 7 | } 8 | -------------------------------------------------------------------------------- /test/fixtureProject/src/entry.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom' 3 | import App from './components/App' 4 | 5 | ReactDOM.render(, document.getElementById('main')) 6 | -------------------------------------------------------------------------------- /test/support/createTempFixtureProject.js: -------------------------------------------------------------------------------- 1 | import { copySync, removeSync } from 'fs-extra' 2 | import path from "path" 3 | 4 | export default function(name) { 5 | const normalCaseDir = path.resolve(__dirname, '..', 'fixtureProject') 6 | const tempDir = path.resolve(__dirname, '..', 'temp', name) 7 | removeSync(tempDir) 8 | copySync(normalCaseDir, tempDir) 9 | return tempDir 10 | } 11 | -------------------------------------------------------------------------------- /test/support/normaliseConfigForWebpackVersion.js: -------------------------------------------------------------------------------- 1 | /* global require */ 2 | 3 | const webpackMajorVersion = Number( 4 | require('webpack/package.json').version.split('.')[0] 5 | ) 6 | 7 | if (isNaN(webpackMajorVersion)) { 8 | throw new Error('Cannot parse webpack major version') 9 | } 10 | 11 | export default config => { 12 | if (webpackMajorVersion < 4) { 13 | delete config.mode 14 | } 15 | return config 16 | } 17 | -------------------------------------------------------------------------------- /test/fixtureProject/.babelrc.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | module.exports = { 4 | "env": { 5 | "artifactDirectoryTest": { 6 | "plugins": [ 7 | ["relay", { 8 | "artifactDirectory": path.resolve(__dirname, "src", "__generated__") 9 | }] 10 | ], 11 | } 12 | }, 13 | "plugins": ["relay"], 14 | "presets": [ 15 | "@babel/preset-react", 16 | "@babel/preset-env" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /test/fixtureProject/src/components/HomeItem.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { graphql, createFragmentContainer } from 'react-relay' 3 | 4 | const HomeItem = ({ person: { id, fullName } }) => ( 5 |
  • 6 | {fullName} 7 |
  • 8 | ) 9 | 10 | export default createFragmentContainer( 11 | HomeItem, 12 | graphql` 13 | fragment HomeItem_person on Person { 14 | id 15 | fullName 16 | } 17 | ` 18 | ) 19 | -------------------------------------------------------------------------------- /test/fixtureProject/src/components/Home.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { graphql, createFragmentContainer } from 'react-relay' 3 | import HomeItem from './HomeItem' 4 | 5 | const Home = ({ people }) => ( 6 |
    7 |

    Home

    8 | 13 |
    14 | ) 15 | 16 | export default createFragmentContainer( 17 | Home, 18 | graphql` 19 | fragment Home_people on Person @relay(plural: true) { 20 | id 21 | ...HomeItem_person 22 | } 23 | ` 24 | ) 25 | -------------------------------------------------------------------------------- /src/getFilepathsFromGlob.js: -------------------------------------------------------------------------------- 1 | import glob from 'fast-glob'; 2 | 3 | export default function getFilepathsFromGlob( 4 | baseDir, 5 | options: { 6 | extensions: Array, 7 | include: Array, 8 | exclude: Array 9 | }, 10 | ): Array { 11 | const { extensions, include, exclude } = options; 12 | const patterns = include.map((inc) => `${inc}/*.+(${extensions.join('|')})`); 13 | 14 | return glob.sync(patterns, { 15 | cwd: baseDir, 16 | bashNative: [], 17 | onlyFiles: true, 18 | dot: true, // match behavior of watchman from relay-compiler 19 | ignore: exclude, 20 | }); 21 | } 22 | -------------------------------------------------------------------------------- /test/fixtureProject/src/mutations/updateFirstNameMutation.js: -------------------------------------------------------------------------------- 1 | import { commitMutation, graphql } from 'react-relay' 2 | 3 | const mutation = graphql` 4 | mutation updateFirstNameMutation($id: ID!, $firstName: String!) { 5 | updateFirstName(id: $id, firstName: $firstName) { 6 | person { 7 | firstName 8 | fullName 9 | } 10 | } 11 | } 12 | ` 13 | 14 | export default (environment, variables) => { 15 | return new Promise((resolve, reject) => { 16 | commitMutation(environment, { 17 | mutation, 18 | variables, 19 | onError: reject, 20 | onCompleted: resolve 21 | }) 22 | }) 23 | } 24 | -------------------------------------------------------------------------------- /test/fixtureProject/createWebpackConfig.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | 3 | module.exports = ({ relayCompilerWebpackPlugin, plugins = [] }) => ({ 4 | mode: 'production', 5 | context: __dirname, 6 | entry: './src/entry.js', 7 | output: { 8 | path: path.resolve('dist'), 9 | filename: 'index.js' 10 | }, 11 | performance: { 12 | hints: false 13 | }, 14 | module: { 15 | rules: [ 16 | { 17 | test: /\.js$/, 18 | exclude: path.join(__dirname, 'node_modules'), 19 | use: [{ loader: 'babel-loader' }] 20 | } 21 | ] 22 | }, 23 | plugins: [relayCompilerWebpackPlugin, ...plugins] 24 | }) 25 | -------------------------------------------------------------------------------- /src/getSchemaSource.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import fs from 'fs'; 4 | import { buildClientSchema, printSchema, Source } from 'graphql'; 5 | import path from 'path'; 6 | 7 | // Taken from relay-compiler/bin/RelayCompilerMain.js 8 | export default function getSchemaSource(schemaPath: string): Source { 9 | let source = fs.readFileSync(schemaPath, 'utf8'); 10 | if (path.extname(schemaPath) === '.json') { 11 | source = printSchema(buildClientSchema(JSON.parse(source).data)); 12 | } 13 | source = ` 14 | directive @include(if: Boolean) on FRAGMENT_SPREAD | FIELD 15 | directive @skip(if: Boolean) on FRAGMENT_SPREAD | FIELD 16 | 17 | ${source} 18 | `; 19 | return new Source(source, schemaPath); 20 | } 21 | -------------------------------------------------------------------------------- /dist/getRelayCompilerPluginHooks.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.default = _default; 7 | 8 | var _tapable = require("tapable"); 9 | 10 | const relayCompilerPluginHooksMap = new WeakMap(); 11 | 12 | function createRelayCompilerPluginHooks() { 13 | return { 14 | beforeWrite: new _tapable.AsyncSeriesWaterfallHook(['pluginArgs']), 15 | afterWrite: new _tapable.AsyncSeriesWaterfallHook(['pluginArgs']) 16 | }; 17 | } 18 | 19 | function _default(compilation) { 20 | let hooks = relayCompilerPluginHooksMap.get(compilation); 21 | 22 | if (!hooks) { 23 | hooks = createRelayCompilerPluginHooks(); 24 | relayCompilerPluginHooksMap.set(compilation, hooks); 25 | } 26 | 27 | return hooks; 28 | } -------------------------------------------------------------------------------- /dist/getFilepathsFromGlob.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.default = getFilepathsFromGlob; 7 | 8 | var _fastGlob = _interopRequireDefault(require("fast-glob")); 9 | 10 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 11 | 12 | function getFilepathsFromGlob(baseDir, options) { 13 | const { 14 | extensions, 15 | include, 16 | exclude 17 | } = options; 18 | const patterns = include.map(inc => `${inc}/*.+(${extensions.join('|')})`); 19 | return _fastGlob.default.sync(patterns, { 20 | cwd: baseDir, 21 | bashNative: [], 22 | onlyFiles: true, 23 | dot: true, 24 | // match behavior of watchman from relay-compiler 25 | ignore: exclude 26 | }); 27 | } -------------------------------------------------------------------------------- /test/fixtureProject/src/components/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Home from './Home' 3 | import About from './About' 4 | import { graphql, QueryRenderer } from 'react-relay' 5 | 6 | export default () => ( 7 | { 17 | if (error) { 18 | console.error(error) 19 | return
    Error
    20 | } 21 | 22 | if (!props) { 23 | return
    Loading
    24 | } 25 | 26 | const { people } = props 27 | return ( 28 |
    29 | 30 | 31 |
    32 | ) 33 | }} 34 | /> 35 | ) 36 | -------------------------------------------------------------------------------- /src/getRelayCompilerPluginHooks.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { AsyncSeriesWaterfallHook } from 'tapable'; 4 | import type { Compilation } from 'webpack'; 5 | 6 | const relayCompilerPluginHooksMap = new WeakMap(); 7 | 8 | export interface PluginHooks { 9 | beforeWrite: AsyncSeriesWaterfallHook, 10 | afterWrite: AsyncSeriesWaterfallHook 11 | } 12 | 13 | function createRelayCompilerPluginHooks(): PluginHooks { 14 | return { 15 | beforeWrite: new AsyncSeriesWaterfallHook(['pluginArgs']), 16 | afterWrite: new AsyncSeriesWaterfallHook(['pluginArgs']), 17 | }; 18 | } 19 | 20 | export default function (compilation: Compilation): PluginHooks { 21 | let hooks = relayCompilerPluginHooksMap.get(compilation); 22 | if (!hooks) { 23 | hooks = createRelayCompilerPluginHooks(); 24 | relayCompilerPluginHooksMap.set(compilation, hooks); 25 | } 26 | return hooks; 27 | } 28 | -------------------------------------------------------------------------------- /dist/getSchemaSource.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.default = getSchemaSource; 7 | 8 | var _fs = _interopRequireDefault(require("fs")); 9 | 10 | var _graphql = require("graphql"); 11 | 12 | var _path = _interopRequireDefault(require("path")); 13 | 14 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 15 | 16 | // Taken from relay-compiler/bin/RelayCompilerMain.js 17 | function getSchemaSource(schemaPath) { 18 | let source = _fs.default.readFileSync(schemaPath, 'utf8'); 19 | 20 | if (_path.default.extname(schemaPath) === '.json') { 21 | source = (0, _graphql.printSchema)((0, _graphql.buildClientSchema)(JSON.parse(source).data)); 22 | } 23 | 24 | source = ` 25 | directive @include(if: Boolean) on FRAGMENT_SPREAD | FIELD 26 | directive @skip(if: Boolean) on FRAGMENT_SPREAD | FIELD 27 | 28 | ${source} 29 | `; 30 | return new _graphql.Source(source, schemaPath); 31 | } -------------------------------------------------------------------------------- /dist/createRaiseErrorsReporter.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.default = createRaiseErrorsReporter; 7 | 8 | // Was using a ConsoleReporter with quiet true (which is essentially a no-op) 9 | // This implements graphql-compiler GraphQLReporter 10 | // https://github.com/facebook/relay/blob/v1.7.0/packages/graphql-compiler/reporters/GraphQLReporter.js 11 | // Wasn't able to find a way to import the GraphQLReporter interface to declare that it is 12 | // implemented 13 | function createRaiseErrorsReporter(logger) { 14 | return { 15 | reportMessage(message) { 16 | if (logger) logger.log(message);else console.log(message); // eslint-disable-line no-console 17 | }, 18 | 19 | /* eslint-disable no-unused-vars */ 20 | reportTime(name, ms) {// process.stdout.write('Report time: ' + name + ' ' + ms + '\n'); 21 | }, 22 | 23 | /* eslint-enable no-unused-vars */ 24 | reportError(caughtLocation, error) { 25 | // process.stdout.write('Report error: ' + caughtLocation + ' ' + error.toString() + '\n'); 26 | throw error; 27 | } 28 | 29 | }; 30 | } -------------------------------------------------------------------------------- /src/createRaiseErrorsReporter.js: -------------------------------------------------------------------------------- 1 | // Was using a ConsoleReporter with quiet true (which is essentially a no-op) 2 | // This implements graphql-compiler GraphQLReporter 3 | // https://github.com/facebook/relay/blob/v1.7.0/packages/graphql-compiler/reporters/GraphQLReporter.js 4 | // Wasn't able to find a way to import the GraphQLReporter interface to declare that it is 5 | // implemented 6 | 7 | import type { WebpackLogger } from './WebpackLogger'; 8 | 9 | export default function createRaiseErrorsReporter(logger?: WebpackLogger) { 10 | return { 11 | reportMessage(message: string): void { 12 | if (logger) logger.log(message); 13 | else console.log(message); // eslint-disable-line no-console 14 | }, 15 | 16 | /* eslint-disable no-unused-vars */ 17 | reportTime(name: string, ms: number): void { 18 | // process.stdout.write('Report time: ' + name + ' ' + ms + '\n'); 19 | }, 20 | /* eslint-enable no-unused-vars */ 21 | 22 | reportError(caughtLocation: string, error: Error): void { 23 | // process.stdout.write('Report error: ' + caughtLocation + ' ' + error.toString() + '\n'); 24 | throw error; 25 | }, 26 | }; 27 | } 28 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | deploy: 10 | name: Build on Node ${{ matrix.node_version }}, Webpack ${{ matrix.webpack_version }} 11 | strategy: 12 | matrix: 13 | webpack_version: [3, 4, 5] 14 | node_version: [10, 12, 14, 15, 16] 15 | 16 | runs-on: ubuntu-18.04 17 | 18 | steps: 19 | - uses: actions/checkout@v2 20 | - name: Get yarn cache directory path 21 | id: yarn-cache-dir-path 22 | run: echo "::set-output name=dir::$(yarn cache dir)" 23 | - uses: actions/cache@v2 24 | id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`) 25 | with: 26 | path: ${{ steps.yarn-cache-dir-path.outputs.dir }} 27 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 28 | restore-keys: | 29 | ${{ runner.os }}-yarn- 30 | - name: Setup Node 31 | uses: actions/setup-node@v1 32 | with: 33 | node-version: ${{ matrix.node_version }} 34 | - name: Install Yarn Deps 35 | run: | 36 | yarn 37 | yarn remove webpack 38 | yarn add webpack@${{ env.webpack_version }} 39 | - name: Build 40 | run: yarn build 41 | -------------------------------------------------------------------------------- /src/getWriter.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { FileWriter, IRTransforms } from 'relay-compiler'; 4 | import type { WriteFilesOptions } from 'relay-compiler'; 5 | 6 | export type WriterConfig = { 7 | outputDir?: string, 8 | baseDir: string, 9 | customScalars?: any 10 | } 11 | 12 | const { 13 | commonTransforms, 14 | codegenTransforms, 15 | fragmentTransforms, 16 | printTransforms, 17 | queryTransforms, 18 | schemaExtensions, 19 | } = IRTransforms; 20 | 21 | // Taken from relay-compiler/bin/RelayCompilerMain.js 22 | export default (languagePlugin: any, config: WriterConfig) => ({ 23 | onlyValidate, 24 | schema, 25 | documents, 26 | baseDocuments, 27 | sourceControl, 28 | reporter, 29 | }: WriteFilesOptions) => FileWriter.writeAll({ 30 | config: { 31 | customScalars: {}, 32 | ...config, 33 | compilerTransforms: { 34 | commonTransforms, 35 | codegenTransforms, 36 | fragmentTransforms, 37 | printTransforms, 38 | queryTransforms, 39 | }, 40 | formatModule: languagePlugin.formatModule, 41 | optionalInputFieldsForFlow: [], 42 | schemaExtensions, 43 | useHaste: false, 44 | extension: languagePlugin.outputExtension, 45 | typeGenerator: languagePlugin.typeGenerator, 46 | }, 47 | onlyValidate, 48 | schema, 49 | baseDocuments, 50 | documents, 51 | reporter, 52 | sourceControl, 53 | }); 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2017, ThusFresh Inc 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /test/fixtureProject/src/components/About.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { graphql, QueryRenderer } from 'react-relay' 3 | import updateFirstNameMutation from '../mutations/updateFirstNameMutation' 4 | 5 | export default ({ id, relayEnvironment }) => { 6 | const onSubmit = e => { 7 | e.preventDefault() 8 | 9 | updateFirstNameMutation(relayEnvironment, { 10 | id, 11 | firstName: e.target.firstName.value 12 | }) 13 | } 14 | 15 | return ( 16 | { 29 | if (!props) { 30 | return
    Loading
    31 | } 32 | 33 | const { personById } = props 34 | if (!personById) { 35 | return
    No match
    36 | } 37 | return ( 38 |
    39 |

    About

    40 |
    41 | Id: {personById.id} 42 |
    43 | Name: {personById.fullName} 44 |
    45 |
    46 |
    47 | Change first name: 48 | 54 | 55 |
    56 |
    57 |
    58 | ) 59 | }} 60 | /> 61 | ) 62 | } 63 | -------------------------------------------------------------------------------- /dist/getWriter.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.default = void 0; 7 | 8 | var _relayCompiler = require("relay-compiler"); 9 | 10 | function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } 11 | 12 | function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } 13 | 14 | function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } 15 | 16 | const { 17 | commonTransforms, 18 | codegenTransforms, 19 | fragmentTransforms, 20 | printTransforms, 21 | queryTransforms, 22 | schemaExtensions 23 | } = _relayCompiler.IRTransforms; // Taken from relay-compiler/bin/RelayCompilerMain.js 24 | 25 | var _default = (languagePlugin, config) => ({ 26 | onlyValidate, 27 | schema, 28 | documents, 29 | baseDocuments, 30 | sourceControl, 31 | reporter 32 | }) => _relayCompiler.FileWriter.writeAll({ 33 | config: _objectSpread(_objectSpread({ 34 | customScalars: {} 35 | }, config), {}, { 36 | compilerTransforms: { 37 | commonTransforms, 38 | codegenTransforms, 39 | fragmentTransforms, 40 | printTransforms, 41 | queryTransforms 42 | }, 43 | formatModule: languagePlugin.formatModule, 44 | optionalInputFieldsForFlow: [], 45 | schemaExtensions, 46 | useHaste: false, 47 | extension: languagePlugin.outputExtension, 48 | typeGenerator: languagePlugin.typeGenerator 49 | }), 50 | onlyValidate, 51 | schema, 52 | baseDocuments, 53 | documents, 54 | reporter, 55 | sourceControl 56 | }); 57 | 58 | exports.default = _default; -------------------------------------------------------------------------------- /test/hooks.test.js: -------------------------------------------------------------------------------- 1 | /* global jest, describe, it, beforeEach */ 2 | /* eslint-env jest */ 3 | import webpack from 'webpack' 4 | import path from 'path' 5 | import rimraf from 'rimraf' 6 | import RelayCompilerWebpackPlugin from '../src/index' 7 | import normaliseConfigForWebpackVersion from './support/normaliseConfigForWebpackVersion' 8 | import createTempFixtureProject from './support/createTempFixtureProject' 9 | import { removeSync } from 'fs-extra' 10 | 11 | jest.setTimeout(30000) 12 | 13 | const DEFAULT_NODE_ENV = process.env.NODE_ENV 14 | 15 | describe('RelayCompilerWebpackPlugin', () => { 16 | let fixtureDir 17 | let createWebpackConfig 18 | let srcDir 19 | 20 | beforeEach(() => { 21 | process.env.NODE_ENV = DEFAULT_NODE_ENV 22 | fixtureDir = createTempFixtureProject('hooks') 23 | createWebpackConfig = require(fixtureDir + '/createWebpackConfig') 24 | srcDir = path.join(fixtureDir, 'src') 25 | }) 26 | 27 | afterEach(() => { 28 | removeSync(fixtureDir) 29 | }) 30 | 31 | it('Calls hooks appropriately', done => { 32 | const beforeWriteSpy = jest.fn() 33 | const afterWriteSpy = jest.fn() 34 | const relayCompilerWebpackPlugin = new RelayCompilerWebpackPlugin({ 35 | schema: path.resolve(fixtureDir, 'schema.json'), 36 | src: srcDir 37 | }) 38 | 39 | const plugin = { 40 | apply (compiler) { 41 | const setUpHooks = (compilation) => { 42 | const hooks = RelayCompilerWebpackPlugin.getHooks(compilation) 43 | hooks.beforeWrite.tapPromise('test-hooks', async () => { 44 | beforeWriteSpy() 45 | }) 46 | 47 | hooks.afterWrite.tapPromise('test-hooks', async (result) => { 48 | afterWriteSpy(result) 49 | }) 50 | } 51 | 52 | if (compiler.hooks) { 53 | compiler.hooks.compilation.tap('TestHooksPlugin', setUpHooks) 54 | } else { 55 | compiler.plugin('compilation', setUpHooks) 56 | } 57 | } 58 | } 59 | 60 | const webpackConfig = normaliseConfigForWebpackVersion( 61 | createWebpackConfig({ 62 | relayCompilerWebpackPlugin, 63 | plugins: [plugin] 64 | }) 65 | ) 66 | 67 | webpack(webpackConfig, (err, stats) => { 68 | expect(err).toBeFalsy() 69 | expect(stats.compilation.errors).toHaveLength(0) 70 | expect(stats.compilation.warnings).toHaveLength(0) 71 | 72 | expect(beforeWriteSpy).toHaveBeenCalledTimes(1) 73 | expect(afterWriteSpy).toHaveBeenCalledTimes(1) 74 | expect(afterWriteSpy).toHaveBeenCalledWith('HAS_CHANGES') 75 | done() 76 | }) 77 | }) 78 | }) 79 | -------------------------------------------------------------------------------- /test/invalidArgs.test.js: -------------------------------------------------------------------------------- 1 | /* global jest, describe, it, beforeEach */ 2 | /* eslint-env jest */ 3 | import path from 'path' 4 | import RelayCompilerWebpackPlugin from '../src/index' 5 | import normaliseConfigForWebpackVersion from './support/normaliseConfigForWebpackVersion' 6 | import webpack from 'webpack' 7 | import createTempFixtureProject from './support/createTempFixtureProject' 8 | import { removeSync } from 'fs-extra' 9 | 10 | jest.setTimeout(20000) 11 | 12 | describe('RelayCompilerWebpackPlugin', () => { 13 | let fixtureDir 14 | let createWebpackConfig 15 | let srcDir 16 | 17 | beforeEach(() => { 18 | fixtureDir = createTempFixtureProject('invalidArgs') 19 | createWebpackConfig = require(fixtureDir + '/createWebpackConfig') 20 | srcDir = path.join(fixtureDir, 'src') 21 | }) 22 | 23 | afterEach(() => { 24 | removeSync(fixtureDir) 25 | }) 26 | 27 | it('throws if an empty constructor', () => { 28 | expect(() => new RelayCompilerWebpackPlugin()).toThrow( 29 | 'You must provide options to RelayCompilerWebpackPlugin.' 30 | ) 31 | }) 32 | 33 | it("throws if the schema isn't found", () => { 34 | const schema = path.join(fixtureDir, 'schema-doesnt-exist.json') 35 | 36 | expect(() => new RelayCompilerWebpackPlugin({ schema, src: srcDir })).toThrow( 37 | `Could not find the [schema] provided (${schema})` 38 | ) 39 | }) 40 | 41 | it('throws if no schema provided', () => { 42 | expect(() => new RelayCompilerWebpackPlugin({ src: srcDir })).toThrow( 43 | 'You must provide a Relay Schema.' 44 | ) 45 | }) 46 | 47 | it('throws if invalid schema provided', done => { 48 | const webpackConfig = normaliseConfigForWebpackVersion( 49 | createWebpackConfig({ RelayCompilerWebpackPlugin }) 50 | ) 51 | webpackConfig.plugins = [ 52 | new RelayCompilerWebpackPlugin({ 53 | src: srcDir, 54 | schema: path.resolve(__dirname, '..', 'package.json') 55 | }) 56 | ] 57 | 58 | webpack(webpackConfig, (err, stats) => { 59 | expect(err).toBeFalsy() 60 | expect(stats.compilation.errors).toHaveLength(5) 61 | expect(stats.compilation.warnings).toHaveLength(0) 62 | stats.compilation.errors 63 | .map(e => e.message) 64 | .forEach(message => 65 | expect(message).toEqual( 66 | expect.stringContaining( 67 | 'Error: Invalid or incomplete introspection result.' 68 | ) 69 | ) 70 | ) 71 | done() 72 | }) 73 | }) 74 | 75 | it('throws if no src provided', () => { 76 | const schema = path.join(fixtureDir, 'schema.json') 77 | 78 | expect(() => new RelayCompilerWebpackPlugin({ schema })).toThrow( 79 | 'You must provide a Relay `src` path.' 80 | ) 81 | }) 82 | 83 | it("throws if source isn't found", () => { 84 | const src = path.join(fixtureDir, 'src-doesnt-exist') 85 | const schema = path.join(fixtureDir, 'schema.json') 86 | 87 | expect(() => new RelayCompilerWebpackPlugin({ src, schema })).toThrow( 88 | `Could not find the [src] provided (${src})` 89 | ) 90 | }) 91 | }) 92 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "relay-compiler-webpack-plugin", 3 | "version": "9.1.0", 4 | "description": "Automatically run the Relay Compiler from Webpack", 5 | "keywords": [ 6 | "relay", 7 | "webpack", 8 | "webpack3", 9 | "webpack4", 10 | "webpack5", 11 | "react", 12 | "reactjs", 13 | "relay-compiler", 14 | "graphql" 15 | ], 16 | "repository": { 17 | "type": "git", 18 | "url": "https://github.com/danielholmes/relay-compiler-webpack-plugin" 19 | }, 20 | "main": "dist/index.js", 21 | "jsnext/main": "src/index.js", 22 | "files": [ 23 | "dist" 24 | ], 25 | "author": "Daniel Holmes", 26 | "license": "BSD-3-Clause", 27 | "scripts": { 28 | "clean": "rimraf dist", 29 | "flow": "flow", 30 | "lint": "eslint --fix 'src/**/*.js'", 31 | "lint:check": "eslint 'src/**/*.js'", 32 | "test": "jest test --env node", 33 | "test:update": "jest test --env node -u", 34 | "test:coverage": "jest test --env node --coverage --coverageDirectory ./reports src", 35 | "compile": "babel src --out-dir dist", 36 | "build": "npm-run-all --parallel flow lint:check test --sequential compile", 37 | "prepublishOnly": "run-s clean build" 38 | }, 39 | "peerDependencies": { 40 | "relay-compiler": ">=8.0.0", 41 | "webpack": ">=3.0.0", 42 | "graphql": ">=14.0.0" 43 | }, 44 | "dependencies": { 45 | "fast-glob": "^3.1.0", 46 | "tapable": "^1.1.3" 47 | }, 48 | "devDependencies": { 49 | "@babel/cli": "^7.13.16", 50 | "@babel/core": "^7.14.2", 51 | "@babel/plugin-proposal-class-properties": "^7.13.0", 52 | "@babel/preset-env": "^7.14.2", 53 | "@babel/preset-flow": "^7.13.13", 54 | "@babel/preset-react": "^7.13.13", 55 | "@types/relay-compiler": "^7.0.2", 56 | "@types/tapable": "^1.0.4", 57 | "@types/webpack": "^4.41.28", 58 | "babel-core": "^7.0.0-bridge.0", 59 | "babel-eslint": "^10.1.0", 60 | "babel-jest": "^26.6.3", 61 | "babel-loader": "^8.2.2", 62 | "babel-plugin-relay": "^8.0.0", 63 | "eslint": "^7.26.0", 64 | "eslint-config-airbnb-base": "^14.2.1", 65 | "eslint-config-standard": "^16.0.2", 66 | "eslint-plugin-flowtype": "^5.7.2", 67 | "eslint-plugin-import": "^2.23.2", 68 | "eslint-plugin-node": "^11.1.0", 69 | "eslint-plugin-promise": "^5.1.0", 70 | "eslint-plugin-standard": "^5.0.0", 71 | "flow-bin": "^0.120.0", 72 | "graphql": "^14.0.0", 73 | "fs-extra": "^8.1.0", 74 | "jest": "^26.6.3", 75 | "npm-run-all": "^4.1.5", 76 | "react": "^16.11.0", 77 | "react-dom": "^16.11.0", 78 | "react-relay": "^8.0.0", 79 | "relay-compiler": "^8.0.0", 80 | "rimraf": "^3.0.0", 81 | "standard": "^16.0.3", 82 | "webpack": "^4.46.0" 83 | }, 84 | "babel": { 85 | "presets": [ 86 | [ 87 | "@babel/preset-env", 88 | { 89 | "targets": { 90 | "node": "8.0" 91 | } 92 | } 93 | ], 94 | "@babel/preset-flow" 95 | ], 96 | "plugins": [ 97 | "@babel/plugin-proposal-class-properties" 98 | ] 99 | }, 100 | "jest": { 101 | "watchPathIgnorePatterns": [ 102 | "dist/" 103 | ], 104 | "coveragePathIgnorePatterns": [ 105 | "/node_modules/", 106 | "/test/" 107 | ] 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Relay Compiler Webpack Plugin 2 | 3 | [![npm version](https://badge.fury.io/js/relay-compiler-webpack-plugin.svg)](https://badge.fury.io/js/relay-compiler-webpack-plugin) 4 | [![Travis Build Status](https://travis-ci.org/relay-tools/relay-compiler-webpack-plugin.svg?branch=master)](https://travis-ci.org/relay-tools/relay-compiler-webpack-plugin) 5 | ![Github Actions Build Status](https://github.com/relay-tools/relay-compiler-webpack-plugin/workflows/Build/badge.svg) 6 | 7 | Are you running Relay Modern? Are you annoyed with constantly running the `relay-compiler` to generate code, especially 8 | if you're already running Webpack? 9 | 10 | Well be annoyed no more! Simply install this plugin to automatically hook into Webpack's build process to generate these 11 | files for you. 12 | 13 | 14 | ## Installation 15 | 16 | 1. Add this to your project: 17 | 18 | ```sh 19 | yarn add --dev relay-compiler-webpack-plugin graphql 20 | # Or if you're using npm 21 | npm install --save-dev relay-compiler-webpack-plugin graphql 22 | ``` 23 | 24 | 2. Add the plugin to your Webpack configuration: 25 | 26 | ```javascript 27 | // const RelayCompilerLanguageTypescript = require('relay-compiler-language-typescript').default 28 | const RelayCompilerWebpackPlugin = require('relay-compiler-webpack-plugin') 29 | const path = require('path') 30 | 31 | module.exports = { 32 | // ... Your existing Webpack configuration 33 | plugins: [ 34 | // ... 35 | new RelayCompilerWebpackPlugin({ 36 | // languagePlugin: RelayCompilerLanguageTypescript, 37 | schema: path.resolve(__dirname, './relative/path/to/schema.graphql'), // or schema.json 38 | src: path.resolve(__dirname, './relative/path/to/source/files'), 39 | }) 40 | ] 41 | // ... 42 | } 43 | ``` 44 | Note: TypeScript projects also needs to add the TypeScript language plugin as shown in the commented out lines in the above snippet. 45 | 46 | 3. :tada: 47 | 48 | 49 | ### Gotchas 50 | 51 | If there are multiple versions of GraphQL in your dependency tree it will cause schema validation errors. To get around 52 | this, ensure you have the same graphql version as your relay-compiler version depends on. To assist this you can 53 | [install dependencies as flat](https://yarnpkg.com/lang/en/docs/cli/install/#toc-yarn-install-flat) which ensures only 54 | one version of each dependency. 55 | 56 | 57 | ### Plugin hooks 58 | 59 | `relay-compiler-webpack-plugin` exposes a few [tapable](https://github.com/webpack/tapable/tree/master) hooks, for plugins or tooling to use. 60 | 61 | - `beforeWrite` called before the plugin starts to compile queries 62 | - `afterWrite(compileResult)`: called after writing is complete 63 | 64 | ```js 65 | class MyPlugin { 66 | apply (compiler) { 67 | compiler.hooks.compilation.tap('MyPlugin', async (compilation) => { 68 | RelayCompilerWebpackPlugin.getHooks(compilation).afterWrite.tapPromise( 69 | 'MyPlugin', // <-- Set a meaningful name for stacktraces 70 | async (compileResult) => { 71 | if (compileResult === 'HAS_CHANGES') { 72 | await doSomething() 73 | } 74 | } 75 | ) 76 | }) 77 | } 78 | } 79 | ``` 80 | 81 | ## Example Project 82 | 83 | To see an example of its usage within a project, see 84 | [relay-compiler-webpack-plugin-example](https://github.com/danielholmes/relay-compiler-webpack-plugin-example). 85 | 86 | 87 | ## Development 88 | 89 | Running tests: 90 | 91 | ```bash 92 | yarn test 93 | ``` 94 | 95 | Running tests with coverage: 96 | 97 | ```bash 98 | yarn test:coverage 99 | ``` 100 | 101 | 102 | ## License 103 | 104 | Relay Compiler Webpack Plugin may be redistributed according to the [BSD 3-Clause License](LICENSE). 105 | -------------------------------------------------------------------------------- /test/normalCase.test.js: -------------------------------------------------------------------------------- 1 | /* global jest, describe, it, beforeEach */ 2 | /* eslint-env jest */ 3 | import webpack from 'webpack' 4 | import fs from 'fs' 5 | import path from 'path' 6 | import RelayCompilerWebpackPlugin from '../src/index' 7 | import normaliseConfigForWebpackVersion from './support/normaliseConfigForWebpackVersion' 8 | import createTempFixtureProject from './support/createTempFixtureProject' 9 | import { removeSync } from 'fs-extra' 10 | 11 | // TODO: Move to jest setupTests or something like that 12 | jest.setTimeout(30000) 13 | 14 | const DEFAULT_NODE_ENV = process.env.NODE_ENV 15 | 16 | describe('RelayCompilerWebpackPlugin', () => { 17 | let fixtureDir 18 | let createWebpackConfig 19 | 20 | beforeEach(() => { 21 | process.env.NODE_ENV = DEFAULT_NODE_ENV 22 | fixtureDir = createTempFixtureProject('normalCase') 23 | createWebpackConfig = require(fixtureDir + '/createWebpackConfig') 24 | }) 25 | 26 | afterEach(() => { 27 | process.env.NODE_ENV = DEFAULT_NODE_ENV 28 | removeSync(fixtureDir) 29 | }) 30 | 31 | it('generates graphql files correctly for a normal example', done => { 32 | const relayCompilerWebpackPlugin = new RelayCompilerWebpackPlugin({ 33 | schema: path.resolve(fixtureDir, 'schema.json'), 34 | src: path.resolve(fixtureDir, 'src') 35 | }) 36 | 37 | const webpackConfig = normaliseConfigForWebpackVersion( 38 | createWebpackConfig({ relayCompilerWebpackPlugin }) 39 | ) 40 | 41 | webpack(webpackConfig, (err, stats) => { 42 | expect(err).toBeFalsy() 43 | expect(stats.compilation.errors).toHaveLength(0) 44 | expect(stats.compilation.warnings).toHaveLength(0) 45 | 46 | const expectedFiles = [ 47 | path.join( 48 | 'mutations', 49 | '__generated__', 50 | 'updateFirstNameMutation.graphql.js' 51 | ), 52 | path.join( 53 | 'components', 54 | '__generated__', 55 | 'HomeItem_person.graphql.js' 56 | ), 57 | path.join( 58 | 'components', 59 | '__generated__', 60 | 'Home_people.graphql.js' 61 | ), 62 | path.join( 63 | 'components', 64 | '__generated__', 65 | 'AppQuery.graphql.js' 66 | ), 67 | path.join( 68 | 'components', 69 | '__generated__', 70 | 'AboutQuery.graphql.js' 71 | ) 72 | ] 73 | expectedFiles.forEach(generatedSrcPath => { 74 | const absPath = path.resolve(fixtureDir, 'src', generatedSrcPath) 75 | expect(fs.existsSync(absPath)).toBe(true) 76 | expect(fs.readFileSync(absPath, 'utf8')).toMatchSnapshot() 77 | }) 78 | 79 | done() 80 | }) 81 | }) 82 | 83 | it('generates graphql files correctly for a normal example with --artifactDirectory option', done => { 84 | const relayCompilerWebpackPlugin = new RelayCompilerWebpackPlugin({ 85 | schema: path.resolve(fixtureDir, 'schema.json'), 86 | src: path.resolve(fixtureDir, 'src'), 87 | artifactDirectory: path.resolve(fixtureDir, 'src', '__generated__') 88 | }) 89 | 90 | const webpackConfig = normaliseConfigForWebpackVersion( 91 | createWebpackConfig({ relayCompilerWebpackPlugin }) 92 | ) 93 | 94 | process.env.NODE_ENV = 'artifactDirectoryTest' 95 | webpack(webpackConfig, (err, stats) => { 96 | expect(err).toBeFalsy() 97 | if (stats.compilation.logging) { 98 | expect(stats.compilation.logging.get('RelayCompilerPlugin')).toHaveLength(2) 99 | } 100 | 101 | expect(stats.compilation.errors).toHaveLength(0) 102 | expect(stats.compilation.warnings).toHaveLength(0) 103 | 104 | const expectedFiles = [ 105 | 'updateFirstNameMutation.graphql.js', 106 | 'HomeItem_person.graphql.js', 107 | 'Home_people.graphql.js', 108 | 'AppQuery.graphql.js', 109 | 'AboutQuery.graphql.js' 110 | ] 111 | expectedFiles.forEach(fileName => { 112 | const generatedFilepath = path.resolve( 113 | fixtureDir, 'src', '__generated__', fileName 114 | ) 115 | expect(fs.existsSync(generatedFilepath)).toBe(true) 116 | expect(fs.readFileSync(generatedFilepath, 'utf8')).toMatchSnapshot() 117 | }) 118 | 119 | done() 120 | }) 121 | }) 122 | }) 123 | -------------------------------------------------------------------------------- /dist/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _relayCompiler = require("relay-compiler"); 4 | 5 | var _RelayLanguagePluginJavaScript = _interopRequireDefault(require("relay-compiler/lib/language/javascript/RelayLanguagePluginJavaScript")); 6 | 7 | var _RelaySourceModuleParser = _interopRequireDefault(require("relay-compiler/lib/core/RelaySourceModuleParser")); 8 | 9 | var _fs = _interopRequireDefault(require("fs")); 10 | 11 | var _path = _interopRequireDefault(require("path")); 12 | 13 | var _webpack = require("webpack"); 14 | 15 | var _getSchemaSource = _interopRequireDefault(require("./getSchemaSource")); 16 | 17 | var _getWriter = _interopRequireDefault(require("./getWriter")); 18 | 19 | var _getFilepathsFromGlob = _interopRequireDefault(require("./getFilepathsFromGlob")); 20 | 21 | var _getRelayCompilerPluginHooks = _interopRequireDefault(require("./getRelayCompilerPluginHooks")); 22 | 23 | var _createRaiseErrorsReporter = _interopRequireDefault(require("./createRaiseErrorsReporter")); 24 | 25 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 26 | 27 | function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; } 28 | 29 | function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } 30 | 31 | function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } 32 | 33 | const { 34 | schemaExtensions 35 | } = _relayCompiler.IRTransforms; 36 | const isWebpack5 = parseInt(_webpack.version, 10) === 5; 37 | 38 | function createParserConfigs({ 39 | baseDir, 40 | getParser, 41 | sourceParserName, 42 | languagePlugin, 43 | include, 44 | exclude, 45 | schema, 46 | extensions 47 | }) { 48 | const sourceModuleParser = (0, _RelaySourceModuleParser.default)(languagePlugin.findGraphQLTags); 49 | const fileOptions = { 50 | extensions, 51 | include, 52 | exclude 53 | }; 54 | return { 55 | [sourceParserName]: { 56 | baseDir, 57 | getFileFilter: sourceModuleParser.getFileFilter, 58 | getParser: getParser || sourceModuleParser.getParser, 59 | getSchemaSource: () => (0, _getSchemaSource.default)(schema), 60 | schemaExtensions, 61 | filepaths: (0, _getFilepathsFromGlob.default)(baseDir, fileOptions) 62 | }, 63 | graphql: { 64 | baseDir, 65 | getParser: _relayCompiler.DotGraphQLParser.getParser, 66 | getSchemaSource: () => (0, _getSchemaSource.default)(schema), 67 | schemaExtensions, 68 | filepaths: (0, _getFilepathsFromGlob.default)(baseDir, _objectSpread(_objectSpread({}, fileOptions), {}, { 69 | extensions: ['graphql'] 70 | })) 71 | } 72 | }; 73 | } 74 | 75 | class RelayCompilerWebpackPlugin { 76 | constructor(options) { 77 | _defineProperty(this, "parserConfigs", void 0); 78 | 79 | _defineProperty(this, "writerConfigs", void 0); 80 | 81 | _defineProperty(this, "languagePlugin", void 0); 82 | 83 | _defineProperty(this, "options", void 0); 84 | 85 | if (!options) { 86 | throw new Error('You must provide options to RelayCompilerWebpackPlugin.'); 87 | } 88 | 89 | if (!options.schema) { 90 | throw new Error('You must provide a Relay Schema.'); 91 | } 92 | 93 | if (typeof options.schema === 'string' && !_fs.default.existsSync(options.schema)) { 94 | throw new Error(`Could not find the [schema] provided (${options.schema}).`); 95 | } 96 | 97 | if (!options.src) { 98 | throw new Error('You must provide a Relay `src` path.'); 99 | } 100 | 101 | if (!_fs.default.existsSync(options.src)) { 102 | throw new Error(`Could not find the [src] provided (${options.src})`); 103 | } 104 | 105 | this.options = options; 106 | } 107 | 108 | createWriterConfigs({ 109 | sourceParserName, 110 | languagePlugin, 111 | config 112 | }) { 113 | return { 114 | [languagePlugin.outputExtension]: { 115 | writeFiles: (0, _getWriter.default)(languagePlugin, config), 116 | isGeneratedFile: filePath => { 117 | if (filePath.endsWith(`.graphql.${languagePlugin.outputExtension}`)) { 118 | if (this.options.artifactDirectory) { 119 | return filePath.startsWith(this.options.artifactDirectory); 120 | } 121 | 122 | return filePath.includes('__generated__'); 123 | } 124 | 125 | return false; 126 | }, 127 | parser: sourceParserName, 128 | baseParsers: ['graphql'] 129 | } 130 | }; 131 | } 132 | 133 | async compile(issuer, request, compilation, hooks) { 134 | let logger; // webpack 4.38+ 135 | 136 | if (compilation.getLogger) { 137 | logger = compilation.getLogger('RelayCompilerPlugin'); 138 | } 139 | 140 | const reporter = this.options.getReporter ? this.options.getReporter(logger) : (0, _createRaiseErrorsReporter.default)(logger); // Can this be set up in constructor and use same instance every time? 141 | 142 | const runner = new _relayCompiler.Runner({ 143 | parserConfigs: this.parserConfigs, 144 | writerConfigs: this.writerConfigs, 145 | reporter, 146 | onlyValidate: false, 147 | skipPersist: true 148 | }); 149 | return hooks.beforeWrite.promise().then(() => runner.compile(this.languagePlugin.outputExtension)).then(compileResult => hooks.afterWrite.promise(compileResult)); 150 | } 151 | 152 | cachedCompiler(compilation) { 153 | const hooks = (0, _getRelayCompilerPluginHooks.default)(compilation); 154 | let result; 155 | return (issuer, request) => { 156 | if (!result) result = this.compile(issuer, request, compilation, hooks); 157 | return result; 158 | }; 159 | } 160 | 161 | runCompile(compile, result, callback) { 162 | const passResult = isWebpack5 ? undefined : result; 163 | 164 | if (result && result.contextInfo.issuer && (this.options.artifactDirectory || result.request.match(/__generated__/))) { 165 | const request = _path.default.resolve(_path.default.dirname(result.contextInfo.issuer), result.request); 166 | 167 | if (this.options.artifactDirectory && !request.startsWith(this.options.artifactDirectory)) { 168 | callback(null, passResult); 169 | return; 170 | } 171 | 172 | compile(result.contextInfo.issuer, request).then(() => callback(null, passResult)).catch(error => callback(error)); 173 | return; 174 | } 175 | 176 | callback(null, passResult); 177 | } 178 | 179 | apply(compiler) { 180 | const { 181 | options 182 | } = this; 183 | 184 | const language = (options.languagePlugin || _RelayLanguagePluginJavaScript.default)(); 185 | 186 | const extensions = options.extensions !== undefined ? options.extensions : language.inputExtensions; 187 | const sourceParserName = extensions.join('/'); 188 | const include = options.include !== undefined ? options.include : ['**']; 189 | const exclude = options.exclude !== undefined ? options.exclude : ['**/node_modules/**', '**/__mocks__/**', '**/__tests__/**', '**/__generated__/**']; 190 | this.parserConfigs = createParserConfigs({ 191 | sourceParserName, 192 | languagePlugin: language, 193 | include, 194 | exclude, 195 | schema: options.schema, 196 | getParser: options.getParser, 197 | baseDir: options.src, 198 | extensions 199 | }); 200 | this.writerConfigs = this.createWriterConfigs({ 201 | sourceParserName, 202 | languagePlugin: language, 203 | config: _objectSpread(_objectSpread({}, options.config), {}, { 204 | outputDir: options.artifactDirectory, 205 | baseDir: options.src 206 | }) 207 | }); 208 | this.languagePlugin = language; 209 | 210 | if (compiler.hooks) { 211 | compiler.hooks.compilation.tap('RelayCompilerWebpackPlugin', (compilation, params) => { 212 | const compile = this.cachedCompiler(compilation); 213 | params.normalModuleFactory.hooks.beforeResolve.tapAsync('RelayCompilerWebpackPlugin', (result, callback) => { 214 | this.runCompile(compile, result, callback); 215 | }); 216 | }); 217 | } else { 218 | compiler.plugin('compilation', (compilation, params) => { 219 | const compile = this.cachedCompiler(compilation); 220 | params.normalModuleFactory.plugin('before-resolve', (result, callback) => { 221 | this.runCompile(compile, result, callback); 222 | }); 223 | }); 224 | } 225 | } 226 | 227 | } 228 | 229 | _defineProperty(RelayCompilerWebpackPlugin, "getHooks", _getRelayCompilerPluginHooks.default); 230 | 231 | module.exports = RelayCompilerWebpackPlugin; -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | // @flow 2 | 3 | import { DotGraphQLParser, IRTransforms, Runner } from 'relay-compiler'; 4 | import RelayLanguagePluginJavaScript from 'relay-compiler/lib/language/javascript/RelayLanguagePluginJavaScript'; 5 | import RelaySourceModuleParser from 'relay-compiler/lib/core/RelaySourceModuleParser'; 6 | 7 | import fs from 'fs'; 8 | import path from 'path'; 9 | 10 | import { version } from 'webpack'; 11 | import type { Compiler, Compilation } from 'webpack'; 12 | import type { PluginInterface } from 'relay-compiler/lib/language/RelayLanguagePluginInterface'; 13 | import getSchemaSource from './getSchemaSource'; 14 | import getWriter from './getWriter'; 15 | import getFilepathsFromGlob from './getFilepathsFromGlob'; 16 | import getRelayCompilerPluginHooks from './getRelayCompilerPluginHooks'; 17 | import type { WriterConfig } from './getWriter'; 18 | import type { PluginHooks } from './getRelayCompilerPluginHooks'; 19 | import type { WebpackLogger } from './WebpackLogger'; 20 | import createRaiseErrorsReporter from './createRaiseErrorsReporter'; 21 | 22 | const { schemaExtensions } = IRTransforms; 23 | 24 | type RelayCompilerWebpackPluginOptions = { 25 | schema: string, 26 | src: string, 27 | getParser?: Function, 28 | extensions: Array, 29 | include: Array, 30 | exclude: Array, 31 | languagePlugin?: () => PluginInterface, 32 | artifactDirectory?: string, 33 | getReporter?: (logger?: WebpackLogger) => any, 34 | config: any 35 | } 36 | 37 | const isWebpack5 = parseInt(version, 10) === 5; 38 | 39 | function createParserConfigs({ 40 | baseDir, 41 | getParser, 42 | sourceParserName, 43 | languagePlugin, 44 | include, 45 | exclude, 46 | schema, 47 | extensions, 48 | }: { 49 | baseDir: string, 50 | getParser?: Function, 51 | sourceParserName: string, 52 | languagePlugin: PluginInterface, 53 | schema: string, 54 | include: Array, 55 | exclude: Array, 56 | extensions: Array 57 | }) { 58 | const sourceModuleParser = RelaySourceModuleParser( 59 | languagePlugin.findGraphQLTags, 60 | ); 61 | 62 | const fileOptions = { extensions, include, exclude }; 63 | 64 | return { 65 | [sourceParserName]: { 66 | baseDir, 67 | getFileFilter: sourceModuleParser.getFileFilter, 68 | getParser: getParser || sourceModuleParser.getParser, 69 | getSchemaSource: () => getSchemaSource(schema), 70 | schemaExtensions, 71 | filepaths: getFilepathsFromGlob(baseDir, fileOptions), 72 | }, 73 | graphql: { 74 | baseDir, 75 | getParser: DotGraphQLParser.getParser, 76 | getSchemaSource: () => getSchemaSource(schema), 77 | schemaExtensions, 78 | filepaths: getFilepathsFromGlob(baseDir, { 79 | ...fileOptions, 80 | extensions: ['graphql'], 81 | }), 82 | }, 83 | }; 84 | } 85 | 86 | class RelayCompilerWebpackPlugin { 87 | parserConfigs: {} 88 | 89 | writerConfigs: {} 90 | 91 | languagePlugin: PluginInterface 92 | 93 | options: RelayCompilerWebpackPluginOptions 94 | 95 | static getHooks = getRelayCompilerPluginHooks 96 | 97 | constructor(options: RelayCompilerWebpackPluginOptions) { 98 | if (!options) { 99 | throw new Error('You must provide options to RelayCompilerWebpackPlugin.'); 100 | } 101 | 102 | if (!options.schema) { 103 | throw new Error('You must provide a Relay Schema.'); 104 | } 105 | 106 | if (typeof options.schema === 'string' && !fs.existsSync(options.schema)) { 107 | throw new Error( 108 | `Could not find the [schema] provided (${options.schema}).`, 109 | ); 110 | } 111 | 112 | if (!options.src) { 113 | throw new Error('You must provide a Relay `src` path.'); 114 | } 115 | 116 | if (!fs.existsSync(options.src)) { 117 | throw new Error(`Could not find the [src] provided (${options.src})`); 118 | } 119 | 120 | this.options = options; 121 | } 122 | 123 | createWriterConfigs({ 124 | sourceParserName, 125 | languagePlugin, 126 | config, 127 | }: { 128 | sourceParserName: string, 129 | languagePlugin: PluginInterface, 130 | config: WriterConfig, 131 | }) { 132 | return { 133 | [languagePlugin.outputExtension]: { 134 | writeFiles: getWriter(languagePlugin, config), 135 | isGeneratedFile: (filePath: string) => { 136 | if (filePath.endsWith(`.graphql.${languagePlugin.outputExtension}`)) { 137 | if (this.options.artifactDirectory) { 138 | return filePath.startsWith(this.options.artifactDirectory); 139 | } 140 | return filePath.includes('__generated__'); 141 | } 142 | 143 | return false; 144 | }, 145 | parser: sourceParserName, 146 | baseParsers: ['graphql'], 147 | }, 148 | }; 149 | } 150 | 151 | async compile( 152 | issuer: string, 153 | request: string, 154 | compilation: Compilation, 155 | hooks: PluginHooks, 156 | ): Promise { 157 | let logger; 158 | 159 | // webpack 4.38+ 160 | if (compilation.getLogger) { 161 | logger = compilation.getLogger('RelayCompilerPlugin'); 162 | } 163 | 164 | const reporter = this.options.getReporter 165 | ? this.options.getReporter(logger) 166 | : createRaiseErrorsReporter(logger); 167 | 168 | // Can this be set up in constructor and use same instance every time? 169 | const runner = new Runner({ 170 | parserConfigs: this.parserConfigs, 171 | writerConfigs: this.writerConfigs, 172 | reporter, 173 | onlyValidate: false, 174 | skipPersist: true, 175 | }); 176 | 177 | return hooks.beforeWrite.promise() 178 | .then(() => runner.compile(this.languagePlugin.outputExtension)) 179 | .then((compileResult) => hooks.afterWrite.promise(compileResult)); 180 | } 181 | 182 | cachedCompiler(compilation: Compilation) { 183 | const hooks = getRelayCompilerPluginHooks(compilation); 184 | let result; 185 | return (issuer: string, request: string) => { 186 | if (!result) result = this.compile(issuer, request, compilation, hooks); 187 | return result; 188 | }; 189 | } 190 | 191 | runCompile( 192 | compile: (issuer: string, request: string) => any, 193 | result: any, 194 | callback: (error: Error | null, value: string | typeof undefined) => void, 195 | ) { 196 | const passResult = isWebpack5 ? undefined : result; 197 | 198 | if ( 199 | result 200 | && result.contextInfo.issuer 201 | && (this.options.artifactDirectory || result.request.match(/__generated__/)) 202 | ) { 203 | const request = path.resolve( 204 | path.dirname(result.contextInfo.issuer), 205 | result.request, 206 | ); 207 | 208 | if (this.options.artifactDirectory && !request.startsWith(this.options.artifactDirectory)) { 209 | callback(null, passResult); 210 | return; 211 | } 212 | 213 | compile(result.contextInfo.issuer, request) 214 | .then(() => callback(null, passResult)) 215 | .catch((error) => callback(error)); 216 | 217 | return; 218 | } 219 | 220 | callback(null, passResult); 221 | } 222 | 223 | apply(compiler: Compiler) { 224 | const { options } = this; 225 | const language = (options.languagePlugin || RelayLanguagePluginJavaScript)(); 226 | 227 | const extensions = options.extensions !== undefined 228 | ? options.extensions 229 | : language.inputExtensions; 230 | const sourceParserName = extensions.join('/'); 231 | const include = options.include !== undefined ? options.include : ['**']; 232 | const exclude = options.exclude !== undefined 233 | ? options.exclude 234 | : [ 235 | '**/node_modules/**', 236 | '**/__mocks__/**', 237 | '**/__tests__/**', 238 | '**/__generated__/**', 239 | ]; 240 | 241 | this.parserConfigs = createParserConfigs({ 242 | sourceParserName, 243 | languagePlugin: language, 244 | include, 245 | exclude, 246 | schema: options.schema, 247 | getParser: options.getParser, 248 | baseDir: options.src, 249 | extensions, 250 | }); 251 | 252 | this.writerConfigs = this.createWriterConfigs({ 253 | sourceParserName, 254 | languagePlugin: language, 255 | config: { 256 | ...options.config, 257 | outputDir: options.artifactDirectory, 258 | baseDir: options.src, 259 | }, 260 | }); 261 | 262 | this.languagePlugin = language; 263 | 264 | if (compiler.hooks) { 265 | compiler.hooks.compilation.tap( 266 | 'RelayCompilerWebpackPlugin', 267 | (compilation: Compilation, params) => { 268 | const compile = this.cachedCompiler(compilation); 269 | params.normalModuleFactory.hooks.beforeResolve.tapAsync( 270 | 'RelayCompilerWebpackPlugin', 271 | (result, callback) => { 272 | this.runCompile(compile, result, callback); 273 | }, 274 | ); 275 | }, 276 | ); 277 | } else { 278 | compiler.plugin('compilation', (compilation: Compilation, params) => { 279 | const compile = this.cachedCompiler(compilation); 280 | params.normalModuleFactory.plugin( 281 | 'before-resolve', 282 | (result, callback) => { 283 | this.runCompile(compile, result, callback); 284 | }, 285 | ); 286 | }); 287 | } 288 | } 289 | } 290 | 291 | module.exports = RelayCompilerWebpackPlugin; 292 | -------------------------------------------------------------------------------- /test/__snapshots__/normalCase.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`RelayCompilerWebpackPlugin generates graphql files correctly for a normal example 1`] = ` 4 | "/** 5 | * @flow 6 | * @relayHash 5bd09d29f21e8c7772312a5d6d361bdd 7 | */ 8 | 9 | /* eslint-disable */ 10 | 11 | 'use strict'; 12 | 13 | /*:: 14 | import type { ConcreteRequest } from 'relay-runtime'; 15 | export type updateFirstNameMutationVariables = {| 16 | id: string, 17 | firstName: string, 18 | |}; 19 | export type updateFirstNameMutationResponse = {| 20 | +updateFirstName: {| 21 | +person: {| 22 | +firstName: string, 23 | +fullName: string, 24 | |} 25 | |} 26 | |}; 27 | export type updateFirstNameMutation = {| 28 | variables: updateFirstNameMutationVariables, 29 | response: updateFirstNameMutationResponse, 30 | |}; 31 | */ 32 | 33 | 34 | /* 35 | mutation updateFirstNameMutation( 36 | $id: ID! 37 | $firstName: String! 38 | ) { 39 | updateFirstName(id: $id, firstName: $firstName) { 40 | person { 41 | firstName 42 | fullName 43 | id 44 | } 45 | } 46 | } 47 | */ 48 | 49 | const node/*: ConcreteRequest*/ = (function(){ 50 | var v0 = [ 51 | { 52 | \\"kind\\": \\"LocalArgument\\", 53 | \\"name\\": \\"id\\", 54 | \\"type\\": \\"ID!\\", 55 | \\"defaultValue\\": null 56 | }, 57 | { 58 | \\"kind\\": \\"LocalArgument\\", 59 | \\"name\\": \\"firstName\\", 60 | \\"type\\": \\"String!\\", 61 | \\"defaultValue\\": null 62 | } 63 | ], 64 | v1 = [ 65 | { 66 | \\"kind\\": \\"Variable\\", 67 | \\"name\\": \\"firstName\\", 68 | \\"variableName\\": \\"firstName\\" 69 | }, 70 | { 71 | \\"kind\\": \\"Variable\\", 72 | \\"name\\": \\"id\\", 73 | \\"variableName\\": \\"id\\" 74 | } 75 | ], 76 | v2 = { 77 | \\"kind\\": \\"ScalarField\\", 78 | \\"alias\\": null, 79 | \\"name\\": \\"firstName\\", 80 | \\"args\\": null, 81 | \\"storageKey\\": null 82 | }, 83 | v3 = { 84 | \\"kind\\": \\"ScalarField\\", 85 | \\"alias\\": null, 86 | \\"name\\": \\"fullName\\", 87 | \\"args\\": null, 88 | \\"storageKey\\": null 89 | }; 90 | return { 91 | \\"kind\\": \\"Request\\", 92 | \\"fragment\\": { 93 | \\"kind\\": \\"Fragment\\", 94 | \\"name\\": \\"updateFirstNameMutation\\", 95 | \\"type\\": \\"Mutation\\", 96 | \\"metadata\\": null, 97 | \\"argumentDefinitions\\": (v0/*: any*/), 98 | \\"selections\\": [ 99 | { 100 | \\"kind\\": \\"LinkedField\\", 101 | \\"alias\\": null, 102 | \\"name\\": \\"updateFirstName\\", 103 | \\"storageKey\\": null, 104 | \\"args\\": (v1/*: any*/), 105 | \\"concreteType\\": \\"UpdateFirstNameOutput\\", 106 | \\"plural\\": false, 107 | \\"selections\\": [ 108 | { 109 | \\"kind\\": \\"LinkedField\\", 110 | \\"alias\\": null, 111 | \\"name\\": \\"person\\", 112 | \\"storageKey\\": null, 113 | \\"args\\": null, 114 | \\"concreteType\\": \\"Person\\", 115 | \\"plural\\": false, 116 | \\"selections\\": [ 117 | (v2/*: any*/), 118 | (v3/*: any*/) 119 | ] 120 | } 121 | ] 122 | } 123 | ] 124 | }, 125 | \\"operation\\": { 126 | \\"kind\\": \\"Operation\\", 127 | \\"name\\": \\"updateFirstNameMutation\\", 128 | \\"argumentDefinitions\\": (v0/*: any*/), 129 | \\"selections\\": [ 130 | { 131 | \\"kind\\": \\"LinkedField\\", 132 | \\"alias\\": null, 133 | \\"name\\": \\"updateFirstName\\", 134 | \\"storageKey\\": null, 135 | \\"args\\": (v1/*: any*/), 136 | \\"concreteType\\": \\"UpdateFirstNameOutput\\", 137 | \\"plural\\": false, 138 | \\"selections\\": [ 139 | { 140 | \\"kind\\": \\"LinkedField\\", 141 | \\"alias\\": null, 142 | \\"name\\": \\"person\\", 143 | \\"storageKey\\": null, 144 | \\"args\\": null, 145 | \\"concreteType\\": \\"Person\\", 146 | \\"plural\\": false, 147 | \\"selections\\": [ 148 | (v2/*: any*/), 149 | (v3/*: any*/), 150 | { 151 | \\"kind\\": \\"ScalarField\\", 152 | \\"alias\\": null, 153 | \\"name\\": \\"id\\", 154 | \\"args\\": null, 155 | \\"storageKey\\": null 156 | } 157 | ] 158 | } 159 | ] 160 | } 161 | ] 162 | }, 163 | \\"params\\": { 164 | \\"operationKind\\": \\"mutation\\", 165 | \\"name\\": \\"updateFirstNameMutation\\", 166 | \\"id\\": null, 167 | \\"text\\": \\"mutation updateFirstNameMutation(\\\\n $id: ID!\\\\n $firstName: String!\\\\n) {\\\\n updateFirstName(id: $id, firstName: $firstName) {\\\\n person {\\\\n firstName\\\\n fullName\\\\n id\\\\n }\\\\n }\\\\n}\\\\n\\", 168 | \\"metadata\\": {} 169 | } 170 | }; 171 | })(); 172 | // prettier-ignore 173 | (node/*: any*/).hash = '8ea2fcfefe6dc15aea4c7e70663a11d8'; 174 | module.exports = node; 175 | " 176 | `; 177 | 178 | exports[`RelayCompilerWebpackPlugin generates graphql files correctly for a normal example 2`] = ` 179 | "/** 180 | * @flow 181 | */ 182 | 183 | /* eslint-disable */ 184 | 185 | 'use strict'; 186 | 187 | /*:: 188 | import type { ReaderFragment } from 'relay-runtime'; 189 | import type { FragmentReference } from \\"relay-runtime\\"; 190 | declare export opaque type HomeItem_person$ref: FragmentReference; 191 | declare export opaque type HomeItem_person$fragmentType: HomeItem_person$ref; 192 | export type HomeItem_person = {| 193 | +id: string, 194 | +fullName: string, 195 | +$refType: HomeItem_person$ref, 196 | |}; 197 | export type HomeItem_person$data = HomeItem_person; 198 | export type HomeItem_person$key = { 199 | +$data?: HomeItem_person$data, 200 | +$fragmentRefs: HomeItem_person$ref, 201 | ... 202 | }; 203 | */ 204 | 205 | 206 | const node/*: ReaderFragment*/ = { 207 | \\"kind\\": \\"Fragment\\", 208 | \\"name\\": \\"HomeItem_person\\", 209 | \\"type\\": \\"Person\\", 210 | \\"metadata\\": null, 211 | \\"argumentDefinitions\\": [], 212 | \\"selections\\": [ 213 | { 214 | \\"kind\\": \\"ScalarField\\", 215 | \\"alias\\": null, 216 | \\"name\\": \\"id\\", 217 | \\"args\\": null, 218 | \\"storageKey\\": null 219 | }, 220 | { 221 | \\"kind\\": \\"ScalarField\\", 222 | \\"alias\\": null, 223 | \\"name\\": \\"fullName\\", 224 | \\"args\\": null, 225 | \\"storageKey\\": null 226 | } 227 | ] 228 | }; 229 | // prettier-ignore 230 | (node/*: any*/).hash = 'bd415f20a6f47ec9a0098ed50db4d1df'; 231 | module.exports = node; 232 | " 233 | `; 234 | 235 | exports[`RelayCompilerWebpackPlugin generates graphql files correctly for a normal example 3`] = ` 236 | "/** 237 | * @flow 238 | */ 239 | 240 | /* eslint-disable */ 241 | 242 | 'use strict'; 243 | 244 | /*:: 245 | import type { ReaderFragment } from 'relay-runtime'; 246 | type HomeItem_person$ref = any; 247 | import type { FragmentReference } from \\"relay-runtime\\"; 248 | declare export opaque type Home_people$ref: FragmentReference; 249 | declare export opaque type Home_people$fragmentType: Home_people$ref; 250 | export type Home_people = $ReadOnlyArray<{| 251 | +id: string, 252 | +$fragmentRefs: HomeItem_person$ref, 253 | +$refType: Home_people$ref, 254 | |}>; 255 | export type Home_people$data = Home_people; 256 | export type Home_people$key = $ReadOnlyArray<{ 257 | +$data?: Home_people$data, 258 | +$fragmentRefs: Home_people$ref, 259 | ... 260 | }>; 261 | */ 262 | 263 | 264 | const node/*: ReaderFragment*/ = { 265 | \\"kind\\": \\"Fragment\\", 266 | \\"name\\": \\"Home_people\\", 267 | \\"type\\": \\"Person\\", 268 | \\"metadata\\": { 269 | \\"plural\\": true 270 | }, 271 | \\"argumentDefinitions\\": [], 272 | \\"selections\\": [ 273 | { 274 | \\"kind\\": \\"ScalarField\\", 275 | \\"alias\\": null, 276 | \\"name\\": \\"id\\", 277 | \\"args\\": null, 278 | \\"storageKey\\": null 279 | }, 280 | { 281 | \\"kind\\": \\"FragmentSpread\\", 282 | \\"name\\": \\"HomeItem_person\\", 283 | \\"args\\": null 284 | } 285 | ] 286 | }; 287 | // prettier-ignore 288 | (node/*: any*/).hash = 'e4f600a68462819c933487c314d53ed6'; 289 | module.exports = node; 290 | " 291 | `; 292 | 293 | exports[`RelayCompilerWebpackPlugin generates graphql files correctly for a normal example 4`] = ` 294 | "/** 295 | * @flow 296 | * @relayHash d299eee1fc201946b5e3b7ea7f83bb73 297 | */ 298 | 299 | /* eslint-disable */ 300 | 301 | 'use strict'; 302 | 303 | /*:: 304 | import type { ConcreteRequest } from 'relay-runtime'; 305 | type Home_people$ref = any; 306 | export type AppQueryVariables = {||}; 307 | export type AppQueryResponse = {| 308 | +people: ?$ReadOnlyArray 311 | |}; 312 | export type AppQuery = {| 313 | variables: AppQueryVariables, 314 | response: AppQueryResponse, 315 | |}; 316 | */ 317 | 318 | 319 | /* 320 | query AppQuery { 321 | people { 322 | ...Home_people 323 | id 324 | } 325 | } 326 | 327 | fragment HomeItem_person on Person { 328 | id 329 | fullName 330 | } 331 | 332 | fragment Home_people on Person { 333 | id 334 | ...HomeItem_person 335 | } 336 | */ 337 | 338 | const node/*: ConcreteRequest*/ = { 339 | \\"kind\\": \\"Request\\", 340 | \\"fragment\\": { 341 | \\"kind\\": \\"Fragment\\", 342 | \\"name\\": \\"AppQuery\\", 343 | \\"type\\": \\"Query\\", 344 | \\"metadata\\": null, 345 | \\"argumentDefinitions\\": [], 346 | \\"selections\\": [ 347 | { 348 | \\"kind\\": \\"LinkedField\\", 349 | \\"alias\\": null, 350 | \\"name\\": \\"people\\", 351 | \\"storageKey\\": null, 352 | \\"args\\": null, 353 | \\"concreteType\\": \\"Person\\", 354 | \\"plural\\": true, 355 | \\"selections\\": [ 356 | { 357 | \\"kind\\": \\"FragmentSpread\\", 358 | \\"name\\": \\"Home_people\\", 359 | \\"args\\": null 360 | } 361 | ] 362 | } 363 | ] 364 | }, 365 | \\"operation\\": { 366 | \\"kind\\": \\"Operation\\", 367 | \\"name\\": \\"AppQuery\\", 368 | \\"argumentDefinitions\\": [], 369 | \\"selections\\": [ 370 | { 371 | \\"kind\\": \\"LinkedField\\", 372 | \\"alias\\": null, 373 | \\"name\\": \\"people\\", 374 | \\"storageKey\\": null, 375 | \\"args\\": null, 376 | \\"concreteType\\": \\"Person\\", 377 | \\"plural\\": true, 378 | \\"selections\\": [ 379 | { 380 | \\"kind\\": \\"ScalarField\\", 381 | \\"alias\\": null, 382 | \\"name\\": \\"id\\", 383 | \\"args\\": null, 384 | \\"storageKey\\": null 385 | }, 386 | { 387 | \\"kind\\": \\"ScalarField\\", 388 | \\"alias\\": null, 389 | \\"name\\": \\"fullName\\", 390 | \\"args\\": null, 391 | \\"storageKey\\": null 392 | } 393 | ] 394 | } 395 | ] 396 | }, 397 | \\"params\\": { 398 | \\"operationKind\\": \\"query\\", 399 | \\"name\\": \\"AppQuery\\", 400 | \\"id\\": null, 401 | \\"text\\": \\"query AppQuery {\\\\n people {\\\\n ...Home_people\\\\n id\\\\n }\\\\n}\\\\n\\\\nfragment HomeItem_person on Person {\\\\n id\\\\n fullName\\\\n}\\\\n\\\\nfragment Home_people on Person {\\\\n id\\\\n ...HomeItem_person\\\\n}\\\\n\\", 402 | \\"metadata\\": {} 403 | } 404 | }; 405 | // prettier-ignore 406 | (node/*: any*/).hash = '09882a000717ccc4afa2102269b7f17f'; 407 | module.exports = node; 408 | " 409 | `; 410 | 411 | exports[`RelayCompilerWebpackPlugin generates graphql files correctly for a normal example 5`] = ` 412 | "/** 413 | * @flow 414 | * @relayHash fa2411483bd3de9f45b5ba00eb49a2b7 415 | */ 416 | 417 | /* eslint-disable */ 418 | 419 | 'use strict'; 420 | 421 | /*:: 422 | import type { ConcreteRequest } from 'relay-runtime'; 423 | export type AboutQueryVariables = {| 424 | id: string 425 | |}; 426 | export type AboutQueryResponse = {| 427 | +personById: ?{| 428 | +id: string, 429 | +firstName: string, 430 | +fullName: string, 431 | |} 432 | |}; 433 | export type AboutQuery = {| 434 | variables: AboutQueryVariables, 435 | response: AboutQueryResponse, 436 | |}; 437 | */ 438 | 439 | 440 | /* 441 | query AboutQuery( 442 | $id: ID! 443 | ) { 444 | personById(id: $id) { 445 | id 446 | firstName 447 | fullName 448 | } 449 | } 450 | */ 451 | 452 | const node/*: ConcreteRequest*/ = (function(){ 453 | var v0 = [ 454 | { 455 | \\"kind\\": \\"LocalArgument\\", 456 | \\"name\\": \\"id\\", 457 | \\"type\\": \\"ID!\\", 458 | \\"defaultValue\\": null 459 | } 460 | ], 461 | v1 = [ 462 | { 463 | \\"kind\\": \\"LinkedField\\", 464 | \\"alias\\": null, 465 | \\"name\\": \\"personById\\", 466 | \\"storageKey\\": null, 467 | \\"args\\": [ 468 | { 469 | \\"kind\\": \\"Variable\\", 470 | \\"name\\": \\"id\\", 471 | \\"variableName\\": \\"id\\" 472 | } 473 | ], 474 | \\"concreteType\\": \\"Person\\", 475 | \\"plural\\": false, 476 | \\"selections\\": [ 477 | { 478 | \\"kind\\": \\"ScalarField\\", 479 | \\"alias\\": null, 480 | \\"name\\": \\"id\\", 481 | \\"args\\": null, 482 | \\"storageKey\\": null 483 | }, 484 | { 485 | \\"kind\\": \\"ScalarField\\", 486 | \\"alias\\": null, 487 | \\"name\\": \\"firstName\\", 488 | \\"args\\": null, 489 | \\"storageKey\\": null 490 | }, 491 | { 492 | \\"kind\\": \\"ScalarField\\", 493 | \\"alias\\": null, 494 | \\"name\\": \\"fullName\\", 495 | \\"args\\": null, 496 | \\"storageKey\\": null 497 | } 498 | ] 499 | } 500 | ]; 501 | return { 502 | \\"kind\\": \\"Request\\", 503 | \\"fragment\\": { 504 | \\"kind\\": \\"Fragment\\", 505 | \\"name\\": \\"AboutQuery\\", 506 | \\"type\\": \\"Query\\", 507 | \\"metadata\\": null, 508 | \\"argumentDefinitions\\": (v0/*: any*/), 509 | \\"selections\\": (v1/*: any*/) 510 | }, 511 | \\"operation\\": { 512 | \\"kind\\": \\"Operation\\", 513 | \\"name\\": \\"AboutQuery\\", 514 | \\"argumentDefinitions\\": (v0/*: any*/), 515 | \\"selections\\": (v1/*: any*/) 516 | }, 517 | \\"params\\": { 518 | \\"operationKind\\": \\"query\\", 519 | \\"name\\": \\"AboutQuery\\", 520 | \\"id\\": null, 521 | \\"text\\": \\"query AboutQuery(\\\\n $id: ID!\\\\n) {\\\\n personById(id: $id) {\\\\n id\\\\n firstName\\\\n fullName\\\\n }\\\\n}\\\\n\\", 522 | \\"metadata\\": {} 523 | } 524 | }; 525 | })(); 526 | // prettier-ignore 527 | (node/*: any*/).hash = '244cfa95d89768b907f6815ff6a3007d'; 528 | module.exports = node; 529 | " 530 | `; 531 | 532 | exports[`RelayCompilerWebpackPlugin generates graphql files correctly for a normal example with --artifactDirectory option 1`] = ` 533 | "/** 534 | * @flow 535 | * @relayHash 5bd09d29f21e8c7772312a5d6d361bdd 536 | */ 537 | 538 | /* eslint-disable */ 539 | 540 | 'use strict'; 541 | 542 | /*:: 543 | import type { ConcreteRequest } from 'relay-runtime'; 544 | export type updateFirstNameMutationVariables = {| 545 | id: string, 546 | firstName: string, 547 | |}; 548 | export type updateFirstNameMutationResponse = {| 549 | +updateFirstName: {| 550 | +person: {| 551 | +firstName: string, 552 | +fullName: string, 553 | |} 554 | |} 555 | |}; 556 | export type updateFirstNameMutation = {| 557 | variables: updateFirstNameMutationVariables, 558 | response: updateFirstNameMutationResponse, 559 | |}; 560 | */ 561 | 562 | 563 | /* 564 | mutation updateFirstNameMutation( 565 | $id: ID! 566 | $firstName: String! 567 | ) { 568 | updateFirstName(id: $id, firstName: $firstName) { 569 | person { 570 | firstName 571 | fullName 572 | id 573 | } 574 | } 575 | } 576 | */ 577 | 578 | const node/*: ConcreteRequest*/ = (function(){ 579 | var v0 = [ 580 | { 581 | \\"kind\\": \\"LocalArgument\\", 582 | \\"name\\": \\"id\\", 583 | \\"type\\": \\"ID!\\", 584 | \\"defaultValue\\": null 585 | }, 586 | { 587 | \\"kind\\": \\"LocalArgument\\", 588 | \\"name\\": \\"firstName\\", 589 | \\"type\\": \\"String!\\", 590 | \\"defaultValue\\": null 591 | } 592 | ], 593 | v1 = [ 594 | { 595 | \\"kind\\": \\"Variable\\", 596 | \\"name\\": \\"firstName\\", 597 | \\"variableName\\": \\"firstName\\" 598 | }, 599 | { 600 | \\"kind\\": \\"Variable\\", 601 | \\"name\\": \\"id\\", 602 | \\"variableName\\": \\"id\\" 603 | } 604 | ], 605 | v2 = { 606 | \\"kind\\": \\"ScalarField\\", 607 | \\"alias\\": null, 608 | \\"name\\": \\"firstName\\", 609 | \\"args\\": null, 610 | \\"storageKey\\": null 611 | }, 612 | v3 = { 613 | \\"kind\\": \\"ScalarField\\", 614 | \\"alias\\": null, 615 | \\"name\\": \\"fullName\\", 616 | \\"args\\": null, 617 | \\"storageKey\\": null 618 | }; 619 | return { 620 | \\"kind\\": \\"Request\\", 621 | \\"fragment\\": { 622 | \\"kind\\": \\"Fragment\\", 623 | \\"name\\": \\"updateFirstNameMutation\\", 624 | \\"type\\": \\"Mutation\\", 625 | \\"metadata\\": null, 626 | \\"argumentDefinitions\\": (v0/*: any*/), 627 | \\"selections\\": [ 628 | { 629 | \\"kind\\": \\"LinkedField\\", 630 | \\"alias\\": null, 631 | \\"name\\": \\"updateFirstName\\", 632 | \\"storageKey\\": null, 633 | \\"args\\": (v1/*: any*/), 634 | \\"concreteType\\": \\"UpdateFirstNameOutput\\", 635 | \\"plural\\": false, 636 | \\"selections\\": [ 637 | { 638 | \\"kind\\": \\"LinkedField\\", 639 | \\"alias\\": null, 640 | \\"name\\": \\"person\\", 641 | \\"storageKey\\": null, 642 | \\"args\\": null, 643 | \\"concreteType\\": \\"Person\\", 644 | \\"plural\\": false, 645 | \\"selections\\": [ 646 | (v2/*: any*/), 647 | (v3/*: any*/) 648 | ] 649 | } 650 | ] 651 | } 652 | ] 653 | }, 654 | \\"operation\\": { 655 | \\"kind\\": \\"Operation\\", 656 | \\"name\\": \\"updateFirstNameMutation\\", 657 | \\"argumentDefinitions\\": (v0/*: any*/), 658 | \\"selections\\": [ 659 | { 660 | \\"kind\\": \\"LinkedField\\", 661 | \\"alias\\": null, 662 | \\"name\\": \\"updateFirstName\\", 663 | \\"storageKey\\": null, 664 | \\"args\\": (v1/*: any*/), 665 | \\"concreteType\\": \\"UpdateFirstNameOutput\\", 666 | \\"plural\\": false, 667 | \\"selections\\": [ 668 | { 669 | \\"kind\\": \\"LinkedField\\", 670 | \\"alias\\": null, 671 | \\"name\\": \\"person\\", 672 | \\"storageKey\\": null, 673 | \\"args\\": null, 674 | \\"concreteType\\": \\"Person\\", 675 | \\"plural\\": false, 676 | \\"selections\\": [ 677 | (v2/*: any*/), 678 | (v3/*: any*/), 679 | { 680 | \\"kind\\": \\"ScalarField\\", 681 | \\"alias\\": null, 682 | \\"name\\": \\"id\\", 683 | \\"args\\": null, 684 | \\"storageKey\\": null 685 | } 686 | ] 687 | } 688 | ] 689 | } 690 | ] 691 | }, 692 | \\"params\\": { 693 | \\"operationKind\\": \\"mutation\\", 694 | \\"name\\": \\"updateFirstNameMutation\\", 695 | \\"id\\": null, 696 | \\"text\\": \\"mutation updateFirstNameMutation(\\\\n $id: ID!\\\\n $firstName: String!\\\\n) {\\\\n updateFirstName(id: $id, firstName: $firstName) {\\\\n person {\\\\n firstName\\\\n fullName\\\\n id\\\\n }\\\\n }\\\\n}\\\\n\\", 697 | \\"metadata\\": {} 698 | } 699 | }; 700 | })(); 701 | // prettier-ignore 702 | (node/*: any*/).hash = '8ea2fcfefe6dc15aea4c7e70663a11d8'; 703 | module.exports = node; 704 | " 705 | `; 706 | 707 | exports[`RelayCompilerWebpackPlugin generates graphql files correctly for a normal example with --artifactDirectory option 2`] = ` 708 | "/** 709 | * @flow 710 | */ 711 | 712 | /* eslint-disable */ 713 | 714 | 'use strict'; 715 | 716 | /*:: 717 | import type { ReaderFragment } from 'relay-runtime'; 718 | import type { FragmentReference } from \\"relay-runtime\\"; 719 | declare export opaque type HomeItem_person$ref: FragmentReference; 720 | declare export opaque type HomeItem_person$fragmentType: HomeItem_person$ref; 721 | export type HomeItem_person = {| 722 | +id: string, 723 | +fullName: string, 724 | +$refType: HomeItem_person$ref, 725 | |}; 726 | export type HomeItem_person$data = HomeItem_person; 727 | export type HomeItem_person$key = { 728 | +$data?: HomeItem_person$data, 729 | +$fragmentRefs: HomeItem_person$ref, 730 | ... 731 | }; 732 | */ 733 | 734 | 735 | const node/*: ReaderFragment*/ = { 736 | \\"kind\\": \\"Fragment\\", 737 | \\"name\\": \\"HomeItem_person\\", 738 | \\"type\\": \\"Person\\", 739 | \\"metadata\\": null, 740 | \\"argumentDefinitions\\": [], 741 | \\"selections\\": [ 742 | { 743 | \\"kind\\": \\"ScalarField\\", 744 | \\"alias\\": null, 745 | \\"name\\": \\"id\\", 746 | \\"args\\": null, 747 | \\"storageKey\\": null 748 | }, 749 | { 750 | \\"kind\\": \\"ScalarField\\", 751 | \\"alias\\": null, 752 | \\"name\\": \\"fullName\\", 753 | \\"args\\": null, 754 | \\"storageKey\\": null 755 | } 756 | ] 757 | }; 758 | // prettier-ignore 759 | (node/*: any*/).hash = 'bd415f20a6f47ec9a0098ed50db4d1df'; 760 | module.exports = node; 761 | " 762 | `; 763 | 764 | exports[`RelayCompilerWebpackPlugin generates graphql files correctly for a normal example with --artifactDirectory option 3`] = ` 765 | "/** 766 | * @flow 767 | */ 768 | 769 | /* eslint-disable */ 770 | 771 | 'use strict'; 772 | 773 | /*:: 774 | import type { ReaderFragment } from 'relay-runtime'; 775 | import type { HomeItem_person$ref } from \\"./HomeItem_person.graphql\\"; 776 | import type { FragmentReference } from \\"relay-runtime\\"; 777 | declare export opaque type Home_people$ref: FragmentReference; 778 | declare export opaque type Home_people$fragmentType: Home_people$ref; 779 | export type Home_people = $ReadOnlyArray<{| 780 | +id: string, 781 | +$fragmentRefs: HomeItem_person$ref, 782 | +$refType: Home_people$ref, 783 | |}>; 784 | export type Home_people$data = Home_people; 785 | export type Home_people$key = $ReadOnlyArray<{ 786 | +$data?: Home_people$data, 787 | +$fragmentRefs: Home_people$ref, 788 | ... 789 | }>; 790 | */ 791 | 792 | 793 | const node/*: ReaderFragment*/ = { 794 | \\"kind\\": \\"Fragment\\", 795 | \\"name\\": \\"Home_people\\", 796 | \\"type\\": \\"Person\\", 797 | \\"metadata\\": { 798 | \\"plural\\": true 799 | }, 800 | \\"argumentDefinitions\\": [], 801 | \\"selections\\": [ 802 | { 803 | \\"kind\\": \\"ScalarField\\", 804 | \\"alias\\": null, 805 | \\"name\\": \\"id\\", 806 | \\"args\\": null, 807 | \\"storageKey\\": null 808 | }, 809 | { 810 | \\"kind\\": \\"FragmentSpread\\", 811 | \\"name\\": \\"HomeItem_person\\", 812 | \\"args\\": null 813 | } 814 | ] 815 | }; 816 | // prettier-ignore 817 | (node/*: any*/).hash = 'e4f600a68462819c933487c314d53ed6'; 818 | module.exports = node; 819 | " 820 | `; 821 | 822 | exports[`RelayCompilerWebpackPlugin generates graphql files correctly for a normal example with --artifactDirectory option 4`] = ` 823 | "/** 824 | * @flow 825 | * @relayHash ae570241219ec7981a375a9d6bb66a2c 826 | */ 827 | 828 | /* eslint-disable */ 829 | 830 | 'use strict'; 831 | 832 | /*:: 833 | import type { ConcreteRequest } from 'relay-runtime'; 834 | import type { Home_people$ref } from \\"./Home_people.graphql\\"; 835 | export type AppQueryVariables = {||}; 836 | export type AppQueryResponse = {| 837 | +people: ?$ReadOnlyArray 840 | |}; 841 | export type AppQuery = {| 842 | variables: AppQueryVariables, 843 | response: AppQueryResponse, 844 | |}; 845 | */ 846 | 847 | 848 | /* 849 | query AppQuery { 850 | people { 851 | ...Home_people 852 | id 853 | } 854 | } 855 | 856 | fragment HomeItem_person on Person { 857 | id 858 | fullName 859 | } 860 | 861 | fragment Home_people on Person { 862 | id 863 | ...HomeItem_person 864 | } 865 | */ 866 | 867 | const node/*: ConcreteRequest*/ = { 868 | \\"kind\\": \\"Request\\", 869 | \\"fragment\\": { 870 | \\"kind\\": \\"Fragment\\", 871 | \\"name\\": \\"AppQuery\\", 872 | \\"type\\": \\"Query\\", 873 | \\"metadata\\": null, 874 | \\"argumentDefinitions\\": [], 875 | \\"selections\\": [ 876 | { 877 | \\"kind\\": \\"LinkedField\\", 878 | \\"alias\\": null, 879 | \\"name\\": \\"people\\", 880 | \\"storageKey\\": null, 881 | \\"args\\": null, 882 | \\"concreteType\\": \\"Person\\", 883 | \\"plural\\": true, 884 | \\"selections\\": [ 885 | { 886 | \\"kind\\": \\"FragmentSpread\\", 887 | \\"name\\": \\"Home_people\\", 888 | \\"args\\": null 889 | } 890 | ] 891 | } 892 | ] 893 | }, 894 | \\"operation\\": { 895 | \\"kind\\": \\"Operation\\", 896 | \\"name\\": \\"AppQuery\\", 897 | \\"argumentDefinitions\\": [], 898 | \\"selections\\": [ 899 | { 900 | \\"kind\\": \\"LinkedField\\", 901 | \\"alias\\": null, 902 | \\"name\\": \\"people\\", 903 | \\"storageKey\\": null, 904 | \\"args\\": null, 905 | \\"concreteType\\": \\"Person\\", 906 | \\"plural\\": true, 907 | \\"selections\\": [ 908 | { 909 | \\"kind\\": \\"ScalarField\\", 910 | \\"alias\\": null, 911 | \\"name\\": \\"id\\", 912 | \\"args\\": null, 913 | \\"storageKey\\": null 914 | }, 915 | { 916 | \\"kind\\": \\"ScalarField\\", 917 | \\"alias\\": null, 918 | \\"name\\": \\"fullName\\", 919 | \\"args\\": null, 920 | \\"storageKey\\": null 921 | } 922 | ] 923 | } 924 | ] 925 | }, 926 | \\"params\\": { 927 | \\"operationKind\\": \\"query\\", 928 | \\"name\\": \\"AppQuery\\", 929 | \\"id\\": null, 930 | \\"text\\": \\"query AppQuery {\\\\n people {\\\\n ...Home_people\\\\n id\\\\n }\\\\n}\\\\n\\\\nfragment HomeItem_person on Person {\\\\n id\\\\n fullName\\\\n}\\\\n\\\\nfragment Home_people on Person {\\\\n id\\\\n ...HomeItem_person\\\\n}\\\\n\\", 931 | \\"metadata\\": {} 932 | } 933 | }; 934 | // prettier-ignore 935 | (node/*: any*/).hash = '09882a000717ccc4afa2102269b7f17f'; 936 | module.exports = node; 937 | " 938 | `; 939 | 940 | exports[`RelayCompilerWebpackPlugin generates graphql files correctly for a normal example with --artifactDirectory option 5`] = ` 941 | "/** 942 | * @flow 943 | * @relayHash fa2411483bd3de9f45b5ba00eb49a2b7 944 | */ 945 | 946 | /* eslint-disable */ 947 | 948 | 'use strict'; 949 | 950 | /*:: 951 | import type { ConcreteRequest } from 'relay-runtime'; 952 | export type AboutQueryVariables = {| 953 | id: string 954 | |}; 955 | export type AboutQueryResponse = {| 956 | +personById: ?{| 957 | +id: string, 958 | +firstName: string, 959 | +fullName: string, 960 | |} 961 | |}; 962 | export type AboutQuery = {| 963 | variables: AboutQueryVariables, 964 | response: AboutQueryResponse, 965 | |}; 966 | */ 967 | 968 | 969 | /* 970 | query AboutQuery( 971 | $id: ID! 972 | ) { 973 | personById(id: $id) { 974 | id 975 | firstName 976 | fullName 977 | } 978 | } 979 | */ 980 | 981 | const node/*: ConcreteRequest*/ = (function(){ 982 | var v0 = [ 983 | { 984 | \\"kind\\": \\"LocalArgument\\", 985 | \\"name\\": \\"id\\", 986 | \\"type\\": \\"ID!\\", 987 | \\"defaultValue\\": null 988 | } 989 | ], 990 | v1 = [ 991 | { 992 | \\"kind\\": \\"LinkedField\\", 993 | \\"alias\\": null, 994 | \\"name\\": \\"personById\\", 995 | \\"storageKey\\": null, 996 | \\"args\\": [ 997 | { 998 | \\"kind\\": \\"Variable\\", 999 | \\"name\\": \\"id\\", 1000 | \\"variableName\\": \\"id\\" 1001 | } 1002 | ], 1003 | \\"concreteType\\": \\"Person\\", 1004 | \\"plural\\": false, 1005 | \\"selections\\": [ 1006 | { 1007 | \\"kind\\": \\"ScalarField\\", 1008 | \\"alias\\": null, 1009 | \\"name\\": \\"id\\", 1010 | \\"args\\": null, 1011 | \\"storageKey\\": null 1012 | }, 1013 | { 1014 | \\"kind\\": \\"ScalarField\\", 1015 | \\"alias\\": null, 1016 | \\"name\\": \\"firstName\\", 1017 | \\"args\\": null, 1018 | \\"storageKey\\": null 1019 | }, 1020 | { 1021 | \\"kind\\": \\"ScalarField\\", 1022 | \\"alias\\": null, 1023 | \\"name\\": \\"fullName\\", 1024 | \\"args\\": null, 1025 | \\"storageKey\\": null 1026 | } 1027 | ] 1028 | } 1029 | ]; 1030 | return { 1031 | \\"kind\\": \\"Request\\", 1032 | \\"fragment\\": { 1033 | \\"kind\\": \\"Fragment\\", 1034 | \\"name\\": \\"AboutQuery\\", 1035 | \\"type\\": \\"Query\\", 1036 | \\"metadata\\": null, 1037 | \\"argumentDefinitions\\": (v0/*: any*/), 1038 | \\"selections\\": (v1/*: any*/) 1039 | }, 1040 | \\"operation\\": { 1041 | \\"kind\\": \\"Operation\\", 1042 | \\"name\\": \\"AboutQuery\\", 1043 | \\"argumentDefinitions\\": (v0/*: any*/), 1044 | \\"selections\\": (v1/*: any*/) 1045 | }, 1046 | \\"params\\": { 1047 | \\"operationKind\\": \\"query\\", 1048 | \\"name\\": \\"AboutQuery\\", 1049 | \\"id\\": null, 1050 | \\"text\\": \\"query AboutQuery(\\\\n $id: ID!\\\\n) {\\\\n personById(id: $id) {\\\\n id\\\\n firstName\\\\n fullName\\\\n }\\\\n}\\\\n\\", 1051 | \\"metadata\\": {} 1052 | } 1053 | }; 1054 | })(); 1055 | // prettier-ignore 1056 | (node/*: any*/).hash = '244cfa95d89768b907f6815ff6a3007d'; 1057 | module.exports = node; 1058 | " 1059 | `; 1060 | -------------------------------------------------------------------------------- /test/fixtureProject/schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "data": { 3 | "__schema": { 4 | "queryType": { 5 | "name": "Query" 6 | }, 7 | "mutationType": { 8 | "name": "Mutation" 9 | }, 10 | "subscriptionType": null, 11 | "types": [ 12 | { 13 | "kind": "OBJECT", 14 | "name": "Query", 15 | "description": null, 16 | "fields": [ 17 | { 18 | "name": "people", 19 | "description": null, 20 | "args": [], 21 | "type": { 22 | "kind": "LIST", 23 | "name": null, 24 | "ofType": { 25 | "kind": "OBJECT", 26 | "name": "Person", 27 | "ofType": null 28 | } 29 | }, 30 | "isDeprecated": false, 31 | "deprecationReason": null 32 | }, 33 | { 34 | "name": "personById", 35 | "description": null, 36 | "args": [ 37 | { 38 | "name": "id", 39 | "description": null, 40 | "type": { 41 | "kind": "NON_NULL", 42 | "name": null, 43 | "ofType": { 44 | "kind": "SCALAR", 45 | "name": "ID", 46 | "ofType": null 47 | } 48 | }, 49 | "defaultValue": null 50 | } 51 | ], 52 | "type": { 53 | "kind": "OBJECT", 54 | "name": "Person", 55 | "ofType": null 56 | }, 57 | "isDeprecated": false, 58 | "deprecationReason": null 59 | } 60 | ], 61 | "inputFields": null, 62 | "interfaces": [], 63 | "enumValues": null, 64 | "possibleTypes": null 65 | }, 66 | { 67 | "kind": "OBJECT", 68 | "name": "Person", 69 | "description": null, 70 | "fields": [ 71 | { 72 | "name": "id", 73 | "description": null, 74 | "args": [], 75 | "type": { 76 | "kind": "NON_NULL", 77 | "name": null, 78 | "ofType": { 79 | "kind": "SCALAR", 80 | "name": "ID", 81 | "ofType": null 82 | } 83 | }, 84 | "isDeprecated": false, 85 | "deprecationReason": null 86 | }, 87 | { 88 | "name": "firstName", 89 | "description": null, 90 | "args": [], 91 | "type": { 92 | "kind": "NON_NULL", 93 | "name": null, 94 | "ofType": { 95 | "kind": "SCALAR", 96 | "name": "String", 97 | "ofType": null 98 | } 99 | }, 100 | "isDeprecated": false, 101 | "deprecationReason": null 102 | }, 103 | { 104 | "name": "lastName", 105 | "description": null, 106 | "args": [], 107 | "type": { 108 | "kind": "NON_NULL", 109 | "name": null, 110 | "ofType": { 111 | "kind": "SCALAR", 112 | "name": "String", 113 | "ofType": null 114 | } 115 | }, 116 | "isDeprecated": false, 117 | "deprecationReason": null 118 | }, 119 | { 120 | "name": "fullName", 121 | "description": null, 122 | "args": [], 123 | "type": { 124 | "kind": "NON_NULL", 125 | "name": null, 126 | "ofType": { 127 | "kind": "SCALAR", 128 | "name": "String", 129 | "ofType": null 130 | } 131 | }, 132 | "isDeprecated": false, 133 | "deprecationReason": null 134 | } 135 | ], 136 | "inputFields": null, 137 | "interfaces": [], 138 | "enumValues": null, 139 | "possibleTypes": null 140 | }, 141 | { 142 | "kind": "SCALAR", 143 | "name": "ID", 144 | "description": "The `ID` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `\"4\"`) or integer (such as `4`) input value will be accepted as an ID.", 145 | "fields": null, 146 | "inputFields": null, 147 | "interfaces": null, 148 | "enumValues": null, 149 | "possibleTypes": null 150 | }, 151 | { 152 | "kind": "SCALAR", 153 | "name": "String", 154 | "description": "The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.", 155 | "fields": null, 156 | "inputFields": null, 157 | "interfaces": null, 158 | "enumValues": null, 159 | "possibleTypes": null 160 | }, 161 | { 162 | "kind": "OBJECT", 163 | "name": "Mutation", 164 | "description": null, 165 | "fields": [ 166 | { 167 | "name": "updateFirstName", 168 | "description": null, 169 | "args": [ 170 | { 171 | "name": "id", 172 | "description": null, 173 | "type": { 174 | "kind": "NON_NULL", 175 | "name": null, 176 | "ofType": { 177 | "kind": "SCALAR", 178 | "name": "ID", 179 | "ofType": null 180 | } 181 | }, 182 | "defaultValue": null 183 | }, 184 | { 185 | "name": "firstName", 186 | "description": null, 187 | "type": { 188 | "kind": "NON_NULL", 189 | "name": null, 190 | "ofType": { 191 | "kind": "SCALAR", 192 | "name": "String", 193 | "ofType": null 194 | } 195 | }, 196 | "defaultValue": null 197 | } 198 | ], 199 | "type": { 200 | "kind": "NON_NULL", 201 | "name": null, 202 | "ofType": { 203 | "kind": "OBJECT", 204 | "name": "UpdateFirstNameOutput", 205 | "ofType": null 206 | } 207 | }, 208 | "isDeprecated": false, 209 | "deprecationReason": null 210 | } 211 | ], 212 | "inputFields": null, 213 | "interfaces": [], 214 | "enumValues": null, 215 | "possibleTypes": null 216 | }, 217 | { 218 | "kind": "OBJECT", 219 | "name": "UpdateFirstNameOutput", 220 | "description": null, 221 | "fields": [ 222 | { 223 | "name": "person", 224 | "description": null, 225 | "args": [], 226 | "type": { 227 | "kind": "NON_NULL", 228 | "name": null, 229 | "ofType": { 230 | "kind": "OBJECT", 231 | "name": "Person", 232 | "ofType": null 233 | } 234 | }, 235 | "isDeprecated": false, 236 | "deprecationReason": null 237 | } 238 | ], 239 | "inputFields": null, 240 | "interfaces": [], 241 | "enumValues": null, 242 | "possibleTypes": null 243 | }, 244 | { 245 | "kind": "OBJECT", 246 | "name": "__Schema", 247 | "description": "A GraphQL Schema defines the capabilities of a GraphQL server. It exposes all available types and directives on the server, as well as the entry points for query, mutation, and subscription operations.", 248 | "fields": [ 249 | { 250 | "name": "types", 251 | "description": "A list of all types supported by this server.", 252 | "args": [], 253 | "type": { 254 | "kind": "NON_NULL", 255 | "name": null, 256 | "ofType": { 257 | "kind": "LIST", 258 | "name": null, 259 | "ofType": { 260 | "kind": "NON_NULL", 261 | "name": null, 262 | "ofType": { 263 | "kind": "OBJECT", 264 | "name": "__Type", 265 | "ofType": null 266 | } 267 | } 268 | } 269 | }, 270 | "isDeprecated": false, 271 | "deprecationReason": null 272 | }, 273 | { 274 | "name": "queryType", 275 | "description": "The type that query operations will be rooted at.", 276 | "args": [], 277 | "type": { 278 | "kind": "NON_NULL", 279 | "name": null, 280 | "ofType": { 281 | "kind": "OBJECT", 282 | "name": "__Type", 283 | "ofType": null 284 | } 285 | }, 286 | "isDeprecated": false, 287 | "deprecationReason": null 288 | }, 289 | { 290 | "name": "mutationType", 291 | "description": "If this server supports mutation, the type that mutation operations will be rooted at.", 292 | "args": [], 293 | "type": { 294 | "kind": "OBJECT", 295 | "name": "__Type", 296 | "ofType": null 297 | }, 298 | "isDeprecated": false, 299 | "deprecationReason": null 300 | }, 301 | { 302 | "name": "subscriptionType", 303 | "description": "If this server support subscription, the type that subscription operations will be rooted at.", 304 | "args": [], 305 | "type": { 306 | "kind": "OBJECT", 307 | "name": "__Type", 308 | "ofType": null 309 | }, 310 | "isDeprecated": false, 311 | "deprecationReason": null 312 | }, 313 | { 314 | "name": "directives", 315 | "description": "A list of all directives supported by this server.", 316 | "args": [], 317 | "type": { 318 | "kind": "NON_NULL", 319 | "name": null, 320 | "ofType": { 321 | "kind": "LIST", 322 | "name": null, 323 | "ofType": { 324 | "kind": "NON_NULL", 325 | "name": null, 326 | "ofType": { 327 | "kind": "OBJECT", 328 | "name": "__Directive", 329 | "ofType": null 330 | } 331 | } 332 | } 333 | }, 334 | "isDeprecated": false, 335 | "deprecationReason": null 336 | } 337 | ], 338 | "inputFields": null, 339 | "interfaces": [], 340 | "enumValues": null, 341 | "possibleTypes": null 342 | }, 343 | { 344 | "kind": "OBJECT", 345 | "name": "__Type", 346 | "description": "The fundamental unit of any GraphQL Schema is the type. There are many kinds of types in GraphQL as represented by the `__TypeKind` enum.\n\nDepending on the kind of a type, certain fields describe information about that type. Scalar types provide no information beyond a name and description, while Enum types provide their values. Object and Interface types provide the fields they describe. Abstract types, Union and Interface, provide the Object types possible at runtime. List and NonNull types compose other types.", 347 | "fields": [ 348 | { 349 | "name": "kind", 350 | "description": null, 351 | "args": [], 352 | "type": { 353 | "kind": "NON_NULL", 354 | "name": null, 355 | "ofType": { 356 | "kind": "ENUM", 357 | "name": "__TypeKind", 358 | "ofType": null 359 | } 360 | }, 361 | "isDeprecated": false, 362 | "deprecationReason": null 363 | }, 364 | { 365 | "name": "name", 366 | "description": null, 367 | "args": [], 368 | "type": { 369 | "kind": "SCALAR", 370 | "name": "String", 371 | "ofType": null 372 | }, 373 | "isDeprecated": false, 374 | "deprecationReason": null 375 | }, 376 | { 377 | "name": "description", 378 | "description": null, 379 | "args": [], 380 | "type": { 381 | "kind": "SCALAR", 382 | "name": "String", 383 | "ofType": null 384 | }, 385 | "isDeprecated": false, 386 | "deprecationReason": null 387 | }, 388 | { 389 | "name": "fields", 390 | "description": null, 391 | "args": [ 392 | { 393 | "name": "includeDeprecated", 394 | "description": null, 395 | "type": { 396 | "kind": "SCALAR", 397 | "name": "Boolean", 398 | "ofType": null 399 | }, 400 | "defaultValue": "false" 401 | } 402 | ], 403 | "type": { 404 | "kind": "LIST", 405 | "name": null, 406 | "ofType": { 407 | "kind": "NON_NULL", 408 | "name": null, 409 | "ofType": { 410 | "kind": "OBJECT", 411 | "name": "__Field", 412 | "ofType": null 413 | } 414 | } 415 | }, 416 | "isDeprecated": false, 417 | "deprecationReason": null 418 | }, 419 | { 420 | "name": "interfaces", 421 | "description": null, 422 | "args": [], 423 | "type": { 424 | "kind": "LIST", 425 | "name": null, 426 | "ofType": { 427 | "kind": "NON_NULL", 428 | "name": null, 429 | "ofType": { 430 | "kind": "OBJECT", 431 | "name": "__Type", 432 | "ofType": null 433 | } 434 | } 435 | }, 436 | "isDeprecated": false, 437 | "deprecationReason": null 438 | }, 439 | { 440 | "name": "possibleTypes", 441 | "description": null, 442 | "args": [], 443 | "type": { 444 | "kind": "LIST", 445 | "name": null, 446 | "ofType": { 447 | "kind": "NON_NULL", 448 | "name": null, 449 | "ofType": { 450 | "kind": "OBJECT", 451 | "name": "__Type", 452 | "ofType": null 453 | } 454 | } 455 | }, 456 | "isDeprecated": false, 457 | "deprecationReason": null 458 | }, 459 | { 460 | "name": "enumValues", 461 | "description": null, 462 | "args": [ 463 | { 464 | "name": "includeDeprecated", 465 | "description": null, 466 | "type": { 467 | "kind": "SCALAR", 468 | "name": "Boolean", 469 | "ofType": null 470 | }, 471 | "defaultValue": "false" 472 | } 473 | ], 474 | "type": { 475 | "kind": "LIST", 476 | "name": null, 477 | "ofType": { 478 | "kind": "NON_NULL", 479 | "name": null, 480 | "ofType": { 481 | "kind": "OBJECT", 482 | "name": "__EnumValue", 483 | "ofType": null 484 | } 485 | } 486 | }, 487 | "isDeprecated": false, 488 | "deprecationReason": null 489 | }, 490 | { 491 | "name": "inputFields", 492 | "description": null, 493 | "args": [], 494 | "type": { 495 | "kind": "LIST", 496 | "name": null, 497 | "ofType": { 498 | "kind": "NON_NULL", 499 | "name": null, 500 | "ofType": { 501 | "kind": "OBJECT", 502 | "name": "__InputValue", 503 | "ofType": null 504 | } 505 | } 506 | }, 507 | "isDeprecated": false, 508 | "deprecationReason": null 509 | }, 510 | { 511 | "name": "ofType", 512 | "description": null, 513 | "args": [], 514 | "type": { 515 | "kind": "OBJECT", 516 | "name": "__Type", 517 | "ofType": null 518 | }, 519 | "isDeprecated": false, 520 | "deprecationReason": null 521 | } 522 | ], 523 | "inputFields": null, 524 | "interfaces": [], 525 | "enumValues": null, 526 | "possibleTypes": null 527 | }, 528 | { 529 | "kind": "ENUM", 530 | "name": "__TypeKind", 531 | "description": "An enum describing what kind of type a given `__Type` is.", 532 | "fields": null, 533 | "inputFields": null, 534 | "interfaces": null, 535 | "enumValues": [ 536 | { 537 | "name": "SCALAR", 538 | "description": "Indicates this type is a scalar.", 539 | "isDeprecated": false, 540 | "deprecationReason": null 541 | }, 542 | { 543 | "name": "OBJECT", 544 | "description": "Indicates this type is an object. `fields` and `interfaces` are valid fields.", 545 | "isDeprecated": false, 546 | "deprecationReason": null 547 | }, 548 | { 549 | "name": "INTERFACE", 550 | "description": "Indicates this type is an interface. `fields` and `possibleTypes` are valid fields.", 551 | "isDeprecated": false, 552 | "deprecationReason": null 553 | }, 554 | { 555 | "name": "UNION", 556 | "description": "Indicates this type is a union. `possibleTypes` is a valid field.", 557 | "isDeprecated": false, 558 | "deprecationReason": null 559 | }, 560 | { 561 | "name": "ENUM", 562 | "description": "Indicates this type is an enum. `enumValues` is a valid field.", 563 | "isDeprecated": false, 564 | "deprecationReason": null 565 | }, 566 | { 567 | "name": "INPUT_OBJECT", 568 | "description": "Indicates this type is an input object. `inputFields` is a valid field.", 569 | "isDeprecated": false, 570 | "deprecationReason": null 571 | }, 572 | { 573 | "name": "LIST", 574 | "description": "Indicates this type is a list. `ofType` is a valid field.", 575 | "isDeprecated": false, 576 | "deprecationReason": null 577 | }, 578 | { 579 | "name": "NON_NULL", 580 | "description": "Indicates this type is a non-null. `ofType` is a valid field.", 581 | "isDeprecated": false, 582 | "deprecationReason": null 583 | } 584 | ], 585 | "possibleTypes": null 586 | }, 587 | { 588 | "kind": "SCALAR", 589 | "name": "Boolean", 590 | "description": "The `Boolean` scalar type represents `true` or `false`.", 591 | "fields": null, 592 | "inputFields": null, 593 | "interfaces": null, 594 | "enumValues": null, 595 | "possibleTypes": null 596 | }, 597 | { 598 | "kind": "OBJECT", 599 | "name": "__Field", 600 | "description": "Object and Interface types are described by a list of Fields, each of which has a name, potentially a list of arguments, and a return type.", 601 | "fields": [ 602 | { 603 | "name": "name", 604 | "description": null, 605 | "args": [], 606 | "type": { 607 | "kind": "NON_NULL", 608 | "name": null, 609 | "ofType": { 610 | "kind": "SCALAR", 611 | "name": "String", 612 | "ofType": null 613 | } 614 | }, 615 | "isDeprecated": false, 616 | "deprecationReason": null 617 | }, 618 | { 619 | "name": "description", 620 | "description": null, 621 | "args": [], 622 | "type": { 623 | "kind": "SCALAR", 624 | "name": "String", 625 | "ofType": null 626 | }, 627 | "isDeprecated": false, 628 | "deprecationReason": null 629 | }, 630 | { 631 | "name": "args", 632 | "description": null, 633 | "args": [], 634 | "type": { 635 | "kind": "NON_NULL", 636 | "name": null, 637 | "ofType": { 638 | "kind": "LIST", 639 | "name": null, 640 | "ofType": { 641 | "kind": "NON_NULL", 642 | "name": null, 643 | "ofType": { 644 | "kind": "OBJECT", 645 | "name": "__InputValue", 646 | "ofType": null 647 | } 648 | } 649 | } 650 | }, 651 | "isDeprecated": false, 652 | "deprecationReason": null 653 | }, 654 | { 655 | "name": "type", 656 | "description": null, 657 | "args": [], 658 | "type": { 659 | "kind": "NON_NULL", 660 | "name": null, 661 | "ofType": { 662 | "kind": "OBJECT", 663 | "name": "__Type", 664 | "ofType": null 665 | } 666 | }, 667 | "isDeprecated": false, 668 | "deprecationReason": null 669 | }, 670 | { 671 | "name": "isDeprecated", 672 | "description": null, 673 | "args": [], 674 | "type": { 675 | "kind": "NON_NULL", 676 | "name": null, 677 | "ofType": { 678 | "kind": "SCALAR", 679 | "name": "Boolean", 680 | "ofType": null 681 | } 682 | }, 683 | "isDeprecated": false, 684 | "deprecationReason": null 685 | }, 686 | { 687 | "name": "deprecationReason", 688 | "description": null, 689 | "args": [], 690 | "type": { 691 | "kind": "SCALAR", 692 | "name": "String", 693 | "ofType": null 694 | }, 695 | "isDeprecated": false, 696 | "deprecationReason": null 697 | } 698 | ], 699 | "inputFields": null, 700 | "interfaces": [], 701 | "enumValues": null, 702 | "possibleTypes": null 703 | }, 704 | { 705 | "kind": "OBJECT", 706 | "name": "__InputValue", 707 | "description": "Arguments provided to Fields or Directives and the input fields of an InputObject are represented as Input Values which describe their type and optionally a default value.", 708 | "fields": [ 709 | { 710 | "name": "name", 711 | "description": null, 712 | "args": [], 713 | "type": { 714 | "kind": "NON_NULL", 715 | "name": null, 716 | "ofType": { 717 | "kind": "SCALAR", 718 | "name": "String", 719 | "ofType": null 720 | } 721 | }, 722 | "isDeprecated": false, 723 | "deprecationReason": null 724 | }, 725 | { 726 | "name": "description", 727 | "description": null, 728 | "args": [], 729 | "type": { 730 | "kind": "SCALAR", 731 | "name": "String", 732 | "ofType": null 733 | }, 734 | "isDeprecated": false, 735 | "deprecationReason": null 736 | }, 737 | { 738 | "name": "type", 739 | "description": null, 740 | "args": [], 741 | "type": { 742 | "kind": "NON_NULL", 743 | "name": null, 744 | "ofType": { 745 | "kind": "OBJECT", 746 | "name": "__Type", 747 | "ofType": null 748 | } 749 | }, 750 | "isDeprecated": false, 751 | "deprecationReason": null 752 | }, 753 | { 754 | "name": "defaultValue", 755 | "description": "A GraphQL-formatted string representing the default value for this input value.", 756 | "args": [], 757 | "type": { 758 | "kind": "SCALAR", 759 | "name": "String", 760 | "ofType": null 761 | }, 762 | "isDeprecated": false, 763 | "deprecationReason": null 764 | } 765 | ], 766 | "inputFields": null, 767 | "interfaces": [], 768 | "enumValues": null, 769 | "possibleTypes": null 770 | }, 771 | { 772 | "kind": "OBJECT", 773 | "name": "__EnumValue", 774 | "description": "One possible value for a given Enum. Enum values are unique values, not a placeholder for a string or numeric value. However an Enum value is returned in a JSON response as a string.", 775 | "fields": [ 776 | { 777 | "name": "name", 778 | "description": null, 779 | "args": [], 780 | "type": { 781 | "kind": "NON_NULL", 782 | "name": null, 783 | "ofType": { 784 | "kind": "SCALAR", 785 | "name": "String", 786 | "ofType": null 787 | } 788 | }, 789 | "isDeprecated": false, 790 | "deprecationReason": null 791 | }, 792 | { 793 | "name": "description", 794 | "description": null, 795 | "args": [], 796 | "type": { 797 | "kind": "SCALAR", 798 | "name": "String", 799 | "ofType": null 800 | }, 801 | "isDeprecated": false, 802 | "deprecationReason": null 803 | }, 804 | { 805 | "name": "isDeprecated", 806 | "description": null, 807 | "args": [], 808 | "type": { 809 | "kind": "NON_NULL", 810 | "name": null, 811 | "ofType": { 812 | "kind": "SCALAR", 813 | "name": "Boolean", 814 | "ofType": null 815 | } 816 | }, 817 | "isDeprecated": false, 818 | "deprecationReason": null 819 | }, 820 | { 821 | "name": "deprecationReason", 822 | "description": null, 823 | "args": [], 824 | "type": { 825 | "kind": "SCALAR", 826 | "name": "String", 827 | "ofType": null 828 | }, 829 | "isDeprecated": false, 830 | "deprecationReason": null 831 | } 832 | ], 833 | "inputFields": null, 834 | "interfaces": [], 835 | "enumValues": null, 836 | "possibleTypes": null 837 | }, 838 | { 839 | "kind": "OBJECT", 840 | "name": "__Directive", 841 | "description": "A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document.\n\nIn some cases, you need to provide options to alter GraphQL's execution behavior in ways field arguments will not suffice, such as conditionally including or skipping a field. Directives provide this by describing additional information to the executor.", 842 | "fields": [ 843 | { 844 | "name": "name", 845 | "description": null, 846 | "args": [], 847 | "type": { 848 | "kind": "NON_NULL", 849 | "name": null, 850 | "ofType": { 851 | "kind": "SCALAR", 852 | "name": "String", 853 | "ofType": null 854 | } 855 | }, 856 | "isDeprecated": false, 857 | "deprecationReason": null 858 | }, 859 | { 860 | "name": "description", 861 | "description": null, 862 | "args": [], 863 | "type": { 864 | "kind": "SCALAR", 865 | "name": "String", 866 | "ofType": null 867 | }, 868 | "isDeprecated": false, 869 | "deprecationReason": null 870 | }, 871 | { 872 | "name": "locations", 873 | "description": null, 874 | "args": [], 875 | "type": { 876 | "kind": "NON_NULL", 877 | "name": null, 878 | "ofType": { 879 | "kind": "LIST", 880 | "name": null, 881 | "ofType": { 882 | "kind": "NON_NULL", 883 | "name": null, 884 | "ofType": { 885 | "kind": "ENUM", 886 | "name": "__DirectiveLocation", 887 | "ofType": null 888 | } 889 | } 890 | } 891 | }, 892 | "isDeprecated": false, 893 | "deprecationReason": null 894 | }, 895 | { 896 | "name": "args", 897 | "description": null, 898 | "args": [], 899 | "type": { 900 | "kind": "NON_NULL", 901 | "name": null, 902 | "ofType": { 903 | "kind": "LIST", 904 | "name": null, 905 | "ofType": { 906 | "kind": "NON_NULL", 907 | "name": null, 908 | "ofType": { 909 | "kind": "OBJECT", 910 | "name": "__InputValue", 911 | "ofType": null 912 | } 913 | } 914 | } 915 | }, 916 | "isDeprecated": false, 917 | "deprecationReason": null 918 | }, 919 | { 920 | "name": "onOperation", 921 | "description": null, 922 | "args": [], 923 | "type": { 924 | "kind": "NON_NULL", 925 | "name": null, 926 | "ofType": { 927 | "kind": "SCALAR", 928 | "name": "Boolean", 929 | "ofType": null 930 | } 931 | }, 932 | "isDeprecated": true, 933 | "deprecationReason": "Use `locations`." 934 | }, 935 | { 936 | "name": "onFragment", 937 | "description": null, 938 | "args": [], 939 | "type": { 940 | "kind": "NON_NULL", 941 | "name": null, 942 | "ofType": { 943 | "kind": "SCALAR", 944 | "name": "Boolean", 945 | "ofType": null 946 | } 947 | }, 948 | "isDeprecated": true, 949 | "deprecationReason": "Use `locations`." 950 | }, 951 | { 952 | "name": "onField", 953 | "description": null, 954 | "args": [], 955 | "type": { 956 | "kind": "NON_NULL", 957 | "name": null, 958 | "ofType": { 959 | "kind": "SCALAR", 960 | "name": "Boolean", 961 | "ofType": null 962 | } 963 | }, 964 | "isDeprecated": true, 965 | "deprecationReason": "Use `locations`." 966 | } 967 | ], 968 | "inputFields": null, 969 | "interfaces": [], 970 | "enumValues": null, 971 | "possibleTypes": null 972 | }, 973 | { 974 | "kind": "ENUM", 975 | "name": "__DirectiveLocation", 976 | "description": "A Directive can be adjacent to many parts of the GraphQL language, a __DirectiveLocation describes one such possible adjacencies.", 977 | "fields": null, 978 | "inputFields": null, 979 | "interfaces": null, 980 | "enumValues": [ 981 | { 982 | "name": "QUERY", 983 | "description": "Location adjacent to a query operation.", 984 | "isDeprecated": false, 985 | "deprecationReason": null 986 | }, 987 | { 988 | "name": "MUTATION", 989 | "description": "Location adjacent to a mutation operation.", 990 | "isDeprecated": false, 991 | "deprecationReason": null 992 | }, 993 | { 994 | "name": "SUBSCRIPTION", 995 | "description": "Location adjacent to a subscription operation.", 996 | "isDeprecated": false, 997 | "deprecationReason": null 998 | }, 999 | { 1000 | "name": "FIELD", 1001 | "description": "Location adjacent to a field.", 1002 | "isDeprecated": false, 1003 | "deprecationReason": null 1004 | }, 1005 | { 1006 | "name": "FRAGMENT_DEFINITION", 1007 | "description": "Location adjacent to a fragment definition.", 1008 | "isDeprecated": false, 1009 | "deprecationReason": null 1010 | }, 1011 | { 1012 | "name": "FRAGMENT_SPREAD", 1013 | "description": "Location adjacent to a fragment spread.", 1014 | "isDeprecated": false, 1015 | "deprecationReason": null 1016 | }, 1017 | { 1018 | "name": "INLINE_FRAGMENT", 1019 | "description": "Location adjacent to an inline fragment.", 1020 | "isDeprecated": false, 1021 | "deprecationReason": null 1022 | }, 1023 | { 1024 | "name": "SCHEMA", 1025 | "description": "Location adjacent to a schema definition.", 1026 | "isDeprecated": false, 1027 | "deprecationReason": null 1028 | }, 1029 | { 1030 | "name": "SCALAR", 1031 | "description": "Location adjacent to a scalar definition.", 1032 | "isDeprecated": false, 1033 | "deprecationReason": null 1034 | }, 1035 | { 1036 | "name": "OBJECT", 1037 | "description": "Location adjacent to an object type definition.", 1038 | "isDeprecated": false, 1039 | "deprecationReason": null 1040 | }, 1041 | { 1042 | "name": "FIELD_DEFINITION", 1043 | "description": "Location adjacent to a field definition.", 1044 | "isDeprecated": false, 1045 | "deprecationReason": null 1046 | }, 1047 | { 1048 | "name": "ARGUMENT_DEFINITION", 1049 | "description": "Location adjacent to an argument definition.", 1050 | "isDeprecated": false, 1051 | "deprecationReason": null 1052 | }, 1053 | { 1054 | "name": "INTERFACE", 1055 | "description": "Location adjacent to an interface definition.", 1056 | "isDeprecated": false, 1057 | "deprecationReason": null 1058 | }, 1059 | { 1060 | "name": "UNION", 1061 | "description": "Location adjacent to a union definition.", 1062 | "isDeprecated": false, 1063 | "deprecationReason": null 1064 | }, 1065 | { 1066 | "name": "ENUM", 1067 | "description": "Location adjacent to an enum definition.", 1068 | "isDeprecated": false, 1069 | "deprecationReason": null 1070 | }, 1071 | { 1072 | "name": "ENUM_VALUE", 1073 | "description": "Location adjacent to an enum value definition.", 1074 | "isDeprecated": false, 1075 | "deprecationReason": null 1076 | }, 1077 | { 1078 | "name": "INPUT_OBJECT", 1079 | "description": "Location adjacent to an input object type definition.", 1080 | "isDeprecated": false, 1081 | "deprecationReason": null 1082 | }, 1083 | { 1084 | "name": "INPUT_FIELD_DEFINITION", 1085 | "description": "Location adjacent to an input object field definition.", 1086 | "isDeprecated": false, 1087 | "deprecationReason": null 1088 | } 1089 | ], 1090 | "possibleTypes": null 1091 | } 1092 | ], 1093 | "directives": [ 1094 | { 1095 | "name": "skip", 1096 | "description": "Directs the executor to skip this field or fragment when the `if` argument is true.", 1097 | "locations": [ 1098 | "FIELD", 1099 | "FRAGMENT_SPREAD", 1100 | "INLINE_FRAGMENT" 1101 | ], 1102 | "args": [ 1103 | { 1104 | "name": "if", 1105 | "description": "Skipped when true.", 1106 | "type": { 1107 | "kind": "NON_NULL", 1108 | "name": null, 1109 | "ofType": { 1110 | "kind": "SCALAR", 1111 | "name": "Boolean", 1112 | "ofType": null 1113 | } 1114 | }, 1115 | "defaultValue": null 1116 | } 1117 | ] 1118 | }, 1119 | { 1120 | "name": "include", 1121 | "description": "Directs the executor to include this field or fragment only when the `if` argument is true.", 1122 | "locations": [ 1123 | "FIELD", 1124 | "FRAGMENT_SPREAD", 1125 | "INLINE_FRAGMENT" 1126 | ], 1127 | "args": [ 1128 | { 1129 | "name": "if", 1130 | "description": "Included when true.", 1131 | "type": { 1132 | "kind": "NON_NULL", 1133 | "name": null, 1134 | "ofType": { 1135 | "kind": "SCALAR", 1136 | "name": "Boolean", 1137 | "ofType": null 1138 | } 1139 | }, 1140 | "defaultValue": null 1141 | } 1142 | ] 1143 | }, 1144 | { 1145 | "name": "deprecated", 1146 | "description": "Marks an element of a GraphQL schema as no longer supported.", 1147 | "locations": [ 1148 | "FIELD_DEFINITION", 1149 | "ENUM_VALUE" 1150 | ], 1151 | "args": [ 1152 | { 1153 | "name": "reason", 1154 | "description": "Explains why this element was deprecated, usually also including a suggestion for how to access supported similar data. Formatted in [Markdown](https://daringfireball.net/projects/markdown/).", 1155 | "type": { 1156 | "kind": "SCALAR", 1157 | "name": "String", 1158 | "ofType": null 1159 | }, 1160 | "defaultValue": "\"No longer supported\"" 1161 | } 1162 | ] 1163 | } 1164 | ] 1165 | } 1166 | } 1167 | } --------------------------------------------------------------------------------