Repository: graphcool/graphql-cli Branch: master Commit: aa709566b74a Files: 145 Total size: 234.9 KB Directory structure: gitextract_ex53v_nb/ ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.md │ │ └── feature_request.md │ └── workflows/ │ ├── algolia-integrity.yml │ ├── algolia-publish.yml │ ├── main.yml │ └── website.yml ├── .gitignore ├── .npmrc ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── docs/ │ ├── CUSTOM_EXTENSION.md │ └── MIGRATION.md ├── integration/ │ ├── package.json │ ├── test-project/ │ │ ├── .dockerignore │ │ ├── .gitignore │ │ ├── .graphqlrc.yml │ │ ├── package.json │ │ ├── renovate.json │ │ ├── schema/ │ │ │ └── schema.graphql │ │ ├── tsconfig.json │ │ └── tslint.json │ ├── tests/ │ │ └── workflow.ts │ └── tsconfig.json ├── lerna.json ├── package.json ├── packages/ │ ├── cli/ │ │ ├── .gitignore │ │ ├── .npmignore │ │ ├── CONTRIBUTING.md │ │ ├── LICENSE │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ ├── bin.ts │ │ │ ├── discover.ts │ │ │ └── index.ts │ │ └── tsconfig.json │ ├── commands/ │ │ ├── generate/ │ │ │ ├── .gitignore │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ └── index.ts │ │ │ └── tsconfig.json │ │ ├── init/ │ │ │ ├── .gitignore │ │ │ ├── .npmignore │ │ │ ├── README.md │ │ │ ├── package.json │ │ │ ├── src/ │ │ │ │ ├── common.ts │ │ │ │ ├── features/ │ │ │ │ │ ├── codegen.ts │ │ │ │ │ └── inspector.ts │ │ │ │ ├── index.ts │ │ │ │ ├── search-codegen-config.ts │ │ │ │ └── sources/ │ │ │ │ ├── from-existing.ts │ │ │ │ ├── from-open-api.ts │ │ │ │ └── from-scratch.ts │ │ │ └── tsconfig.json │ │ └── serve/ │ │ ├── .gitignore │ │ ├── .npmignore │ │ ├── README.md │ │ ├── package.json │ │ ├── src/ │ │ │ └── index.ts │ │ └── tsconfig.json │ ├── common/ │ │ ├── .gitignore │ │ ├── .npmignore │ │ ├── package.json │ │ ├── src/ │ │ │ ├── command.ts │ │ │ ├── index.ts │ │ │ └── types.ts │ │ └── tsconfig.json │ └── loaders/ │ ├── .gitignore │ ├── .npmignore │ ├── package.json │ ├── src/ │ │ └── index.ts │ └── tsconfig.json ├── renovate.json ├── scripts/ │ ├── introspect-config.js │ └── release.js ├── templates/ │ └── fullstack/ │ ├── .dockerignore │ ├── .gitignore │ ├── .graphqlrc.yml │ ├── Dockerfile │ ├── README.md │ ├── client/ │ │ ├── .gitignore │ │ ├── generated-types.tsx │ │ ├── package.json │ │ ├── public/ │ │ │ ├── index.html │ │ │ ├── manifest.json │ │ │ └── robots.txt │ │ ├── src/ │ │ │ ├── App.css │ │ │ ├── App.test.tsx │ │ │ ├── App.tsx │ │ │ ├── components/ │ │ │ │ ├── comment/ │ │ │ │ │ ├── Comment.css │ │ │ │ │ ├── CreateComment.tsx │ │ │ │ │ └── OneComment.tsx │ │ │ │ └── notes/ │ │ │ │ ├── CreateNote.tsx │ │ │ │ ├── EditNote.tsx │ │ │ │ ├── Note.css │ │ │ │ └── OneNote.tsx │ │ │ ├── generated-types.tsx │ │ │ ├── graphql/ │ │ │ │ └── graphback.graphql │ │ │ ├── index.css │ │ │ ├── index.tsx │ │ │ ├── react-app-env.d.ts │ │ │ └── serviceWorker.ts │ │ └── tsconfig.json │ ├── docker-compose.yml │ ├── model/ │ │ └── datamodel.graphql │ ├── package.json │ ├── renovate.json │ ├── schemats.json │ ├── server/ │ │ ├── package.json │ │ ├── src/ │ │ │ ├── db.ts │ │ │ ├── generated-db-types.ts │ │ │ ├── generated-types.ts │ │ │ ├── graphql.ts │ │ │ ├── index.ts │ │ │ └── schema/ │ │ │ └── schema.graphql │ │ └── tsconfig.json │ └── tslint.json ├── templates.json └── website/ ├── .gitignore ├── README.md ├── algolia-lockfile.json ├── babel.config.js ├── docs/ │ ├── codegen.md │ ├── coverage.md │ ├── custom-commands.md │ ├── diff.md │ ├── discover.md │ ├── generate.md │ ├── init.md │ ├── introduction.md │ ├── introspect.md │ ├── migration.md │ ├── serve.md │ ├── similar.md │ └── validate.md ├── docusaurus.config.js ├── package.json ├── scripts/ │ └── algolia-ci.ts ├── sidebars.js ├── src/ │ ├── css/ │ │ └── custom.css │ ├── pages/ │ │ ├── index.js │ │ └── styles.module.css │ └── theme/ │ └── Root.js └── static/ ├── CNAME └── js/ └── light-mode-by-default.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.md ================================================ --- name: Bug report about: Create a report to help us improve --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error **Expected behavior** A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. **Versions (please complete the following information):** - OS: [e.g. `Windows 10`, `OS X High Sierra`, `Ubuntu 16.04`] - `graphql-cli`: [e.g. `2.16.5`] - other packages if applicable **Additional context** Add any other context about the problem here. ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.md ================================================ --- name: Feature request about: Suggest an idea for this project --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. ================================================ FILE: .github/workflows/algolia-integrity.yml ================================================ name: Algolia Integrity on: pull_request: paths: - 'website/**' jobs: algolia-records-check: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v3 with: fetch-depth: 0 - name: Use Node 18 uses: actions/setup-node@v3 with: node-version: 16 cache: 'yarn' - name: Install Dependencies run: yarn --ignore-engines working-directory: ./website - name: Build Packages run: yarn build working-directory: ./website - name: Algolia generate run: yarn algolia-sync working-directory: ./website env: ALGOLIA_DRY_RUN: true SITE_URL: https://www.graphql-cli.com/ - name: Yarn build at root run: yarn - name: Prettier run: yarn prettier -w website/algolia-lockfile.json - name: Compare run: git diff origin/${{ github.base_ref }}.. -- website/algolia-lockfile.json - name: Diff to file if: always() id: diff_result run: | OUTPUT=$(git diff origin/${{ github.base_ref }}.. -- website/algolia-lockfile.json) OUTPUT="${OUTPUT//'%'/'%25'}" OUTPUT="${OUTPUT//$'\n'/'%0A'}" OUTPUT="${OUTPUT//$'\r'/'%0D'}" echo "::set-output name=result::$OUTPUT" - name: Publish a message if: always() && contains(steps.diff_result.outputs.result, 'diff') uses: marocchino/sticky-pull-request-comment@v2 with: message: | ```diff ${{ steps.diff_result.outputs.result }} ``` ================================================ FILE: .github/workflows/algolia-publish.yml ================================================ name: Algolia Publish on: push: branches: - master jobs: algolia-push-records: name: Push new records if changes runs-on: ubuntu-latest steps: - name: Checkout Repo uses: actions/checkout@v3 - name: Use Node uses: actions/setup-node@v3 with: node-version: 16 cache: 'yarn' - name: Install Dependencies run: yarn working-directory: ./website - name: Build Packages run: yarn build working-directory: ./website - name: Algolia push run: yarn algolia-sync working-directory: ./website env: ALGOLIA_APP_ID: ${{ secrets.ALGOLIA_APP_ID }} ALGOLIA_ADMIN_API_KEY: ${{ secrets.ALGOLIA_ADMIN_API_KEY }} ALGOLIA_INDEX_NAME: ${{ secrets.ALGOLIA_INDEX_NAME }} SITE_URL: https://www.graphql-cli.com/ - name: Yarn build at root run: yarn - name: Prettier run: yarn prettier -w website/algolia-lockfile.json - name: Compare run: git diff website/algolia-lockfile.json - name: Diff to file if: always() id: diff_result run: | OUTPUT=$(git diff website/algolia-lockfile.json) OUTPUT="${OUTPUT//'%'/'%25'}" OUTPUT="${OUTPUT//$'\n'/'%0A'}" OUTPUT="${OUTPUT//$'\r'/'%0D'}" echo "::set-output name=result::$OUTPUT" - name: Commit algolia-lockfile.json if: always() && contains(steps.diff_result.outputs.result, 'diff') uses: EndBug/add-and-commit@v9 with: commit: website/algolia-lockfile.json message: Update algolia-lockfile.json default_author: github_actions ================================================ FILE: .github/workflows/main.yml ================================================ name: CI on: push: branches: - master pull_request: branches: - master release: types: [released, prereleased] jobs: test: name: Testing on ${{matrix.os}} and Node ${{matrix.node_version}} runs-on: ${{matrix.os}} strategy: matrix: os: [ubuntu-latest, windows-latest] node_version: [12, 14, 16] steps: - name: Checkout Master uses: actions/checkout@v2 - name: Use Node ${{matrix.node_version}} uses: actions/setup-node@master with: version: ${{ matrix.node_version }} - name: Install Dependencies using Yarn run: yarn --ignore-engines - name: Build run: yarn build - name: Test run: yarn test publish-canary: name: Publish Canary runs-on: ubuntu-latest if: ${{ github.event_name != 'release' }} steps: - name: Checkout Master uses: actions/checkout@v2 - name: Use Node uses: actions/setup-node@v2 with: node-version: 14 - name: Install Dependencies using Yarn run: yarn --ignore-engines - name: Build run: yarn build - name: Release Canary run: | echo "Fork PR: ${{github.repository}}" if [ "${{github.repository}}" == "Urigo/graphql-cli" ] && [ "${{ secrets.NODE_AUTH_TOKEN }}" != "" ] then echo "//registry.npmjs.org/:_authToken=${{ secrets.NODE_AUTH_TOKEN }}" > .npmrc npm run release:canary else echo "Skipping canary publish due to a fork/PR..." fi publish: # publish to npm only when doing the release if: ${{ github.event_name == 'release' }} name: Publish Release runs-on: ubuntu-latest steps: - name: Checkout Master uses: actions/checkout@v2 - name: Use Node uses: actions/setup-node@v2 with: node-version: 14 - name: Install Dependencies using Yarn run: yarn --ignore-engines - name: Build run: yarn build - name: Release run: echo "//registry.npmjs.org/:_authToken=${{secrets.NODE_AUTH_TOKEN}}" > ~/.npmrc && TAG=${GITHUB_REF#"refs/tags/"} npm run release ================================================ FILE: .github/workflows/website.yml ================================================ name: Website Deployment on: push: branches: - master jobs: deploy-website: name: Deploy Website timeout-minutes: 60 runs-on: ubuntu-latest if: contains(github.event.head_commit.message, '[deploy_website]') || contains(github.ref, 'refs/tags/') steps: - name: Checkout Master uses: actions/checkout@v2 - name: Configure Git Credientials run: | git config --global user.email "${{github.actor}}@users.noreply.github.com" git config --global user.name "${{github.actor}}" echo "machine github.com login ${{github.actor}} password ${{secrets.GITHUB_TOKEN}}" > ~/.netrc - name: Add origin remote and refetch master run: | git remote rm origin git remote add origin "https://github.com/${{github.repository}}" git fetch git checkout master git reset --hard - name: Use Node uses: actions/setup-node@v2 with: node-version: '14.x' - name: Install Dependencies using Yarn run: yarn install --ignore-engines --frozen-lockfile - name: Deploy 🚀 run: yarn deploy:website env: GIT_USER: ${{github.actor}} NEXT_PUBLIC_ALGOLIA_APP_ID: ${{ secrets.ALGOLIA_APP_ID }} NEXT_PUBLIC_ALGOLIA_SEARCH_API_KEY: a5522203ca95675199cc21edf09e6d75 NEXT_PUBLIC_ALGOLIA_INDEX_NAME: ${{ secrets.ALGOLIA_INDEX_NAME }} ================================================ FILE: .gitignore ================================================ # Logs logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* # Runtime data pids *.pid *.seed *.pid.lock # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # nyc test coverage .nyc_output # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) .grunt # Bower dependency directory (https://bower.io/) bower_components # node-waf configuration .lock-wscript # Compiled binary addons (https://nodejs.org/api/addons.html) build/Release # Dependency directories node_modules/ jspm_packages/ # TypeScript v1 declaration files typings/ # Optional npm cache directory .npm # Optional eslint cache .eslintcache # Optional REPL history .node_repl_history # Output of 'npm pack' *.tgz # Yarn Integrity file .yarn-integrity # dotenv environment variables file .env # next.js build output .next package-lock.json ================================================ FILE: .npmrc ================================================ save-exact=true ================================================ FILE: CONTRIBUTING.md ================================================ # Contributing guide We are using Yarn workspaces, so make sure you have the latest version of Yarn installed. ## Building project To build the entire monorepo, start by installing the dependencies by running `yarn` in the root directory, and then: ```sh yarn build ``` ## Using command line tool from source ```sh cd packages/cli npm link . ``` ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2019 Dotan Simha Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # GraphQL CLI ![image](https://user-images.githubusercontent.com/20847995/67651234-85bf1500-f916-11e9-90e5-cb3bd0e6a338.png) ![CI](https://github.com/Urigo/graphql-cli/workflows/CI/badge.svg) [![npm version](http://img.shields.io/npm/v/graphql-cli.svg?style=flat)](https://npmjs.org/package/graphql-cli "View this project on npm") [![Discord Chat](https://img.shields.io/discord/625400653321076807)](https://the-guild.dev/discord) Help us to improve new GraphQL CLI. Check out the new structure and commands below! Feel free to contact us in Discord channel. We would love to hear your feedback. ## Features - Helpful commands to improve your workflows - Compatible with editors and IDEs based on [`graphql-config`](https://github.com/kamilkisiela/graphql-config) - Powerful plugin system to extend `graphql-cli` with custom commands ## Install You can install the CLI using `yarn` by running the following command. This will add the `graphql` binary to your path. ```sh yarn global add graphql-cli ``` The equivalent npm global install will also work. ## Migration from 3.x.x to 4.x.x **Important: many aspects of GraphQL CLI syntax and structure have changed in 4.x.x.** Please check out the [Migration Guide](./docs/MIGRATION.md) to learn more. ## Usage / Initialization At the heart of a project created using GraphQL CLI is the GraphQL Config configuration file. For starters, this configuration lets the cd CLI tools know where all of the GraphQL documents and operations are. For more information about GraphQL Config, [you can click here to learn more](https://graphql-config.com/docs/introduction). The most straightforward way to launch a GraphQL CLI-capable project with a working GraphQL Config setup is to use the `init` command from your desired workspace: ```sh npx graphql-cli init ``` After a series of questions from the command-prompt, the system will use the inputs and selected project templates to generate a working project complete with a GraphQL Config setup. The GraphQL Config file is generated referencing the necessary files and ecosystem plugins. You can also get started with GraphQL CLI by creating your own GraphQL Config file using an editor of your choice. Starting with a filename `.graphqlrc.yml`, for instance, we could add: ```yml schema: "server/src/schema/**/*.graphql" documents: "client/src/documents/**/*.graphql" ``` This is now a valid YAML-syntax GraphQL Config file. Using `init` from the GraphQL CLI will generate a project based on the instructions in your YAML. Finally, one of the options with `graphql init` is to access schema using an OpenAPI or Swagger endpoint. Choose this option at the start of the Init question tree, and then follow the instructions to navigate to the URL of your choice. ## Plugin System Each command in GraphQL CLI is a seperate package, so you can have your own plugins or use the ones we maintain. You can have those commands by installing them like `@graphql-cli/[COMMAND-NAME]`. To configure a command/plugin, you need to update the `extensions` field in your GraphQL Config file (`.graphqlrc.yml`). See `extensions:` in the example below. ```yml schema: ./server/src/schema/**/*.ts: require: ts-node/register documents: ./client/src/graphql/**/*.ts extensions: codegen: generates: ./server/src/generated-types.d.ts: plugins: - typescript - typescript-resolvers ./client/src/generated-types.tsx: plugins: - typescript - typescript-operations - typescript-react-apollo config: withHooks: true graphback: model: './model/*.graphql' plugins: graphback-schema: outputPath: './src/schema/schema.graphql' ... ``` [For a detailed example check out a template file here.](https://github.com/Urigo/graphql-cli/blob/master/templates/fullstack/.graphqlrc.yml) Some of the available Plugins are: - [`init`](https://github.com/Urigo/graphql-cli/tree/focs/packages/commands/init) - Creates a GraphQL project using a template or GraphQL Config file for your existing project. - [`codegen`](https://github.com/dotansimha/graphql-code-generator/tree/master/packages/graphql-cli-codegen-plugin) - GraphQL Code Generator's GraphQL CLI plugin. GraphQL Code Generator is a tool that generates code from your GraphQL schema and documents for your backend or frontend with flexible support for custom plugins and templates. [Learn More](https://graphql-code-generator.com) - [`generate`](https://github.com/Urigo/graphql-cli/tree/master/packages/commands/generate) - Generate schema and client-side documents for your GraphQL project by using [Graphback](https://graphback.dev). - [`coverage`](https://github.com/kamilkisiela/graphql-inspector/tree/master/packages/graphql-cli/coverage) - Schema coverage based on documents. Find out how many times types and fields are used in your application using [GraphQL Inspector](https://graphql-inspector.com/docs/essentials/coverage). - [`diff`](https://github.com/kamilkisiela/graphql-inspector/tree/master/packages/graphql-cli/diff) - Compares schemas and finds breaking or dangerous changes using [GraphQL Inspector](https://graphql-inspector.com/docs/essentials/diff). - You can also compare your current schema against a base schema using URL, Git link and local file. You can give this pointer in the command line after `graphql diff` or in GraphQL Config file: ```yml # ... extensions: diff: baseSchema: git:origin/master:schema.graphql ``` - [`similar`]((https://github.com/kamilkisiela/graphql-inspector/tree/master/packages/graphql-cli/similar)) - Get a list of similar types in order to find duplicates using [GraphQL Inspector](https://graphql-inspector.com/docs/essentials/similar). - [`validate`]((https://github.com/kamilkisiela/graphql-inspector/tree/master/packages/graphql-cli/validate)) - Validates documents against a schema and looks for deprecated usage using [GraphQL Inspector](https://graphql-inspector.com/docs/essentials/validate). - [`serve`](https://github.com/Urigo/graphql-cli/tree/master/packages/commands/serve) - Serves a GraphQL server, using an in memory database and a defined GraphQL schema. Please read through [serve documentation](./website/docs/command-serve.md) to learn more about this command. More plugins are definitely welcome! Please check the existing ones to see how to use GraphQL Config and GraphQL CLI API. ## Contributing Please read through the [contributing guidelines](./CONTRIBUTING.md) ## Writing your own plugin GraphQL CLI supports custom plugins, [you can find a tutorial and example here](./docs/CUSTOM_EXTENSION.md) ## Help & Community [![Discord Chat](https://img.shields.io/discord/625400653321076807)](https://the-guild.dev/discord) Join our [Discord chat](https://the-guild.dev/discord) if you run into issues or have questions. We're excited to welcome you to the community! ================================================ FILE: docs/CUSTOM_EXTENSION.md ================================================ ## Writing your own GraphQL-CLI Extension `graphql-cli` allow you to write your own plugin/extenion, and intergrate external tools and configuration, and run it from a single CLI. The current implementation of `graphql-cli` is using [Yargs](https://yargs.js.org/) to manage it's CLI commands. Plugins and extension are treated as NodeJS module by the `graphql-cli`, so it means you can use JavaScript/TypeScript/Any other super-set of JavaScript to write your extension. It means that you plugin will be loaded by it's name under `node_modules` - for example `graphql-cli my-custom-plugin ...`. `graphql-cli` also supports `graphql-config`, so it can help you easily load your GraphQL schema, operations and configuration from a unified config file. > If you are wrapping an existing tool that has it's own CLI already, consider to expose a programatic API so it will be easier to consume. ### TL;DR We have a ready-to-use boilerplate for that purpose, [you can find it here](https://github.com/dotansimha/graphql-cli-plugin-example). Also, inside this repo, under `packages/commands` you can find a set of plugins implementation you can use as reference. ### Getting Started Start by creating a simple JavaScript/TypeScript project, according to your preference. Install `@graphql-cli/common` package and use `defineCommand` utility in your entry point (usually `index` file): ```ts import { defineCommand } from '@graphql-cli/common'; export default defineCommand((api) => { return {}; }); ``` To register your CLI command, give it a name first. Use the `command` property: ```ts export default defineCommand((api) => { return { command: 'my-plugin', async handler() { // code here }, }; }); ``` Now, your plugin will be avaiable to use with the following command: `graphql my-plugin`. You can also add custom validations, flags, default values and much more with Yargs. [You can read the documentation here](https://yargs.js.org/docs/#api-commandcmd-desc-module). ## Testing your plugin locally To test your plugin locally, install `graphql-cli` in your project as a `devDependency`, and run the following command: ``` graphql ./src/index.js ``` If you registerd sub-commands, you should be able to run those this way: ``` graphql ./src/index.js do-something ``` > The path should point to the entry point of your script, and if you are using TypeScript - point to the compile file. ## Loading GraphQL Schema To easily load GraphQL schema, you can use `graphql-config`: ```ts import { defineCommand } from '@graphql-cli/common'; export default defineCommand((api) => { return { command: 'my-plugin', builder(build) { return build.options({ project: { type: 'string', describe: 'Name of your project', }, }); }, async handler(args) { // use graphql-config and find configuration const config = await api.useConfig(); // pick project const project = args.project ? config.getProject(args.project) : config.getDefault(); // get schema const schema = await config.getSchema(); }, }; }); ``` If you are using `graphql-config` to define your configuration, and you wish to load your extenion config from it, do: ```ts type MyConfig = { ... }; const extensionConfig = await config.extension('my-plugin'); ``` ## Error Handling If you wish to fail the execution of your plugin and report it back to GraphQL CLI host, simply throw an error: ```ts import { defineCommand } from '@graphql-cli/common'; export default defineCommand(() => { return { command: 'check-if-missing', handler() { if (somethingIsMissing) { throw new Error(`Ooops, something is missing`); } }, }; }); ``` ================================================ FILE: docs/MIGRATION.md ================================================ # Migration from GraphQL CLI 3.x or older Starting with GraphQL CLI 4.0 and higher, the way projects are set up is significantly restructured. ## Install the new version To get started, install the new version: ```sh yarn global add graphql-cli ``` You can also globally install using npm. > NOTE: If you have previous version of the GraphQL-CLI installed make sure to uninstall it first ```bash npm uninstall graphql-cli ``` ## Update your configuration file If you are working from an existing project, the GraphQL Config file that is used by GraphQL CLI is now called `.graphqlrc.yml` (by default) instead of `.graphqlconfig`. [Other options exist](https://graphql-config.com/usage) for naming the config files supported by GraphQL CLI, but this guide will assume you're using YAML syntax. To migrate, you will first need to update your GraphQL Configuration file to match GraphQL Config's updated syntax and structure. You can [check here](https://graphql-config.com/usage) for more information about the new structure. ###Specifying schema(s): ```yml schema: ./src/schema/**/*.graphql #You can have URL endpoint, Git URL and local files using globs here. ``` `schemaPath` is replaced by `schema`, which is now more flexible then the previous approach. This field is used by all commands and plugins of GraphQL CLI. ## Comparison of old commands ### `get-schema` is no longer available In previous versions, you were able to download the schema to the given path in `schemaPath` from the URL given inside `endpoint`. In the new version, `schema` refers to the endpoint of the schema. If you use Prisma or any other tool that provides your schema under URL endpoint, you must specify it using the following syntax in your configuration YAML: ```yaml schema: http://localhost:4000/graphql #This is the schema path ``` If you want to download the schema from this URL to your local file system, you will also need to install `codegen` plugin and its `schema-ast` plugin using the following command or its npm equivalent: ```bash yarn add @graphql-cli/codegen @graphql-codegen/schema-ast --dev ``` After that, you can specify the output path of the local schema file: ```yaml schema: http://localhost:4000/graphql extensions: codegen: generates: ./schema.graphql: plugins: - schema-ast ``` By running `graphql codegen`, the `schema.graphql` file is generated in the root path of your project. #### For JSON Output If you want to download the schema as a `json` introspection file, you will need to install `@graphql-codegen/introspection` instead, and add `introspection` instead of `schema-ast`. ```yaml schema: http://localhost:4000/graphql extensions: codegen: generates: ./schema.json: plugins: - introspection ``` ### `create` is no longer available: it is replaced by the `init` command. If you want to create a GraphQL Config file on an existing project or create a project using a template from scratch, you can use `graphql init` command. This command will ask some questions about your new or existing project to update your dependencies and create a new configuration file for GraphQL CLI. ### `diff` has been changed If you want to see the differences between your schema and another schema, use the `diff` command as follows: ```yaml graphql diff git:origin/master:schema.graphql ``` For example, `diff` will show the differences between the version in `schema` field of GraphQL Configuration file and the `schema.graphql` file in the remote master branch. Alternatively, you can compare `schema` with a URL endpoint: ```yaml graphql diff http://my-dev-instance.com/graphql ``` ### `add-endpoint`, `add-project`, `schema-status`, `ping`, `query`, `prepare`, `lint` and `playground` commands are no longer available. GraphQL CLI (as well as GraphQL Config) no longer separates `endpoints` and `schemaPath`. The new `schema` field refers to the single endpoint of your GraphQL schema, so it can be a URL endpoint or a local file. If your project uses a remote schema, you can directly define this URL in `schema` path without downloading it or defining it as an extra `endpoint` etc. Instead of using these legacy commands, you can create a faked server to test your schema using the `yarn serve` command. ### `codegen` now uses GraphQL Code Generator GraphQL CLI now uses GraphQL Code Generator which has a lot of plugins and templates for various environments, platforms and use cases. You can generate resolver signatures, TypeScript representations of your GraphQL Schema and more. [Check it out](https://graphql-code-generator.com/) The usage is slightly different from the old one: ```yaml schema: src/schema/**/*.graphql extensions: codegen: src/generated-types.ts: # Output file name - typescript # Plugin names to be used - typescript-resolvers ``` For instance, consider a hypothetical case where you need to generate TypeScript resolvers signatures for your GraphQL project. To do this, you would install the `codegen` plugin and the additional plugins and templates for GraphQL Code Generator. For this case, you would need `typescript` and `typescript-resolvers` plugins: ```bash yarn add @graphql-cli/codegen @graphql-codegen/typescript @graphql-codegen/typescript-resolvers --dev ``` Now, using a single command, you can run GraphQL Code Generator using GraphQL CLI: ```bash graphql codegen ``` ## Special Notes for Prisma users Prisma users will need to download a schema from a URL endpoint. For example, here is a *legacy GraphQL Config file* doing this: `.graphqlconfig` ```yaml projects: app: schemaPath: src/schema.graphql extensions: endpoints: default: http://localhost:4000 database: schemaPath: src/generated/prisma-client/prisma.graphql extensions: prisma: prisma/prisma.yml ``` **To update the configuration for the new GraphQL CLI** you need to rename the file to `.graphqlrc.yml`, and then update the file as follows: `.graphqlrc.yml` ```yaml projects: app: schema: src/schema.graphql database: schema: prisma/prisma.yml extensions: codegen: generates: ./src/generated/prisma-client/prisma.graphql: plugins: - schema-ast ``` You can directly point to your `prisma.yml` file instead of the URL endpoint. Before running the GraphQL CLI command to use this new configuration, make sure you have installed the `@graphql-cli/codegen` and `@graphql-codegen/schema-ast` plugins using: ```sh yarn add @graphql-cli/codegen @graphql-codegen/schema-ast --dev ``` Now you can run `graphql codegen --project database` for generating your `prisma.graphql` file. You will also need to update your `prisma.yml` file if you're using `graphql get-schema` with Prisma: ```yaml ... # Ensures Prisma client is re-generated after a datamodel change. hooks: post-deploy: - graphql codegen --project database # instead of graphql get-schema - prisma generate ``` ================================================ FILE: integration/package.json ================================================ { "name": "graphql-cli-integration-test", "version": "4.1.0", "license": "MIT", "private": true, "main": "dist/index.js", "publishConfig": { "access": "public" }, "scripts": { "test": "ava" }, "dependencies": { "log-symbols": "4.1.0", "tslib": "2.3.1" }, "devDependencies": { "ava": "3.15.0", "execa": "5.1.1" }, "ava": { "files": [ "tests/**/*" ], "extensions": [ "ts" ], "require": [ "ts-node/register" ] } } ================================================ FILE: integration/test-project/.dockerignore ================================================ node_modules npm-debug.log ================================================ FILE: integration/test-project/.gitignore ================================================ node_modules dist types yarn.lock yarn-error.log ================================================ FILE: integration/test-project/.graphqlrc.yml ================================================ schema: schema/schema.graphql extensions: codegen: generates: ./src/generated-types.ts: config: mappers: Comment: './generated-db-types#comment' Note: './generated-db-types#note' useIndexSignature: true skipDocumentsValidation: true plugins: - add: '/* tslint:disable */' - typescript - typescript-resolvers ================================================ FILE: integration/test-project/package.json ================================================ { "private": true, "name": "test-project", "version": "4.1.0", "dependencies": { "@graphql-tools/load-files": "6.5.2", "@types/node": "13.13.45", "graphql": "15.7.2", "graphql-config": "3.4.1", "graphql-tag": "2.12.6" }, "devDependencies": { "@graphql-codegen/add": "2.0.2", "@graphql-codegen/typescript": "1.23.0", "@graphql-codegen/typescript-operations": "1.18.4", "@graphql-codegen/typescript-react-apollo": "2.3.1", "@graphql-codegen/typescript-resolvers": "1.20.0", "@graphql-cli/codegen": "1.17.27", "@graphql-cli/coverage": "2.1.0", "@graphql-cli/diff": "2.1.0", "@graphql-cli/generate": "4.1.0", "@graphql-cli/serve": "4.1.0", "@graphql-cli/similar": "2.1.0", "@graphql-cli/validate": "2.1.0", "graphql": "15.7.2", "graphql-cli": "4.1.0", "tslint": "6.1.3", "typescript": "4.4.4" }, "license": "MIT" } ================================================ FILE: integration/test-project/renovate.json ================================================ { "extends": [ "config:base" ], "automerge": true, "major": { "automerge": false } } ================================================ FILE: integration/test-project/schema/schema.graphql ================================================ ## NOTE: This schema was generated by Graphback and should not be changed manually """Exposes a URL that specifies the behaviour of this scalar.""" directive @specifiedBy( """The URL that specifies the behaviour of this scalar.""" url: String! ) on SCALAR """ @model """ type Comment { id: ID! text: String description: String """@manyToOne(field: 'comments', key: 'noteId')""" note: Note } input CommentFilter { id: IDInput text: StringInput description: StringInput noteId: IDInput and: [CommentFilter!] or: [CommentFilter!] not: CommentFilter } type CommentResultList { items: [Comment]! offset: Int limit: Int count: Int } input CommentSubscriptionFilter { id: ID text: String description: String } input CreateCommentInput { id: ID text: String description: String noteId: ID } input CreateNoteInput { id: ID title: String! description: String } input IDInput { ne: ID eq: ID le: ID lt: ID ge: ID gt: ID in: [ID!] } input MutateCommentInput { id: ID! text: String description: String noteId: ID } input MutateNoteInput { id: ID! title: String description: String } type Mutation { createNote(input: CreateNoteInput!): Note updateNote(input: MutateNoteInput!): Note deleteNote(input: MutateNoteInput!): Note createComment(input: CreateCommentInput!): Comment updateComment(input: MutateCommentInput!): Comment deleteComment(input: MutateCommentInput!): Comment } """ @model """ type Note { id: ID! title: String! description: String """@oneToMany(field: 'note', key: 'noteId')""" comments(filter: CommentFilter): [Comment]! } input NoteFilter { id: IDInput title: StringInput description: StringInput and: [NoteFilter!] or: [NoteFilter!] not: NoteFilter } type NoteResultList { items: [Note]! offset: Int limit: Int count: Int } input NoteSubscriptionFilter { id: ID title: String description: String } input OrderByInput { field: String! order: SortDirectionEnum = ASC } input PageRequest { limit: Int offset: Int } type Query { getNote(id: ID!): Note findNotes(filter: NoteFilter, page: PageRequest, orderBy: OrderByInput): NoteResultList! getComment(id: ID!): Comment findComments(filter: CommentFilter, page: PageRequest, orderBy: OrderByInput): CommentResultList! } enum SortDirectionEnum { DESC ASC } input StringInput { ne: String eq: String le: String lt: String ge: String gt: String in: [String!] contains: String startsWith: String endsWith: String } type Subscription { newNote(filter: NoteSubscriptionFilter): Note! updatedNote(filter: NoteSubscriptionFilter): Note! deletedNote(filter: NoteSubscriptionFilter): Note! newComment(filter: CommentSubscriptionFilter): Comment! updatedComment(filter: CommentSubscriptionFilter): Comment! deletedComment(filter: CommentSubscriptionFilter): Comment! } ================================================ FILE: integration/test-project/tsconfig.json ================================================ { "compilerOptions": { "outDir": "./dist/", "declarationDir": "./types", "lib": [ "esnext", "dom" ], "resolveJsonModule": true, "noImplicitAny": false, "preserveConstEnums": true, "strict": false, "strictNullChecks": true, "esModuleInterop": true, "target": "esnext", "module": "commonjs", "moduleResolution": "node", "allowSyntheticDefaultImports": true, "importHelpers": true, "alwaysStrict": false, "sourceMap": true, "declaration": true, "noImplicitReturns": true, "noUnusedLocals": false, "noUnusedParameters": false, "noImplicitThis": false }, } ================================================ FILE: integration/test-project/tslint.json ================================================ { "extends": "tslint:recommended", "rules": { "max-line-length": { "options": [120] }, "semicolon": false, "quotemark": false, "new-parens": true, "no-arg": true, "no-bitwise": true, "no-conditional-assignment": false, "no-consecutive-blank-lines": false, "object-literal-sort-keys": false, "trailing-comma": false, "object-literal-shorthand": false, "no-console": false, "interface-name": false }, "jsRules": { "max-line-length": { "options": [120] } } } ================================================ FILE: integration/tests/workflow.ts ================================================ // tslint:disable-next-line: match-default-export-name no-implicit-dependencies import ava, { ExecutionContext } from 'ava'; import { resolve } from 'path'; import { execSync } from 'child_process'; ava('Test cli workflow', (t: ExecutionContext) => { const basePath = resolve(`${__dirname}/../test-project`); process.chdir(basePath); // Workaround for github actions symlinking issue const graphQLCmd = 'node ../../packages/cli/dist/index.js'; console.log(`Running commands in ${basePath}`); try { let generate = execSync(`${graphQLCmd} generate --backend`, { encoding: 'utf8', cwd: basePath }); const codegen = execSync(`${graphQLCmd} codegen`, { encoding: 'utf8', cwd: basePath }); console.log(` Generate: ${generate}\n Codegen: ${codegen}\n `); t.true(codegen.indexOf('error') === -1); t.true(generate.indexOf('failed') === -1); } catch (error) { t.fail(`build failed with ${error}`); } }); ================================================ FILE: integration/tsconfig.json ================================================ { "compilerOptions": { "skipLibCheck": true, "esModuleInterop": true, "importHelpers": true, "experimentalDecorators": true, "module": "commonjs", "target": "es2018", "lib": ["es6", "esnext", "es2015", "dom"], "moduleResolution": "node", "emitDecoratorMetadata": true, "sourceMap": true, "declaration": true, "outDir": "./dist", "rootDir": "./tests", "noImplicitAny": true, "noImplicitThis": true, "alwaysStrict": true, "noImplicitReturns": true, "noUnusedLocals": true, "noUnusedParameters": false }, "files": ["src/index.ts"], "exclude": ["node_modules"] } ================================================ FILE: lerna.json ================================================ { "version": "4.1.0", "npmClient": "yarn", "useWorkspaces": true } ================================================ FILE: package.json ================================================ { "name": "graphql-cli-monorepo", "version": "4.1.0", "private": true, "author": "dotansimha ", "license": "MIT", "workspaces": { "packages": [ "packages/*", "packages/commands/*", "website", "integration" ] }, "scripts": { "release": "node scripts/release.js", "release:next": "node scripts/release.js --tag=next", "release:canary": "node scripts/release.js --canary", "test": "lerna run test", "build": "lerna run build", "format": "prettier --write \"**/*.{ts,tsx}\"", "deploy:website": "cd website && yarn deploy" }, "devDependencies": { "@graphql-tools/load": "6.2.8", "@types/express": "4.17.13", "@types/fs-extra": "9.0.13", "@types/fullname": "2.1.29", "@types/inquirer": "7.3.3", "@types/js-yaml": "3.12.7", "@types/node": "14.17.33", "@types/rimraf": "3.0.2", "@types/tmp": "0.2.2", "graphql": "15.7.2", "husky": "4.3.8", "lerna": "3.22.1", "lint-staged": "10.5.4", "prettier": "2.4.1", "rimraf": "3.0.2", "ts-node": "9.1.1", "typescript": "4.4.4" }, "publishConfig": { "access": "public" }, "lint-staged": { "*.{ts,tsx}": [ "tslint --fix", "prettier --write", "git add" ] }, "prettier": { "printWidth": 120, "singleQuote": true }, "resolutions": { "graphql": "15.7.2" }, "dependencies": { "@types/yargs": "15.0.14" } } ================================================ FILE: packages/cli/.gitignore ================================================ node_modules npm-debug.log dist temp yarn-error.log tests/coverage/ ================================================ FILE: packages/cli/.npmignore ================================================ src/ ================================================ FILE: packages/cli/CONTRIBUTING.md ================================================ # Contributing guide Please see the project root at https://github.com/Urigo/graphql-cli/CONTRIBUTING.md for more information about contributing to GraphQL CLI and the greater GraphQL ecosystem. ================================================ FILE: packages/cli/LICENSE ================================================ MIT License Copyright (c) 2019 Dotan Simha Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: packages/cli/README.md ================================================ # GraphQL CLI ![image](https://user-images.githubusercontent.com/20847995/67651234-85bf1500-f916-11e9-90e5-cb3bd0e6a338.png) Repo and documentation at: https://github.com/Urigo/graphql-cli ================================================ FILE: packages/cli/package.json ================================================ { "name": "graphql-cli", "description": "Command line tool for common GraphQL development workflows", "keywords": [ "graphql", "graphql-cli", "cli" ], "repository": { "type": "git", "url": "https://github.com/Urigo/graphql-cli" }, "author": { "name": "Uri Goldshtein", "email": "uri.goldshtein@gmail.com", "url": "https://github.com/Urigo" }, "version": "5.0.0", "license": "MIT", "main": "dist/index.js", "publishConfig": { "access": "public" }, "bin": { "graphql": "dist/bin.js" }, "scripts": { "start": "ts-node src/bin.ts", "build": "tsc" }, "dependencies": { "@graphql-cli/common": "^5.0.0", "@graphql-cli/init": "^5.0.0", "globby": "^11.0.2", "graphql-config": "^3.2.0", "open": "^7.4.2", "yargs": "^16.2.0" } } ================================================ FILE: packages/cli/src/bin.ts ================================================ #!/usr/bin/env node import { cli } from './index'; cli(); ================================================ FILE: packages/cli/src/discover.ts ================================================ import { defineCommand } from '@graphql-cli/common'; import open from 'open'; export default defineCommand(() => { return { command: 'discover', describe: 'Opens a list of all GraphQL CLI Plugins', handler() { return open('https://www.npmjs.com/search?q=keywords:graphql-cli-plugin'); }, }; }); ================================================ FILE: packages/cli/src/index.ts ================================================ import yargs from 'yargs'; import globby from 'globby'; import { join } from 'path'; import { CommandFactory, useConfig, useLoaders } from '@graphql-cli/common'; import discover from './discover'; export async function cli(): Promise { const program = yargs .scriptName('graphql') .detectLocale(false) .epilog('Visit https://github.com/Urigo/graphql-cli for more information') .version(); const commandPackageNames = await discoverCommands(); const commandFactories = await Promise.all(commandPackageNames.map(loadCommand)); [discover, ...commandFactories].forEach((cmd) => { program.command( cmd({ useConfig, useLoaders, }) ); }); program.demandCommand().recommendCommands().help().showHelpOnFail(false).argv; } async function discoverCommands() { const commandNames: string[] = []; const paths = require.resolve.paths('graphql-cli'); await Promise.all(paths.map(findInDirectory)); async function findInDirectory(directory: string) { const results = await globby('*', { cwd: join(directory, '@graphql-cli'), onlyDirectories: true, deep: 1, ignore: ['common', 'loaders'], }); if (results.length) { commandNames.push(...results); } } // unique names return commandNames.filter((val, i, list) => list.indexOf(val) === i); } function loadCommand(name: string): CommandFactory { const mod = require(`@graphql-cli/${name}`); return mod.default || mod; } ================================================ FILE: packages/cli/tsconfig.json ================================================ { "compilerOptions": { "skipLibCheck": true, "esModuleInterop": true, "importHelpers": true, "experimentalDecorators": true, "module": "commonjs", "target": "es2018", "lib": ["es6", "esnext", "es2015", "dom"], "moduleResolution": "node", "emitDecoratorMetadata": true, "sourceMap": true, "declaration": true, "outDir": "./dist", "rootDir": "./src", "noImplicitAny": true, "noImplicitThis": true, "alwaysStrict": true, "noImplicitReturns": true, "noUnusedLocals": true, "noUnusedParameters": false }, "include": ["src"], "exclude": ["node_modules"] } ================================================ FILE: packages/commands/generate/.gitignore ================================================ node_modules npm-debug.log dist temp yarn-error.log tests/coverage/ ================================================ FILE: packages/commands/generate/README.md ================================================ ## GraphQL CLI Generate command This package is part of the GraphQL CLI ecosystem and it is not designed to be consumed separately. Go to: https://github.com/Urigo/graphql-cli for more information ================================================ FILE: packages/commands/generate/package.json ================================================ { "name": "@graphql-cli/generate", "description": "Generate schema and client-side documents for your GraphQL project by using Graphback.", "version": "5.0.0", "license": "MIT", "main": "dist/index.js", "repository": { "type": "git", "url": "https://github.com/Urigo/graphql-cli", "directory": "packages/commands/generate" }, "publishConfig": { "access": "public" }, "keywords": [ "graphql-cli", "graphql-cli-plugin" ], "scripts": { "build": "tsc" }, "peerDependencies": { "graphql": "15.7.2" }, "dependencies": { "@graphql-cli/common": "^5.0.0", "graphback-cli": "1.1.2", "tslib": "2.3.1" } } ================================================ FILE: packages/commands/generate/src/index.ts ================================================ import { defineCommand } from '@graphql-cli/common'; import { command, builder as builderConfig, handler } from 'graphback-cli'; export default defineCommand(() => { return { command, builder(builder: any) { return builder.options(builderConfig); }, handler, }; }); ================================================ FILE: packages/commands/generate/tsconfig.json ================================================ { "compilerOptions": { "skipLibCheck": true, "esModuleInterop": true, "importHelpers": true, "experimentalDecorators": true, "module": "commonjs", "target": "es2018", "lib": ["es6", "esnext", "es2015", "dom"], "moduleResolution": "node", "emitDecoratorMetadata": true, "sourceMap": true, "declaration": true, "outDir": "./dist", "rootDir": "./src", "noImplicitAny": true, "noImplicitThis": true, "alwaysStrict": true, "noImplicitReturns": true, "noUnusedLocals": true, "noUnusedParameters": false }, "files": ["src/index.ts"], "exclude": ["node_modules"] } ================================================ FILE: packages/commands/init/.gitignore ================================================ node_modules npm-debug.log dist temp yarn-error.log tests/coverage/ ================================================ FILE: packages/commands/init/.npmignore ================================================ src/ ================================================ FILE: packages/commands/init/README.md ================================================ ## GraphQL CLI Init command This package is part of the GraphQL CLI ecosystem and it is not designed to be consumed separately. Go to: https://github.com/Urigo/graphql-cli for more information ================================================ FILE: packages/commands/init/package.json ================================================ { "name": "@graphql-cli/init", "description": "Creates a GraphQL project using a template or GraphQL Config file for your existing project.", "version": "5.0.0", "license": "MIT", "main": "dist/index.js", "publishConfig": { "access": "public" }, "repository": { "type": "git", "url": "https://github.com/Urigo/graphql-cli", "directory": "packages/commands/init" }, "keywords": [ "graphql-cli", "graphql-cli-plugin" ], "scripts": { "build": "tsc" }, "dependencies": { "@graphql-cli/common": "^5.0.0", "chalk": "^4.1.0", "cosmiconfig": "^7.0.0", "create-graphback": "^1.1.2", "cross-fetch": "^3.0.6", "fs-extra": "^9.1.0", "fullname": "^4.0.1", "graphql": "^15.5.0", "inquirer": "7.3.3", "js-yaml": "^3.14.1", "latest-version": "^5.1.0", "openapi-to-graphql": "^2.2.6", "ora": "^5.3.0", "rimraf": "^3.0.2", "simple-git": "^2.36.1", "string-env-interpolation": "^1.0.1", "tmp": "^0.2.1", "tslib": "~2.3.0" } } ================================================ FILE: packages/commands/init/src/common.ts ================================================ import { join } from 'path'; import { ensureFile, writeFileSync } from 'fs-extra'; import { prompt } from 'inquirer'; import fullName from 'fullname'; import latestVersion from 'latest-version'; type StandardEnum = { [id: string]: T | string; [nu: number]: string; }; export async function askForEnum>(options: { enum: Enum; message: string; defaultValue?: T; ignoreList?: (T | string)[]; }): Promise { let choices: (T | string)[]; const enumValues = Object.values(options.enum); if (options.ignoreList) { choices = enumValues.filter((enumValue) => !options.ignoreList.includes(enumValue)); } else { choices = enumValues; } const { answer } = await prompt<{ answer: T }>([ { type: 'list', name: 'answer', message: options.message, choices, default: options.defaultValue, }, ]); return answer; } export interface Context { name: string; path: string; type?: ProjectType; graphqlConfig: any; } export enum InitializationType { FromScratch = 'I want to create a new project from a GraphQL CLI Project Template.', ExistingOpenAPI = 'I have an existing project using OpenAPI/Swagger Schema Definition.', ExistingGraphQL = 'I have an existing project using GraphQL and want to add GraphQL CLI (run from project root).', } export enum ProjectType { FullStack = 'Full Stack', FrontendOnly = 'Frontend only', BackendOnly = 'Backend only', } export enum FrontendType { TSReactApollo = 'TypeScript React Apollo', ApolloAngular = 'Apollo Angular', StencilApollo = 'Stencil Apollo', TSUrql = 'TypeScript Urql', GraphQLRequest = 'GraphQL Request', ApolloAndroid = 'Apollo Android', Other = 'Other', } export enum BackendType { TS = 'TypeScript', Java = 'Java', Kotlin = 'Kotlin', Other = 'Other', } export type PackageManifest = ReturnType; export function managePackageManifest() { const packages = new Set(); const scripts = new Map(); return { addDependency(name: string) { packages.add(name); }, addScript(name: string, script: string) { scripts.set(name, script); }, async writePackage({ path, name, initializationType, }: { path: string; name: string; initializationType: InitializationType; }) { let packageJson: any = {}; const packageJsonPath = join(path, 'package.json'); // Try to load existing package.json try { const importedPackageJson = require(packageJsonPath); packageJson = importedPackageJson || {}; } catch (err) {} if (initializationType === InitializationType.FromScratch) { packageJson.private = true; packageJson.name = name; const author = await fullName(); if (author) { packageJson.author = { name: author, }; } } for (const [scriptName, scriptValue] of scripts) { if (!packageJson.scripts) { packageJson.scripts = {}; } if (!packageJson.scripts[scriptName]) { packageJson.scripts[scriptName] = scriptValue; } } // Add dev dependencies packageJson.devDependencies = packageJson.devDependencies || {}; for await (const npmDependency of packages) { if (!(npmDependency in packageJson.devDependencies)) { packageJson.devDependencies[npmDependency] = await latestVersion(npmDependency); } } await ensureFile(packageJsonPath); writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2)); }, }; } ================================================ FILE: packages/commands/init/src/features/codegen.ts ================================================ import { prompt } from 'inquirer'; import { Context, PackageManifest, ProjectType, FrontendType, BackendType, askForEnum } from '../common'; export async function askForCodegen({ context, project }: { context: Context; project: PackageManifest }) { if (!context.graphqlConfig.extensions.codegen) { const { isCodegenAsked } = await prompt([ { type: 'confirm', name: 'isCodegenAsked', message: 'Do you want to use GraphQL Code Generator?', default: true, }, ]); if (isCodegenAsked) { project.addDependency('@graphql-cli/codegen'); project.addScript('graphql:codegen', 'graphql codegen'); context.graphqlConfig.extensions.codegen = { generates: {}, }; let codegenPlugins = new Set(); if (context.type === ProjectType.FullStack || context.type === ProjectType.BackendOnly) { const backendType = await askForEnum({ enum: BackendType, message: 'What type of backend do you use?', defaultValue: BackendType.TS, }); switch (backendType) { case BackendType.TS: codegenPlugins.add('typescript'); codegenPlugins.add('typescript-resolvers'); break; case BackendType.Java: codegenPlugins.add('java'); codegenPlugins.add('java-resolvers'); break; case BackendType.Kotlin: codegenPlugins.add('java'); codegenPlugins.add('java-kotlin'); break; } const { backendGeneratedFile } = await prompt([ { type: 'input', name: 'backendGeneratedFile', message: 'Where do you want to have generated backend code?', default: './generated-backend.ts', }, ]); context.graphqlConfig.extensions.codegen.generates[backendGeneratedFile] = { plugins: [...codegenPlugins], }; } if (context.type === ProjectType.FullStack || context.type === ProjectType.FrontendOnly) { const frontendType = await askForEnum({ enum: FrontendType, message: 'What type of frontend do you use?', defaultValue: FrontendType.TSReactApollo, }); switch (frontendType) { case FrontendType.TSReactApollo: codegenPlugins.add('typescript'); codegenPlugins.add('typescript-react-apollo'); break; case FrontendType.ApolloAngular: codegenPlugins.add('typescript'); codegenPlugins.add('typescript-apollo-angular'); break; case FrontendType.StencilApollo: codegenPlugins.add('typescript'); codegenPlugins.add('typescript-stencil-apollo'); break; case FrontendType.TSUrql: codegenPlugins.add('typescript'); codegenPlugins.add('typescript-urql'); break; case FrontendType.GraphQLRequest: codegenPlugins.add('typescript'); codegenPlugins.add('typescript-graphql-request'); break; case FrontendType.ApolloAndroid: codegenPlugins.add('java-apollo-android'); break; } const { frontendGeneratedFile } = await prompt([ { type: 'input', name: 'frontendGeneratedFile', message: 'Where do you want to have generated frontend code?', default: './generated-frontend.ts', }, ]); context.graphqlConfig.extensions.codegen.generates[frontendGeneratedFile] = { plugins: [...codegenPlugins], }; } for (const codegenPlugin of codegenPlugins) { project.addDependency('@graphql-codegen/' + codegenPlugin); } } } } ================================================ FILE: packages/commands/init/src/features/inspector.ts ================================================ import { prompt } from 'inquirer'; import { Context, PackageManifest, ProjectType } from '../common'; export async function askForInspector({ context, project }: { context: Context; project: PackageManifest }) { if (context.type === ProjectType.FullStack || context.type === ProjectType.FrontendOnly) { const { isFrontendInspectorAsked } = await prompt([ { type: 'confirm', name: 'isFrontendInspectorAsked', message: 'Do you want to have GraphQL Inspector tools for your frontend?', default: true, }, ]); if (isFrontendInspectorAsked) { project.addDependency('@graphql-cli/coverage'); project.addDependency('@graphql-cli/validate'); project.addScript('graphql:coverage', 'graphql coverage'); project.addScript('graphql:validate', 'graphql validate'); } } if (context.type === ProjectType.FullStack || context.type === ProjectType.BackendOnly) { const { isBackendInspectorAsked } = await prompt([ { type: 'confirm', name: 'isBackendInspectorAsked', message: 'Do you want to have GraphQL Inspector tools for your backend?', default: true, }, ]); if (isBackendInspectorAsked) { project.addDependency('@graphql-cli/diff'); project.addDependency('@graphql-cli/similar'); project.addScript('graphql:diff', 'graphql diff'); project.addScript('graphql:similar', 'graphql similar'); if (!context.graphqlConfig.extensions.diff) { context.graphqlConfig.extensions.diff = { baseSchema: 'your-base-schema-here', }; } } } } ================================================ FILE: packages/commands/init/src/index.ts ================================================ import { defineCommand } from '@graphql-cli/common'; import { prompt } from 'inquirer'; import { join } from 'path'; import chalk from 'chalk'; import { ensureFile, writeFileSync, readFileSync, existsSync } from 'fs-extra'; import { safeLoad as YAMLParse, safeDump as YAMLStringify } from 'js-yaml'; import { Context, InitializationType, ProjectType, askForEnum, managePackageManifest } from './common'; import { askForInspector } from './features/inspector'; import { askForCodegen } from './features/codegen'; import { fromExisting } from './sources/from-existing'; import { fromScratch } from './sources/from-scratch'; import { fromExistingOpenAPI } from './sources/from-open-api'; export default defineCommand< {}, { projectName?: string; templateName?: string; templateUrl?: string; } >(() => { return { command: 'init', builder(yargs) { return yargs.options({ projectName: { describe: 'Project name', type: 'string', }, templateName: { describe: 'Name of the predefined template', type: 'string', }, templateUrl: { describe: 'GitHub URL of the template. For example (http://github.com/example/graphql-cli-example-template)', type: 'string', }, }); }, async handler(args) { let { projectName, templateName, templateUrl } = args; const initializationType = await askForInitializationType(); const context: Context = { name: projectName, path: process.cwd(), graphqlConfig: { extensions: {}, }, }; const project = managePackageManifest(); project.addDependency('graphql-cli'); if (initializationType === InitializationType.ExistingGraphQL) { await fromExisting({ context, project, }); } else if (initializationType === InitializationType.FromScratch) { await fromScratch({ context, templateName, templateUrl, }); } if (initializationType !== InitializationType.FromScratch) { context.type = await askForProject(); } loadGraphQLConfig(context); if (initializationType === InitializationType.ExistingOpenAPI) { await fromExistingOpenAPI(context); } await askForSchema(context); await askForDocuments(context); await askForCodegen({ context, project }); await askForInspector({ context, project }); await Promise.all([ writeGraphQLConfig(context), project.writePackage({ path: context.path, name: projectName, initializationType, })]); const successMessages = [ `🚀 GraphQL CLI project successfully initialized:`, context.name, 'Next Steps:', `- Change directory to the project folder - ${chalk.cyan(`cd ${context.path}`)}`, `- Run ${chalk.cyan(`yarn install`)} to install dependencies`, ]; if (initializationType !== InitializationType.ExistingGraphQL) { successMessages.push( `- ${chalk.cyan(`(Optional)`)} Initialize your git repo. ${chalk.cyan(`git init`)}.`, `- Follow the instructions in README.md to continue...` ); } console.info(successMessages.join('\n')); process.exit(0); }, }; }); function askForProject() { return askForEnum({ enum: ProjectType, message: 'What is the type of the project?', defaultValue: ProjectType.FullStack, }); } function askForInitializationType() { return askForEnum({ enum: InitializationType, message: 'Select the best option for you', defaultValue: InitializationType.FromScratch, ignoreList: [], }); } function loadGraphQLConfig(context: Context) { const graphqlConfigPath = join(context.path, '.graphqlrc.yml'); try { if (existsSync(graphqlConfigPath)) { context.graphqlConfig = YAMLParse(readFileSync(graphqlConfigPath, 'utf8')); } } catch (e) { console.warn(`Existing GraphQL Config file looks broken! Skipping...`); } } async function askForSchema(context: Context) { if (!context.graphqlConfig.schema) { const { schema } = await prompt([ { type: 'input', name: 'schema', message: 'Where is your schema?', default: './schema.graphql', }, ]); context.graphqlConfig.schema = schema.endsWith('.ts') ? { [schema]: { require: 'ts-node/register', }, } : schema; } } async function askForDocuments(context: Context) { if ( !context.graphqlConfig.documents && (context.type === ProjectType.FullStack || context.type === ProjectType.FrontendOnly) ) { const { documents } = await prompt([ { type: 'input', name: 'documents', message: 'Where are your operation documents?', }, ]); context.graphqlConfig.documents = documents; } } async function writeGraphQLConfig(context: Context) { const configPath = join(context.path, '.graphqlrc.yml'); await ensureFile(configPath); const keys = ['schema', 'documents', 'extensions']; function sortKeys(a: string, b: string) { const ai = keys.indexOf(a); const bi = keys.indexOf(b); if (ai === -1 && bi === -1) { return a.localeCompare(b); } return ai <= bi ? -1 : 1; } writeFileSync( configPath, YAMLStringify(context.graphqlConfig, { sortKeys, }) ); } ================================================ FILE: packages/commands/init/src/search-codegen-config.ts ================================================ import { cosmiconfig, defaultLoaders } from 'cosmiconfig'; import { env } from 'string-env-interpolation'; function generateSearchPlaces(moduleName: string) { const extensions = ['json', 'yaml', 'yml', 'js', 'config.js']; // gives codegen.json... const regular = extensions.map((ext) => `${moduleName}.${ext}`); // gives .codegenrc.json... but no .codegenrc.config.js const dot = extensions.filter((ext) => ext !== 'config.js').map((ext) => `.${moduleName}rc.${ext}`); return regular.concat(dot); } function customLoader(ext: 'json' | 'yaml' | 'js') { function loader(filepath: string, content: string) { if (typeof process !== 'undefined' && 'env' in process) { content = env(content); } if (ext === 'json') { return defaultLoaders['.json'](filepath, content); } if (ext === 'yaml') { return defaultLoaders['.yaml'](filepath, content); } if (ext === 'js') { return defaultLoaders['.js'](filepath, content); } } return loader; } export async function searchCodegenConfig(cwd: string) { const moduleName = 'codegen'; const cosmi = cosmiconfig(moduleName, { searchPlaces: generateSearchPlaces(moduleName), loaders: { '.json': customLoader('json'), '.yaml': customLoader('yaml'), '.yml': customLoader('yaml'), '.js': customLoader('js'), noExt: customLoader('yaml'), }, }); return cosmi.search(cwd); } ================================================ FILE: packages/commands/init/src/sources/from-existing.ts ================================================ import rimraf from 'rimraf'; import { join } from 'path'; import { existsSync } from 'fs-extra'; import { prompt } from 'inquirer'; import { Context, PackageManifest } from '../common'; import { searchCodegenConfig } from '../search-codegen-config'; export async function fromExisting({ context, project }: { context: Context; project: PackageManifest }) { const manifestPath = join(context.path, 'package.json'); if (existsSync(manifestPath)) { const { name: projectName } = require(manifestPath); context.name = projectName; } const result = await searchCodegenConfig(context.path); if (result && !result.isEmpty) { const codegenFilePath = result.filepath; const { willBeMerged } = await prompt([ { type: 'confirm', name: 'willBeMerged', message: `GraphQL Code Generator configuration has been detected in ${codegenFilePath}.\n Do you want to use the same configuration with GraphQL CLI?`, default: true, }, ]); if (willBeMerged) { project.addDependency('@graphql-cli/codegen'); const codegenConfig = result.config; context.graphqlConfig.extensions.codegen = { generates: {}, }; for (const key in codegenConfig) { if (key === 'schema') { context.graphqlConfig.schema = codegenConfig.schema; } else if (key === 'documents') { context.graphqlConfig.documents = codegenConfig.documents; } else { context.graphqlConfig.extensions.codegen[key] = codegenConfig[key]; } } rimraf.sync(result.filepath); } } } ================================================ FILE: packages/commands/init/src/sources/from-open-api.ts ================================================ import ora from 'ora'; import { safeLoad as YAMLParse } from 'js-yaml'; import { readFileSync, writeFileSync, ensureFile } from 'fs-extra'; import { prompt } from 'inquirer'; import { Context } from '../common'; export async function fromExistingOpenAPI(context: Context) { const { openApiPath } = await prompt<{ openApiPath: string }>([ { type: 'input', name: 'openApiPath', message: 'Enter your OpenAPI schema path', default: './swagger.json', }, ]); const processingOpenAPISpinner = ora(`Processing OpenAPI definition: ${openApiPath}`).start(); const schemaText: string = readFileSync(`${openApiPath}`, 'utf8'); const parsedObject = openApiPath.endsWith('yaml') || openApiPath.endsWith('yml') ? YAMLParse(schemaText) : JSON.parse(schemaText); const datamodelPath = `${context.graphqlConfig.extensions.graphback.model}/datamodel.graphql`; try { const { createGraphQLSchema } = await import('openapi-to-graphql'); const { schema } = await createGraphQLSchema(parsedObject, { strict: true, fillEmptyResponses: true, equivalentToMessages: false, }); const { printSchema } = await import('graphql'); const schemaString = printSchema(schema); await ensureFile(datamodelPath); writeFileSync(datamodelPath, schemaString); processingOpenAPISpinner.succeed(); } catch (err) { processingOpenAPISpinner.fail(`Failed to process OpenAPI definition: ${datamodelPath}. Error: ${err}`); } } ================================================ FILE: packages/commands/init/src/sources/from-scratch.ts ================================================ import { join } from 'path'; import { prompt } from 'inquirer'; import { Context } from '../common'; import { handler } from 'create-graphback'; export async function fromScratch({ context, templateName, templateUrl, }: { context: Context; templateName: string; templateUrl: string; }) { if (!context.name) { const { projectName: enteredName } = await prompt([ { type: 'input', name: 'projectName', message: 'What is the name of the project?', default: 'my-graphql-project', }, ]); context.name = enteredName; context.path = join(process.cwd(), context.name); } await handler({ name: context.name, templateName, templateUrl }); } ================================================ FILE: packages/commands/init/tsconfig.json ================================================ { "compilerOptions": { "skipLibCheck": true, "esModuleInterop": true, "importHelpers": true, "experimentalDecorators": true, "module": "commonjs", "target": "es2018", "lib": ["es6", "esnext", "es2015", "dom"], "moduleResolution": "node", "emitDecoratorMetadata": true, "sourceMap": true, "declaration": true, "outDir": "./dist", "rootDir": "./src", "noImplicitAny": true, "noImplicitThis": true, "alwaysStrict": true, "noImplicitReturns": true, "noUnusedLocals": true, "noUnusedParameters": false, "resolveJsonModule": true }, "files": ["src/index.ts"], "exclude": ["node_modules"] } ================================================ FILE: packages/commands/serve/.gitignore ================================================ node_modules npm-debug.log dist temp yarn-error.log tests/coverage/ ================================================ FILE: packages/commands/serve/.npmignore ================================================ src/ ================================================ FILE: packages/commands/serve/README.md ================================================ ## GraphQL CLI Serve command This package is part of the GraphQL CLI ecosystem and it is not designed to be consumed separately. Go to: https://github.com/Urigo/graphql-cli for more information ================================================ FILE: packages/commands/serve/package.json ================================================ { "name": "@graphql-cli/serve", "description": "Serves a GraphQL server using an in memory MongoDB", "version": "5.0.0", "license": "MIT", "main": "dist/index.js", "publishConfig": { "access": "public" }, "repository": { "type": "git", "url": "https://github.com/Urigo/graphql-cli", "directory": "packages/commands/serve" }, "keywords": [ "graphql-cli", "graphql-cli-plugin" ], "scripts": { "build": "tsc" }, "peerDependencies": { "graphql": "15.7.2" }, "dependencies": { "@graphql-cli/common": "^5.0.0", "graphql-serve": "^1.1.2", "tslib": "~2.3.0" } } ================================================ FILE: packages/commands/serve/src/index.ts ================================================ import { defineCommand } from '@graphql-cli/common'; import { serve } from 'graphql-serve'; export default defineCommand(() => { return { builder: (builder: any) => { serve.builder(builder); return builder; }, command: serve.command, handler: serve.handler, }; }); ================================================ FILE: packages/commands/serve/tsconfig.json ================================================ { "compilerOptions": { "skipLibCheck": true, "esModuleInterop": true, "importHelpers": true, "experimentalDecorators": true, "module": "commonjs", "target": "es2018", "lib": ["es6", "esnext", "es2015", "dom"], "moduleResolution": "node", "emitDecoratorMetadata": true, "sourceMap": true, "declaration": true, "outDir": "./dist", "rootDir": "./src", "noImplicitAny": true, "noImplicitThis": true, "alwaysStrict": true, "noImplicitReturns": true, "noUnusedLocals": true, "noUnusedParameters": false }, "files": ["src/index.ts"], "exclude": ["node_modules"] } ================================================ FILE: packages/common/.gitignore ================================================ node_modules npm-debug.log dist temp yarn-error.log tests/coverage/ ================================================ FILE: packages/common/.npmignore ================================================ src/ ================================================ FILE: packages/common/package.json ================================================ { "name": "@graphql-cli/common", "version": "5.0.0", "license": "MIT", "main": "dist/index.js", "publishConfig": { "access": "public" }, "scripts": { "build": "tsc" }, "peerDependencies": { "graphql-config": "3.4.1", "yargs": "16.2.0" }, "dependencies": { "@graphql-tools/load": "^7.1.6", "tslib": "^2.3.1" } } ================================================ FILE: packages/common/src/command.ts ================================================ import { CommandModule } from 'yargs'; import { loadConfig, GraphQLConfig } from 'graphql-config'; import { loadDocuments, loadSchema } from '@graphql-tools/load'; import { Loader } from '@graphql-tools/utils'; import { LoadConfigOptions } from './types'; export type CommandFactory = (api: { useConfig: (options?: LoadConfigOptions) => Promise; useLoaders: typeof useLoaders; }) => CommandModule; export function defineCommand(factory: CommandFactory) { return factory; } export function useConfig(options: LoadConfigOptions = {}) { return loadConfig({ rootDir: process.cwd(), throwOnEmpty: true, throwOnMissing: true, ...options, }); } type PointerOf any> = Parameters[0]; type OptionsOf any> = Omit[1], 'loaders'>; export function useLoaders({ loaders }: { loaders: Loader[] }) { return { loadDocuments(pointer: PointerOf, options: OptionsOf) { return loadDocuments(pointer, { loaders, ...options }); }, loadSchema(pointer: PointerOf, options: OptionsOf) { return loadSchema(pointer, { loaders, ...options }); }, }; } ================================================ FILE: packages/common/src/index.ts ================================================ export * from './types'; export * from './command'; ================================================ FILE: packages/common/src/types.ts ================================================ import { loadConfig } from 'graphql-config'; export type LoadConfigOptions = Partial[0]>; ================================================ FILE: packages/common/tsconfig.json ================================================ { "compilerOptions": { "skipLibCheck": true, "importHelpers": true, "experimentalDecorators": true, "module": "commonjs", "target": "es2018", "lib": ["es6", "esnext", "es2015", "dom"], "moduleResolution": "node", "emitDecoratorMetadata": true, "sourceMap": true, "declaration": true, "outDir": "./dist", "rootDir": "./src", "noImplicitAny": true, "noImplicitThis": true, "alwaysStrict": true, "noImplicitReturns": true, "noUnusedLocals": true, "noUnusedParameters": false }, "files": ["src/index.ts"], "exclude": ["node_modules"] } ================================================ FILE: packages/loaders/.gitignore ================================================ node_modules npm-debug.log dist temp yarn-error.log tests/coverage/ ================================================ FILE: packages/loaders/.npmignore ================================================ src/ ================================================ FILE: packages/loaders/package.json ================================================ { "name": "@graphql-cli/loaders", "description": "Internal usage", "version": "5.0.0", "license": "MIT", "main": "dist/index.js", "publishConfig": { "access": "public" }, "scripts": { "build": "tsc" }, "peerDependencies": { "graphql": "15.7.2" }, "dependencies": { "@graphql-tools/apollo-engine-loader": "^7.0.4", "@graphql-tools/code-file-loader": "^7.0.4", "@graphql-tools/git-loader": "^7.0.4", "@graphql-tools/github-loader": "^7.0.4", "@graphql-tools/prisma-loader": "^7.0.5", "@graphql-tools/url-loader": "^7.0.10", "@graphql-tools/utils": "^8.1.1" } } ================================================ FILE: packages/loaders/src/index.ts ================================================ import { Loader } from '@graphql-tools/utils'; import { ApolloEngineLoader } from '@graphql-tools/apollo-engine-loader'; import { CodeFileLoader } from '@graphql-tools/code-file-loader'; import { GitLoader } from '@graphql-tools/git-loader'; import { GithubLoader } from '@graphql-tools/github-loader'; import { PrismaLoader } from '@graphql-tools/prisma-loader'; import { UrlLoader } from '@graphql-tools/url-loader'; export const loaders: Loader[] = [ new ApolloEngineLoader(), new CodeFileLoader(), new GitLoader(), new GithubLoader(), new PrismaLoader(), new UrlLoader(), ]; ================================================ FILE: packages/loaders/tsconfig.json ================================================ { "compilerOptions": { "skipLibCheck": true, "importHelpers": true, "experimentalDecorators": true, "module": "commonjs", "target": "es2018", "lib": ["es6", "esnext", "es2015", "dom"], "moduleResolution": "node", "emitDecoratorMetadata": true, "sourceMap": true, "declaration": true, "outDir": "./dist", "rootDir": "./src", "noImplicitAny": true, "noImplicitThis": true, "alwaysStrict": true, "noImplicitReturns": true, "noUnusedLocals": true, "noUnusedParameters": false }, "files": ["src/index.ts"], "exclude": ["node_modules"] } ================================================ FILE: renovate.json ================================================ { "extends": [ "config:base" ], "automerge": true, "major": { "automerge": false }, "rangeStrategy": "replace" } ================================================ FILE: scripts/introspect-config.js ================================================ const { loadSchema } = require('@graphql-tools/load'); const { GraphQLFileLoader } = require('@graphql-tools/graphql-file-loader'); const { printSchemaWithDirectives } = require('@graphql-tools/utils'); const { writeFileSync } = require('fs'); const { resolve } = require('path'); const { DIRECTIVES } = require('graphql-to-config-schema'); async function main() { const schema = await loadSchema([DIRECTIVES, './**/yaml-config.graphql'], { loaders: [new GraphQLFileLoader()], assumeValidSDL: true, }); writeFileSync(resolve(__dirname, '../schema.graphql'), printSchemaWithDirectives(schema)); } main().catch(console.error); ================================================ FILE: scripts/release.js ================================================ const { argv } = require('yargs'); const { sync: glob } = require('globby'); const { writeFile } = require('fs-extra'); const { resolve, dirname, join } = require('path'); const semver = require('semver'); const cp = require('child_process'); const rootPackageJson = require('../package.json'); async function release() { let version = process.env.RELEASE_VERSION || rootPackageJson.version; if (version.startsWith('v')) { version = version.replace('v', ''); } let tag = argv.tag || 'latest'; if (argv.canary) { const gitHash = cp.spawnSync('git', ['rev-parse', '--short', 'HEAD']).stdout.toString().trim(); version = semver.inc(version, 'prerelease', true, 'alpha-' + gitHash); tag = 'canary'; } const workspaceGlobs = (rootPackageJson.workspaces.packages || rootPackageJson.workspaces).map( (workspace) => workspace + '/package.json' ); const packageJsonPaths = glob(workspaceGlobs).map((packageJsonPath) => resolve(process.cwd(), packageJsonPath)); const packageNames = packageJsonPaths.map((packageJsonPath) => require(packageJsonPath).name); rootPackageJson.version = version; await writeFile(resolve(__dirname, '../package.json'), JSON.stringify(rootPackageJson, null, 2)); await Promise.all( packageJsonPaths.map(async (packageJsonPath) => { const packageJson = require(packageJsonPath); packageJson.version = version; for (const dependency in packageJson.dependencies) { if (packageNames.includes(dependency)) { packageJson.dependencies[dependency] = version; } } for (const dependency in packageJson.devDependencies) { if (packageNames.includes(dependency)) { packageJson.devDependencies[dependency] = version; } } await writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2)); if (!packageJson.private) { const distDirName = (packageJson.publishConfig && packageJson.publishConfig.directory) || ''; const distPath = join(dirname(packageJsonPath), distDirName); //Fix package.json in dist directory const distPackageJsonPath = join(distPath, 'package.json'); const distPackageJson = require(distPackageJsonPath); distPackageJson.name = packageJson.name; distPackageJson.version = packageJson.version; distPackageJson.dependencies = packageJson.dependencies; distPackageJson.devDependencies = packageJson.devDependencies; distPackageJson.publishConfig = { access: (packageJson.publishConfig && packageJson.publishConfig.access) || 'public', }; await writeFile(distPackageJsonPath, JSON.stringify(distPackageJson, null, 2)); return new Promise((resolve, reject) => { const publishSpawn = cp.spawn('npm', [ 'publish', distPath, '--tag', tag, '--access', distPackageJson.publishConfig.access, ]); publishSpawn.stdout.on('data', (data) => { console.info(data.toString('utf8')); }); publishSpawn.stderr.on('data', function (data) { console.error(data.toString('utf8')); }); publishSpawn.on('exit', function (code, signal) { if (code !== 0) { reject(new Error(`npm publish exited with code: ${code} and signal: ${signal}`)); } else { resolve(); } }); }); } }) ); console.info(`${tag} => ${version}`); } release().catch((err) => { console.error(err); process.exit(1); }); ================================================ FILE: templates/fullstack/.dockerignore ================================================ node_modules npm-debug.log ================================================ FILE: templates/fullstack/.gitignore ================================================ node_modules dist types yarn.lock yarn-error.log ================================================ FILE: templates/fullstack/.graphqlrc.yml ================================================ schema: ./server/src/schema/**/*.graphql documents: ./client/src/graphql/**/*.graphql extensions: codegen: generates: ./client/src/generated-types.tsx: config: withComponent: false withHOC: false withHooks: true skipDocumentsValidation: true plugins: - add: '/* tslint:disable */' - typescript - typescript-operations - typescript-react-apollo ./server/src/generated-types.ts: config: contextType: '@graphback/core#GraphbackContext' mappers: Comment: './generated-db-types#comment' Note: './generated-db-types#note' useIndexSignature: true skipDocumentsValidation: true plugins: - add: '/* tslint:disable */' - typescript - typescript-resolvers # Graphback configuration graphback: ## Input schema model: ./model ## Global configuration for CRUD generator crud: create: true update: true find: true findOne: true delete: true subCreate: true subUpdate: true subDelete: true ## Codegen plugins plugins: graphback-schema: outputPath: ./server/src/schema/schema.graphql graphback-client: outputFile: ./client/src/graphql/graphback.graphql ================================================ FILE: templates/fullstack/Dockerfile ================================================ # Stage 1 - the build process FROM node:10 as compile-server WORKDIR /usr/src/app COPY . . RUN npm install RUN npm run build CMD [ "npm", "start" ] ================================================ FILE: templates/fullstack/README.md ================================================ # GraphQL CLI Basic Full Stack Template Starter Full Stack template using GraphQL CLI. ## Usage This project has been created using GraphQL CLI. Run the project using the following steps: ### Install yarn install ### Database Start the database docker-compose up -d Generate resources(schema and resolvers) and create database yarn graphql generate --backend yarn graphql generate --db Generate typings for Database Schema and Resolvers yarn schemats generate yarn graphql codegen ### Server Start the server yarn start:server ### Client Generate queries, mutations and subscriptions for client-side project yarn graphql generate --client Generate React components yarn graphql codegen Start React App yarn start:client ================================================ FILE: templates/fullstack/client/.gitignore ================================================ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # dependencies /node_modules /.pnp .pnp.js # testing /coverage # production /build # misc .DS_Store .env.local .env.development.local .env.test.local .env.production.local npm-debug.log* yarn-debug.log* yarn-error.log* ================================================ FILE: templates/fullstack/client/generated-types.tsx ================================================ /* tslint:disable */ import gql from 'graphql-tag'; import * as ApolloReactCommon from '@apollo/react-common'; import * as ApolloReactHooks from '@apollo/react-hooks'; export type Maybe = T | null; /** All built-in and custom scalars, mapped to their actual values */ export type Scalars = { ID: string; String: string; Boolean: boolean; Int: number; Float: number; }; /** @model */ export type Comment = { __typename?: 'Comment'; id: Scalars['ID']; text?: Maybe; description?: Maybe; /** @manyToOne field: 'comments', key: 'noteId' */ note?: Maybe; }; export type CommentInput = { id?: Maybe; text?: Maybe; description?: Maybe; noteId?: Maybe; }; export type Mutation = { __typename?: 'Mutation'; createNote: Note; updateNote: Note; deleteNote: Note; createComment: Comment; updateComment: Comment; deleteComment: Comment; }; export type MutationCreateNoteArgs = { input?: Maybe; }; export type MutationUpdateNoteArgs = { input?: Maybe; }; export type MutationDeleteNoteArgs = { input?: Maybe; }; export type MutationCreateCommentArgs = { input?: Maybe; }; export type MutationUpdateCommentArgs = { input?: Maybe; }; export type MutationDeleteCommentArgs = { input?: Maybe; }; /** @model */ export type Note = { __typename?: 'Note'; id: Scalars['ID']; title: Scalars['String']; description?: Maybe; /** @oneToMany field: 'note', key: 'noteId' */ comments: Array>; }; export type NoteInput = { id?: Maybe; title?: Maybe; description?: Maybe; }; export type Query = { __typename?: 'Query'; findAllNotes: Array>; findNotes: Array>; findAllComments: Array>; findComments: Array>; }; export type QueryFindAllNotesArgs = { limit?: Maybe; offset?: Maybe; }; export type QueryFindNotesArgs = { fields?: Maybe; limit?: Maybe; offset?: Maybe; }; export type QueryFindAllCommentsArgs = { limit?: Maybe; offset?: Maybe; }; export type QueryFindCommentsArgs = { fields?: Maybe; limit?: Maybe; offset?: Maybe; }; export type Subscription = { __typename?: 'Subscription'; newNote: Note; updatedNote: Note; deletedNote: Note; newComment: Comment; updatedComment: Comment; deletedComment: Comment; }; export type SubscriptionNewNoteArgs = { input?: Maybe; }; export type SubscriptionUpdatedNoteArgs = { input?: Maybe; }; export type SubscriptionDeletedNoteArgs = { input?: Maybe; }; export type SubscriptionNewCommentArgs = { input?: Maybe; }; export type SubscriptionUpdatedCommentArgs = { input?: Maybe; }; export type SubscriptionDeletedCommentArgs = { input?: Maybe; }; export type CommentFieldsFragment = { __typename?: 'Comment' } & Pick; export type CommentExpandedFieldsFragment = { __typename?: 'Comment' } & Pick< Comment, 'id' | 'text' | 'description' > & { note: Maybe<{ __typename?: 'Note' } & Pick> }; export type NoteFieldsFragment = { __typename?: 'Note' } & Pick; export type NoteExpandedFieldsFragment = { __typename?: 'Note' } & Pick & { comments: Array>>; }; export type CreateCommentMutationVariables = { input: CommentInput; }; export type CreateCommentMutation = { __typename?: 'Mutation' } & { createComment: { __typename?: 'Comment' } & CommentFieldsFragment; }; export type CreateNoteMutationVariables = { input: NoteInput; }; export type CreateNoteMutation = { __typename?: 'Mutation' } & { createNote: { __typename?: 'Note' } & NoteFieldsFragment; }; export type DeleteCommentMutationVariables = { input: CommentInput; }; export type DeleteCommentMutation = { __typename?: 'Mutation' } & { deleteComment: { __typename?: 'Comment' } & CommentFieldsFragment; }; export type DeleteNoteMutationVariables = { input: NoteInput; }; export type DeleteNoteMutation = { __typename?: 'Mutation' } & { deleteNote: { __typename?: 'Note' } & NoteFieldsFragment; }; export type UpdateCommentMutationVariables = { input: CommentInput; }; export type UpdateCommentMutation = { __typename?: 'Mutation' } & { updateComment: { __typename?: 'Comment' } & CommentFieldsFragment; }; export type UpdateNoteMutationVariables = { input: NoteInput; }; export type UpdateNoteMutation = { __typename?: 'Mutation' } & { updateNote: { __typename?: 'Note' } & NoteFieldsFragment; }; export type FindAllCommentsQueryVariables = { limit?: Maybe; offset?: Maybe; }; export type FindAllCommentsQuery = { __typename?: 'Query' } & { findAllComments: Array>; }; export type FindAllNotesQueryVariables = { limit?: Maybe; offset?: Maybe; }; export type FindAllNotesQuery = { __typename?: 'Query' } & { findAllNotes: Array>; }; export type FindCommentsQueryVariables = { fields: CommentInput; limit?: Maybe; offset?: Maybe; }; export type FindCommentsQuery = { __typename?: 'Query' } & { findComments: Array>; }; export type FindNotesQueryVariables = { fields: NoteInput; limit?: Maybe; offset?: Maybe; }; export type FindNotesQuery = { __typename?: 'Query' } & { findNotes: Array>; }; export type DeletedCommentSubscriptionVariables = {}; export type DeletedCommentSubscription = { __typename?: 'Subscription' } & { deletedComment: { __typename?: 'Comment' } & CommentFieldsFragment; }; export type DeletedNoteSubscriptionVariables = {}; export type DeletedNoteSubscription = { __typename?: 'Subscription' } & { deletedNote: { __typename?: 'Note' } & NoteFieldsFragment; }; export type NewCommentSubscriptionVariables = {}; export type NewCommentSubscription = { __typename?: 'Subscription' } & { newComment: { __typename?: 'Comment' } & CommentFieldsFragment; }; export type NewNoteSubscriptionVariables = {}; export type NewNoteSubscription = { __typename?: 'Subscription' } & { newNote: { __typename?: 'Note' } & NoteFieldsFragment; }; export type UpdatedCommentSubscriptionVariables = {}; export type UpdatedCommentSubscription = { __typename?: 'Subscription' } & { updatedComment: { __typename?: 'Comment' } & CommentFieldsFragment; }; export type UpdatedNoteSubscriptionVariables = {}; export type UpdatedNoteSubscription = { __typename?: 'Subscription' } & { updatedNote: { __typename?: 'Note' } & NoteFieldsFragment; }; export const CommentFieldsFragmentDoc = gql` fragment CommentFields on Comment { id text description } `; export const CommentExpandedFieldsFragmentDoc = gql` fragment CommentExpandedFields on Comment { id text description note { id title description } } `; export const NoteFieldsFragmentDoc = gql` fragment NoteFields on Note { id title description } `; export const NoteExpandedFieldsFragmentDoc = gql` fragment NoteExpandedFields on Note { id title description comments { id text description } } `; export const CreateCommentDocument = gql` mutation createComment($input: CommentInput!) { createComment(input: $input) { ...CommentFields } } ${CommentFieldsFragmentDoc} `; export type CreateCommentMutationFn = ApolloReactCommon.MutationFunction< CreateCommentMutation, CreateCommentMutationVariables >; /** * __useCreateCommentMutation__ * * To run a mutation, you first call `useCreateCommentMutation` within a React component and pass it any options that fit your needs. * When your component renders, `useCreateCommentMutation` returns a tuple that includes: * - A mutate function that you can call at any time to execute the mutation * - An object with fields that represent the current status of the mutation's execution * * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; * * @example * const [createCommentMutation, { data, loading, error }] = useCreateCommentMutation({ * variables: { * input: // value for 'input' * }, * }); */ export function useCreateCommentMutation( baseOptions?: ApolloReactHooks.MutationHookOptions ) { return ApolloReactHooks.useMutation( CreateCommentDocument, baseOptions ); } export type CreateCommentMutationHookResult = ReturnType; export type CreateCommentMutationResult = ApolloReactCommon.MutationResult; export type CreateCommentMutationOptions = ApolloReactCommon.BaseMutationOptions< CreateCommentMutation, CreateCommentMutationVariables >; export const CreateNoteDocument = gql` mutation createNote($input: NoteInput!) { createNote(input: $input) { ...NoteFields } } ${NoteFieldsFragmentDoc} `; export type CreateNoteMutationFn = ApolloReactCommon.MutationFunction; /** * __useCreateNoteMutation__ * * To run a mutation, you first call `useCreateNoteMutation` within a React component and pass it any options that fit your needs. * When your component renders, `useCreateNoteMutation` returns a tuple that includes: * - A mutate function that you can call at any time to execute the mutation * - An object with fields that represent the current status of the mutation's execution * * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; * * @example * const [createNoteMutation, { data, loading, error }] = useCreateNoteMutation({ * variables: { * input: // value for 'input' * }, * }); */ export function useCreateNoteMutation( baseOptions?: ApolloReactHooks.MutationHookOptions ) { return ApolloReactHooks.useMutation(CreateNoteDocument, baseOptions); } export type CreateNoteMutationHookResult = ReturnType; export type CreateNoteMutationResult = ApolloReactCommon.MutationResult; export type CreateNoteMutationOptions = ApolloReactCommon.BaseMutationOptions< CreateNoteMutation, CreateNoteMutationVariables >; export const DeleteCommentDocument = gql` mutation deleteComment($input: CommentInput!) { deleteComment(input: $input) { ...CommentFields } } ${CommentFieldsFragmentDoc} `; export type DeleteCommentMutationFn = ApolloReactCommon.MutationFunction< DeleteCommentMutation, DeleteCommentMutationVariables >; /** * __useDeleteCommentMutation__ * * To run a mutation, you first call `useDeleteCommentMutation` within a React component and pass it any options that fit your needs. * When your component renders, `useDeleteCommentMutation` returns a tuple that includes: * - A mutate function that you can call at any time to execute the mutation * - An object with fields that represent the current status of the mutation's execution * * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; * * @example * const [deleteCommentMutation, { data, loading, error }] = useDeleteCommentMutation({ * variables: { * input: // value for 'input' * }, * }); */ export function useDeleteCommentMutation( baseOptions?: ApolloReactHooks.MutationHookOptions ) { return ApolloReactHooks.useMutation( DeleteCommentDocument, baseOptions ); } export type DeleteCommentMutationHookResult = ReturnType; export type DeleteCommentMutationResult = ApolloReactCommon.MutationResult; export type DeleteCommentMutationOptions = ApolloReactCommon.BaseMutationOptions< DeleteCommentMutation, DeleteCommentMutationVariables >; export const DeleteNoteDocument = gql` mutation deleteNote($input: NoteInput!) { deleteNote(input: $input) { ...NoteFields } } ${NoteFieldsFragmentDoc} `; export type DeleteNoteMutationFn = ApolloReactCommon.MutationFunction; /** * __useDeleteNoteMutation__ * * To run a mutation, you first call `useDeleteNoteMutation` within a React component and pass it any options that fit your needs. * When your component renders, `useDeleteNoteMutation` returns a tuple that includes: * - A mutate function that you can call at any time to execute the mutation * - An object with fields that represent the current status of the mutation's execution * * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; * * @example * const [deleteNoteMutation, { data, loading, error }] = useDeleteNoteMutation({ * variables: { * input: // value for 'input' * }, * }); */ export function useDeleteNoteMutation( baseOptions?: ApolloReactHooks.MutationHookOptions ) { return ApolloReactHooks.useMutation(DeleteNoteDocument, baseOptions); } export type DeleteNoteMutationHookResult = ReturnType; export type DeleteNoteMutationResult = ApolloReactCommon.MutationResult; export type DeleteNoteMutationOptions = ApolloReactCommon.BaseMutationOptions< DeleteNoteMutation, DeleteNoteMutationVariables >; export const UpdateCommentDocument = gql` mutation updateComment($input: CommentInput!) { updateComment(input: $input) { ...CommentFields } } ${CommentFieldsFragmentDoc} `; export type UpdateCommentMutationFn = ApolloReactCommon.MutationFunction< UpdateCommentMutation, UpdateCommentMutationVariables >; /** * __useUpdateCommentMutation__ * * To run a mutation, you first call `useUpdateCommentMutation` within a React component and pass it any options that fit your needs. * When your component renders, `useUpdateCommentMutation` returns a tuple that includes: * - A mutate function that you can call at any time to execute the mutation * - An object with fields that represent the current status of the mutation's execution * * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; * * @example * const [updateCommentMutation, { data, loading, error }] = useUpdateCommentMutation({ * variables: { * input: // value for 'input' * }, * }); */ export function useUpdateCommentMutation( baseOptions?: ApolloReactHooks.MutationHookOptions ) { return ApolloReactHooks.useMutation( UpdateCommentDocument, baseOptions ); } export type UpdateCommentMutationHookResult = ReturnType; export type UpdateCommentMutationResult = ApolloReactCommon.MutationResult; export type UpdateCommentMutationOptions = ApolloReactCommon.BaseMutationOptions< UpdateCommentMutation, UpdateCommentMutationVariables >; export const UpdateNoteDocument = gql` mutation updateNote($input: NoteInput!) { updateNote(input: $input) { ...NoteFields } } ${NoteFieldsFragmentDoc} `; export type UpdateNoteMutationFn = ApolloReactCommon.MutationFunction; /** * __useUpdateNoteMutation__ * * To run a mutation, you first call `useUpdateNoteMutation` within a React component and pass it any options that fit your needs. * When your component renders, `useUpdateNoteMutation` returns a tuple that includes: * - A mutate function that you can call at any time to execute the mutation * - An object with fields that represent the current status of the mutation's execution * * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; * * @example * const [updateNoteMutation, { data, loading, error }] = useUpdateNoteMutation({ * variables: { * input: // value for 'input' * }, * }); */ export function useUpdateNoteMutation( baseOptions?: ApolloReactHooks.MutationHookOptions ) { return ApolloReactHooks.useMutation(UpdateNoteDocument, baseOptions); } export type UpdateNoteMutationHookResult = ReturnType; export type UpdateNoteMutationResult = ApolloReactCommon.MutationResult; export type UpdateNoteMutationOptions = ApolloReactCommon.BaseMutationOptions< UpdateNoteMutation, UpdateNoteMutationVariables >; export const FindAllCommentsDocument = gql` query findAllComments($limit: Int, $offset: Int) { findAllComments(limit: $limit, offset: $offset) { ...CommentExpandedFields } } ${CommentExpandedFieldsFragmentDoc} `; /** * __useFindAllCommentsQuery__ * * To run a query within a React component, call `useFindAllCommentsQuery` and pass it any options that fit your needs. * When your component renders, `useFindAllCommentsQuery` returns an object from Apollo Client that contains loading, error, and data properties * you can use to render your UI. * * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; * * @example * const { data, loading, error } = useFindAllCommentsQuery({ * variables: { * limit: // value for 'limit' * offset: // value for 'offset' * }, * }); */ export function useFindAllCommentsQuery( baseOptions?: ApolloReactHooks.QueryHookOptions ) { return ApolloReactHooks.useQuery( FindAllCommentsDocument, baseOptions ); } export function useFindAllCommentsLazyQuery( baseOptions?: ApolloReactHooks.LazyQueryHookOptions ) { return ApolloReactHooks.useLazyQuery( FindAllCommentsDocument, baseOptions ); } export type FindAllCommentsQueryHookResult = ReturnType; export type FindAllCommentsLazyQueryHookResult = ReturnType; export type FindAllCommentsQueryResult = ApolloReactCommon.QueryResult< FindAllCommentsQuery, FindAllCommentsQueryVariables >; export const FindAllNotesDocument = gql` query findAllNotes($limit: Int, $offset: Int) { findAllNotes(limit: $limit, offset: $offset) { ...NoteExpandedFields } } ${NoteExpandedFieldsFragmentDoc} `; /** * __useFindAllNotesQuery__ * * To run a query within a React component, call `useFindAllNotesQuery` and pass it any options that fit your needs. * When your component renders, `useFindAllNotesQuery` returns an object from Apollo Client that contains loading, error, and data properties * you can use to render your UI. * * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; * * @example * const { data, loading, error } = useFindAllNotesQuery({ * variables: { * limit: // value for 'limit' * offset: // value for 'offset' * }, * }); */ export function useFindAllNotesQuery( baseOptions?: ApolloReactHooks.QueryHookOptions ) { return ApolloReactHooks.useQuery(FindAllNotesDocument, baseOptions); } export function useFindAllNotesLazyQuery( baseOptions?: ApolloReactHooks.LazyQueryHookOptions ) { return ApolloReactHooks.useLazyQuery( FindAllNotesDocument, baseOptions ); } export type FindAllNotesQueryHookResult = ReturnType; export type FindAllNotesLazyQueryHookResult = ReturnType; export type FindAllNotesQueryResult = ApolloReactCommon.QueryResult; export const FindCommentsDocument = gql` query findComments($fields: CommentInput!, $limit: Int, $offset: Int) { findComments(fields: $fields, limit: $limit, offset: $offset) { ...CommentExpandedFields } } ${CommentExpandedFieldsFragmentDoc} `; /** * __useFindCommentsQuery__ * * To run a query within a React component, call `useFindCommentsQuery` and pass it any options that fit your needs. * When your component renders, `useFindCommentsQuery` returns an object from Apollo Client that contains loading, error, and data properties * you can use to render your UI. * * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; * * @example * const { data, loading, error } = useFindCommentsQuery({ * variables: { * fields: // value for 'fields' * limit: // value for 'limit' * offset: // value for 'offset' * }, * }); */ export function useFindCommentsQuery( baseOptions?: ApolloReactHooks.QueryHookOptions ) { return ApolloReactHooks.useQuery(FindCommentsDocument, baseOptions); } export function useFindCommentsLazyQuery( baseOptions?: ApolloReactHooks.LazyQueryHookOptions ) { return ApolloReactHooks.useLazyQuery( FindCommentsDocument, baseOptions ); } export type FindCommentsQueryHookResult = ReturnType; export type FindCommentsLazyQueryHookResult = ReturnType; export type FindCommentsQueryResult = ApolloReactCommon.QueryResult; export const FindNotesDocument = gql` query findNotes($fields: NoteInput!, $limit: Int, $offset: Int) { findNotes(fields: $fields, limit: $limit, offset: $offset) { ...NoteExpandedFields } } ${NoteExpandedFieldsFragmentDoc} `; /** * __useFindNotesQuery__ * * To run a query within a React component, call `useFindNotesQuery` and pass it any options that fit your needs. * When your component renders, `useFindNotesQuery` returns an object from Apollo Client that contains loading, error, and data properties * you can use to render your UI. * * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; * * @example * const { data, loading, error } = useFindNotesQuery({ * variables: { * fields: // value for 'fields' * limit: // value for 'limit' * offset: // value for 'offset' * }, * }); */ export function useFindNotesQuery( baseOptions?: ApolloReactHooks.QueryHookOptions ) { return ApolloReactHooks.useQuery(FindNotesDocument, baseOptions); } export function useFindNotesLazyQuery( baseOptions?: ApolloReactHooks.LazyQueryHookOptions ) { return ApolloReactHooks.useLazyQuery(FindNotesDocument, baseOptions); } export type FindNotesQueryHookResult = ReturnType; export type FindNotesLazyQueryHookResult = ReturnType; export type FindNotesQueryResult = ApolloReactCommon.QueryResult; export const DeletedCommentDocument = gql` subscription deletedComment { deletedComment { ...CommentFields } } ${CommentFieldsFragmentDoc} `; /** * __useDeletedCommentSubscription__ * * To run a query within a React component, call `useDeletedCommentSubscription` and pass it any options that fit your needs. * When your component renders, `useDeletedCommentSubscription` returns an object from Apollo Client that contains loading, error, and data properties * you can use to render your UI. * * @param baseOptions options that will be passed into the subscription, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; * * @example * const { data, loading, error } = useDeletedCommentSubscription({ * variables: { * }, * }); */ export function useDeletedCommentSubscription( baseOptions?: ApolloReactHooks.SubscriptionHookOptions< DeletedCommentSubscription, DeletedCommentSubscriptionVariables > ) { return ApolloReactHooks.useSubscription( DeletedCommentDocument, baseOptions ); } export type DeletedCommentSubscriptionHookResult = ReturnType; export type DeletedCommentSubscriptionResult = ApolloReactCommon.SubscriptionResult; export const DeletedNoteDocument = gql` subscription deletedNote { deletedNote { ...NoteFields } } ${NoteFieldsFragmentDoc} `; /** * __useDeletedNoteSubscription__ * * To run a query within a React component, call `useDeletedNoteSubscription` and pass it any options that fit your needs. * When your component renders, `useDeletedNoteSubscription` returns an object from Apollo Client that contains loading, error, and data properties * you can use to render your UI. * * @param baseOptions options that will be passed into the subscription, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; * * @example * const { data, loading, error } = useDeletedNoteSubscription({ * variables: { * }, * }); */ export function useDeletedNoteSubscription( baseOptions?: ApolloReactHooks.SubscriptionHookOptions ) { return ApolloReactHooks.useSubscription( DeletedNoteDocument, baseOptions ); } export type DeletedNoteSubscriptionHookResult = ReturnType; export type DeletedNoteSubscriptionResult = ApolloReactCommon.SubscriptionResult; export const NewCommentDocument = gql` subscription newComment { newComment { ...CommentFields } } ${CommentFieldsFragmentDoc} `; /** * __useNewCommentSubscription__ * * To run a query within a React component, call `useNewCommentSubscription` and pass it any options that fit your needs. * When your component renders, `useNewCommentSubscription` returns an object from Apollo Client that contains loading, error, and data properties * you can use to render your UI. * * @param baseOptions options that will be passed into the subscription, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; * * @example * const { data, loading, error } = useNewCommentSubscription({ * variables: { * }, * }); */ export function useNewCommentSubscription( baseOptions?: ApolloReactHooks.SubscriptionHookOptions ) { return ApolloReactHooks.useSubscription( NewCommentDocument, baseOptions ); } export type NewCommentSubscriptionHookResult = ReturnType; export type NewCommentSubscriptionResult = ApolloReactCommon.SubscriptionResult; export const NewNoteDocument = gql` subscription newNote { newNote { ...NoteFields } } ${NoteFieldsFragmentDoc} `; /** * __useNewNoteSubscription__ * * To run a query within a React component, call `useNewNoteSubscription` and pass it any options that fit your needs. * When your component renders, `useNewNoteSubscription` returns an object from Apollo Client that contains loading, error, and data properties * you can use to render your UI. * * @param baseOptions options that will be passed into the subscription, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; * * @example * const { data, loading, error } = useNewNoteSubscription({ * variables: { * }, * }); */ export function useNewNoteSubscription( baseOptions?: ApolloReactHooks.SubscriptionHookOptions ) { return ApolloReactHooks.useSubscription( NewNoteDocument, baseOptions ); } export type NewNoteSubscriptionHookResult = ReturnType; export type NewNoteSubscriptionResult = ApolloReactCommon.SubscriptionResult; export const UpdatedCommentDocument = gql` subscription updatedComment { updatedComment { ...CommentFields } } ${CommentFieldsFragmentDoc} `; /** * __useUpdatedCommentSubscription__ * * To run a query within a React component, call `useUpdatedCommentSubscription` and pass it any options that fit your needs. * When your component renders, `useUpdatedCommentSubscription` returns an object from Apollo Client that contains loading, error, and data properties * you can use to render your UI. * * @param baseOptions options that will be passed into the subscription, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; * * @example * const { data, loading, error } = useUpdatedCommentSubscription({ * variables: { * }, * }); */ export function useUpdatedCommentSubscription( baseOptions?: ApolloReactHooks.SubscriptionHookOptions< UpdatedCommentSubscription, UpdatedCommentSubscriptionVariables > ) { return ApolloReactHooks.useSubscription( UpdatedCommentDocument, baseOptions ); } export type UpdatedCommentSubscriptionHookResult = ReturnType; export type UpdatedCommentSubscriptionResult = ApolloReactCommon.SubscriptionResult; export const UpdatedNoteDocument = gql` subscription updatedNote { updatedNote { ...NoteFields } } ${NoteFieldsFragmentDoc} `; /** * __useUpdatedNoteSubscription__ * * To run a query within a React component, call `useUpdatedNoteSubscription` and pass it any options that fit your needs. * When your component renders, `useUpdatedNoteSubscription` returns an object from Apollo Client that contains loading, error, and data properties * you can use to render your UI. * * @param baseOptions options that will be passed into the subscription, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; * * @example * const { data, loading, error } = useUpdatedNoteSubscription({ * variables: { * }, * }); */ export function useUpdatedNoteSubscription( baseOptions?: ApolloReactHooks.SubscriptionHookOptions ) { return ApolloReactHooks.useSubscription( UpdatedNoteDocument, baseOptions ); } export type UpdatedNoteSubscriptionHookResult = ReturnType; export type UpdatedNoteSubscriptionResult = ApolloReactCommon.SubscriptionResult; ================================================ FILE: templates/fullstack/client/package.json ================================================ { "name": "full-stack-template-client", "version": "4.1.0", "private": true, "devDependencies": { "@types/jest": "26.0.24", "@types/node": "13.13.45", "@types/react": "17.0.34", "@types/react-dom": "17.0.11", "react-scripts": "4.0.3", "typescript": "4.4.4" }, "dependencies": { "@apollo/react-common": "3.1.4", "@apollo/react-hooks": "3.1.5", "@material-ui/core": "4.12.3", "apollo-boost": "0.4.9", "graphql": "15.7.2", "react": "17.0.2", "react-dom": "17.0.2" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject" }, "eslintConfig": { "extends": "react-app" }, "browserslist": { "production": [ ">0.2%", "not dead", "not op_mini all" ], "development": [ "last 1 chrome version", "last 1 firefox version", "last 1 safari version" ] } } ================================================ FILE: templates/fullstack/client/public/index.html ================================================ React App
================================================ FILE: templates/fullstack/client/public/manifest.json ================================================ { "short_name": "React App", "name": "Create React App Sample", "icons": [ { "src": "favicon.ico", "sizes": "64x64 32x32 24x24 16x16", "type": "image/x-icon" }, { "src": "logo192.png", "type": "image/png", "sizes": "192x192" }, { "src": "logo512.png", "type": "image/png", "sizes": "512x512" } ], "start_url": ".", "display": "standalone", "theme_color": "#000000", "background_color": "#ffffff" } ================================================ FILE: templates/fullstack/client/public/robots.txt ================================================ # https://www.robotstxt.org/robotstxt.html User-agent: * ================================================ FILE: templates/fullstack/client/src/App.css ================================================ .App { text-align: center; } .App-logo { height: 40vmin; } .App-header { background-color: #282c34; min-height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center; font-size: calc(10px + 2vmin); color: white; } .App-link { color: #09d3ac; } ================================================ FILE: templates/fullstack/client/src/App.test.tsx ================================================ import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; it('renders without crashing', () => { const div = document.createElement('div'); ReactDOM.render(, div); ReactDOM.unmountComponentAtNode(div); }); ================================================ FILE: templates/fullstack/client/src/App.tsx ================================================ import React from 'react'; import './App.css'; import { useFindNotesQuery } from './generated-types'; import CreateNote from './components/notes/CreateNote'; import OneNote from './components/notes/OneNote'; const App: React.FC = () => { const allNotes = useFindNotesQuery(); allNotes.startPolling(2000); console.log(allNotes.data?.findNotes); return (
    { // TODO fix typings allNotes.data && allNotes.data.findNotes.items.map((note: any) => ( )) }
); }; export default App; ================================================ FILE: templates/fullstack/client/src/components/comment/Comment.css ================================================ .comment{ color:grey; } ================================================ FILE: templates/fullstack/client/src/components/comment/CreateComment.tsx ================================================ import React, { useState } from 'react'; import { useCreateCommentMutation } from '../../generated-types'; import { Card, TextField, Button } from '@material-ui/core'; import './../notes/Note.css'; type createCommentProps = { noteId: string; addCommentState: any; }; const CreateComment = ({ noteId, addCommentState }: createCommentProps) => { const [createComment] = useCreateCommentMutation(); const [newCommentTitle, setNewCommentTitle] = useState(''); const [newCommentDescription, setNewCommentDescription] = useState(''); return (

Create Comment

setNewCommentTitle(e.target.value)} value={newCommentTitle} /> setNewCommentDescription(e.target.value)} value={newCommentDescription} />
); }; export default CreateComment; ================================================ FILE: templates/fullstack/client/src/components/comment/OneComment.tsx ================================================ import React from 'react'; import { useDeleteCommentMutation, Comment } from '../../generated-types'; import { Button } from '@material-ui/core'; import './Comment.css'; const OneComment = ({ id, text, description }: Comment) => { const [deleteComment] = useDeleteCommentMutation(); return (
  • {text}:  {description}
  • ); }; export default OneComment; ================================================ FILE: templates/fullstack/client/src/components/notes/CreateNote.tsx ================================================ import React, { useState } from 'react'; import { useCreateNoteMutation } from '../../generated-types'; import { Button, TextField, Card } from '@material-ui/core'; import './Note.css'; const CreateNote: React.FC = () => { const [createNote] = useCreateNoteMutation(); const [newNoteTitle, setNewNoteTitle] = useState(''); const [newNoteDescription, setNewNoteDescription] = useState(''); return (

    Create Note

    This application works only with sample Node/Comment model

    setNewNoteTitle(e.target.value)} value={newNoteTitle} /> setNewNoteDescription(e.target.value)} value={newNoteDescription} />
    ); }; export default CreateNote; ================================================ FILE: templates/fullstack/client/src/components/notes/EditNote.tsx ================================================ import React, { useState } from 'react'; import { useUpdateNoteMutation } from '../../generated-types'; import { Button, TextField, Card } from '@material-ui/core'; import './Note.css'; type noteProps = { id: string; title: string; description: string | undefined; editState: any; }; const EditNote = ({ id, title, description, editState }: noteProps) => { const [updateNote] = useUpdateNoteMutation(); const [NoteTitle, setNoteTitle] = useState(title); const [NoteDescription, setNoteDescription] = useState(description); return (

    Edit Note

    setNoteTitle(e.target.value)} value={NoteTitle} /> setNoteDescription(e.target.value)} value={NoteDescription} />
    ); }; export default EditNote; ================================================ FILE: templates/fullstack/client/src/components/notes/Note.css ================================================ .inputForm{ display:grid; margin: 15%; width: 70%; } .inputCard{ width: 50%; margin-left: 25%; } .OneNote{ margin:5%; } ================================================ FILE: templates/fullstack/client/src/components/notes/OneNote.tsx ================================================ import React, { useState } from 'react'; import EditNote from './EditNote'; import { Note } from '../../generated-types'; import CreateComment from '../comment/CreateComment'; import OneComment from '../comment/OneComment'; import { Button, Card } from '@material-ui/core'; const OneNote = ({ id, title, description, comments }: Note) => { const [noteEdit, setNoteEdit] = useState(false); const [addComment, setAddComment] = useState(false); return (
  • {title}:  {description} {noteEdit ? ( ) : (
    )} {addComment ? :
    }
      {comments && comments.length > 0 ? ( comments.map((com) => { if (!com) { return; } return ; }) ) : (
      )}
  • ); }; export default OneNote; ================================================ FILE: templates/fullstack/client/src/generated-types.tsx ================================================ /* tslint:disable */ import gql from 'graphql-tag'; import * as ApolloReactCommon from '@apollo/react-common'; import * as ApolloReactHooks from '@apollo/react-hooks'; export type Maybe = T | null; export type Exact = { [K in keyof T]: T[K] }; /** All built-in and custom scalars, mapped to their actual values */ export type Scalars = { ID: string; String: string; Boolean: boolean; Int: number; Float: number; }; /** @model */ export type Comment = { __typename?: 'Comment'; id: Scalars['ID']; text?: Maybe; description?: Maybe; /** @manyToOne(field: 'comments', key: 'noteId') */ note?: Maybe; }; export type CommentFilter = { id?: Maybe; text?: Maybe; description?: Maybe; noteId?: Maybe; and?: Maybe>; or?: Maybe>; not?: Maybe; }; export type CommentResultList = { __typename?: 'CommentResultList'; items: Array>; offset?: Maybe; limit?: Maybe; count?: Maybe; }; export type CommentSubscriptionFilter = { id?: Maybe; text?: Maybe; description?: Maybe; }; export type CreateCommentInput = { id?: Maybe; text?: Maybe; description?: Maybe; noteId?: Maybe; }; export type CreateNoteInput = { id?: Maybe; title: Scalars['String']; description?: Maybe; }; export type IdInput = { ne?: Maybe; eq?: Maybe; le?: Maybe; lt?: Maybe; ge?: Maybe; gt?: Maybe; in?: Maybe>; }; export type MutateCommentInput = { id: Scalars['ID']; text?: Maybe; description?: Maybe; noteId?: Maybe; }; export type MutateNoteInput = { id: Scalars['ID']; title?: Maybe; description?: Maybe; }; export type Mutation = { __typename?: 'Mutation'; createNote?: Maybe; updateNote?: Maybe; deleteNote?: Maybe; createComment?: Maybe; updateComment?: Maybe; deleteComment?: Maybe; }; export type MutationCreateNoteArgs = { input: CreateNoteInput; }; export type MutationUpdateNoteArgs = { input: MutateNoteInput; }; export type MutationDeleteNoteArgs = { input: MutateNoteInput; }; export type MutationCreateCommentArgs = { input: CreateCommentInput; }; export type MutationUpdateCommentArgs = { input: MutateCommentInput; }; export type MutationDeleteCommentArgs = { input: MutateCommentInput; }; /** @model */ export type Note = { __typename?: 'Note'; id: Scalars['ID']; title: Scalars['String']; description?: Maybe; /** @oneToMany(field: 'note', key: 'noteId') */ comments: Array>; }; /** @model */ export type NoteCommentsArgs = { filter?: Maybe; }; export type NoteFilter = { id?: Maybe; title?: Maybe; description?: Maybe; and?: Maybe>; or?: Maybe>; not?: Maybe; }; export type NoteResultList = { __typename?: 'NoteResultList'; items: Array>; offset?: Maybe; limit?: Maybe; count?: Maybe; }; export type NoteSubscriptionFilter = { id?: Maybe; title?: Maybe; description?: Maybe; }; export type OrderByInput = { field: Scalars['String']; order?: Maybe; }; export type PageRequest = { limit?: Maybe; offset?: Maybe; }; export type Query = { __typename?: 'Query'; getNote?: Maybe; findNotes: NoteResultList; getComment?: Maybe; findComments: CommentResultList; }; export type QueryGetNoteArgs = { id: Scalars['ID']; }; export type QueryFindNotesArgs = { filter?: Maybe; page?: Maybe; orderBy?: Maybe; }; export type QueryGetCommentArgs = { id: Scalars['ID']; }; export type QueryFindCommentsArgs = { filter?: Maybe; page?: Maybe; orderBy?: Maybe; }; export enum SortDirectionEnum { Desc = 'DESC', Asc = 'ASC' } export type StringInput = { ne?: Maybe; eq?: Maybe; le?: Maybe; lt?: Maybe; ge?: Maybe; gt?: Maybe; in?: Maybe>; contains?: Maybe; startsWith?: Maybe; endsWith?: Maybe; }; export type Subscription = { __typename?: 'Subscription'; newNote: Note; updatedNote: Note; deletedNote: Note; newComment: Comment; updatedComment: Comment; deletedComment: Comment; }; export type SubscriptionNewNoteArgs = { filter?: Maybe; }; export type SubscriptionUpdatedNoteArgs = { filter?: Maybe; }; export type SubscriptionDeletedNoteArgs = { filter?: Maybe; }; export type SubscriptionNewCommentArgs = { filter?: Maybe; }; export type SubscriptionUpdatedCommentArgs = { filter?: Maybe; }; export type SubscriptionDeletedCommentArgs = { filter?: Maybe; }; export type NoteFieldsFragment = ( { __typename?: 'Note' } & Pick ); export type NoteExpandedFieldsFragment = ( { __typename?: 'Note' } & Pick & { comments: Array )>> } ); export type CommentFieldsFragment = ( { __typename?: 'Comment' } & Pick ); export type CommentExpandedFieldsFragment = ( { __typename?: 'Comment' } & Pick & { note?: Maybe<( { __typename?: 'Note' } & Pick )> } ); export type FindNotesQueryVariables = Exact<{ filter?: Maybe; page?: Maybe; orderBy?: Maybe; }>; export type FindNotesQuery = ( { __typename?: 'Query' } & { findNotes: ( { __typename?: 'NoteResultList' } & Pick & { items: Array> } ) } ); export type GetNoteQueryVariables = Exact<{ id: Scalars['ID']; }>; export type GetNoteQuery = ( { __typename?: 'Query' } & { getNote?: Maybe<( { __typename?: 'Note' } & NoteExpandedFieldsFragment )> } ); export type FindCommentsQueryVariables = Exact<{ filter?: Maybe; page?: Maybe; orderBy?: Maybe; }>; export type FindCommentsQuery = ( { __typename?: 'Query' } & { findComments: ( { __typename?: 'CommentResultList' } & Pick & { items: Array> } ) } ); export type GetCommentQueryVariables = Exact<{ id: Scalars['ID']; }>; export type GetCommentQuery = ( { __typename?: 'Query' } & { getComment?: Maybe<( { __typename?: 'Comment' } & CommentExpandedFieldsFragment )> } ); export type CreateNoteMutationVariables = Exact<{ input: CreateNoteInput; }>; export type CreateNoteMutation = ( { __typename?: 'Mutation' } & { createNote?: Maybe<( { __typename?: 'Note' } & NoteFieldsFragment )> } ); export type UpdateNoteMutationVariables = Exact<{ input: MutateNoteInput; }>; export type UpdateNoteMutation = ( { __typename?: 'Mutation' } & { updateNote?: Maybe<( { __typename?: 'Note' } & NoteFieldsFragment )> } ); export type DeleteNoteMutationVariables = Exact<{ input: MutateNoteInput; }>; export type DeleteNoteMutation = ( { __typename?: 'Mutation' } & { deleteNote?: Maybe<( { __typename?: 'Note' } & NoteFieldsFragment )> } ); export type CreateCommentMutationVariables = Exact<{ input: CreateCommentInput; }>; export type CreateCommentMutation = ( { __typename?: 'Mutation' } & { createComment?: Maybe<( { __typename?: 'Comment' } & CommentFieldsFragment )> } ); export type UpdateCommentMutationVariables = Exact<{ input: MutateCommentInput; }>; export type UpdateCommentMutation = ( { __typename?: 'Mutation' } & { updateComment?: Maybe<( { __typename?: 'Comment' } & CommentFieldsFragment )> } ); export type DeleteCommentMutationVariables = Exact<{ input: MutateCommentInput; }>; export type DeleteCommentMutation = ( { __typename?: 'Mutation' } & { deleteComment?: Maybe<( { __typename?: 'Comment' } & CommentFieldsFragment )> } ); export type NewNoteSubscriptionVariables = Exact<{ filter?: Maybe; }>; export type NewNoteSubscription = ( { __typename?: 'Subscription' } & { newNote: ( { __typename?: 'Note' } & NoteFieldsFragment ) } ); export type UpdatedNoteSubscriptionVariables = Exact<{ filter?: Maybe; }>; export type UpdatedNoteSubscription = ( { __typename?: 'Subscription' } & { updatedNote: ( { __typename?: 'Note' } & NoteFieldsFragment ) } ); export type DeletedNoteSubscriptionVariables = Exact<{ filter?: Maybe; }>; export type DeletedNoteSubscription = ( { __typename?: 'Subscription' } & { deletedNote: ( { __typename?: 'Note' } & NoteFieldsFragment ) } ); export type NewCommentSubscriptionVariables = Exact<{ filter?: Maybe; }>; export type NewCommentSubscription = ( { __typename?: 'Subscription' } & { newComment: ( { __typename?: 'Comment' } & CommentFieldsFragment ) } ); export type UpdatedCommentSubscriptionVariables = Exact<{ filter?: Maybe; }>; export type UpdatedCommentSubscription = ( { __typename?: 'Subscription' } & { updatedComment: ( { __typename?: 'Comment' } & CommentFieldsFragment ) } ); export type DeletedCommentSubscriptionVariables = Exact<{ filter?: Maybe; }>; export type DeletedCommentSubscription = ( { __typename?: 'Subscription' } & { deletedComment: ( { __typename?: 'Comment' } & CommentFieldsFragment ) } ); export const NoteFieldsFragmentDoc = gql` fragment NoteFields on Note { id title description } `; export const NoteExpandedFieldsFragmentDoc = gql` fragment NoteExpandedFields on Note { id title description comments { id text description } } `; export const CommentFieldsFragmentDoc = gql` fragment CommentFields on Comment { id text description } `; export const CommentExpandedFieldsFragmentDoc = gql` fragment CommentExpandedFields on Comment { id text description note { id title description } } `; export const FindNotesDocument = gql` query findNotes($filter: NoteFilter, $page: PageRequest, $orderBy: OrderByInput) { findNotes(filter: $filter, page: $page, orderBy: $orderBy) { items { ...NoteExpandedFields } offset limit count } } ${NoteExpandedFieldsFragmentDoc}`; /** * __useFindNotesQuery__ * * To run a query within a React component, call `useFindNotesQuery` and pass it any options that fit your needs. * When your component renders, `useFindNotesQuery` returns an object from Apollo Client that contains loading, error, and data properties * you can use to render your UI. * * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; * * @example * const { data, loading, error } = useFindNotesQuery({ * variables: { * filter: // value for 'filter' * page: // value for 'page' * orderBy: // value for 'orderBy' * }, * }); */ export function useFindNotesQuery(baseOptions?: ApolloReactHooks.QueryHookOptions) { return ApolloReactHooks.useQuery(FindNotesDocument, baseOptions); } export function useFindNotesLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions) { return ApolloReactHooks.useLazyQuery(FindNotesDocument, baseOptions); } export type FindNotesQueryHookResult = ReturnType; export type FindNotesLazyQueryHookResult = ReturnType; export type FindNotesQueryResult = ApolloReactCommon.QueryResult; export const GetNoteDocument = gql` query getNote($id: ID!) { getNote(id: $id) { ...NoteExpandedFields } } ${NoteExpandedFieldsFragmentDoc}`; /** * __useGetNoteQuery__ * * To run a query within a React component, call `useGetNoteQuery` and pass it any options that fit your needs. * When your component renders, `useGetNoteQuery` returns an object from Apollo Client that contains loading, error, and data properties * you can use to render your UI. * * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; * * @example * const { data, loading, error } = useGetNoteQuery({ * variables: { * id: // value for 'id' * }, * }); */ export function useGetNoteQuery(baseOptions?: ApolloReactHooks.QueryHookOptions) { return ApolloReactHooks.useQuery(GetNoteDocument, baseOptions); } export function useGetNoteLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions) { return ApolloReactHooks.useLazyQuery(GetNoteDocument, baseOptions); } export type GetNoteQueryHookResult = ReturnType; export type GetNoteLazyQueryHookResult = ReturnType; export type GetNoteQueryResult = ApolloReactCommon.QueryResult; export const FindCommentsDocument = gql` query findComments($filter: CommentFilter, $page: PageRequest, $orderBy: OrderByInput) { findComments(filter: $filter, page: $page, orderBy: $orderBy) { items { ...CommentExpandedFields } offset limit count } } ${CommentExpandedFieldsFragmentDoc}`; /** * __useFindCommentsQuery__ * * To run a query within a React component, call `useFindCommentsQuery` and pass it any options that fit your needs. * When your component renders, `useFindCommentsQuery` returns an object from Apollo Client that contains loading, error, and data properties * you can use to render your UI. * * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; * * @example * const { data, loading, error } = useFindCommentsQuery({ * variables: { * filter: // value for 'filter' * page: // value for 'page' * orderBy: // value for 'orderBy' * }, * }); */ export function useFindCommentsQuery(baseOptions?: ApolloReactHooks.QueryHookOptions) { return ApolloReactHooks.useQuery(FindCommentsDocument, baseOptions); } export function useFindCommentsLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions) { return ApolloReactHooks.useLazyQuery(FindCommentsDocument, baseOptions); } export type FindCommentsQueryHookResult = ReturnType; export type FindCommentsLazyQueryHookResult = ReturnType; export type FindCommentsQueryResult = ApolloReactCommon.QueryResult; export const GetCommentDocument = gql` query getComment($id: ID!) { getComment(id: $id) { ...CommentExpandedFields } } ${CommentExpandedFieldsFragmentDoc}`; /** * __useGetCommentQuery__ * * To run a query within a React component, call `useGetCommentQuery` and pass it any options that fit your needs. * When your component renders, `useGetCommentQuery` returns an object from Apollo Client that contains loading, error, and data properties * you can use to render your UI. * * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; * * @example * const { data, loading, error } = useGetCommentQuery({ * variables: { * id: // value for 'id' * }, * }); */ export function useGetCommentQuery(baseOptions?: ApolloReactHooks.QueryHookOptions) { return ApolloReactHooks.useQuery(GetCommentDocument, baseOptions); } export function useGetCommentLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions) { return ApolloReactHooks.useLazyQuery(GetCommentDocument, baseOptions); } export type GetCommentQueryHookResult = ReturnType; export type GetCommentLazyQueryHookResult = ReturnType; export type GetCommentQueryResult = ApolloReactCommon.QueryResult; export const CreateNoteDocument = gql` mutation createNote($input: CreateNoteInput!) { createNote(input: $input) { ...NoteFields } } ${NoteFieldsFragmentDoc}`; export type CreateNoteMutationFn = ApolloReactCommon.MutationFunction; /** * __useCreateNoteMutation__ * * To run a mutation, you first call `useCreateNoteMutation` within a React component and pass it any options that fit your needs. * When your component renders, `useCreateNoteMutation` returns a tuple that includes: * - A mutate function that you can call at any time to execute the mutation * - An object with fields that represent the current status of the mutation's execution * * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; * * @example * const [createNoteMutation, { data, loading, error }] = useCreateNoteMutation({ * variables: { * input: // value for 'input' * }, * }); */ export function useCreateNoteMutation(baseOptions?: ApolloReactHooks.MutationHookOptions) { return ApolloReactHooks.useMutation(CreateNoteDocument, baseOptions); } export type CreateNoteMutationHookResult = ReturnType; export type CreateNoteMutationResult = ApolloReactCommon.MutationResult; export type CreateNoteMutationOptions = ApolloReactCommon.BaseMutationOptions; export const UpdateNoteDocument = gql` mutation updateNote($input: MutateNoteInput!) { updateNote(input: $input) { ...NoteFields } } ${NoteFieldsFragmentDoc}`; export type UpdateNoteMutationFn = ApolloReactCommon.MutationFunction; /** * __useUpdateNoteMutation__ * * To run a mutation, you first call `useUpdateNoteMutation` within a React component and pass it any options that fit your needs. * When your component renders, `useUpdateNoteMutation` returns a tuple that includes: * - A mutate function that you can call at any time to execute the mutation * - An object with fields that represent the current status of the mutation's execution * * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; * * @example * const [updateNoteMutation, { data, loading, error }] = useUpdateNoteMutation({ * variables: { * input: // value for 'input' * }, * }); */ export function useUpdateNoteMutation(baseOptions?: ApolloReactHooks.MutationHookOptions) { return ApolloReactHooks.useMutation(UpdateNoteDocument, baseOptions); } export type UpdateNoteMutationHookResult = ReturnType; export type UpdateNoteMutationResult = ApolloReactCommon.MutationResult; export type UpdateNoteMutationOptions = ApolloReactCommon.BaseMutationOptions; export const DeleteNoteDocument = gql` mutation deleteNote($input: MutateNoteInput!) { deleteNote(input: $input) { ...NoteFields } } ${NoteFieldsFragmentDoc}`; export type DeleteNoteMutationFn = ApolloReactCommon.MutationFunction; /** * __useDeleteNoteMutation__ * * To run a mutation, you first call `useDeleteNoteMutation` within a React component and pass it any options that fit your needs. * When your component renders, `useDeleteNoteMutation` returns a tuple that includes: * - A mutate function that you can call at any time to execute the mutation * - An object with fields that represent the current status of the mutation's execution * * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; * * @example * const [deleteNoteMutation, { data, loading, error }] = useDeleteNoteMutation({ * variables: { * input: // value for 'input' * }, * }); */ export function useDeleteNoteMutation(baseOptions?: ApolloReactHooks.MutationHookOptions) { return ApolloReactHooks.useMutation(DeleteNoteDocument, baseOptions); } export type DeleteNoteMutationHookResult = ReturnType; export type DeleteNoteMutationResult = ApolloReactCommon.MutationResult; export type DeleteNoteMutationOptions = ApolloReactCommon.BaseMutationOptions; export const CreateCommentDocument = gql` mutation createComment($input: CreateCommentInput!) { createComment(input: $input) { ...CommentFields } } ${CommentFieldsFragmentDoc}`; export type CreateCommentMutationFn = ApolloReactCommon.MutationFunction; /** * __useCreateCommentMutation__ * * To run a mutation, you first call `useCreateCommentMutation` within a React component and pass it any options that fit your needs. * When your component renders, `useCreateCommentMutation` returns a tuple that includes: * - A mutate function that you can call at any time to execute the mutation * - An object with fields that represent the current status of the mutation's execution * * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; * * @example * const [createCommentMutation, { data, loading, error }] = useCreateCommentMutation({ * variables: { * input: // value for 'input' * }, * }); */ export function useCreateCommentMutation(baseOptions?: ApolloReactHooks.MutationHookOptions) { return ApolloReactHooks.useMutation(CreateCommentDocument, baseOptions); } export type CreateCommentMutationHookResult = ReturnType; export type CreateCommentMutationResult = ApolloReactCommon.MutationResult; export type CreateCommentMutationOptions = ApolloReactCommon.BaseMutationOptions; export const UpdateCommentDocument = gql` mutation updateComment($input: MutateCommentInput!) { updateComment(input: $input) { ...CommentFields } } ${CommentFieldsFragmentDoc}`; export type UpdateCommentMutationFn = ApolloReactCommon.MutationFunction; /** * __useUpdateCommentMutation__ * * To run a mutation, you first call `useUpdateCommentMutation` within a React component and pass it any options that fit your needs. * When your component renders, `useUpdateCommentMutation` returns a tuple that includes: * - A mutate function that you can call at any time to execute the mutation * - An object with fields that represent the current status of the mutation's execution * * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; * * @example * const [updateCommentMutation, { data, loading, error }] = useUpdateCommentMutation({ * variables: { * input: // value for 'input' * }, * }); */ export function useUpdateCommentMutation(baseOptions?: ApolloReactHooks.MutationHookOptions) { return ApolloReactHooks.useMutation(UpdateCommentDocument, baseOptions); } export type UpdateCommentMutationHookResult = ReturnType; export type UpdateCommentMutationResult = ApolloReactCommon.MutationResult; export type UpdateCommentMutationOptions = ApolloReactCommon.BaseMutationOptions; export const DeleteCommentDocument = gql` mutation deleteComment($input: MutateCommentInput!) { deleteComment(input: $input) { ...CommentFields } } ${CommentFieldsFragmentDoc}`; export type DeleteCommentMutationFn = ApolloReactCommon.MutationFunction; /** * __useDeleteCommentMutation__ * * To run a mutation, you first call `useDeleteCommentMutation` within a React component and pass it any options that fit your needs. * When your component renders, `useDeleteCommentMutation` returns a tuple that includes: * - A mutate function that you can call at any time to execute the mutation * - An object with fields that represent the current status of the mutation's execution * * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; * * @example * const [deleteCommentMutation, { data, loading, error }] = useDeleteCommentMutation({ * variables: { * input: // value for 'input' * }, * }); */ export function useDeleteCommentMutation(baseOptions?: ApolloReactHooks.MutationHookOptions) { return ApolloReactHooks.useMutation(DeleteCommentDocument, baseOptions); } export type DeleteCommentMutationHookResult = ReturnType; export type DeleteCommentMutationResult = ApolloReactCommon.MutationResult; export type DeleteCommentMutationOptions = ApolloReactCommon.BaseMutationOptions; export const NewNoteDocument = gql` subscription newNote($filter: NoteSubscriptionFilter) { newNote(filter: $filter) { ...NoteFields } } ${NoteFieldsFragmentDoc}`; /** * __useNewNoteSubscription__ * * To run a query within a React component, call `useNewNoteSubscription` and pass it any options that fit your needs. * When your component renders, `useNewNoteSubscription` returns an object from Apollo Client that contains loading, error, and data properties * you can use to render your UI. * * @param baseOptions options that will be passed into the subscription, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; * * @example * const { data, loading, error } = useNewNoteSubscription({ * variables: { * filter: // value for 'filter' * }, * }); */ export function useNewNoteSubscription(baseOptions?: ApolloReactHooks.SubscriptionHookOptions) { return ApolloReactHooks.useSubscription(NewNoteDocument, baseOptions); } export type NewNoteSubscriptionHookResult = ReturnType; export type NewNoteSubscriptionResult = ApolloReactCommon.SubscriptionResult; export const UpdatedNoteDocument = gql` subscription updatedNote($filter: NoteSubscriptionFilter) { updatedNote(filter: $filter) { ...NoteFields } } ${NoteFieldsFragmentDoc}`; /** * __useUpdatedNoteSubscription__ * * To run a query within a React component, call `useUpdatedNoteSubscription` and pass it any options that fit your needs. * When your component renders, `useUpdatedNoteSubscription` returns an object from Apollo Client that contains loading, error, and data properties * you can use to render your UI. * * @param baseOptions options that will be passed into the subscription, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; * * @example * const { data, loading, error } = useUpdatedNoteSubscription({ * variables: { * filter: // value for 'filter' * }, * }); */ export function useUpdatedNoteSubscription(baseOptions?: ApolloReactHooks.SubscriptionHookOptions) { return ApolloReactHooks.useSubscription(UpdatedNoteDocument, baseOptions); } export type UpdatedNoteSubscriptionHookResult = ReturnType; export type UpdatedNoteSubscriptionResult = ApolloReactCommon.SubscriptionResult; export const DeletedNoteDocument = gql` subscription deletedNote($filter: NoteSubscriptionFilter) { deletedNote(filter: $filter) { ...NoteFields } } ${NoteFieldsFragmentDoc}`; /** * __useDeletedNoteSubscription__ * * To run a query within a React component, call `useDeletedNoteSubscription` and pass it any options that fit your needs. * When your component renders, `useDeletedNoteSubscription` returns an object from Apollo Client that contains loading, error, and data properties * you can use to render your UI. * * @param baseOptions options that will be passed into the subscription, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; * * @example * const { data, loading, error } = useDeletedNoteSubscription({ * variables: { * filter: // value for 'filter' * }, * }); */ export function useDeletedNoteSubscription(baseOptions?: ApolloReactHooks.SubscriptionHookOptions) { return ApolloReactHooks.useSubscription(DeletedNoteDocument, baseOptions); } export type DeletedNoteSubscriptionHookResult = ReturnType; export type DeletedNoteSubscriptionResult = ApolloReactCommon.SubscriptionResult; export const NewCommentDocument = gql` subscription newComment($filter: CommentSubscriptionFilter) { newComment(filter: $filter) { ...CommentFields } } ${CommentFieldsFragmentDoc}`; /** * __useNewCommentSubscription__ * * To run a query within a React component, call `useNewCommentSubscription` and pass it any options that fit your needs. * When your component renders, `useNewCommentSubscription` returns an object from Apollo Client that contains loading, error, and data properties * you can use to render your UI. * * @param baseOptions options that will be passed into the subscription, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; * * @example * const { data, loading, error } = useNewCommentSubscription({ * variables: { * filter: // value for 'filter' * }, * }); */ export function useNewCommentSubscription(baseOptions?: ApolloReactHooks.SubscriptionHookOptions) { return ApolloReactHooks.useSubscription(NewCommentDocument, baseOptions); } export type NewCommentSubscriptionHookResult = ReturnType; export type NewCommentSubscriptionResult = ApolloReactCommon.SubscriptionResult; export const UpdatedCommentDocument = gql` subscription updatedComment($filter: CommentSubscriptionFilter) { updatedComment(filter: $filter) { ...CommentFields } } ${CommentFieldsFragmentDoc}`; /** * __useUpdatedCommentSubscription__ * * To run a query within a React component, call `useUpdatedCommentSubscription` and pass it any options that fit your needs. * When your component renders, `useUpdatedCommentSubscription` returns an object from Apollo Client that contains loading, error, and data properties * you can use to render your UI. * * @param baseOptions options that will be passed into the subscription, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; * * @example * const { data, loading, error } = useUpdatedCommentSubscription({ * variables: { * filter: // value for 'filter' * }, * }); */ export function useUpdatedCommentSubscription(baseOptions?: ApolloReactHooks.SubscriptionHookOptions) { return ApolloReactHooks.useSubscription(UpdatedCommentDocument, baseOptions); } export type UpdatedCommentSubscriptionHookResult = ReturnType; export type UpdatedCommentSubscriptionResult = ApolloReactCommon.SubscriptionResult; export const DeletedCommentDocument = gql` subscription deletedComment($filter: CommentSubscriptionFilter) { deletedComment(filter: $filter) { ...CommentFields } } ${CommentFieldsFragmentDoc}`; /** * __useDeletedCommentSubscription__ * * To run a query within a React component, call `useDeletedCommentSubscription` and pass it any options that fit your needs. * When your component renders, `useDeletedCommentSubscription` returns an object from Apollo Client that contains loading, error, and data properties * you can use to render your UI. * * @param baseOptions options that will be passed into the subscription, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; * * @example * const { data, loading, error } = useDeletedCommentSubscription({ * variables: { * filter: // value for 'filter' * }, * }); */ export function useDeletedCommentSubscription(baseOptions?: ApolloReactHooks.SubscriptionHookOptions) { return ApolloReactHooks.useSubscription(DeletedCommentDocument, baseOptions); } export type DeletedCommentSubscriptionHookResult = ReturnType; export type DeletedCommentSubscriptionResult = ApolloReactCommon.SubscriptionResult; ================================================ FILE: templates/fullstack/client/src/graphql/graphback.graphql ================================================ fragment NoteFields on Note { id title description } fragment NoteExpandedFields on Note { id title description comments { id text description } } fragment CommentFields on Comment { id text description } fragment CommentExpandedFields on Comment { id text description note { id title description } } query findNotes($filter: NoteFilter, $page: PageRequest, $orderBy: OrderByInput) { findNotes(filter: $filter, page: $page, orderBy: $orderBy) { items { ...NoteExpandedFields } offset limit count } } query getNote($id: ID!) { getNote(id: $id) { ...NoteExpandedFields } } query findComments($filter: CommentFilter, $page: PageRequest, $orderBy: OrderByInput) { findComments(filter: $filter, page: $page, orderBy: $orderBy) { items { ...CommentExpandedFields } offset limit count } } query getComment($id: ID!) { getComment(id: $id) { ...CommentExpandedFields } } mutation createNote($input: CreateNoteInput!) { createNote(input: $input) { ...NoteFields } } mutation updateNote($input: MutateNoteInput!) { updateNote(input: $input) { ...NoteFields } } mutation deleteNote($input: MutateNoteInput!) { deleteNote(input: $input) { ...NoteFields } } mutation createComment($input: CreateCommentInput!) { createComment(input: $input) { ...CommentFields } } mutation updateComment($input: MutateCommentInput!) { updateComment(input: $input) { ...CommentFields } } mutation deleteComment($input: MutateCommentInput!) { deleteComment(input: $input) { ...CommentFields } } subscription newNote($filter: NoteSubscriptionFilter) { newNote(filter: $filter) { ...NoteFields } } subscription updatedNote($filter: NoteSubscriptionFilter) { updatedNote(filter: $filter) { ...NoteFields } } subscription deletedNote($filter: NoteSubscriptionFilter) { deletedNote(filter: $filter) { ...NoteFields } } subscription newComment($filter: CommentSubscriptionFilter) { newComment(filter: $filter) { ...CommentFields } } subscription updatedComment($filter: CommentSubscriptionFilter) { updatedComment(filter: $filter) { ...CommentFields } } subscription deletedComment($filter: CommentSubscriptionFilter) { deletedComment(filter: $filter) { ...CommentFields } } ================================================ FILE: templates/fullstack/client/src/index.css ================================================ body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } code { font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace; } ================================================ FILE: templates/fullstack/client/src/index.tsx ================================================ import { ApolloProvider } from '@apollo/react-hooks'; import ApolloClient from 'apollo-boost'; import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; import './index.css'; import * as serviceWorker from './serviceWorker'; const apolloClient = new ApolloClient({ uri: 'http://localhost:4000/graphql', }); ReactDOM.render( , document.getElementById('root') ); // If you want your app to work offline and load faster, you can change // unregister() to register() below. Note this comes with some pitfalls. // Learn more about service workers: https://bit.ly/CRA-PWA serviceWorker.unregister(); ================================================ FILE: templates/fullstack/client/src/react-app-env.d.ts ================================================ /// ================================================ FILE: templates/fullstack/client/src/serviceWorker.ts ================================================ // This optional code is used to register a service worker. // register() is not called by default. // This lets the app load faster on subsequent visits in production, and gives // it offline capabilities. However, it also means that developers (and users) // will only see deployed updates on subsequent visits to a page, after all the // existing tabs open on the page have been closed, since previously cached // resources are updated in the background. // To learn more about the benefits of this model and instructions on how to // opt-in, read https://bit.ly/CRA-PWA const isLocalhost = Boolean( window.location.hostname === 'localhost' || // [::1] is the IPv6 localhost address. window.location.hostname === '[::1]' || // 127.0.0.1/8 is considered localhost for IPv4. window.location.hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/) ); type Config = { onSuccess?: (registration: ServiceWorkerRegistration) => void; onUpdate?: (registration: ServiceWorkerRegistration) => void; }; export function register(config?: Config) { if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { // The URL constructor is available in all browsers that support SW. const publicUrl = new URL((process as { env: { [key: string]: string } }).env.PUBLIC_URL, window.location.href); if (publicUrl.origin !== window.location.origin) { // Our service worker won't work if PUBLIC_URL is on a different origin // from what our page is served on. This might happen if a CDN is used to // serve assets; see https://github.com/facebook/create-react-app/issues/2374 return; } window.addEventListener('load', () => { const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; if (isLocalhost) { // This is running on localhost. Let's check if a service worker still exists or not. checkValidServiceWorker(swUrl, config); // Add some additional logging to localhost, pointing developers to the // service worker/PWA documentation. navigator.serviceWorker.ready.then(() => { console.log( 'This web app is being served cache-first by a service ' + 'worker. To learn more, visit https://bit.ly/CRA-PWA' ); }); } else { // Is not localhost. Just register service worker registerValidSW(swUrl, config); } }); } } function registerValidSW(swUrl: string, config?: Config) { navigator.serviceWorker .register(swUrl) .then((registration) => { registration.onupdatefound = () => { const installingWorker = registration.installing; if (installingWorker == null) { return; } installingWorker.onstatechange = () => { if (installingWorker.state === 'installed') { if (navigator.serviceWorker.controller) { // At this point, the updated precached content has been fetched, // but the previous service worker will still serve the older // content until all client tabs are closed. console.log( 'New content is available and will be used when all ' + 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' ); // Execute callback if (config && config.onUpdate) { config.onUpdate(registration); } } else { // At this point, everything has been precached. // It's the perfect time to display a // "Content is cached for offline use." message. console.log('Content is cached for offline use.'); // Execute callback if (config && config.onSuccess) { config.onSuccess(registration); } } } }; }; }) .catch((error) => { console.error('Error during service worker registration:', error); }); } function checkValidServiceWorker(swUrl: string, config?: Config) { // Check if the service worker can be found. If it can't reload the page. fetch(swUrl) .then((response) => { // Ensure service worker exists, and that we really are getting a JS file. const contentType = response.headers.get('content-type'); if (response.status === 404 || (contentType != null && contentType.indexOf('javascript') === -1)) { // No service worker found. Probably a different app. Reload the page. navigator.serviceWorker.ready.then((registration) => { registration.unregister().then(() => { window.location.reload(); }); }); } else { // Service worker found. Proceed as normal. registerValidSW(swUrl, config); } }) .catch(() => { console.log('No internet connection found. App is running in offline mode.'); }); } export function unregister() { if ('serviceWorker' in navigator) { navigator.serviceWorker.ready.then((registration) => { registration.unregister(); }); } } ================================================ FILE: templates/fullstack/client/tsconfig.json ================================================ { "compilerOptions": { "target": "es5", "lib": [ "dom", "dom.iterable", "esnext" ], "allowJs": true, "skipLibCheck": true, "esModuleInterop": true, "allowSyntheticDefaultImports": true, "strict": true, "forceConsistentCasingInFileNames": true, "module": "esnext", "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, "jsx": "react" }, "include": [ "src" ] } ================================================ FILE: templates/fullstack/docker-compose.yml ================================================ version: '3' services: postgres: image: postgres:9.6 ports: - "55432:5432" environment: POSTGRES_PASSWORD: postgres POSTGRES_USER: postgresql POSTGRES_DB: users ================================================ FILE: templates/fullstack/model/datamodel.graphql ================================================ """ @model """ type Note { id: ID! title: String! description: String """ @oneToMany(field: 'note') """ comments: [Comment]! } """ @model """ type Comment { id: ID! text: String description: String } ================================================ FILE: templates/fullstack/package.json ================================================ { "name": "full-stack-template", "version": "4.1.0", "private": true, "workspaces": [ "client", "server" ], "scripts": { "start:server": "cd server/ && yarn start", "start:client": "cd client/ && yarn start" }, "license": "MIT", "devDependencies": { "@graphql-codegen/add": "2.0.2", "@graphql-codegen/typescript": "1.23.0", "@graphql-codegen/typescript-operations": "1.18.4", "@graphql-codegen/typescript-react-apollo": "2.3.1", "@graphql-codegen/typescript-resolvers": "1.20.0", "@graphql-cli/codegen": "1.17.27", "@graphql-cli/coverage": "2.1.0", "@graphql-cli/diff": "2.1.0", "@graphql-cli/generate": "4.1.0", "@graphql-cli/serve": "4.1.0", "@graphql-cli/similar": "2.1.0", "@graphql-cli/validate": "2.1.0", "graphql": "15.7.2", "graphql-cli": "4.1.0", "schemats": "3.0.3", "tslint": "6.1.3", "typescript": "4.4.4" }, "resolutions": { "graphql": "15.7.2" }, "author": { "name": "Arda TANRIKULU" } } ================================================ FILE: templates/fullstack/renovate.json ================================================ { "extends": [ "config:base" ], "automerge": true, "major": { "automerge": false } } ================================================ FILE: templates/fullstack/schemats.json ================================================ { "conn": "postgres://postgresql:postgres@localhost:55432/users", "output": "server/src/generated-db-types.ts" } ================================================ FILE: templates/fullstack/server/package.json ================================================ { "private": true, "name": "full-stack-template-server", "version": "4.1.0", "scripts": { "develop": "ts-node-dev src/index.ts", "start": "ts-node src/index.ts" }, "dependencies": { "graphback": "1.1.2", "@graphback/runtime-knex": "1.1.2", "@graphback/codegen-schema": "1.1.2", "graphql-migrations": "1.1.2", "@graphql-tools/load-files": "6.5.2", "@types/cors": "2.8.12", "@types/express": "4.17.13", "@types/node": "13.13.45", "apollo-server-express": "2.25.3", "cors": "2.8.5", "express": "4.17.1", "graphql": "15.7.2", "graphql-config": "3.4.1", "graphql-tag": "2.12.6", "knex": "0.95.14", "pg": "8.7.1", "ts-node": "9.1.1", "ts-node-dev": "1.1.8", "typescript": "4.4.4" } } ================================================ FILE: templates/fullstack/server/src/db.ts ================================================ import { loadConfig } from 'graphql-config'; import Knex from 'knex'; export const getConfig = async () => { const config = await loadConfig({ extensions: [() => ({ name: 'dbmigrations' })], }); if (!config) { throw new Error('Missing dbmigrations config'); } const conf = await config.getDefault().extension('dbmigrations'); return conf; }; /** * Creates knex based database using migration configuration * For production use please use different source of the configuration */ export const createDB = async () => { const dbmigrations = await getConfig(); // connect to db const db = Knex(dbmigrations); return db as any; }; ================================================ FILE: templates/fullstack/server/src/generated-db-types.ts ================================================ /* tslint:disable */ /** * AUTO-GENERATED FILE @ 2020-06-03 11:49:44 - DO NOT EDIT! * * This file was automatically generated by schemats v.3.0.3 * $ schemats generate -c postgres://username:password@localhost:55432/users -t note -t comment -s public * */ export namespace noteFields { export type title = string; export type description = string | null; export type id = number; } export interface note { title: noteFields.title; description: noteFields.description; id: noteFields.id; } export namespace commentFields { export type text = string | null; export type description = string | null; export type noteId = number | null; export type id = number; } export interface comment { text: commentFields.text; description: commentFields.description; noteId: commentFields.noteId; id: commentFields.id; } ================================================ FILE: templates/fullstack/server/src/generated-types.ts ================================================ /* tslint:disable */ import { GraphQLResolveInfo } from 'graphql'; import { comment, note } from './generated-db-types'; import { GraphbackRuntimeContext } from '@graphback/runtime'; export type Maybe = T | null; export type Exact = { [K in keyof T]: T[K] }; export type Omit = Pick>; export type RequireFields = { [X in Exclude]?: T[X] } & { [P in K]-?: NonNullable }; /** All built-in and custom scalars, mapped to their actual values */ export type Scalars = { ID: string; String: string; Boolean: boolean; Int: number; Float: number; }; /** @model */ export type Comment = { __typename?: 'Comment'; id: Scalars['ID']; text?: Maybe; description?: Maybe; /** @manyToOne(field: 'comments', key: 'noteId') */ note?: Maybe; }; export type CommentFilter = { id?: Maybe; text?: Maybe; description?: Maybe; noteId?: Maybe; and?: Maybe>; or?: Maybe>; not?: Maybe; }; export type CommentResultList = { __typename?: 'CommentResultList'; items: Array>; offset?: Maybe; limit?: Maybe; count?: Maybe; }; export type CommentSubscriptionFilter = { id?: Maybe; text?: Maybe; description?: Maybe; }; export type CreateCommentInput = { id?: Maybe; text?: Maybe; description?: Maybe; noteId?: Maybe; }; export type CreateNoteInput = { id?: Maybe; title: Scalars['String']; description?: Maybe; }; export type IdInput = { ne?: Maybe; eq?: Maybe; le?: Maybe; lt?: Maybe; ge?: Maybe; gt?: Maybe; in?: Maybe>; }; export type MutateCommentInput = { id: Scalars['ID']; text?: Maybe; description?: Maybe; noteId?: Maybe; }; export type MutateNoteInput = { id: Scalars['ID']; title?: Maybe; description?: Maybe; }; export type Mutation = { __typename?: 'Mutation'; createNote?: Maybe; updateNote?: Maybe; deleteNote?: Maybe; createComment?: Maybe; updateComment?: Maybe; deleteComment?: Maybe; }; export type MutationCreateNoteArgs = { input: CreateNoteInput; }; export type MutationUpdateNoteArgs = { input: MutateNoteInput; }; export type MutationDeleteNoteArgs = { input: MutateNoteInput; }; export type MutationCreateCommentArgs = { input: CreateCommentInput; }; export type MutationUpdateCommentArgs = { input: MutateCommentInput; }; export type MutationDeleteCommentArgs = { input: MutateCommentInput; }; /** @model */ export type Note = { __typename?: 'Note'; id: Scalars['ID']; title: Scalars['String']; description?: Maybe; /** @oneToMany(field: 'note', key: 'noteId') */ comments: Array>; }; /** @model */ export type NoteCommentsArgs = { filter?: Maybe; }; export type NoteFilter = { id?: Maybe; title?: Maybe; description?: Maybe; and?: Maybe>; or?: Maybe>; not?: Maybe; }; export type NoteResultList = { __typename?: 'NoteResultList'; items: Array>; offset?: Maybe; limit?: Maybe; count?: Maybe; }; export type NoteSubscriptionFilter = { id?: Maybe; title?: Maybe; description?: Maybe; }; export type OrderByInput = { field: Scalars['String']; order?: Maybe; }; export type PageRequest = { limit?: Maybe; offset?: Maybe; }; export type Query = { __typename?: 'Query'; getNote?: Maybe; findNotes: NoteResultList; getComment?: Maybe; findComments: CommentResultList; }; export type QueryGetNoteArgs = { id: Scalars['ID']; }; export type QueryFindNotesArgs = { filter?: Maybe; page?: Maybe; orderBy?: Maybe; }; export type QueryGetCommentArgs = { id: Scalars['ID']; }; export type QueryFindCommentsArgs = { filter?: Maybe; page?: Maybe; orderBy?: Maybe; }; export enum SortDirectionEnum { Desc = 'DESC', Asc = 'ASC' } export type StringInput = { ne?: Maybe; eq?: Maybe; le?: Maybe; lt?: Maybe; ge?: Maybe; gt?: Maybe; in?: Maybe>; contains?: Maybe; startsWith?: Maybe; endsWith?: Maybe; }; export type Subscription = { __typename?: 'Subscription'; newNote: Note; updatedNote: Note; deletedNote: Note; newComment: Comment; updatedComment: Comment; deletedComment: Comment; }; export type SubscriptionNewNoteArgs = { filter?: Maybe; }; export type SubscriptionUpdatedNoteArgs = { filter?: Maybe; }; export type SubscriptionDeletedNoteArgs = { filter?: Maybe; }; export type SubscriptionNewCommentArgs = { filter?: Maybe; }; export type SubscriptionUpdatedCommentArgs = { filter?: Maybe; }; export type SubscriptionDeletedCommentArgs = { filter?: Maybe; }; export type WithIndex = TObject & Record; export type ResolversObject = WithIndex; export type ResolverTypeWrapper = Promise | T; export type LegacyStitchingResolver = { fragment: string; resolve: ResolverFn; }; export type NewStitchingResolver = { selectionSet: string; resolve: ResolverFn; }; export type StitchingResolver = LegacyStitchingResolver | NewStitchingResolver; export type Resolver = | ResolverFn | StitchingResolver; export type ResolverFn = ( parent: TParent, args: TArgs, context: TContext, info: GraphQLResolveInfo ) => Promise | TResult; export type SubscriptionSubscribeFn = ( parent: TParent, args: TArgs, context: TContext, info: GraphQLResolveInfo ) => AsyncIterator | Promise>; export type SubscriptionResolveFn = ( parent: TParent, args: TArgs, context: TContext, info: GraphQLResolveInfo ) => TResult | Promise; export interface SubscriptionSubscriberObject { subscribe: SubscriptionSubscribeFn<{ [key in TKey]: TResult }, TParent, TContext, TArgs>; resolve?: SubscriptionResolveFn; } export interface SubscriptionResolverObject { subscribe: SubscriptionSubscribeFn; resolve: SubscriptionResolveFn; } export type SubscriptionObject = | SubscriptionSubscriberObject | SubscriptionResolverObject; export type SubscriptionResolver = | ((...args: any[]) => SubscriptionObject) | SubscriptionObject; export type TypeResolveFn = ( parent: TParent, context: TContext, info: GraphQLResolveInfo ) => Maybe | Promise>; export type IsTypeOfResolverFn = (obj: T, info: GraphQLResolveInfo) => boolean | Promise; export type NextResolverFn = () => Promise; export type DirectiveResolverFn = ( next: NextResolverFn, parent: TParent, args: TArgs, context: TContext, info: GraphQLResolveInfo ) => TResult | Promise; /** Mapping between all available schema types and the resolvers types */ export type ResolversTypes = ResolversObject<{ Comment: ResolverTypeWrapper; ID: ResolverTypeWrapper; String: ResolverTypeWrapper; CommentFilter: CommentFilter; CommentResultList: ResolverTypeWrapper & { items: Array> }>; Int: ResolverTypeWrapper; CommentSubscriptionFilter: CommentSubscriptionFilter; CreateCommentInput: CreateCommentInput; CreateNoteInput: CreateNoteInput; IDInput: IdInput; MutateCommentInput: MutateCommentInput; MutateNoteInput: MutateNoteInput; Mutation: ResolverTypeWrapper<{}>; Note: ResolverTypeWrapper; NoteFilter: NoteFilter; NoteResultList: ResolverTypeWrapper & { items: Array> }>; NoteSubscriptionFilter: NoteSubscriptionFilter; OrderByInput: OrderByInput; PageRequest: PageRequest; Query: ResolverTypeWrapper<{}>; SortDirectionEnum: SortDirectionEnum; StringInput: StringInput; Subscription: ResolverTypeWrapper<{}>; Boolean: ResolverTypeWrapper; }>; /** Mapping between all available schema types and the resolvers parents */ export type ResolversParentTypes = ResolversObject<{ Comment: comment; ID: Scalars['ID']; String: Scalars['String']; CommentFilter: CommentFilter; CommentResultList: Omit & { items: Array> }; Int: Scalars['Int']; CommentSubscriptionFilter: CommentSubscriptionFilter; CreateCommentInput: CreateCommentInput; CreateNoteInput: CreateNoteInput; IDInput: IdInput; MutateCommentInput: MutateCommentInput; MutateNoteInput: MutateNoteInput; Mutation: {}; Note: note; NoteFilter: NoteFilter; NoteResultList: Omit & { items: Array> }; NoteSubscriptionFilter: NoteSubscriptionFilter; OrderByInput: OrderByInput; PageRequest: PageRequest; Query: {}; StringInput: StringInput; Subscription: {}; Boolean: Scalars['Boolean']; }>; export type CommentResolvers = ResolversObject<{ id?: Resolver; text?: Resolver, ParentType, ContextType>; description?: Resolver, ParentType, ContextType>; note?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }>; export type CommentResultListResolvers = ResolversObject<{ items?: Resolver>, ParentType, ContextType>; offset?: Resolver, ParentType, ContextType>; limit?: Resolver, ParentType, ContextType>; count?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }>; export type MutationResolvers = ResolversObject<{ createNote?: Resolver, ParentType, ContextType, RequireFields>; updateNote?: Resolver, ParentType, ContextType, RequireFields>; deleteNote?: Resolver, ParentType, ContextType, RequireFields>; createComment?: Resolver, ParentType, ContextType, RequireFields>; updateComment?: Resolver, ParentType, ContextType, RequireFields>; deleteComment?: Resolver, ParentType, ContextType, RequireFields>; }>; export type NoteResolvers = ResolversObject<{ id?: Resolver; title?: Resolver; description?: Resolver, ParentType, ContextType>; comments?: Resolver>, ParentType, ContextType, RequireFields>; __isTypeOf?: IsTypeOfResolverFn; }>; export type NoteResultListResolvers = ResolversObject<{ items?: Resolver>, ParentType, ContextType>; offset?: Resolver, ParentType, ContextType>; limit?: Resolver, ParentType, ContextType>; count?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }>; export type QueryResolvers = ResolversObject<{ getNote?: Resolver, ParentType, ContextType, RequireFields>; findNotes?: Resolver>; getComment?: Resolver, ParentType, ContextType, RequireFields>; findComments?: Resolver>; }>; export type SubscriptionResolvers = ResolversObject<{ newNote?: SubscriptionResolver>; updatedNote?: SubscriptionResolver>; deletedNote?: SubscriptionResolver>; newComment?: SubscriptionResolver>; updatedComment?: SubscriptionResolver>; deletedComment?: SubscriptionResolver>; }>; export type Resolvers = ResolversObject<{ Comment?: CommentResolvers; CommentResultList?: CommentResultListResolvers; Mutation?: MutationResolvers; Note?: NoteResolvers; NoteResultList?: NoteResultListResolvers; Query?: QueryResolvers; Subscription?: SubscriptionResolvers; }>; /** * @deprecated * Use "Resolvers" root object instead. If you wish to get "IResolvers", add "typesPrefix: I" to your config. */ export type IResolvers = Resolvers; ================================================ FILE: templates/fullstack/server/src/graphql.ts ================================================ import { join } from 'path'; import { loadFiles } from '@graphql-tools/load-files'; import { ApolloServer } from 'apollo-server-express'; import { buildGraphbackAPI } from 'graphback'; import { createKnexDbProvider } from '@graphback/runtime-knex'; import { migrateDB, removeNonSafeOperationsFilter } from 'graphql-migrations'; import { createDB, getConfig } from './db'; import { SchemaCRUDPlugin } from '@graphback/codegen-schema'; /** * Creates Apollo server */ export const createApolloServer = async () => { const db = await createDB(); const dbConfig = await getConfig(); const schema = (await loadFiles(join(__dirname, '../../model/'))).join('\n'); const { resolvers, contextCreator, typeDefs} = buildGraphbackAPI(schema, { dataProviderCreator: createKnexDbProvider(db), plugins: [new SchemaCRUDPlugin({ outputPath: "./src/schema/schema.graphql" })] }); migrateDB(dbConfig, typeDefs, { operationFilter: removeNonSafeOperationsFilter }).then(() => { console.log("Migrated database"); }); const apolloServer = new ApolloServer({ typeDefs, resolvers, context: contextCreator, playground: true, }); return apolloServer; }; ================================================ FILE: templates/fullstack/server/src/index.ts ================================================ import cors from 'cors'; import express from 'express'; import http from 'http'; import { createApolloServer } from './graphql'; async function start() { const app = express(); app.use(cors()); app.get('/health', (req, res) => res.sendStatus(200)); const apolloServer = await createApolloServer(); apolloServer.applyMiddleware({ app }); const httpServer = http.createServer(app); apolloServer.installSubscriptionHandlers(httpServer); const port = process.env.PORT || 4000; httpServer.listen({ port }, () => { console.log(`🚀 Server ready at http://localhost:${port}/graphql`); }); } start().catch((err) => { console.error(err); process.exit(1); }); ================================================ FILE: templates/fullstack/server/src/schema/schema.graphql ================================================ ## NOTE: This schema was generated by Graphback and should not be changed manually """Exposes a URL that specifies the behaviour of this scalar.""" directive @specifiedBy( """The URL that specifies the behaviour of this scalar.""" url: String! ) on SCALAR """ @model """ type Comment { id: ID! text: String description: String """@manyToOne(field: 'comments', key: 'noteId')""" note: Note } input CommentFilter { id: IDInput text: StringInput description: StringInput noteId: IDInput and: [CommentFilter!] or: [CommentFilter!] not: CommentFilter } type CommentResultList { items: [Comment]! offset: Int limit: Int count: Int } input CommentSubscriptionFilter { id: ID text: String description: String } input CreateCommentInput { id: ID text: String description: String noteId: ID } input CreateNoteInput { id: ID title: String! description: String } input IDInput { ne: ID eq: ID le: ID lt: ID ge: ID gt: ID in: [ID!] } input MutateCommentInput { id: ID! text: String description: String noteId: ID } input MutateNoteInput { id: ID! title: String description: String } type Mutation { createNote(input: CreateNoteInput!): Note updateNote(input: MutateNoteInput!): Note deleteNote(input: MutateNoteInput!): Note createComment(input: CreateCommentInput!): Comment updateComment(input: MutateCommentInput!): Comment deleteComment(input: MutateCommentInput!): Comment } """ @model """ type Note { id: ID! title: String! description: String """@oneToMany(field: 'note', key: 'noteId')""" comments(filter: CommentFilter): [Comment]! } input NoteFilter { id: IDInput title: StringInput description: StringInput and: [NoteFilter!] or: [NoteFilter!] not: NoteFilter } type NoteResultList { items: [Note]! offset: Int limit: Int count: Int } input NoteSubscriptionFilter { id: ID title: String description: String } input OrderByInput { field: String! order: SortDirectionEnum = ASC } input PageRequest { limit: Int offset: Int } type Query { getNote(id: ID!): Note findNotes(filter: NoteFilter, page: PageRequest, orderBy: OrderByInput): NoteResultList! getComment(id: ID!): Comment findComments(filter: CommentFilter, page: PageRequest, orderBy: OrderByInput): CommentResultList! } enum SortDirectionEnum { DESC ASC } input StringInput { ne: String eq: String le: String lt: String ge: String gt: String in: [String!] contains: String startsWith: String endsWith: String } type Subscription { newNote(filter: NoteSubscriptionFilter): Note! updatedNote(filter: NoteSubscriptionFilter): Note! deletedNote(filter: NoteSubscriptionFilter): Note! newComment(filter: CommentSubscriptionFilter): Comment! updatedComment(filter: CommentSubscriptionFilter): Comment! deletedComment(filter: CommentSubscriptionFilter): Comment! } ================================================ FILE: templates/fullstack/server/tsconfig.json ================================================ { "compilerOptions": { "outDir": "./dist/", "declarationDir": "./types", "lib": [ "esnext", "dom" ], "resolveJsonModule": true, "noImplicitAny": false, "preserveConstEnums": true, "strict": false, "strictNullChecks": true, "esModuleInterop": true, "target": "esnext", "module": "commonjs", "moduleResolution": "node", "allowSyntheticDefaultImports": true, "importHelpers": true, "alwaysStrict": false, "sourceMap": true, "declaration": true, "noImplicitReturns": true, "noUnusedLocals": false, "noUnusedParameters": false, "noImplicitThis": false }, } ================================================ FILE: templates/fullstack/tslint.json ================================================ { "extends": "tslint:recommended", "rules": { "max-line-length": { "options": [120] }, "semicolon": false, "quotemark": false, "new-parens": true, "no-arg": true, "no-bitwise": true, "no-conditional-assignment": false, "no-consecutive-blank-lines": false, "object-literal-sort-keys": false, "trailing-comma": false, "object-literal-shorthand": false, "no-console": false, "interface-name": false }, "jsRules": { "max-line-length": { "options": [120] } } } ================================================ FILE: templates.json ================================================ { "Full Stack Template with React Hooks, Apollo, NodeJS and PostgreSQL": { "repository": "https://github.com/Urigo/graphql-cli.git", "path": "templates/fullstack", "projectType": "Full Stack" } } ================================================ FILE: website/.gitignore ================================================ # Dependencies /node_modules # Production /build # Generated files .docusaurus .cache-loader # Misc .DS_Store .env.local .env.development.local .env.test.local .env.production.local npm-debug.log* yarn-debug.log* yarn-error.log* ================================================ FILE: website/README.md ================================================ # Website This website is built using [Docusaurus 2](https://v2.docusaurus.io/), a modern static website generator. ### Installation ``` $ yarn ``` ### Local Development ``` $ yarn start ``` This command starts a local development server and open up a browser window. Most changes are reflected live without having to restart the server. ### Build ``` $ yarn build ``` This command generates static content into the `build` directory and can be served using any static contents hosting service. ### Deployment ``` $ GIT_USER= USE_SSH=true yarn deploy ``` If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. ================================================ FILE: website/algolia-lockfile.json ================================================ [ { "objectID": "cli-codegen", "headings": [], "toc": [], "content": "-", "url": "https://www.graphql-cli.com/codegen", "domain": "https://www.graphql-cli.com/", "hierarchy": ["CLI", "codegen"], "source": "CLI", "title": "codegen", "type": "Documentation" }, { "objectID": "cli-coverage", "headings": [], "toc": [], "content": "-", "url": "https://www.graphql-cli.com/coverage", "domain": "https://www.graphql-cli.com/", "hierarchy": ["CLI", "coverage"], "source": "CLI", "title": "coverage", "type": "Documentation" }, { "objectID": "cli-custom-commands", "headings": [ "Writing your own commands", "Testing your plugin locally", "Loading GraphQL Schema", "Error Handling" ], "toc": [ { "children": [ { "children": [ { "children": [], "title": "Getting Started", "anchor": "getting-started" } ], "title": "TL;DR", "anchor": "tldr" } ], "title": "Writing your own commands", "anchor": "writing-your-own-commands" }, { "children": [], "title": "Testing your plugin locally", "anchor": "testing-your-plugin-locally" }, { "children": [], "title": "Loading GraphQL Schema", "anchor": "loading-graphql-schema" }, { "children": [], "title": "Error Handling", "anchor": "error-handling" } ], "content": "-", "url": "https://www.graphql-cli.com/custom-commands", "domain": "https://www.graphql-cli.com/", "hierarchy": ["CLI", "Custom commands"], "source": "CLI", "title": "Custom commands", "type": "Documentation" }, { "objectID": "cli-diff", "headings": [], "toc": [], "content": "-", "url": "https://www.graphql-cli.com/diff", "domain": "https://www.graphql-cli.com/", "hierarchy": ["CLI", "diff"], "source": "CLI", "title": "diff", "type": "Documentation" }, { "objectID": "cli-discover", "headings": [], "toc": [], "content": "-", "url": "https://www.graphql-cli.com/discover", "domain": "https://www.graphql-cli.com/", "hierarchy": ["CLI", "discover"], "source": "CLI", "title": "discover", "type": "Documentation" }, { "objectID": "cli-generate", "headings": [], "toc": [], "content": "-", "url": "https://www.graphql-cli.com/generate", "domain": "https://www.graphql-cli.com/", "hierarchy": ["CLI", "generate"], "source": "CLI", "title": "generate", "type": "Documentation" }, { "objectID": "cli-init", "headings": [], "toc": [], "content": "-", "url": "https://www.graphql-cli.com/init", "domain": "https://www.graphql-cli.com/", "hierarchy": ["CLI", "init"], "source": "CLI", "title": "init", "type": "Documentation" }, { "objectID": "cli-introduction", "headings": [], "toc": [], "content": "-", "url": "https://www.graphql-cli.com/introduction", "domain": "https://www.graphql-cli.com/", "hierarchy": ["CLI", "Introduction"], "source": "CLI", "title": "Introduction", "type": "Documentation" }, { "objectID": "cli-introspect", "headings": [], "toc": [], "content": "-", "url": "https://www.graphql-cli.com/introspect", "domain": "https://www.graphql-cli.com/", "hierarchy": ["CLI", "introspect"], "source": "CLI", "title": "introspect", "type": "Documentation" }, { "objectID": "cli-migration", "headings": ["Migration from GraphQL CLI 3.x or older"], "toc": [ { "children": [ { "children": [ { "children": [], "title": "Update your configuration file", "anchor": "update-your-configuration-file" }, { "children": [], "title": "Comparison of old commands", "anchor": "comparison-of-old-commands" }, { "children": [], "title": "Special Notes for Prisma users", "anchor": "special-notes-for-prisma-users" } ], "title": "Install the new version", "anchor": "install-the-new-version" } ], "title": "Migration from GraphQL CLI 3.x or older", "anchor": "migration-from-graphql-cli-3x-or-older" } ], "content": "-", "url": "https://www.graphql-cli.com/migration", "domain": "https://www.graphql-cli.com/", "hierarchy": ["CLI", "Migration"], "source": "CLI", "title": "Migration", "type": "Documentation" }, { "objectID": "cli-serve", "headings": [], "toc": [], "content": "-", "url": "https://www.graphql-cli.com/serve", "domain": "https://www.graphql-cli.com/", "hierarchy": ["CLI", "serve"], "source": "CLI", "title": "serve", "type": "Documentation" }, { "objectID": "cli-similar", "headings": [], "toc": [], "content": "-", "url": "https://www.graphql-cli.com/similar", "domain": "https://www.graphql-cli.com/", "hierarchy": ["CLI", "similar"], "source": "CLI", "title": "similar", "type": "Documentation" }, { "objectID": "cli-validate", "headings": [], "toc": [], "content": "-", "url": "https://www.graphql-cli.com/validate", "domain": "https://www.graphql-cli.com/", "hierarchy": ["CLI", "validate"], "source": "CLI", "title": "validate", "type": "Documentation" } ] ================================================ FILE: website/babel.config.js ================================================ module.exports = { presets: [require.resolve('@docusaurus/core/lib/babel/preset')], }; ================================================ FILE: website/docs/codegen.md ================================================ --- id: codegen title: codegen sidebar_label: codegen --- Generate code from your GraphQL schema and operations. See the official [GraphQL Code Generator](https://graphql-code-generator.com/) site for complete documentation, guides and more. ### Installation import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; ``` yarn global add @graphql-cli/codegen ``` ``` npm i -g @graphql-cli/codegen ``` Note: GraphQL Code Generator also utilizes a plugin system, so make sure you also install any plugins you include inside your configuration. See [here](https://graphql-code-generator.com/docs/plugins/index) for a list of plugins. ### Example Configuration ```yml schema: - http://localhost:4000/graphql extensions: codegen: generates: ./graphql.schema.json: plugins: - "introspection" ``` See [the docs](https://graphql-code-generator.com/docs/getting-started/codegen-config) for more details. ### Usage ``` graphql codegen ``` #### Arguments *None* #### Options | option | alias | description | default | | --- | --- | --- | --- | | `--config` | `-c` | Path to GraphQL codegen YAML config file | `codegen.yml` or GraphQL configuration file in cwd | | `--watch` | `-w` | Watch for changes and execute generation automatically. You can also specify a glob expreession for custom watch list. | | | `--require` | `-r` | Loads specific require.extensions before running the codegen and reading the configuration | `[]` | | `--overwrite` | `-o` | Overwrites existing files | `true` | | `--silent` | `-s` | Suppresses printing errors | `false` | | `--project` | `-p` | Name of a project in GraphQL Config | `undefined` | ================================================ FILE: website/docs/coverage.md ================================================ --- id: coverage title: coverage sidebar_label: coverage --- Schema coverage based on documents. Find out how many times types and fields are used in your application. See the [official GraphQL Inspector documentation](https://graphql-inspector.com/docs/essentials/coverage) for details. ### Installation import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; ``` yarn global add @graphql-cli/coverage ``` ``` npm i -g @graphql-cli/coverage ``` ### Usage ``` graphql coverage [DOCUMENTS] [SCHEMA] ``` #### Arguments | argument | description | default | | --- | --- | --- | | `DOCUMENTS` | A glob pattern that points to GraphQL Documents / Operations | `documents` property in GraphQL Config file | | `SCHEMA` | A pointer to a schema | `schema` property in GraphQL Config file | #### Options | option | alias | description | default | | --- | --- | --- | --- | | `--silent` | `-s` | Do not render any stats in the terminal | `false` | | `--write` | `-w` | Write a file with coverage stats | disabled | | `--deprecated` | `-d` | Fail on deprecated usage | `false` | | `--require` | `-r` | Require a module | `[]` | | `--token` | `-t` | An access token | `undefined` | | `--header` | `-h` | Set HTTP header (`--header 'Auth: Basic 123'`) | `undefined` | ================================================ FILE: website/docs/custom-commands.md ================================================ --- id: custom-commands title: Custom commands sidebar_label: Custom commands --- ## Writing your own commands `graphql-cli` allow you to write your own plugin/extenion, and intergrate external tools and configuration, and run it from a single CLI. The current implementation of `graphql-cli` is using [Yargs](https://yargs.js.org/) to manage it's CLI commands. Plugins and extension are treated as NodeJS module by the `graphql-cli`, so it means you can use JavaScript/TypeScript/Any other super-set of JavaScript to write your extension. It means that you plugin will be loaded by it's name under `node_modules` - for example `graphql-cli my-custom-plugin ...`. `graphql-cli` also supports `graphql-config`, so it can help you easily load your GraphQL schema, operations and configuration from a unified config file. > If you are wrapping an existing tool that has it's own CLI already, consider to expose a programatic API so it will be easier to consume. ### TL;DR We have a ready-to-use boilerplate for that purpose, [you can find it here](https://github.com/dotansimha/graphql-cli-plugin-example). Also, inside this repo, under `packages/commands` you can find a set of plugins implementation you can use as reference. ### Getting Started Start by creating a simple JavaScript/TypeScript project, according to your preference. Install `@graphql-cli/common` package and use `defineCommand` utility in your entry point (usually `index` file): ```ts import { defineCommand } from '@graphql-cli/common'; export default defineCommand((api) => { return {}; }); ``` To register your CLI command, give it a name first. Use the `command` property: ```ts export default defineCommand((api) => { return { command: 'my-plugin', async handler() { // code here }, }; }); ``` Now, your plugin will be avaiable to use with the following command: `graphql my-plugin`. You can also add custom validations, flags, default values and much more with Yargs. [You can read the documentation here](https://yargs.js.org/docs/#api-commandcmd-desc-module). ## Testing your plugin locally To test your plugin locally, install `graphql-cli` in your project as a `devDependency`, and run the following command: ``` graphql ./src/index.js ``` If you registerd sub-commands, you should be able to run those this way: ``` graphql ./src/index.js do-something ``` > The path should point to the entry point of your script, and if you are using TypeScript - point to the compile file. ## Loading GraphQL Schema To easily load GraphQL schema, you can use `graphql-config`: ```ts import { defineCommand } from '@graphql-cli/common'; export default defineCommand((api) => { return { command: 'my-plugin', builder(build) { return build.options({ project: { type: 'string', describe: 'Name of your project', }, }); }, async handler(args) { // use graphql-config and find configuration const config = await api.useConfig(); // pick project const project = args.project ? config.getProject(args.project) : config.getDefault(); // get schema const schema = await config.getSchema(); }, }; }); ``` If you are using `graphql-config` to define your configuration, and you wish to load your extenion config from it, do: ```ts type MyConfig = { ... }; const extensionConfig = await config.extension('my-plugin'); ``` ## Error Handling If you wish to fail the execution of your plugin and report it back to GraphQL CLI host, simply throw an error: ```ts import { defineCommand } from '@graphql-cli/common'; export default defineCommand(() => { return { command: 'check-if-missing', handler() { if (somethingIsMissing) { throw new Error(`Ooops, something is missing`); } }, }; }); ``` ================================================ FILE: website/docs/diff.md ================================================ --- id: diff title: diff sidebar_label: diff --- Detect changes to your GraphQL Schema and prevent breaking your existing applications. See the [official GraphQL Inspector documentation](https://graphql-inspector.com/docs/essentials/diff) for details. ### Installation import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; ``` yarn global add @graphql-cli/diff ``` ``` npm i -g @graphql-cli/diff ``` ### Usage ``` graphql diff [OLD_SCHEMA] [NEW_SCHEMA] ``` #### Arguments | argument | description | default | | --- | --- | --- | | `OLD_SCHEMA` | A pointer to the old schema | `extensions.diff.baseSchema` property in GraphQL Config file | | `NEW_SCHEMA` | A pointer to the new schema | `schema` property in GraphQL Config file | #### Options | option | alias | description | default | | --- | --- | --- | --- | | `--require` | `-r` | Require a module | `[]` | | `--token` | `-t` | An access token | `undefined` | | `--header` | `-h` | Set HTTP header (`--header 'Auth: Basic 123'`) | `undefined` | ================================================ FILE: website/docs/discover.md ================================================ --- id: discover title: discover sidebar_label: discover --- Open a list of available plugins in your browser. ### Installation This command does not need to be installed. ### Usage ``` graphql discover ``` #### Arguments *None* #### Options *None* ================================================ FILE: website/docs/generate.md ================================================ --- id: generate title: generate sidebar_label: generate --- Generate schema and client documents for your GraphQL project by using [Graphback](https://graphback.dev). ### Installation import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; ``` yarn global add @graphql-cli/generate ``` ``` npm i -g @graphql-cli/generate ``` ### Example Configuration ```yml schema: './src/schema.graphql' documents: './client/src/graphql/**/*.graphql' extensions: graphback: model: './model/*.graphql' plugins: graphback-schema: outputPath: './src/schema/schema.graphql' graphback-client: outputFile: './client/src/graphql/graphback.graphql' ``` See [the docs](https://graphback.dev/docs/introduction) for more details. ### Usage ``` graphql generate ``` #### Arguments *None* #### Options | option | alias | description | default | | --- | --- | --- | --- | | `--watch` | `-w` | Watch for changes and execute generation automatically | | | `--db` | | | | | `--backend` | | | | | `--silent` | | | | ================================================ FILE: website/docs/init.md ================================================ --- id: init title: init sidebar_label: init --- Create a GraphQL project using a template or GraphQL Config file for your existing project. ### Installation import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; ``` yarn global add @graphql-cli/init ``` ``` npm i -g @graphql-cli/init ``` Note: Because you probably won't need to run this command again after bootstrapping your project, you can also run it using `npx` instead of installing the package: `npx graphql init`. ### Usage ``` graphql init ``` Follow the prompts to set up your project. #### Arguments *None* #### Options | option | alias | description | default | | --- | --- | --- | --- | | `--projectName` | | Name of a project in GraphQL Config | `undefined` | | `--templateName` | | Name of one of the predefined templates | `undefined` | | `--templateUrl` | | GitHub URL of the template. For example http://github.com/example/graphql-cli-example-template | `undefined` | ================================================ FILE: website/docs/introduction.md ================================================ --- id: introduction title: Introduction sidebar_label: Introduction --- ![GraphQL CLI](https://user-images.githubusercontent.com/20847995/67651234-85bf1500-f916-11e9-90e5-cb3bd0e6a338.png) ### Features * Helpful commands to improve your workflows * Compatible with editors and IDEs based on [graphql-config](https://www.graphql-config.com) * Powerful plugin system to extend graphql-cli with custom commands ### Installation Run the following command to install GraphQL CLI globally: import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; ``` yarn global add graphql-cli graphql ``` ``` npm i -g graphql-cli graphql ``` ### Configuration GraphQL CLI utilizes GraphQL Config for its configuration. You can learn more about GraphQL Config [here](https://www.graphql-config.com). The easiest way to get started is to run `init` command from your desired workspace: ``` npx graphql-cli init ``` After a series of questions from the command-prompt, the command will use the inputs and selected project templates to generate your configuration file for you. You can also write your own configuration file using an editor of your choice. For example, you could create a `.graphqlrc.yml` file with the following content: ``` schema: "server/src/schema/**/*.graphql" documents: "client/src/documents/**/*.graphql" ``` If you can run the `init` command with an existing file like the one above, its contents will be included with inputs you provide. ### Commands Each command in GraphQL CLI is treated as a plugin. In order to use the command, you have to install it first. Each command's package name follows this pattern: `@graphql-cli/[COMMAND-NAME]`. So to install the `init` command we used above, we would run ``` yarn global add @graphql-cli/init ``` ``` npm i -g @graphql-cli/init ``` After installing the command, we can then run it like this: ``` graphql init ``` Each command can be configured by updating the `extensions` field in your configuration file (`.graphqlrc.yml`). For example, if we install the `codegen` command, we can provide additional options to it like this: ```yml schema: ./server/src/schema/**/*.ts: require: ts-node/register documents: ./client/src/graphql/**/*.ts extensions: codegen: generates: ./server/src/generated-types.d.ts: plugins: - typescript - typescript-resolvers ./client/src/generated-types.tsx: plugins: - typescript - typescript-operations - typescript-react-apollo ``` You can learn more about each command by navigating to its page from the menu. You can also write your own commands; see [this guide](custom-commands) for a detailed explanation. :::tip Note: You can execute the command `graphql discover` to open a list of GraphQL CLI plugins you can still. This is the only command that is available without installing additional packages. ::: ================================================ FILE: website/docs/introspect.md ================================================ --- id: introspect title: introspect sidebar_label: introspect --- Dumps an introspection file based on a schema. See the [official GraphQL Inspector documentation](https://graphql-inspector.com/docs/essentials/introspect) for details. ### Installation import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; ``` yarn global add @graphql-cli/introspect ``` ``` npm i -g @graphql-cli/introspect ``` ### Usage ``` graphql introspect [SCHEMA] ``` #### Arguments | argument | description | default | | --- | --- | --- | | `SCHEMA` | A pointer to a schema | `schema` property in GraphQL Config file | #### Options | option | alias | description | default | | --- | --- | --- | --- | | `--write` | `-w` | Overwrite the output | `graphql.schema.json` | | `--require` | `-r` | Require a module | `[]` | | `--token` | `-t` | An access token | `undefined` | | `--header` | `-h` | Set HTTP header (`--header 'Auth: Basic 123'`) | `undefined` | ================================================ FILE: website/docs/migration.md ================================================ --- id: migration title: Migration sidebar_label: Migration --- ## Migration from GraphQL CLI 3.x or older Starting with GraphQL CLI 4.0 and higher, the way projects are set up is significantly restructured. ### Install the new version To get started, install the new version: ```sh yarn global add graphql-cli ``` You can also globally install using npm. > NOTE: If you have previous version of the GraphQL-CLI installed make sure to uninstall it first ```bash npm uninstall graphql-cli ``` ### Update your configuration file If you are working from an existing project, the GraphQL Config file that is used by GraphQL CLI is now called `.graphqlrc.yml` (by default) instead of `.graphqlconfig`. [Other options exist](https://graphql-config.com/usage) for naming the config files supported by GraphQL CLI, but this guide will assume you're using YAML syntax. To migrate, you will first need to update your GraphQL Configuration file to match GraphQL Config's updated syntax and structure. You can [check here](https://graphql-config.com/usage) for more information about the new structure. ####Specifying schema(s): ```yml schema: ./src/schema/**/*.graphql #You can have URL endpoint, Git URL and local files using globs here. ``` `schemaPath` is replaced by `schema`, which is now more flexible then the previous approach. This field is used by all commands and plugins of GraphQL CLI. ### Comparison of old commands #### `get-schema` is no longer available In previous versions, you were able to download the schema to the given path in `schemaPath` from the URL given inside `endpoint`. In the new version, `schema` refers to the endpoint of the schema. If you use Prisma or any other tool that provides your schema under URL endpoint, you must to specify it using the following syntax in your configuration YAML: ```yaml schema: http://localhost:4000/graphql #This is the schema path ``` If you want to download the schema from this URL to your local file system, you will also need to install `codegen` plugin and its `schema-ast` plugin using the following command or its npm equivalent: ```bash yarn add @graphql-cli/codegen @graphql-codegen/schema-ast --dev ``` After that, you can specify the output path of the local schema file: ```yaml schema: http://localhost:4000/graphql extensions: codegen: generates: ./schema.graphql: plugins: - schema-ast ``` By running `graphql codegen`, the `schema.graphql` file is generated in the root path of your project. ##### For JSON Output If you want to download the schema as a `json` introspection file, you will need to install `@graphql-codegen/introspection` instead, and add `introspection` instead of `schema-ast`. ```yaml schema: http://localhost:4000/graphql extensions: codegen: generates: ./schema.json: plugins: - introspection ``` #### `create` is no longer available: it is replaced by the `init` command. If you want to create a GraphQL Config file on an existing project or create a project using a template from scratch, you can use `graphql init` command. This command will ask some questions about your new or existing project to update your dependencies and create a new configuration file for GraphQL CLI. #### `diff` has been changed If you want to see the differences between your schema and another schema, use the `diff` command as follows: ```yaml graphql diff git:origin/master:schema.graphql ``` For example, `diff` will show the differences between the version in `schema` field of GraphQL Configuration file and the `schema.graphql` file in the remote master branch. Alternatively, you can compare `schema` with a URL endpoint: ```yaml graphql diff http://my-dev-instance.com/graphql ``` #### `add-endpoint`, `add-project`, `schema-status`, `ping`, `query`, `prepare`, `lint` and `playground` commands are no longer available. GraphQL CLI (as well as GraphQL Config) no longer separates `endpoints` and `schemaPath`. The new `schema` field refers to the single endpoint of your GraphQL schema, so it can be a URL endpoint or a local file. If your project uses a remote schema, you can directly define this URL in `schema` path without downloading it or defining it as an extra `endpoint` etc. Instead of using these legacy commands, you can create a faked server to test your schema using the `yarn serve` command. #### `codegen` now uses GraphQL Code Generator GraphQL CLI now uses GraphQL Code Generator which has a lot of plugins and templates for various environments, platforms and use cases. You can generate resolver signatures, TypeScript representations of your GraphQL Schema and more. [Check it out](https://graphql-code-generator.com/) The usage is slightly different from the old one: ```yaml schema: src/schema/**/*.graphql extensions: codegen: src/generated-types.ts: # Output file name - typescript # Plugin names to be used - typescript-resolvers ``` For instance, consider a hypothetical case where you need to generate TypeScript resolvers signatures for your GraphQL project. To do this, you would install the `codegen` plugin and the additional plugins and templates for GraphQL Code Generator. For this case, you would need `typescript` and `typescript-resolvers` plugins: ```bash yarn add @graphql-cli/codegen @graphql-codegen/typescript @graphql-codegen/typescript-resolvers --dev ``` Now, using a single command, you can run GraphQL Code Generator using GraphQL CLI: ```bash graphql codegen ``` ### Special Notes for Prisma users Prisma users will need to download a schema from a URL endpoint. For example, here is a *legacy GraphQL Config file* doing this: `.graphqlconfig` ```yaml projects: app: schemaPath: src/schema.graphql extensions: endpoints: default: http://localhost:4000 database: schemaPath: src/generated/prisma-client/prisma.graphql extensions: prisma: prisma/prisma.yml ``` **To update the configuration for the new GraphQL CLI** you need to rename the file to `.graphqlrc.yml`, and then update the file as follows: `.graphqlrc.yml` ```yaml projects: app: schema: src/schema.graphql database: schema: prisma/prisma.yml extensions: codegen: generates: ./src/generated/prisma-client/prisma.graphql: plugins: - schema-ast ``` You can directly point to your `prisma.yml` file instead of the URL endpoint. Before running the GraphQL CLI command to use this new configuration, make sure you have installed the `@graphql-cli/codegen` and `@graphql-codegen/schema-ast` plugins using: ```sh yarn add @graphql-cli/codegen @graphql-codegen/schema-ast --dev ``` Now you can run `graphql codegen --project database` for generating your `prisma.graphql` file. You will also need to update your `prisma.yml` file if you're using `graphql get-schema` with Prisma: ```yaml ... ## Ensures Prisma client is re-generated after a datamodel change. hooks: post-deploy: - graphql codegen --project database # instead of graphql get-schema - prisma generate ``` ================================================ FILE: website/docs/serve.md ================================================ --- id: serve title: serve sidebar_label: serve --- Serves a full featured [GraphQL CRUD](https://graphqlcrud.org/) API with subscriptions and data synchronization running in just a few seconds without writing a single line of code - all you need is a data model `.graphql` file. GraphQL Serve is a CLI tool that leverages the power of Graphback to generate a codeless Node.js GraphQL API complete with schema and CRUD resolvers and an in-memory MongoDB database. ### Installation import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; ``` yarn global add @graphql-cli/serve ``` ``` npm i -g @graphql-cli/serve ``` ### Usage The bare minimum you need is a GraphQL file with your data models. Create a file called `Note.graphql` and add the following: ```graphql """ @model """ type Note { _id: GraphbackObjectID! title: String! description: String likes: Int } scalar GraphbackObjectID ``` The `@model` annotation indicates that `Note` is a data model and Graphback will generate resolvers, a CRUD service and data source for it. You can learn how to build more complex data models in [Data Model](https://graphback.dev/docs/model/datamodel#model). #### Running your codeless GraphQL server To start your server, run the following command from the same directory as `Note.graphql`: ```bash graphql serve Note.graphql ``` This will start a GraphQL server on a random port using the `Note.graphql` data models we just added. You can customise the directory of the data models: ```bash graphql serve ./path/to/models ``` You can also specify where to load the data models from with a Glob pattern: ```bash graphql serve ./schema/**/*.graphql ``` You can specify which port to start the server on: ```bash $ graphql serve ./path/to/models --port 8080 Starting server... Listening at: http://localhost:8080/graphql ``` ### Enable Data Synchronization GraphQL Serve can also operate on data sync models. Under the hood this uses the [Data Sync](https://graphback.dev/docs/datasync/intro) package. To enable data synchronization, all we need to do is enable datasync capabilities on our models via the `@datasync` annotation. For the `Note` model defined above, this would look like: ```graphql """ @model @datasync """ type Note { _id: GraphbackObjectID! title: String! description: String likes: Int } scalar GraphbackObjectID ``` Once we have a model with datasync capabilities, we can run our GraphQL server by enabling data synchronization as shown below: ```bash graphql serve Note.graphql --datasync ``` Conflict resolution strategies for datasync enabled models can be specified via the --conflict option: ```bash graphql serve Note.graphql --datasync --conflict=clientSideWins ``` This defaults to ClientSideWins, if unset. The TTL for delta tables, can also be set using the --deltaTTL option: ```bash graphql serve Note.graphql --datasync --deltaTTL=172800 ``` This value defaults to `172800` when unused #### Arguments | argument | description | default | | --- | --- | --- | | `Model` | Directory to search for data models | `undefined` | #### Options | option | alias | description | default | | --- | --- | --- | --- | | `--port` | `-p` | Port on which to run the HTTP server | `Random port` | | `--datasync` | `--ds` | Enable datasynchronization features | `false` | | `--deltaTTL` | N/A | Specify a conflict resolution strategy with --datasync. Choices: `clientSideWins`, `serverSideWins`, `throwOnConflict` | `clientSideWins` | | `--conflict` | N/A | Specify a TTL for delta tables with --datasync | `172800` | ================================================ FILE: website/docs/similar.md ================================================ --- id: similar title: similar sidebar_label: similar --- Get a list of similar types in order to find duplicates. See the [official GraphQL Inspector documentation](https://graphql-inspector.com/docs/essentials/similar) for details. ### Installation import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; ``` yarn global add @graphql-cli/similar ``` ``` npm i -g @graphql-cli/similar ``` ### Usage ``` graphql similar [SCHEMA] ``` #### Arguments | argument | description | default | | --- | --- | --- | | `SCHEMA` | A pointer to a schema | `schema` property in GraphQL Config file | #### Options | option | alias | description | default | | --- | --- | --- | --- | | `--type` | `-n` | Check only a single type | all types | | `--threshold` | `-t` | Threshold of similarity ratio | `0.4` | | `--write` | `-w` | Write a file with results | disabled | | `--require` | `-r` | Require a module | `[]` | | `--token` | `-t` | An access token | `undefined` | | `--header` | `-h` | Set HTTP header (`--header 'Auth: Basic 123'`) | `undefined` | ================================================ FILE: website/docs/validate.md ================================================ --- id: validate title: validate sidebar_label: validate --- Validates documents against a schema and looks for deprecated usage.. See the [official GraphQL Inspector documentation](https://graphql-inspector.com/docs/essentials/validate) for details. ### Installation import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; ``` yarn global add @graphql-cli/validate ``` ``` npm i -g @graphql-cli/validate ``` ### Usage ``` graphql validate [DOCUMENTS] [SCHEMA] ``` #### Arguments | argument | description | default | | --- | --- | --- | | `DOCUMENTS` | A glob pattern that points to GraphQL Documents / Operations | `documents` property in GraphQL Config file | | `SCHEMA` | A pointer to a schema | `schema` property in GraphQL Config file | #### Options | option | alias | description | default | | --- | --- | --- | --- | | `--deprecated` | `-d` | Fail on deprecated usage | `false` | | `--noStrictFragments` | | Do not fail on duplicated fragment names | `false` | | `--apollo` | | Support Apollo directives (@client and @connection) | `false` | | `--keepClientFields` | | Keeps the fields with @client, but removes @client directive from them - works only with combination of `--apollo` | `false` | | `--maxDepth` | | Fail when operation depth exceeds maximum depth | `undefined` | | `--require` | `-r` | Require a module | `[]` | | `--token` | `-t` | An access token | `undefined` | | `--header` | `-h` | Set HTTP header (`--header 'Auth: Basic 123'`) | `undefined` | ================================================ FILE: website/docusaurus.config.js ================================================ module.exports = { title: 'GraphQL CLI', tagline: '📟 Command line tool for common GraphQL development workflows', url: 'https://graphql-cli.com', baseUrl: '/', favicon: 'img/logo.ico', organizationName: 'urigo', projectName: 'graphql-cli', themeConfig: { colorMode: { disableSwitch: true, }, image: 'img/logo.png', navbar: { title: 'GraphQL CLI', logo: { alt: 'GraphQL CLI Logo', src: 'img/logo.png', }, items: [ { to: '/introduction', label: 'Documentation', position: 'right', }, { href: 'https://github.com/urigo/graphql-cli', label: 'GitHub', position: 'right', }, ], }, footer: { style: 'dark', copyright: `Copyright © ${new Date().getFullYear()} The Guild. All rights reserved.`, links: [ { title: 'Docs', items: [ { label: 'Introduction', to: 'introduction', }, ], }, { title: 'Community', items: [ { label: 'Discord', href: 'https://discord.gg/xud7bH9', }, { label: 'Other projects', href: 'https://github.com/the-guild-org/Stack', }, { label: 'Mailing List', href: 'https://upscri.be/19qjhi', }, { label: 'Community Meetings', href: 'https://github.com/the-guild-org/community-meetings', }, ], }, { title: 'Social', items: [ { label: 'Blog', href: 'https://the-guild.dev/blog', }, { label: 'GitHub', href: 'https://github.com/urigo/graphql-cli', }, { label: 'Twitter', href: 'https://twitter.com/TheGuildDev', }, { label: 'LinkedIn', href: 'https://www.linkedin.com/company/the-guild-software', }, ], }, ], }, prism: { theme: require('prism-react-renderer/themes/dracula'), }, }, scripts: ['/js/light-mode-by-default.js'], customFields: { algolia: { appId: process.env.NEXT_PUBLIC_ALGOLIA_APP_ID, searchApiKey: process.env.NEXT_PUBLIC_ALGOLIA_SEARCH_API_KEY, indexName: process.env.NEXT_PUBLIC_ALGOLIA_INDEX_NAME, }, }, presets: [ [ require.resolve('@docusaurus/preset-classic'), { docs: { path: 'docs', routeBasePath: '/', include: ['**/*.md', '**/*.mdx'], sidebarPath: require.resolve('./sidebars.js'), editUrl: 'https://github.com/urigo/graphql-cli/edit/master/website/', }, theme: { customCss: require.resolve('./src/css/custom.css'), }, sitemap: { // cacheTime: 600 * 1001, // 600 sec - cache purge period changefreq: 'weekly', priority: 0.5, }, }, ], ], }; ================================================ FILE: website/package.json ================================================ { "name": "website", "version": "4.1.0", "private": true, "scripts": { "start": "docusaurus start", "build": "docusaurus build", "swizzle": "docusaurus swizzle", "deploy": "docusaurus deploy", "algolia-sync": "ts-node scripts/algolia-ci.ts" }, "dependencies": { "@docusaurus/core": "^2.0.0-beta.0", "@guild-docs/algolia": "0.0.6-alpha-1bcf597.0", "@docusaurus/preset-classic": "^2.0.0-beta.0", "clsx": "^1.1.1", "react": "^17.0.0", "react-dom": "^17.0.0", "styled-components": "5.3.3", "the-guild-components": "1.5.3" }, "browserslist": { "production": [ ">0.2%", "not dead", "not op_mini all" ], "development": [ "last 1 chrome version", "last 1 firefox version", "last 1 safari version" ] } } ================================================ FILE: website/scripts/algolia-ci.ts ================================================ import { indexToAlgolia } from '@guild-docs/algolia'; import { resolve } from 'path'; import * as sidebars from '../sidebars'; indexToAlgolia({ docusaurus: { sidebars, }, // needed because GraphQL CLI has a weird routing config with content at `/` postProcessor: (objects) => objects.map((o) => ({ ...o, url: o.url.replace('docs/', ''), })), source: 'CLI', domain: process.env.SITE_URL!, lockfilePath: resolve(__dirname, '../algolia-lockfile.json'), dryMode: process.env.ALGOLIA_DRY_RUN === 'true', }); ================================================ FILE: website/sidebars.js ================================================ module.exports = { docs: { 'Getting Started': [ 'introduction', ], 'Commands': [ 'codegen', 'coverage', 'diff', 'discover', 'generate', 'init', 'introspect', 'serve', 'similar', 'validate', ], 'Recipes': [ 'migration', 'custom-commands', ], }, }; ================================================ FILE: website/src/css/custom.css ================================================ :root { --ifm-color-primary: #8c0082; --ifm-color-primary-dark: #8c0082; --ifm-color-primary-darker: #8c0082; --ifm-color-primary-darkest: #8c0082; --ifm-color-primary-light: #8c0082; --ifm-color-primary-lighter: #8c0082; --ifm-color-primary-lightest: #8c0082; --ifm-color-secondary: #bfc7d5; --ifm-footer-link-hover-color: #fff; } a { transition: all 0.2s ease 0s; } button { --ifm-color-content: #fff; } td > code { white-space: nowrap; } ================================================ FILE: website/src/pages/index.js ================================================ import React from 'react'; import {Redirect} from '@docusaurus/router'; function Home() { return ; } export default Home; ================================================ FILE: website/src/pages/styles.module.css ================================================ /* stylelint-disable docusaurus/copyright-header */ /** * CSS files with the .module.css suffix will be treated as CSS modules * and scoped locally. */ .heroBanner { padding: 4rem 0; text-align: center; position: relative; overflow: hidden; } @media screen and (max-width: 966px) { .heroBanner { padding: 2rem; } } .buttons { display: flex; align-items: center; justify-content: center; } .features { display: flex; align-items: center; padding: 2rem 0; width: 100%; } .featureImage { height: 200px; width: 200px; } ================================================ FILE: website/src/theme/Root.js ================================================ import React from 'react'; import BrowserOnly from '@docusaurus/BrowserOnly'; import { ThemeProvider, Header } from 'the-guild-components'; // Default implementation, that you can customize function Root({ children }) { return ( <> {() => (
    )} {children} ); } export default Root; ================================================ FILE: website/static/CNAME ================================================ www.graphql-cli.com ================================================ FILE: website/static/js/light-mode-by-default.js ================================================ if (!localStorage.getItem('theme')) { localStorage.setItem('theme', 'light') }