diff --git a/.eslintrc.js b/.eslintrc.js index 199408e5ceff0..142af38ed418f 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -34,6 +34,7 @@ module.exports = { 'array-bracket-spacing': 'off', 'comma-dangle': 'off', 'max-len': 'off', + 'no-extra-parens': 'off', 'space-before-function-paren': 'off', 'ft-flow/object-type-delimiter': 'off', 'babel/flow-object-type': 'off', @@ -89,4 +90,50 @@ module.exports = { 'relay-internal/no-mixed-import-and-require': 'error', 'relay-internal/sort-imports': 'error', }, + overrides: [ + { + files: ['packages/relay-runtime/**/*.js', 'packages/react-relay/**/*.js'], + excludedFiles: [ + '**/__tests__/**', + + // The following files should eventually be migrated to not break this + // rule. Until then, we'll grandfather them in here. + // If anyone feels inspired, they can: + // + // 1. Remove a file from this list + // 2. Run `yarn lint` + // 3. Fix/supress all warnings + // 4. Open a PR + // 5. Profit? + 'packages/react-relay/relay-hooks/readFragmentInternal.js', + 'packages/react-relay/relay-hooks/useEntryPointLoader.js', + 'packages/react-relay/relay-hooks/useFragmentInternal_CURRENT.js', + 'packages/react-relay/relay-hooks/useFragmentInternal_EXPERIMENTAL.js', + 'packages/react-relay/relay-hooks/useQueryLoader.js', + 'packages/react-relay/relay-hooks/useQueryLoader_EXPERIMENTAL.js', + 'packages/relay-runtime/handlers/connection/MutationHandlers.js', + 'packages/relay-runtime/multi-actor-environment/MultiActorEnvironment.js', + 'packages/relay-runtime/mutations/RelayDeclarativeMutationConfig.js', + 'packages/relay-runtime/mutations/createUpdatableProxy.js', + 'packages/relay-runtime/store/DataChecker.js', + 'packages/relay-runtime/store/OperationExecutor.js', + 'packages/relay-runtime/store/RelayErrorTrie.js', + 'packages/relay-runtime/store/RelayExperimentalGraphResponseHandler.js', + 'packages/relay-runtime/store/RelayExperimentalGraphResponseTransform.js', + 'packages/relay-runtime/store/RelayModernStore.js', + 'packages/relay-runtime/store/RelayOperationTracker.js', + 'packages/relay-runtime/store/RelayRecordSource.js', + 'packages/relay-runtime/store/RelayReferenceMarker.js', + 'packages/relay-runtime/store/RelayResponseNormalizer.js', + 'packages/relay-runtime/store/live-resolvers/LiveResolverCache.js', + 'packages/relay-runtime/store/observeFragmentExperimental.js', + 'packages/relay-runtime/util/RelayReplaySubject.js', + 'packages/relay-runtime/util/getValueAtPath.js', + 'packages/relay-runtime/util/handlePotentialSnapshotErrors.js', + ], + rules: { + 'relay-internal/no-for-of-loops': 'error', + }, + }, + ], }; diff --git a/.flowconfig b/.flowconfig index 9b8e5571f73fe..d8dfdf6a5e445 100644 --- a/.flowconfig +++ b/.flowconfig @@ -5,14 +5,8 @@ .*/node_modules/resolve/test/resolver/malformed_package_json/package.json [options] -as_const=true component_syntax=true module.system=haste -module.system.haste.use_name_reducers=true -# get basename -module.system.haste.name_reducers='^.*/\([a-zA-Z0-9$_.-]+\.js\(\.flow\)?\)$' -> '\1' -# strip .js or .js.flow suffix -module.system.haste.name_reducers='^\(.*\)\.js\(\.flow\)?$' -> '\1' module.system.haste.paths.excludes=.*/__tests__/.* module.system.haste.paths.excludes=.*/__mocks__/.* @@ -21,10 +15,7 @@ module.system.haste.paths.includes=/node_modules/fbjs/lib/.* munge_underscores=true ; https://fburl.com/code/h2jnts20 - to match internval value -suppress_type=$FlowIssue suppress_type=$FlowFixMe -suppress_type=$FlowFixMeProps -suppress_type=$FlowFixMeState suppress_type=$FlowExpectedError format.bracket_spacing=false @@ -42,4 +33,4 @@ untyped-import untyped-type-import [version] -^0.253.0 +^0.278.0 diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 44b3b87082896..1ea2481c13dcb 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -72,6 +72,10 @@ following steps should work for all Relay packages: 1. Because of symbolic links and how Node resolves dependencies, you will probably want to instruct Node to resolve preserve the links `yarn node --preserve-symlinks-main --preserve-symlinks ./node_modules/.bin/relay-compiler […]`. +## VSCode + +This repository has configuration files in the `.vscode/` directory to make development in VSCode easier. This includes enumerating recommended exensions (Flow, Rust Analyzer, Prettier, etc.) and sensible default settings (enable auto formatting etc.) to use when developing in this repository as well as some defined [Tasks](https://code.visualstudio.com/docs/debugtest/tasks) to easily trigger common actions directly via the command pallet. + ## License By contributing to Relay, you agree that your contributions will be licensed under its MIT license. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3a162706e4373..1cb5d963716ef 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,8 +15,8 @@ jobs: run: working-directory: ./vscode-extension steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 20.x cache: 'yarn' @@ -36,8 +36,8 @@ jobs: matrix: node-version: [18.x, 20.x] steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} cache: 'yarn' @@ -50,8 +50,8 @@ jobs: name: JS Lint runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 20.x cache: 'yarn' @@ -68,8 +68,8 @@ jobs: name: Flow Typecheck runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 20.x cache: 'yarn' @@ -92,10 +92,13 @@ jobs: os: windows-latest runs-on: ${{ matrix.target.os }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - uses: actions-rs/toolchain@v1 with: - toolchain: 1.76.0 + # Should stay in sync with fbcode/buck2/rust-toolchain + # And other similar references in this file and + # docusaurus.yml + toolchain: nightly-2025-05-09 override: true - name: "Run tests" run: cargo test --manifest-path=compiler/Cargo.toml --locked ${{ matrix.target.features && '--features' }} ${{ matrix.target.features }} @@ -115,10 +118,13 @@ jobs: # os: windows-latest runs-on: ${{ matrix.target.os }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - uses: actions-rs/toolchain@v1 with: - toolchain: 1.76.0 + # Should stay in sync with fbcode/buck2/rust-toolchain + # And other similar references in this file and + # docusaurus.yml + toolchain: nightly-2025-05-09 override: true - name: "Update fixture tests" run: ./scripts/update-fixtures.sh @@ -133,11 +139,11 @@ jobs: name: Rust Lint runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - uses: actions-rs/toolchain@v1 with: # Should stay in sync with tools/third-party/rustfmt/.rustfmt-version - toolchain: nightly-2024-07-28 + toolchain: nightly-2025-04-14 override: true components: rustfmt - name: "rustfmt" @@ -175,13 +181,16 @@ jobs: artifact-name: relay-bin-win-x64 runs-on: ${{ matrix.target.os }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - uses: actions-rs/toolchain@v1 with: - toolchain: 1.76.0 + # Should stay in sync with fbcode/buck2/rust-toolchain + # And other similar references in this file and + # docusaurus.yml + toolchain: nightly-2025-05-09 override: true target: ${{ matrix.target.target }} - - uses: actions/setup-node@v2 + - uses: actions/setup-node@v4 if: github.event_name == 'push' && github.repository == 'facebook/relay' && github.ref == 'refs/heads/main' with: node-version: 20.x @@ -221,8 +230,8 @@ jobs: if: github.event_name == 'push' && github.repository == 'facebook/relay' needs: [js-tests, js-lint, typecheck, build-tests, build-compiler] steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: 18.x registry-url: https://registry.npmjs.org/ diff --git a/.github/workflows/docusaurus.yml b/.github/workflows/docusaurus.yml index 18feee2077769..6187e1cceec68 100644 --- a/.github/workflows/docusaurus.yml +++ b/.github/workflows/docusaurus.yml @@ -21,7 +21,16 @@ jobs: - uses: actions/checkout@v4 - uses: actions-rs/toolchain@v1 with: - toolchain: 1.76.0 + # Should stay in sync with fbcode/buck2/rust-toolchain + # And other similar references in this file and + # ci.yml + # toolchain: nightly-2025-05-09 + + # Bug in rust causes wasm-pack to fail. Bug is fixed in + # https://github.com/rust-lang/rust/pull/139498 so we're temporarily + # ahead here while we wait for that fix to land in the common + # toolchain version used at Meta. + toolchain: nightly-2025-05-12 override: true - name: Install wasm-pack run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh diff --git a/.github/workflows/update-cargo-lock.yml b/.github/workflows/update-cargo-lock.yml index 92b35c81bbfa8..79c6eea4ba549 100644 --- a/.github/workflows/update-cargo-lock.yml +++ b/.github/workflows/update-cargo-lock.yml @@ -21,7 +21,10 @@ jobs: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: - toolchain: 1.76.0 + # Should stay in sync with fbcode/buck2/rust-toolchain + # And other similar references in this file and + # ci.yml + toolchain: nightly-2025-05-09 override: true - name: cargo check run: cargo check --features vendored --manifest-path=compiler/Cargo.toml diff --git a/.github/workflows/vscode.yml b/.github/workflows/vscode.yml index 4c30236b3380a..baa27038e010f 100644 --- a/.github/workflows/vscode.yml +++ b/.github/workflows/vscode.yml @@ -10,10 +10,10 @@ jobs: run: working-directory: ./vscode-extension steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: - node-version: 16.x + node-version: 20.x cache: 'yarn' - name: Install dependencies run: yarn install --frozen-lockfile --ignore-scripts diff --git a/.prettier-plugins.fb.js b/.prettier-plugins.fb.js new file mode 100644 index 0000000000000..8e8c72d4eca77 --- /dev/null +++ b/.prettier-plugins.fb.js @@ -0,0 +1,14 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +module.exports = [ + require.resolve( + '../../../../../../../tools/third-party/prettier/node_modules/prettier-plugin-hermes-parser/index.mjs', + ), +]; diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 0000000000000..b590dd3dd4911 --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1,25 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @oncall relay + */ + +let plugins = ['prettier-plugin-hermes-parser']; +try { + plugins = require('./.prettier-plugins.fb.js'); +} catch {} + +module.exports = { + arrowParens: 'avoid', + bracketSameLine: true, + bracketSpacing: false, + requirePragma: true, + singleQuote: true, + trailingComma: 'all', + parser: 'hermes', + plugins, +}; diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000000000..6fe16f9921879 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,43 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Run Rust Compiler Tests", + "type": "shell", + "command": "cargo test", + "options": { + "cwd": "${workspaceFolder}/compiler" + }, + "problemMatcher": [ + "$rustc" + ] + }, + { + "label": "Run Rust Compiler Tests (Update Snapshots)", + "type": "shell", + "command": "cargo test", + "options": { + "cwd": "${workspaceFolder}/compiler", + "env": { + "UPDATE_SNAPSHOTS": "1" + } + }, + "problemMatcher": [ + "$rustc" + ] + }, + { + "label": "Regenerate Rust Tests for Fixture Files", + "type": "shell", + "command": "./scripts/update-fixtures.sh", + "problemMatcher": [ + "$rustc" + ] + }, + { + "label": "Build Relay artifacts for runtime tests", + "type": "shell", + "command": "./scripts/compile-tests.sh" + } + ] +} \ No newline at end of file diff --git a/compiler/Cargo.lock b/compiler/Cargo.lock index 3a614fa28e40d..aa1b63adfa03b 100644 --- a/compiler/Cargo.lock +++ b/compiler/Cargo.lock @@ -1,29 +1,29 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" -version = "0.21.0" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" dependencies = [ "gimli", ] [[package]] -name = "adler" -version = "1.0.2" +name = "adler2" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "ahash" -version = "0.8.7" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.3", "once_cell", "version_check", "zerocopy", @@ -31,9 +31,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] @@ -46,15 +46,15 @@ checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" [[package]] name = "allocator-api2" -version = "0.2.16" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "anstream" -version = "0.6.15" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" dependencies = [ "anstyle", "anstyle-parse", @@ -67,49 +67,50 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.8" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anstyle-parse" -version = "0.2.5" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.1.1" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.60.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.4" +version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "once_cell_polyfill", + "windows-sys 0.60.2", ] [[package]] name = "anyhow" -version = "1.0.79" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" [[package]] name = "arbitrary" -version = "1.3.2" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" [[package]] name = "assert_matches" @@ -119,13 +120,13 @@ checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" [[package]] name = "async-trait" -version = "0.1.77" +version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] @@ -134,30 +135,24 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi 0.1.19", + "hermit-abi", "libc", "winapi", ] -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" dependencies = [ "addr2line", - "cc", - "cfg-if 1.0.0", + "cfg-if 1.0.3", "libc", "miniz_oxide", "object", "rustc-demangle", + "windows-link", ] [[package]] @@ -183,9 +178,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.2" +version = "2.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" [[package]] name = "block-buffer" @@ -198,9 +193,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.10.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c" +checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" dependencies = [ "memchr", "serde", @@ -208,9 +203,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.14.0" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "byteorder" @@ -220,31 +215,23 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "0.4.12" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" -dependencies = [ - "byteorder", - "iovec", -] - -[[package]] -name = "bytes" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" dependencies = [ "serde", ] [[package]] name = "cc" -version = "1.0.83" +version = "1.2.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +checksum = "e1d05d92f4b1fd76aad469d46cdd858ca761576082cd37df81416691e50199fb" dependencies = [ + "find-msvc-tools", "jobserver", "libc", + "shlex", ] [[package]] @@ -255,15 +242,15 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" [[package]] name = "clap" -version = "4.5.20" +version = "4.5.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" +checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae" dependencies = [ "clap_builder", "clap_derive", @@ -271,9 +258,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.20" +version = "4.5.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" +checksum = "c2ba64afa3c0a6df7fa517765e31314e983f51dda798ffba27b988194fb65dc9" dependencies = [ "anstream", "anstyle", @@ -286,36 +273,36 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.18" +version = "4.5.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] name = "clap_lex" -version = "0.7.2" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] name = "colorchoice" -version = "1.0.2" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "colored" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" +checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" dependencies = [ "lazy_static", - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -347,7 +334,7 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.3", "wasm-bindgen", ] @@ -363,15 +350,15 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", ] @@ -391,18 +378,18 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.11" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "176dc175b78f56c0f321911d9c8eb2b77a78a4860b9c19db83835fea1a46649b" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-deque" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -419,18 +406,18 @@ dependencies = [ [[package]] name = "crossbeam-queue" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.19" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crypto-common" @@ -448,8 +435,8 @@ version = "5.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" dependencies = [ - "cfg-if 1.0.0", - "hashbrown", + "cfg-if 1.0.3", + "hashbrown 0.14.5", "lock_api", "once_cell", "parking_lot_core", @@ -477,9 +464,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.11" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +checksum = "a41953f86f8a05768a6cda24def994fd2f424b04ec5c719cf89989779f199071" dependencies = [ "powerfmt", ] @@ -500,6 +487,17 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "docblock-shared" version = "0.0.0" @@ -522,27 +520,27 @@ dependencies = [ "graphql-test-helpers", "intern", "serde", - "thiserror", + "thiserror 2.0.17", "tokio", ] [[package]] name = "dunce" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" [[package]] name = "dyn-clone" -version = "1.0.17" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" [[package]] name = "either" -version = "1.9.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "env_logger" @@ -559,27 +557,29 @@ dependencies = [ [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "erased-serde" -version = "0.4.2" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55d05712b2d8d88102bc9868020c9e5c7a1f5527c452b9b97450a1d006140ba7" +checksum = "259d404d09818dec19332e31d94558aeb442fea04c817006456c24b5460bbd4b" dependencies = [ "serde", + "serde_core", + "typeid", ] [[package]] name = "errno" -version = "0.3.8" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -602,9 +602,15 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.1" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "find-msvc-tools" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +checksum = "0399f9d26e5191ce32c498bebd31e7a3ceabc2745f0ac54af3f335126c3f24b3" [[package]] name = "fixedbitset" @@ -626,13 +632,12 @@ dependencies = [ [[package]] name = "flatbuffers" -version = "2.1.2" +version = "25.9.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b428b715fdbdd1c364b84573b5fdc0f84f8e423661b9f398735278bc7f2b6a" +checksum = "09b6620799e7340ebd9968d2e0708eb82cf1971e9a16821e2091b6d6e475eed5" dependencies = [ - "bitflags 1.3.2", - "smallvec", - "thiserror", + "bitflags 2.9.4", + "rustc_version", ] [[package]] @@ -658,9 +663,9 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" dependencies = [ "percent-encoding", ] @@ -673,9 +678,9 @@ checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" [[package]] name = "futures" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -688,9 +693,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", @@ -698,15 +703,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", @@ -715,38 +720,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-macro" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures 0.1.31", "futures-channel", @@ -773,38 +778,50 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.12" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if 1.0.3", + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.3", "libc", - "wasi", + "r-efi", + "wasi 0.14.7+wasi-0.2.4", ] [[package]] name = "gimli" -version = "0.28.1" +version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" [[package]] name = "glob" -version = "0.3.1" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "globset" -version = "0.4.15" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15f1ce686646e7f1e19bf7d5533fe443a45dbfb990e00629110797578b42fb19" +checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5" dependencies = [ "aho-corasick", "bstr", "log", "regex-automata", - "regex-syntax 0.8.2", + "regex-syntax", "serde", ] @@ -834,7 +851,7 @@ dependencies = [ "schema", "serde", "strum", - "thiserror", + "thiserror 2.0.17", "tokio", ] @@ -856,7 +873,7 @@ dependencies = [ "relay-test-schema", "schema", "serde", - "thiserror", + "thiserror 2.0.17", "tokio", ] @@ -870,7 +887,7 @@ dependencies = [ "intern", "logos", "serde", - "thiserror", + "thiserror 2.0.17", "tokio", ] @@ -917,11 +934,11 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.24" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" +checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d" dependencies = [ - "bytes 1.5.0", + "bytes", "fnv", "futures-core", "futures-sink", @@ -930,21 +947,27 @@ dependencies = [ "indexmap", "slab", "tokio", - "tokio-util 0.7.10", + "tokio-util 0.7.16", "tracing", ] [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash", "allocator-api2", "serde", ] +[[package]] +name = "hashbrown" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" + [[package]] name = "heck" version = "0.4.1" @@ -966,12 +989,6 @@ dependencies = [ "libc", ] -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - [[package]] name = "hex" version = "0.4.3" @@ -980,11 +997,11 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "http" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ - "bytes 1.5.0", + "bytes", "fnv", "itoa", ] @@ -995,16 +1012,16 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ - "bytes 1.5.0", + "bytes", "http", "pin-project-lite", ] [[package]] name = "httparse" -version = "1.8.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "httpdate" @@ -1023,11 +1040,11 @@ dependencies = [ [[package]] name = "hyper" -version = "0.14.28" +version = "0.14.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" dependencies = [ - "bytes 1.5.0", + "bytes", "futures-channel", "futures-core", "futures-util", @@ -1038,7 +1055,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2", + "socket2 0.5.10", "tokio", "tower-service", "tracing", @@ -1051,34 +1068,132 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ - "bytes 1.5.0", + "bytes", "hyper", "native-tls", "tokio", "tokio-native-tls", ] +[[package]] +name = "icu_collections" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" + +[[package]] +name = "icu_properties" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "potential_utf", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" + +[[package]] +name = "icu_provider" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +dependencies = [ + "displaydoc", + "icu_locale_core", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + [[package]] name = "idna" -version = "0.5.0" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "icu_normalizer", + "icu_properties", ] [[package]] name = "indexmap" -version = "2.2.6" +version = "2.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" dependencies = [ "arbitrary", "equivalent", - "hashbrown", + "hashbrown 0.16.0", "rayon", "serde", + "serde_core", ] [[package]] @@ -1087,7 +1202,7 @@ version = "0.1.0" dependencies = [ "bincode", "fnv", - "hashbrown", + "hashbrown 0.14.5", "indexmap", "once_cell", "parking_lot", @@ -1113,16 +1228,21 @@ dependencies = [ [[package]] name = "inventory" -version = "0.3.15" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f958d3d68f4167080a18141e10381e7634563984a537f2a49a30fd8e53ac5767" +checksum = "bc61209c082fbeb19919bee74b176221b27223e27b65d781eb91af24eb1fb46e" +dependencies = [ + "rustversion", +] [[package]] -name = "iovec" -version = "0.1.4" +name = "io-uring" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" dependencies = [ + "bitflags 2.9.4", + "cfg-if 1.0.3", "libc", ] @@ -1134,34 +1254,26 @@ checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "itertools" -version = "0.12.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - -[[package]] -name = "itertools" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" dependencies = [ "either", ] [[package]] name = "itoa" -version = "1.0.10" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jobserver" -version = "0.1.28" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" dependencies = [ + "getrandom 0.3.3", "libc", ] @@ -1172,87 +1284,105 @@ dependencies = [ "serde", "serde_json", "tempfile", - "thiserror", + "thiserror 2.0.17", ] [[package]] name = "js-sys" -version = "0.3.68" +version = "0.3.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "406cda4b368d531c842222cf9d2600a9a4acce8d29423695379c6868a143a9ee" +checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305" dependencies = [ + "once_cell", "wasm-bindgen", ] [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.153" +version = "0.2.176" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" [[package]] name = "linux-raw-sys" -version = "0.4.13" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + +[[package]] +name = "litemap" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" dependencies = [ - "autocfg", "scopeguard", ] [[package]] name = "log" -version = "0.4.22" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" dependencies = [ "value-bag", ] [[package]] name = "logos" -version = "0.12.1" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf8b031682c67a8e3d5446840f9573eb7fe26efe7ec8d195c9ac4c0647c502f1" +checksum = "ff472f899b4ec2d99161c51f60ff7075eeb3097069a36050d8037a6325eb8154" dependencies = [ "logos-derive", ] [[package]] -name = "logos-derive" -version = "0.12.1" +name = "logos-codegen" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d849148dbaf9661a6151d1ca82b13bb4c4c128146a88d05253b38d4e2f496c" +checksum = "192a3a2b90b0c05b27a0b2c43eecdb7c415e29243acc3f89cc8247a5b693045c" dependencies = [ "beef", "fnv", + "lazy_static", "proc-macro2", "quote", - "regex-syntax 0.6.29", - "syn 1.0.109", + "regex-syntax", + "rustc_version", + "syn", +] + +[[package]] +name = "logos-derive" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "605d9697bcd5ef3a42d38efc51541aa3d6a4a25f7ab6d1ed0da5ac632a26b470" +dependencies = [ + "logos-codegen", ] [[package]] name = "lsp-server" -version = "0.7.6" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248f65b78f6db5d8e1b1604b4098a28b43d21a8eb1deeca22b1c421b276c7095" +checksum = "7d6ada348dbc2703cbe7637b2dda05cff84d3da2819c24abcb305dd613e0ba2e" dependencies = [ "crossbeam-channel", "log", "serde", + "serde_derive", "serde_json", ] @@ -1281,15 +1411,15 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.3", "digest", ] [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "memory_units" @@ -1297,34 +1427,42 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3" +[[package]] +name = "minicov" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27fe9f1cc3c22e1687f9446c2083c4c5fc7f0bcf1c7a86bdbded14985895b4b" +dependencies = [ + "cc", + "walkdir", +] + [[package]] name = "miniz_oxide" -version = "0.7.2" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ - "adler", + "adler2", ] [[package]] name = "mio" -version = "1.0.2" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ - "hermit-abi 0.3.9", "libc", - "wasi", - "windows-sys 0.52.0", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.59.0", ] [[package]] name = "native-tls" -version = "0.2.11" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" dependencies = [ - "lazy_static", "libc", "log", "openssl", @@ -1353,27 +1491,33 @@ dependencies = [ [[package]] name = "object" -version = "0.32.2" +version = "0.37.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.19.0" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" [[package]] name = "openssl" -version = "0.10.63" +version = "0.10.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15c9d69dd87a29568d4d017cfe8ec518706046a05184e5aea92d0af890b803c8" +checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" dependencies = [ - "bitflags 2.4.2", - "cfg-if 1.0.0", + "bitflags 2.9.4", + "cfg-if 1.0.3", "foreign-types", "libc", "once_cell", @@ -1389,29 +1533,29 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] name = "openssl-probe" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-src" -version = "300.2.2+3.2.1" +version = "300.5.3+3.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bbfad0063610ac26ee79f7484739e2b07555a75c42453b89263830b5c8103bc" +checksum = "dc6bad8cd0233b63971e232cc9c5e83039375b8586d2312f31fda85db8f888c2" dependencies = [ "cc", ] [[package]] name = "openssl-sys" -version = "0.9.99" +version = "0.9.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e1bf214306098e4832460f797824c05d25aacdf896f64a985fb0fd992454ae" +checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" dependencies = [ "cc", "libc", @@ -1422,9 +1566,9 @@ dependencies = [ [[package]] name = "ouroboros" -version = "0.18.4" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "944fa20996a25aded6b4795c6d63f10014a7a83f8be9828a11860b08c5fc4a67" +checksum = "1e0f050db9c44b97a94723127e6be766ac5c340c48f2c4bb3ffa11713744be59" dependencies = [ "aliasable", "ouroboros_macro", @@ -1433,23 +1577,22 @@ dependencies = [ [[package]] name = "ouroboros_macro" -version = "0.18.4" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39b0deead1528fd0e5947a8546a9642a9777c25f6e1e26f34c97b204bbb465bd" +checksum = "3c7028bdd3d43083f6d8d4d5187680d0d3560d54df4cc9d752005268b41e64d0" dependencies = [ "heck 0.4.1", - "itertools 0.12.1", "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.48", + "syn", ] [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" dependencies = [ "lock_api", "parking_lot_core", @@ -1457,28 +1600,28 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.3", "libc", "redox_syscall", "smallvec", - "windows-targets 0.48.5", + "windows-link", ] [[package]] name = "pathdiff" -version = "0.2.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" [[package]] name = "percent-encoding" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "persist-query" @@ -1488,15 +1631,15 @@ dependencies = [ "hyper-tls", "serde", "serde_json", - "thiserror", + "thiserror 2.0.17", "url", ] [[package]] name = "petgraph" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", "indexmap", @@ -1506,9 +1649,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" @@ -1518,9 +1661,18 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.29" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "potential_utf" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" +dependencies = [ + "zerovec", +] [[package]] name = "powerfmt" @@ -1530,15 +1682,18 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] @@ -1551,7 +1706,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", "version_check", "yansi", ] @@ -1564,13 +1719,19 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.35" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + [[package]] name = "rand" version = "0.8.5" @@ -1598,14 +1759,14 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.16", ] [[package]] name = "rayon" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" dependencies = [ "either", "rayon-core", @@ -1613,9 +1774,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.12.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" dependencies = [ "crossbeam-deque", "crossbeam-utils", @@ -1623,51 +1784,65 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.4.1" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.9.4", +] + +[[package]] +name = "ref-cast" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] name = "regex" -version = "1.10.3" +version = "1.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +checksum = "8b5288124840bee7b386bc413c487869b360b2b4ec421ea56425128692f2a82c" dependencies = [ "aho-corasick", "memchr", "regex-automata", - "regex-syntax 0.8.2", + "regex-syntax", ] [[package]] name = "regex-automata" -version = "0.4.5" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd" +checksum = "833eb9ce86d40ef33cb1306d8accf7bc8ec2bfea4355cbdebb3df68b40925cad" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.2", + "regex-syntax", ] [[package]] name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - -[[package]] -name = "regex-syntax" -version = "0.8.2" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" [[package]] name = "relay" -version = "18.2.0" +version = "20.1.1" dependencies = [ "clap", "common", @@ -1679,7 +1854,7 @@ dependencies = [ "schema", "schema-documentation", "simplelog", - "thiserror", + "thiserror 2.0.17", "tokio", ] @@ -1736,7 +1911,7 @@ dependencies = [ "extract-graphql", "fixture-tests", "fnv", - "futures 0.3.30", + "futures 0.3.31", "futures-util", "glob", "globset", @@ -1776,7 +1951,7 @@ dependencies = [ "sha1", "sha2", "signedsource", - "thiserror", + "thiserror 2.0.17", "tokio", "walkdir", "watchman_client", @@ -1845,7 +2020,7 @@ dependencies = [ "relay-test-schema", "schema", "serde", - "thiserror", + "thiserror 2.0.17", "tokio", ] @@ -1869,7 +2044,7 @@ dependencies = [ "graphql-text-printer", "graphql-watchman", "intern", - "itertools 0.13.0", + "itertools", "lazy_static", "log", "lsp-server", @@ -1940,7 +2115,7 @@ dependencies = [ "graphql-text-printer", "indexmap", "intern", - "itertools 0.13.0", + "itertools", "lazy_static", "parking_lot", "regex", @@ -1950,7 +2125,7 @@ dependencies = [ "rustc-hash", "schema", "serde", - "thiserror", + "thiserror 2.0.17", "tokio", ] @@ -1967,7 +2142,7 @@ dependencies = [ "graphql-test-helpers", "indexmap", "intern", - "itertools 0.13.0", + "itertools", "lazy_static", "log", "regex", @@ -1994,40 +2169,49 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" [[package]] name = "rustc-hash" -version = "1.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] [[package]] name = "rustix" -version = "0.38.31" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea3e1a662af26cd7a3ba09c0297a31af215563ecf42817c98df621387f4e949" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ - "bitflags 2.4.2", + "bitflags 2.9.4", "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] name = "rustversion" -version = "1.0.14" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" -version = "1.0.16" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "same-file" @@ -2040,11 +2224,11 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.23" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -2065,7 +2249,7 @@ dependencies = [ "schema-flatbuffer", "serde", "strsim 0.10.0", - "thiserror", + "thiserror 2.0.17", "tokio", ] @@ -2106,7 +2290,7 @@ dependencies = [ "fixture-tests", "fnv", "intern", - "itertools 0.13.0", + "itertools", "rayon", "schema", "tokio", @@ -2127,18 +2311,19 @@ dependencies = [ "regex", "schema", "serde", - "thiserror", + "thiserror 2.0.17", "tokio", ] [[package]] name = "schemars" -version = "0.8.21" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09c024468a378b7e36765cd36702b7a90cc3cba11654f6685c8f233408e89e92" +checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0" dependencies = [ "dyn-clone", "indexmap", + "ref-cast", "schemars_derive", "serde", "serde_json", @@ -2146,22 +2331,16 @@ dependencies = [ [[package]] name = "schemars_derive" -version = "0.8.21" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1eee588578aff73f856ab961cd2f79e36bc45d7ded33a7562adba4667aecc0e" +checksum = "33d020396d1d138dc19f1165df7545479dcd58d93810dc5d646a16e55abefa80" dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.48", + "syn", ] -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - [[package]] name = "scopeguard" version = "1.2.0" @@ -2170,11 +2349,11 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "security-framework" -version = "2.9.2" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.9.4", "core-foundation", "core-foundation-sys", "libc", @@ -2183,54 +2362,72 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.9.1" +version = "2.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" dependencies = [ "core-foundation-sys", "libc", ] +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + [[package]] name = "serde" -version = "1.0.196" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ + "serde_core", "serde_derive", ] [[package]] name = "serde_bser" -version = "0.3.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b929ea725591083cbca8b8ea178ed6efc918eccd40b784e199ce88967104199" +checksum = "a56b4bcc15e42e5b5ae16c6f75582bef80d36c6ffe2c03b1b5317754b38f8717" dependencies = [ "anyhow", "byteorder", - "bytes 0.4.12", + "bytes", "serde", - "thiserror", + "serde_bytes", + "thiserror 1.0.69", ] [[package]] name = "serde_bytes" -version = "0.11.14" +version = "0.11.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b8497c313fd43ab992087548117643f6fcd935cbf36f176ffda0aacf9591734" +checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" dependencies = [ "serde", + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.196" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] @@ -2241,7 +2438,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] @@ -2255,25 +2452,26 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.132" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ "itoa", "memchr", "ryu", "serde", + "serde_core", ] [[package]] name = "serde_repr" -version = "0.1.18" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] @@ -2282,27 +2480,33 @@ version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.3", "cpufeatures", "digest", ] [[package]] name = "sha2" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.3", "cpufeatures", "digest", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "signal-hook-registry" -version = "1.4.1" +version = "1.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" dependencies = [ "libc", ] @@ -2330,32 +2534,45 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.9" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "smallvec" -version = "1.13.1" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" dependencies = [ "serde", ] [[package]] name = "socket2" -version = "0.5.5" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", +] + +[[package]] +name = "socket2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +dependencies = [ + "libc", + "windows-sys 0.59.0", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "static_assertions" version = "1.1.0" @@ -2376,37 +2593,36 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "strum" -version = "0.26.2" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" dependencies = [ "strum_macros", ] [[package]] name = "strum_macros" -version = "0.26.2" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" dependencies = [ - "heck 0.4.1", + "heck 0.5.0", "proc-macro2", "quote", - "rustversion", - "syn 2.0.48", + "syn", ] [[package]] name = "sval" -version = "2.13.2" +version = "2.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6dc0f9830c49db20e73273ffae9b5240f63c42e515af1da1fceefb69fceafd8" +checksum = "d94c4464e595f0284970fd9c7e9013804d035d4a61ab74b113242c874c05814d" [[package]] name = "sval_buffer" -version = "2.13.2" +version = "2.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "429922f7ad43c0ef8fd7309e14d750e38899e32eb7e8da656ea169dd28ee212f" +checksum = "a0f46e34b20a39e6a2bf02b926983149b3af6609fd1ee8a6e63f6f340f3e2164" dependencies = [ "sval", "sval_ref", @@ -2414,18 +2630,18 @@ dependencies = [ [[package]] name = "sval_dynamic" -version = "2.13.2" +version = "2.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f16ff5d839396c11a30019b659b0976348f3803db0626f736764c473b50ff4" +checksum = "03d0970e53c92ab5381d3b2db1828da8af945954d4234225f6dd9c3afbcef3f5" dependencies = [ "sval", ] [[package]] name = "sval_fmt" -version = "2.13.2" +version = "2.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c01c27a80b6151b0557f9ccbe89c11db571dc5f68113690c1e028d7e974bae94" +checksum = "43e5e6e1613e1e7fc2e1a9fdd709622e54c122ceb067a60d170d75efd491a839" dependencies = [ "itoa", "ryu", @@ -2434,9 +2650,9 @@ dependencies = [ [[package]] name = "sval_json" -version = "2.13.2" +version = "2.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0deef63c70da622b2a8069d8600cf4b05396459e665862e7bdb290fd6cf3f155" +checksum = "aec382f7bfa6e367b23c9611f129b94eb7daaf3d8fae45a8d0a0211eb4d4c8e6" dependencies = [ "itoa", "ryu", @@ -2445,9 +2661,9 @@ dependencies = [ [[package]] name = "sval_nested" -version = "2.13.2" +version = "2.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a39ce5976ae1feb814c35d290cf7cf8cd4f045782fe1548d6bc32e21f6156e9f" +checksum = "3049d0f99ce6297f8f7d9953b35a0103b7584d8f638de40e64edb7105fa578ae" dependencies = [ "sval", "sval_buffer", @@ -2456,29 +2672,29 @@ dependencies = [ [[package]] name = "sval_ref" -version = "2.13.2" +version = "2.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb7c6ee3751795a728bc9316a092023529ffea1783499afbc5c66f5fabebb1fa" +checksum = "f88913e77506085c0a8bf6912bb6558591a960faf5317df6c1d9b227224ca6e1" dependencies = [ "sval", ] [[package]] name = "sval_serde" -version = "2.13.2" +version = "2.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a5572d0321b68109a343634e3a5d576bf131b82180c6c442dee06349dfc652a" +checksum = "f579fd7254f4be6cd7b450034f856b78523404655848789c451bacc6aa8b387d" dependencies = [ - "serde", + "serde_core", "sval", "sval_nested", ] [[package]] name = "syn" -version = "1.0.109" +version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", @@ -2486,72 +2702,93 @@ dependencies = [ ] [[package]] -name = "syn" -version = "2.0.48" +name = "synstructure" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "unicode-ident", + "syn", ] [[package]] name = "tempfile" -version = "3.10.0" +version = "3.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a365e8cd18e44762ef95d87f284f4b5cd04107fec2ff3052bd6a3e6069669e67" +checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" dependencies = [ - "cfg-if 1.0.0", "fastrand", + "getrandom 0.3.3", + "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] name = "termcolor" -version = "1.1.3" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ "winapi-util", ] [[package]] name = "terminal_size" -version = "0.4.0" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f599bd7ca042cfdf8f4512b277c02ba102247820f9d9d4a9f521f496751a6ef" +checksum = "60b8cb979cb11c32ce1603f8137b22262a9d131aaa5c37b5678025f22b8becd0" dependencies = [ "rustix", - "windows-sys 0.59.0", + "windows-sys 0.60.2", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", ] [[package]] name = "thiserror" -version = "1.0.64" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +dependencies = [ + "thiserror-impl 2.0.17", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ - "thiserror-impl", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "thiserror-impl" -version = "1.0.64" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] name = "time" -version = "0.3.36" +version = "0.3.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" dependencies = [ "deranged", "itoa", @@ -2566,63 +2803,60 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.2" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" [[package]] name = "time-macros" -version = "0.2.18" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" dependencies = [ "num-conv", "time-core", ] [[package]] -name = "tinyvec" -version = "1.6.0" +name = "tinystr" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" dependencies = [ - "tinyvec_macros", + "displaydoc", + "zerovec", ] -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - [[package]] name = "tokio" -version = "1.41.0" +version = "1.47.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145f3413504347a2be84393cc8a7d2fb4d863b375909ea59f2158261aa258bbb" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" dependencies = [ "backtrace", - "bytes 1.5.0", + "bytes", + "io-uring", "libc", "mio", "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2", + "slab", + "socket2 0.6.0", "tokio-macros", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "tokio-macros" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] @@ -2641,7 +2875,7 @@ version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36943ee01a6d67977dd3f84a5a1d2efeb4ada3a1ae771cadfaa535d9d9fc6507" dependencies = [ - "bytes 1.5.0", + "bytes", "futures-core", "futures-io", "futures-sink", @@ -2653,29 +2887,28 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.10" +version = "0.7.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" dependencies = [ - "bytes 1.5.0", + "bytes", "futures-core", "futures-sink", "pin-project-lite", "tokio", - "tracing", ] [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "pin-project-lite", "tracing-core", @@ -2683,9 +2916,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", ] @@ -2696,17 +2929,23 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + [[package]] name = "typenum" -version = "1.17.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "typetag" -version = "0.2.15" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c43148481c7b66502c48f35b8eef38b6ccdc7a9f04bd4cc294226d901ccc9bc7" +checksum = "be2212c8a9b9bcfca32024de14998494cf9a5dfa59ea1b829de98bac374b86bf" dependencies = [ "erased-serde", "inventory", @@ -2717,56 +2956,38 @@ dependencies = [ [[package]] name = "typetag-impl" -version = "0.2.15" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291db8a81af4840c10d636e047cac67664e343be44e24dfdbd1492df9a5d3390" +checksum = "27a7a9b72ba121f6f1f6c3632b85604cac41aedb5ddc70accbebb6cac83de846" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] name = "unicase" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" -dependencies = [ - "version_check", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.15" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] +checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" [[package]] name = "unicode-width" -version = "0.2.0" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" [[package]] name = "url" -version = "2.5.2" +version = "2.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" dependencies = [ "form_urlencoded", "idna", @@ -2774,6 +2995,12 @@ dependencies = [ "serde", ] +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.2" @@ -2782,9 +3009,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "value-bag" -version = "1.7.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "126e423afe2dd9ac52142e7e9d5ce4135d7e13776c529d27fd6bc49f19e3280b" +checksum = "943ce29a8a743eb10d6082545d861b24f9d1b160b7d741e0f2cdf726bec909c5" dependencies = [ "value-bag-serde1", "value-bag-sval2", @@ -2792,9 +3019,9 @@ dependencies = [ [[package]] name = "value-bag-serde1" -version = "1.10.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bb773bd36fd59c7ca6e336c94454d9c66386416734817927ac93d81cb3c5b0b" +checksum = "35540706617d373b118d550d41f5dfe0b78a0c195dc13c6815e92e2638432306" dependencies = [ "erased-serde", "serde", @@ -2803,9 +3030,9 @@ dependencies = [ [[package]] name = "value-bag-sval2" -version = "1.10.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53a916a702cac43a88694c97657d449775667bcd14b70419441d05b7fea4a83a" +checksum = "6fe7e140a2658cc16f7ee7a86e413e803fc8f9b5127adc8755c19f9fefa63a52" dependencies = [ "sval", "sval_buffer", @@ -2824,15 +3051,15 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "walkdir" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", @@ -2849,52 +3076,73 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasi" +version = "0.14.7+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" +dependencies = [ + "wasip2", +] + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", +] [[package]] name = "wasm-bindgen" -version = "0.2.91" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1e124130aee3fb58c5bdd6b639a0509486b0338acaaae0c84a5124b0f588b7f" +checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.3", + "once_cell", + "rustversion", "wasm-bindgen-macro", + "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.91" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9e7e1900c352b609c8488ad12639a311045f40a35491fb69ba8c12f758af70b" +checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", - "syn 2.0.48", + "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.41" +version = "0.4.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877b9c3f61ceea0e56331985743b13f3d25c406a7098d45180fb5f09bc19ed97" +checksum = "7e038d41e478cc73bae0ff9b36c60cff1c98b8f38f8d7e8061e79ee63608ac5c" dependencies = [ - "cfg-if 1.0.0", + "cfg-if 1.0.3", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.91" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b30af9e2d358182b5c7449424f017eba305ed32a7010509ede96cdc4696c46ed" +checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2902,32 +3150,34 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.91" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66" +checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.91" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f186bd2dcf04330886ce82d6f33dd75a7bfcf69ecf5763b89fcde53b6ac9838" +checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1" +dependencies = [ + "unicode-ident", +] [[package]] name = "wasm-bindgen-test" -version = "0.3.41" +version = "0.3.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143ddeb4f833e2ed0d252e618986e18bfc7b0e52f2d28d77d05b2f045dd8eb61" +checksum = "4e381134e148c1062f965a42ed1f5ee933eef2927c3f70d1812158f711d39865" dependencies = [ - "console_error_panic_hook", "js-sys", - "scoped-tls", + "minicov", "wasm-bindgen", "wasm-bindgen-futures", "wasm-bindgen-test-macro", @@ -2935,28 +3185,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-test-macro" -version = "0.3.41" +version = "0.3.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5211b7550606857312bba1d978a8ec75692eae187becc5e680444fffc5e6f89" +checksum = "b673bca3298fe582aeef8352330ecbad91849f85090805582400850f8270a2e8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] name = "watchman_client" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "839fea2d85719bb69089290d7970bba2131f544448db8f990ea75813c30775ca" +checksum = "88bc4c9bb443a7aae10d4fa7807bffc397805315e2305288c90c80e2f66cfb52" dependencies = [ "anyhow", - "bytes 1.5.0", - "futures 0.3.30", + "bytes", + "futures 0.3.31", "maplit", "serde", "serde_bser", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-util 0.6.10", "winapi", @@ -2964,9 +3214,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.68" +version = "0.3.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96565907687f7aceb35bc5fc03770a8a0471d82e479f25832f54a0e3f4b28446" +checksum = "9367c417a924a74cae129e6a2ae3b47fabb1f8995595ab474029da749a8be120" dependencies = [ "js-sys", "wasm-bindgen", @@ -3002,11 +3252,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.6" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "winapi", + "windows-sys 0.61.2", ] [[package]] @@ -3016,13 +3266,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows-sys" -version = "0.48.0" +name = "windows-link" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-sys" @@ -3043,18 +3290,21 @@ dependencies = [ ] [[package]] -name = "windows-targets" -version = "0.48.5" +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", + "windows-link", ] [[package]] @@ -3066,7 +3316,7 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", @@ -3074,10 +3324,21 @@ dependencies = [ ] [[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" +name = "windows-targets" +version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] [[package]] name = "windows_aarch64_gnullvm" @@ -3086,10 +3347,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" +name = "windows_aarch64_gnullvm" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" [[package]] name = "windows_aarch64_msvc" @@ -3098,10 +3359,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] -name = "windows_i686_gnu" -version = "0.48.5" +name = "windows_aarch64_msvc" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" [[package]] name = "windows_i686_gnu" @@ -3109,6 +3370,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" @@ -3116,10 +3383,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] -name = "windows_i686_msvc" -version = "0.48.5" +name = "windows_i686_gnullvm" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" [[package]] name = "windows_i686_msvc" @@ -3128,10 +3395,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" +name = "windows_i686_msvc" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" [[package]] name = "windows_x86_64_gnu" @@ -3140,10 +3407,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" +name = "windows_x86_64_gnu" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" [[package]] name = "windows_x86_64_gnullvm" @@ -3152,10 +3419,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" +name = "windows_x86_64_gnullvm" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" [[package]] name = "windows_x86_64_msvc" @@ -3163,55 +3430,151 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + +[[package]] +name = "writeable" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" + [[package]] name = "yansi" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" +[[package]] +name = "yoke" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + [[package]] name = "zerocopy" -version = "0.7.34" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.34" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] name = "zstd" -version = "0.13.0" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bffb3309596d527cfcba7dfc6ed6052f1d39dfbd7c867aa2e865e4a449c10110" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" dependencies = [ "zstd-safe", ] [[package]] name = "zstd-safe" -version = "7.0.0" +version = "7.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43747c7422e2924c11144d5229878b98180ef8b06cca4ab5af37afc8a8d8ea3e" +checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" dependencies = [ "zstd-sys", ] [[package]] name = "zstd-sys" -version = "2.0.9+zstd.1.5.5" +version = "2.0.16+zstd.1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" +checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" dependencies = [ "cc", "pkg-config", diff --git a/compiler/crates/common/Cargo.toml b/compiler/crates/common/Cargo.toml index df6d4848f7100..18befc1ebebab 100644 --- a/compiler/crates/common/Cargo.toml +++ b/compiler/crates/common/Cargo.toml @@ -4,19 +4,19 @@ name = "common" version = "0.0.0" authors = ["Facebook"] -edition = "2021" +edition = "2024" repository = "https://github.com/facebook/relay" license = "MIT" [dependencies] colored = "2.1.0" -indexmap = { version = "2.2.6", features = ["arbitrary", "rayon", "serde"] } +indexmap = { version = "2.9.0", features = ["arbitrary", "rayon", "serde"] } intern = { path = "../intern" } -log = { version = "0.4.22", features = ["kv_unstable"] } +log = { version = "0.4.27", features = ["kv_unstable", "kv_unstable_std"] } lsp-types = "0.94.1" md-5 = "0.10" rayon = "1.9.0" -schemars = { version = "0.8.21", features = ["indexmap2"] } -serde = { version = "1.0.185", features = ["derive", "rc"] } -serde_json = { version = "1.0.132", features = ["float_roundtrip", "unbounded_depth"] } +schemars = { version = "1.0.4", features = ["indexmap2"] } +serde = { version = "1.0.219", features = ["derive", "rc"] } +serde_json = { version = "1.0.140", features = ["alloc", "float_roundtrip", "unbounded_depth"] } typetag = "0.2.15" diff --git a/compiler/crates/common/src/diagnostic.rs b/compiler/crates/common/src/diagnostic.rs index 187d766f2b04c..b0a8cb533674d 100644 --- a/compiler/crates/common/src/diagnostic.rs +++ b/compiler/crates/common/src/diagnostic.rs @@ -184,7 +184,7 @@ impl Diagnostic { self } - pub fn message(&self) -> &impl DiagnosticDisplay { + pub fn message(&self) -> &(impl DiagnosticDisplay + use<>) { &self.0.message } @@ -192,7 +192,7 @@ impl Diagnostic { self.0.location } - pub fn get_data(&self) -> &[impl DiagnosticDisplay] { + pub fn get_data(&self) -> &[impl DiagnosticDisplay + use<>] { &self.0.data } diff --git a/compiler/crates/common/src/feature_flags.rs b/compiler/crates/common/src/feature_flags.rs index 8e3943e4c5022..9cc2d8fc4b6e1 100644 --- a/compiler/crates/common/src/feature_flags.rs +++ b/compiler/crates/common/src/feature_flags.rs @@ -10,21 +10,34 @@ use std::fmt::Formatter; use std::fmt::Result as FmtResult; use indexmap::IndexSet; -use intern::string_key::StringKey; use intern::Lookup; +use intern::string_key::StringKey; use schemars::JsonSchema; use serde::Deserialize; use serde::Serialize; use crate::Rollout; +use crate::rollout::RolloutRange; #[derive(Default, Debug, Serialize, Deserialize, Clone, JsonSchema)] #[serde(deny_unknown_fields)] pub struct FeatureFlags { #[serde(default)] - // Enable returning interfaces from Relay Resolvers without @outputType + /// Enable returning interfaces from Relay Resolvers without @outputType pub relay_resolver_enable_interface_output_type: FeatureFlag, + #[serde(default)] + /// @outputType resolvers are a discontinued experimental feature. This flag + /// allows users to allowlist old uses of this feature while they work to + /// remove them. Weak types (types without an `id` field) returned by a Relay + /// Resolver should be limited to types defined using `@RelayResolver` with `@weak`. + /// + /// If using the "limited" feature flag variant, users can allowlist a + /// specific list of field names. + /// + /// https://relay.dev/docs/next/guides/relay-resolvers/defining-types/#defining-a-weak-type + pub allow_output_type_resolvers: FeatureFlag, + /// For now, this also disallows fragments with variable definitions /// This also makes @module to opt in using @no_inline internally /// NOTE that the presence of a fragment in this list only controls whether a fragment is *allowed* to @@ -49,7 +62,7 @@ pub struct FeatureFlags { /// Enforce that you must add `@alias` to a fragment if it may not match, /// due to type mismatch or `@skip`/`@include` - #[serde(default)] + #[serde(default = "enabled_feature_flag")] pub enforce_fragment_alias_where_ambiguous: FeatureFlag, /// Print queries in compact form @@ -168,7 +181,7 @@ pub struct FeatureFlags { pub legacy_include_path_in_required_reader_nodes: FeatureFlag, } -#[derive(Debug, Deserialize, Clone, Serialize, Default, JsonSchema)] +#[derive(Debug, serde::Deserialize, Clone, Serialize, Default, JsonSchema)] #[serde(tag = "kind", rename_all = "lowercase")] pub enum FeatureFlag { /// Fully disabled: developers may not use this feature @@ -183,6 +196,14 @@ pub enum FeatureFlag { /// Partially enabled: used for gradual rollout of the feature Rollout { rollout: Rollout }, + + /// Partially enabled: used for gradual rollout of the feature + RolloutRange { rollout: RolloutRange }, +} + +/// Used for making feature flags enabled by default via Serde's default attribute. +fn enabled_feature_flag() -> FeatureFlag { + FeatureFlag::Enabled } impl FeatureFlag { @@ -191,6 +212,7 @@ impl FeatureFlag { FeatureFlag::Enabled => true, FeatureFlag::Limited { allowlist } => allowlist.contains(&name), FeatureFlag::Rollout { rollout } => rollout.check(name.lookup()), + FeatureFlag::RolloutRange { rollout } => rollout.check(name.lookup()), FeatureFlag::Disabled => false, } } @@ -211,6 +233,7 @@ impl Display for FeatureFlag { f.write_str(&items.join(", ")) } FeatureFlag::Rollout { rollout } => write!(f, "Rollout: {:#?}", rollout), + FeatureFlag::RolloutRange { rollout } => write!(f, "RolloutRange: {:#?}", rollout), } } } diff --git a/compiler/crates/common/src/lib.rs b/compiler/crates/common/src/lib.rs index b2f4b8b066a8f..2af2aefc1039a 100644 --- a/compiler/crates/common/src/lib.rs +++ b/compiler/crates/common/src/lib.rs @@ -8,7 +8,9 @@ #![deny(warnings)] #![deny(rust_2018_idioms)] #![deny(clippy::all)] - +// This warning is triggered by code generated by derive macro typetag-impl-0.2.15 +// Supressing for now to get Relay building +#![allow(non_local_definitions)] mod console_logger; mod diagnostic; mod diagnostic_check; @@ -23,11 +25,9 @@ mod span; pub mod sync; mod text_source; -pub use console_logger::print_time; pub use console_logger::ConsoleLogEvent; pub use console_logger::ConsoleLogger; -pub use diagnostic::diagnostics_result; -pub use diagnostic::get_diagnostics_data; +pub use console_logger::print_time; pub use diagnostic::Diagnostic; pub use diagnostic::DiagnosticDisplay; pub use diagnostic::DiagnosticRelatedInformation; @@ -35,10 +35,12 @@ pub use diagnostic::Diagnostics; pub use diagnostic::DiagnosticsResult; pub use diagnostic::WithDiagnosticData; pub use diagnostic::WithDiagnostics; -pub use diagnostic_check::escalate_and_check; +pub use diagnostic::diagnostics_result; +pub use diagnostic::get_diagnostics_data; pub use diagnostic_check::CriticalDiagnostics; pub use diagnostic_check::DiagnosticCheck; pub use diagnostic_check::StableDiagnostics; +pub use diagnostic_check::escalate_and_check; pub use feature_flags::FeatureFlag; pub use feature_flags::FeatureFlags; pub use location::Location; @@ -62,5 +64,6 @@ pub use perf_logger::PerfLogEvent; pub use perf_logger::PerfLogger; pub use pointer_address::PointerAddress; pub use rollout::Rollout; +pub use rollout::RolloutRange; pub use span::Span; pub use text_source::TextSource; diff --git a/compiler/crates/common/src/location.rs b/compiler/crates/common/src/location.rs index 60d03a43aecd4..95efd4a762715 100644 --- a/compiler/crates/common/src/location.rs +++ b/compiler/crates/common/src/location.rs @@ -9,9 +9,9 @@ use core::cmp::Ordering; use std::fmt; use std::path::PathBuf; +use intern::Lookup; use intern::string_key::Intern; use intern::string_key::StringKey; -use intern::Lookup; use crate::span::Span; diff --git a/compiler/crates/common/src/named_item.rs b/compiler/crates/common/src/named_item.rs index 0144e213b0877..6b748f48581a6 100644 --- a/compiler/crates/common/src/named_item.rs +++ b/compiler/crates/common/src/named_item.rs @@ -8,10 +8,10 @@ use std::fmt; use std::str::FromStr; +use intern::Lookup; use intern::impl_lookup; use intern::string_key::Intern; use intern::string_key::StringKey; -use intern::Lookup; use schemars::JsonSchema; use serde::Deserialize; use serde::Serialize; @@ -96,7 +96,8 @@ impl fmt::Display for ArgumentName { PartialOrd, Hash, Serialize, - Deserialize + Deserialize, + JsonSchema )] pub struct ScalarName(pub StringKey); diff --git a/compiler/crates/common/src/rollout.rs b/compiler/crates/common/src/rollout.rs index 92afe103cd1da..f69c4d8fb1903 100644 --- a/compiler/crates/common/src/rollout.rs +++ b/compiler/crates/common/src/rollout.rs @@ -31,3 +31,23 @@ impl Rollout { } } } + +/// A utility to enable gradual rollout of large codegen changes. Allows you to +/// specify a range of percentages to rollout. +#[derive(Debug, Serialize, Deserialize, Clone, Copy, JsonSchema)] +pub struct RolloutRange { + pub start: u8, + pub end: u8, +} + +impl RolloutRange { + /// Checks some key deterministically and passes on average the given + /// percentage of the rollout. + /// A typical key to pass in could be the fragment or operation name. + pub fn check(&self, key: impl AsRef<[u8]>) -> bool { + let hash = Md5::digest(key.as_ref()); + let hash: u16 = ((hash[1] as u16) << 8) | (hash[0] as u16); + let percent = hash % 100; + (percent) <= (self.end as u16) && (percent) >= (self.start as u16) + } +} diff --git a/compiler/crates/dependency-analyzer/Cargo.toml b/compiler/crates/dependency-analyzer/Cargo.toml index aabc37ba9337a..a93de3fc917b4 100644 --- a/compiler/crates/dependency-analyzer/Cargo.toml +++ b/compiler/crates/dependency-analyzer/Cargo.toml @@ -4,7 +4,7 @@ name = "dependency-analyzer" version = "0.0.0" authors = ["Facebook"] -edition = "2021" +edition = "2024" repository = "https://github.com/facebook/relay" license = "MIT" @@ -21,13 +21,13 @@ common = { path = "../common" } graphql-ir = { path = "../graphql-ir" } graphql-syntax = { path = "../graphql-syntax" } relay-transforms = { path = "../relay-transforms" } -rustc-hash = "1.1.0" +rustc-hash = "2.1.1" schema = { path = "../schema" } schema-diff = { path = "../schema-diff" } -serde = { version = "1.0.185", features = ["derive", "rc"] } +serde = { version = "1.0.219", features = ["derive", "rc"] } [dev-dependencies] fixture-tests = { path = "../fixture-tests" } intern = { path = "../intern" } relay-test-schema = { path = "../relay-test-schema" } -tokio = { version = "1.41.0", features = ["full", "test-util", "tracing"] } +tokio = { version = "1.46.1", features = ["full", "test-util", "tracing"] } diff --git a/compiler/crates/dependency-analyzer/src/ir.rs b/compiler/crates/dependency-analyzer/src/ir.rs index 70d8780b4ab71..bf0f09d488976 100644 --- a/compiler/crates/dependency-analyzer/src/ir.rs +++ b/compiler/crates/dependency-analyzer/src/ir.rs @@ -5,8 +5,8 @@ * LICENSE file in the root directory of this source tree. */ -use std::collections::hash_map::Entry; use std::collections::HashMap; +use std::collections::hash_map::Entry; use std::fmt; use common::PerfLogEvent; diff --git a/compiler/crates/dependency-analyzer/src/lib.rs b/compiler/crates/dependency-analyzer/src/lib.rs index f34d5b28d220a..18a88cafa5d04 100644 --- a/compiler/crates/dependency-analyzer/src/lib.rs +++ b/compiler/crates/dependency-analyzer/src/lib.rs @@ -15,12 +15,12 @@ mod ir; mod minimized_executable; mod schema_change_analyzer; +pub use ast::ReachableAst; pub use ast::get_definition_references; pub use ast::get_reachable_ast; -pub use ast::ReachableAst; -pub use ir::get_ir_definition_references; -pub use ir::get_reachable_ir; pub use ir::ExecutableDefinitionNameMap; pub use ir::ExecutableDefinitionNameSet; pub use ir::ExecutableDefinitionNameVec; +pub use ir::get_ir_definition_references; +pub use ir::get_reachable_ir; pub use minimized_executable::MinProgram; diff --git a/compiler/crates/dependency-analyzer/src/schema_change_analyzer.rs b/compiler/crates/dependency-analyzer/src/schema_change_analyzer.rs index ab395f1fce2a4..a47ec05c36086 100644 --- a/compiler/crates/dependency-analyzer/src/schema_change_analyzer.rs +++ b/compiler/crates/dependency-analyzer/src/schema_change_analyzer.rs @@ -9,9 +9,9 @@ use std::collections::HashSet; use graphql_ir::*; use rustc_hash::FxHashSet; -use schema::definitions::Type; use schema::SDLSchema; use schema::Schema; +use schema::definitions::Type; use schema_diff::check::IncrementalBuildSchemaChange; use crate::ExecutableDefinitionNameSet; diff --git a/compiler/crates/dependency-analyzer/tests/ast.rs b/compiler/crates/dependency-analyzer/tests/ast.rs index 484fe7347ac94..ba220fd160482 100644 --- a/compiler/crates/dependency-analyzer/tests/ast.rs +++ b/compiler/crates/dependency-analyzer/tests/ast.rs @@ -6,8 +6,8 @@ */ use common::SourceLocationKey; -use dependency_analyzer::get_reachable_ast; use dependency_analyzer::ReachableAst; +use dependency_analyzer::get_reachable_ast; use fixture_tests::Fixture; use graphql_syntax::*; use intern::Lookup; diff --git a/compiler/crates/docblock-shared/Cargo.toml b/compiler/crates/docblock-shared/Cargo.toml index 6b28e5837e2d3..05cd13c88aa1b 100644 --- a/compiler/crates/docblock-shared/Cargo.toml +++ b/compiler/crates/docblock-shared/Cargo.toml @@ -4,14 +4,14 @@ name = "docblock-shared" version = "0.0.0" authors = ["Facebook"] -edition = "2021" +edition = "2024" repository = "https://github.com/facebook/relay" license = "MIT" [dependencies] common = { path = "../common" } -hex = "0.4.3" +hex = { version = "0.4.3", features = ["alloc"] } intern = { path = "../intern" } -lazy_static = "1.4" +lazy_static = "1.5" md-5 = "0.10" -serde = { version = "1.0.185", features = ["derive", "rc"] } +serde = { version = "1.0.219", features = ["derive", "rc"] } diff --git a/compiler/crates/docblock-shared/src/lib.rs b/compiler/crates/docblock-shared/src/lib.rs index 931b9677965f7..7652c5119a362 100644 --- a/compiler/crates/docblock-shared/src/lib.rs +++ b/compiler/crates/docblock-shared/src/lib.rs @@ -38,6 +38,9 @@ lazy_static! { /// a Relay Resolver model. pub static ref RELAY_RESOLVER_MODEL_DIRECTIVE_NAME: DirectiveName = DirectiveName("__RelayResolverModel".intern()); + /// A field directive which indicates that the field is the generated ID field for a model type. + pub static ref RELAY_RESOLVER_MODEL_GENERATED_ID_FIELD_DIRECTIVE_NAME: DirectiveName = + DirectiveName("__RelayResolverModelGeneratedIDField".intern()); /// If a field or model type has a @relay_resolver directive (see above) /// this argument name is used to track its @rootFragment (if any). pub static ref FRAGMENT_KEY_ARGUMENT_NAME: ArgumentName = @@ -46,6 +49,10 @@ lazy_static! { /// has validated that its Flow/TypeScript type matches the GraphQL type. pub static ref TYPE_CONFIRMED_ARGUMENT_NAME: ArgumentName = ArgumentName("type_confirmed".intern()); + /// Indicates that the resolver is just a property lookup on the underlying model (and we need to generate + /// code to do this lookup) + pub static ref RESOLVER_PROPERTY_LOOKUP_NAME: ArgumentName = + ArgumentName("property_lookup_name".intern()); /// "Weak" resolver types are types which are backed by a JS model value, but which don't have a stable /// identity. Types in the generated schema are annotated with a directive using this name to signal /// to the rest of Relay that they are backed by a "weak" Relay Resolver model. diff --git a/compiler/crates/docblock-syntax/Cargo.toml b/compiler/crates/docblock-syntax/Cargo.toml index 7882c978138f2..0e338bbaee893 100644 --- a/compiler/crates/docblock-syntax/Cargo.toml +++ b/compiler/crates/docblock-syntax/Cargo.toml @@ -4,7 +4,7 @@ name = "docblock-syntax" version = "0.0.0" authors = ["Facebook"] -edition = "2021" +edition = "2024" repository = "https://github.com/facebook/relay" license = "MIT" @@ -16,10 +16,10 @@ path = "tests/parse_test.rs" common = { path = "../common" } docblock-shared = { path = "../docblock-shared" } intern = { path = "../intern" } -serde = { version = "1.0.185", features = ["derive", "rc"] } -thiserror = "1.0.64" +serde = { version = "1.0.219", features = ["derive", "rc"] } +thiserror = "2.0.12" [dev-dependencies] fixture-tests = { path = "../fixture-tests" } graphql-test-helpers = { path = "../graphql-test-helpers" } -tokio = { version = "1.41.0", features = ["full", "test-util", "tracing"] } +tokio = { version = "1.46.1", features = ["full", "test-util", "tracing"] } diff --git a/compiler/crates/docblock-syntax/src/lib.rs b/compiler/crates/docblock-syntax/src/lib.rs index 146209356c6c8..ecc6496b83bba 100644 --- a/compiler/crates/docblock-syntax/src/lib.rs +++ b/compiler/crates/docblock-syntax/src/lib.rs @@ -238,7 +238,11 @@ impl<'a> DocblockParser<'a> { /// Read until the end of the line. fn parse_free_text_line(&mut self) -> ParseResult { let start = self.offset; - let free_text = self.take_while(|c| c != &'\n'); + let mut free_text: String = self.take_while(|c| c != &'\n'); + // Handle CRLF by stripping `\r` suffix. + if free_text.ends_with('\r') { + free_text.pop(); + } let end = self.offset; Ok(SpanString::new(Span::new(start, end), free_text)) } diff --git a/compiler/crates/errors/Cargo.toml b/compiler/crates/errors/Cargo.toml index 7a450ec16ea0c..b400444a54145 100644 --- a/compiler/crates/errors/Cargo.toml +++ b/compiler/crates/errors/Cargo.toml @@ -4,7 +4,7 @@ name = "errors" version = "0.0.0" authors = ["Facebook"] -edition = "2021" +edition = "2024" repository = "https://github.com/facebook/relay" license = "MIT" diff --git a/compiler/crates/errors/src/error_combinators.rs b/compiler/crates/errors/src/error_combinators.rs index f13a1f7163531..1c579cb125d93 100644 --- a/compiler/crates/errors/src/error_combinators.rs +++ b/compiler/crates/errors/src/error_combinators.rs @@ -132,14 +132,16 @@ where } /// Similar to `try_map` but performs the transform in parallel. -pub fn par_try_map( +pub fn par_try_map< + T: Sync + Send, + E: Sync + Send, + U: Sync + Send, + I: IntoParallelIterator + IntoIterator, + F: Sync + Send + Fn(U) -> Result>, +>( items: I, f: F, -) -> Result, Vec> -where - I: IntoParallelIterator + IntoIterator, - F: Fn(U) -> Result>, -{ +) -> Result, Vec> { let results: Vec>> = par_iter(items).map(f).collect(); let mut errors = Vec::new(); let mut values = Vec::with_capacity(results.len()); @@ -158,7 +160,7 @@ where #[macro_export] macro_rules! validate { - ($($args:expr),*) => {{ + ($($args:expr_2021),*) => {{ let mut errors = Vec::new(); $( match $args { diff --git a/compiler/crates/extract-graphql/Cargo.toml b/compiler/crates/extract-graphql/Cargo.toml index 1eab4a53e2541..a6127ad9440ba 100644 --- a/compiler/crates/extract-graphql/Cargo.toml +++ b/compiler/crates/extract-graphql/Cargo.toml @@ -4,7 +4,7 @@ name = "extract-graphql" version = "0.0.0" authors = ["Facebook"] -edition = "2021" +edition = "2024" repository = "https://github.com/facebook/relay" license = "MIT" @@ -19,4 +19,4 @@ graphql-syntax = { path = "../graphql-syntax" } [dev-dependencies] fixture-tests = { path = "../fixture-tests" } -tokio = { version = "1.41.0", features = ["full", "test-util", "tracing"] } +tokio = { version = "1.46.1", features = ["full", "test-util", "tracing"] } diff --git a/compiler/crates/extract-graphql/src/lib.rs b/compiler/crates/extract-graphql/src/lib.rs index 32f039fa9db82..c4a753c5cded6 100644 --- a/compiler/crates/extract-graphql/src/lib.rs +++ b/compiler/crates/extract-graphql/src/lib.rs @@ -55,9 +55,21 @@ impl<'a> CharReader<'a> { column_index: 0, } } + + pub fn get_line_index(&self) -> usize { + self.line_index + } + + pub fn get_column_index(&self) -> usize { + self.column_index + } + + pub fn get_chars(&self) -> Peekable> { + self.chars.clone() + } } -impl<'a> Iterator for CharReader<'a> { +impl Iterator for CharReader<'_> { type Item = (usize, char); fn next(&mut self) -> Option { let pair = self.chars.next(); @@ -216,11 +228,11 @@ pub fn extract(input: &str) -> Vec { res } -fn consume_escaped_char(it: &mut CharReader<'_>) { +pub fn consume_escaped_char(it: &mut CharReader<'_>) { it.next(); } -fn consume_identifier(it: &mut CharReader<'_>) { +pub fn consume_identifier(it: &mut CharReader<'_>) { while it.chars.peek().is_some() { match it.chars.peek() { Some((_, 'a'..='z' | 'A'..='Z' | '_' | '0'..='9')) => { @@ -231,7 +243,7 @@ fn consume_identifier(it: &mut CharReader<'_>) { } } -fn consume_line_comment(it: &mut CharReader<'_>) { +pub fn consume_line_comment(it: &mut CharReader<'_>) { for (_, c) in it { match c { '\n' | '\r' => { @@ -242,7 +254,7 @@ fn consume_line_comment(it: &mut CharReader<'_>) { } } -fn consume_string(it: &mut CharReader<'_>, quote: char) { +pub fn consume_string(it: &mut CharReader<'_>, quote: char) { while let Some((_, c)) = it.next() { match c { '\\' => { diff --git a/compiler/crates/extract-graphql/tests/extract.rs b/compiler/crates/extract-graphql/tests/extract.rs index b7abbc2ceba29..56abe1682851f 100644 --- a/compiler/crates/extract-graphql/tests/extract.rs +++ b/compiler/crates/extract-graphql/tests/extract.rs @@ -5,8 +5,8 @@ * LICENSE file in the root directory of this source tree. */ -use extract_graphql::extract; use extract_graphql::JavaScriptSourceFeature; +use extract_graphql::extract; use fixture_tests::Fixture; pub async fn transform_fixture(fixture: &Fixture<'_>) -> Result { diff --git a/compiler/crates/fixture-tests/Cargo.toml b/compiler/crates/fixture-tests/Cargo.toml index 75b4e3a539010..2f355d61888c6 100644 --- a/compiler/crates/fixture-tests/Cargo.toml +++ b/compiler/crates/fixture-tests/Cargo.toml @@ -4,7 +4,7 @@ name = "fixture-tests" version = "0.0.0" authors = ["Facebook"] -edition = "2021" +edition = "2024" repository = "https://github.com/facebook/relay" license = "MIT" @@ -17,9 +17,9 @@ name = "fixture_tests_tests" path = "tests/uppercase_test.rs" [dependencies] -clap = { version = "4.5.20", features = ["derive", "env", "string", "unicode", "wrap_help"] } +clap = { version = "4.5.42", features = ["derive", "env", "string", "unicode", "wrap_help"] } colored = "2.1.0" diff = "0.1" -lazy_static = "1.4" +lazy_static = "1.5" signedsource = { path = "../signedsource" } -tokio = { version = "1.41.0", features = ["full", "test-util", "tracing"] } +tokio = { version = "1.46.1", features = ["full", "test-util", "tracing"] } diff --git a/compiler/crates/fixture-tests/src/lib.rs b/compiler/crates/fixture-tests/src/lib.rs index 8e7710edd84d3..1d89c2ee2a308 100644 --- a/compiler/crates/fixture-tests/src/lib.rs +++ b/compiler/crates/fixture-tests/src/lib.rs @@ -111,7 +111,7 @@ pub async fn test_fixture( // race condition where if an async test is running at the same time as // another test is trying to check the workspace root, it will get the wrong // value. To mitigate that risk we compute the workspace root early. - let workspace_root = &WORKSPACE_ROOT; + let workspace_root = WORKSPACE_ROOT.clone(); let fixture = Fixture { file_name: input_file_name, content: input, diff --git a/compiler/crates/fixture-tests/src/main.rs b/compiler/crates/fixture-tests/src/main.rs index fca883018bd3f..2ff4d7b929ec2 100644 --- a/compiler/crates/fixture-tests/src/main.rs +++ b/compiler/crates/fixture-tests/src/main.rs @@ -16,8 +16,8 @@ use std::path::PathBuf; use clap::Parser; use colored::Colorize; -use signedsource::sign_file; use signedsource::SIGNING_TOKEN; +use signedsource::sign_file; #[derive(Debug, Parser)] #[clap(name = "fixture-tests", about = "Generates fixture tests.")] diff --git a/compiler/crates/graphql-cli/Cargo.toml b/compiler/crates/graphql-cli/Cargo.toml index ae040e4dd2393..58b960d908325 100644 --- a/compiler/crates/graphql-cli/Cargo.toml +++ b/compiler/crates/graphql-cli/Cargo.toml @@ -4,7 +4,7 @@ name = "graphql-cli" version = "0.0.0" authors = ["Facebook"] -edition = "2021" +edition = "2024" repository = "https://github.com/facebook/relay" license = "MIT" diff --git a/compiler/crates/graphql-ir-validations/Cargo.toml b/compiler/crates/graphql-ir-validations/Cargo.toml index 293c255af43a6..9fac86688d075 100644 --- a/compiler/crates/graphql-ir-validations/Cargo.toml +++ b/compiler/crates/graphql-ir-validations/Cargo.toml @@ -4,7 +4,7 @@ name = "graphql-ir-validations" version = "0.0.0" authors = ["Facebook"] -edition = "2021" +edition = "2024" repository = "https://github.com/facebook/relay" license = "MIT" @@ -21,8 +21,8 @@ graphql-text-printer = { path = "../graphql-text-printer" } intern = { path = "../intern" } relay-config = { path = "../relay-config" } schema = { path = "../schema" } -serde = { version = "1.0.185", features = ["derive", "rc"] } -thiserror = "1.0.64" +serde = { version = "1.0.219", features = ["derive", "rc"] } +thiserror = "2.0.12" [dev-dependencies] fixture-tests = { path = "../fixture-tests" } @@ -30,4 +30,4 @@ graphql-cli = { path = "../graphql-cli" } graphql-syntax = { path = "../graphql-syntax" } graphql-test-helpers = { path = "../graphql-test-helpers" } relay-test-schema = { path = "../relay-test-schema" } -tokio = { version = "1.41.0", features = ["full", "test-util", "tracing"] } +tokio = { version = "1.46.1", features = ["full", "test-util", "tracing"] } diff --git a/compiler/crates/graphql-ir-validations/src/validate_selection_conflict.rs b/compiler/crates/graphql-ir-validations/src/validate_selection_conflict.rs index 38eb9cf7815f6..f0b63385b6291 100644 --- a/compiler/crates/graphql-ir-validations/src/validate_selection_conflict.rs +++ b/compiler/crates/graphql-ir-validations/src/validate_selection_conflict.rs @@ -20,7 +20,6 @@ use dashmap::DashMap; use dashmap::DashSet; use errors::par_try_map; use errors::validate_map; -use graphql_ir::node_identifier::LocationAgnosticBehavior; use graphql_ir::Argument; use graphql_ir::Field as IRField; use graphql_ir::FragmentDefinition; @@ -30,8 +29,9 @@ use graphql_ir::OperationDefinition; use graphql_ir::Program; use graphql_ir::ScalarField; use graphql_ir::Selection; -use intern::string_key::StringKey; +use graphql_ir::node_identifier::LocationAgnosticBehavior; use intern::Lookup; +use intern::string_key::StringKey; use relay_config::ProjectConfig; use schema::FieldID; use schema::SDLSchema; @@ -552,10 +552,10 @@ impl<'s> Field<'s> { } mod ignoring_type_and_location { - use graphql_ir::node_identifier::LocationAgnosticBehavior; - use graphql_ir::node_identifier::LocationAgnosticPartialEq; use graphql_ir::Argument; use graphql_ir::Value; + use graphql_ir::node_identifier::LocationAgnosticBehavior; + use graphql_ir::node_identifier::LocationAgnosticPartialEq; /// Verify that two sets of arguments are equivalent - same argument names /// and values. Notably, this ignores the types of arguments and values, diff --git a/compiler/crates/graphql-ir-validations/tests/validate_selection_conflict.rs b/compiler/crates/graphql-ir-validations/tests/validate_selection_conflict.rs index cd8acbc0c056c..c234aba5d20ee 100644 --- a/compiler/crates/graphql-ir-validations/tests/validate_selection_conflict.rs +++ b/compiler/crates/graphql-ir-validations/tests/validate_selection_conflict.rs @@ -12,14 +12,14 @@ use common::SourceLocationKey; use common::TextSource; use fixture_tests::Fixture; use graphql_cli::DiagnosticPrinter; +use graphql_ir::Program; use graphql_ir::build; use graphql_ir::node_identifier::LocationAgnosticBehavior; -use graphql_ir::Program; use graphql_ir_validations::validate_selection_conflict; use graphql_syntax::parse_executable; use graphql_test_helpers::diagnostics_to_sorted_string; -use relay_test_schema::get_test_schema_with_located_extensions; use relay_test_schema::TEST_SCHEMA; +use relay_test_schema::get_test_schema_with_located_extensions; #[derive(Clone)] struct LocationAgnosticBehaviorForTestOnly; diff --git a/compiler/crates/graphql-ir/Cargo.toml b/compiler/crates/graphql-ir/Cargo.toml index 35e01f54a70c8..b42832311fb46 100644 --- a/compiler/crates/graphql-ir/Cargo.toml +++ b/compiler/crates/graphql-ir/Cargo.toml @@ -4,7 +4,7 @@ name = "graphql-ir" version = "0.0.0" authors = ["Facebook"] -edition = "2021" +edition = "2024" repository = "https://github.com/facebook/relay" license = "MIT" @@ -25,17 +25,17 @@ common = { path = "../common" } errors = { path = "../errors" } fnv = "1.0" graphql-syntax = { path = "../graphql-syntax" } -indexmap = { version = "2.2.6", features = ["arbitrary", "rayon", "serde"] } +indexmap = { version = "2.9.0", features = ["arbitrary", "rayon", "serde"] } intern = { path = "../intern" } -lazy_static = "1.4" -once_cell = "1.12" +lazy_static = "1.5" +once_cell = "1.21" schema = { path = "../schema" } -serde = { version = "1.0.185", features = ["derive", "rc"] } -strum = { version = "0.26.2", features = ["derive"] } -thiserror = "1.0.64" +serde = { version = "1.0.219", features = ["derive", "rc"] } +strum = { version = "0.27.1", features = ["derive"] } +thiserror = "2.0.12" [dev-dependencies] fixture-tests = { path = "../fixture-tests" } graphql-cli = { path = "../graphql-cli" } relay-test-schema = { path = "../relay-test-schema" } -tokio = { version = "1.41.0", features = ["full", "test-util", "tracing"] } +tokio = { version = "1.46.1", features = ["full", "test-util", "tracing"] } diff --git a/compiler/crates/graphql-ir/src/build.rs b/compiler/crates/graphql-ir/src/build.rs index fef0c540a4e20..f1135aeb2e48c 100644 --- a/compiler/crates/graphql-ir/src/build.rs +++ b/compiler/crates/graphql-ir/src/build.rs @@ -19,10 +19,10 @@ use common::ScalarName; use common::Span; use common::WithLocation; use errors::par_try_map; -use errors::try2; -use errors::try3; use errors::try_all; use errors::try_map; +use errors::try2; +use errors::try3; use graphql_syntax::DefaultValue; use graphql_syntax::DirectiveLocation; use graphql_syntax::Identifier; @@ -31,14 +31,12 @@ use graphql_syntax::OperationKind; use graphql_syntax::Token; use graphql_syntax::TokenKind; use indexmap::IndexMap; +use intern::Lookup; use intern::string_key::Intern; use intern::string_key::StringKey; use intern::string_key::StringKeyMap; use intern::string_key::StringKeySet; -use intern::Lookup; use lazy_static::lazy_static; -use schema::suggestion_list; -use schema::suggestion_list::GraphQLSuggestions; use schema::ArgumentDefinitions; use schema::Enum; use schema::FieldID; @@ -48,16 +46,18 @@ use schema::Scalar; use schema::Schema; use schema::Type; use schema::TypeReference; +use schema::suggestion_list; +use schema::suggestion_list::GraphQLSuggestions; use crate::constants::ARGUMENT_DEFINITION; use crate::errors::MachineMetadataKey; use crate::errors::ValidationMessage; use crate::errors::ValidationMessageWithData; use crate::ir::*; -use crate::signatures::build_signatures; use crate::signatures::FragmentSignature; use crate::signatures::FragmentSignatures; use crate::signatures::ProvidedVariableMetadata; +use crate::signatures::build_signatures; lazy_static! { static ref TYPENAME_FIELD_NAME: StringKey = "__typename".intern(); diff --git a/compiler/crates/graphql-ir/src/errors.rs b/compiler/crates/graphql-ir/src/errors.rs index 9d6527031c7fc..b6949a9421c67 100644 --- a/compiler/crates/graphql-ir/src/errors.rs +++ b/compiler/crates/graphql-ir/src/errors.rs @@ -13,18 +13,22 @@ use common::DirectiveName; use common::ScalarName; use common::WithDiagnosticData; use graphql_syntax::OperationKind; -use intern::string_key::StringKey; use intern::Lookup; -use schema::suggestion_list::did_you_mean; +use intern::string_key::StringKey; use schema::Type; use schema::TypeReference; +use schema::suggestion_list::did_you_mean; +use serde::Deserialize; use thiserror::Error; -use crate::ir::FragmentDefinitionName; use crate::VariableName; +use crate::ir::FragmentDefinitionName; #[derive( + Debug, + Deserialize, Eq, + Hash, Ord, PartialEq, PartialOrd, @@ -350,7 +354,7 @@ pub enum ValidationMessage { }, #[error( - "Expected the {key_arg_name} argument to @{connection_directive_name} to be of form '_{postfix}', got '{key_arg_value}'. For a detailed explanation, check out https://relay.dev/docs/en/pagination-container#connection" + "Expected the {key_arg_name} argument to @{connection_directive_name} to be of form '_{postfix}', got '{key_arg_value}'. For a detailed explanation, check out https://relay.dev/docs/tutorial/connections-pagination/" )] InvalidConnectionKeyArgPostfix { connection_directive_name: DirectiveName, @@ -461,7 +465,7 @@ pub enum ValidationMessage { #[error("The field `{parent_name}.{field_name}` is deprecated.{}", match deprecation_reason { - Some(reason) => format!(" Deprecation reason: \"{}\"", reason), + Some(reason) => format!(" Deprecation reason: \"{reason}\""), None => "".to_string() } )] @@ -473,7 +477,7 @@ pub enum ValidationMessage { #[error("The argument `{argument_name}` of the field `{parent_name}.{field_name}` is deprecated.{}", match deprecation_reason { - Some(reason) => format!(" Deprecation reason: \"{}\"", reason), + Some(reason) => format!(" Deprecation reason: \"{reason}\""), None => "".to_string() })] DeprecatedFieldArgument { @@ -485,7 +489,7 @@ pub enum ValidationMessage { #[error("The argument `{argument_name}` of the directive `@{directive_name}` is deprecated.{}", match deprecation_reason { - Some(reason) => format!(" Deprecation reason: \"{}\"", reason), + Some(reason) => format!(" Deprecation reason: \"{reason}\""), None => "".to_string() })] DeprecatedDirectiveArgument { @@ -641,7 +645,7 @@ impl WithDiagnosticData for ValidationMessageWithData { .map(|suggestion| into_box(*suggestion)) .collect::<_>(), ValidationMessageWithData::ExpectedSelectionsOnObjectField { field_name, .. } => { - vec![Box::new(format!("{} {{ }}", field_name))] + vec![Box::new(format!("{field_name} {{ }}"))] } ValidationMessageWithData::DeprecatedDangerouslyUnaliasedDirective => { vec![Box::new("@alias".to_string())] diff --git a/compiler/crates/graphql-ir/src/ir.rs b/compiler/crates/graphql-ir/src/ir.rs index 7ab3e58a5bd4e..7f385313cb2ce 100644 --- a/compiler/crates/graphql-ir/src/ir.rs +++ b/compiler/crates/graphql-ir/src/ir.rs @@ -11,15 +11,16 @@ use std::fmt; use std::fmt::Display; use std::fmt::Formatter; use std::hash::Hash; +use std::hash::Hasher; use std::str::FromStr; use std::sync::Arc; +use ::intern::BuildIdHasher; +use ::intern::Lookup; use ::intern::impl_lookup; use ::intern::intern; use ::intern::string_key::Intern; use ::intern::string_key::StringKey; -use ::intern::BuildIdHasher; -use ::intern::Lookup; use common::ArgumentName; use common::Diagnostic; use common::DiagnosticsResult; @@ -38,9 +39,9 @@ use schema::TypeReference; use serde::Deserialize; use serde::Serialize; -use crate::signatures::FragmentSignature; use crate::AssociatedData; use crate::ValidationMessage; +use crate::signatures::FragmentSignature; // Definitions #[derive(Clone, Debug, Eq, PartialEq)] @@ -50,6 +51,13 @@ pub enum ExecutableDefinition { } impl ExecutableDefinition { + pub fn directives(&self) -> &[Directive] { + match self { + ExecutableDefinition::Operation(node) => &node.directives, + ExecutableDefinition::Fragment(node) => &node.directives, + } + } + pub fn has_directive(&self, directive_name: DirectiveName) -> bool { match self { ExecutableDefinition::Operation(node) => node @@ -151,7 +159,9 @@ pub type FragmentDefinitionNameSet = HashSet, + /// Local variables defined in the fragment using the `@argumentDefinitions` directive. pub variable_definitions: Vec, + /// Global variables that are used but NOT defined within the fragment (they can come from a parent query or fragment). pub used_global_variables: Vec, pub type_condition: Type, pub directives: Vec, @@ -297,7 +307,7 @@ impl Named for VariableDefinition { // Selections /// A selection within an operation or fragment -#[derive(Clone, Eq, PartialEq)] +#[derive(Clone, Eq, PartialEq, Hash)] pub enum Selection { FragmentSpread(Arc), InlineFragment(Arc), @@ -402,7 +412,7 @@ impl fmt::Debug for Selection { } /// ... Name -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Hash)] pub struct FragmentSpread { pub fragment: WithLocation, pub arguments: Vec, @@ -438,6 +448,16 @@ pub struct InlineFragment { pub spread_location: Location, } +impl Hash for InlineFragment { + fn hash(&self, state: &mut H) { + self.type_condition.hash(state); + self.directives.hash(state); + self.spread_location.hash(state); + // Avoid descending into selections, which leads to large recursion + self.selections.len().hash(state); + } +} + impl InlineFragment { /// Get the alias of this inline fragment from the optional `@alias` directive. /// If the `as` argument is not present, the type condition is used as the fallback. @@ -496,6 +516,17 @@ pub struct LinkedField { pub selections: Vec, } +impl Hash for LinkedField { + fn hash(&self, state: &mut H) { + self.alias.hash(state); + self.definition.hash(state); + self.arguments.hash(state); + self.directives.hash(state); + // Avoid descending into selections, which leads to large recursion + self.selections.len().hash(state); + } +} + impl Field for LinkedField { fn alias(&self) -> Option> { self.alias @@ -515,7 +546,7 @@ impl Field for LinkedField { } /// Name Arguments? -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Hash)] pub struct ScalarField { pub alias: Option>, pub definition: WithLocation, @@ -550,6 +581,16 @@ pub struct Condition { pub location: Location, } +impl Hash for Condition { + fn hash(&self, state: &mut H) { + self.value.hash(state); + self.passing_value.hash(state); + self.location.hash(state); + // Avoid descending into selections, which leads to large recursion + self.selections.len().hash(state); + } +} + impl Condition { pub fn directive_name(&self) -> &'static str { if self.passing_value { @@ -656,7 +697,7 @@ impl Named for ConstantArgument { } macro_rules! generate_unwrap_fn { - ($fn_name:ident,$self:ident,$t:ty,$cv:pat => $result:expr) => { + ($fn_name:ident,$self:ident,$t:ty,$cv:pat => $result:expr_2021) => { pub fn $fn_name(&$self) -> $t { match $self { $cv => $result, @@ -703,7 +744,7 @@ impl ConstantValue { generate_unwrap_fn!(unwrap_object, self, &Vec, ConstantValue::Object(o) => o); } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Hash)] pub enum ConditionValue { Constant(bool), Variable(Variable), diff --git a/compiler/crates/graphql-ir/src/lib.rs b/compiler/crates/graphql-ir/src/lib.rs index b17a3c956e34c..9db9aca8de377 100644 --- a/compiler/crates/graphql-ir/src/lib.rs +++ b/compiler/crates/graphql-ir/src/lib.rs @@ -23,28 +23,30 @@ mod validator; mod visitor; pub use associated_data::AssociatedData; +pub use build::BuilderOptions; +pub use build::DIRECTIVE_ARGUMENTS; +pub use build::FIXME_FAT_INTERFACE; +pub use build::FragmentVariablesSemantic; +pub use build::RelayMode; pub use build::build_directive; pub use build::build_ir as build; pub use build::build_ir_in_relay_mode; pub use build::build_ir_with_extra_features; -pub use build::BuilderOptions; -pub use build::FragmentVariablesSemantic; -pub use build::RelayMode; -pub use build::DIRECTIVE_ARGUMENTS; -pub use build::FIXME_FAT_INTERFACE; pub use constants::ARGUMENT_DEFINITION; pub use ir::*; pub use program::Program; pub use signatures::FragmentSignature; +pub use signatures::FragmentSignatures; pub use signatures::ProvidedVariableMetadata; pub use signatures::UNUSED_LOCAL_VARIABLE_DEPRECATED; -pub use transform::transform_list; -pub use transform::transform_list_multi; +pub use signatures::build_signatures; pub use transform::TransformProgramPipe; pub use transform::Transformed; pub use transform::TransformedMulti; pub use transform::TransformedValue; pub use transform::Transformer; +pub use transform::transform_list; +pub use transform::transform_list_multi; pub use validator::Validator; pub use visitor::Visitor; diff --git a/compiler/crates/graphql-ir/src/program.rs b/compiler/crates/graphql-ir/src/program.rs index 57fcfb208dbfc..c6a79a05d8c4c 100644 --- a/compiler/crates/graphql-ir/src/program.rs +++ b/compiler/crates/graphql-ir/src/program.rs @@ -11,13 +11,13 @@ use std::sync::Arc; use intern::BuildIdHasher; use schema::SDLSchema; +use crate::ExecutableDefinitionName; use crate::ir::ExecutableDefinition; use crate::ir::FragmentDefinition; use crate::ir::FragmentDefinitionName; use crate::ir::FragmentDefinitionNameMap; use crate::ir::OperationDefinition; use crate::ir::OperationDefinitionName; -use crate::ExecutableDefinitionName; /// A collection of all documents that are being compiled. #[derive(Debug, Clone)] diff --git a/compiler/crates/graphql-ir/src/signatures.rs b/compiler/crates/graphql-ir/src/signatures.rs index e04cddd274aa2..ee16a0f88ba05 100644 --- a/compiler/crates/graphql-ir/src/signatures.rs +++ b/compiler/crates/graphql-ir/src/signatures.rs @@ -17,31 +17,33 @@ use common::SourceLocationKey; use common::WithLocation; use errors::par_try_map; use errors::try3; +use intern::Lookup; use intern::string_key::Intern; use intern::string_key::StringKey; -use intern::Lookup; use lazy_static::lazy_static; -use schema::suggestion_list::GraphQLSuggestions; use schema::SDLSchema; use schema::Schema; use schema::Type; use schema::TypeReference; +use schema::suggestion_list::GraphQLSuggestions; +use crate::VariableName; use crate::associated_data_impl; +use crate::build::ValidationLevel; use crate::build::build_constant_value; use crate::build::build_directives; use crate::build::build_type_annotation; use crate::build::build_variable_definitions; -use crate::build::ValidationLevel; use crate::build_directive; use crate::constants::ARGUMENT_DEFINITION; +use crate::errors::MachineMetadataKey; use crate::errors::ValidationMessage; use crate::errors::ValidationMessageWithData; use crate::ir::ConstantValue; +use crate::ir::FragmentDefinition; use crate::ir::FragmentDefinitionName; use crate::ir::FragmentDefinitionNameMap; use crate::ir::VariableDefinition; -use crate::VariableName; lazy_static! { static ref TYPE: StringKey = "type".intern(); @@ -62,6 +64,15 @@ pub struct ProvidedVariableMetadata { } impl ProvidedVariableMetadata { + /// Returns true if the provider module's path is a bare identifier (i.e. + /// doesn't start with './' nor '../' nor '/'), otherwise false. + pub fn is_bare(&self) -> bool { + let module_name = self.module_name.lookup(); + !(module_name.starts_with("./") + || module_name.starts_with("../") + || module_name.starts_with("/")) + } + /// Return a path to the provider module, based on the fragment location /// where this provider is used. pub fn module_path(&self) -> PathBuf { @@ -82,7 +93,7 @@ associated_data_impl!(ProvidedVariableMetadata); /// would depend on having checked its body! Since recursive fragments /// are allowed, we break the cycle by first computing signatures /// and using these to type check fragment spreads in selections. -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq, Hash)] pub struct FragmentSignature { pub name: WithLocation, pub variable_definitions: Vec, @@ -90,6 +101,17 @@ pub struct FragmentSignature { pub directives: Vec, } +impl From<&FragmentDefinition> for FragmentSignature { + fn from(fragment: &FragmentDefinition) -> Self { + Self { + name: fragment.name, + variable_definitions: fragment.variable_definitions.clone(), + type_condition: fragment.type_condition, + directives: fragment.directives.clone(), + } + } +} + pub fn build_signatures( schema: &SDLSchema, definitions: &[graphql_syntax::ExecutableDefinition], @@ -151,7 +173,7 @@ fn build_fragment_signature( .location .with_span(fragment.type_condition.type_.span), ) - .metadata_for_machine("unknown_type", type_name.lookup()) + .metadata_for_machine(MachineMetadataKey::UnknownType, type_name.lookup()) .into()), }; let argument_definition_directives = fragment diff --git a/compiler/crates/graphql-ir/src/transform.rs b/compiler/crates/graphql-ir/src/transform.rs index ce48df44e4e4b..5cfd41099e387 100644 --- a/compiler/crates/graphql-ir/src/transform.rs +++ b/compiler/crates/graphql-ir/src/transform.rs @@ -12,17 +12,17 @@ use common::WithLocation; use crate::ir::*; use crate::program::Program; -pub trait Transformer { +pub trait Transformer<'a> { const NAME: &'static str; const VISIT_ARGUMENTS: bool; const VISIT_DIRECTIVES: bool; const RETAIN_EMPTY_SELECTION_SETS: bool = false; - fn transform_program(&mut self, program: &Program) -> TransformedValue { + fn transform_program(&mut self, program: &'a Program) -> TransformedValue { self.default_transform_program(program) } - fn default_transform_program(&mut self, program: &Program) -> TransformedValue { + fn default_transform_program(&mut self, program: &'a Program) -> TransformedValue { let mut next_program = Program::new(Arc::clone(&program.schema)); let mut has_changes = false; for operation in program.operations() { @@ -55,14 +55,14 @@ pub trait Transformer { // Fragment Definition fn transform_fragment( &mut self, - fragment: &FragmentDefinition, + fragment: &'a FragmentDefinition, ) -> Transformed { self.default_transform_fragment(fragment) } fn default_transform_fragment( &mut self, - fragment: &FragmentDefinition, + fragment: &'a FragmentDefinition, ) -> Transformed { let selections = self.transform_selections(&fragment.selections); let directives = self.transform_directives(&fragment.directives); @@ -95,14 +95,14 @@ pub trait Transformer { // Operation Definition fn transform_operation( &mut self, - operation: &OperationDefinition, + operation: &'a OperationDefinition, ) -> Transformed { self.default_transform_operation(operation) } fn default_transform_operation( &mut self, - operation: &OperationDefinition, + operation: &'a OperationDefinition, ) -> Transformed { let selections = self.transform_selections(&operation.selections); let directives = self.transform_directives(&operation.directives); @@ -172,16 +172,16 @@ pub trait Transformer { // Selection fn transform_selections( &mut self, - selections: &[Selection], + selections: &'a [Selection], ) -> TransformedValue> { transform_list(selections, |selection| self.transform_selection(selection)) } - fn transform_selection(&mut self, selection: &Selection) -> Transformed { + fn transform_selection(&mut self, selection: &'a Selection) -> Transformed { self.default_transform_selection(selection) } - fn default_transform_selection(&mut self, selection: &Selection) -> Transformed { + fn default_transform_selection(&mut self, selection: &'a Selection) -> Transformed { match selection { Selection::FragmentSpread(selection) => self.transform_fragment_spread(selection), Selection::InlineFragment(selection) => self.transform_inline_fragment(selection), @@ -209,11 +209,11 @@ pub trait Transformer { }))) } - fn transform_linked_field(&mut self, field: &LinkedField) -> Transformed { + fn transform_linked_field(&mut self, field: &'a LinkedField) -> Transformed { self.default_transform_linked_field(field) } - fn default_transform_linked_field(&mut self, field: &LinkedField) -> Transformed { + fn default_transform_linked_field(&mut self, field: &'a LinkedField) -> Transformed { // Special-case for empty selections let selections = self.transform_selections(&field.selections); if let TransformedValue::Replace(selections) = &selections { @@ -234,13 +234,16 @@ pub trait Transformer { }))) } - fn transform_inline_fragment(&mut self, fragment: &InlineFragment) -> Transformed { + fn transform_inline_fragment( + &mut self, + fragment: &'a InlineFragment, + ) -> Transformed { self.default_transform_inline_fragment(fragment) } fn default_transform_inline_fragment( &mut self, - fragment: &InlineFragment, + fragment: &'a InlineFragment, ) -> Transformed { // Special-case for empty selections let selections = self.transform_selections(&fragment.selections); @@ -280,11 +283,11 @@ pub trait Transformer { } // Conditions - fn transform_condition(&mut self, condition: &Condition) -> Transformed { + fn transform_condition(&mut self, condition: &'a Condition) -> Transformed { self.default_transform_condition(condition) } - fn default_transform_condition(&mut self, condition: &Condition) -> Transformed { + fn default_transform_condition(&mut self, condition: &'a Condition) -> Transformed { // Special-case for empty selections let selections = self.transform_selections(&condition.selections); if let TransformedValue::Replace(selections) = &selections { @@ -399,10 +402,10 @@ pub trait Transformer { } // Helpers -pub fn transform_list(list: &[T], mut transform: F) -> TransformedValue> +pub fn transform_list<'a, T, F, R>(list: &'a [T], mut transform: F) -> TransformedValue> where T: Clone, - F: FnMut(&T) -> R, + F: FnMut(&'a T) -> R, R: Into>, { let mut result = Vec::new(); @@ -519,7 +522,7 @@ impl TransformProgramPipe { pub fn pipe(self, transformer: T) -> Self where - T: Transformer, + T: for<'a> Transformer<'a>, { let mut transformer = transformer; let initial = self.initial; @@ -539,7 +542,7 @@ impl TransformProgramPipe { pub fn pipe_option(self, option: Option, get_transformer: F) -> Self where - T: Transformer, + T: for<'a> Transformer<'a>, F: FnOnce(X) -> T, { if let Some(x) = option { diff --git a/compiler/crates/graphql-ir/tests/parse.rs b/compiler/crates/graphql-ir/tests/parse.rs index 99e6ffad128bf..9b515fb7f0f66 100644 --- a/compiler/crates/graphql-ir/tests/parse.rs +++ b/compiler/crates/graphql-ir/tests/parse.rs @@ -11,9 +11,9 @@ use fixture_tests::Fixture; use fnv::FnvHashMap; use graphql_cli::DiagnosticPrinter; use graphql_ir::build; -use graphql_syntax::parse_executable_with_features; use graphql_syntax::FragmentArgumentSyntaxKind; use graphql_syntax::ParserFeatures; +use graphql_syntax::parse_executable_with_features; use relay_test_schema::TEST_SCHEMA; pub async fn transform_fixture(fixture: &Fixture<'_>) -> Result { @@ -21,6 +21,7 @@ pub async fn transform_fixture(fixture: &Fixture<'_>) -> Result let features = ParserFeatures { fragment_argument_capability: FragmentArgumentSyntaxKind::SpreadArgumentsAndFragmentVariableDefinitions, + allow_string_literal_alias: false, }; let ast = parse_executable_with_features(fixture.content, source_location, features).unwrap(); let mut sources = FnvHashMap::default(); diff --git a/compiler/crates/graphql-ir/tests/parse_with_extensions.rs b/compiler/crates/graphql-ir/tests/parse_with_extensions.rs index ec75bf03a9aa7..50ebd4d1b2a39 100644 --- a/compiler/crates/graphql-ir/tests/parse_with_extensions.rs +++ b/compiler/crates/graphql-ir/tests/parse_with_extensions.rs @@ -10,9 +10,9 @@ use common::TextSource; use fixture_tests::Fixture; use fnv::FnvHashMap; use graphql_cli::DiagnosticPrinter; -use graphql_ir::build_ir_with_extra_features; use graphql_ir::BuilderOptions; use graphql_ir::FragmentVariablesSemantic; +use graphql_ir::build_ir_with_extra_features; use graphql_syntax::parse_executable; use relay_test_schema::get_test_schema_with_extensions; diff --git a/compiler/crates/graphql-ir/tests/parse_with_provider.rs b/compiler/crates/graphql-ir/tests/parse_with_provider.rs index e73bf5dc27b2e..47e18ed35bc9f 100644 --- a/compiler/crates/graphql-ir/tests/parse_with_provider.rs +++ b/compiler/crates/graphql-ir/tests/parse_with_provider.rs @@ -10,13 +10,13 @@ use common::TextSource; use fixture_tests::Fixture; use fnv::FnvHashMap; use graphql_cli::DiagnosticPrinter; -use graphql_ir::build_ir_with_extra_features; use graphql_ir::BuilderOptions; use graphql_ir::FragmentVariablesSemantic; use graphql_ir::RelayMode; -use graphql_syntax::parse_executable_with_features; +use graphql_ir::build_ir_with_extra_features; use graphql_syntax::FragmentArgumentSyntaxKind; use graphql_syntax::ParserFeatures; +use graphql_syntax::parse_executable_with_features; use relay_test_schema::TEST_SCHEMA; pub async fn transform_fixture(fixture: &Fixture<'_>) -> Result { @@ -24,6 +24,7 @@ pub async fn transform_fixture(fixture: &Fixture<'_>) -> Result let features = ParserFeatures { fragment_argument_capability: FragmentArgumentSyntaxKind::SpreadArgumentsAndFragmentVariableDefinitions, + allow_string_literal_alias: false, }; let ast = parse_executable_with_features(fixture.content, source_location, features).unwrap(); let mut sources = FnvHashMap::default(); diff --git a/compiler/crates/graphql-syntax/Cargo.toml b/compiler/crates/graphql-syntax/Cargo.toml index 45c63da19dbee..d55ea3ab27b21 100644 --- a/compiler/crates/graphql-syntax/Cargo.toml +++ b/compiler/crates/graphql-syntax/Cargo.toml @@ -1,10 +1,10 @@ -# @generated by autocargo from //relay/oss/crates/graphql-syntax:[advance_schema_document_test,graphql-syntax,graphql-syntax_print_test,parse_document_test,parse_document_with_features_test,parse_executable_document_test,parse_executable_document_with_error_recovery_test,parse_schema_document_test] +# @generated by autocargo from //relay/oss/crates/graphql-syntax:[advance_schema_document_test,graphql-syntax,graphql-syntax_print_test,parse_document_test,parse_document_with_enquoted_alias_test,parse_document_with_features_test,parse_executable_document_test,parse_executable_document_with_error_recovery_test,parse_schema_document_test] [package] name = "graphql-syntax" version = "0.0.0" authors = ["Facebook"] -edition = "2021" +edition = "2024" repository = "https://github.com/facebook/relay" license = "MIT" @@ -15,11 +15,11 @@ path = "tests/print_test.rs" [dependencies] common = { path = "../common" } intern = { path = "../intern" } -logos = "0.12" -serde = { version = "1.0.185", features = ["derive", "rc"] } -thiserror = "1.0.64" +logos = "0.15" +serde = { version = "1.0.219", features = ["derive", "rc"] } +thiserror = "2.0.12" [dev-dependencies] fixture-tests = { path = "../fixture-tests" } graphql-cli = { path = "../graphql-cli" } -tokio = { version = "1.41.0", features = ["full", "test-util", "tracing"] } +tokio = { version = "1.46.1", features = ["full", "test-util", "tracing"] } diff --git a/compiler/crates/graphql-syntax/src/lexer.rs b/compiler/crates/graphql-syntax/src/lexer.rs index a36f113264b80..20de671d5a694 100644 --- a/compiler/crates/graphql-syntax/src/lexer.rs +++ b/compiler/crates/graphql-syntax/src/lexer.rs @@ -33,11 +33,8 @@ pub struct TokenKindExtras { )] #[serde(tag = "type")] #[logos(extras = TokenKindExtras)] +#[logos(skip r"[ \t\r\n\f,\ufeff]+|#[^\n\r]*")] // whitespace pub enum TokenKind { - #[regex(r"[ \t\r\n\f,\ufeff]+|#[^\n\r]*", logos::skip)] - #[error] - Error, - ErrorUnterminatedString, ErrorUnsupportedStringCharacter, ErrorUnterminatedBlockString, @@ -124,9 +121,6 @@ pub enum TokenKind { #[derive(Logos, Debug)] pub enum StringToken { - #[error] - Error, - #[regex(r#"\\["\\/bfnrt]"#)] EscapedCharacter, @@ -148,19 +142,19 @@ fn lex_string(lexer: &mut Lexer<'_, TokenKind>) -> bool { let mut string_lexer = StringToken::lexer(remainder); while let Some(string_token) = string_lexer.next() { match string_token { - StringToken::Quote => { + Ok(StringToken::Quote) => { lexer.bump(string_lexer.span().end); return true; } - StringToken::LineTerminator => { + Ok(StringToken::LineTerminator) => { lexer.bump(string_lexer.span().start); lexer.extras.error_token = Some(TokenKind::ErrorUnterminatedString); return false; } - StringToken::EscapedCharacter - | StringToken::EscapedUnicode - | StringToken::StringCharacters => {} - StringToken::Error => { + Ok(StringToken::EscapedCharacter) + | Ok(StringToken::EscapedUnicode) + | Ok(StringToken::StringCharacters) => {} + Err(_) => { lexer.extras.error_token = Some(TokenKind::ErrorUnsupportedStringCharacter); return false; } @@ -175,12 +169,12 @@ fn lex_block_string(lexer: &mut Lexer<'_, TokenKind>) -> bool { let mut string_lexer = BlockStringToken::lexer(remainder); while let Some(string_token) = string_lexer.next() { match string_token { - BlockStringToken::TripleQuote => { + Ok(BlockStringToken::TripleQuote) => { lexer.bump(string_lexer.span().end); return true; } - BlockStringToken::EscapedTripleQuote | BlockStringToken::Other => {} - BlockStringToken::Error => unreachable!(), + Ok(BlockStringToken::EscapedTripleQuote) | Ok(BlockStringToken::Other) => {} + Err(_) => unreachable!(), } } lexer.extras.error_token = Some(TokenKind::ErrorUnterminatedBlockString); @@ -189,9 +183,6 @@ fn lex_block_string(lexer: &mut Lexer<'_, TokenKind>) -> bool { #[derive(Logos, Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] pub enum BlockStringToken { - #[error] - Error, - #[token("\\\"\"\"")] EscapedTripleQuote, @@ -226,7 +217,6 @@ impl fmt::Display for TokenKind { TokenKind::Pipe => "pipe ('|')", TokenKind::Spread => "spread ('...')", TokenKind::BlockStringLiteral => "block string (e.g. '\"\"\"hi\"\"\"')", - TokenKind::Error => "error", TokenKind::ErrorFloatLiteralMissingZero => "unsupported number (int or float) literal", TokenKind::ErrorNumberLiteralLeadingZero => "unsupported number (int or float) literal", TokenKind::ErrorNumberLiteralTrailingInvalid => { @@ -246,7 +236,7 @@ impl fmt::Display for TokenKind { mod tests { use super::*; - fn assert_token(source: &str, kind: TokenKind, length: usize) { + fn assert_token(source: &str, kind: Result, length: usize) { let mut lexer = TokenKind::lexer(source); assert_eq!( lexer.next(), @@ -264,52 +254,82 @@ mod tests { #[test] fn test_number_successes() { - assert_token("4", TokenKind::IntegerLiteral, 1); - assert_token("4.123", TokenKind::FloatLiteral, 5); - assert_token("-4", TokenKind::IntegerLiteral, 2); - assert_token("9", TokenKind::IntegerLiteral, 1); - assert_token("0", TokenKind::IntegerLiteral, 1); - assert_token("-4.123", TokenKind::FloatLiteral, 6); - assert_token("0.123", TokenKind::FloatLiteral, 5); - assert_token("123e4", TokenKind::FloatLiteral, 5); - assert_token("123E4", TokenKind::FloatLiteral, 5); - assert_token("123e-4", TokenKind::FloatLiteral, 6); - assert_token("123e+4", TokenKind::FloatLiteral, 6); - assert_token("-1.123e4", TokenKind::FloatLiteral, 8); - assert_token("-1.123E4", TokenKind::FloatLiteral, 8); - assert_token("-1.123e-4", TokenKind::FloatLiteral, 9); - assert_token("-1.123e+4", TokenKind::FloatLiteral, 9); - assert_token("-1.123e4567", TokenKind::FloatLiteral, 11); - assert_token("-0", TokenKind::IntegerLiteral, 2); + assert_token("4", Ok(TokenKind::IntegerLiteral), 1); + assert_token("4.123", Ok(TokenKind::FloatLiteral), 5); + assert_token("-4", Ok(TokenKind::IntegerLiteral), 2); + assert_token("9", Ok(TokenKind::IntegerLiteral), 1); + assert_token("0", Ok(TokenKind::IntegerLiteral), 1); + assert_token("-4.123", Ok(TokenKind::FloatLiteral), 6); + assert_token("0.123", Ok(TokenKind::FloatLiteral), 5); + assert_token("123e4", Ok(TokenKind::FloatLiteral), 5); + assert_token("123E4", Ok(TokenKind::FloatLiteral), 5); + assert_token("123e-4", Ok(TokenKind::FloatLiteral), 6); + assert_token("123e+4", Ok(TokenKind::FloatLiteral), 6); + assert_token("-1.123e4", Ok(TokenKind::FloatLiteral), 8); + assert_token("-1.123E4", Ok(TokenKind::FloatLiteral), 8); + assert_token("-1.123e-4", Ok(TokenKind::FloatLiteral), 9); + assert_token("-1.123e+4", Ok(TokenKind::FloatLiteral), 9); + assert_token("-1.123e4567", Ok(TokenKind::FloatLiteral), 11); + assert_token("-0", Ok(TokenKind::IntegerLiteral), 2); } #[test] fn test_number_failures() { - assert_token("00", TokenKind::ErrorNumberLiteralLeadingZero, 2); - assert_token("01", TokenKind::ErrorNumberLiteralLeadingZero, 2); - assert_token("-01", TokenKind::ErrorNumberLiteralLeadingZero, 3); - assert_token("+1", TokenKind::Error, 1); - assert_token("01.23", TokenKind::ErrorNumberLiteralLeadingZero, 5); - assert_token("1.", TokenKind::ErrorNumberLiteralTrailingInvalid, 2); - assert_token("1e", TokenKind::ErrorNumberLiteralTrailingInvalid, 2); - assert_token("1.e1", TokenKind::ErrorNumberLiteralTrailingInvalid, 2); - assert_token("1.A", TokenKind::ErrorNumberLiteralTrailingInvalid, 2); - assert_token("-A", TokenKind::Error, 1); - assert_token("1.0e", TokenKind::ErrorNumberLiteralTrailingInvalid, 4); - assert_token("1.0eA", TokenKind::ErrorNumberLiteralTrailingInvalid, 4); - assert_token("1.2e3e", TokenKind::ErrorNumberLiteralTrailingInvalid, 6); - assert_token("1.2e3.4", TokenKind::ErrorNumberLiteralTrailingInvalid, 6); - assert_token("1.23.4", TokenKind::ErrorNumberLiteralTrailingInvalid, 5); - assert_token(".123", TokenKind::ErrorFloatLiteralMissingZero, 4); + assert_token("00", Ok(TokenKind::ErrorNumberLiteralLeadingZero), 2); + assert_token("01", Ok(TokenKind::ErrorNumberLiteralLeadingZero), 2); + assert_token("-01", Ok(TokenKind::ErrorNumberLiteralLeadingZero), 3); + assert_token("+1", Err(()), 1); + assert_token("01.23", Ok(TokenKind::ErrorNumberLiteralLeadingZero), 5); + assert_token("1.", Ok(TokenKind::ErrorNumberLiteralTrailingInvalid), 2); + assert_token("1e", Ok(TokenKind::ErrorNumberLiteralTrailingInvalid), 2); + assert_token("1.e1", Ok(TokenKind::ErrorNumberLiteralTrailingInvalid), 2); + assert_token("1.A", Ok(TokenKind::ErrorNumberLiteralTrailingInvalid), 2); + // This should be an error case, but we'll expect it to fail lexing + let mut lexer = TokenKind::lexer("-A"); + assert_eq!(lexer.next(), Some(Err(()))); + assert_token("1.0e", Ok(TokenKind::ErrorNumberLiteralTrailingInvalid), 4); + assert_token("1.0eA", Ok(TokenKind::ErrorNumberLiteralTrailingInvalid), 4); + assert_token( + "1.2e3e", + Ok(TokenKind::ErrorNumberLiteralTrailingInvalid), + 6, + ); + assert_token( + "1.2e3.4", + Ok(TokenKind::ErrorNumberLiteralTrailingInvalid), + 6, + ); + assert_token( + "1.23.4", + Ok(TokenKind::ErrorNumberLiteralTrailingInvalid), + 5, + ); + assert_token(".123", Ok(TokenKind::ErrorFloatLiteralMissingZero), 4); // check that we don't consume trailing valid items - assert_token("1.23.{}", TokenKind::ErrorNumberLiteralTrailingInvalid, 5); - assert_token("1.23. {}", TokenKind::ErrorNumberLiteralTrailingInvalid, 5); - assert_token("1.23. []", TokenKind::ErrorNumberLiteralTrailingInvalid, 5); - assert_token("1.23. foo", TokenKind::ErrorNumberLiteralTrailingInvalid, 5); + assert_token( + "1.23.{}", + Ok(TokenKind::ErrorNumberLiteralTrailingInvalid), + 5, + ); + assert_token( + "1.23. {}", + Ok(TokenKind::ErrorNumberLiteralTrailingInvalid), + 5, + ); + assert_token( + "1.23. []", + Ok(TokenKind::ErrorNumberLiteralTrailingInvalid), + 5, + ); + assert_token( + "1.23. foo", + Ok(TokenKind::ErrorNumberLiteralTrailingInvalid), + 5, + ); assert_token( "1.23. $foo", - TokenKind::ErrorNumberLiteralTrailingInvalid, + Ok(TokenKind::ErrorNumberLiteralTrailingInvalid), 5, ); } @@ -326,94 +346,94 @@ mod tests { "; let mut lexer = TokenKind::lexer(input); - assert_eq!(lexer.next(), Some(TokenKind::Identifier)); + assert_eq!(lexer.next(), Some(Ok(TokenKind::Identifier))); assert_eq!(lexer.slice(), "query"); - assert_eq!(lexer.next(), Some(TokenKind::Identifier)); + assert_eq!(lexer.next(), Some(Ok(TokenKind::Identifier))); assert_eq!(lexer.slice(), "EmptyQuery"); - assert_eq!(lexer.next(), Some(TokenKind::OpenParen)); + assert_eq!(lexer.next(), Some(Ok(TokenKind::OpenParen))); assert_eq!(lexer.slice(), "("); - assert_eq!(lexer.next(), Some(TokenKind::Dollar)); + assert_eq!(lexer.next(), Some(Ok(TokenKind::Dollar))); assert_eq!(lexer.slice(), "$"); - assert_eq!(lexer.next(), Some(TokenKind::Identifier)); + assert_eq!(lexer.next(), Some(Ok(TokenKind::Identifier))); assert_eq!(lexer.slice(), "id"); - assert_eq!(lexer.next(), Some(TokenKind::Colon)); + assert_eq!(lexer.next(), Some(Ok(TokenKind::Colon))); assert_eq!(lexer.slice(), ":"); - assert_eq!(lexer.next(), Some(TokenKind::Identifier)); + assert_eq!(lexer.next(), Some(Ok(TokenKind::Identifier))); assert_eq!(lexer.slice(), "ID"); - assert_eq!(lexer.next(), Some(TokenKind::Exclamation)); + assert_eq!(lexer.next(), Some(Ok(TokenKind::Exclamation))); assert_eq!(lexer.slice(), "!"); - assert_eq!(lexer.next(), Some(TokenKind::CloseParen)); + assert_eq!(lexer.next(), Some(Ok(TokenKind::CloseParen))); assert_eq!(lexer.slice(), ")"); - assert_eq!(lexer.next(), Some(TokenKind::OpenBrace)); + assert_eq!(lexer.next(), Some(Ok(TokenKind::OpenBrace))); assert_eq!(lexer.slice(), "{"); - assert_eq!(lexer.next(), Some(TokenKind::Identifier)); + assert_eq!(lexer.next(), Some(Ok(TokenKind::Identifier))); assert_eq!(lexer.slice(), "node"); - assert_eq!(lexer.next(), Some(TokenKind::OpenParen)); + assert_eq!(lexer.next(), Some(Ok(TokenKind::OpenParen))); assert_eq!(lexer.slice(), "("); - assert_eq!(lexer.next(), Some(TokenKind::Identifier)); + assert_eq!(lexer.next(), Some(Ok(TokenKind::Identifier))); assert_eq!(lexer.slice(), "id"); - assert_eq!(lexer.next(), Some(TokenKind::Colon)); + assert_eq!(lexer.next(), Some(Ok(TokenKind::Colon))); assert_eq!(lexer.slice(), ":"); - assert_eq!(lexer.next(), Some(TokenKind::Dollar)); + assert_eq!(lexer.next(), Some(Ok(TokenKind::Dollar))); assert_eq!(lexer.slice(), "$"); - assert_eq!(lexer.next(), Some(TokenKind::Identifier)); + assert_eq!(lexer.next(), Some(Ok(TokenKind::Identifier))); assert_eq!(lexer.slice(), "id"); - assert_eq!(lexer.next(), Some(TokenKind::CloseParen)); + assert_eq!(lexer.next(), Some(Ok(TokenKind::CloseParen))); assert_eq!(lexer.slice(), ")"); - assert_eq!(lexer.next(), Some(TokenKind::OpenBrace)); + assert_eq!(lexer.next(), Some(Ok(TokenKind::OpenBrace))); assert_eq!(lexer.slice(), "{"); - assert_eq!(lexer.next(), Some(TokenKind::Identifier)); + assert_eq!(lexer.next(), Some(Ok(TokenKind::Identifier))); assert_eq!(lexer.slice(), "id"); - assert_eq!(lexer.next(), Some(TokenKind::At)); + assert_eq!(lexer.next(), Some(Ok(TokenKind::At))); assert_eq!(lexer.slice(), "@"); - assert_eq!(lexer.next(), Some(TokenKind::Identifier)); + assert_eq!(lexer.next(), Some(Ok(TokenKind::Identifier))); assert_eq!(lexer.slice(), "skip"); - assert_eq!(lexer.next(), Some(TokenKind::OpenParen)); + assert_eq!(lexer.next(), Some(Ok(TokenKind::OpenParen))); assert_eq!(lexer.slice(), "("); - assert_eq!(lexer.next(), Some(TokenKind::Identifier)); + assert_eq!(lexer.next(), Some(Ok(TokenKind::Identifier))); assert_eq!(lexer.slice(), "if"); - assert_eq!(lexer.next(), Some(TokenKind::Colon)); + assert_eq!(lexer.next(), Some(Ok(TokenKind::Colon))); assert_eq!(lexer.slice(), ":"); - assert_eq!(lexer.next(), Some(TokenKind::Identifier)); + assert_eq!(lexer.next(), Some(Ok(TokenKind::Identifier))); assert_eq!(lexer.slice(), "false"); - assert_eq!(lexer.next(), Some(TokenKind::CloseParen)); + assert_eq!(lexer.next(), Some(Ok(TokenKind::CloseParen))); assert_eq!(lexer.slice(), ")"); - assert_eq!(lexer.next(), Some(TokenKind::Spread)); + assert_eq!(lexer.next(), Some(Ok(TokenKind::Spread))); assert_eq!(lexer.slice(), "..."); - assert_eq!(lexer.next(), Some(TokenKind::Identifier)); + assert_eq!(lexer.next(), Some(Ok(TokenKind::Identifier))); assert_eq!(lexer.slice(), "E1"); - assert_eq!(lexer.next(), Some(TokenKind::CloseBrace)); + assert_eq!(lexer.next(), Some(Ok(TokenKind::CloseBrace))); assert_eq!(lexer.slice(), "}"); - assert_eq!(lexer.next(), Some(TokenKind::CloseBrace)); + assert_eq!(lexer.next(), Some(Ok(TokenKind::CloseBrace))); assert_eq!(lexer.slice(), "}"); assert_eq!(lexer.next(), None); @@ -429,13 +449,13 @@ mod tests { "#; let mut lexer = TokenKind::lexer(input); - assert_eq!(lexer.next(), Some(TokenKind::StringLiteral)); + assert_eq!(lexer.next(), Some(Ok(TokenKind::StringLiteral))); assert_eq!(lexer.slice(), "\"test\""); - assert_eq!(lexer.next(), Some(TokenKind::StringLiteral)); + assert_eq!(lexer.next(), Some(Ok(TokenKind::StringLiteral))); assert_eq!(lexer.slice(), r#""escaped \" quote""#); - assert_eq!(lexer.next(), Some(TokenKind::Error)); + assert_eq!(lexer.next(), Some(Err(()))); assert_eq!( lexer.extras.error_token, Some(TokenKind::ErrorUnterminatedString) @@ -454,25 +474,25 @@ mod tests { "#; let mut lexer = TokenKind::lexer(input); - assert_eq!(lexer.next(), Some(TokenKind::OpenBrace)); + assert_eq!(lexer.next(), Some(Ok(TokenKind::OpenBrace))); assert_eq!(lexer.slice(), "{"); - assert_eq!(lexer.next(), Some(TokenKind::Error)); + assert_eq!(lexer.next(), Some(Err(()))); assert_eq!(lexer.slice(), "%"); - assert_eq!(lexer.next(), Some(TokenKind::Error)); + assert_eq!(lexer.next(), Some(Err(()))); assert_eq!(lexer.slice(), "%"); - assert_eq!(lexer.next(), Some(TokenKind::Error)); + assert_eq!(lexer.next(), Some(Err(()))); assert_eq!(lexer.slice(), "%"); - assert_eq!(lexer.next(), Some(TokenKind::Identifier)); + assert_eq!(lexer.next(), Some(Ok(TokenKind::Identifier))); assert_eq!(lexer.slice(), "__typename"); - assert_eq!(lexer.next(), Some(TokenKind::Error)); + assert_eq!(lexer.next(), Some(Err(()))); assert_eq!(lexer.slice(), "*"); - assert_eq!(lexer.next(), Some(TokenKind::CloseBrace)); + assert_eq!(lexer.next(), Some(Ok(TokenKind::CloseBrace))); assert_eq!(lexer.slice(), "}"); assert_eq!(lexer.next(), None); @@ -495,16 +515,16 @@ mod tests { "#; let mut lexer = TokenKind::lexer(input); - assert_eq!(lexer.next(), Some(TokenKind::BlockStringLiteral)); + assert_eq!(lexer.next(), Some(Ok(TokenKind::BlockStringLiteral))); assert_eq!(lexer.slice(), r#""""tes\"""t""""#); - assert_eq!(lexer.next(), Some(TokenKind::BlockStringLiteral)); + assert_eq!(lexer.next(), Some(Ok(TokenKind::BlockStringLiteral))); assert_eq!(lexer.slice(), r#""""""""#); - assert_eq!(lexer.next(), Some(TokenKind::BlockStringLiteral)); + assert_eq!(lexer.next(), Some(Ok(TokenKind::BlockStringLiteral))); assert_eq!(lexer.slice(), r#"""""" """"#); - assert_eq!(lexer.next(), Some(TokenKind::BlockStringLiteral)); + assert_eq!(lexer.next(), Some(Ok(TokenKind::BlockStringLiteral))); assert_eq!( lexer.slice(), r#"""" @@ -513,7 +533,7 @@ mod tests { """"# ); - assert_eq!(lexer.next(), Some(TokenKind::Error)); + assert_eq!(lexer.next(), Some(Err(()))); assert_eq!( lexer.extras.error_token, Some(TokenKind::ErrorUnterminatedBlockString) diff --git a/compiler/crates/graphql-syntax/src/lib.rs b/compiler/crates/graphql-syntax/src/lib.rs index 8b37cc4868b29..7e523f2ec09a8 100644 --- a/compiler/crates/graphql-syntax/src/lib.rs +++ b/compiler/crates/graphql-syntax/src/lib.rs @@ -105,10 +105,10 @@ pub fn parse_schema_document( /// Parses a GraphQL schema document into a list of slices of the original /// source text where each slice is a type system definition. -pub fn parse_schema_document_into_type_system_definitions<'a>( - source: &'a str, +pub fn parse_schema_document_into_type_system_definitions( + source: &str, source_location: SourceLocationKey, -) -> DiagnosticsResult> { +) -> DiagnosticsResult> { let features = ParserFeatures::default(); let parser = Parser::new(source, source_location, features); parser.parse_schema_document_into_type_system_definitions() diff --git a/compiler/crates/graphql-syntax/src/node/constant_value.rs b/compiler/crates/graphql-syntax/src/node/constant_value.rs index 5324063d2c499..e75d74b8d6210 100644 --- a/compiler/crates/graphql-syntax/src/node/constant_value.rs +++ b/compiler/crates/graphql-syntax/src/node/constant_value.rs @@ -26,7 +26,7 @@ pub enum ConstantValue { } macro_rules! generate_unwrap_fn { - ($fn_name:ident,$self:ident,$t:ty,$cv:pat => $result:expr) => { + ($fn_name:ident,$self:ident,$t:ty,$cv:pat => $result:expr_2021) => { pub fn $fn_name(&$self) -> $t { match $self { $cv => $result, @@ -63,22 +63,30 @@ impl ConstantValue { } } + pub fn get_list_literal(&self) -> Option<&List> { + match self { + ConstantValue::List(list) => Some(list), + _ => None, + } + } + generate_unwrap_fn!(unwrap_int, self, i64, ConstantValue::Int(i) => i.value); generate_unwrap_fn!(unwrap_float, self, FloatValue, ConstantValue::Float(f) => f.value); generate_unwrap_fn!(unwrap_boolean, self, bool, ConstantValue::Boolean(b) => b.value); generate_unwrap_fn!(unwrap_string, self, StringKey, ConstantValue::String(s) => s.value); generate_unwrap_fn!(unwrap_enum, self, StringKey, ConstantValue::Enum(e) => e.value); + generate_unwrap_fn!(unwrap_list, self, &List, ConstantValue::List(l) => l); } impl fmt::Display for ConstantValue { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - ConstantValue::Int(value) => f.write_fmt(format_args!("{}", value)), - ConstantValue::Float(value) => f.write_fmt(format_args!("{}", value)), - ConstantValue::String(value) => f.write_fmt(format_args!("\"{}\"", value)), - ConstantValue::Boolean(value) => f.write_fmt(format_args!("{}", value)), + ConstantValue::Int(value) => f.write_fmt(format_args!("{value}")), + ConstantValue::Float(value) => f.write_fmt(format_args!("{value}",)), + ConstantValue::String(value) => f.write_fmt(format_args!("\"{value}\"")), + ConstantValue::Boolean(value) => f.write_fmt(format_args!("{value}")), ConstantValue::Null(_) => f.write_str("null"), - ConstantValue::Enum(value) => f.write_fmt(format_args!("{}", value)), + ConstantValue::Enum(value) => f.write_fmt(format_args!("{value}")), ConstantValue::List(value) => f.write_fmt(format_args!( "[{}]", value diff --git a/compiler/crates/graphql-syntax/src/node/executable.rs b/compiler/crates/graphql-syntax/src/node/executable.rs index b2cb4d2fae610..2db18f1e342e0 100644 --- a/compiler/crates/graphql-syntax/src/node/executable.rs +++ b/compiler/crates/graphql-syntax/src/node/executable.rs @@ -160,6 +160,11 @@ impl fmt::Display for Alias { } } +pub enum IdentifierOrString { + Identifier(Identifier), + StringNode(StringNode), +} + #[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] pub struct TypeCondition { pub span: Span, diff --git a/compiler/crates/graphql-syntax/src/parser.rs b/compiler/crates/graphql-syntax/src/parser.rs index 0789b371f1276..82eb7b441d4e9 100644 --- a/compiler/crates/graphql-syntax/src/parser.rs +++ b/compiler/crates/graphql-syntax/src/parser.rs @@ -34,6 +34,9 @@ pub enum FragmentArgumentSyntaxKind { pub struct ParserFeatures { /// Whether and how to enable the experimental fragment variables definitions syntax pub fragment_argument_capability: FragmentArgumentSyntaxKind, + + /// Allows string literals as field aliases, as in: `query { "alias": field }` + pub allow_string_literal_alias: bool, } impl ParserFeatures { @@ -1792,7 +1795,6 @@ impl<'a> Parser<'a> { match token.kind { TokenKind::Identifier => { self.advance_identifier()?; - () } TokenKind::OpenBracket => { self.advance_kind(TokenKind::OpenBracket)?; // open @@ -1906,7 +1908,7 @@ impl<'a> Parser<'a> { let token = self.peek(); match token.kind { TokenKind::Spread => self.parse_spread(), - TokenKind::Identifier => self.parse_field(), + TokenKind::Identifier | TokenKind::StringLiteral => self.parse_field(), // hint for invalid spreads TokenKind::Period | TokenKind::PeriodPeriod => { let error = Diagnostic::error( @@ -1927,13 +1929,49 @@ impl<'a> Parser<'a> { } } + fn parse_name_or_string_literal(&mut self) -> ParseResult { + let token_kind = self.peek_token_kind(); + if token_kind == TokenKind::Identifier { + return Ok(IdentifierOrString::Identifier(self.parse_identifier()?)); + } + + let token = self.parse_token(); + if !self.features.allow_string_literal_alias { + self.record_error(Diagnostic::error( + SyntaxError::Expected(TokenKind::Identifier), + Location::new(self.source_location, token.span), + )); + return Err(()); + } + + match token.kind { + TokenKind::StringLiteral => Ok(IdentifierOrString::StringNode( + self.parse_string_literal(token, self.source(&token)), + )), + _ => { + self.record_error(Diagnostic::error( + SyntaxError::Expected(TokenKind::StringLiteral), + Location::new(self.source_location, token.span), + )); + Err(()) + } + } + } + /// Field : Alias? Name Arguments? Directives? SelectionSet? fn parse_field(&mut self) -> ParseResult { let start = self.index(); - let name = self.parse_identifier()?; + let maybe_alias = self.parse_name_or_string_literal()?; let (name, alias) = if self.peek_token_kind() == TokenKind::Colon { let colon = self.parse_kind(TokenKind::Colon)?; - let alias = name; + let alias = match maybe_alias { + IdentifierOrString::Identifier(node) => node, + IdentifierOrString::StringNode(node) => Identifier { + span: node.token.span, + token: node.token, + value: node.value, + }, + }; let name = { match self.peek_token_kind() { TokenKind::Identifier => self.parse_identifier()?, @@ -1960,8 +1998,18 @@ impl<'a> Parser<'a> { }), ) } else { - (name, None) + match maybe_alias { + IdentifierOrString::StringNode(node) => { + self.record_error(Diagnostic::error( + SyntaxError::Expected(TokenKind::Identifier), + Location::new(self.source_location, node.token.span), + )); + (self.empty_identifier(), None) + } + IdentifierOrString::Identifier(node) => (node, None), + } }; + let arguments = self.parse_optional_arguments()?; let directives = self.parse_directives()?; if self.peek_token_kind() == TokenKind::OpenBrace { @@ -2387,13 +2435,9 @@ impl<'a> Parser<'a> { let token = self.parse_token(); let source = self.source(&token); match &token.kind { - TokenKind::StringLiteral => { - let value = source[1..source.len() - 1].to_string(); - Ok(ConstantValue::String(StringNode { - token, - value: value.intern(), - })) - } + TokenKind::StringLiteral => Ok(ConstantValue::String( + self.parse_string_literal(token, source), + )), TokenKind::BlockStringLiteral => { let value = clean_block_string_literal(source); Ok(ConstantValue::String(StringNode { @@ -2497,6 +2541,14 @@ impl<'a> Parser<'a> { } } + fn parse_string_literal(&self, token: Token, source: &str) -> StringNode { + let value = source[1..source.len() - 1].to_string(); + StringNode { + token, + value: value.intern(), + } + } + /// IntValue /// FloatValue /// StringValue @@ -2896,9 +2948,9 @@ impl<'a> Parser<'a> { fn parse_token(&mut self) -> Token { // Skip over (and record) any invalid tokens until either a valid token or an EOF is encountered loop { - let kind = self.lexer.next().unwrap_or(TokenKind::EndOfFile); + let kind = self.lexer.next().unwrap_or(Ok(TokenKind::EndOfFile)); match kind { - TokenKind::Error => { + Err(_) => { if let Some(error_token_kind) = self.lexer.extras.error_token { // Reset the error token self.lexer.extras.error_token = None; @@ -2922,7 +2974,7 @@ impl<'a> Parser<'a> { self.record_error(error); } } - _ => { + Ok(kind) => { self.end_index = self.current.span.end; let span = self.lexer_span(); return std::mem::replace(&mut self.current, Token { kind, span }); @@ -2984,13 +3036,13 @@ fn clean_block_string_literal(source: &str) -> String { while formatted_lines .front() - .map_or(false, |line| line_is_whitespace(line)) + .is_some_and(|line| line_is_whitespace(line)) { formatted_lines.pop_front(); } while formatted_lines .back() - .map_or(false, |line| line_is_whitespace(line)) + .is_some_and(|line| line_is_whitespace(line)) { formatted_lines.pop_back(); } @@ -3004,7 +3056,7 @@ fn get_common_indent(source: &str) -> usize { let mut common_indent: Option = None; for line in lines { if let Some((first_index, _)) = line.match_indices(is_not_whitespace).next() { - if common_indent.map_or(true, |indent| first_index < indent) { + if common_indent.is_none_or(|indent| first_index < indent) { common_indent = Some(first_index) } } diff --git a/compiler/crates/graphql-syntax/tests/parse_document_with_enquoted_alias.rs b/compiler/crates/graphql-syntax/tests/parse_document_with_enquoted_alias.rs new file mode 100644 index 0000000000000..90fbca77e3d06 --- /dev/null +++ b/compiler/crates/graphql-syntax/tests/parse_document_with_enquoted_alias.rs @@ -0,0 +1,39 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +use common::Diagnostic; +use common::SourceLocationKey; +use common::TextSource; +use fixture_tests::Fixture; +use graphql_cli::DiagnosticPrinter; +use graphql_syntax::ParserFeatures; +use graphql_syntax::parse_executable_with_features; + +pub async fn transform_fixture(fixture: &Fixture<'_>) -> Result { + parse_executable_with_features( + fixture.content, + SourceLocationKey::standalone(fixture.file_name), + ParserFeatures { + allow_string_literal_alias: true, + ..Default::default() + }, + ) + .map(|x| format!("{:#?}", x)) + .map_err(|diagnostics| diagnostics_to_sorted_string(fixture.content, &diagnostics)) +} + +// NOTE: copied from graphql-test-helpers to avoid cyclic dependency breaking Rust Analyzer +fn diagnostics_to_sorted_string(source: &str, diagnostics: &[Diagnostic]) -> String { + let printer = + DiagnosticPrinter::new(|_| Some(TextSource::from_whole_document(source.to_string()))); + let mut printed = diagnostics + .iter() + .map(|diagnostic| printer.diagnostic_to_string(diagnostic)) + .collect::>(); + printed.sort(); + printed.join("\n\n") +} diff --git a/compiler/crates/graphql-syntax/tests/parse_document_with_enquoted_alias/fixtures/enquoted_alias_with_enquoted_field.invalid.expected b/compiler/crates/graphql-syntax/tests/parse_document_with_enquoted_alias/fixtures/enquoted_alias_with_enquoted_field.invalid.expected new file mode 100644 index 0000000000000..b3bf97ed5a644 --- /dev/null +++ b/compiler/crates/graphql-syntax/tests/parse_document_with_enquoted_alias/fixtures/enquoted_alias_with_enquoted_field.invalid.expected @@ -0,0 +1,23 @@ +==================================== INPUT ==================================== +# expected-to-throw + +query EnquotedAliasWithEnquotedFieldName { + "alias": "field_name" +} +==================================== ERROR ==================================== +✖︎ Expected a non-variable identifier (e.g. 'x' or 'Foo') + + enquoted_alias_with_enquoted_field.invalid.graphql:4:12 + 3 │ query EnquotedAliasWithEnquotedFieldName { + 4 │ "alias": "field_name" + │ ^^^^^^^^^^^^ + 5 │ } + + +✖︎ Incomplete field alias, expected non-variable identifier (e.g. 'x' or 'Foo') but found string literal (e.g. '"..."') + + enquoted_alias_with_enquoted_field.invalid.graphql:4:3 + 3 │ query EnquotedAliasWithEnquotedFieldName { + 4 │ "alias": "field_name" + │ ^^^^^^^^ + 5 │ } diff --git a/compiler/crates/graphql-syntax/tests/parse_document_with_enquoted_alias/fixtures/enquoted_alias_with_enquoted_field.invalid.graphql b/compiler/crates/graphql-syntax/tests/parse_document_with_enquoted_alias/fixtures/enquoted_alias_with_enquoted_field.invalid.graphql new file mode 100644 index 0000000000000..48c499cd5051e --- /dev/null +++ b/compiler/crates/graphql-syntax/tests/parse_document_with_enquoted_alias/fixtures/enquoted_alias_with_enquoted_field.invalid.graphql @@ -0,0 +1,5 @@ +# expected-to-throw + +query EnquotedAliasWithEnquotedFieldName { + "alias": "field_name" +} diff --git a/compiler/crates/graphql-syntax/tests/parse_document_with_enquoted_alias/fixtures/enquoted_alias_with_escape.expected b/compiler/crates/graphql-syntax/tests/parse_document_with_enquoted_alias/fixtures/enquoted_alias_with_escape.expected new file mode 100644 index 0000000000000..78415b1802253 --- /dev/null +++ b/compiler/crates/graphql-syntax/tests/parse_document_with_enquoted_alias/fixtures/enquoted_alias_with_escape.expected @@ -0,0 +1,77 @@ +==================================== INPUT ==================================== +query EnquotedAlias { + "1$ali\"as": escaped_quote_alias +} +==================================== OUTPUT =================================== +ExecutableDocument { + span: 0:59, + definitions: [ + OperationDefinition { + location: enquoted_alias_with_escape.graphql:0:58, + operation: Some( + ( + Token { + span: 0:5, + kind: Identifier, + }, + Query, + ), + ), + name: Some( + Identifier { + span: 6:19, + token: Token { + span: 6:19, + kind: Identifier, + }, + value: "EnquotedAlias", + }, + ), + variable_definitions: None, + directives: [], + selections: List { + span: 20:58, + start: Token { + span: 20:21, + kind: OpenBrace, + }, + items: [ + ScalarField { + span: 24:56, + alias: Some( + Alias { + span: 24:56, + alias: Identifier { + span: 24:35, + token: Token { + span: 24:35, + kind: StringLiteral, + }, + value: "1$ali\\\"as", + }, + colon: Token { + span: 35:36, + kind: Colon, + }, + }, + ), + name: Identifier { + span: 37:56, + token: Token { + span: 37:56, + kind: Identifier, + }, + value: "escaped_quote_alias", + }, + arguments: None, + directives: [], + }, + ], + end: Token { + span: 57:58, + kind: CloseBrace, + }, + }, + }, + ], +} diff --git a/compiler/crates/graphql-syntax/tests/parse_document_with_enquoted_alias/fixtures/enquoted_alias_with_escape.graphql b/compiler/crates/graphql-syntax/tests/parse_document_with_enquoted_alias/fixtures/enquoted_alias_with_escape.graphql new file mode 100644 index 0000000000000..cea038bc12eb2 --- /dev/null +++ b/compiler/crates/graphql-syntax/tests/parse_document_with_enquoted_alias/fixtures/enquoted_alias_with_escape.graphql @@ -0,0 +1,3 @@ +query EnquotedAlias { + "1$ali\"as": escaped_quote_alias +} diff --git a/compiler/crates/graphql-syntax/tests/parse_document_with_enquoted_alias/fixtures/enquoted_field_name.invalid.expected b/compiler/crates/graphql-syntax/tests/parse_document_with_enquoted_alias/fixtures/enquoted_field_name.invalid.expected new file mode 100644 index 0000000000000..97723b1676c27 --- /dev/null +++ b/compiler/crates/graphql-syntax/tests/parse_document_with_enquoted_alias/fixtures/enquoted_field_name.invalid.expected @@ -0,0 +1,14 @@ +==================================== INPUT ==================================== +# expected-to-throw + +query EnquotedFieldName { + "field_name" +} +==================================== ERROR ==================================== +✖︎ Expected a non-variable identifier (e.g. 'x' or 'Foo') + + enquoted_field_name.invalid.graphql:4:3 + 3 │ query EnquotedFieldName { + 4 │ "field_name" + │ ^^^^^^^^^^^^ + 5 │ } diff --git a/compiler/crates/graphql-syntax/tests/parse_document_with_enquoted_alias/fixtures/enquoted_field_name.invalid.graphql b/compiler/crates/graphql-syntax/tests/parse_document_with_enquoted_alias/fixtures/enquoted_field_name.invalid.graphql new file mode 100644 index 0000000000000..44d10fa6c7451 --- /dev/null +++ b/compiler/crates/graphql-syntax/tests/parse_document_with_enquoted_alias/fixtures/enquoted_field_name.invalid.graphql @@ -0,0 +1,5 @@ +# expected-to-throw + +query EnquotedFieldName { + "field_name" +} diff --git a/compiler/crates/graphql-syntax/tests/parse_document_with_enquoted_alias/fixtures/simple_enquoted_alias.expected b/compiler/crates/graphql-syntax/tests/parse_document_with_enquoted_alias/fixtures/simple_enquoted_alias.expected new file mode 100644 index 0000000000000..88f30c4e8d678 --- /dev/null +++ b/compiler/crates/graphql-syntax/tests/parse_document_with_enquoted_alias/fixtures/simple_enquoted_alias.expected @@ -0,0 +1,77 @@ +==================================== INPUT ==================================== +query EnquotedAlias { + "1$alias": simple_alias, +} +==================================== OUTPUT =================================== +ExecutableDocument { + span: 0:51, + definitions: [ + OperationDefinition { + location: simple_enquoted_alias.graphql:0:50, + operation: Some( + ( + Token { + span: 0:5, + kind: Identifier, + }, + Query, + ), + ), + name: Some( + Identifier { + span: 6:19, + token: Token { + span: 6:19, + kind: Identifier, + }, + value: "EnquotedAlias", + }, + ), + variable_definitions: None, + directives: [], + selections: List { + span: 20:50, + start: Token { + span: 20:21, + kind: OpenBrace, + }, + items: [ + ScalarField { + span: 24:47, + alias: Some( + Alias { + span: 24:47, + alias: Identifier { + span: 24:33, + token: Token { + span: 24:33, + kind: StringLiteral, + }, + value: "1$alias", + }, + colon: Token { + span: 33:34, + kind: Colon, + }, + }, + ), + name: Identifier { + span: 35:47, + token: Token { + span: 35:47, + kind: Identifier, + }, + value: "simple_alias", + }, + arguments: None, + directives: [], + }, + ], + end: Token { + span: 49:50, + kind: CloseBrace, + }, + }, + }, + ], +} diff --git a/compiler/crates/graphql-syntax/tests/parse_document_with_enquoted_alias/fixtures/simple_enquoted_alias.graphql b/compiler/crates/graphql-syntax/tests/parse_document_with_enquoted_alias/fixtures/simple_enquoted_alias.graphql new file mode 100644 index 0000000000000..250aba5a8fb3d --- /dev/null +++ b/compiler/crates/graphql-syntax/tests/parse_document_with_enquoted_alias/fixtures/simple_enquoted_alias.graphql @@ -0,0 +1,3 @@ +query EnquotedAlias { + "1$alias": simple_alias, +} diff --git a/compiler/crates/graphql-syntax/tests/parse_document_with_enquoted_alias_test.rs b/compiler/crates/graphql-syntax/tests/parse_document_with_enquoted_alias_test.rs new file mode 100644 index 0000000000000..c84a61ff6f45c --- /dev/null +++ b/compiler/crates/graphql-syntax/tests/parse_document_with_enquoted_alias_test.rs @@ -0,0 +1,41 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @generated SignedSource<<8206a3292d96e0cd429bb5089b3d2e71>> + */ + +mod parse_document_with_enquoted_alias; + +use parse_document_with_enquoted_alias::transform_fixture; +use fixture_tests::test_fixture; + +#[tokio::test] +async fn enquoted_alias_with_enquoted_field_invalid() { + let input = include_str!("parse_document_with_enquoted_alias/fixtures/enquoted_alias_with_enquoted_field.invalid.graphql"); + let expected = include_str!("parse_document_with_enquoted_alias/fixtures/enquoted_alias_with_enquoted_field.invalid.expected"); + test_fixture(transform_fixture, file!(), "enquoted_alias_with_enquoted_field.invalid.graphql", "parse_document_with_enquoted_alias/fixtures/enquoted_alias_with_enquoted_field.invalid.expected", input, expected).await; +} + +#[tokio::test] +async fn enquoted_alias_with_escape() { + let input = include_str!("parse_document_with_enquoted_alias/fixtures/enquoted_alias_with_escape.graphql"); + let expected = include_str!("parse_document_with_enquoted_alias/fixtures/enquoted_alias_with_escape.expected"); + test_fixture(transform_fixture, file!(), "enquoted_alias_with_escape.graphql", "parse_document_with_enquoted_alias/fixtures/enquoted_alias_with_escape.expected", input, expected).await; +} + +#[tokio::test] +async fn enquoted_field_name_invalid() { + let input = include_str!("parse_document_with_enquoted_alias/fixtures/enquoted_field_name.invalid.graphql"); + let expected = include_str!("parse_document_with_enquoted_alias/fixtures/enquoted_field_name.invalid.expected"); + test_fixture(transform_fixture, file!(), "enquoted_field_name.invalid.graphql", "parse_document_with_enquoted_alias/fixtures/enquoted_field_name.invalid.expected", input, expected).await; +} + +#[tokio::test] +async fn simple_enquoted_alias() { + let input = include_str!("parse_document_with_enquoted_alias/fixtures/simple_enquoted_alias.graphql"); + let expected = include_str!("parse_document_with_enquoted_alias/fixtures/simple_enquoted_alias.expected"); + test_fixture(transform_fixture, file!(), "simple_enquoted_alias.graphql", "parse_document_with_enquoted_alias/fixtures/simple_enquoted_alias.expected", input, expected).await; +} diff --git a/compiler/crates/graphql-syntax/tests/parse_document_with_features.rs b/compiler/crates/graphql-syntax/tests/parse_document_with_features.rs index 9a747d03c8306..6cce9a261e7c4 100644 --- a/compiler/crates/graphql-syntax/tests/parse_document_with_features.rs +++ b/compiler/crates/graphql-syntax/tests/parse_document_with_features.rs @@ -10,9 +10,9 @@ use common::SourceLocationKey; use common::TextSource; use fixture_tests::Fixture; use graphql_cli::DiagnosticPrinter; -use graphql_syntax::parse_document_with_features; use graphql_syntax::FragmentArgumentSyntaxKind; use graphql_syntax::ParserFeatures; +use graphql_syntax::parse_document_with_features; pub async fn transform_fixture(fixture: &Fixture<'_>) -> Result { parse_document_with_features( @@ -21,6 +21,7 @@ pub async fn transform_fixture(fixture: &Fixture<'_>) -> Result ParserFeatures { fragment_argument_capability: FragmentArgumentSyntaxKind::SpreadArgumentsAndFragmentVariableDefinitions, + allow_string_literal_alias: false, }, ) .map(|x| format!("{:#?}", x)) diff --git a/compiler/crates/graphql-syntax/tests/parse_executable_document/fixtures/enquoted_alias.invalid.expected b/compiler/crates/graphql-syntax/tests/parse_executable_document/fixtures/enquoted_alias.invalid.expected new file mode 100644 index 0000000000000..f3d99b658d1d0 --- /dev/null +++ b/compiler/crates/graphql-syntax/tests/parse_executable_document/fixtures/enquoted_alias.invalid.expected @@ -0,0 +1,14 @@ +==================================== INPUT ==================================== +# expected-to-throw + +query EnquotedAlias { + "1$alias": simple_alias, +} +==================================== ERROR ==================================== +✖︎ Expected a non-variable identifier (e.g. 'x' or 'Foo') + + enquoted_alias.invalid.graphql:4:3 + 3 │ query EnquotedAlias { + 4 │ "1$alias": simple_alias, + │ ^^^^^^^^^ + 5 │ } diff --git a/compiler/crates/graphql-syntax/tests/parse_executable_document/fixtures/enquoted_alias.invalid.graphql b/compiler/crates/graphql-syntax/tests/parse_executable_document/fixtures/enquoted_alias.invalid.graphql new file mode 100644 index 0000000000000..638052161f6aa --- /dev/null +++ b/compiler/crates/graphql-syntax/tests/parse_executable_document/fixtures/enquoted_alias.invalid.graphql @@ -0,0 +1,5 @@ +# expected-to-throw + +query EnquotedAlias { + "1$alias": simple_alias, +} diff --git a/compiler/crates/graphql-syntax/tests/parse_executable_document/fixtures/enquoted_field_name.invalid.expected b/compiler/crates/graphql-syntax/tests/parse_executable_document/fixtures/enquoted_field_name.invalid.expected new file mode 100644 index 0000000000000..97723b1676c27 --- /dev/null +++ b/compiler/crates/graphql-syntax/tests/parse_executable_document/fixtures/enquoted_field_name.invalid.expected @@ -0,0 +1,14 @@ +==================================== INPUT ==================================== +# expected-to-throw + +query EnquotedFieldName { + "field_name" +} +==================================== ERROR ==================================== +✖︎ Expected a non-variable identifier (e.g. 'x' or 'Foo') + + enquoted_field_name.invalid.graphql:4:3 + 3 │ query EnquotedFieldName { + 4 │ "field_name" + │ ^^^^^^^^^^^^ + 5 │ } diff --git a/compiler/crates/graphql-syntax/tests/parse_executable_document/fixtures/enquoted_field_name.invalid.graphql b/compiler/crates/graphql-syntax/tests/parse_executable_document/fixtures/enquoted_field_name.invalid.graphql new file mode 100644 index 0000000000000..44d10fa6c7451 --- /dev/null +++ b/compiler/crates/graphql-syntax/tests/parse_executable_document/fixtures/enquoted_field_name.invalid.graphql @@ -0,0 +1,5 @@ +# expected-to-throw + +query EnquotedFieldName { + "field_name" +} diff --git a/compiler/crates/graphql-syntax/tests/parse_executable_document_test.rs b/compiler/crates/graphql-syntax/tests/parse_executable_document_test.rs index 7071897bca421..404817c58c06f 100644 --- a/compiler/crates/graphql-syntax/tests/parse_executable_document_test.rs +++ b/compiler/crates/graphql-syntax/tests/parse_executable_document_test.rs @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<4934dcac68b7399c330d6808663cc57f>> + * @generated SignedSource<<9c4963ceb07420900e7e9b2602c71a71>> */ mod parse_executable_document; @@ -19,6 +19,20 @@ async fn block_string() { test_fixture(transform_fixture, file!(), "block_string.graphql", "parse_executable_document/fixtures/block_string.expected", input, expected).await; } +#[tokio::test] +async fn enquoted_alias_invalid() { + let input = include_str!("parse_executable_document/fixtures/enquoted_alias.invalid.graphql"); + let expected = include_str!("parse_executable_document/fixtures/enquoted_alias.invalid.expected"); + test_fixture(transform_fixture, file!(), "enquoted_alias.invalid.graphql", "parse_executable_document/fixtures/enquoted_alias.invalid.expected", input, expected).await; +} + +#[tokio::test] +async fn enquoted_field_name_invalid() { + let input = include_str!("parse_executable_document/fixtures/enquoted_field_name.invalid.graphql"); + let expected = include_str!("parse_executable_document/fixtures/enquoted_field_name.invalid.expected"); + test_fixture(transform_fixture, file!(), "enquoted_field_name.invalid.graphql", "parse_executable_document/fixtures/enquoted_field_name.invalid.expected", input, expected).await; +} + #[tokio::test] async fn fragment_with_variable_defs_invalid() { let input = include_str!("parse_executable_document/fixtures/fragment_with_variable_defs.invalid.graphql"); diff --git a/compiler/crates/graphql-test-helpers/Cargo.toml b/compiler/crates/graphql-test-helpers/Cargo.toml index b6f482b324340..66482d1254cb6 100644 --- a/compiler/crates/graphql-test-helpers/Cargo.toml +++ b/compiler/crates/graphql-test-helpers/Cargo.toml @@ -4,7 +4,7 @@ name = "graphql-test-helpers" version = "0.0.0" authors = ["Facebook"] -edition = "2021" +edition = "2024" repository = "https://github.com/facebook/relay" license = "MIT" diff --git a/compiler/crates/graphql-test-helpers/src/lib.rs b/compiler/crates/graphql-test-helpers/src/lib.rs index be2a75c25d0b1..79ebfa2815451 100644 --- a/compiler/crates/graphql-test-helpers/src/lib.rs +++ b/compiler/crates/graphql-test-helpers/src/lib.rs @@ -17,15 +17,15 @@ use common::TextSource; use fixture_tests::Fixture; use graphql_cli::DiagnosticPrinter; use graphql_cli::Sources as DiagnosticPrinterSources; -use graphql_ir::build_ir_with_extra_features; use graphql_ir::BuilderOptions; use graphql_ir::FragmentVariablesSemantic; use graphql_ir::Program; use graphql_ir::RelayMode; +use graphql_ir::build_ir_with_extra_features; use graphql_syntax::parse_executable; +use graphql_text_printer::PrinterOptions; use graphql_text_printer::print_fragment; use graphql_text_printer::print_operation; -use graphql_text_printer::PrinterOptions; pub use project_fixture::ProjectFixture; use relay_test_schema::get_test_schema; use relay_test_schema::get_test_schema_with_located_extensions; diff --git a/compiler/crates/graphql-test-helpers/src/project_fixture.rs b/compiler/crates/graphql-test-helpers/src/project_fixture.rs index fb0e227733ab0..06cbed3aa7ce8 100644 --- a/compiler/crates/graphql-test-helpers/src/project_fixture.rs +++ b/compiler/crates/graphql-test-helpers/src/project_fixture.rs @@ -6,9 +6,9 @@ */ use std::fs; +use std::path::MAIN_SEPARATOR; use std::path::Path; use std::path::PathBuf; -use std::path::MAIN_SEPARATOR; use fnv::FnvHashMap; use walkdir::WalkDir; diff --git a/compiler/crates/graphql-text-printer/Cargo.toml b/compiler/crates/graphql-text-printer/Cargo.toml index 71512a1e12c10..3444bc4b51baf 100644 --- a/compiler/crates/graphql-text-printer/Cargo.toml +++ b/compiler/crates/graphql-text-printer/Cargo.toml @@ -4,7 +4,7 @@ name = "graphql-text-printer" version = "0.0.0" authors = ["Facebook"] -edition = "2021" +edition = "2024" repository = "https://github.com/facebook/relay" license = "MIT" @@ -32,4 +32,4 @@ schema = { path = "../schema" } fixture-tests = { path = "../fixture-tests" } relay-test-schema = { path = "../relay-test-schema" } relay-transforms = { path = "../relay-transforms" } -tokio = { version = "1.41.0", features = ["full", "test-util", "tracing"] } +tokio = { version = "1.46.1", features = ["full", "test-util", "tracing"] } diff --git a/compiler/crates/graphql-text-printer/src/lib.rs b/compiler/crates/graphql-text-printer/src/lib.rs index 4fb8d6ec459fb..dcca2e4c29148 100644 --- a/compiler/crates/graphql-text-printer/src/lib.rs +++ b/compiler/crates/graphql-text-printer/src/lib.rs @@ -16,8 +16,9 @@ mod print_to_text; pub use print_ast_to_text::print_executable_definition_ast; pub use print_ast_to_text::print_fragment_ast; pub use print_ast_to_text::print_operation_ast; -pub use print_full_operation::print_full_operation; pub use print_full_operation::OperationPrinter; +pub use print_full_operation::print_full_operation; +pub use print_to_text::PrinterOptions; pub use print_to_text::print_arguments; pub use print_to_text::print_definition; pub use print_to_text::print_directives; @@ -30,4 +31,3 @@ pub use print_to_text::print_value; pub use print_to_text::write_arguments; pub use print_to_text::write_directives; pub use print_to_text::write_value; -pub use print_to_text::PrinterOptions; diff --git a/compiler/crates/graphql-text-printer/src/print_full_operation.rs b/compiler/crates/graphql-text-printer/src/print_full_operation.rs index c9f059503e55c..f1d4d392abe6d 100644 --- a/compiler/crates/graphql-text-printer/src/print_full_operation.rs +++ b/compiler/crates/graphql-text-printer/src/print_full_operation.rs @@ -16,9 +16,9 @@ use graphql_ir::Program; use graphql_ir::ScalarField; use graphql_ir::Visitor; +use crate::PrinterOptions; use crate::print_fragment; use crate::print_operation; -use crate::PrinterOptions; pub fn print_full_operation( program: &Program, @@ -72,7 +72,7 @@ impl<'s> OperationPrinter<'s> { } } -impl<'s> Visitor for OperationPrinter<'s> { +impl Visitor for OperationPrinter<'_> { const NAME: &'static str = "OperationPrinter"; const VISIT_ARGUMENTS: bool = false; const VISIT_DIRECTIVES: bool = false; diff --git a/compiler/crates/graphql-text-printer/src/print_to_text.rs b/compiler/crates/graphql-text-printer/src/print_to_text.rs index dd7b2c4bdba0b..ed6dc59ba51db 100644 --- a/compiler/crates/graphql-text-printer/src/print_to_text.rs +++ b/compiler/crates/graphql-text-printer/src/print_to_text.rs @@ -29,9 +29,9 @@ use graphql_ir::Selection; use graphql_ir::Value; use graphql_ir::VariableDefinition; use graphql_ir::VariableName; +use intern::Lookup; use intern::string_key::Intern; use intern::string_key::StringKey; -use intern::Lookup; use schema::SDLSchema; use schema::Schema; use schema::Type; diff --git a/compiler/crates/graphql-text-printer/tests/compact.rs b/compiler/crates/graphql-text-printer/tests/compact.rs index 263dfd3c617bf..7144f4477fce5 100644 --- a/compiler/crates/graphql-text-printer/tests/compact.rs +++ b/compiler/crates/graphql-text-printer/tests/compact.rs @@ -9,13 +9,13 @@ use std::sync::Arc; use common::SourceLocationKey; use fixture_tests::Fixture; -use graphql_ir::build; -use graphql_ir::node_identifier::LocationAgnosticPartialEq; use graphql_ir::ExecutableDefinition; use graphql_ir::Program; +use graphql_ir::build; +use graphql_ir::node_identifier::LocationAgnosticPartialEq; use graphql_syntax::parse_executable; -use graphql_text_printer::print_full_operation; use graphql_text_printer::PrinterOptions; +use graphql_text_printer::print_full_operation; use relay_test_schema::TEST_SCHEMA; use relay_transforms::RelayLocationAgnosticBehavior; diff --git a/compiler/crates/graphql-text-printer/tests/operation_printer.rs b/compiler/crates/graphql-text-printer/tests/operation_printer.rs index 29eaee6ab44e4..cb2a0f2fd5744 100644 --- a/compiler/crates/graphql-text-printer/tests/operation_printer.rs +++ b/compiler/crates/graphql-text-printer/tests/operation_printer.rs @@ -9,9 +9,9 @@ use std::sync::Arc; use common::SourceLocationKey; use fixture_tests::Fixture; -use graphql_ir::build; use graphql_ir::ExecutableDefinition; use graphql_ir::Program; +use graphql_ir::build; use graphql_syntax::parse_executable; use graphql_text_printer::print_full_operation; use relay_test_schema::TEST_SCHEMA; diff --git a/compiler/crates/graphql-text-printer/tests/print.rs b/compiler/crates/graphql-text-printer/tests/print.rs index e0a7c80e74f6c..bdcc0cad2a1ee 100644 --- a/compiler/crates/graphql-text-printer/tests/print.rs +++ b/compiler/crates/graphql-text-printer/tests/print.rs @@ -8,9 +8,9 @@ use common::SourceLocationKey; use fixture_tests::Fixture; use graphql_ir::build; -use graphql_syntax::parse_executable_with_features; use graphql_syntax::FragmentArgumentSyntaxKind; use graphql_syntax::ParserFeatures; +use graphql_syntax::parse_executable_with_features; use graphql_text_printer::print_ir; use relay_test_schema::TEST_SCHEMA; @@ -22,6 +22,7 @@ pub async fn transform_fixture(fixture: &Fixture<'_>) -> Result ParserFeatures { fragment_argument_capability: FragmentArgumentSyntaxKind::SpreadArgumentsAndFragmentVariableDefinitions, + allow_string_literal_alias: false, }, ) .unwrap(); diff --git a/compiler/crates/graphql-watchman/Cargo.toml b/compiler/crates/graphql-watchman/Cargo.toml index 2ecda44a9ab62..32e028aff6941 100644 --- a/compiler/crates/graphql-watchman/Cargo.toml +++ b/compiler/crates/graphql-watchman/Cargo.toml @@ -4,12 +4,12 @@ name = "graphql-watchman" version = "0.0.0" authors = ["Facebook"] -edition = "2021" +edition = "2024" repository = "https://github.com/facebook/relay" license = "MIT" [dependencies] -log = { version = "0.4.22", features = ["kv_unstable"] } -serde = { version = "1.0.185", features = ["derive", "rc"] } -serde_bser = "0.3" -watchman_client = "0.8.0" +log = { version = "0.4.27", features = ["kv_unstable", "kv_unstable_std"] } +serde = { version = "1.0.219", features = ["derive", "rc"] } +serde_bser = "0.4" +watchman_client = "0.9.0" diff --git a/compiler/crates/graphql-watchman/src/subscription.rs b/compiler/crates/graphql-watchman/src/subscription.rs index 434c22728601e..fef0f06d8df96 100644 --- a/compiler/crates/graphql-watchman/src/subscription.rs +++ b/compiler/crates/graphql-watchman/src/subscription.rs @@ -9,9 +9,9 @@ use std::process::Command; use log::debug; use serde_bser::value::Value; -use watchman_client::prelude::*; use watchman_client::Subscription as WatchmanSubscription; use watchman_client::SubscriptionData; +use watchman_client::prelude::*; use crate::WatchmanFile; diff --git a/compiler/crates/intern/Cargo.toml b/compiler/crates/intern/Cargo.toml index 2a2a3edb5b91a..87c3238251642 100644 --- a/compiler/crates/intern/Cargo.toml +++ b/compiler/crates/intern/Cargo.toml @@ -4,24 +4,27 @@ name = "intern" version = "0.1.0" authors = ["Facebook"] -edition = "2021" +edition = "2024" description = "Intern data into a 32-bit id" repository = "https://github.com/facebook/relay" license = "MIT" [dependencies] fnv = "1.0" -hashbrown = { version = "0.14.3", features = ["raw", "serde"] } -indexmap = { version = "2.2.6", features = ["arbitrary", "rayon", "serde"] } -once_cell = "1.12" +hashbrown = { version = "0.14.5", features = ["raw", "serde"] } +indexmap = { version = "2.9.0", features = ["arbitrary", "rayon", "serde"] } +once_cell = "1.21" parking_lot = { version = "0.12.1", features = ["send_guard"] } -schemars = { version = "0.8.21", features = ["indexmap2"] } -serde = { version = "1.0.185", features = ["derive", "rc"] } +schemars = { version = "1.0.4", features = ["indexmap2"] } +serde = { version = "1.0.219", features = ["derive", "rc"] } serde_bytes = "0.11" serde_derive = "1.0.185" -smallvec = { version = "1.6.1", features = ["serde", "union"] } +smallvec = { version = "1.15", features = ["serde", "union"] } [dev-dependencies] bincode = "1.3.3" rand = { version = "0.8", features = ["small_rng"] } -serde_json = { version = "1.0.132", features = ["float_roundtrip", "unbounded_depth"] } +serde_json = { version = "1.0.140", features = ["alloc", "float_roundtrip", "unbounded_depth"] } + +[lints] +rust = { unexpected_cfgs = { check-cfg = ["cfg(memory_consistency_assertions)"], level = "warn" } } diff --git a/compiler/crates/intern/src/atomic_arena.rs b/compiler/crates/intern/src/atomic_arena.rs index 55f5b9f69c1fa..3df9bf3f39246 100644 --- a/compiler/crates/intern/src/atomic_arena.rs +++ b/compiler/crates/intern/src/atomic_arena.rs @@ -25,7 +25,7 @@ const MIN_SHIFT: u32 = 7; const U32_BITS: usize = 32; const MIN_SIZE: u32 = 1 << MIN_SHIFT; const NUM_SIZES: usize = U32_BITS - MIN_SHIFT as usize; -const MAX_INDEX: u32 = std::u32::MAX - MIN_SIZE; +const MAX_INDEX: u32 = u32::MAX - MIN_SIZE; // Memory consistency assertions provide a lot of checking of the internals, // but have a huge runtime cost. Be warned! These are really for active @@ -156,8 +156,10 @@ impl<'a, T> Ref<'a, T> { /// Re-create ref from biased index. pub unsafe fn from_index(index: u32) -> Self { - // OK because MIN_SIZE must be > 0 for algorithmic correctness. - Self::from_raw(NonZeroU32::new_unchecked(index + MIN_SIZE)) + unsafe { + // OK because MIN_SIZE must be > 0 for algorithmic correctness. + Self::from_raw(NonZeroU32::new_unchecked(index + MIN_SIZE)) + } } /// Internal value of ref; only use this if you know what you're @@ -207,7 +209,7 @@ fn bucket_capacity(a: usize) -> usize { fn index(i: u32) -> (usize, usize) { memory_consistency_assert!(i >= MIN_SIZE); memory_consistency_assert!(i - MIN_SIZE <= MAX_INDEX); - memory_consistency_assert!(i as u64 <= std::usize::MAX as u64); + memory_consistency_assert!(i as u64 <= usize::MAX as u64); let a = i.leading_zeros() as usize; memory_consistency_assert!(a < NUM_SIZES, "{} < {}", a, NUM_SIZES); let b = (i & ((bucket_capacity(0) as u32 - 1) >> a)) as usize; @@ -665,8 +667,8 @@ mod tests { use parking_lot::Condvar; use parking_lot::Mutex; - use rand::thread_rng; use rand::Rng; + use rand::thread_rng; use super::*; @@ -703,7 +705,7 @@ mod tests { let (a, b) = index(MIN_SIZE); assert_eq!(a, NUM_SIZES - 1); assert_eq!(b, 0); - let (a, b) = index(std::u32::MAX); + let (a, b) = index(u32::MAX); assert_eq!(a, 0); assert_eq!(b, (1 << 31) - 1); assert_eq!(b, bucket_capacity(0) - 1); diff --git a/compiler/crates/intern/src/intern.rs b/compiler/crates/intern/src/intern.rs index d11e3847f61ec..c09574c72d641 100644 --- a/compiler/crates/intern/src/intern.rs +++ b/compiler/crates/intern/src/intern.rs @@ -15,7 +15,6 @@ use std::hash::Hasher; use std::num::NonZeroU32; use std::sync::atomic::AtomicU32; use std::sync::atomic::Ordering; -use std::u32; use once_cell::sync::OnceCell; use serde::Deserialize; @@ -110,7 +109,7 @@ pub trait InternId: 'static + Eq + Copy { #[doc(hidden)] #[inline] unsafe fn from_index(i: u32) -> Self { - Self::wrap(Ref::from_index(i)) + unsafe { Self::wrap(Ref::from_index(i)) } } /// Raw index for internal use only. @@ -124,7 +123,7 @@ pub trait InternId: 'static + Eq + Copy { #[doc(hidden)] #[inline] unsafe fn from_raw(i: NonZeroU32) -> Self { - Self::wrap(Ref::from_raw(i)) + unsafe { Self::wrap(Ref::from_raw(i)) } } } @@ -242,7 +241,7 @@ impl InternTable { impl InternTable { /// The methods from here on are internal and private. - fn shards(&'static self) -> &Shards { + fn shards(&'static self) -> &'static Shards { self.shards.get_or_init(|| { let shards: Shards = ShardedSet::default(); if !self.arena.is_empty() { @@ -291,7 +290,7 @@ impl InternTable { /// Get a shared reference to the underlying `Id::Intern`. /// Usually you can rely on `deref` to do this implicitly. #[inline] - fn get(&'static self, r: Id) -> &Id::Intern { + fn get(&'static self, r: Id) -> &'static Id::Intern { self.arena.get(r.unwrap()) } @@ -552,11 +551,11 @@ where macro_rules! intern_struct { () => { }; ($(#[$attr:meta])* struct $Name:ident = Intern<$T:ty> { - $(serdes($l:expr);)? + $(serdes($l:expr_2021);)? $(type Lookup = $L:ty;)? $(type Set = $S:ident;)? $(type Map = $M:ident;)? - $(const $Z:ident = $ze:expr;)? + $(const $Z:ident = $ze:expr_2021;)? } $($rest:tt)*) => { intern_struct!(@DOIT, ($(#[$attr])*), (), $Name, $T, @@ -564,11 +563,11 @@ macro_rules! intern_struct { intern_struct!{ $($rest)* } }; ($(#[$attr:meta])* pub struct $Name:ident = Intern<$T:ty> { - $(serdes($l:expr);)? + $(serdes($l:expr_2021);)? $(type Lookup = $L:ty;)? $(type Set = $S:ident;)? $(type Map = $M:ident;)? - $(const $Z:ident = $ze:expr;)? + $(const $Z:ident = $ze:expr_2021;)? } $($rest:tt)*) => { intern_struct!(@DOIT, ($(#[$attr])*), (pub), $Name, $T, @@ -576,11 +575,11 @@ macro_rules! intern_struct { intern_struct!{ $($rest)* } }; ($(#[$attr:meta])* pub(crate) struct $Name:ident = Intern<$T:ty> { - $(serdes($l:expr);)? + $(serdes($l:expr_2021);)? $(type Lookup = $L:ty;)? $(type Set = $S:ident;)? $(type Map = $M:ident;)? - $(const $Z:ident = $ze:expr;)? + $(const $Z:ident = $ze:expr_2021;)? } $($rest:tt)*) => { intern_struct!(@DOIT, ($(#[$attr])*), (pub(crate)), $Name, $T, @@ -588,7 +587,7 @@ macro_rules! intern_struct { intern_struct!{ $($rest)* } }; (@SERDESDERIVE(); $($decl:tt)*) => { $($decl)* }; - (@SERDESDERIVE($l:expr); $($decl:tt)*) => { + (@SERDESDERIVE($l:expr_2021); $($decl:tt)*) => { #[derive(serde_derive::Deserialize, serde_derive::Serialize)] #[serde(from = $l)] #[serde(into = $l)] @@ -623,18 +622,18 @@ macro_rules! intern_struct { $($vis)* type $S = std::collections::HashSet<$Name, $crate::idhasher::BuildIdHasher>; }; (@TABLE($T:ty, ())) => { $crate::intern::InternTable::new() }; - (@TABLE($T:ty, ($v:ident, $zero:expr))) => {{ + (@TABLE($T:ty, ($v:ident, $zero:expr_2021))) => {{ static ZERO: $crate::Zero<$T> = $crate::Zero::new($zero); $crate::intern::InternTable::with_zero(&ZERO) }}; (@ZERO($Name:ident, ())) => { }; - (@ZERO($Name:ident, ($v:ident, $zero:expr))) => { + (@ZERO($Name:ident, ($v:ident, $zero:expr_2021))) => { impl $Name { pub const $v: Self = $Name($crate::Zero::zero()); } }; (@DOIT, ($(#[$attr:meta])*), ($($vis:tt)*), $Name:ident, $T:ty, - ( $($Lookup:ty)? ), ( $($Z:ident, $ze:expr)* ), ( $($serdes:tt)? ), + ( $($Lookup:ty)? ), ( $($Z:ident, $ze:expr_2021)* ), ( $($serdes:tt)? ), ( $($S:ident)? ), ( $($M:ident)? ) ) => { intern_struct!{ @SERDESDERIVE($($serdes)?); @@ -736,7 +735,7 @@ mod tests { impl std::cmp::PartialOrd for MyId { fn partial_cmp(&self, other: &Self) -> std::option::Option { - self.get().partial_cmp(other.get()) + Some(self.get().cmp(other.get())) } } diff --git a/compiler/crates/intern/src/lib.rs b/compiler/crates/intern/src/lib.rs index 14fb5b0585fae..38a9bf1c8bcc6 100644 --- a/compiler/crates/intern/src/lib.rs +++ b/compiler/crates/intern/src/lib.rs @@ -63,9 +63,9 @@ //! static reference to the interned object referred to by `my_id`. //! ``` //! # #[macro_use] -//! use intern::intern_struct; //! use intern::InternId; //! use intern::InternSerdes; +//! use intern::intern_struct; //! use serde_derive::Deserialize; //! use serde_derive::Serialize; //! diff --git a/compiler/crates/intern/src/path.rs b/compiler/crates/intern/src/path.rs index 9550a2e46593a..bd09d5714bb23 100644 --- a/compiler/crates/intern/src/path.rs +++ b/compiler/crates/intern/src/path.rs @@ -15,11 +15,11 @@ use std::path::PathBuf; use serde_derive::Deserialize; use serde_derive::Serialize; +use crate::InternId; +use crate::InternSerdes; use crate::idhasher::BuildIdHasher; use crate::intern_struct; use crate::string; -use crate::InternId; -use crate::InternSerdes; intern_struct! { /// A path whose path components are interned. diff --git a/compiler/crates/intern/src/sharded_set.rs b/compiler/crates/intern/src/sharded_set.rs index d2673ed0c4d6e..806caf0ca50b8 100644 --- a/compiler/crates/intern/src/sharded_set.rs +++ b/compiler/crates/intern/src/sharded_set.rs @@ -11,7 +11,7 @@ use std::fmt; use std::hash::BuildHasher; use std::hash::Hash; -use hashbrown::raw::RawTable; +use hashbrown::HashTable; use parking_lot::RwLock; use parking_lot::RwLockWriteGuard; @@ -20,7 +20,7 @@ const SHARDS: usize = 1 << SHARD_SHIFT; pub struct ShardedSet { build_hasher: S, - shards: [RwLock>; SHARDS], + shards: [RwLock>; SHARDS], } impl fmt::Debug for ShardedSet { @@ -308,7 +308,7 @@ fn hash_one(build_hasher: &B, x: T) -> u64 { pub struct InsertLock<'a, T, S = RandomState> { build_hasher: &'a S, hash: u64, - shard: RwLockWriteGuard<'a, RawTable>, + shard: RwLockWriteGuard<'a, HashTable>, } impl<'a, T, S> fmt::Debug for InsertLock<'a, T, S> { @@ -322,7 +322,7 @@ impl ShardedSet { /// hashbrown uses the upper 7 bits for disambiguation and the lower bits /// for bucket indexing, we take the bits just above the top 7. #[inline(always)] - fn hash_and_shard(&self, q: &Q) -> (u64, &RwLock>) + fn hash_and_shard(&self, q: &Q) -> (u64, &RwLock>) where T: Borrow, Q: ?Sized + Hash, @@ -344,22 +344,23 @@ impl ShardedSet { let (hash, shard) = self.hash_and_shard(q); // Assume load is low and try to take lock for writing. // We don't faff around with upgradability right now. - let shard = if let Some(write_lock) = shard.try_write() { - write_lock - } else { - // Write contention. Try reading first to see if the entry already exists. - if let Some(t) = shard.read().get(hash, |other| q == other.borrow()) { - // Already exists. - return Ok(t.clone()); + let shard = match shard.try_write() { + Some(write_lock) => write_lock, + _ => { + // Write contention. Try reading first to see if the entry already exists. + if let Some(t) = shard.read().find(hash, |other| q == other.borrow()) { + // Already exists. + return Ok(t.clone()); + } + // Unconditionally write lock. + shard.write() } - // Unconditionally write lock. - shard.write() }; // Now check for the data. We need to do this even if we already // checked in the write contention case above. We don't use an // upgradable read lock because those are exclusive from one another // just like write locks. - if let Some(t) = shard.get(hash, |other| q == other.borrow()) { + if let Some(t) = shard.find(hash, |other| q == other.borrow()) { return Ok(t.clone()); } Err(InsertLock { @@ -378,15 +379,17 @@ impl ShardedSet { let (hash, shard) = self.hash_and_shard(q); shard .read() - .get(hash, |other| q == other.borrow()) - .map(Clone::clone) + .find(hash, |other| q == other.borrow()) + .cloned() } /// Unconditionally insert `t` without checking if it's in the set. pub fn unchecked_insert(&self, t: T) { let build_hasher = &self.build_hasher; let (hash, shard) = self.hash_and_shard(&t); - shard.write().insert(hash, t, |v| hash_one(build_hasher, v)); + shard + .write() + .insert_unique(hash, t, |v| hash_one(build_hasher, v)); } } @@ -396,6 +399,6 @@ impl InsertLock<'_, T, S> { pub fn insert>(&mut self, q: Q) { let build_hasher = self.build_hasher; self.shard - .insert(self.hash, q.into(), |v| hash_one(build_hasher, v)); + .insert_unique(self.hash, q.into(), |v| hash_one(build_hasher, v)); } } diff --git a/compiler/crates/intern/src/string.rs b/compiler/crates/intern/src/string.rs index b4cf020abe1c2..8f826634ec688 100644 --- a/compiler/crates/intern/src/string.rs +++ b/compiler/crates/intern/src/string.rs @@ -103,7 +103,7 @@ impl StringId { } pub unsafe fn from_index(index: u32) -> Self { - Self(BytesId::from_index(index)) + unsafe { Self(BytesId::from_index(index)) } } /// 0-cost conversion to interned bytes. @@ -282,7 +282,7 @@ macro_rules! string_id { $crate::string::Lazy::new(|| $crate::string::intern($value)); *INSTANCE }}; - ($_:expr) => { + ($_:expr_2021) => { compile_error!("string_id! macro can only be used with string literals.") }; } @@ -295,7 +295,7 @@ macro_rules! bytes_id { $crate::string::Lazy::new(|| $crate::string::intern_bytes($value as &[u8])); *INSTANCE }}; - ($_:expr) => { + ($_:expr_2021) => { compile_error!("bytes_id! macro can only be used with literals.") }; } @@ -376,14 +376,13 @@ mod tests { #[test] fn multithreaded() { + use std::sync::Arc; use std::sync::atomic::AtomicU32; use std::sync::atomic::Ordering; - use std::sync::Arc; use std::thread; - use std::u32; - use rand::thread_rng; use rand::Rng; + use rand::thread_rng; // Load test lots of threads creating strings, with load // gradually getting heavier on later (popular) strings. diff --git a/compiler/crates/intern/src/string_key.rs b/compiler/crates/intern/src/string_key.rs index bc6feccd4e49c..cebc3814d3aca 100644 --- a/compiler/crates/intern/src/string_key.rs +++ b/compiler/crates/intern/src/string_key.rs @@ -12,22 +12,20 @@ use std::fmt::Formatter; use std::str::FromStr; use indexmap::IndexMap; -use schemars::gen::SchemaGenerator; -use schemars::schema::InstanceType; -use schemars::schema::Schema; -use schemars::schema::SchemaObject; -use schemars::schema::SingleOrVec; use schemars::JsonSchema; +use schemars::Schema; +use schemars::SchemaGenerator; +use schemars::json_schema; use serde::Deserialize; use serde::Deserializer; use serde::Serialize; use serde::Serializer; +pub use crate::Lookup; use crate::idhasher::BuildIdHasher; use crate::string; use crate::string::IntoUtf8Bytes; use crate::string::StringId; -pub use crate::Lookup; // StringKey is a small impedence matcher around StringId. // NOTE in particular that it does NOT do de-duplicating serde. @@ -36,17 +34,16 @@ pub use crate::Lookup; pub struct StringKey(StringId); impl JsonSchema for StringKey { - fn schema_name() -> std::string::String { - String::from("StringKey") + fn schema_name() -> std::borrow::Cow<'static, str> { + String::from("StringKey").into() } fn json_schema(_gen: &mut SchemaGenerator) -> Schema { - SchemaObject { - instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::String))), - format: None, - ..Default::default() - } - .into() + json_schema!({ + "type": "string", + "format": null, + } + ) } } @@ -72,7 +69,7 @@ impl StringKey { } pub unsafe fn from_index(index: u32) -> Self { - Self(StringId::from_index(index)) + unsafe { Self(StringId::from_index(index)) } } } @@ -128,7 +125,7 @@ macro_rules! intern { static INSTANCE: Lazy<$crate::string_key::StringKey> = Lazy::new(|| $value.intern()); *INSTANCE }}; - ($_:expr) => { + ($_:expr_2021) => { compile_error!("intern! macro can only be used with string literals.") }; } diff --git a/compiler/crates/interner/Cargo.toml b/compiler/crates/interner/Cargo.toml index f5e510d01beda..3424514f2e0e5 100644 --- a/compiler/crates/interner/Cargo.toml +++ b/compiler/crates/interner/Cargo.toml @@ -4,13 +4,13 @@ name = "interner" version = "0.0.0" authors = ["Facebook"] -edition = "2021" +edition = "2024" repository = "https://github.com/facebook/relay" license = "MIT" [dependencies] fnv = "1.0" -lazy_static = "1.4" -once_cell = "1.12" +lazy_static = "1.5" +once_cell = "1.21" parking_lot = { version = "0.12.1", features = ["send_guard"] } -serde = { version = "1.0.185", features = ["derive", "rc"] } +serde = { version = "1.0.219", features = ["derive", "rc"] } diff --git a/compiler/crates/interner/src/bytes.rs b/compiler/crates/interner/src/bytes.rs index 787a2e9138a42..d39b6202b9992 100644 --- a/compiler/crates/interner/src/bytes.rs +++ b/compiler/crates/interner/src/bytes.rs @@ -88,7 +88,7 @@ impl Ord for StringKey { impl PartialOrd for StringKey { fn partial_cmp(&self, other: &Self) -> Option { - self.lookup().partial_cmp(other.lookup()) + Some(self.lookup().cmp(other.lookup())) } } diff --git a/compiler/crates/interner/src/macros.rs b/compiler/crates/interner/src/macros.rs index 278502db192dc..8961d86bb8f8b 100644 --- a/compiler/crates/interner/src/macros.rs +++ b/compiler/crates/interner/src/macros.rs @@ -14,13 +14,13 @@ #[macro_export] macro_rules! intern { ($value:literal) => {{ - use $crate::reexport::Lazy; use $crate::Intern; use $crate::StringKey; + use $crate::reexport::Lazy; static KEY: Lazy = Lazy::new(|| Intern::intern($value)); *KEY }}; - ($_:expr) => { + ($_:expr_2021) => { compile_error!("intern! macro can only be used with string literals.") }; } diff --git a/compiler/crates/js-config-loader/Cargo.toml b/compiler/crates/js-config-loader/Cargo.toml index 2796fa2efd5e6..e8e6d889811f7 100644 --- a/compiler/crates/js-config-loader/Cargo.toml +++ b/compiler/crates/js-config-loader/Cargo.toml @@ -4,7 +4,7 @@ name = "js-config-loader" version = "0.0.0" authors = ["Facebook"] -edition = "2021" +edition = "2024" repository = "https://github.com/facebook/relay" license = "MIT" @@ -13,9 +13,9 @@ name = "tests" path = "tests/lib.rs" [dependencies] -serde = { version = "1.0.185", features = ["derive", "rc"] } -serde_json = { version = "1.0.132", features = ["float_roundtrip", "unbounded_depth"] } -thiserror = "1.0.64" +serde = { version = "1.0.219", features = ["derive", "rc"] } +serde_json = { version = "1.0.140", features = ["alloc", "float_roundtrip", "unbounded_depth"] } +thiserror = "2.0.12" [dev-dependencies] -tempfile = "3.8" +tempfile = "3.15" diff --git a/compiler/crates/js-config-loader/tests/lib.rs b/compiler/crates/js-config-loader/tests/lib.rs index acba6dd2647c2..6c738ef903e07 100644 --- a/compiler/crates/js-config-loader/tests/lib.rs +++ b/compiler/crates/js-config-loader/tests/lib.rs @@ -7,9 +7,9 @@ use std::fs::create_dir_all; -use js_config_loader::search; use js_config_loader::ConfigError; use js_config_loader::ErrorCode; +use js_config_loader::search; use serde::Deserialize; use tempfile::tempdir; diff --git a/compiler/crates/persist-query/Cargo.toml b/compiler/crates/persist-query/Cargo.toml index 7bd691f27e616..61486f2eac4ad 100644 --- a/compiler/crates/persist-query/Cargo.toml +++ b/compiler/crates/persist-query/Cargo.toml @@ -4,17 +4,17 @@ name = "persist-query" version = "0.0.0" authors = ["Facebook"] -edition = "2021" +edition = "2024" repository = "https://github.com/facebook/relay" license = "MIT" [dependencies] hyper = { version = "0.14.26", features = ["client", "http1", "http2", "stream"] } hyper-tls = "0.5" -serde = { version = "1.0.185", features = ["derive", "rc"] } -serde_json = { version = "1.0.132", features = ["float_roundtrip", "unbounded_depth"] } -thiserror = "1.0.64" -url = "2.5.2" +serde = { version = "1.0.219", features = ["derive", "rc"] } +serde_json = { version = "1.0.140", features = ["alloc", "float_roundtrip", "unbounded_depth"] } +thiserror = "2.0.12" +url = "2.5.4" [features] vendored = ["hyper-tls/vendored"] diff --git a/compiler/crates/relay-bin/Cargo.toml b/compiler/crates/relay-bin/Cargo.toml index 0366395f8ec5a..10beebfede024 100644 --- a/compiler/crates/relay-bin/Cargo.toml +++ b/compiler/crates/relay-bin/Cargo.toml @@ -2,22 +2,22 @@ [package] name = "relay" -version = "18.2.0" +version = "20.1.1" authors = ["Facebook"] -edition = "2021" +edition = "2024" repository = "https://github.com/facebook/relay" license = "MIT" [dependencies] -clap = { version = "4.5.20", features = ["derive", "env", "string", "unicode", "wrap_help"] } +clap = { version = "4.5.42", features = ["derive", "env", "string", "unicode", "wrap_help"] } common = { path = "../common" } intern = { path = "../intern" } -log = { version = "0.4.22", features = ["kv_unstable"] } +log = { version = "0.4.27", features = ["kv_unstable", "kv_unstable_std"] } relay-codemod = { path = "../relay-codemod" } relay-compiler = { path = "../relay-compiler" } relay-lsp = { path = "../relay-lsp" } schema = { path = "../schema" } schema-documentation = { path = "../schema-documentation" } simplelog = "0.12.2" -thiserror = "1.0.64" -tokio = { version = "1.41.0", features = ["full", "test-util", "tracing"] } +thiserror = "2.0.12" +tokio = { version = "1.46.1", features = ["full", "test-util", "tracing"] } diff --git a/compiler/crates/relay-bin/src/main.rs b/compiler/crates/relay-bin/src/main.rs index 0db30cc593385..4e5ff54baff9c 100644 --- a/compiler/crates/relay-bin/src/main.rs +++ b/compiler/crates/relay-bin/src/main.rs @@ -17,25 +17,25 @@ use common::ConsoleLogger; use intern::string_key::Intern; use log::error; use log::info; -use relay_codemod::run_codemod; use relay_codemod::AvailableCodemod; -use relay_compiler::build_project::artifact_writer::ArtifactValidationWriter; -use relay_compiler::compiler::Compiler; -use relay_compiler::config::Config; -use relay_compiler::config::ConfigFile; -use relay_compiler::errors::Error as CompilerError; -use relay_compiler::get_programs; +use relay_codemod::run_codemod; use relay_compiler::FileSourceKind; use relay_compiler::LocalPersister; use relay_compiler::OperationPersister; use relay_compiler::PersistConfig; use relay_compiler::ProjectName; use relay_compiler::RemotePersister; -use relay_lsp::start_language_server; +use relay_compiler::build_project::artifact_writer::ArtifactValidationWriter; +use relay_compiler::compiler::Compiler; +use relay_compiler::config::Config; +use relay_compiler::config::ConfigFile; +use relay_compiler::errors::Error as CompilerError; +use relay_compiler::get_programs; use relay_lsp::DummyExtraDataProvider; use relay_lsp::FieldDefinitionSourceInfo; use relay_lsp::FieldSchemaInfo; use relay_lsp::LSPExtraDataProvider; +use relay_lsp::start_language_server; use schema::SDLSchema; use schema_documentation::SchemaDocumentationLoader; use simplelog::ColorChoice; @@ -285,6 +285,7 @@ async fn handle_codemod_command(command: CodemodCommand) -> Result<(), Error> { let mut config = get_config(command.config)?; set_project_flag(&mut config, command.projects)?; let (programs, _, config) = get_programs(config, Arc::new(ConsoleLogger)).await; + let programs = programs.values().cloned().collect(); match run_codemod(programs, Arc::clone(&config), command.codemod).await { Ok(_) => Ok(()), @@ -407,10 +408,12 @@ impl LSPExtraDataProvider for ExtraDataProvider { } let file_path = result[0]; let line_number = result[1].parse::().unwrap() - 1; + let column_number = result[2].parse::().unwrap_or(1_u64) - 1; Ok(Some(FieldDefinitionSourceInfo { file_path: file_path.to_string(), line_number, + column_number, is_local: true, })) } diff --git a/compiler/crates/relay-codegen/Cargo.toml b/compiler/crates/relay-codegen/Cargo.toml index 1b60e1afcf838..341807c82e0e2 100644 --- a/compiler/crates/relay-codegen/Cargo.toml +++ b/compiler/crates/relay-codegen/Cargo.toml @@ -4,7 +4,7 @@ name = "relay-codegen" version = "0.0.0" authors = ["Facebook"] -edition = "2021" +edition = "2024" repository = "https://github.com/facebook/relay" license = "MIT" @@ -46,10 +46,10 @@ docblock-shared = { path = "../docblock-shared" } fnv = "1.0" graphql-ir = { path = "../graphql-ir" } graphql-syntax = { path = "../graphql-syntax" } -hex = "0.4.3" -indexmap = { version = "2.2.6", features = ["arbitrary", "rayon", "serde"] } +hex = { version = "0.4.3", features = ["alloc"] } +indexmap = { version = "2.9.0", features = ["arbitrary", "rayon", "serde"] } intern = { path = "../intern" } -lazy_static = "1.4" +lazy_static = "1.5" md-5 = "0.10" relay-config = { path = "../relay-config" } relay-transforms = { path = "../relay-transforms" } @@ -59,4 +59,4 @@ schema = { path = "../schema" } fixture-tests = { path = "../fixture-tests" } graphql-test-helpers = { path = "../graphql-test-helpers" } relay-test-schema = { path = "../relay-test-schema" } -tokio = { version = "1.41.0", features = ["full", "test-util", "tracing"] } +tokio = { version = "1.46.1", features = ["full", "test-util", "tracing"] } diff --git a/compiler/crates/relay-codegen/src/ast.rs b/compiler/crates/relay-codegen/src/ast.rs index f0ec76f38939d..0839840edfa7d 100644 --- a/compiler/crates/relay-codegen/src/ast.rs +++ b/compiler/crates/relay-codegen/src/ast.rs @@ -11,7 +11,7 @@ use graphql_syntax::FloatValue; use graphql_syntax::OperationKind; use indexmap::IndexSet; use intern::string_key::StringKey; -use relay_config::DynamicModuleProvider; +use relay_config::ModuleProvider; #[derive(Eq, PartialEq, Hash, Debug)] pub struct ObjectEntry { @@ -23,7 +23,7 @@ pub struct ObjectEntry { /// For now, field names are defined in `CODEGEN_CONSTANTS #[macro_export] macro_rules! object { - { $ ( $(:$func: expr,)* $key:ident: $value:expr,)* } => ({ + { $ ( $(:$func: expr_2021,)* $key:ident: $value:expr_2021,)* } => ({ vec![ $( $( @@ -97,6 +97,12 @@ pub enum GraphQLModuleDependency { }, } +#[derive(Eq, PartialEq, Hash, Debug)] +pub enum ResolverJSFunction { + Module(JSModuleDependency), + PropertyLookup(String), +} + #[derive(Eq, PartialEq, Hash, Debug)] pub enum Primitive { Key(AstKey), @@ -116,13 +122,13 @@ pub enum Primitive { // skip_printing_nulls is enabled SkippableNull, DynamicImport { - provider: DynamicModuleProvider, + provider: ModuleProvider, module: StringKey, }, RelayResolverModel { graphql_module_name: StringKey, graphql_module_path: StringKey, - js_module: JSModuleDependency, + resolver_fn: ResolverJSFunction, injected_field_name_details: Option<(StringKey, bool)>, }, } @@ -189,7 +195,7 @@ pub struct RequestParameters<'a> { pub text: Option, } -impl<'a> RequestParameters<'a> { +impl RequestParameters<'_> { pub fn is_client_request(&self) -> bool { self.id.is_none() && self.text.is_none() } diff --git a/compiler/crates/relay-codegen/src/build_ast.rs b/compiler/crates/relay-codegen/src/build_ast.rs index ae3a19b629739..3f4f0915282d1 100644 --- a/compiler/crates/relay-codegen/src/build_ast.rs +++ b/compiler/crates/relay-codegen/src/build_ast.rs @@ -7,12 +7,12 @@ use std::path::PathBuf; +use ::intern::Lookup; use ::intern::intern; use ::intern::string_key::Intern; use ::intern::string_key::StringKey; -use ::intern::Lookup; +use common::ArgumentName; use common::DirectiveName; -use common::FeatureFlag; use common::NamedItem; use common::ObjectName; use common::WithLocation; @@ -42,17 +42,7 @@ use md5::Md5; use relay_config::JsModuleFormat; use relay_config::ProjectConfig; use relay_config::Surface; -use relay_transforms::extract_connection_metadata_from_directive; -use relay_transforms::extract_handle_field_directives; -use relay_transforms::extract_values_from_handle_field_directive; -use relay_transforms::generate_abstract_type_refinement_key; -use relay_transforms::get_normalization_fragment_filename; -use relay_transforms::get_normalization_operation_name; -use relay_transforms::get_resolver_fragment_dependency_name; -use relay_transforms::relay_resolvers::get_resolver_info; -use relay_transforms::relay_resolvers::resolver_import_alias; -use relay_transforms::relay_resolvers::ResolverInfo; -use relay_transforms::remove_directive; +use relay_transforms::CLIENT_EXTENSION_DIRECTIVE_NAME; use relay_transforms::CatchMetadataDirective; use relay_transforms::ClientEdgeMetadata; use relay_transforms::ClientEdgeMetadataDirective; @@ -60,26 +50,39 @@ use relay_transforms::ClientEdgeModelResolver; use relay_transforms::ClientExtensionAbstractTypeMetadataDirective; use relay_transforms::ConnectionConstants; use relay_transforms::ConnectionMetadata; +use relay_transforms::DIRECTIVE_SPLIT_OPERATION; use relay_transforms::DeferDirective; use relay_transforms::FragmentAliasMetadata; use relay_transforms::FragmentDataInjectionMode; +use relay_transforms::INLINE_DIRECTIVE_NAME; +use relay_transforms::INTERNAL_METADATA_DIRECTIVE; use relay_transforms::InlineDirectiveMetadata; use relay_transforms::ModuleMetadata; use relay_transforms::NoInlineFragmentSpreadMetadata; +use relay_transforms::RELAY_ACTOR_CHANGE_DIRECTIVE_FOR_CODEGEN; +use relay_transforms::RESOLVER_BELONGS_TO_BASE_SCHEMA_DIRECTIVE; use relay_transforms::RefetchableMetadata; use relay_transforms::RelayDirective; use relay_transforms::RelayResolverMetadata; use relay_transforms::RequiredMetadataDirective; use relay_transforms::ResolverOutputTypeInfo; use relay_transforms::StreamDirective; -use relay_transforms::CLIENT_EXTENSION_DIRECTIVE_NAME; -use relay_transforms::DIRECTIVE_SPLIT_OPERATION; -use relay_transforms::INLINE_DIRECTIVE_NAME; -use relay_transforms::INTERNAL_METADATA_DIRECTIVE; -use relay_transforms::RELAY_ACTOR_CHANGE_DIRECTIVE_FOR_CODEGEN; -use relay_transforms::RESOLVER_BELONGS_TO_BASE_SCHEMA_DIRECTIVE; use relay_transforms::TYPE_DISCRIMINATOR_DIRECTIVE_NAME; +use relay_transforms::extract_connection_metadata_from_directive; +use relay_transforms::extract_handle_field_directives; +use relay_transforms::extract_values_from_handle_field_directive; +use relay_transforms::generate_abstract_type_refinement_key; +use relay_transforms::get_normalization_fragment_filename; +use relay_transforms::get_normalization_operation_name; +use relay_transforms::get_resolver_fragment_dependency_name; +use relay_transforms::raw_text::get_raw_text_value; +use relay_transforms::relay_resolvers::ResolverInfo; +use relay_transforms::relay_resolvers::ResolverSchemaGenType; +use relay_transforms::relay_resolvers::get_resolver_info; +use relay_transforms::relay_resolvers::resolver_import_alias; +use relay_transforms::remove_directive; use schema::Field; +use schema::FieldID; use schema::SDLSchema; use schema::Schema; use schema::Type; @@ -94,6 +97,7 @@ use crate::ast::ObjectEntry; use crate::ast::Primitive; use crate::ast::QueryID; use crate::ast::RequestParameters; +use crate::ast::ResolverJSFunction; use crate::ast::ResolverModuleReference; use crate::constants::CODEGEN_CONSTANTS; use crate::object; @@ -103,6 +107,9 @@ lazy_static! { DirectiveName("throwOnFieldError".intern()); pub static ref EXEC_TIME_RESOLVERS: DirectiveName = DirectiveName("exec_time_resolvers".intern()); + static ref EXEC_TIME_RESOLVERS_ENABLED_ARGUMENT: ArgumentName = + ArgumentName("enabledProvider".intern()); + static ref FRAGMENT_KEY: StringKey = "fragment".intern(); } pub fn build_request_params_ast_key( @@ -368,21 +375,44 @@ impl<'schema, 'builder, 'config> CodegenBuilder<'schema, 'builder, 'config> { let feature_flags = &self.project_config.feature_flags; feature_flags.enable_resolver_normalization_ast || (feature_flags.enable_exec_time_resolvers_directive - && context.has_exec_time_resolvers_directive) + && context.has_exec_time_resolvers_directive + && !context.has_exec_time_resolvers_enabled_provider) + } + + fn use_exec_and_read_time_resolvers(&self, context: &ContextualMetadata) -> bool { + let feature_flags = &self.project_config.feature_flags; + feature_flags.enable_exec_time_resolvers_directive + && context.has_exec_time_resolvers_directive + && context.has_exec_time_resolvers_enabled_provider } fn build_operation(&mut self, operation: &OperationDefinition) -> AstKey { + let has_exec_time_resolvers_directive = + operation.directives.named(*EXEC_TIME_RESOLVERS).is_some(); + let exec_time_resolvers_enabled_provider = operation + .directives + .named(*EXEC_TIME_RESOLVERS) + .and_then(|directive| { + directive + .arguments + .named(*EXEC_TIME_RESOLVERS_ENABLED_ARGUMENT) + .map(|arg| match &arg.value.item { + Value::Constant(ConstantValue::String(cons)) => WithLocation { + item: *cons, + location: arg.value.location, + }, + _ => panic!( + "The enabled argument in exec_time_resolvers directive should be the string name of your provider file." + ), + }) + }); + let mut context = ContextualMetadata { has_client_edges: false, - has_exec_time_resolvers_directive: operation - .directives - .named(*EXEC_TIME_RESOLVERS) + has_exec_time_resolvers_directive, + has_exec_time_resolvers_enabled_provider: exec_time_resolvers_enabled_provider .is_some(), }; - let exec_time_resolvers_field = ObjectEntry { - key: "use_exec_time_resolvers".intern(), - value: Primitive::Bool(context.has_exec_time_resolvers_directive), - }; match operation.directives.named(*DIRECTIVE_SPLIT_OPERATION) { Some(_split_directive) => { let metadata = Primitive::Key(self.object(vec![])); @@ -393,9 +423,6 @@ impl<'schema, 'builder, 'config> CodegenBuilder<'schema, 'builder, 'config> { name: Primitive::String(operation.name.item.0), selections: selections, }; - if context.has_exec_time_resolvers_directive { - fields.push(exec_time_resolvers_field); - } if !operation.variable_definitions.is_empty() { let argument_definitions = self.build_operation_variable_definitions(&operation.variable_definitions); @@ -420,7 +447,32 @@ impl<'schema, 'builder, 'config> CodegenBuilder<'schema, 'builder, 'config> { selections: selections, }; if context.has_exec_time_resolvers_directive { - fields.push(exec_time_resolvers_field); + fields.push( + if let Some(provider) = exec_time_resolvers_enabled_provider { + let mut provider_path = + PathBuf::from(provider.location.source_location().path()); + provider_path.pop(); + provider_path.push(PathBuf::from(provider.item.lookup())); + let artifact_path = self + .project_config + .artifact_path_for_definition(self.definition_source_location); + ObjectEntry { + key: "exec_time_resolvers_enabled_provider".intern(), + value: Primitive::JSModuleDependency(JSModuleDependency { + path: self.project_config.js_module_import_identifier( + &artifact_path, + &provider_path, + ), + import_name: ModuleImportName::Default(provider.item), + }), + } + } else { + ObjectEntry { + key: "use_exec_time_resolvers".intern(), + value: Primitive::Bool(true), + } + }, + ); } if let Some(client_abstract_types) = self.maybe_build_client_abstract_types(operation) @@ -843,19 +895,34 @@ impl<'schema, 'builder, 'config> CodegenBuilder<'schema, 'builder, 'config> { { self.build_normalization_relay_resolver_execution_time_for_worker(resolver_metadata) } else if self.use_exec_time_resolvers(context) { - self.build_normalization_relay_resolver_execution_time(resolver_metadata) + self.build_normalization_relay_resolver_exec_and_read_time( + resolver_metadata, + inline_fragment, + true, + ) + } else if self.use_exec_and_read_time_resolvers(context) { + // We must handle both read time resolvers case and exec time resolvers case + // since the mode it is in (read time resolvers vs exec time resolvers) + // is now determined at runtime. + self.build_normalization_relay_resolver_exec_and_read_time( + resolver_metadata, + inline_fragment, + false, + ) } else { self.build_normalization_relay_resolver_read_time(resolver_metadata, inline_fragment) } } - // For read time execution time Relay Resolvers in the normalization AST, - // we do not need to include resolver modules since those modules will be - // evaluated at read time. - fn build_normalization_relay_resolver_read_time( + // This function generates a Normalization AST node that is the UNION of the node that would be + // generated for the exec time resolvers case and the node that would be generated for the read + // time resolvers case. This is because we need information to fulfill requests for both cases + // in the runtime now, since the query's mode is determined dynamically at runtime. + fn build_normalization_relay_resolver_exec_and_read_time( &mut self, resolver_metadata: &RelayResolverMetadata, inline_fragment: Option, + exec_resolvers_only: bool, ) -> Primitive { let field_name = resolver_metadata.field_name(self.schema); let field_arguments = &resolver_metadata.field_arguments; @@ -863,17 +930,36 @@ impl<'schema, 'builder, 'config> CodegenBuilder<'schema, 'builder, 'config> { let is_output_type = resolver_metadata .output_type_info .normalization_ast_should_have_is_output_type_true(); - Primitive::Key(self.object(object! { + let kind = if resolver_metadata.live { + CODEGEN_CONSTANTS.relay_live_resolver + } else { + CODEGEN_CONSTANTS.relay_resolver + }; + let variable_name = resolver_metadata.generate_local_resolver_name(self.schema); + let artifact_path = &self + .project_config + .artifact_path_for_definition(self.definition_source_location); + let resolver_info = build_resolver_info( + self.ast_builder, + self.project_config, + artifact_path, + self.schema.field(resolver_metadata.field_id), + resolver_metadata.import_path, + match resolver_metadata.import_name { + Some(name) => ModuleImportName::Named { + name, + import_as: Some(variable_name), + }, + None => ModuleImportName::Default(variable_name), + }, + ); + let mut obj = object! { name: Primitive::String(field_name), args: match args { None => Primitive::SkippableNull, Some(key) => Primitive::Key(key), }, - fragment: match inline_fragment { - None => Primitive::SkippableNull, - Some(fragment) => fragment, - }, - kind: Primitive::String(CODEGEN_CONSTANTS.relay_resolver), + kind: Primitive::String(kind), storage_key: match args { None => Primitive::SkippableNull, Some(key) => { @@ -885,17 +971,27 @@ impl<'schema, 'builder, 'config> CodegenBuilder<'schema, 'builder, 'config> { } }, is_output_type: Primitive::Bool(is_output_type), - })) + resolver_info: Primitive::Key(resolver_info), + }; + if !exec_resolvers_only { + obj.push(ObjectEntry { + key: *FRAGMENT_KEY, + value: match inline_fragment { + None => Primitive::SkippableNull, + Some(fragment) => fragment, + }, + }); + } + Primitive::Key(self.object(obj)) } - // For execution time Relay Resolvers in the normalization AST, we need to - // also include enough information for resolver function backing each field, - // so that normalization AST have full information on how to resolve client - // edges and fields. That means we need to include the resolver module. Note - // that we don't support inline fragment as we did for read time resolvers - fn build_normalization_relay_resolver_execution_time( + // For read time Relay Resolvers in the normalization AST, we do not + // need to include resolver modules since those modules will be + // evaluated at read time. + fn build_normalization_relay_resolver_read_time( &mut self, resolver_metadata: &RelayResolverMetadata, + inline_fragment: Option, ) -> Primitive { let field_name = resolver_metadata.field_name(self.schema); let field_arguments = &resolver_metadata.field_arguments; @@ -903,38 +999,17 @@ impl<'schema, 'builder, 'config> CodegenBuilder<'schema, 'builder, 'config> { let is_output_type = resolver_metadata .output_type_info .normalization_ast_should_have_is_output_type_true(); - - let variable_name = resolver_metadata.generate_local_resolver_name(self.schema); - let artifact_path = &self - .project_config - .artifact_path_for_definition(self.definition_source_location); - let kind = if resolver_metadata.live { - CODEGEN_CONSTANTS.relay_live_resolver - } else { - CODEGEN_CONSTANTS.relay_resolver - }; - let resolver_info = build_resolver_info( - self.ast_builder, - self.project_config, - artifact_path, - self.schema.field(resolver_metadata.field_id), - resolver_metadata.import_path, - match resolver_metadata.import_name { - Some(name) => ModuleImportName::Named { - name, - import_as: Some(variable_name), - }, - None => ModuleImportName::Default(variable_name), - }, - ); - Primitive::Key(self.object(object! { name: Primitive::String(field_name), args: match args { None => Primitive::SkippableNull, Some(key) => Primitive::Key(key), }, - kind: Primitive::String(kind), + fragment: match inline_fragment { + None => Primitive::SkippableNull, + Some(fragment) => fragment, + }, + kind: Primitive::String(CODEGEN_CONSTANTS.relay_resolver), storage_key: match args { None => Primitive::SkippableNull, Some(key) => { @@ -946,7 +1021,6 @@ impl<'schema, 'builder, 'config> CodegenBuilder<'schema, 'builder, 'config> { } }, is_output_type: Primitive::Bool(is_output_type), - resolver_info: Primitive::Key(resolver_info), })) } @@ -1372,8 +1446,9 @@ impl<'schema, 'builder, 'config> CodegenBuilder<'schema, 'builder, 'config> { key: type_name, value: match self.variant { CodegenVariant::Reader => self.build_reader_client_edge_model_resolver( - model_resolver.type_name, - model_resolver.is_live, + &model_resolver.model_field_id, + &model_resolver.type_name, + &model_resolver.resolver_info, relay_resolver_metadata, ), CodegenVariant::Normalization => self @@ -1416,44 +1491,38 @@ impl<'schema, 'builder, 'config> CodegenBuilder<'schema, 'builder, 'config> { fn build_reader_client_edge_model_resolver( &mut self, - type_name: WithLocation, - is_live: bool, - relay_resolver_metadata: &RelayResolverMetadata, + model_field_id: &FieldID, + type_name: &WithLocation, + resolver_info: &ResolverInfo, + backing_resolver_metadata: &RelayResolverMetadata, ) -> Primitive { - let id_fragment_artifact_name = self - .project_config - .name - .generate_name_for_object_and_field(type_name.item.0, CODEGEN_CONSTANTS.id); + let fragment_name = resolver_info.fragment_name.unwrap(); + let path = format!( "{}.{}", - relay_resolver_metadata.field_path, *RELAY_RESOLVER_MODEL_INSTANCE_FIELD + backing_resolver_metadata.field_path, *RELAY_RESOLVER_MODEL_INSTANCE_FIELD ) .intern(); + let model_resolver_metadata = RelayResolverMetadata { - field_id: relay_resolver_metadata.field_id, - import_path: type_name.location.source_location().path().intern(), + field_id: *model_field_id, + import_path: resolver_info.import_path, import_name: Some(type_name.item.0), field_alias: None, field_path: path, field_arguments: vec![], // The model resolver field does not take GraphQL arguments. - live: is_live, - output_type_info: relay_resolver_metadata.output_type_info.clone(), - fragment_data_injection_mode: Some(( - WithLocation::new( - type_name.location, - FragmentDefinitionName(id_fragment_artifact_name.clone().intern()), - ), - FragmentDataInjectionMode::Field { - name: CODEGEN_CONSTANTS.id, - is_required: true, - }, - )), - type_confirmed: relay_resolver_metadata.type_confirmed, + live: resolver_info.live, + output_type_info: ResolverOutputTypeInfo::ScalarField, + fragment_data_injection_mode: resolver_info + .fragment_data_injection_mode + .map(|mode| (WithLocation::new(type_name.location, fragment_name), mode)), + type_confirmed: resolver_info.type_confirmed, + resolver_type: resolver_info.resolver_type, }; let fragment_primitive = Primitive::Key(self.object(object! { args: Primitive::SkippableNull, kind: Primitive::String(CODEGEN_CONSTANTS.fragment_spread), - name: Primitive::String(id_fragment_artifact_name.clone().intern()), + name: Primitive::String(fragment_name.0), })); self.build_reader_relay_resolver(&model_resolver_metadata, Some(fragment_primitive)) } @@ -1534,10 +1603,19 @@ impl<'schema, 'builder, 'config> CodegenBuilder<'schema, 'builder, 'config> { ), ); + let resolver_fn = match relay_resolver_metadata.resolver_type { + ResolverSchemaGenType::ResolverModule => { + ResolverJSFunction::Module(resolver_js_module) + } + ResolverSchemaGenType::PropertyLookup { property_name } => { + ResolverJSFunction::PropertyLookup(property_name.to_string()) + } + }; + Primitive::RelayResolverModel { graphql_module_name: fragment_name.item.0, graphql_module_path: fragment_import_path, - js_module: resolver_js_module, + resolver_fn, injected_field_name_details: match injection_mode { FragmentDataInjectionMode::Field { name, is_required } => { Some((name, is_required)) @@ -1552,6 +1630,25 @@ impl<'schema, 'builder, 'config> CodegenBuilder<'schema, 'builder, 'config> { // information to _read_ the resolver. Specifically, enough data // to construct a fragment key, and an import of the resolver // module itself. + // For model resolvers that inject fragment data via a special backing field, + // keep the displayed `name` equal to the public field name (the first segment in the path), + // not the injected backing field name (e.g. `__relay_model_instance`). + let display_name = if relay_resolver_metadata + .fragment_data_injection_mode + .is_some() + { + let path_str = path.lookup(); + if path_str.ends_with(".__relay_model_instance") { + match path_str.split('.').next() { + Some(first) => first.intern(), + None => field_name, + } + } else { + field_name + } + } else { + field_name + }; let mut object_props = object! { :build_alias(field_alias, field_name), args: args, @@ -1560,7 +1657,7 @@ impl<'schema, 'builder, 'config> CodegenBuilder<'schema, 'builder, 'config> { Some(fragment_primitive) => fragment_primitive, }, kind: Primitive::String(kind), - name: Primitive::String(field_name), + name: Primitive::String(display_name), resolver_module: resolver_module, path: Primitive::String(path), }; @@ -1751,13 +1848,17 @@ impl<'schema, 'builder, 'config> CodegenBuilder<'schema, 'builder, 'config> { }) } - fn build_client_edge_with_enabled_resolver_normalization_ast( + // This function creates a node that is the UNION of the nodes that would be created for read time resolvers + // and for exec time resolvers (so runtime has ALL the information it needs to run for both resolver modes.) + // Surprisingly, this function can stay exactly the same as build_client_edge_with_enabled_resolver_normalization_ast + // (the function for exec time resolver nodes). + fn build_client_edge_exec_and_read_time( &mut self, context: &mut ContextualMetadata, client_edge_metadata: ClientEdgeMetadata<'_>, ) -> Primitive { let backing_field_primitives = - self.build_selections_from_selection(context, &client_edge_metadata.backing_field); + self.build_selections_from_selection(context, client_edge_metadata.backing_field); if backing_field_primitives.len() != 1 { panic!( @@ -1825,7 +1926,7 @@ impl<'schema, 'builder, 'config> CodegenBuilder<'schema, 'builder, 'config> { client_edge_metadata: ClientEdgeMetadata<'_>, ) -> Primitive { let backing_field_primitives = - self.build_selections_from_selection(context, &client_edge_metadata.backing_field); + self.build_selections_from_selection(context, client_edge_metadata.backing_field); if backing_field_primitives.len() != 1 { panic!( @@ -1994,8 +2095,13 @@ impl<'schema, 'builder, 'config> CodegenBuilder<'schema, 'builder, 'config> { ) } CodegenVariant::Normalization => { - if self.use_exec_time_resolvers(context) { - self.build_client_edge_with_enabled_resolver_normalization_ast( + // In this case, the functions we run for exec time mode only and for exec and read time mode + // are the exact same. This is because the node fields for read time is a subset of the node + // fields for exec time. + if self.use_exec_time_resolvers(context) + || self.use_exec_and_read_time_resolvers(context) + { + self.build_client_edge_exec_and_read_time( context, client_edge_metadata, ) @@ -2382,17 +2488,11 @@ impl<'schema, 'builder, 'config> CodegenBuilder<'schema, 'builder, 'config> { kind: Primitive::String(CODEGEN_CONSTANTS.module_import), }; - let should_use_reader_module_imports = - match &self.project_config.feature_flags.use_reader_module_imports { - FeatureFlag::Enabled => true, - FeatureFlag::Disabled => false, - FeatureFlag::Limited { - allowlist: fragment_names, - } => fragment_names.contains(&module_metadata.key), - FeatureFlag::Rollout { rollout } => { - rollout.check(module_metadata.key.lookup().as_bytes()) - } - }; + let should_use_reader_module_imports = self + .project_config + .feature_flags + .use_reader_module_imports + .is_enabled_for(module_metadata.key); match self.variant { CodegenVariant::Reader => { @@ -2413,32 +2513,39 @@ impl<'schema, 'builder, 'config> CodegenBuilder<'schema, 'builder, 'config> { } } CodegenVariant::Normalization => { - if module_metadata.read_time_resolvers { - return vec![]; - } if let Some(dynamic_module_provider) = self .project_config .module_import_config .dynamic_module_provider { - match self.project_config.module_import_config.surface { - None | Some(Surface::All) => { - module_import.push(ObjectEntry { - key: CODEGEN_CONSTANTS.component_module_provider, - value: Primitive::DynamicImport { - provider: dynamic_module_provider, - module: module_metadata.module_name, - }, - }); - module_import.push(ObjectEntry { - key: CODEGEN_CONSTANTS.operation_module_provider, - value: Primitive::DynamicImport { - provider: dynamic_module_provider, - module: get_normalization_fragment_filename(fragment_name), - }, - }); - } - Some(Surface::Resolvers) => {} + if self.project_config.module_import_config.surface.is_none() + || self.project_config.module_import_config.surface == Some(Surface::All) + || (self.project_config.module_import_config.surface + == Some(Surface::Resolvers) + && module_metadata.read_time_resolvers) + { + let operation_module_provider = match self + .project_config + .module_import_config + .operation_module_provider + { + Some(operation_module_provider) => operation_module_provider, + None => dynamic_module_provider, + }; + module_import.push(ObjectEntry { + key: CODEGEN_CONSTANTS.component_module_provider, + value: Primitive::DynamicImport { + provider: dynamic_module_provider, + module: module_metadata.module_name, + }, + }); + module_import.push(ObjectEntry { + key: CODEGEN_CONSTANTS.operation_module_provider, + value: Primitive::DynamicImport { + provider: operation_module_provider, + module: get_normalization_fragment_filename(fragment_name), + }, + }); } } } @@ -2486,7 +2593,9 @@ impl<'schema, 'builder, 'config> CodegenBuilder<'schema, 'builder, 'config> { let provider = ProvidedVariableMetadata::find(&def.directives)?; let provider_module = - if matches!(self.project_config.js_module_format, JsModuleFormat::Haste) { + if matches!(self.project_config.js_module_format, JsModuleFormat::Haste) + || (!self.project_config.relativize_js_module_paths && provider.is_bare()) + { provider.module_name } else { // This will build a path from the operation artifact to the provider module @@ -2560,7 +2669,7 @@ impl<'schema, 'builder, 'config> CodegenBuilder<'schema, 'builder, 'config> { // Construct metadata object let mut params_object = vec![]; - if let Some(ref text) = &request_parameters.text { + if let Some(text) = &request_parameters.text { params_object.push(ObjectEntry { key: CODEGEN_CONSTANTS.cache_id, value: Primitive::RawString(md5(text)), @@ -2602,9 +2711,10 @@ impl<'schema, 'builder, 'config> CodegenBuilder<'schema, 'builder, 'config> { }), }); + let text = get_raw_text_value(operation).or(request_parameters.text); params_object.push(ObjectEntry { key: CODEGEN_CONSTANTS.text, - value: match request_parameters.text { + value: match text { Some(text) => Primitive::RawString(text), None => Primitive::Null, }, @@ -2738,4 +2848,5 @@ pub fn md5(data: &str) -> String { struct ContextualMetadata { has_client_edges: bool, has_exec_time_resolvers_directive: bool, + has_exec_time_resolvers_enabled_provider: bool, } diff --git a/compiler/crates/relay-codegen/src/lib.rs b/compiler/crates/relay-codegen/src/lib.rs index 2d75941785dc5..b6cf83dc49f1e 100644 --- a/compiler/crates/relay-codegen/src/lib.rs +++ b/compiler/crates/relay-codegen/src/lib.rs @@ -22,17 +22,17 @@ pub use ast::JSModule; pub use ast::Primitive; pub use ast::QueryID; pub use ast::RequestParameters; -pub use build_ast::build_request_params; -pub use build_ast::is_static_storage_key_available; pub use build_ast::CodegenBuilder; pub use build_ast::CodegenVariant; +pub use build_ast::build_request_params; +pub use build_ast::is_static_storage_key_available; pub use constants::CODEGEN_CONSTANTS; +pub use printer::JSONPrinter; +pub use printer::Printer; pub use printer::print_fragment; pub use printer::print_operation; pub use printer::print_provided_variables; pub use printer::print_request; pub use printer::print_request_params; -pub use printer::JSONPrinter; -pub use printer::Printer; pub use relay_config::JsModuleFormat; pub use top_level_statements::{TopLevelStatement, TopLevelStatements}; diff --git a/compiler/crates/relay-codegen/src/printer.rs b/compiler/crates/relay-codegen/src/printer.rs index ec25bac2badea..5d0d77ac80be2 100644 --- a/compiler/crates/relay-codegen/src/printer.rs +++ b/compiler/crates/relay-codegen/src/printer.rs @@ -13,17 +13,21 @@ use std::path::Path; use fnv::FnvBuildHasher; use fnv::FnvHashSet; -use graphql_ir::reexport::Intern; use graphql_ir::ExecutableDefinitionName; use graphql_ir::FragmentDefinition; use graphql_ir::OperationDefinition; +use graphql_ir::reexport::Intern; use indexmap::IndexMap; -use intern::string_key::StringKey; use intern::Lookup; -use relay_config::DynamicModuleProvider; +use intern::string_key::StringKey; +use relay_config::ModuleProvider; +use relay_config::TypegenLanguage; use relay_config::ProjectConfig; use schema::SDLSchema; +use crate::CodegenBuilder; +use crate::CodegenVariant; +use crate::JsModuleFormat; use crate::ast::Ast; use crate::ast::AstBuilder; use crate::ast::AstKey; @@ -34,6 +38,7 @@ use crate::ast::ObjectEntry; use crate::ast::Primitive; use crate::ast::QueryID; use crate::ast::RequestParameters; +use crate::ast::ResolverJSFunction; use crate::ast::ResolverModuleReference; use crate::build_ast::build_fragment; use crate::build_ast::build_operation; @@ -49,9 +54,6 @@ use crate::object; use crate::top_level_statements::TopLevelStatement; use crate::top_level_statements::TopLevelStatements; use crate::utils::escape; -use crate::CodegenBuilder; -use crate::CodegenVariant; -use crate::JsModuleFormat; pub fn print_operation( schema: &SDLSchema, @@ -334,6 +336,8 @@ pub struct JSONPrinter<'b> { js_module_format: JsModuleFormat, top_level_statements: &'b mut TopLevelStatements, skip_printing_nulls: bool, + relativize_js_module_paths: bool, + is_rescript: bool, } impl<'b> JSONPrinter<'b> { @@ -348,11 +352,13 @@ impl<'b> JSONPrinter<'b> { duplicates: Default::default(), builder, js_module_format: project_config.js_module_format, + relativize_js_module_paths: project_config.relativize_js_module_paths, eager_es_modules: project_config.typegen_config.eager_es_modules, skip_printing_nulls: project_config .feature_flags .skip_printing_nulls .is_fully_enabled(), + is_rescript: matches!(project_config.typegen_config.language, TypegenLanguage::ReScript), } } @@ -545,37 +551,38 @@ impl<'b> JSONPrinter<'b> { ), GraphQLModuleDependency::Path { name, path } => (name, path), }; + // TODO(sbarag): this specific codepath needs to force-relativize under commonjs. + // There are likely others. self.write_js_dependency( f, - ModuleImportName::Default(format!("rescript_graphql_node_{}", variable_name).intern()), + ModuleImportName::Default( + format!("rescript_graphql_node_{}", variable_name).intern(), + ), Cow::Owned(format!( - "rescript_graphql_node_{}", - get_module_path(self.js_module_format, *key) + "{}.graphql", + self.get_module_path(*key, ModuleOrigin::Artifact) )), ) } Primitive::JSModuleDependency(JSModuleDependency { path, import_name }) => { - let write_js_dependency = self - .write_js_dependency( - f, - match import_name { - ModuleImportName::Default(_) => ModuleImportName::Default(format!( - "rescript_module_{}", - common::rescript_utils::get_module_name_from_file_path( - &path.to_string().as_str() - ) - ).intern()), - o => o.clone() - }, - Cow::Owned(format!( - "rescript_module_{}", - common::rescript_utils::get_module_name_from_file_path( - &path.to_string().as_str() - ) - )), - ); - write_js_dependency - }, + let adjusted_import_name = match import_name { + ModuleImportName::Default(_) => ModuleImportName::Default( + format!( + "rescript_module_{}", + common::rescript_utils::get_module_name_from_file_path( + &path.to_string().as_str() + ) + ) + .intern(), + ), + other => other.clone(), + }; + self.write_js_dependency( + f, + adjusted_import_name, + self.get_module_path(*path, ModuleOrigin::SourceFile), + ) + } Primitive::ResolverModuleReference(ResolverModuleReference { field_type, resolver_function_name, @@ -583,7 +590,7 @@ impl<'b> JSONPrinter<'b> { self.write_resolver_module_reference(f, resolver_function_name.clone(), field_type) } Primitive::DynamicImport { provider, module } => match provider { - DynamicModuleProvider::JSResource => { + ModuleProvider::JSResource => { self.top_level_statements.insert( "JSResource".to_string(), TopLevelStatement::ImportStatement(JSModuleDependency { @@ -593,10 +600,10 @@ impl<'b> JSONPrinter<'b> { ); write!(f, "() => JSResource('m#{}')", module) } - DynamicModuleProvider::Custom { statement } => { + ModuleProvider::Custom { statement } => { f.push_str(&statement.lookup().replace( "<$module>", - &get_module_path(self.js_module_format, *module), + &self.get_module_path(*module, ModuleOrigin::SourceFile), )); Ok(()) } @@ -604,13 +611,13 @@ impl<'b> JSONPrinter<'b> { Primitive::RelayResolverModel { graphql_module_path, graphql_module_name, - js_module, + resolver_fn, injected_field_name_details, } => self.write_relay_resolver_model( f, *graphql_module_name, *graphql_module_path, - js_module, + resolver_fn, injected_field_name_details.as_ref().copied(), ), } @@ -642,6 +649,30 @@ impl<'b> JSONPrinter<'b> { module_import_name: ModuleImportName, path: Cow<'_, str>, ) -> FmtResult { + // ReScript artifacts expect special sentinel identifiers inside the printed JSON + // so the ReScript post-processor can rewrite them into makeNode params. For ReScript, + // always emit the sentinel identifiers rather than real import paths/aliases. + if self.is_rescript { + match module_import_name { + ModuleImportName::Default(name) => { + // For default imports, emit the provided identifier as-is + // (e.g. `rescript_graphql_node_` or `rescript_module_`) + return write!(f, "{}", name); + } + ModuleImportName::Named { name, .. } => { + // Special-case the injector to keep the raw identifier + if name.to_string() == "resolverDataInjector" { + return write!(f, "{}", name); + } + // For named imports, emit `rescript_module_.` + let module_alias = format!( + "rescript_module_{}", + common::rescript_utils::get_module_name_from_file_path(&path.to_string().as_str()) + ); + return write!(f, "{}.{}", module_alias, name); + } + } + } if self.eager_es_modules { let path = path.into_owned(); let key = match module_import_name { @@ -682,7 +713,7 @@ impl<'b> JSONPrinter<'b> { f: &mut String, graphql_module_name: StringKey, graphql_module_path: StringKey, - js_module: &JSModuleDependency, + resolver_fn: &ResolverJSFunction, injected_field_name_details: Option<(StringKey, bool)>, ) -> FmtResult { let relay_runtime_experimental = "relay-runtime/experimental"; @@ -699,49 +730,96 @@ impl<'b> JSONPrinter<'b> { write!(f, "(")?; self.write_js_dependency( f, - ModuleImportName::Default(format!("rescript_graphql_node_{}", graphql_module_name).intern()), + ModuleImportName::Default( + format!("rescript_graphql_node_{}", graphql_module_name).intern(), + ), Cow::Owned(format!( - "rescript_graphql_node_{}", - get_module_path(self.js_module_format, graphql_module_path) + "{}.graphql", + self.get_module_path(graphql_module_path, ModuleOrigin::Artifact) )), )?; write!(f, ", ")?; - self.write_js_dependency( - f, - js_module.import_name.clone(), - Cow::Owned(format!("rescript_module_{}", get_module_path(self.js_module_format, js_module.path))), - )?; + match resolver_fn { + ResolverJSFunction::Module(js_module) => self.write_js_dependency( + f, + match &js_module.import_name { + ModuleImportName::Default(_) => ModuleImportName::Default( + format!( + "rescript_module_{}", + common::rescript_utils::get_module_name_from_file_path( + &js_module.path.to_string().as_str() + ) + ) + .intern(), + ), + other => other.clone(), + }, + self.get_module_path(js_module.path, ModuleOrigin::SourceFile), + )?, + ResolverJSFunction::PropertyLookup(property) => { + write_arrow_fn(f, &["o"], &format!("o.{}", property))? + } + } if let Some((field_name, is_required_field)) = injected_field_name_details { write!(f, ", '{}'", field_name)?; write!(f, ", {}", is_required_field)?; } write!(f, ")") } -} - -pub fn get_module_path(js_module_format: JsModuleFormat, key: StringKey) -> Cow<'static, str> { - match js_module_format { - JsModuleFormat::CommonJS => { - let path = Path::new(key.lookup()); - let extension = path.extension(); - - if let Some(extension) = extension { - if extension == "ts" || extension == "tsx" || extension == "js" { - let path_without_extension = path.with_extension(""); - let path_without_extension = path_without_extension - .to_str() - .expect("could not convert `path_without_extension` to a str"); - - return Cow::Owned(format!("./{}", path_without_extension)); + fn get_module_path(&self, key: StringKey, origin: ModuleOrigin) -> Cow<'static, str> { + match self.js_module_format { + JsModuleFormat::CommonJS => { + let path = Path::new(key.lookup()); + let extension = path.extension(); + + let has_path_prefix = + path.starts_with("./") || path.starts_with("../") || path.starts_with("/"); + // Files generated by Relay must always be relativized, since authors aren't + // expected to predict the output path. + let should_relativize = matches!(origin, ModuleOrigin::Artifact) + || (self.relativize_js_module_paths && !has_path_prefix); + + if let Some(extension) = extension { + if extension == "ts" || extension == "tsx" || extension == "js" { + let path_without_extension = path.with_extension(""); + + let path_without_extension = path_without_extension + .to_str() + .expect("could not convert `path_without_extension` to a str"); + + return Cow::Owned(if should_relativize { + format!("./{}", path_without_extension) + } else { + path_without_extension.to_string() + }); + } } + Cow::Owned(if should_relativize { + format!("./{}", key.borrow()) + } else { + key.borrow().to_string() + }) } - Cow::Owned(format!("./{}", key.borrow())) + JsModuleFormat::Haste => Cow::Borrowed(key.lookup()), } - JsModuleFormat::Haste => Cow::Borrowed(key.lookup()), } } +/// Describes +#[derive(Debug)] +enum ModuleOrigin { + /// A file maintained outside of Relay. + SourceFile, + /// A file generated by the Relay compiler. + Artifact, +} + +fn write_arrow_fn(f: &mut String, params: &[&str], body: &str) -> FmtResult { + write!(f, "({}) => {}", params.join(", "), body)?; + Ok(()) +} + fn write_static_storage_key( f: &mut String, builder: &AstBuilder, diff --git a/compiler/crates/relay-codegen/tests/aliased_fragments.rs b/compiler/crates/relay-codegen/tests/aliased_fragments.rs index 7f49c09c40bcd..9ceb6685a3345 100644 --- a/compiler/crates/relay-codegen/tests/aliased_fragments.rs +++ b/compiler/crates/relay-codegen/tests/aliased_fragments.rs @@ -10,13 +10,13 @@ use std::sync::Arc; use common::FeatureFlag; use common::SourceLocationKey; use fixture_tests::Fixture; -use graphql_ir::build; use graphql_ir::Program; +use graphql_ir::build; use graphql_syntax::parse_executable; use graphql_test_helpers::diagnostics_to_sorted_string; +use relay_codegen::JsModuleFormat; use relay_codegen::print_fragment; use relay_codegen::print_operation; -use relay_codegen::JsModuleFormat; use relay_config::ProjectConfig; use relay_test_schema::get_test_schema; use relay_transforms::fragment_alias_directive; diff --git a/compiler/crates/relay-codegen/tests/catch_directive_codegen.rs b/compiler/crates/relay-codegen/tests/catch_directive_codegen.rs index c929a273bee90..b38d4cf79b877 100644 --- a/compiler/crates/relay-codegen/tests/catch_directive_codegen.rs +++ b/compiler/crates/relay-codegen/tests/catch_directive_codegen.rs @@ -10,13 +10,13 @@ use std::sync::Arc; use common::FeatureFlag; use common::SourceLocationKey; use fixture_tests::Fixture; -use graphql_ir::build; use graphql_ir::Program; +use graphql_ir::build; use graphql_syntax::parse_executable; use graphql_test_helpers::diagnostics_to_sorted_string; +use relay_codegen::JsModuleFormat; use relay_codegen::print_fragment; use relay_codegen::print_operation; -use relay_codegen::JsModuleFormat; use relay_config::ProjectConfig; use relay_test_schema::get_test_schema; use relay_test_schema::get_test_schema_with_extensions; diff --git a/compiler/crates/relay-codegen/tests/client_edges.rs b/compiler/crates/relay-codegen/tests/client_edges.rs index 765832310f23f..8f37fa0d9ca40 100644 --- a/compiler/crates/relay-codegen/tests/client_edges.rs +++ b/compiler/crates/relay-codegen/tests/client_edges.rs @@ -11,12 +11,12 @@ use common::FeatureFlag; use common::FeatureFlags; use common::SourceLocationKey; use fixture_tests::Fixture; -use graphql_ir::build; use graphql_ir::Program; +use graphql_ir::build; use graphql_syntax::parse_executable; +use relay_codegen::JsModuleFormat; use relay_codegen::print_fragment; use relay_codegen::print_operation; -use relay_codegen::JsModuleFormat; use relay_config::ProjectConfig; use relay_config::ProjectName; use relay_test_schema::get_test_schema_with_extensions; diff --git a/compiler/crates/relay-codegen/tests/client_edges/fixtures/client-edge-exec-time-resolver.expected b/compiler/crates/relay-codegen/tests/client_edges/fixtures/client-edge-exec-time-resolver.expected index a74f18e606e3b..4b9431d1284d5 100644 --- a/compiler/crates/relay-codegen/tests/client_edges/fixtures/client-edge-exec-time-resolver.expected +++ b/compiler/crates/relay-codegen/tests/client_edges/fixtures/client-edge-exec-time-resolver.expected @@ -1,7 +1,7 @@ ==================================== INPUT ==================================== # enable-exec-time-resolvers-directive -query Foo @exec_time_resolvers { +query Foo @exec_time_resolvers(enabledProvider: "execTimeResolversFlagProvider") { me { pet { name @@ -15,7 +15,7 @@ type Cat @__RelayResolverModel { name: String @relay_resolver(import_name: "name", import_path: "CatNameResolver") __relay_model_instance: RelayResolverValue! - @relay_resolver(import_name: "Cat") + @relay_resolver(import_name: "Cat", import_path: "CatNameResolver") @unselectable( reason: "This field is intended only for Relay's internal use" ) @@ -54,7 +54,8 @@ extend type User { "resolverInfo": { "resolverFunction": require('PetResolver').Pet, "rootFragment": null - } + }, + "fragment": null }, "linkedField": { "alias": null, @@ -73,7 +74,8 @@ extend type User { "resolverInfo": { "resolverFunction": require('CatNameResolver').name, "rootFragment": null - } + }, + "fragment": null } ], "storageKey": null @@ -83,5 +85,5 @@ extend type User { "storageKey": null } ], - "use_exec_time_resolvers": true + "exec_time_resolvers_enabled_provider": require('execTimeResolversFlagProvider') } diff --git a/compiler/crates/relay-codegen/tests/client_edges/fixtures/client-edge-exec-time-resolver.graphql b/compiler/crates/relay-codegen/tests/client_edges/fixtures/client-edge-exec-time-resolver.graphql index 98a9f7822a4c6..296b8bb030aaf 100644 --- a/compiler/crates/relay-codegen/tests/client_edges/fixtures/client-edge-exec-time-resolver.graphql +++ b/compiler/crates/relay-codegen/tests/client_edges/fixtures/client-edge-exec-time-resolver.graphql @@ -1,6 +1,6 @@ # enable-exec-time-resolvers-directive -query Foo @exec_time_resolvers { +query Foo @exec_time_resolvers(enabledProvider: "execTimeResolversFlagProvider") { me { pet { name @@ -14,7 +14,7 @@ type Cat @__RelayResolverModel { name: String @relay_resolver(import_name: "name", import_path: "CatNameResolver") __relay_model_instance: RelayResolverValue! - @relay_resolver(import_name: "Cat") + @relay_resolver(import_name: "Cat", import_path: "CatNameResolver") @unselectable( reason: "This field is intended only for Relay's internal use" ) diff --git a/compiler/crates/relay-codegen/tests/client_extensions.rs b/compiler/crates/relay-codegen/tests/client_extensions.rs index f837cb859f827..154e03edf9a6d 100644 --- a/compiler/crates/relay-codegen/tests/client_extensions.rs +++ b/compiler/crates/relay-codegen/tests/client_extensions.rs @@ -9,12 +9,12 @@ use std::sync::Arc; use common::SourceLocationKey; use fixture_tests::Fixture; -use graphql_ir::build; use graphql_ir::Program; +use graphql_ir::build; use graphql_syntax::parse_executable; +use relay_codegen::JsModuleFormat; use relay_codegen::print_fragment; use relay_codegen::print_operation; -use relay_codegen::JsModuleFormat; use relay_config::ProjectConfig; use relay_test_schema::get_test_schema_with_extensions; use relay_transforms::client_extensions; diff --git a/compiler/crates/relay-codegen/tests/client_extensions_abstract_types.rs b/compiler/crates/relay-codegen/tests/client_extensions_abstract_types.rs index 2cd010d60b71e..74588c1185f8b 100644 --- a/compiler/crates/relay-codegen/tests/client_extensions_abstract_types.rs +++ b/compiler/crates/relay-codegen/tests/client_extensions_abstract_types.rs @@ -9,12 +9,12 @@ use std::sync::Arc; use common::SourceLocationKey; use fixture_tests::Fixture; -use graphql_ir::build; use graphql_ir::Program; +use graphql_ir::build; use graphql_syntax::parse_executable; +use relay_codegen::JsModuleFormat; use relay_codegen::print_fragment; use relay_codegen::print_operation; -use relay_codegen::JsModuleFormat; use relay_config::ProjectConfig; use relay_test_schema::get_test_schema_with_extensions; use relay_transforms::client_extensions_abstract_types; diff --git a/compiler/crates/relay-codegen/tests/connections.rs b/compiler/crates/relay-codegen/tests/connections.rs index 3c50397169b6d..8c1e6007ff2dd 100644 --- a/compiler/crates/relay-codegen/tests/connections.rs +++ b/compiler/crates/relay-codegen/tests/connections.rs @@ -9,21 +9,21 @@ use std::sync::Arc; use common::SourceLocationKey; use fixture_tests::Fixture; -use graphql_ir::build; use graphql_ir::FragmentDefinition; use graphql_ir::FragmentDefinitionName; use graphql_ir::Program; +use graphql_ir::build; use graphql_syntax::parse_executable; use graphql_test_helpers::diagnostics_to_sorted_string; -use relay_codegen::build_request_params; use relay_codegen::JsModuleFormat; use relay_codegen::Printer; +use relay_codegen::build_request_params; use relay_config::DeferStreamInterface; use relay_config::ProjectConfig; use relay_test_schema::get_test_schema; +use relay_transforms::ConnectionInterface; use relay_transforms::transform_connections; use relay_transforms::validate_connections; -use relay_transforms::ConnectionInterface; pub async fn transform_fixture(fixture: &Fixture<'_>) -> Result { let project_config = ProjectConfig { diff --git a/compiler/crates/relay-codegen/tests/defer_stream.rs b/compiler/crates/relay-codegen/tests/defer_stream.rs index e8a51a97b938e..94ea7b452ed53 100644 --- a/compiler/crates/relay-codegen/tests/defer_stream.rs +++ b/compiler/crates/relay-codegen/tests/defer_stream.rs @@ -9,12 +9,12 @@ use std::sync::Arc; use common::SourceLocationKey; use fixture_tests::Fixture; -use graphql_ir::build; use graphql_ir::Program; +use graphql_ir::build; use graphql_syntax::parse_executable; +use relay_codegen::JsModuleFormat; use relay_codegen::print_fragment; use relay_codegen::print_operation; -use relay_codegen::JsModuleFormat; use relay_config::DeferStreamInterface; use relay_config::ProjectConfig; use relay_test_schema::get_test_schema; diff --git a/compiler/crates/relay-codegen/tests/json_codegen.rs b/compiler/crates/relay-codegen/tests/json_codegen.rs index e2f1861b73d46..6eada12b2760b 100644 --- a/compiler/crates/relay-codegen/tests/json_codegen.rs +++ b/compiler/crates/relay-codegen/tests/json_codegen.rs @@ -7,12 +7,12 @@ use common::SourceLocationKey; use fixture_tests::Fixture; -use graphql_ir::build; use graphql_ir::ExecutableDefinition; +use graphql_ir::build; use graphql_syntax::parse_executable; +use relay_codegen::JsModuleFormat; use relay_codegen::print_fragment; use relay_codegen::print_operation; -use relay_codegen::JsModuleFormat; use relay_config::ProjectConfig; use relay_test_schema::TEST_SCHEMA; diff --git a/compiler/crates/relay-codegen/tests/relay_actor_change.rs b/compiler/crates/relay-codegen/tests/relay_actor_change.rs index f1bab63be9a56..1cf690e2f1ed7 100644 --- a/compiler/crates/relay-codegen/tests/relay_actor_change.rs +++ b/compiler/crates/relay-codegen/tests/relay_actor_change.rs @@ -10,13 +10,13 @@ use std::sync::Arc; use common::FeatureFlag; use common::SourceLocationKey; use fixture_tests::Fixture; -use graphql_ir::build; use graphql_ir::Program; +use graphql_ir::build; use graphql_syntax::parse_executable; use graphql_test_helpers::diagnostics_to_sorted_string; +use relay_codegen::JsModuleFormat; use relay_codegen::print_fragment; use relay_codegen::print_operation; -use relay_codegen::JsModuleFormat; use relay_config::ProjectConfig; use relay_test_schema::get_test_schema; use relay_transforms::relay_actor_change_transform; diff --git a/compiler/crates/relay-codegen/tests/request_metadata.rs b/compiler/crates/relay-codegen/tests/request_metadata.rs index c1b64489d5a4f..a15622ccdf50b 100644 --- a/compiler/crates/relay-codegen/tests/request_metadata.rs +++ b/compiler/crates/relay-codegen/tests/request_metadata.rs @@ -10,7 +10,6 @@ use common::DirectiveName; use common::SourceLocationKey; use common::WithLocation; use fixture_tests::Fixture; -use graphql_ir::build; use graphql_ir::Argument; use graphql_ir::ConstantValue; use graphql_ir::Directive; @@ -19,12 +18,13 @@ use graphql_ir::FragmentDefinition; use graphql_ir::FragmentDefinitionName; use graphql_ir::OperationDefinition; use graphql_ir::Value; +use graphql_ir::build; use graphql_syntax::parse_executable; use intern::string_key::Intern; +use relay_codegen::JsModuleFormat; use relay_codegen::build_request_params; use relay_codegen::print_fragment; use relay_codegen::print_request; -use relay_codegen::JsModuleFormat; use relay_config::ProjectConfig; use relay_test_schema::TEST_SCHEMA; diff --git a/compiler/crates/relay-codegen/tests/required_directive_codegen.rs b/compiler/crates/relay-codegen/tests/required_directive_codegen.rs index e7e2b284cf250..dfcf66f321fca 100644 --- a/compiler/crates/relay-codegen/tests/required_directive_codegen.rs +++ b/compiler/crates/relay-codegen/tests/required_directive_codegen.rs @@ -9,13 +9,13 @@ use std::sync::Arc; use common::SourceLocationKey; use fixture_tests::Fixture; -use graphql_ir::build; use graphql_ir::Program; +use graphql_ir::build; use graphql_syntax::parse_executable; use graphql_test_helpers::diagnostics_to_sorted_string; +use relay_codegen::JsModuleFormat; use relay_codegen::print_fragment; use relay_codegen::print_operation; -use relay_codegen::JsModuleFormat; use relay_config::ProjectConfig; use relay_test_schema::get_test_schema; use relay_test_schema::get_test_schema_with_extensions; diff --git a/compiler/crates/relay-codegen/tests/skip_printing_nulls.rs b/compiler/crates/relay-codegen/tests/skip_printing_nulls.rs index eaff96dccdfb0..fe111b9102fdf 100644 --- a/compiler/crates/relay-codegen/tests/skip_printing_nulls.rs +++ b/compiler/crates/relay-codegen/tests/skip_printing_nulls.rs @@ -11,8 +11,8 @@ use common::FeatureFlag; use common::FeatureFlags; use common::SourceLocationKey; use fixture_tests::Fixture; -use graphql_ir::build; use graphql_ir::ExecutableDefinition; +use graphql_ir::build; use graphql_syntax::parse_executable; use relay_codegen::print_fragment; use relay_codegen::print_operation; diff --git a/compiler/crates/relay-codegen/tests/throw_on_field_error_directive_codegen.rs b/compiler/crates/relay-codegen/tests/throw_on_field_error_directive_codegen.rs index 3a0f39f3173ff..d6560fbd53564 100644 --- a/compiler/crates/relay-codegen/tests/throw_on_field_error_directive_codegen.rs +++ b/compiler/crates/relay-codegen/tests/throw_on_field_error_directive_codegen.rs @@ -11,8 +11,8 @@ use common::FeatureFlag; use common::FeatureFlags; use common::SourceLocationKey; use fixture_tests::Fixture; -use graphql_ir::build; use graphql_ir::ExecutableDefinition; +use graphql_ir::build; use graphql_syntax::parse_executable; use graphql_test_helpers::diagnostics_to_sorted_string; use relay_codegen::print_fragment; diff --git a/compiler/crates/relay-codemod/Cargo.toml b/compiler/crates/relay-codemod/Cargo.toml index 3f4372fb73a12..094e60c7a0f3b 100644 --- a/compiler/crates/relay-codemod/Cargo.toml +++ b/compiler/crates/relay-codemod/Cargo.toml @@ -4,14 +4,14 @@ name = "relay-codemod" version = "0.0.0" authors = ["Facebook"] -edition = "2021" +edition = "2024" repository = "https://github.com/facebook/relay" license = "MIT" [dependencies] -clap = { version = "4.5.20", features = ["derive", "env", "string", "unicode", "wrap_help"] } +clap = { version = "4.5.42", features = ["derive", "env", "string", "unicode", "wrap_help"] } common = { path = "../common" } -log = { version = "0.4.22", features = ["kv_unstable"] } +log = { version = "0.4.27", features = ["kv_unstable", "kv_unstable_std"] } lsp-types = "0.94.1" relay-compiler = { path = "../relay-compiler" } relay-lsp = { path = "../relay-lsp" } diff --git a/compiler/crates/relay-codemod/src/codemod.rs b/compiler/crates/relay-codemod/src/codemod.rs index e61f6f6e41810..73de6e4ff8056 100644 --- a/compiler/crates/relay-codemod/src/codemod.rs +++ b/compiler/crates/relay-codemod/src/codemod.rs @@ -12,14 +12,15 @@ use clap::Args; use clap::Subcommand; use common::FeatureFlag; use common::Rollout; +use common::RolloutRange; use log::info; use lsp_types::CodeActionOrCommand; use lsp_types::TextEdit; use lsp_types::Url; use relay_compiler::config::Config; +use relay_transforms::Programs; use relay_transforms::disallow_required_on_non_null_field; use relay_transforms::fragment_alias_directive; -use relay_transforms::Programs; #[derive(Subcommand, Debug, Clone)] pub enum AvailableCodemod { @@ -32,9 +33,11 @@ pub enum AvailableCodemod { #[derive(Args, Debug, Clone)] pub struct MarkDangerousConditionalFragmentSpreadsArgs { - /// If using a feature flag, specify the rollout percentage. If omitted, the codemod is fully enabled. - #[clap(long, short, value_parser=valid_percent)] - pub rollout_percentage: Option, + /// Specify a percentage of fragments to codemod. If a number is provided, + /// the first n percentage of fragments will be codemodded. If a range (`20-30`) is + /// provided, then fragments between the start and end of the range will be codemodded. + #[clap(long, short, value_parser=valid_percent, default_value = "100")] + pub rollout_percentage: FeatureFlag, } pub async fn run_codemod( @@ -46,19 +49,16 @@ pub async fn run_codemod( .iter() .flat_map(|programs| match &codemod { AvailableCodemod::MarkDangerousConditionalFragmentSpreads(opts) => { - match fragment_alias_directive( - &programs.source, - &FeatureFlag::Rollout { - rollout: Rollout(opts.rollout_percentage), - }, - ) { + match fragment_alias_directive(&programs.source, &opts.rollout_percentage) { Ok(_) => vec![], Err(e) => e, } } AvailableCodemod::RemoveUnnecessaryRequiredDirectives => { - disallow_required_on_non_null_field(&programs.source.schema, &programs.source) - .unwrap_or_default() + match disallow_required_on_non_null_field(&programs.reader) { + Ok(_) => vec![], + Err(e) => e, + } } }) .collect::>(); @@ -144,13 +144,33 @@ fn sort_changes(url: &Url, changes: &mut Vec) -> Result<(), std::io::E Ok(()) } -fn valid_percent(s: &str) -> Result { - // turn s into a u8 - let s = s.parse::().map_err(|_| "not a number".to_string())?; - // check if s is less than 100 - if (0..=100).contains(&s) { - Ok(s) +fn valid_percent(s: &str) -> Result { + // If the string is a range of the form "x-y", where x and y are numbers, return the range + let parts: Vec<&str> = s.split('-').collect(); + if parts.len() == 2 { + let start = parts[0].parse::().map_err(|_| { + "Expected the value on the left of the rollout range to be a number".to_string() + })?; + let end = parts[1].parse::().map_err(|_| { + "Expected the value on the right of the rollout range to be a number".to_string() + })?; + if (0..=100).contains(&start) && (0..=100).contains(&end) && start <= end { + Ok(FeatureFlag::RolloutRange { + rollout: RolloutRange { start, end }, + }) + } else { + Err("numbers must be between 0 and 100, inclusive, and the first number must be less than or equal to the second".to_string()) + } } else { - Err("number must be between 0 and 100, inclusive".to_string()) + // turn s into a u8 + let s = s.parse::().map_err(|_| "not a number".to_string())?; + // check if s is less than 100 + if (0..=100).contains(&s) { + Ok(FeatureFlag::Rollout { + rollout: Rollout(Some(s)), + }) + } else { + Err("number must be between 0 and 100, inclusive".to_string()) + } } } diff --git a/compiler/crates/relay-codemod/src/lib.rs b/compiler/crates/relay-codemod/src/lib.rs index db29f49cdb183..aa33ac5de6dbb 100644 --- a/compiler/crates/relay-codemod/src/lib.rs +++ b/compiler/crates/relay-codemod/src/lib.rs @@ -11,5 +11,5 @@ mod codemod; -pub use crate::codemod::run_codemod; pub use crate::codemod::AvailableCodemod; +pub use crate::codemod::run_codemod; diff --git a/compiler/crates/relay-compiler-playground/src/lib.rs b/compiler/crates/relay-compiler-playground/src/lib.rs index da3b3183bc747..b94c19b25ef41 100644 --- a/compiler/crates/relay-compiler-playground/src/lib.rs +++ b/compiler/crates/relay-compiler-playground/src/lib.rs @@ -21,12 +21,12 @@ use relay_codegen::print_operation; use relay_codegen::print_provided_variables; use relay_config::ProjectConfig; use relay_schema::build_schema_with_extensions; -use relay_transforms::apply_transforms; use relay_transforms::Programs; -use relay_typegen::generate_fragment_type_exports_section; -use relay_typegen::generate_operation_type_exports_section; +use relay_transforms::apply_transforms; use relay_typegen::FragmentLocations; use relay_typegen::TypegenConfig; +use relay_typegen::generate_fragment_type_exports_section; +use relay_typegen::generate_operation_type_exports_section; use schema::SDLSchema; use serde::Serialize; use wasm_bindgen::prelude::*; diff --git a/compiler/crates/relay-compiler/Cargo.toml b/compiler/crates/relay-compiler/Cargo.toml index 56640a27a6c1e..b98b16514cfea 100644 --- a/compiler/crates/relay-compiler/Cargo.toml +++ b/compiler/crates/relay-compiler/Cargo.toml @@ -4,7 +4,7 @@ name = "relay-compiler" version = "0.0.0" authors = ["Facebook"] -edition = "2021" +edition = "2024" repository = "https://github.com/facebook/relay" license = "MIT" @@ -25,7 +25,7 @@ name = "relay_compiler_relay_config_schema_json_test" path = "tests/relay_config_schema_json_test.rs" [dependencies] -async-trait = "0.1.71" +async-trait = "0.1.86" bincode = "1.3.3" common = { path = "../common" } common-path = "1.0.0" @@ -33,29 +33,29 @@ dashmap = { version = "5.5.3", features = ["rayon", "serde"] } dependency-analyzer = { path = "../dependency-analyzer" } docblock-shared = { path = "../docblock-shared" } docblock-syntax = { path = "../docblock-syntax" } -dunce = "1.0.2" +dunce = "1.0.5" errors = { path = "../errors" } extract-graphql = { path = "../extract-graphql" } fnv = "1.0" -futures = { version = "0.3.30", features = ["async-await", "compat"] } -glob = "0.3" +futures = { version = "0.3.31", features = ["async-await", "compat"] } +glob = "0.3.2" globset = { version = "0.4.13", features = ["serde1"] } graphql-cli = { path = "../graphql-cli" } graphql-ir = { path = "../graphql-ir" } graphql-syntax = { path = "../graphql-syntax" } graphql-text-printer = { path = "../graphql-text-printer" } graphql-watchman = { path = "../graphql-watchman" } -hex = "0.4.3" -indexmap = { version = "2.2.6", features = ["arbitrary", "rayon", "serde"] } +hex = { version = "0.4.3", features = ["alloc"] } +indexmap = { version = "2.9.0", features = ["arbitrary", "rayon", "serde"] } intern = { path = "../intern" } js-config-loader = { path = "../js-config-loader" } -lazy_static = "1.4" -log = { version = "0.4.22", features = ["kv_unstable"] } +lazy_static = "1.5" +log = { version = "0.4.27", features = ["kv_unstable", "kv_unstable_std"] } md-5 = "0.10" persist-query = { path = "../persist-query" } petgraph = { version = "0.6.3", features = ["serde-1"] } rayon = "1.9.0" -regex = "1.9.2" +regex = "1.11.1" relay-codegen = { path = "../relay-codegen" } relay-config = { path = "../relay-config" } relay-docblock = { path = "../relay-docblock" } @@ -63,25 +63,25 @@ relay-saved-state-loader = { path = "../relay-saved-state-loader" } relay-schema = { path = "../relay-schema" } relay-transforms = { path = "../relay-transforms" } relay-typegen = { path = "../relay-typegen" } -rustc-hash = "1.1.0" +rustc-hash = "2.1.1" schema = { path = "../schema" } schema-diff = { path = "../schema-diff" } schema-validate-lib = { path = "../schema-validate" } -schemars = { version = "0.8.21", features = ["indexmap2"] } -serde = { version = "1.0.185", features = ["derive", "rc"] } -serde_bser = "0.3" -serde_json = { version = "1.0.132", features = ["float_roundtrip", "unbounded_depth"] } +schemars = { version = "1.0.4", features = ["indexmap2"] } +serde = { version = "1.0.219", features = ["derive", "rc"] } +serde_bser = "0.4" +serde_json = { version = "1.0.140", features = ["alloc", "float_roundtrip", "unbounded_depth"] } sha1 = "0.10.5" sha2 = "0.10.6" signedsource = { path = "../signedsource" } -thiserror = "1.0.64" -tokio = { version = "1.41.0", features = ["full", "test-util", "tracing"] } +thiserror = "2.0.12" +tokio = { version = "1.46.1", features = ["full", "test-util", "tracing"] } walkdir = "2.3" -watchman_client = "0.8.0" +watchman_client = "0.9.0" zstd = { version = "0.13", features = ["experimental", "zstdmt"] } [dev-dependencies] fixture-tests = { path = "../fixture-tests" } -futures-util = "0.3.30" +futures-util = { version = "0.3.30", features = ["compat"] } graphql-test-helpers = { path = "../graphql-test-helpers" } relay-test-schema = { path = "../relay-test-schema" } diff --git a/compiler/crates/relay-compiler/relay-compiler-config-schema.json b/compiler/crates/relay-compiler/relay-compiler-config-schema.json index af988893ee01b..281ff54c92884 100644 --- a/compiler/crates/relay-compiler/relay-compiler-config-schema.json +++ b/compiler/crates/relay-compiler/relay-compiler-config-schema.json @@ -1,2025 +1,846 @@ { - "$schema": "http://json-schema.org/draft-07/schema#", + "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "ConfigFile", + "description": "Relay's configuration file. Supports a single project config for simple use\ncases and a multi-project config for cases where multiple projects live in\nthe same repository.\n\nIn general, start with the SingleProjectConfigFile.", "anyOf": [ { - "description": "Base case configuration (mostly of OSS) where the project have single schema, and single source directory", + "description": "Base case configuration (mostly of OSS) where the project\nhave single schema, and single source directory", + "$ref": "#/$defs/SingleProjectConfigFile" + }, + { + "description": "Relay can support multiple projects with multiple schemas\nand different options (output, typegen, etc...).\nThis MultiProjectConfigFile is responsible for configuring\nthese type of projects (complex)", + "$ref": "#/$defs/MultiProjectConfigFile" + } + ], + "$defs": { + "ArgumentName": { + "$ref": "#/$defs/StringKey" + }, + "ConfigFileProject": { "type": "object", - "required": [ - "language" - ], "properties": { - "$schema": { - "description": "The user may hard-code the JSON Schema for their version of the config.", - "default": null, - "type": [ - "string", - "null" - ] - }, - "artifactDirectory": { - "description": "A specific directory to output all artifacts to. When enabling this the babel plugin needs `artifactDirectory` set as well.", - "default": null, - "type": [ - "string", - "null" - ] + "base": { + "description": "If a base project is set, the documents of that project can be\nreferenced, but won't produce output artifacts.\nExtensions from the base project will be added as well and the schema\nof the base project should be a subset of the schema of this project.", + "anyOf": [ + { + "$ref": "#/$defs/ProjectName" + }, + { + "type": "null" + } + ], + "default": null }, "codegenCommand": { - "description": "Name of the command that runs the relay compiler", - "default": null, + "description": "Name of the command that runs the relay compiler. This will be added at\nthe top of generated code to let readers know how to regenerate the file.", "type": [ "string", "null" - ] + ], + "default": null }, "customErrorType": { - "description": "A map from GraphQL error name to import path, example: {\"name:: \"MyErrorName\", \"path\": \"../src/MyError\"}", - "type": [ - "object", - "null" - ], - "required": [ - "name", - "path" - ], - "properties": { - "name": { - "type": "string" + "description": "A map from GraphQL error name to import path, example:\n{\"name:: \"MyErrorName\", \"path\": \"../src/MyError\"}", + "anyOf": [ + { + "$ref": "#/$defs/CustomTypeImport" }, - "path": { - "type": "string" + { + "type": "null" } - } + ] }, "customScalarTypes": { - "description": "A map from GraphQL scalar types to a custom JS type, example: { \"Url\": \"String\" } { \"Url\": {\"name:: \"MyURL\", \"path\": \"../src/MyUrlTypes\"} }", - "default": {}, + "description": "A map from GraphQL scalar types to a custom JS type, example:\n{ \"Url\": \"String\" }\n{ \"Url\": {\"name:: \"MyURL\", \"path\": \"../src/MyUrlTypes\"} }", "type": "object", "additionalProperties": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "object", - "required": [ - "name", - "path" - ], - "properties": { - "name": { - "type": "string" - }, - "path": { - "type": "string" - } - } - } - ] + "$ref": "#/$defs/CustomType" + }, + "default": {} + }, + "diagnosticReportConfig": { + "description": "Threshold for diagnostics to be critical to the compiler's execution.\nAll diagnostic with severities at and below this level will cause the\ncompiler to fatally exit.", + "$ref": "#/$defs/DiagnosticReportConfig", + "default": { + "criticalLevel": "error" } }, "eagerEsModules": { - "description": "This option enables emitting es modules artifacts.", - "default": false, - "type": "boolean" + "description": "This option enables opting out of emitting es modules artifacts. When\nset to false, Relay will emit CommonJS modules.", + "type": "boolean", + "default": true }, "enumModuleSuffix": { "title": "For Flow type generation", - "description": "When set, enum values are imported from a module with this suffix. For example, an enum Foo and this property set to \".test\" would be imported from \"Foo.test\". Note: an empty string is allowed and different from not setting the value, in the example above it would just import from \"Foo\".", + "description": "When set, enum values are imported from a module with this suffix.\nFor example, an enum Foo and this property set to \".test\" would be\nimported from \"Foo.test\".\nNote: an empty string is allowed and different from not setting the\nvalue, in the example above it would just import from \"Foo\".", "type": [ "string", "null" ] }, - "excludes": { - "description": "Directories to ignore under src default: ['**/node_modules/**', '**/__mocks__/**', '**/__generated__/**'],", - "default": [ - "**/node_modules/**", - "**/__mocks__/**", - "**/__generated__/**" + "excludesExtensions": { + "description": "Some projects may need to exclude files with certain extensions.", + "type": [ + "array", + "null" ], - "type": "array", "items": { "type": "string" } }, + "extra": { + "description": "A placeholder for allowing extra information in the config file", + "default": null + }, + "extraArtifactsOutput": { + "description": "Some projects may need to generate extra artifacts. For those, we may\nneed to provide an additional directory to put them.\nBy default the will use `output` *if available", + "type": [ + "string", + "null" + ] + }, "featureFlags": { - "default": null, + "description": "Enable and disable experimental or legacy behaviors.\nWARNING! These are not stable and may change at any time.", + "anyOf": [ + { + "$ref": "#/$defs/FeatureFlags" + }, + { + "type": "null" + } + ], + "default": null + }, + "jsModuleFormat": { + "description": "Import/export style to use in generated JavaScript modules.", + "$ref": "#/$defs/JsModuleFormat", + "default": "commonjs" + }, + "language": { + "description": "The desired output language, \"flow\" or \"typescript\".", + "$ref": "#/$defs/TypegenLanguage" + }, + "moduleImportConfig": { + "description": "Configuration for the @module GraphQL directive.", + "$ref": "#/$defs/ModuleImportConfig", + "default": { + "dynamicModuleProvider": null, + "operationModuleProvider": null, + "surface": null + } + }, + "noFutureProofEnums": { + "description": "This option controls whether or not a catch-all entry is added to enum type definitions\nfor values that may be added in the future. Enabling this means you will have to update\nyour application whenever the GraphQL server schema adds new enum values to prevent it\nfrom breaking.", + "type": "boolean", + "default": false + }, + "optionalInputFields": { + "title": "For Flow type generation", + "description": "When set, generated input types will have the listed fields optional\neven if the schema defines them as required.", + "type": "array", + "default": [], + "items": { + "$ref": "#/$defs/StringKey" + } + }, + "output": { + "description": "A project without an output directory will put the generated files in\na __generated__ directory next to the input file.\nAll files in these directories should be generated by the Relay\ncompiler, so that the compiler can cleanup extra files.", "type": [ - "object", + "string", "null" ], - "properties": { - "actor_change_support": { - "default": { - "kind": "disabled" - }, - "oneOf": [ - { - "description": "Fully disabled: developers may not use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "disabled" - ] - } - } - }, - { - "description": "Fully enabled: developers may use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "enabled" - ] - } - } - }, - { - "description": "Partially enabled: developers may only use this feature on the listed items (fragments, fields, types).", - "type": "object", - "required": [ - "allowlist", - "kind" - ], - "properties": { - "allowlist": { - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "kind": { - "type": "string", - "enum": [ - "limited" - ] - } - } - }, - { - "description": "Partially enabled: used for gradual rollout of the feature", - "type": "object", - "required": [ - "kind", - "rollout" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "rollout" - ] - }, - "rollout": { - "description": "A utility to enable gradual rollout of large codegen changes. Can be constructed as the Default which passes or a percentage between 0 and 100.", - "type": [ - "integer", - "null" - ], - "format": "uint8", - "minimum": 0.0 - } - } - } - ] - }, - "allow_required_in_mutation_response": { - "description": "@required with an action of THROW is read-time feature that is not compatible with our mutation APIs. We are in the process of removing any existing examples, but this flag is part of a process of removing any existing examples.", - "default": { - "kind": "disabled" - }, - "oneOf": [ - { - "description": "Fully disabled: developers may not use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "disabled" - ] - } - } - }, - { - "description": "Fully enabled: developers may use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "enabled" - ] - } - } - }, - { - "description": "Partially enabled: developers may only use this feature on the listed items (fragments, fields, types).", - "type": "object", - "required": [ - "allowlist", - "kind" - ], - "properties": { - "allowlist": { - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "kind": { - "type": "string", - "enum": [ - "limited" - ] - } - } - }, - { - "description": "Partially enabled: used for gradual rollout of the feature", - "type": "object", - "required": [ - "kind", - "rollout" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "rollout" - ] - }, - "rollout": { - "description": "A utility to enable gradual rollout of large codegen changes. Can be constructed as the Default which passes or a percentage between 0 and 100.", - "type": [ - "integer", - "null" - ], - "format": "uint8", - "minimum": 0.0 - } - } - } - ] + "default": null + }, + "persist": { + "description": "If this option is set, the compiler will persist queries using this\nconfig.", + "anyOf": [ + { + "$ref": "#/$defs/PersistConfig" }, - "allow_resolver_non_nullable_return_type": { - "description": "Allow non-nullable return types from resolvers.", - "default": { - "kind": "disabled" - }, - "oneOf": [ - { - "description": "Fully disabled: developers may not use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "disabled" - ] - } - } - }, - { - "description": "Fully enabled: developers may use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "enabled" - ] - } - } - }, - { - "description": "Partially enabled: developers may only use this feature on the listed items (fragments, fields, types).", - "type": "object", - "required": [ - "allowlist", - "kind" - ], - "properties": { - "allowlist": { - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "kind": { - "type": "string", - "enum": [ - "limited" - ] - } - } - }, - { - "description": "Partially enabled: used for gradual rollout of the feature", - "type": "object", - "required": [ - "kind", - "rollout" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "rollout" - ] - }, - "rollout": { - "description": "A utility to enable gradual rollout of large codegen changes. Can be constructed as the Default which passes or a percentage between 0 and 100.", - "type": [ - "integer", - "null" - ], - "format": "uint8", - "minimum": 0.0 - } - } - } - ] + { + "type": "null" + } + ] + }, + "relativizeJsModulePaths": { + "description": "Whether to treat all JS module names as relative to './' (true) or not.\ndefault: true", + "type": "boolean", + "default": true + }, + "requireCustomScalarTypes": { + "description": "Require all GraphQL scalar types mapping to be defined, will throw\nif a GraphQL scalar type doesn't have a JS type", + "type": "boolean", + "default": false + }, + "resolverContextType": { + "description": "Indicates the type to import and use as the context for Relay Resolvers.", + "anyOf": [ + { + "$ref": "#/$defs/ResolverContextTypeInput" }, - "allow_resolvers_in_mutation_response": { - "description": "Relay Resolvers are a read-time feature that are not actually handled in our mutation APIs. We are in the process of removing any existing examples, but this flag is part of a process of removing any existing examples.", - "default": { - "kind": "disabled" - }, - "oneOf": [ - { - "description": "Fully disabled: developers may not use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "disabled" - ] - } - } - }, - { - "description": "Fully enabled: developers may use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "enabled" - ] - } - } - }, - { - "description": "Partially enabled: developers may only use this feature on the listed items (fragments, fields, types).", - "type": "object", - "required": [ - "allowlist", - "kind" - ], - "properties": { - "allowlist": { - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "kind": { - "type": "string", - "enum": [ - "limited" - ] - } - } - }, - { - "description": "Partially enabled: used for gradual rollout of the feature", - "type": "object", - "required": [ - "kind", - "rollout" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "rollout" - ] - }, - "rollout": { - "description": "A utility to enable gradual rollout of large codegen changes. Can be constructed as the Default which passes or a percentage between 0 and 100.", - "type": [ - "integer", - "null" - ], - "format": "uint8", - "minimum": 0.0 - } - } - } - ] + { + "type": "null" + } + ], + "default": null + }, + "resolversSchemaModule": { + "anyOf": [ + { + "$ref": "#/$defs/ResolversSchemaModuleConfig" }, - "compact_query_text": { - "description": "Print queries in compact form", - "default": { - "kind": "disabled" - }, - "oneOf": [ - { - "description": "Fully disabled: developers may not use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "disabled" - ] - } - } - }, - { - "description": "Fully enabled: developers may use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "enabled" - ] - } - } - }, - { - "description": "Partially enabled: developers may only use this feature on the listed items (fragments, fields, types).", - "type": "object", - "required": [ - "allowlist", - "kind" - ], - "properties": { - "allowlist": { - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "kind": { - "type": "string", - "enum": [ - "limited" - ] - } - } - }, - { - "description": "Partially enabled: used for gradual rollout of the feature", - "type": "object", - "required": [ - "kind", - "rollout" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "rollout" - ] - }, - "rollout": { - "description": "A utility to enable gradual rollout of large codegen changes. Can be constructed as the Default which passes or a percentage between 0 and 100.", - "type": [ - "integer", - "null" - ], - "format": "uint8", - "minimum": 0.0 - } - } - } - ] + { + "type": "null" + } + ], + "default": null + }, + "rollout": { + "description": "A generic rollout state for larger codegen changes. The default is to\npass, otherwise it should be a number between 0 and 100 as a percentage.", + "$ref": "#/$defs/Rollout", + "default": null + }, + "schema": { + "description": "Path to the schema.graphql or a directory containing a schema broken up\nin multiple *.graphql files.\nExactly 1 of these options needs to be defined.", + "type": [ + "string", + "null" + ] + }, + "schemaConfig": { + "description": "Extra configuration for the GraphQL schema itself.", + "$ref": "#/$defs/SchemaConfig", + "default": { + "connectionInterface": { + "cursor": "cursor", + "edges": "edges", + "endCursor": "endCursor", + "hasNextPage": "hasNextPage", + "hasPreviousPage": "hasPreviousPage", + "node": "node", + "pageInfo": "pageInfo", + "startCursor": "startCursor" }, - "disable_deduping_common_structures_in_artifacts": { - "description": "Skip the optimization which extracts common JavaScript structures in generated artifacts into numbered variables and uses them by reference in each position in which they occur.\n\nThis optimization can make it hard to follow changes to generated code, so being able to disable it can be helpful for debugging.\n\nTo disable deduping for just one fragment or operation's generated artifacts:\n\n```json \"disable_deduping_common_structures_in_artifacts\": { { \"kind\": \"limited\", \"allowList\": [\"\"] } } ```", - "default": { - "kind": "disabled" - }, - "oneOf": [ - { - "description": "Fully disabled: developers may not use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "disabled" - ] - } - } - }, - { - "description": "Fully enabled: developers may use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "enabled" - ] - } - } - }, - { - "description": "Partially enabled: developers may only use this feature on the listed items (fragments, fields, types).", - "type": "object", - "required": [ - "allowlist", - "kind" - ], - "properties": { - "allowlist": { - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "kind": { - "type": "string", - "enum": [ - "limited" - ] - } - } - }, - { - "description": "Partially enabled: used for gradual rollout of the feature", - "type": "object", - "required": [ - "kind", - "rollout" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "rollout" - ] - }, - "rollout": { - "description": "A utility to enable gradual rollout of large codegen changes. Can be constructed as the Default which passes or a percentage between 0 and 100.", - "type": [ - "integer", - "null" - ], - "format": "uint8", - "minimum": 0.0 - } - } - } - ] - }, - "disable_edge_type_name_validation_on_declerative_connection_directives": { - "description": "Disable validation of the `edgeTypeName` argument on `@prependNode` and `@appendNode`.", - "default": { - "kind": "disabled" - }, - "oneOf": [ - { - "description": "Fully disabled: developers may not use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "disabled" - ] - } - } - }, - { - "description": "Fully enabled: developers may use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "enabled" - ] - } - } - }, - { - "description": "Partially enabled: developers may only use this feature on the listed items (fragments, fields, types).", - "type": "object", - "required": [ - "allowlist", - "kind" - ], - "properties": { - "allowlist": { - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "kind": { - "type": "string", - "enum": [ - "limited" - ] - } - } - }, - { - "description": "Partially enabled: used for gradual rollout of the feature", - "type": "object", - "required": [ - "kind", - "rollout" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "rollout" - ] - }, - "rollout": { - "description": "A utility to enable gradual rollout of large codegen changes. Can be constructed as the Default which passes or a percentage between 0 and 100.", - "type": [ - "integer", - "null" - ], - "format": "uint8", - "minimum": 0.0 - } - } - } - ] - }, - "disable_full_argument_type_validation": { - "description": "Disable full GraphQL argument type validation. Historically, we only applied argument type validation to the query that was actually going to be persisted and sent to the server. This meant that we didn't typecheck arguments passed to Relay Resolvers or Client Schema Extensions.\n\nWe also permitted an escape hatch of `uncheckedArguments_DEPRECATED` for defining fragment arguments which were not typechecked.\n\nWe no-longer support `uncheckedArguments_DEPRECATED`, and we typecheck both client and server arguments. This flag allows you to opt out of this new behavior to enable gradual adoption of the new validations.\n\nThis flag will be removed in a future version of Relay.", - "default": { - "kind": "disabled" - }, - "oneOf": [ - { - "description": "Fully disabled: developers may not use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "disabled" - ] - } - } - }, - { - "description": "Fully enabled: developers may use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "enabled" - ] - } - } - }, - { - "description": "Partially enabled: developers may only use this feature on the listed items (fragments, fields, types).", - "type": "object", - "required": [ - "allowlist", - "kind" - ], - "properties": { - "allowlist": { - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "kind": { - "type": "string", - "enum": [ - "limited" - ] - } - } - }, - { - "description": "Partially enabled: used for gradual rollout of the feature", - "type": "object", - "required": [ - "kind", - "rollout" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "rollout" - ] - }, - "rollout": { - "description": "A utility to enable gradual rollout of large codegen changes. Can be constructed as the Default which passes or a percentage between 0 and 100.", - "type": [ - "integer", - "null" - ], - "format": "uint8", - "minimum": 0.0 - } - } - } - ] - }, - "disable_resolver_reader_ast": { - "description": "Mirror of `enable_resolver_normalization_ast` excludes resolver metadata from reader ast", - "default": false, - "type": "boolean" - }, - "disable_schema_validation": { - "description": "Disable validating the composite schema (server, client schema extensions, Relay Resolvers) after its built.", - "default": false, - "type": "boolean" - }, - "enable_3d_branch_arg_generation": { - "default": false, - "type": "boolean" - }, - "enable_exec_time_resolvers_directive": { - "description": "Allow per-query opt in to normalization AST for Resolvers with exec_time_resolvers directive. In contrast to enable_resolver_normalization_ast, if this is true, a normalization AST can be generated for a query using the @exec_time_resolvers directive", - "default": false, - "type": "boolean" - }, - "enable_fragment_argument_transform": { - "description": "Add support for parsing and transforming variable definitions on fragment definitions and arguments on fragment spreads.", - "default": false, - "type": "boolean" - }, - "enable_relay_resolver_mutations": { - "description": "Allow relay resolvers to extend the Mutation type", - "default": false, - "type": "boolean" - }, - "enable_resolver_normalization_ast": { - "description": "Fully build the normalization AST for Resolvers", - "default": false, - "type": "boolean" - }, - "enable_strict_custom_scalars": { - "description": "Perform strict validations when custom scalar types are used", - "default": false, - "type": "boolean" - }, - "enforce_fragment_alias_where_ambiguous": { - "description": "Enforce that you must add `@alias` to a fragment if it may not match, due to type mismatch or `@skip`/`@include`", - "default": { - "kind": "disabled" - }, - "oneOf": [ - { - "description": "Fully disabled: developers may not use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "disabled" - ] - } - } - }, - { - "description": "Fully enabled: developers may use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "enabled" - ] - } - } - }, - { - "description": "Partially enabled: developers may only use this feature on the listed items (fragments, fields, types).", - "type": "object", - "required": [ - "allowlist", - "kind" - ], - "properties": { - "allowlist": { - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "kind": { - "type": "string", - "enum": [ - "limited" - ] - } - } - }, - { - "description": "Partially enabled: used for gradual rollout of the feature", - "type": "object", - "required": [ - "kind", - "rollout" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "rollout" - ] - }, - "rollout": { - "description": "A utility to enable gradual rollout of large codegen changes. Can be constructed as the Default which passes or a percentage between 0 and 100.", - "type": [ - "integer", - "null" - ], - "format": "uint8", - "minimum": 0.0 - } - } - } - ] - }, - "legacy_include_path_in_required_reader_nodes": { - "description": "The `path` field in `@required` Reader AST nodes is no longer used. But removing them in one diff is too large of a change to ship at once.\n\nThis flag will allow us to use the rollout FeatureFlag to remove them across a number of diffs.", - "default": { - "kind": "disabled" - }, - "oneOf": [ - { - "description": "Fully disabled: developers may not use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "disabled" - ] - } - } - }, - { - "description": "Fully enabled: developers may use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "enabled" - ] - } - } - }, - { - "description": "Partially enabled: developers may only use this feature on the listed items (fragments, fields, types).", - "type": "object", - "required": [ - "allowlist", - "kind" - ], - "properties": { - "allowlist": { - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "kind": { - "type": "string", - "enum": [ - "limited" - ] - } - } - }, - { - "description": "Partially enabled: used for gradual rollout of the feature", - "type": "object", - "required": [ - "kind", - "rollout" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "rollout" - ] - }, - "rollout": { - "description": "A utility to enable gradual rollout of large codegen changes. Can be constructed as the Default which passes or a percentage between 0 and 100.", - "type": [ - "integer", - "null" - ], - "format": "uint8", - "minimum": 0.0 - } - } - } - ] - }, - "no_inline": { - "description": "For now, this also disallows fragments with variable definitions This also makes @module to opt in using @no_inline internally NOTE that the presence of a fragment in this list only controls whether a fragment is *allowed* to use @no_inline: whether the fragment is inlined or not depends on whether it actually uses that directive.", - "default": { - "kind": "disabled" - }, - "oneOf": [ - { - "description": "Fully disabled: developers may not use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "disabled" - ] - } - } - }, - { - "description": "Fully enabled: developers may use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "enabled" - ] - } - } - }, - { - "description": "Partially enabled: developers may only use this feature on the listed items (fragments, fields, types).", - "type": "object", - "required": [ - "allowlist", - "kind" - ], - "properties": { - "allowlist": { - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "kind": { - "type": "string", - "enum": [ - "limited" - ] - } - } - }, - { - "description": "Partially enabled: used for gradual rollout of the feature", - "type": "object", - "required": [ - "kind", - "rollout" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "rollout" - ] - }, - "rollout": { - "description": "A utility to enable gradual rollout of large codegen changes. Can be constructed as the Default which passes or a percentage between 0 and 100.", - "type": [ - "integer", - "null" - ], - "format": "uint8", - "minimum": 0.0 - } - } - } - ] - }, - "omit_resolver_type_assertions_for_confirmed_types": { - "description": "Skip generating resolver type assertions for resolvers which have been derived from TS/Flow types.", - "default": { - "kind": "disabled" - }, - "oneOf": [ - { - "description": "Fully disabled: developers may not use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "disabled" - ] - } - } - }, - { - "description": "Fully enabled: developers may use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "enabled" - ] - } - } - }, - { - "description": "Partially enabled: developers may only use this feature on the listed items (fragments, fields, types).", - "type": "object", - "required": [ - "allowlist", - "kind" - ], - "properties": { - "allowlist": { - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "kind": { - "type": "string", - "enum": [ - "limited" - ] - } - } - }, - { - "description": "Partially enabled: used for gradual rollout of the feature", - "type": "object", - "required": [ - "kind", - "rollout" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "rollout" - ] - }, - "rollout": { - "description": "A utility to enable gradual rollout of large codegen changes. Can be constructed as the Default which passes or a percentage between 0 and 100.", - "type": [ - "integer", - "null" - ], - "format": "uint8", - "minimum": 0.0 - } - } - } - ] + "deferStreamInterface": { + "deferName": "defer", + "ifArg": "if", + "initialCountArg": "initialCount", + "labelArg": "label", + "streamName": "stream", + "useCustomizedBatchArg": "useCustomizedBatch" }, - "prefer_fetchable_in_refetch_queries": { - "description": "Feature flag to prefer `fetch_MyType()` generatior over `node()` query generator in @refetchable transform", - "default": false, - "type": "boolean" + "enableTokenField": false, + "nodeInterfaceIdField": "id", + "nodeInterfaceIdVariableName": "id", + "nonNodeIdFields": null, + "unselectableDirectiveName": "unselectable" + } + }, + "schemaDir": { + "type": [ + "string", + "null" + ] + }, + "schemaExtensions": { + "description": "Directory containing *.graphql files with schema extensions.", + "type": "array", + "default": [], + "items": { + "type": "string" + } + }, + "schemaName": { + "description": "Schema name, if differs from project name.\nIf schema name is unset, the project name will be used as schema name.", + "anyOf": [ + { + "$ref": "#/$defs/StringKey" }, - "relay_resolver_enable_interface_output_type": { - "default": { - "kind": "disabled" + { + "type": "null" + } + ], + "default": null + }, + "shardOutput": { + "description": "If `output` is provided and `shard_output` is `true`, shard the files\nby putting them under `{output_dir}/{source_relative_path}`", + "type": "boolean", + "default": false + }, + "shardStripRegex": { + "description": "Regex to match and strip parts of the `source_relative_path`", + "type": [ + "string", + "null" + ], + "default": null + }, + "testPathRegex": { + "description": "Optional regex to restrict @relay_test_operation to directories matching\nthis regex. Defaults to no limitations.", + "type": [ + "string", + "null" + ], + "default": null + }, + "typescriptExcludeUndefinedFromNullableUnion": { + "description": "Keep the previous compiler behavior by outputting an union\nof the raw type and null, and not the **correct** behavior\nof an union with the raw type, null and undefined.", + "type": "boolean", + "default": false + }, + "useImportTypeSyntax": { + "title": "For Typescript type generation", + "description": "Whether to use the `import type` syntax introduced in Typescript\nversion 3.8. This will prevent warnings from `importsNotUsedAsValues`.", + "type": "boolean", + "default": false + }, + "variableNamesComment": { + "description": "Generates a `// @relayVariables name1 name2` header in generated operation files", + "type": "boolean", + "default": false + } + }, + "additionalProperties": false, + "required": [ + "language" + ] + }, + "ConnectionInterface": { + "description": "Configuration where Relay should expect some fields in the schema.", + "type": "object", + "properties": { + "cursor": { + "$ref": "#/$defs/StringKey" + }, + "edges": { + "$ref": "#/$defs/StringKey" + }, + "endCursor": { + "$ref": "#/$defs/StringKey" + }, + "hasNextPage": { + "$ref": "#/$defs/StringKey" + }, + "hasPreviousPage": { + "$ref": "#/$defs/StringKey" + }, + "node": { + "$ref": "#/$defs/StringKey" + }, + "pageInfo": { + "$ref": "#/$defs/StringKey" + }, + "startCursor": { + "$ref": "#/$defs/StringKey" + } + }, + "additionalProperties": false, + "required": [ + "cursor", + "edges", + "endCursor", + "hasNextPage", + "hasPreviousPage", + "node", + "pageInfo", + "startCursor" + ] + }, + "CustomType": { + "description": "Defines a custom GraphQL\ndescrbing a custom scalar.", + "anyOf": [ + { + "description": "A string representing the name of a custom type. e.g. \"string\" or \"number\"", + "$ref": "#/$defs/StringKey" + }, + { + "description": "A module which defines the custom type. e.g. { \"name\": \"MyCustomType\", \"path\": \"./Types.ts\" }", + "$ref": "#/$defs/CustomTypeImport" + } + ] + }, + "CustomTypeImport": { + "description": "Defines a module path and export name of the Flow or TypeScript type\ndescrbing a GraphQL custom scalar.", + "type": "object", + "properties": { + "name": { + "description": "The name under which the type is exported from the module", + "$ref": "#/$defs/StringKey" + }, + "path": { + "description": "The path to the module relative to the project root", + "type": "string" + } + }, + "required": [ + "name", + "path" + ] + }, + "DeferStreamInterface": { + "description": "Configuration where Relay should expect some fields in the schema.", + "type": "object", + "properties": { + "deferName": { + "$ref": "#/$defs/DirectiveName" + }, + "ifArg": { + "$ref": "#/$defs/ArgumentName" + }, + "initialCountArg": { + "$ref": "#/$defs/ArgumentName" + }, + "labelArg": { + "$ref": "#/$defs/ArgumentName" + }, + "streamName": { + "$ref": "#/$defs/DirectiveName" + }, + "useCustomizedBatchArg": { + "$ref": "#/$defs/ArgumentName" + } + }, + "additionalProperties": false, + "required": [ + "deferName", + "streamName", + "ifArg", + "labelArg", + "initialCountArg", + "useCustomizedBatchArg" + ] + }, + "DeserializableProjectSet": { + "anyOf": [ + { + "$ref": "#/$defs/ProjectName" + }, + { + "type": "array", + "items": { + "$ref": "#/$defs/ProjectName" + } + } + ] + }, + "DiagnosticLevel": { + "description": "Levels for reporting errors in the compiler.", + "oneOf": [ + { + "description": "Report only errors", + "type": "string", + "const": "error" + }, + { + "description": "Report diagnostics up to warnings", + "type": "string", + "const": "warning" + }, + { + "description": "Report diagnostics up to informational diagnostics", + "type": "string", + "const": "info" + }, + { + "description": "Report diagnostics up to hints", + "type": "string", + "const": "hint" + } + ] + }, + "DiagnosticReportConfig": { + "description": "Configuration for all diagnostic reporting in the compiler", + "type": "object", + "properties": { + "criticalLevel": { + "description": "Threshold for diagnostics to be critical to the compiler's execution.\nAll diagnostic with severities at and below this level will cause the\ncompiler to fatally exit.", + "$ref": "#/$defs/DiagnosticLevel" + } + }, + "required": [ + "criticalLevel" + ] + }, + "DirectiveName": { + "description": "Wrapper struct for clarity rather than having StringKey everywhere.", + "$ref": "#/$defs/StringKey" + }, + "FeatureFlag": { + "oneOf": [ + { + "description": "Fully disabled: developers may not use this feature", + "type": "object", + "properties": { + "kind": { + "type": "string", + "const": "disabled" + } + }, + "required": [ + "kind" + ] + }, + { + "description": "Fully enabled: developers may use this feature", + "type": "object", + "properties": { + "kind": { + "type": "string", + "const": "enabled" + } + }, + "required": [ + "kind" + ] + }, + { + "description": "Partially enabled: developers may only use this feature on the listed items (fragments, fields, types).", + "type": "object", + "properties": { + "allowlist": { + "type": "array", + "items": { + "$ref": "#/$defs/StringKey" }, - "oneOf": [ - { - "description": "Fully disabled: developers may not use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "disabled" - ] - } - } - }, - { - "description": "Fully enabled: developers may use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "enabled" - ] - } - } - }, - { - "description": "Partially enabled: developers may only use this feature on the listed items (fragments, fields, types).", - "type": "object", - "required": [ - "allowlist", - "kind" - ], - "properties": { - "allowlist": { - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "kind": { - "type": "string", - "enum": [ - "limited" - ] - } - } - }, - { - "description": "Partially enabled: used for gradual rollout of the feature", - "type": "object", - "required": [ - "kind", - "rollout" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "rollout" - ] - }, - "rollout": { - "description": "A utility to enable gradual rollout of large codegen changes. Can be constructed as the Default which passes or a percentage between 0 and 100.", - "type": [ - "integer", - "null" - ], - "format": "uint8", - "minimum": 0.0 - } - } - } - ] + "uniqueItems": true }, - "skip_printing_nulls": { - "default": { - "kind": "disabled" - }, - "oneOf": [ - { - "description": "Fully disabled: developers may not use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "disabled" - ] - } - } - }, - { - "description": "Fully enabled: developers may use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "enabled" - ] - } - } - }, - { - "description": "Partially enabled: developers may only use this feature on the listed items (fragments, fields, types).", - "type": "object", - "required": [ - "allowlist", - "kind" - ], - "properties": { - "allowlist": { - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "kind": { - "type": "string", - "enum": [ - "limited" - ] - } - } - }, - { - "description": "Partially enabled: used for gradual rollout of the feature", - "type": "object", - "required": [ - "kind", - "rollout" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "rollout" - ] - }, - "rollout": { - "description": "A utility to enable gradual rollout of large codegen changes. Can be constructed as the Default which passes or a percentage between 0 and 100.", - "type": [ - "integer", - "null" - ], - "format": "uint8", - "minimum": 0.0 - } - } - } - ] + "kind": { + "type": "string", + "const": "limited" + } + }, + "required": [ + "kind", + "allowlist" + ] + }, + { + "description": "Partially enabled: used for gradual rollout of the feature", + "type": "object", + "properties": { + "kind": { + "type": "string", + "const": "rollout" }, - "text_artifacts": { - "description": "Enable generation of text artifacts used to generate full query strings later.", - "default": { - "kind": "disabled" - }, - "oneOf": [ - { - "description": "Fully disabled: developers may not use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "disabled" - ] - } - } - }, - { - "description": "Fully enabled: developers may use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "enabled" - ] - } - } - }, - { - "description": "Partially enabled: developers may only use this feature on the listed items (fragments, fields, types).", - "type": "object", - "required": [ - "allowlist", - "kind" - ], - "properties": { - "allowlist": { - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "kind": { - "type": "string", - "enum": [ - "limited" - ] - } - } - }, - { - "description": "Partially enabled: used for gradual rollout of the feature", - "type": "object", - "required": [ - "kind", - "rollout" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "rollout" - ] - }, - "rollout": { - "description": "A utility to enable gradual rollout of large codegen changes. Can be constructed as the Default which passes or a percentage between 0 and 100.", - "type": [ - "integer", - "null" - ], - "format": "uint8", - "minimum": 0.0 - } - } - } - ] + "rollout": { + "$ref": "#/$defs/Rollout" + } + }, + "required": [ + "kind", + "rollout" + ] + }, + { + "description": "Partially enabled: used for gradual rollout of the feature", + "type": "object", + "properties": { + "kind": { + "type": "string", + "const": "rolloutrange" }, - "use_reader_module_imports": { - "description": "Generate the `moduleImports` field in the Reader AST.", - "default": { - "kind": "disabled" - }, - "oneOf": [ - { - "description": "Fully disabled: developers may not use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "disabled" - ] - } - } - }, - { - "description": "Fully enabled: developers may use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "enabled" - ] - } - } - }, - { - "description": "Partially enabled: developers may only use this feature on the listed items (fragments, fields, types).", - "type": "object", - "required": [ - "allowlist", - "kind" - ], - "properties": { - "allowlist": { - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "kind": { - "type": "string", - "enum": [ - "limited" - ] - } - } - }, - { - "description": "Partially enabled: used for gradual rollout of the feature", - "type": "object", - "required": [ - "kind", - "rollout" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "rollout" - ] - }, - "rollout": { - "description": "A utility to enable gradual rollout of large codegen changes. Can be constructed as the Default which passes or a percentage between 0 and 100.", - "type": [ - "integer", - "null" - ], - "format": "uint8", - "minimum": 0.0 - } - } - } - ] + "rollout": { + "$ref": "#/$defs/RolloutRange" } }, - "additionalProperties": false + "required": [ + "kind", + "rollout" + ] + } + ] + }, + "FeatureFlags": { + "type": "object", + "properties": { + "actor_change_support": { + "$ref": "#/$defs/FeatureFlag", + "default": { + "kind": "disabled" + } + }, + "allow_output_type_resolvers": { + "description": "@outputType resolvers are a discontinued experimental feature. This flag\nallows users to allowlist old uses of this feature while they work to\nremove them. Weak types (types without an `id` field) returned by a Relay\nResolver should be limited to types defined using `@RelayResolver` with `@weak`.\n\nIf using the \"limited\" feature flag variant, users can allowlist a\nspecific list of field names.\n\nhttps://relay.dev/docs/next/guides/relay-resolvers/defining-types/#defining-a-weak-type", + "$ref": "#/$defs/FeatureFlag", + "default": { + "kind": "disabled" + } + }, + "allow_required_in_mutation_response": { + "description": "@required with an action of THROW is read-time feature that is not\ncompatible with our mutation APIs. We are in the process of removing\nany existing examples, but this flag is part of a process of removing\nany existing examples.", + "$ref": "#/$defs/FeatureFlag", + "default": { + "kind": "disabled" + } + }, + "allow_resolver_non_nullable_return_type": { + "description": "Allow non-nullable return types from resolvers.", + "$ref": "#/$defs/FeatureFlag", + "default": { + "kind": "disabled" + } + }, + "allow_resolvers_in_mutation_response": { + "description": "Relay Resolvers are a read-time feature that are not actually handled in\nour mutation APIs. We are in the process of removing any existing\nexamples, but this flag is part of a process of removing any existing\nexamples.", + "$ref": "#/$defs/FeatureFlag", + "default": { + "kind": "disabled" + } + }, + "compact_query_text": { + "description": "Print queries in compact form", + "$ref": "#/$defs/FeatureFlag", + "default": { + "kind": "disabled" + } + }, + "disable_deduping_common_structures_in_artifacts": { + "description": "Skip the optimization which extracts common JavaScript structures in\ngenerated artifacts into numbered variables and uses them by reference\nin each position in which they occur.\n\nThis optimization can make it hard to follow changes to generated\ncode, so being able to disable it can be helpful for debugging.\n\nTo disable deduping for just one fragment or operation's generated\nartifacts:\n\n```json\n\"disable_deduping_common_structures_in_artifacts\": {\n { \"kind\": \"limited\", \"allowList\": [\"\"] }\n}\n```", + "$ref": "#/$defs/FeatureFlag", + "default": { + "kind": "disabled" + } + }, + "disable_edge_type_name_validation_on_declerative_connection_directives": { + "description": "Disable validation of the `edgeTypeName` argument on `@prependNode` and `@appendNode`.", + "$ref": "#/$defs/FeatureFlag", + "default": { + "kind": "disabled" + } + }, + "disable_full_argument_type_validation": { + "description": "Disable full GraphQL argument type validation. Historically, we only applied argument type\nvalidation to the query that was actually going to be persisted and sent\nto the server. This meant that we didn't typecheck arguments passed to\nRelay Resolvers or Client Schema Extensions.\n\nWe also permitted an escape hatch of `uncheckedArguments_DEPRECATED` for\ndefining fragment arguments which were not typechecked.\n\nWe no-longer support `uncheckedArguments_DEPRECATED`, and we typecheck\nboth client and server arguments. This flag allows you to opt out of\nthis new behavior to enable gradual adoption of the new validations.\n\nThis flag will be removed in a future version of Relay.", + "$ref": "#/$defs/FeatureFlag", + "default": { + "kind": "disabled" + } + }, + "disable_resolver_reader_ast": { + "description": "Mirror of `enable_resolver_normalization_ast`\nexcludes resolver metadata from reader ast", + "type": "boolean", + "default": false + }, + "disable_schema_validation": { + "description": "Disable validating the composite schema (server, client schema\nextensions, Relay Resolvers) after its built.", + "type": "boolean", + "default": false + }, + "enable_3d_branch_arg_generation": { + "type": "boolean", + "default": false + }, + "enable_exec_time_resolvers_directive": { + "description": "Allow per-query opt in to normalization AST for Resolvers with exec_time_resolvers\ndirective. In contrast to enable_resolver_normalization_ast, if this is true, a\nnormalization AST can be generated for a query using the @exec_time_resolvers directive", + "type": "boolean", + "default": false + }, + "enable_fragment_argument_transform": { + "description": "Add support for parsing and transforming variable definitions on fragment\ndefinitions and arguments on fragment spreads.", + "type": "boolean", + "default": false }, - "isDevVariableName": { - "description": "We may generate some content in the artifacts that's stripped in production if __DEV__ variable is set This config option is here to define the name of that special variable", - "default": null, - "type": [ - "string", - "null" - ] + "enable_relay_resolver_mutations": { + "description": "Allow relay resolvers to extend the Mutation type", + "type": "boolean", + "default": false }, - "jsModuleFormat": { - "description": "Formatting style for generated files.", - "default": "commonjs", - "oneOf": [ - { - "description": "Common JS style, e.g. `require('../path/MyModule')`", - "type": "string", - "enum": [ - "commonjs" - ] - }, - { - "description": "Facebook style, e.g. `require('MyModule')`", - "type": "string", - "enum": [ - "haste" - ] - } - ] + "enable_resolver_normalization_ast": { + "description": "Fully build the normalization AST for Resolvers", + "type": "boolean", + "default": false }, - "language": { - "description": "The desired output language, \"flow\" or \"typescript\".", - "type": "string", - "enum": [ - "javascript", - "typescript", - "flow" - ] + "enable_strict_custom_scalars": { + "description": "Perform strict validations when custom scalar types are used", + "type": "boolean", + "default": false }, - "moduleImportConfig": { - "description": "Configuration for @module", + "enforce_fragment_alias_where_ambiguous": { + "description": "Enforce that you must add `@alias` to a fragment if it may not match,\ndue to type mismatch or `@skip`/`@include`", + "$ref": "#/$defs/FeatureFlag", "default": { - "dynamicModuleProvider": null, - "surface": null - }, - "type": "object", - "properties": { - "dynamicModuleProvider": { - "description": "Defines the custom import statement to be generated on the `ModuleImport` node in ASTs, used for dynamically loading components at runtime.", - "anyOf": [ - { - "oneOf": [ - { - "description": "Generates a module provider using JSResource", - "type": "object", - "required": [ - "mode" - ], - "properties": { - "mode": { - "type": "string", - "enum": [ - "JSResource" - ] - } - } - }, - { - "description": "Generates a custom JS import, Use `<$module>` as the placeholder for the actual module. e.g. `\"() => import('<$module>')\"`", - "type": "object", - "required": [ - "mode", - "statement" - ], - "properties": { - "mode": { - "type": "string", - "enum": [ - "Custom" - ] - }, - "statement": { - "type": "string" - } - } - } - ] - }, - { - "type": "null" - } - ] - }, - "surface": { - "description": "Defines the surface upon which @module is enabled.", - "type": [ - "string", - "null" - ], - "enum": [ - "resolvers", - "all" - ] - } - }, - "additionalProperties": false + "kind": "enabled" + } }, - "noFutureProofEnums": { - "description": "This option controls whether or not a catch-all entry is added to enum type definitions for values that may be added in the future. Enabling this means you will have to update your application whenever the GraphQL server schema adds new enum values to prevent it from breaking.", - "default": false, - "type": "boolean" + "legacy_include_path_in_required_reader_nodes": { + "description": "The `path` field in `@required` Reader AST nodes is no longer used. But\nremoving them in one diff is too large of a change to ship at once.\n\nThis flag will allow us to use the rollout FeatureFlag to remove them\nacross a number of diffs.", + "$ref": "#/$defs/FeatureFlag", + "default": { + "kind": "disabled" + } }, - "noSourceControl": { - "description": "Opt out of source control checks/integration.", - "default": null, - "type": [ - "boolean", - "null" - ] + "no_inline": { + "description": "For now, this also disallows fragments with variable definitions\nThis also makes @module to opt in using @no_inline internally\nNOTE that the presence of a fragment in this list only controls whether a fragment is *allowed* to\nuse @no_inline: whether the fragment is inlined or not depends on whether it actually uses that\ndirective.", + "$ref": "#/$defs/FeatureFlag", + "default": { + "kind": "disabled" + } }, - "optionalInputFields": { - "title": "For Flow type generation", - "description": "When set, generated input types will have the listed fields optional even if the schema defines them as required.", - "default": [], - "type": "array", - "items": { - "type": "string" + "omit_resolver_type_assertions_for_confirmed_types": { + "description": "Skip generating resolver type assertions for resolvers which have\nbeen derived from TS/Flow types.", + "$ref": "#/$defs/FeatureFlag", + "default": { + "kind": "disabled" } }, - "persistConfig": { - "description": "Query Persist Configuration It contains URL and addition parameters that will be included with the request (think API_KEY, APP_ID, etc...)", - "default": null, + "prefer_fetchable_in_refetch_queries": { + "description": "Feature flag to prefer `fetch_MyType()` generatior over `node()` query generator\nin @refetchable transform", + "type": "boolean", + "default": false + }, + "relay_resolver_enable_interface_output_type": { + "description": "Enable returning interfaces from Relay Resolvers without @outputType", + "$ref": "#/$defs/FeatureFlag", + "default": { + "kind": "disabled" + } + }, + "skip_printing_nulls": { + "$ref": "#/$defs/FeatureFlag", + "default": { + "kind": "disabled" + } + }, + "text_artifacts": { + "description": "Enable generation of text artifacts used to generate full query strings\nlater.", + "$ref": "#/$defs/FeatureFlag", + "default": { + "kind": "disabled" + } + }, + "use_reader_module_imports": { + "description": "Generate the `moduleImports` field in the Reader AST.", + "$ref": "#/$defs/FeatureFlag", + "default": { + "kind": "disabled" + } + } + }, + "additionalProperties": false + }, + "JsModuleFormat": { + "description": "Formatting style for generated files.", + "oneOf": [ + { + "description": "Common JS style, e.g. `require('../path/MyModule')`", + "type": "string", + "const": "commonjs" + }, + { + "description": "Facebook style, e.g. `require('MyModule')`", + "type": "string", + "const": "haste" + } + ] + }, + "LocalPersistAlgorithm": { + "type": "string", + "enum": [ + "MD5", + "SHA1", + "SHA256" + ] + }, + "LocalPersistConfig": { + "description": "Configuration for local persistence of GraphQL documents.\n\nThis struct contains settings that control how GraphQL documents are persisted locally.", + "type": "object", + "properties": { + "algorithm": { + "description": "The algorithm to use for hashing the operation text.", + "$ref": "#/$defs/LocalPersistAlgorithm", + "default": "MD5" + }, + "file": { + "description": "The file path where the persisted documents will be written.", + "type": "string" + }, + "include_query_text": { + "description": "Whether to include the query text in the persisted document.", + "type": "boolean", + "default": false + } + }, + "additionalProperties": false, + "required": [ + "file" + ] + }, + "ModuleImportConfig": { + "description": "Configuration for @module.", + "type": "object", + "properties": { + "dynamicModuleProvider": { + "description": "Defines the custom import statement to be generated on the\n`ModuleImport` node in ASTs, used for dynamically loading\ncomponents at runtime.", "anyOf": [ { - "description": "Configuration for how the Relay Compiler should persist GraphQL queries.", - "anyOf": [ - { - "description": "This variant represents a remote persistence configuration, where GraphQL queries are sent to a remote endpoint for persistence.", - "type": "object", - "required": [ - "url" - ], - "properties": { - "concurrency": { - "description": "Number of concurrent requests that can be made to the server.", - "default": null, - "type": [ - "integer", - "null" - ], - "format": "uint", - "minimum": 0.0 - }, - "headers": { - "description": "Additional headers to include in the POST request.", - "default": {}, - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "includeQueryText": { - "description": "Whether to include the query text in the persisted document.", - "default": false, - "type": "boolean" - }, - "params": { - "description": "Additional parameters to include in the POST request.\n\nThe main document will be in a POST parameter `text`. This map can contain additional parameters to send.", - "default": {}, - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "url": { - "description": "URL that the document should be persisted to via a POST request.", - "type": "string" - } - }, - "additionalProperties": false - }, - { - "description": "This variant represents a local persistence configuration, where GraphQL queries are persisted to a local JSON file.\n\nWhen this variant is used, the compiler will attempt to read the local file as a hash map, add new queries to the map, and then serialize and write the resulting map to the configured path.", - "type": "object", - "required": [ - "file" - ], - "properties": { - "algorithm": { - "description": "The algorithm to use for hashing the operation text.", - "default": "MD5", - "type": "string", - "enum": [ - "MD5", - "SHA1", - "SHA256" - ] - }, - "file": { - "description": "The file path where the persisted documents will be written.", - "type": "string" - }, - "include_query_text": { - "description": "Whether to include the query text in the persisted document.", - "default": false, - "type": "boolean" - } - }, - "additionalProperties": false - } - ] + "$ref": "#/$defs/ModuleProvider" }, { "type": "null" } ] }, - "requireCustomScalarTypes": { - "description": "Require all GraphQL scalar types mapping to be defined, will throw if a GraphQL scalar type doesn't have a JS type", - "default": false, - "type": "boolean" - }, - "resolverContextType": { - "description": "Indicates the type to import and use as the context for live resolvers.", - "default": null, + "operationModuleProvider": { + "description": "Defines the custom import statement to be generated for the\n`operationModuleProvider` function on the `NormalizationModuleImport`\nnode in ASTs. Used in exec time client 3D.", "anyOf": [ { - "anyOf": [ - { - "description": "Specifies how Relay can import the Resolver context type from a path", - "type": "object", - "required": [ - "name", - "path" - ], - "properties": { - "name": { - "description": "The name under which the type is exported from the module", - "type": "string" - }, - "path": { - "description": "The path to the module relative to the project root", - "type": "string" - } - } - }, - { - "description": "Specifies how Relay can import the Resolver context type from a named package", - "type": "object", - "required": [ - "name", - "package" - ], - "properties": { - "name": { - "description": "The name under which the type is exported from the package", - "type": "string" - }, - "package": { - "description": "The name of the package", - "type": "string" - } - } - } - ] + "$ref": "#/$defs/ModuleProvider" }, { "type": "null" } ] }, - "resolversSchemaModule": { - "description": "Configuration for resolvers_schema_module generation", - "default": null, - "type": [ - "object", - "null" - ], - "properties": { - "applyToNormalizationAst": { - "default": false, - "type": "boolean" + "surface": { + "description": "Defines the surface upon which @module is enabled.", + "anyOf": [ + { + "$ref": "#/$defs/Surface" }, - "path": { - "default": "", - "type": "string" + { + "type": "null" } - }, - "additionalProperties": false - }, - "schema": { - "description": "Path to schema.graphql", - "default": "", - "type": "string" - }, - "schemaConfig": { - "description": "Extra configuration for the schema itself.", - "default": { - "connectionInterface": { - "cursor": "cursor", - "edges": "edges", - "endCursor": "endCursor", - "hasNextPage": "hasNextPage", - "hasPreviousPage": "hasPreviousPage", - "node": "node", - "pageInfo": "pageInfo", - "startCursor": "startCursor" - }, - "deferStreamInterface": { - "deferName": "defer", - "ifArg": "if", - "initialCountArg": "initialCount", - "labelArg": "label", - "streamName": "stream", - "useCustomizedBatchArg": "useCustomizedBatch" - }, - "nodeInterfaceIdField": "id", - "nodeInterfaceIdVariableName": "id", - "nonNodeIdFields": null, - "unselectableDirectiveName": "unselectable" - }, + ] + } + }, + "additionalProperties": false + }, + "ModuleProvider": { + "oneOf": [ + { + "description": "Generates a module provider using JSResource", "type": "object", "properties": { - "connectionInterface": { - "description": "Configuration where Relay should expect some fields in the schema.", - "default": { - "cursor": "cursor", - "edges": "edges", - "endCursor": "endCursor", - "hasNextPage": "hasNextPage", - "hasPreviousPage": "hasPreviousPage", - "node": "node", - "pageInfo": "pageInfo", - "startCursor": "startCursor" - }, - "type": "object", - "required": [ - "cursor", - "edges", - "endCursor", - "hasNextPage", - "hasPreviousPage", - "node", - "pageInfo", - "startCursor" - ], - "properties": { - "cursor": { - "type": "string" - }, - "edges": { - "type": "string" - }, - "endCursor": { - "type": "string" - }, - "hasNextPage": { - "type": "string" - }, - "hasPreviousPage": { - "type": "string" - }, - "node": { - "type": "string" - }, - "pageInfo": { - "type": "string" - }, - "startCursor": { - "type": "string" - } - }, - "additionalProperties": false - }, - "deferStreamInterface": { - "description": "Configuration where Relay should expect some fields in the schema.", - "default": { - "deferName": "defer", - "ifArg": "if", - "initialCountArg": "initialCount", - "labelArg": "label", - "streamName": "stream", - "useCustomizedBatchArg": "useCustomizedBatch" - }, - "type": "object", - "required": [ - "deferName", - "ifArg", - "initialCountArg", - "labelArg", - "streamName", - "useCustomizedBatchArg" - ], - "properties": { - "deferName": { - "description": "Wrapper struct for clarity rather than having StringKey everywhere.", - "type": "string" - }, - "ifArg": { - "type": "string" - }, - "initialCountArg": { - "type": "string" - }, - "labelArg": { - "type": "string" - }, - "streamName": { - "description": "Wrapper struct for clarity rather than having StringKey everywhere.", - "type": "string" - }, - "useCustomizedBatchArg": { - "type": "string" - } - }, - "additionalProperties": false - }, - "nodeInterfaceIdField": { - "description": "The name of the `id` field that exists on the `Node` interface.", - "default": "id", - "type": "string" - }, - "nodeInterfaceIdVariableName": { - "description": "The name of the variable expected by the `node` query.", - "default": "id", - "type": "string" - }, - "nonNodeIdFields": { - "description": "Configuration of Relay's validation for `id` fields outside of the `Node` interface.", - "default": null, - "type": [ - "object", - "null" - ], - "properties": { - "allowedIdTypes": { - "description": "A map of parent type names to allowed type names for fields named `id`", - "default": {}, - "type": "object", - "additionalProperties": { - "type": "string" - } - } - }, - "additionalProperties": false - }, - "unselectableDirectiveName": { - "description": "The name of the directive indicating fields that cannot be selected", - "default": "unselectable", - "type": "string" + "mode": { + "type": "string", + "const": "JSResource" } - } - }, - "schemaExtensions": { - "description": "List of directories with schema extensions.", - "default": [], - "type": "array", - "items": { - "type": "string" - } - }, - "src": { - "description": "Root directory of application code", - "default": "", - "type": "string" - }, - "typegenPhase": { - "description": "Added in 13.1.1 to customize Final/Compat mode in the single project config file Removed in 14.0.0", - "default": null - }, - "typescriptExcludeUndefinedFromNullableUnion": { - "description": "Keep the previous compiler behavior by outputting an union of the raw type and null, and not the **correct** behavior of an union with the raw type, null and undefined.", - "default": false, - "type": "boolean" + }, + "required": [ + "mode" + ] }, - "useImportTypeSyntax": { - "title": "For Typescript type generation", - "description": "Whether to use the `import type` syntax introduced in Typescript version 3.8. This will prevent warnings from `importsNotUsedAsValues`.", - "default": false, - "type": "boolean" + { + "description": "Generates a custom JS import, Use `<$module>` as the placeholder\nfor the actual module. e.g. `\"() => import('<$module>')\"`", + "type": "object", + "properties": { + "mode": { + "type": "string", + "const": "Custom" + }, + "statement": { + "$ref": "#/$defs/StringKey" + } + }, + "required": [ + "mode", + "statement" + ] } - }, - "additionalProperties": false + ] }, - { - "description": "Relay can support multiple projects with multiple schemas and different options (output, typegen, etc...). This MultiProjectConfigFile is responsible for configuring these type of projects (complex)", + "MultiProjectConfigFile": { + "description": "Schema of the compiler configuration JSON file.", "type": "object", - "required": [ - "projects", - "sources" - ], "properties": { "$schema": { "description": "The user may hard-code the JSON Schema for their version of the config.", @@ -2029,29 +850,34 @@ ] }, "codegenCommand": { - "default": null, "type": [ "string", "null" - ] + ], + "default": null }, "excludes": { - "description": "Glob patterns that should not be part of the sources even if they are in the source set directories.", + "description": "Glob patterns that should not be part of the sources even if they are\nin the source set directories.", + "type": "array", "default": [ "**/node_modules/**", "**/__mocks__/**", "**/__generated__/**" ], - "type": "array", "items": { "type": "string" } }, "featureFlags": { + "description": "Enable and disable experimental or legacy behaviors.\nWARNING! These are not stable and may change at any time.", + "$ref": "#/$defs/FeatureFlags", "default": { "actor_change_support": { "kind": "disabled" }, + "allow_output_type_resolvers": { + "kind": "disabled" + }, "allow_required_in_mutation_response": { "kind": "disabled" }, @@ -2078,1482 +904,47 @@ "enable_3d_branch_arg_generation": false, "enable_exec_time_resolvers_directive": false, "enable_fragment_argument_transform": false, - "enable_relay_resolver_mutations": false, - "enable_resolver_normalization_ast": false, - "enable_strict_custom_scalars": false, - "enforce_fragment_alias_where_ambiguous": { - "kind": "disabled" - }, - "legacy_include_path_in_required_reader_nodes": { - "kind": "disabled" - }, - "no_inline": { - "kind": "disabled" - }, - "omit_resolver_type_assertions_for_confirmed_types": { - "kind": "disabled" - }, - "prefer_fetchable_in_refetch_queries": false, - "relay_resolver_enable_interface_output_type": { - "kind": "disabled" - }, - "skip_printing_nulls": { - "kind": "disabled" - }, - "text_artifacts": { - "kind": "disabled" - }, - "use_reader_module_imports": { - "kind": "disabled" - } - }, - "type": "object", - "properties": { - "actor_change_support": { - "default": { - "kind": "disabled" - }, - "oneOf": [ - { - "description": "Fully disabled: developers may not use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "disabled" - ] - } - } - }, - { - "description": "Fully enabled: developers may use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "enabled" - ] - } - } - }, - { - "description": "Partially enabled: developers may only use this feature on the listed items (fragments, fields, types).", - "type": "object", - "required": [ - "allowlist", - "kind" - ], - "properties": { - "allowlist": { - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "kind": { - "type": "string", - "enum": [ - "limited" - ] - } - } - }, - { - "description": "Partially enabled: used for gradual rollout of the feature", - "type": "object", - "required": [ - "kind", - "rollout" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "rollout" - ] - }, - "rollout": { - "description": "A utility to enable gradual rollout of large codegen changes. Can be constructed as the Default which passes or a percentage between 0 and 100.", - "type": [ - "integer", - "null" - ], - "format": "uint8", - "minimum": 0.0 - } - } - } - ] - }, - "allow_required_in_mutation_response": { - "description": "@required with an action of THROW is read-time feature that is not compatible with our mutation APIs. We are in the process of removing any existing examples, but this flag is part of a process of removing any existing examples.", - "default": { - "kind": "disabled" - }, - "oneOf": [ - { - "description": "Fully disabled: developers may not use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "disabled" - ] - } - } - }, - { - "description": "Fully enabled: developers may use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "enabled" - ] - } - } - }, - { - "description": "Partially enabled: developers may only use this feature on the listed items (fragments, fields, types).", - "type": "object", - "required": [ - "allowlist", - "kind" - ], - "properties": { - "allowlist": { - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "kind": { - "type": "string", - "enum": [ - "limited" - ] - } - } - }, - { - "description": "Partially enabled: used for gradual rollout of the feature", - "type": "object", - "required": [ - "kind", - "rollout" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "rollout" - ] - }, - "rollout": { - "description": "A utility to enable gradual rollout of large codegen changes. Can be constructed as the Default which passes or a percentage between 0 and 100.", - "type": [ - "integer", - "null" - ], - "format": "uint8", - "minimum": 0.0 - } - } - } - ] - }, - "allow_resolver_non_nullable_return_type": { - "description": "Allow non-nullable return types from resolvers.", - "default": { - "kind": "disabled" - }, - "oneOf": [ - { - "description": "Fully disabled: developers may not use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "disabled" - ] - } - } - }, - { - "description": "Fully enabled: developers may use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "enabled" - ] - } - } - }, - { - "description": "Partially enabled: developers may only use this feature on the listed items (fragments, fields, types).", - "type": "object", - "required": [ - "allowlist", - "kind" - ], - "properties": { - "allowlist": { - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "kind": { - "type": "string", - "enum": [ - "limited" - ] - } - } - }, - { - "description": "Partially enabled: used for gradual rollout of the feature", - "type": "object", - "required": [ - "kind", - "rollout" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "rollout" - ] - }, - "rollout": { - "description": "A utility to enable gradual rollout of large codegen changes. Can be constructed as the Default which passes or a percentage between 0 and 100.", - "type": [ - "integer", - "null" - ], - "format": "uint8", - "minimum": 0.0 - } - } - } - ] - }, - "allow_resolvers_in_mutation_response": { - "description": "Relay Resolvers are a read-time feature that are not actually handled in our mutation APIs. We are in the process of removing any existing examples, but this flag is part of a process of removing any existing examples.", - "default": { - "kind": "disabled" - }, - "oneOf": [ - { - "description": "Fully disabled: developers may not use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "disabled" - ] - } - } - }, - { - "description": "Fully enabled: developers may use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "enabled" - ] - } - } - }, - { - "description": "Partially enabled: developers may only use this feature on the listed items (fragments, fields, types).", - "type": "object", - "required": [ - "allowlist", - "kind" - ], - "properties": { - "allowlist": { - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "kind": { - "type": "string", - "enum": [ - "limited" - ] - } - } - }, - { - "description": "Partially enabled: used for gradual rollout of the feature", - "type": "object", - "required": [ - "kind", - "rollout" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "rollout" - ] - }, - "rollout": { - "description": "A utility to enable gradual rollout of large codegen changes. Can be constructed as the Default which passes or a percentage between 0 and 100.", - "type": [ - "integer", - "null" - ], - "format": "uint8", - "minimum": 0.0 - } - } - } - ] - }, - "compact_query_text": { - "description": "Print queries in compact form", - "default": { - "kind": "disabled" - }, - "oneOf": [ - { - "description": "Fully disabled: developers may not use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "disabled" - ] - } - } - }, - { - "description": "Fully enabled: developers may use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "enabled" - ] - } - } - }, - { - "description": "Partially enabled: developers may only use this feature on the listed items (fragments, fields, types).", - "type": "object", - "required": [ - "allowlist", - "kind" - ], - "properties": { - "allowlist": { - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "kind": { - "type": "string", - "enum": [ - "limited" - ] - } - } - }, - { - "description": "Partially enabled: used for gradual rollout of the feature", - "type": "object", - "required": [ - "kind", - "rollout" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "rollout" - ] - }, - "rollout": { - "description": "A utility to enable gradual rollout of large codegen changes. Can be constructed as the Default which passes or a percentage between 0 and 100.", - "type": [ - "integer", - "null" - ], - "format": "uint8", - "minimum": 0.0 - } - } - } - ] - }, - "disable_deduping_common_structures_in_artifacts": { - "description": "Skip the optimization which extracts common JavaScript structures in generated artifacts into numbered variables and uses them by reference in each position in which they occur.\n\nThis optimization can make it hard to follow changes to generated code, so being able to disable it can be helpful for debugging.\n\nTo disable deduping for just one fragment or operation's generated artifacts:\n\n```json \"disable_deduping_common_structures_in_artifacts\": { { \"kind\": \"limited\", \"allowList\": [\"\"] } } ```", - "default": { - "kind": "disabled" - }, - "oneOf": [ - { - "description": "Fully disabled: developers may not use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "disabled" - ] - } - } - }, - { - "description": "Fully enabled: developers may use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "enabled" - ] - } - } - }, - { - "description": "Partially enabled: developers may only use this feature on the listed items (fragments, fields, types).", - "type": "object", - "required": [ - "allowlist", - "kind" - ], - "properties": { - "allowlist": { - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "kind": { - "type": "string", - "enum": [ - "limited" - ] - } - } - }, - { - "description": "Partially enabled: used for gradual rollout of the feature", - "type": "object", - "required": [ - "kind", - "rollout" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "rollout" - ] - }, - "rollout": { - "description": "A utility to enable gradual rollout of large codegen changes. Can be constructed as the Default which passes or a percentage between 0 and 100.", - "type": [ - "integer", - "null" - ], - "format": "uint8", - "minimum": 0.0 - } - } - } - ] - }, - "disable_edge_type_name_validation_on_declerative_connection_directives": { - "description": "Disable validation of the `edgeTypeName` argument on `@prependNode` and `@appendNode`.", - "default": { - "kind": "disabled" - }, - "oneOf": [ - { - "description": "Fully disabled: developers may not use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "disabled" - ] - } - } - }, - { - "description": "Fully enabled: developers may use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "enabled" - ] - } - } - }, - { - "description": "Partially enabled: developers may only use this feature on the listed items (fragments, fields, types).", - "type": "object", - "required": [ - "allowlist", - "kind" - ], - "properties": { - "allowlist": { - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "kind": { - "type": "string", - "enum": [ - "limited" - ] - } - } - }, - { - "description": "Partially enabled: used for gradual rollout of the feature", - "type": "object", - "required": [ - "kind", - "rollout" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "rollout" - ] - }, - "rollout": { - "description": "A utility to enable gradual rollout of large codegen changes. Can be constructed as the Default which passes or a percentage between 0 and 100.", - "type": [ - "integer", - "null" - ], - "format": "uint8", - "minimum": 0.0 - } - } - } - ] - }, - "disable_full_argument_type_validation": { - "description": "Disable full GraphQL argument type validation. Historically, we only applied argument type validation to the query that was actually going to be persisted and sent to the server. This meant that we didn't typecheck arguments passed to Relay Resolvers or Client Schema Extensions.\n\nWe also permitted an escape hatch of `uncheckedArguments_DEPRECATED` for defining fragment arguments which were not typechecked.\n\nWe no-longer support `uncheckedArguments_DEPRECATED`, and we typecheck both client and server arguments. This flag allows you to opt out of this new behavior to enable gradual adoption of the new validations.\n\nThis flag will be removed in a future version of Relay.", - "default": { - "kind": "disabled" - }, - "oneOf": [ - { - "description": "Fully disabled: developers may not use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "disabled" - ] - } - } - }, - { - "description": "Fully enabled: developers may use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "enabled" - ] - } - } - }, - { - "description": "Partially enabled: developers may only use this feature on the listed items (fragments, fields, types).", - "type": "object", - "required": [ - "allowlist", - "kind" - ], - "properties": { - "allowlist": { - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "kind": { - "type": "string", - "enum": [ - "limited" - ] - } - } - }, - { - "description": "Partially enabled: used for gradual rollout of the feature", - "type": "object", - "required": [ - "kind", - "rollout" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "rollout" - ] - }, - "rollout": { - "description": "A utility to enable gradual rollout of large codegen changes. Can be constructed as the Default which passes or a percentage between 0 and 100.", - "type": [ - "integer", - "null" - ], - "format": "uint8", - "minimum": 0.0 - } - } - } - ] - }, - "disable_resolver_reader_ast": { - "description": "Mirror of `enable_resolver_normalization_ast` excludes resolver metadata from reader ast", - "default": false, - "type": "boolean" - }, - "disable_schema_validation": { - "description": "Disable validating the composite schema (server, client schema extensions, Relay Resolvers) after its built.", - "default": false, - "type": "boolean" - }, - "enable_3d_branch_arg_generation": { - "default": false, - "type": "boolean" - }, - "enable_exec_time_resolvers_directive": { - "description": "Allow per-query opt in to normalization AST for Resolvers with exec_time_resolvers directive. In contrast to enable_resolver_normalization_ast, if this is true, a normalization AST can be generated for a query using the @exec_time_resolvers directive", - "default": false, - "type": "boolean" - }, - "enable_fragment_argument_transform": { - "description": "Add support for parsing and transforming variable definitions on fragment definitions and arguments on fragment spreads.", - "default": false, - "type": "boolean" - }, - "enable_relay_resolver_mutations": { - "description": "Allow relay resolvers to extend the Mutation type", - "default": false, - "type": "boolean" - }, - "enable_resolver_normalization_ast": { - "description": "Fully build the normalization AST for Resolvers", - "default": false, - "type": "boolean" - }, - "enable_strict_custom_scalars": { - "description": "Perform strict validations when custom scalar types are used", - "default": false, - "type": "boolean" - }, - "enforce_fragment_alias_where_ambiguous": { - "description": "Enforce that you must add `@alias` to a fragment if it may not match, due to type mismatch or `@skip`/`@include`", - "default": { - "kind": "disabled" - }, - "oneOf": [ - { - "description": "Fully disabled: developers may not use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "disabled" - ] - } - } - }, - { - "description": "Fully enabled: developers may use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "enabled" - ] - } - } - }, - { - "description": "Partially enabled: developers may only use this feature on the listed items (fragments, fields, types).", - "type": "object", - "required": [ - "allowlist", - "kind" - ], - "properties": { - "allowlist": { - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "kind": { - "type": "string", - "enum": [ - "limited" - ] - } - } - }, - { - "description": "Partially enabled: used for gradual rollout of the feature", - "type": "object", - "required": [ - "kind", - "rollout" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "rollout" - ] - }, - "rollout": { - "description": "A utility to enable gradual rollout of large codegen changes. Can be constructed as the Default which passes or a percentage between 0 and 100.", - "type": [ - "integer", - "null" - ], - "format": "uint8", - "minimum": 0.0 - } - } - } - ] - }, - "legacy_include_path_in_required_reader_nodes": { - "description": "The `path` field in `@required` Reader AST nodes is no longer used. But removing them in one diff is too large of a change to ship at once.\n\nThis flag will allow us to use the rollout FeatureFlag to remove them across a number of diffs.", - "default": { - "kind": "disabled" - }, - "oneOf": [ - { - "description": "Fully disabled: developers may not use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "disabled" - ] - } - } - }, - { - "description": "Fully enabled: developers may use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "enabled" - ] - } - } - }, - { - "description": "Partially enabled: developers may only use this feature on the listed items (fragments, fields, types).", - "type": "object", - "required": [ - "allowlist", - "kind" - ], - "properties": { - "allowlist": { - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "kind": { - "type": "string", - "enum": [ - "limited" - ] - } - } - }, - { - "description": "Partially enabled: used for gradual rollout of the feature", - "type": "object", - "required": [ - "kind", - "rollout" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "rollout" - ] - }, - "rollout": { - "description": "A utility to enable gradual rollout of large codegen changes. Can be constructed as the Default which passes or a percentage between 0 and 100.", - "type": [ - "integer", - "null" - ], - "format": "uint8", - "minimum": 0.0 - } - } - } - ] + "enable_relay_resolver_mutations": false, + "enable_resolver_normalization_ast": false, + "enable_strict_custom_scalars": false, + "enforce_fragment_alias_where_ambiguous": { + "kind": "disabled" + }, + "legacy_include_path_in_required_reader_nodes": { + "kind": "disabled" }, "no_inline": { - "description": "For now, this also disallows fragments with variable definitions This also makes @module to opt in using @no_inline internally NOTE that the presence of a fragment in this list only controls whether a fragment is *allowed* to use @no_inline: whether the fragment is inlined or not depends on whether it actually uses that directive.", - "default": { - "kind": "disabled" - }, - "oneOf": [ - { - "description": "Fully disabled: developers may not use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "disabled" - ] - } - } - }, - { - "description": "Fully enabled: developers may use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "enabled" - ] - } - } - }, - { - "description": "Partially enabled: developers may only use this feature on the listed items (fragments, fields, types).", - "type": "object", - "required": [ - "allowlist", - "kind" - ], - "properties": { - "allowlist": { - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "kind": { - "type": "string", - "enum": [ - "limited" - ] - } - } - }, - { - "description": "Partially enabled: used for gradual rollout of the feature", - "type": "object", - "required": [ - "kind", - "rollout" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "rollout" - ] - }, - "rollout": { - "description": "A utility to enable gradual rollout of large codegen changes. Can be constructed as the Default which passes or a percentage between 0 and 100.", - "type": [ - "integer", - "null" - ], - "format": "uint8", - "minimum": 0.0 - } - } - } - ] + "kind": "disabled" }, "omit_resolver_type_assertions_for_confirmed_types": { - "description": "Skip generating resolver type assertions for resolvers which have been derived from TS/Flow types.", - "default": { - "kind": "disabled" - }, - "oneOf": [ - { - "description": "Fully disabled: developers may not use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "disabled" - ] - } - } - }, - { - "description": "Fully enabled: developers may use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "enabled" - ] - } - } - }, - { - "description": "Partially enabled: developers may only use this feature on the listed items (fragments, fields, types).", - "type": "object", - "required": [ - "allowlist", - "kind" - ], - "properties": { - "allowlist": { - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "kind": { - "type": "string", - "enum": [ - "limited" - ] - } - } - }, - { - "description": "Partially enabled: used for gradual rollout of the feature", - "type": "object", - "required": [ - "kind", - "rollout" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "rollout" - ] - }, - "rollout": { - "description": "A utility to enable gradual rollout of large codegen changes. Can be constructed as the Default which passes or a percentage between 0 and 100.", - "type": [ - "integer", - "null" - ], - "format": "uint8", - "minimum": 0.0 - } - } - } - ] - }, - "prefer_fetchable_in_refetch_queries": { - "description": "Feature flag to prefer `fetch_MyType()` generatior over `node()` query generator in @refetchable transform", - "default": false, - "type": "boolean" + "kind": "disabled" }, + "prefer_fetchable_in_refetch_queries": false, "relay_resolver_enable_interface_output_type": { - "default": { - "kind": "disabled" - }, - "oneOf": [ - { - "description": "Fully disabled: developers may not use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "disabled" - ] - } - } - }, - { - "description": "Fully enabled: developers may use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "enabled" - ] - } - } - }, - { - "description": "Partially enabled: developers may only use this feature on the listed items (fragments, fields, types).", - "type": "object", - "required": [ - "allowlist", - "kind" - ], - "properties": { - "allowlist": { - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "kind": { - "type": "string", - "enum": [ - "limited" - ] - } - } - }, - { - "description": "Partially enabled: used for gradual rollout of the feature", - "type": "object", - "required": [ - "kind", - "rollout" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "rollout" - ] - }, - "rollout": { - "description": "A utility to enable gradual rollout of large codegen changes. Can be constructed as the Default which passes or a percentage between 0 and 100.", - "type": [ - "integer", - "null" - ], - "format": "uint8", - "minimum": 0.0 - } - } - } - ] + "kind": "disabled" }, "skip_printing_nulls": { - "default": { - "kind": "disabled" - }, - "oneOf": [ - { - "description": "Fully disabled: developers may not use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "disabled" - ] - } - } - }, - { - "description": "Fully enabled: developers may use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "enabled" - ] - } - } - }, - { - "description": "Partially enabled: developers may only use this feature on the listed items (fragments, fields, types).", - "type": "object", - "required": [ - "allowlist", - "kind" - ], - "properties": { - "allowlist": { - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "kind": { - "type": "string", - "enum": [ - "limited" - ] - } - } - }, - { - "description": "Partially enabled: used for gradual rollout of the feature", - "type": "object", - "required": [ - "kind", - "rollout" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "rollout" - ] - }, - "rollout": { - "description": "A utility to enable gradual rollout of large codegen changes. Can be constructed as the Default which passes or a percentage between 0 and 100.", - "type": [ - "integer", - "null" - ], - "format": "uint8", - "minimum": 0.0 - } - } - } - ] + "kind": "disabled" }, "text_artifacts": { - "description": "Enable generation of text artifacts used to generate full query strings later.", - "default": { - "kind": "disabled" - }, - "oneOf": [ - { - "description": "Fully disabled: developers may not use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "disabled" - ] - } - } - }, - { - "description": "Fully enabled: developers may use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "enabled" - ] - } - } - }, - { - "description": "Partially enabled: developers may only use this feature on the listed items (fragments, fields, types).", - "type": "object", - "required": [ - "allowlist", - "kind" - ], - "properties": { - "allowlist": { - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "kind": { - "type": "string", - "enum": [ - "limited" - ] - } - } - }, - { - "description": "Partially enabled: used for gradual rollout of the feature", - "type": "object", - "required": [ - "kind", - "rollout" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "rollout" - ] - }, - "rollout": { - "description": "A utility to enable gradual rollout of large codegen changes. Can be constructed as the Default which passes or a percentage between 0 and 100.", - "type": [ - "integer", - "null" - ], - "format": "uint8", - "minimum": 0.0 - } - } - } - ] + "kind": "disabled" }, "use_reader_module_imports": { - "description": "Generate the `moduleImports` field in the Reader AST.", - "default": { - "kind": "disabled" - }, - "oneOf": [ - { - "description": "Fully disabled: developers may not use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "disabled" - ] - } - } - }, - { - "description": "Fully enabled: developers may use this feature", - "type": "object", - "required": [ - "kind" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "enabled" - ] - } - } - }, - { - "description": "Partially enabled: developers may only use this feature on the listed items (fragments, fields, types).", - "type": "object", - "required": [ - "allowlist", - "kind" - ], - "properties": { - "allowlist": { - "type": "array", - "items": { - "type": "string" - }, - "uniqueItems": true - }, - "kind": { - "type": "string", - "enum": [ - "limited" - ] - } - } - }, - { - "description": "Partially enabled: used for gradual rollout of the feature", - "type": "object", - "required": [ - "kind", - "rollout" - ], - "properties": { - "kind": { - "type": "string", - "enum": [ - "rollout" - ] - }, - "rollout": { - "description": "A utility to enable gradual rollout of large codegen changes. Can be constructed as the Default which passes or a percentage between 0 and 100.", - "type": [ - "integer", - "null" - ], - "format": "uint8", - "minimum": 0.0 - } - } - } - ] + "kind": "disabled" } - }, - "additionalProperties": false + } }, "generatedSources": { "description": "Similar to sources but not affected by excludes.", - "default": {}, "type": "object", "additionalProperties": { - "description": "Set of project names.", - "type": "array", - "items": { - "description": "Represents the name of a project in the Relay configuration.", - "anyOf": [ - { - "description": "No project name is specified.", - "type": "null" - }, - { - "description": "A project name.\n\nThis should match one the keys in the `projects` map in the Relay compiler config.", - "type": "string" - } - ] - } - } + "$ref": "#/$defs/ProjectSet" + }, + "default": {} }, "header": { - "default": [], "type": "array", + "default": [], "items": { "type": "string" } @@ -3566,12 +957,12 @@ ] }, "name": { - "description": "Optional name for this config, might be used for logging or custom extra artifact generator code.", - "default": null, + "description": "Optional name for this config, might be used for logging or custom extra\nartifact generator code.", "type": [ "string", "null" - ] + ], + "default": null }, "noSourceControl": { "description": "Opt out of source control checks/integration.", @@ -3584,11 +975,9 @@ "description": "Configuration of projects to compile.", "type": "object", "additionalProperties": { - "type": "object", - "required": [ - "language" - ], - "properties": { + "$ref": "#/$defs/ConfigFileProject" + } + }, "base": { "description": "If a base project is set, the documents of that project can be referenced, but won't produce output artifacts. Extensions from the base project will be added as well and the schema of the base project should be a subset of the schema of this project.", "default": null, @@ -5691,96 +3080,604 @@ "additionalProperties": false } }, - "root": { - "description": "Root directory relative to the config file. Defaults to the directory where the config is located.", - "default": null, + "root": { + "description": "Root directory relative to the config file. Defaults to the directory\nwhere the config is located.", + "type": [ + "string", + "null" + ], + "default": null + }, + "savedStateConfig": { + "description": "Watchman saved state config.", + "anyOf": [ + { + "$ref": "#/$defs/ScmAwareClockData" + }, + { + "type": "null" + } + ] + }, + "sources": { + "description": "A mapping from directory paths (relative to the root) to a source set.\nIf a path is a subdirectory of another path, the more specific path\nwins.", + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/DeserializableProjectSet" + } + } + }, + "additionalProperties": false, + "required": [ + "sources", + "projects" + ] + }, + "NonNodeIdFieldsConfig": { + "description": "Configuration of Relay's validation for `id` fields outside of the `Node` interface.", + "type": "object", + "properties": { + "allowedIdTypes": { + "description": "A map of parent type names to allowed type names for fields named `id`", + "type": "object", + "additionalProperties": { + "$ref": "#/$defs/StringKey" + }, + "default": {} + } + }, + "additionalProperties": false + }, + "PersistConfig": { + "description": "Configuration for how the Relay Compiler should persist GraphQL queries.", + "anyOf": [ + { + "description": "This variant represents a remote persistence configuration, where GraphQL queries are sent to a remote endpoint for persistence.", + "$ref": "#/$defs/RemotePersistConfig" + }, + { + "description": "This variant represents a local persistence configuration, where GraphQL queries are persisted to a local JSON file.\n\nWhen this variant is used, the compiler will attempt to read the local file as a hash map,\nadd new queries to the map, and then serialize and write the resulting map to the configured path.", + "$ref": "#/$defs/LocalPersistConfig" + } + ] + }, + "ProjectName": { + "description": "Represents the name of a project in the Relay configuration.", + "anyOf": [ + { + "description": "No project name is specified.", + "type": "null" + }, + { + "description": "A project name.\n\nThis should match one the keys in the `projects` map in the Relay compiler config.", + "$ref": "#/$defs/StringKey" + } + ] + }, + "ProjectSet": { + "description": "Set of project names.", + "anyOf": [ + { + "$ref": "#/$defs/ProjectName" + }, + { + "type": "array", + "items": { + "$ref": "#/$defs/ProjectName" + } + } + ] + }, + "RemotePersistConfig": { + "description": "Configuration for remote persistence of GraphQL documents.", + "type": "object", + "properties": { + "concurrency": { + "description": "Number of concurrent requests that can be made to the server.", + "type": [ + "integer", + "null" + ], + "format": "uint", + "default": null, + "minimum": 0 + }, + "headers": { + "description": "Additional headers to include in the POST request.", + "type": "object", + "additionalProperties": { + "type": "string" + }, + "default": {} + }, + "includeQueryText": { + "description": "Whether to include the query text in the persisted document.", + "type": "boolean", + "default": false + }, + "params": { + "description": "Additional parameters to include in the POST request.\n\nThe main document will be in a POST parameter `text`. This map can contain\nadditional parameters to send.", + "type": "object", + "additionalProperties": { + "type": "string" + }, + "default": {} + }, + "url": { + "description": "URL that the document should be persisted to via a POST request.", + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "url" + ] + }, + "ResolverContextTypeInput": { + "description": "Describes the type to import and use as the context for Relay Resolvers.", + "anyOf": [ + { + "description": "The type imported using a relative path", + "$ref": "#/$defs/ResolverContextTypeInputPath" + }, + { + "description": "The type imported using a named package", + "$ref": "#/$defs/ResolverContextTypeInputPackage" + } + ] + }, + "ResolverContextTypeInputPackage": { + "description": "Specifies how Relay can import the Resolver context type from a named package", + "type": "object", + "properties": { + "name": { + "description": "The name under which the type is exported from the package", + "$ref": "#/$defs/StringKey" + }, + "package": { + "description": "The name of the package", + "$ref": "#/$defs/StringKey" + } + }, + "required": [ + "name", + "package" + ] + }, + "ResolverContextTypeInputPath": { + "description": "Specifies how Relay can import the Resolver context type from a path", + "type": "object", + "properties": { + "name": { + "description": "The name under which the type is exported from the module", + "$ref": "#/$defs/StringKey" + }, + "path": { + "description": "The path to the module relative to the project root", + "type": "string" + } + }, + "required": [ + "name", + "path" + ] + }, + "ResolversSchemaModuleConfig": { + "description": "Configuration for resolvers_schema_module generation", + "type": "object", + "properties": { + "applyToNormalizationAst": { + "type": "boolean", + "default": false + }, + "path": { + "type": "string", + "default": "" + } + }, + "additionalProperties": false + }, + "Rollout": { + "description": "A utility to enable gradual rollout of large codegen changes.\nCan be constructed as the Default which passes or a percentage between 0 and\n100.", + "type": [ + "integer", + "null" + ], + "format": "uint8", + "maximum": 255, + "minimum": 0 + }, + "RolloutRange": { + "description": "A utility to enable gradual rollout of large codegen changes. Allows you to\nspecify a range of percentages to rollout.", + "type": "object", + "properties": { + "end": { + "type": "integer", + "format": "uint8", + "maximum": 255, + "minimum": 0 + }, + "start": { + "type": "integer", + "format": "uint8", + "maximum": 255, + "minimum": 0 + } + }, + "required": [ + "start", + "end" + ] + }, + "SavedStateClockData": { + "description": "Holds extended clock data that includes source control aware\nquery metadata.\n", + "type": "object", + "properties": { + "commit-id": { + "type": [ + "string", + "null" + ] + }, + "config": true, + "storage": { + "type": [ + "string", + "null" + ] + } + } + }, + "SchemaConfig": { + "type": "object", + "properties": { + "connectionInterface": { + "$ref": "#/$defs/ConnectionInterface", + "default": { + "cursor": "cursor", + "edges": "edges", + "endCursor": "endCursor", + "hasNextPage": "hasNextPage", + "hasPreviousPage": "hasPreviousPage", + "node": "node", + "pageInfo": "pageInfo", + "startCursor": "startCursor" + } + }, + "deferStreamInterface": { + "$ref": "#/$defs/DeferStreamInterface", + "default": { + "deferName": "defer", + "ifArg": "if", + "initialCountArg": "initialCount", + "labelArg": "label", + "streamName": "stream", + "useCustomizedBatchArg": "useCustomizedBatch" + } + }, + "enableTokenField": { + "description": "If we should select __token field on fetchable types", + "type": "boolean", + "default": false + }, + "nodeInterfaceIdField": { + "description": "The name of the `id` field that exists on the `Node` interface.", + "$ref": "#/$defs/StringKey", + "default": "id" + }, + "nodeInterfaceIdVariableName": { + "description": "The name of the variable expected by the `node` query.", + "$ref": "#/$defs/StringKey", + "default": "id" + }, + "nonNodeIdFields": { + "anyOf": [ + { + "$ref": "#/$defs/NonNodeIdFieldsConfig" + }, + { + "type": "null" + } + ], + "default": null + }, + "unselectableDirectiveName": { + "description": "The name of the directive indicating fields that cannot be selected", + "$ref": "#/$defs/DirectiveName", + "default": "unselectable" + } + } + }, + "ScmAwareClockData": { + "description": "Holds extended clock data that includes source control aware\nquery metadata.\n", + "type": "object", + "properties": { + "mergebase": { "type": [ "string", "null" ] }, - "savedStateConfig": { - "description": "Watchman saved state config.", + "mergebase-with": { + "type": [ + "string", + "null" + ] + }, + "saved-state": { + "anyOf": [ + { + "$ref": "#/$defs/SavedStateClockData" + }, + { + "type": "null" + } + ] + } + } + }, + "SingleProjectConfigFile": { + "type": "object", + "properties": { + "$schema": { + "description": "The user may hard-code the JSON Schema for their version of the config.", "type": [ - "object", + "string", "null" ], - "properties": { - "mergebase": { - "type": [ - "string", - "null" - ] - }, - "mergebase-with": { - "type": [ - "string", - "null" - ] + "default": null + }, + "artifactDirectory": { + "description": "A specific directory to output all artifacts to. When enabling this\nthe babel plugin needs `artifactDirectory` set as well.", + "type": [ + "string", + "null" + ], + "default": null + }, + "codegenCommand": { + "description": "Name of the command that runs the relay compiler. This will be added at\nthe top of generated code to let readers know how to regenerate the file.", + "type": [ + "string", + "null" + ], + "default": null + }, + "customErrorType": { + "description": "A map from GraphQL error name to import path, example:\n{\"name:: \"MyErrorName\", \"path\": \"../src/MyError\"}", + "anyOf": [ + { + "$ref": "#/$defs/CustomTypeImport" }, - "saved-state": { - "description": "Holds extended clock data that includes source control aware query metadata. ", - "type": [ - "object", - "null" - ], - "properties": { - "commit-id": { - "type": [ - "string", - "null" - ] - }, - "config": true, - "storage": { - "type": [ - "string", - "null" - ] - } - } + { + "type": "null" } - } + ] }, - "sources": { - "description": "A mapping from directory paths (relative to the root) to a source set. If a path is a subdirectory of another path, the more specific path wins.", + "customScalarTypes": { + "description": "A map from GraphQL scalar types to a custom JS type, example:\n{ \"Url\": \"String\" }\n{ \"Url\": {\"name:: \"MyURL\", \"path\": \"../src/MyUrlTypes\"} }", "type": "object", "additionalProperties": { - "anyOf": [ - { - "description": "Represents the name of a project in the Relay configuration.", - "anyOf": [ - { - "description": "No project name is specified.", - "type": "null" - }, - { - "description": "A project name.\n\nThis should match one the keys in the `projects` map in the Relay compiler config.", - "type": "string" - } - ] - }, - { - "type": "array", - "items": { - "description": "Represents the name of a project in the Relay configuration.", - "anyOf": [ - { - "description": "No project name is specified.", - "type": "null" - }, - { - "description": "A project name.\n\nThis should match one the keys in the `projects` map in the Relay compiler config.", - "type": "string" - } - ] - } - } - ] + "$ref": "#/$defs/CustomType" + }, + "default": {} + }, + "eagerEsModules": { + "description": "This option enables opting out of emitting es modules artifacts. When\nset to false, Relay will emit CommonJS modules.", + "type": "boolean", + "default": true + }, + "enumModuleSuffix": { + "title": "For Flow type generation", + "description": "When set, enum values are imported from a module with this suffix.\nFor example, an enum Foo and this property set to \".test\" would be\nimported from \"Foo.test\".\nNote: an empty string is allowed and different from not setting the\nvalue, in the example above it would just import from \"Foo\".", + "type": [ + "string", + "null" + ] + }, + "excludes": { + "description": "Directories to ignore under src\ndefault: ['**/node_modules/**', '**/__mocks__/**', '**/__generated__/**'],", + "type": "array", + "default": [ + "**/node_modules/**", + "**/__mocks__/**", + "**/__generated__/**" + ], + "items": { + "type": "string" + } + }, + "extra": { + "description": "A placeholder for allowing extra information in the config file", + "default": null + }, + "featureFlags": { + "description": "Enable and disable experimental or legacy behaviors.\nWARNING! These are not stable and may change at any time.", + "anyOf": [ + { + "$ref": "#/$defs/FeatureFlags" + }, + { + "type": "null" + } + ], + "default": null + }, + "isDevVariableName": { + "description": "We may generate some content in the artifacts that's stripped in production if __DEV__ variable is set\nThis config option is here to define the name of that special variable", + "type": [ + "string", + "null" + ], + "default": null + }, + "jsModuleFormat": { + "description": "Import/export style to use in generated JavaScript modules.", + "$ref": "#/$defs/JsModuleFormat", + "default": "commonjs" + }, + "language": { + "description": "The desired output language, \"flow\" or \"typescript\".", + "$ref": "#/$defs/TypegenLanguage" + }, + "moduleImportConfig": { + "description": "Configuration for @module", + "$ref": "#/$defs/ModuleImportConfig", + "default": { + "dynamicModuleProvider": null, + "operationModuleProvider": null, + "surface": null + } + }, + "noFutureProofEnums": { + "description": "This option controls whether or not a catch-all entry is added to enum type definitions\nfor values that may be added in the future. Enabling this means you will have to update\nyour application whenever the GraphQL server schema adds new enum values to prevent it\nfrom breaking.", + "type": "boolean", + "default": false + }, + "noSourceControl": { + "description": "Opt out of source control checks/integration.", + "type": [ + "boolean", + "null" + ], + "default": null + }, + "optionalInputFields": { + "title": "For Flow type generation", + "description": "When set, generated input types will have the listed fields optional\neven if the schema defines them as required.", + "type": "array", + "default": [], + "items": { + "$ref": "#/$defs/StringKey" + } + }, + "persistConfig": { + "description": "Query Persist Configuration\nIt contains URL and addition parameters that will be included\nwith the request (think API_KEY, APP_ID, etc...)", + "anyOf": [ + { + "$ref": "#/$defs/PersistConfig" + }, + { + "type": "null" + } + ], + "default": null + }, + "relativizeJsModulePaths": { + "description": "Whether to treat all JS module names as relative to './' (true) or not.\ndefault: true", + "type": "boolean", + "default": true + }, + "requireCustomScalarTypes": { + "description": "Require all GraphQL scalar types mapping to be defined, will throw\nif a GraphQL scalar type doesn't have a JS type", + "type": "boolean", + "default": false + }, + "resolverContextType": { + "description": "Indicates the type to import and use as the context for Relay Resolvers.", + "anyOf": [ + { + "$ref": "#/$defs/ResolverContextTypeInput" + }, + { + "type": "null" + } + ], + "default": null + }, + "resolversSchemaModule": { + "anyOf": [ + { + "$ref": "#/$defs/ResolversSchemaModuleConfig" + }, + { + "type": "null" + } + ], + "default": null + }, + "schema": { + "description": "Path to schema.graphql", + "type": "string", + "default": "" + }, + "schemaConfig": { + "description": "Extra configuration for the GraphQL schema itself.", + "$ref": "#/$defs/SchemaConfig", + "default": { + "connectionInterface": { + "cursor": "cursor", + "edges": "edges", + "endCursor": "endCursor", + "hasNextPage": "hasNextPage", + "hasPreviousPage": "hasPreviousPage", + "node": "node", + "pageInfo": "pageInfo", + "startCursor": "startCursor" + }, + "deferStreamInterface": { + "deferName": "defer", + "ifArg": "if", + "initialCountArg": "initialCount", + "labelArg": "label", + "streamName": "stream", + "useCustomizedBatchArg": "useCustomizedBatch" + }, + "enableTokenField": false, + "nodeInterfaceIdField": "id", + "nodeInterfaceIdVariableName": "id", + "nonNodeIdFields": null, + "unselectableDirectiveName": "unselectable" + } + }, + "schemaExtensions": { + "description": "List of directories with schema extensions.", + "type": "array", + "default": [], + "items": { + "type": "string" } + }, + "src": { + "description": "Root directory of application code", + "type": "string", + "default": "" + }, + "typescriptExcludeUndefinedFromNullableUnion": { + "description": "Keep the previous compiler behavior by outputting an union\nof the raw type and null, and not the **correct** behavior\nof an union with the raw type, null and undefined.", + "type": "boolean", + "default": false + }, + "useImportTypeSyntax": { + "title": "For Typescript type generation", + "description": "Whether to use the `import type` syntax introduced in Typescript\nversion 3.8. This will prevent warnings from `importsNotUsedAsValues`.", + "type": "boolean", + "default": false } }, - "additionalProperties": false + "additionalProperties": false, + "required": [ + "language" + ] + }, + "StringKey": { + "type": "string", + "format": null + }, + "Surface": { + "type": "string", + "enum": [ + "resolvers", + "all" + ] + }, + "TypegenLanguage": { + "type": "string", + "enum": [ + "javascript", + "typescript", + "flow" + ] } - ] -} \ No newline at end of file + } +} diff --git a/compiler/crates/relay-compiler/src/artifact_content/content.rs b/compiler/crates/relay-compiler/src/artifact_content/content.rs index 3257df395cf71..c2a1928e60f63 100644 --- a/compiler/crates/relay-compiler/src/artifact_content/content.rs +++ b/compiler/crates/relay-compiler/src/artifact_content/content.rs @@ -17,20 +17,20 @@ use graphql_ir::FragmentDefinition; use graphql_ir::FragmentDefinitionName; use graphql_ir::OperationDefinition; use intern::string_key::Intern; -use relay_codegen::build_request_params; use relay_codegen::Printer; use relay_codegen::QueryID; -use relay_transforms::is_operation_preloadable; -use relay_transforms::RelayDataDrivenDependencyMetadata; +use relay_codegen::build_request_params; use relay_transforms::ASSIGNABLE_DIRECTIVE; +use relay_transforms::RelayDataDrivenDependencyMetadata; use relay_transforms::UPDATABLE_DIRECTIVE; +use relay_transforms::is_operation_preloadable; +use relay_typegen::FragmentLocations; +use relay_typegen::TypegenConfig; +use relay_typegen::TypegenLanguage; use relay_typegen::generate_fragment_type_exports_section; use relay_typegen::generate_named_validator_export; use relay_typegen::generate_operation_type_exports_section; use relay_typegen::generate_split_operation_type_exports_section; -use relay_typegen::FragmentLocations; -use relay_typegen::TypegenConfig; -use relay_typegen::TypegenLanguage; use relay_typegen::rescript_utils::find_provided_variables; use schema::SDLSchema; use signedsource::SIGNING_TOKEN; @@ -302,7 +302,7 @@ pub fn generate_operation( if project_config .persist .as_ref() - .map_or(false, |config| config.include_query_text()) + .is_some_and(|config| config.include_query_text()) { request_parameters.text.clone_from(text); } diff --git a/compiler/crates/relay-compiler/src/artifact_map.rs b/compiler/crates/relay-compiler/src/artifact_map.rs index f2601065ab92e..f9f2910926fbd 100644 --- a/compiler/crates/relay-compiler/src/artifact_map.rs +++ b/compiler/crates/relay-compiler/src/artifact_map.rs @@ -7,8 +7,8 @@ use std::path::PathBuf; -use dashmap::mapref::entry::Entry; use dashmap::DashMap; +use dashmap::mapref::entry::Entry; use docblock_shared::ResolverSourceHash; use graphql_ir::ExecutableDefinitionName; use relay_codegen::QueryID; diff --git a/compiler/crates/relay-compiler/src/build_project.rs b/compiler/crates/relay-compiler/src/build_project.rs index 3f671ae4d7152..0b77e0a92c3b1 100644 --- a/compiler/crates/relay-compiler/src/build_project.rs +++ b/compiler/crates/relay-compiler/src/build_project.rs @@ -36,23 +36,23 @@ pub use artifact_generated_types::ArtifactGeneratedTypes; use build_ir::BuildIRResult; pub use build_ir::SourceHashes; pub use build_schema::build_schema; -use common::sync::*; use common::Diagnostic; use common::DirectiveName; use common::PerfLogEvent; use common::PerfLogger; use common::WithDiagnostics; -use dashmap::mapref::entry::Entry; +use common::sync::*; use dashmap::DashSet; +use dashmap::mapref::entry::Entry; use dependency_analyzer::get_ir_definition_references; use errors::try_all; use fnv::FnvBuildHasher; use fnv::FnvHashMap; use fnv::FnvHashSet; -pub use generate_artifacts::generate_artifacts; -pub use generate_artifacts::generate_preloadable_query_parameters_artifact; pub use generate_artifacts::Artifact; pub use generate_artifacts::ArtifactContent; +pub use generate_artifacts::generate_artifacts; +pub use generate_artifacts::generate_preloadable_query_parameters_artifact; use graphql_ir::ExecutableDefinition; use graphql_ir::ExecutableDefinitionName; use graphql_ir::FragmentDefinitionNameSet; @@ -66,9 +66,9 @@ use rayon::iter::IntoParallelRefIterator; use rayon::slice::ParallelSlice; use relay_codegen::Printer; use relay_config::ProjectName; -use relay_transforms::apply_transforms; use relay_transforms::CustomTransformsConfig; use relay_transforms::Programs; +use relay_transforms::apply_transforms; use relay_typegen::FragmentLocations; use rustc_hash::FxHashMap; use rustc_hash::FxHashSet; @@ -76,15 +76,15 @@ use schema::SDLSchema; use schema_diff::check::IncrementalBuildSchemaChange; use schema_diff::check::SchemaChangeSafety; pub use source_control::source_control_for_root; +pub use validate::AdditionalValidations; pub use validate::validate; pub use validate::validate_reader; -pub use validate::AdditionalValidations; use self::log_program_stats::print_stats; -pub use self::project_asts::find_duplicates; -pub use self::project_asts::get_project_asts; pub use self::project_asts::ProjectAstData; pub use self::project_asts::ProjectAsts; +pub use self::project_asts::find_duplicates; +pub use self::project_asts::get_project_asts; use super::artifact_content; use crate::artifact_map::ArtifactMap; use crate::artifact_map::ArtifactSourceKey; diff --git a/compiler/crates/relay-compiler/src/build_project/artifact_generated_types.rs b/compiler/crates/relay-compiler/src/build_project/artifact_generated_types.rs index ead7238e647e4..067b70673105b 100644 --- a/compiler/crates/relay-compiler/src/build_project/artifact_generated_types.rs +++ b/compiler/crates/relay-compiler/src/build_project/artifact_generated_types.rs @@ -10,8 +10,8 @@ use graphql_ir::FragmentDefinition; use graphql_ir::OperationDefinition; use graphql_syntax::OperationKind; use relay_config::TypegenLanguage; -use relay_transforms::RefetchableMetadata; use relay_transforms::INLINE_DIRECTIVE_NAME; +use relay_transforms::RefetchableMetadata; use relay_transforms::UPDATABLE_DIRECTIVE; use relay_typegen::has_raw_response_type_directive; diff --git a/compiler/crates/relay-compiler/src/build_project/artifact_writer.rs b/compiler/crates/relay-compiler/src/build_project/artifact_writer.rs index 44b0278be972b..fb1ea7c86a9fd 100644 --- a/compiler/crates/relay-compiler/src/build_project/artifact_writer.rs +++ b/compiler/crates/relay-compiler/src/build_project/artifact_writer.rs @@ -6,8 +6,8 @@ */ use std::fmt::Write as _; -use std::fs::create_dir_all; use std::fs::File; +use std::fs::create_dir_all; use std::io; use std::io::prelude::*; use std::path::Path; @@ -15,6 +15,7 @@ use std::path::PathBuf; use std::sync::Mutex; use dashmap::DashSet; +use log::debug; use log::info; use sha1::Digest; use sha1::Sha1; @@ -104,7 +105,10 @@ impl ArtifactWriter for ArtifactFileWriter { Ok(_) => { self.removed.lock().unwrap().push(path); } - _ => info!("tried to delete already deleted file: {:?}", path), + Err(error) => { + info!("tried to delete already deleted file: {:?}", path); + debug!("[artifact_writer] error when deleting file: {:?}", error); + } } Ok(()) } diff --git a/compiler/crates/relay-compiler/src/build_project/build_resolvers_schema/extract_docblock_ir.rs b/compiler/crates/relay-compiler/src/build_project/build_resolvers_schema/extract_docblock_ir.rs index 0572fc6254065..4ac203d4301ab 100644 --- a/compiler/crates/relay-compiler/src/build_project/build_resolvers_schema/extract_docblock_ir.rs +++ b/compiler/crates/relay-compiler/src/build_project/build_resolvers_schema/extract_docblock_ir.rs @@ -13,18 +13,18 @@ use docblock_syntax::DocblockAST; use fnv::FnvHashMap; use graphql_syntax::ExecutableDefinition; use relay_config::ProjectName; -use relay_docblock::parse_docblock_ast; use relay_docblock::DocblockIr; use relay_docblock::ParseOptions; use relay_docblock::ResolverFieldDocblockIr; use relay_docblock::ResolverTypeDocblockIr; +use relay_docblock::parse_docblock_ast; use rustc_hash::FxHashMap; +use crate::GraphQLAsts; use crate::compiler_state::CompilerState; use crate::config::Config; use crate::config::ProjectConfig; use crate::docblocks::parse_docblock_asts_from_sources; -use crate::GraphQLAsts; #[derive(Default)] pub struct ExtractedDocblockIr { diff --git a/compiler/crates/relay-compiler/src/build_project/build_resolvers_schema/ir_to_schema.rs b/compiler/crates/relay-compiler/src/build_project/build_resolvers_schema/ir_to_schema.rs index 35c8a313005f5..c03df97f99b6c 100644 --- a/compiler/crates/relay-compiler/src/build_project/build_resolvers_schema/ir_to_schema.rs +++ b/compiler/crates/relay-compiler/src/build_project/build_resolvers_schema/ir_to_schema.rs @@ -8,9 +8,9 @@ use common::DiagnosticsResult; use errors::try_all; use graphql_syntax::SchemaDocument; -use relay_docblock::extend_schema_with_resolver_type_system_definition; use relay_docblock::ResolverFieldDocblockIr; use relay_docblock::ResolverTypeDocblockIr; +use relay_docblock::extend_schema_with_resolver_type_system_definition; use schema::SDLSchema; use super::extract_docblock_ir::AllocatedDocblockIr; diff --git a/compiler/crates/relay-compiler/src/build_project/build_schema.rs b/compiler/crates/relay-compiler/src/build_project/build_schema.rs index 6c0060fe8872d..9d0b93ddea304 100644 --- a/compiler/crates/relay-compiler/src/build_project/build_schema.rs +++ b/compiler/crates/relay-compiler/src/build_project/build_schema.rs @@ -12,19 +12,19 @@ use common::PerfLogEvent; use fnv::FnvHashMap; use relay_config::ProjectName; use relay_docblock::validate_resolver_schema; -use schema::parse_schema_with_extensions; use schema::SDLSchema; use schema::SchemaDocuments; -use schema_validate_lib::validate; +use schema::parse_schema_with_extensions; use schema_validate_lib::SchemaValidationOptions; +use schema_validate_lib::validate; use super::build_resolvers_schema::build_resolver_types_schema_documents; use super::build_resolvers_schema::extend_schema_with_field_ir; use super::build_resolvers_schema::extract_docblock_ir; +use crate::GraphQLAsts; use crate::compiler_state::CompilerState; use crate::config::Config; use crate::config::ProjectConfig; -use crate::GraphQLAsts; pub fn build_schema( compiler_state: &CompilerState, diff --git a/compiler/crates/relay-compiler/src/build_project/generate_artifacts.rs b/compiler/crates/relay-compiler/src/build_project/generate_artifacts.rs index 4558eb8c63597..0d68adca2576c 100644 --- a/compiler/crates/relay-compiler/src/build_project/generate_artifacts.rs +++ b/compiler/crates/relay-compiler/src/build_project/generate_artifacts.rs @@ -15,8 +15,8 @@ use graphql_ir::FragmentDefinition; use graphql_ir::OperationDefinition; use graphql_text_printer::OperationPrinter; use graphql_text_printer::PrinterOptions; -use intern::string_key::StringKey; use intern::Lookup; +use intern::string_key::StringKey; use relay_codegen::QueryID; use relay_config::ResolversSchemaModuleConfig; use relay_transforms::ArtifactSourceKeyData; @@ -57,7 +57,7 @@ pub fn generate_artifacts( ..Default::default() }; let mut operation_printer = OperationPrinter::new(&programs.operation_text, printer_options); - return group_operations(programs).into_values().map(|operations| { + group_operations(programs).into_values().map(|operations| { if let Some(normalization) = operations.normalization { // We have a normalization AST... so we'll move forward with that if let Some(metadata) = SplitOperationMetadata::find(&normalization.directives) @@ -196,7 +196,7 @@ pub fn generate_artifacts( _ => vec![], } ) - .collect(); + .collect() } fn generate_normalization_artifact( @@ -309,7 +309,7 @@ struct OperationGroup<'a> { typegen: Option<&'a Arc>, } -impl<'a> OperationGroup<'a> { +impl OperationGroup<'_> { fn new() -> Self { OperationGroup { normalization: None, diff --git a/compiler/crates/relay-compiler/src/build_project/generate_extra_artifacts.rs b/compiler/crates/relay-compiler/src/build_project/generate_extra_artifacts.rs index e39797efe1101..4ce75458ad1c6 100644 --- a/compiler/crates/relay-compiler/src/build_project/generate_extra_artifacts.rs +++ b/compiler/crates/relay-compiler/src/build_project/generate_extra_artifacts.rs @@ -8,11 +8,11 @@ use relay_transforms::is_operation_preloadable; use schema::SDLSchema; -use super::generate_preloadable_query_parameters_artifact; use super::Artifact; use super::Config; use super::Programs; use super::ProjectConfig; +use super::generate_preloadable_query_parameters_artifact; use crate::ArtifactContent; pub type GenerateExtraArtifactsFn = Box< diff --git a/compiler/crates/relay-compiler/src/build_project/persist_operations.rs b/compiler/crates/relay-compiler/src/build_project/persist_operations.rs index 8652d87c282cb..430a0e28b708c 100644 --- a/compiler/crates/relay-compiler/src/build_project/persist_operations.rs +++ b/compiler/crates/relay-compiler/src/build_project/persist_operations.rs @@ -9,8 +9,8 @@ use std::fs; use std::path::Path; use std::path::PathBuf; -use common::sync::ParallelIterator; use common::PerfLogEvent; +use common::sync::ParallelIterator; use lazy_static::lazy_static; use log::debug; use md5::Digest; @@ -20,13 +20,13 @@ use regex::Regex; use relay_codegen::QueryID; use relay_transforms::Programs; +use crate::Artifact; +use crate::ArtifactContent; +use crate::OperationPersister; use crate::config::ArtifactForPersister; use crate::config::Config; use crate::config::ProjectConfig; use crate::errors::BuildProjectError; -use crate::Artifact; -use crate::ArtifactContent; -use crate::OperationPersister; lazy_static! { static ref RELAY_HASH_REGEX: Regex = Regex::new(r#"@relayHash (\w{32})\n"#).unwrap(); diff --git a/compiler/crates/relay-compiler/src/build_project/project_asts.rs b/compiler/crates/relay-compiler/src/build_project/project_asts.rs index b7522fd200a65..85eed3f9dbea4 100644 --- a/compiler/crates/relay-compiler/src/build_project/project_asts.rs +++ b/compiler/crates/relay-compiler/src/build_project/project_asts.rs @@ -6,9 +6,9 @@ */ use common::Diagnostic; -use dependency_analyzer::get_reachable_ast; use dependency_analyzer::ExecutableDefinitionNameSet; use dependency_analyzer::ReachableAst; +use dependency_analyzer::get_reachable_ast; use fnv::FnvHashMap; use graphql_ir::FragmentDefinitionName; use graphql_ir::FragmentDefinitionNameSet; @@ -20,8 +20,8 @@ use relay_transforms::get_resolver_fragment_dependency_name; use schema::SDLSchema; use schema::Schema; -use crate::errors::BuildProjectError; use crate::GraphQLAsts; +use crate::errors::BuildProjectError; pub struct ProjectAsts { pub changed_names: ExecutableDefinitionNameSet, diff --git a/compiler/crates/relay-compiler/src/build_project/validate.rs b/compiler/crates/relay-compiler/src/build_project/validate.rs index 6406ec2be24c8..257605ffd474f 100644 --- a/compiler/crates/relay-compiler/src/build_project/validate.rs +++ b/compiler/crates/relay-compiler/src/build_project/validate.rs @@ -5,19 +5,22 @@ * LICENSE file in the root directory of this source tree. */ -use common::escalate_and_check; use common::CriticalDiagnostics; use common::DiagnosticsResult; use common::StableDiagnostics; use common::WithDiagnostics; +use common::escalate_and_check; use errors::try_all; use graphql_ir::Program; use relay_config::ProjectConfig; +use relay_transforms::ValidateVariablesOptions; use relay_transforms::disallow_circular_no_inline_fragments; use relay_transforms::disallow_readtime_features_in_mutations; +use relay_transforms::disallow_required_on_non_null_field; use relay_transforms::disallow_reserved_aliases; use relay_transforms::disallow_typename_on_root; use relay_transforms::validate_assignable_directive; +use relay_transforms::validate_client_schema_extensions_use_catch; use relay_transforms::validate_connections; use relay_transforms::validate_fragment_alias_conflict; use relay_transforms::validate_global_variable_names; @@ -34,7 +37,6 @@ use relay_transforms::validate_unused_fragment_variables; use relay_transforms::validate_unused_variables; use relay_transforms::validate_updatable_directive; use relay_transforms::validate_updatable_fragment_spread; -use relay_transforms::ValidateVariablesOptions; pub type AdditionalValidations = Box DiagnosticsResult<()> + Sync + Send>; @@ -46,11 +48,16 @@ pub fn validate_reader( project_config: &ProjectConfig, additional_validations: &Option, ) -> DiagnosticsResult> { - let output = try_all(vec![if let Some(ref validate) = additional_validations { - validate(program, project_config) - } else { - Ok(()) - }]); + let output = try_all(vec![ + // This validation is in this list because it depends upon + // metadata added by the required_directive transform. + disallow_required_on_non_null_field(program), + if let Some(validate) = additional_validations { + validate(program, project_config) + } else { + Ok(()) + }, + ]); transform_errors(output, project_config) } @@ -72,10 +79,11 @@ pub fn validate( validate_global_variable_names(program), validate_module_names(program), validate_exhaustive_directive(program, project_config), + validate_client_schema_extensions_use_catch(program), validate_no_inline_fragments_with_raw_response_type(program), disallow_typename_on_root(program), validate_static_args(program), - if let Some(ref validate) = additional_validations { + if let Some(validate) = additional_validations { validate(program, project_config) } else { Ok(()) diff --git a/compiler/crates/relay-compiler/src/compiler.rs b/compiler/crates/relay-compiler/src/compiler.rs index 132a5cfa4d77b..221351805dd60 100644 --- a/compiler/crates/relay-compiler/src/compiler.rs +++ b/compiler/crates/relay-compiler/src/compiler.rs @@ -29,10 +29,11 @@ use tokio::sync::Notify; use tokio::task; use tokio::task::JoinHandle; +use crate::FileSourceResult; use crate::artifact_map::ArtifactSourceKey; +use crate::build_project::BuildProjectFailure; use crate::build_project::build_project; use crate::build_project::commit_project; -use crate::build_project::BuildProjectFailure; use crate::compiler_state::ArtifactMapKind; use crate::compiler_state::CompilerState; use crate::compiler_state::DocblockSources; @@ -45,7 +46,6 @@ use crate::file_source::FileSourceSubscriptionNextChange; use crate::file_source::LocatedDocblockSource; use crate::graphql_asts::GraphQLAsts; use crate::red_to_green::RedToGreen; -use crate::FileSourceResult; pub struct Compiler where @@ -500,12 +500,15 @@ fn get_removed_docblock_artifact_source_keys( fn get_removed_full_sources(full_sources: Option<&FullSources>) -> Vec { let mut removed_full_sources: Vec = vec![]; if let Some(full_sources) = full_sources { - for (file, source) in full_sources.pending.iter() { - if source.is_empty() { - if let Some(text) = full_sources.processed.get(file) { + for (file, pending_source_text) in full_sources.pending.iter() { + if let Some(processed_source_text) = full_sources.processed.get(file) { + // Full sources are keyed by a hash of their contents. + // Therefore if the contents of a file have changed we must + // treat the hash of the old version of that file as removed. + if pending_source_text != processed_source_text { // For now, full sources are only used for ResolverHash removed_full_sources.push(ArtifactSourceKey::ResolverHash( - ResolverSourceHash::new(text), + ResolverSourceHash::new(processed_source_text), )) } } diff --git a/compiler/crates/relay-compiler/src/compiler_state.rs b/compiler/crates/relay-compiler/src/compiler_state.rs index 15a72c435bdbd..3e761aeb5d711 100644 --- a/compiler/crates/relay-compiler/src/compiler_state.rs +++ b/compiler/crates/relay-compiler/src/compiler_state.rs @@ -10,8 +10,8 @@ //! This module provides a way to manage the state of the Relay compiler, including the current project, //! schema, and other configuration options. It also provides methods for updating the state and //! generating artifacts. -use std::collections::hash_map::Entry; use std::collections::HashMap; +use std::collections::hash_map::Entry; use std::env; use std::fmt; use std::fs::File as FsFile; @@ -51,9 +51,6 @@ use crate::artifact_map::ArtifactSourceKey; use crate::config::Config; use crate::errors::Error; use crate::errors::Result; -use crate::file_source::categorize_files; -use crate::file_source::extract_javascript_features_from_file; -use crate::file_source::read_file_to_string; use crate::file_source::Clock; use crate::file_source::File; use crate::file_source::FileGroup; @@ -62,6 +59,9 @@ use crate::file_source::LocatedDocblockSource; use crate::file_source::LocatedGraphQLSource; use crate::file_source::LocatedJavascriptSourceFeatures; use crate::file_source::SourceControlUpdateStatus; +use crate::file_source::categorize_files; +use crate::file_source::extract_javascript_features_from_file; +use crate::file_source::read_file_to_string; /// Set of project names. #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash, JsonSchema)] @@ -186,8 +186,7 @@ impl IncrementalSources { entry.insert(value.clone()); } Entry::Vacant(vacant) => { - if !value.is_empty() || self.processed.get(key).map_or(false, |v| !v.is_empty()) - { + if !value.is_empty() || self.processed.get(key).is_some_and(|v| !v.is_empty()) { vacant.insert(value.clone()); } } @@ -408,7 +407,7 @@ impl CompilerState { pub fn project_has_pending_changes(&self, project_name: ProjectName) -> bool { self.graphql_sources .get(&project_name) - .map_or(false, |sources| !sources.pending.is_empty()) + .is_some_and(|sources| !sources.pending.is_empty()) || self.project_has_pending_schema_changes(project_name) || self.dirty_artifact_paths.contains_key(&project_name) } @@ -416,19 +415,19 @@ impl CompilerState { pub fn project_has_pending_schema_changes(&self, project_name: ProjectName) -> bool { self.schemas .get(&project_name) - .map_or(false, |sources| !sources.pending.is_empty()) + .is_some_and(|sources| !sources.pending.is_empty()) || self .extensions .get(&project_name) - .map_or(false, |sources| !sources.pending.is_empty()) + .is_some_and(|sources| !sources.pending.is_empty()) || self .docblocks .get(&project_name) - .map_or(false, |sources| !sources.pending.is_empty()) + .is_some_and(|sources| !sources.pending.is_empty()) || self .full_sources .get(&project_name) - .map_or(false, |sources| !sources.pending.is_empty()) + .is_some_and(|sources| !sources.pending.is_empty()) } pub fn has_processed_changes(&self) -> bool { @@ -914,11 +913,11 @@ fn extract_sources( /// which requires "self descriptive" serialization formats and `bincode` does not /// support those enums. mod clock_json_string { + use serde::Deserializer; + use serde::Serializer; use serde::de::Error as DeserializationError; use serde::de::Visitor; use serde::ser::Error as SerializationError; - use serde::Deserializer; - use serde::Serializer; use crate::file_source::Clock; @@ -940,7 +939,7 @@ mod clock_json_string { } struct JSONStringVisitor; - impl<'de> Visitor<'de> for JSONStringVisitor { + impl Visitor<'_> for JSONStringVisitor { type Value = Option; fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/compiler/crates/relay-compiler/src/config.rs b/compiler/crates/relay-compiler/src/config.rs index 966a735293f62..6e3482e003e5c 100644 --- a/compiler/crates/relay-compiler/src/config.rs +++ b/compiler/crates/relay-compiler/src/config.rs @@ -51,24 +51,26 @@ pub use relay_config::TypegenLanguage; use relay_docblock::DocblockIr; use relay_saved_state_loader::SavedStateLoader; use relay_transforms::CustomTransformsConfig; -use schemars::gen::SchemaSettings; -use schemars::gen::{self}; use schemars::JsonSchema; -use serde::de::Error as DeError; +use schemars::SchemaGenerator; +use schemars::generate::SchemaSettings; +use schemars::{self}; use serde::Deserialize; use serde::Deserializer; use serde::Serialize; +use serde::de::Error as DeError; use serde_json::Value; use sha1::Digest; use sha1::Sha1; use watchman_client::pdu::ScmAwareClockData; +use crate::GraphQLAsts; +use crate::build_project::AdditionalValidations; use crate::build_project::artifact_writer::ArtifactFileWriter; use crate::build_project::artifact_writer::ArtifactWriter; use crate::build_project::generate_extra_artifacts::GenerateExtraArtifactsFn; use crate::build_project::rescript_generate_extra_files::rescript_generate_extra_artifacts; use crate::build_project::get_artifacts_file_hash_map::GetArtifactsFileHashMapFn; -use crate::build_project::AdditionalValidations; use crate::compiler_state::CompilerState; use crate::compiler_state::DeserializableProjectSet; use crate::compiler_state::ProjectSet; @@ -78,7 +80,6 @@ use crate::errors::Result; use crate::source_control_for_root; use crate::status_reporter::ConsoleStatusReporter; use crate::status_reporter::StatusReporter; -use crate::GraphQLAsts; pub type FnvIndexMap = IndexMap; @@ -190,7 +191,7 @@ pub struct Config { /// Runs in `try_saved_state` when the compiler state is initialized from saved state. pub update_compiler_state_from_saved_state: UpdateCompilerStateFromSavedState, - // Allow incremental build for some schema changes + /// Allow incremental build for some schema changes pub has_schema_change_incremental_build: bool, /// A custom function to extract resolver Dockblock IRs from sources @@ -309,7 +310,7 @@ impl Config { ), }), Err(error) => Err(Error::ConfigError { - details: format!("Error searching config: {}", error), + details: format!("Error searching config: {error}"), }), } } @@ -431,6 +432,7 @@ impl Config { ), rollout: config_file_project.rollout, js_module_format: config_file_project.js_module_format, + relativize_js_module_paths: config_file_project.relativize_js_module_paths, module_import_config: config_file_project.module_import_config, diagnostic_report_config: config_file_project.diagnostic_report_config, resolvers_schema_module: config_file_project.resolvers_schema_module, @@ -616,6 +618,138 @@ impl Config { } } } + + /// Compute all root paths that we need to query. All files relevant to the + /// compiler should be in these directories. + pub fn get_all_roots(&self) -> Vec { + let source_roots = self.get_source_roots(); + let extra_sources_roots = self.get_generated_sources_roots(); + let output_roots = self.get_output_dir_paths(); + let extension_roots = self.get_extension_roots(); + let schema_file_roots = self.get_schema_file_roots(); + let schema_dir_roots = self.get_schema_dir_paths(); + + unify_roots( + source_roots + .into_iter() + .chain(extra_sources_roots) + .chain(output_roots) + .chain(extension_roots) + .chain(schema_file_roots) + .chain(schema_dir_roots) + .collect(), + ) + } + + /// Returns all root directories of JS source files for the config. + pub fn get_source_roots(&self) -> Vec { + self.sources.keys().cloned().collect() + } + + /// Returns all root directories of JS source files for the config. + pub fn get_generated_sources_roots(&self) -> Vec { + self.generated_sources.keys().cloned().collect() + } + + /// Returns all root directories of GraphQL schema extension files for the + /// config. + pub fn get_extension_roots(&self) -> Vec { + self.projects + .values() + .flat_map(|project_config| project_config.schema_extensions.iter().cloned()) + .collect() + } + + /// Returns all output and extra artifact output directories for the config. + pub fn get_output_dir_paths(&self) -> Vec { + let output_dirs = self + .projects + .values() + .filter_map(|project_config| project_config.output.clone()); + + let extra_artifact_output_dirs = self + .projects + .values() + .filter_map(|project_config| project_config.extra_artifacts_output.clone()); + + output_dirs.chain(extra_artifact_output_dirs).collect() + } + + /// Returns all paths that contain GraphQL schema files for the config. + pub fn get_schema_file_paths(&self) -> Vec { + self.projects + .values() + .filter_map(|project_config| match &project_config.schema_location { + SchemaLocation::File(schema_file) => Some(schema_file.clone()), + SchemaLocation::Directory(_) => None, + }) + .collect() + } + + /// Returns all GraphQL schema directories for the config. + pub fn get_schema_dir_paths(&self) -> Vec { + self.projects + .values() + .filter_map(|project_config| match &project_config.schema_location { + SchemaLocation::File(_) => None, + SchemaLocation::Directory(schema_dir) => Some(schema_dir.clone()), + }) + .collect() + } + + /// Returns root directories that contain GraphQL schema files. + pub fn get_schema_file_roots(&self) -> impl Iterator + use<> { + self.get_schema_file_paths().into_iter().map(|schema_path| { + schema_path + .parent() + .expect("A schema in the project root directory is currently not supported.") + .to_owned() + }) + } +} + +/// Finds the roots of a set of paths. This filters any paths +/// that are a subdirectory of other paths in the input. +fn unify_roots(mut paths: Vec) -> Vec { + paths.sort(); + let mut roots = Vec::new(); + for path in paths { + match roots.last() { + Some(prev) if path.starts_with(prev) => { + // skip + } + _ => { + roots.push(path); + } + } + } + roots +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_unify_roots() { + assert_eq!(unify_roots(vec![]).len(), 0); + assert_eq!( + unify_roots(vec!["Apps".into(), "Libraries".into()]), + &[PathBuf::from("Apps"), PathBuf::from("Libraries")] + ); + assert_eq!( + unify_roots(vec!["Apps".into(), "Apps/Foo".into()]), + &[PathBuf::from("Apps")] + ); + assert_eq!( + unify_roots(vec!["Apps/Foo".into(), "Apps".into()]), + &[PathBuf::from("Apps")] + ); + assert_eq!( + unify_roots(vec!["Foo".into(), "Foo2".into()]), + &[PathBuf::from("Foo"), PathBuf::from("Foo2"),] + ); + } } impl fmt::Debug for Config { @@ -692,6 +826,10 @@ fn get_default_excludes() -> Vec { ] } +fn default_true() -> bool { + true +} + /// Schema of the compiler configuration JSON file. #[derive(Debug, Serialize, Deserialize, Default, JsonSchema)] #[serde(deny_unknown_fields, rename_all = "camelCase")] @@ -734,6 +872,8 @@ pub struct MultiProjectConfigFile { /// Configuration of projects to compile. projects: FnvIndexMap, + /// Enable and disable experimental or legacy behaviors. + /// WARNING! These are not stable and may change at any time. #[serde(default)] feature_flags: FeatureFlags, @@ -788,24 +928,27 @@ pub struct SingleProjectConfigFile { /// This config option is here to define the name of that special variable pub is_dev_variable_name: Option, - /// Name of the command that runs the relay compiler + /// Name of the command that runs the relay compiler. This will be added at + /// the top of generated code to let readers know how to regenerate the file. pub codegen_command: Option, - /// Formatting style for generated files. + /// Import/export style to use in generated JavaScript modules. pub js_module_format: JsModuleFormat, - /// Extra configuration for the schema itself. + /// Whether to treat all JS module names as relative to './' (true) or not. + /// default: true + #[serde(default = "default_true")] + pub relativize_js_module_paths: bool, + + /// Extra configuration for the GraphQL schema itself. pub schema_config: SchemaConfig, /// Configuration for @module #[serde(default)] pub module_import_config: ModuleImportConfig, - /// Added in 13.1.1 to customize Final/Compat mode in the single project config file - /// Removed in 14.0.0 - #[serde(default)] - pub typegen_phase: Option, - + /// Enable and disable experimental or legacy behaviors. + /// WARNING! These are not stable and may change at any time. #[serde(default)] pub feature_flags: Option, @@ -817,6 +960,10 @@ pub struct SingleProjectConfigFile { /// Opt out of source control checks/integration. #[serde(default)] pub no_source_control: Option, + + /// A placeholder for allowing extra information in the config file + #[serde(default)] + pub extra: serde_json::Value, } impl Default for SingleProjectConfigFile { @@ -834,13 +981,14 @@ impl Default for SingleProjectConfigFile { persist_config: None, is_dev_variable_name: None, codegen_command: None, - js_module_format: JsModuleFormat::Haste, - typegen_phase: None, + js_module_format: JsModuleFormat::CommonJS, + relativize_js_module_paths: true, feature_flags: None, module_import_config: Default::default(), resolvers_schema_module: Default::default(), input_unions: Default::default(), no_source_control: Some(false), + extra: Default::default(), } } } @@ -873,12 +1021,12 @@ impl SingleProjectConfigFile { } })?, ); - for extension_dir in self.schema_extensions.iter() { + for extension_path in self.schema_extensions.iter() { paths.push( - canonicalize(root_dir.join(extension_dir.clone())).map_err(|_| { - ConfigValidationError::ExtensionDirNotExistent { + canonicalize(root_dir.join(extension_path.clone())).map_err(|_| { + ConfigValidationError::ExtensionPathNotExistent { project_name: self.project_name, - extension_dir: extension_dir.clone(), + extension_path: extension_path.clone(), } })?, ); @@ -888,16 +1036,6 @@ impl SingleProjectConfigFile { } fn create_multi_project_config(self, config_path: &Path) -> Result { - if self.typegen_phase.is_some() { - return Err(Error::ConfigFileValidation { - config_path: config_path.into(), - validation_errors: vec![ConfigValidationError::RemovedConfigField { - name: "typegenPhase", - action: "Please remove the option and update type imports from generated files to new names.", - }], - }); - } - let current_dir = std::env::current_dir().unwrap(); let common_root_dir = self.get_common_root(current_dir.clone()).map_err(|err| { Error::ConfigFileValidation { @@ -934,6 +1072,7 @@ impl SingleProjectConfigFile { module_import_config: self.module_import_config, resolvers_schema_module: self.resolvers_schema_module, input_unions: self.input_unions, + extra: self.extra, ..Default::default() }; @@ -958,8 +1097,14 @@ impl SingleProjectConfigFile { } } +/// Relay's configuration file. Supports a single project config for simple use +/// cases and a multi-project config for cases where multiple projects live in +/// the same repository. +/// +/// In general, start with the SingleProjectConfigFile. #[derive(Serialize, JsonSchema)] #[serde(untagged)] +#[allow(clippy::large_enum_variant)] pub enum ConfigFile { /// Base case configuration (mostly of OSS) where the project /// have single schema, and single source directory @@ -973,9 +1118,8 @@ pub enum ConfigFile { impl ConfigFile { pub fn json_schema() -> String { - let mut settings: SchemaSettings = Default::default(); - settings.inline_subschemas = true; - let generator = gen::SchemaGenerator::from(settings); + let settings: SchemaSettings = Default::default(); + let generator = SchemaGenerator::from(settings); let schema = generator.into_root_schema_for::(); serde_json::to_string_pretty(&schema).unwrap() } @@ -991,11 +1135,10 @@ impl<'de> Deserialize<'de> for ConfigFile { Err(single_project_error) => { let error_message = format!( r#"The config file cannot be parsed as a single-project config file due to: - - {:?}. + - {single_project_error:?}. It also cannot be a multi-project config file due to: - - {:?}."#, - single_project_error, multi_project_error, + - {multi_project_error:?}."# ); Err(DeError::custom(error_message)) @@ -1079,6 +1222,8 @@ pub struct ConfigFileProject { #[serde(default)] extra: serde_json::Value, + /// Enable and disable experimental or legacy behaviors. + /// WARNING! These are not stable and may change at any time. #[serde(default)] pub feature_flags: Option, @@ -1087,21 +1232,34 @@ pub struct ConfigFileProject { #[serde(default)] pub rollout: Rollout, + /// Import/export style to use in generated JavaScript modules. #[serde(default)] pub js_module_format: JsModuleFormat, + /// Whether to treat all JS module names as relative to './' (true) or not. + /// default: true + #[serde(default = "default_true")] + pub relativize_js_module_paths: bool, + + /// Extra configuration for the GraphQL schema itself. #[serde(default)] pub schema_config: SchemaConfig, + /// Configuration for the @module GraphQL directive. #[serde(default)] pub module_import_config: ModuleImportConfig, + /// Threshold for diagnostics to be critical to the compiler's execution. + /// All diagnostic with severities at and below this level will cause the + /// compiler to fatally exit. #[serde(default)] pub diagnostic_report_config: DiagnosticReportConfig, #[serde(default)] pub resolvers_schema_module: Option, + /// Name of the command that runs the relay compiler. This will be added at + /// the top of generated code to let readers know how to regenerate the file. #[serde(default)] pub codegen_command: Option, diff --git a/compiler/crates/relay-compiler/src/docblocks.rs b/compiler/crates/relay-compiler/src/docblocks.rs index 95fcf1cd772f6..904e8eb7940ae 100644 --- a/compiler/crates/relay-compiler/src/docblocks.rs +++ b/compiler/crates/relay-compiler/src/docblocks.rs @@ -9,8 +9,8 @@ use std::path::Path; use common::DiagnosticsResult; use common::SourceLocationKey; -use docblock_syntax::parse_docblock; use docblock_syntax::DocblockAST; +use docblock_syntax::parse_docblock; use errors::try_all; use relay_docblock::resolver_maybe_defining_type; diff --git a/compiler/crates/relay-compiler/src/errors.rs b/compiler/crates/relay-compiler/src/errors.rs index 0f45dcb71ba1b..4c2bc417fae8a 100644 --- a/compiler/crates/relay-compiler/src/errors.rs +++ b/compiler/crates/relay-compiler/src/errors.rs @@ -218,11 +218,11 @@ pub enum ConfigValidationError { }, #[error( - "The `schemaExtensions` configured for project `{project_name}` does not exist at `{extension_dir}`." + "The `schemaExtensions` configured for project `{project_name}` does not exist at `{extension_path}`." )] - ExtensionDirNotExistent { + ExtensionPathNotExistent { project_name: ProjectName, - extension_dir: PathBuf, + extension_path: PathBuf, }, #[error( diff --git a/compiler/crates/relay-compiler/src/file_source.rs b/compiler/crates/relay-compiler/src/file_source.rs index 37529984360cd..89307a1ca8436 100644 --- a/compiler/crates/relay-compiler/src/file_source.rs +++ b/compiler/crates/relay-compiler/src/file_source.rs @@ -27,8 +27,8 @@ use std::sync::Arc; use common::PerfLogEvent; use common::PerfLogger; use external_file_source::ExternalFileSource; -pub use file_categorizer::categorize_files; pub use file_categorizer::FileCategorizer; +pub use file_categorizer::categorize_files; pub use file_group::FileGroup; use graphql_watchman::WatchmanFileSourceResult; use graphql_watchman::WatchmanFileSourceSubscription; @@ -42,13 +42,13 @@ pub use watchman_client::prelude::Clock; use watchman_file_source::WatchmanFileSource; pub use self::external_file_source::ExternalFileSourceResult; -pub use self::extract_graphql::extract_javascript_features_from_file; -pub use self::extract_graphql::source_for_location; pub use self::extract_graphql::FsSourceReader; pub use self::extract_graphql::LocatedDocblockSource; pub use self::extract_graphql::LocatedGraphQLSource; pub use self::extract_graphql::LocatedJavascriptSourceFeatures; pub use self::extract_graphql::SourceReader; +pub use self::extract_graphql::extract_javascript_features_from_file; +pub use self::extract_graphql::source_for_location; use self::walk_dir_file_source::WalkDirFileSource; use self::walk_dir_file_source::WalkDirFileSourceResult; use crate::compiler_state::CompilerState; diff --git a/compiler/crates/relay-compiler/src/file_source/external_file_source.rs b/compiler/crates/relay-compiler/src/file_source/external_file_source.rs index 6406e6cd5bfd9..fb383daac8bf1 100644 --- a/compiler/crates/relay-compiler/src/file_source/external_file_source.rs +++ b/compiler/crates/relay-compiler/src/file_source/external_file_source.rs @@ -15,11 +15,11 @@ use rayon::prelude::*; use serde::Deserialize; use super::File; +use crate::FileSourceResult; use crate::compiler_state::CompilerState; use crate::config::Config; use crate::errors::Error; use crate::errors::Result; -use crate::FileSourceResult; /// The purpose of this module is to handle saved state and list of changed files /// from the external source, and not from the watchman diff --git a/compiler/crates/relay-compiler/src/file_source/extract_graphql.rs b/compiler/crates/relay-compiler/src/file_source/extract_graphql.rs index db9a6c501a289..f1a7f5b37c52b 100644 --- a/compiler/crates/relay-compiler/src/file_source/extract_graphql.rs +++ b/compiler/crates/relay-compiler/src/file_source/extract_graphql.rs @@ -16,9 +16,9 @@ use intern::Lookup; use serde::Deserialize; use serde::Serialize; -use super::read_file_to_string; use super::File; use super::FileSourceResult; +use super::read_file_to_string; use crate::errors::Result; use crate::file_source::Config; diff --git a/compiler/crates/relay-compiler/src/file_source/file_categorizer.rs b/compiler/crates/relay-compiler/src/file_source/file_categorizer.rs index 95c75ad24f7c0..46eba7191d499 100644 --- a/compiler/crates/relay-compiler/src/file_source/file_categorizer.rs +++ b/compiler/crates/relay-compiler/src/file_source/file_categorizer.rs @@ -7,8 +7,8 @@ use core::panic; use std::borrow::Cow; -use std::collections::hash_map::Entry; use std::collections::HashMap; +use std::collections::hash_map::Entry; use std::ffi::OsStr; use std::path::Component; use std::path::Path; @@ -21,13 +21,13 @@ use rayon::iter::IntoParallelRefIterator; use relay_config::ProjectName; use relay_typegen::TypegenLanguage; -use super::file_filter::FileFilter; use super::File; use super::FileGroup; +use super::file_filter::FileFilter; +use crate::FileSourceResult; use crate::compiler_state::ProjectSet; use crate::config::Config; use crate::config::SchemaLocation; -use crate::FileSourceResult; /// The watchman query returns a list of files, but for the compiler we /// need to categorize these files into multiple groups of files like diff --git a/compiler/crates/relay-compiler/src/file_source/source_control_update_status.rs b/compiler/crates/relay-compiler/src/file_source/source_control_update_status.rs index 3b30070aae80a..bfc790e180781 100644 --- a/compiler/crates/relay-compiler/src/file_source/source_control_update_status.rs +++ b/compiler/crates/relay-compiler/src/file_source/source_control_update_status.rs @@ -5,9 +5,9 @@ * LICENSE file in the root directory of this source tree. */ +use std::sync::Arc; use std::sync::atomic::AtomicI8; use std::sync::atomic::Ordering; -use std::sync::Arc; #[derive(Clone, Default, Debug)] /// This structure is representing the state of the current source control update. diff --git a/compiler/crates/relay-compiler/src/file_source/walk_dir_file_source.rs b/compiler/crates/relay-compiler/src/file_source/walk_dir_file_source.rs index 6a1280b10ecfb..be00017a5ab4e 100644 --- a/compiler/crates/relay-compiler/src/file_source/walk_dir_file_source.rs +++ b/compiler/crates/relay-compiler/src/file_source/walk_dir_file_source.rs @@ -17,10 +17,10 @@ use relay_typegen::TypegenLanguage; use walkdir::WalkDir; use super::File; +use crate::FileSourceResult; use crate::compiler_state::CompilerState; use crate::config::Config; use crate::errors::Result; -use crate::FileSourceResult; #[derive(Debug)] pub struct WalkDirFileSourceResult { @@ -80,8 +80,11 @@ impl WalkDirFileSource { } fn find_files(&self) -> Vec { - WalkDir::new(self.config.root_dir.clone()) - .into_iter() + self.config + .get_all_roots() + .iter() + .map(|source| self.config.root_dir.join(source)) + .flat_map(WalkDir::new) .filter_map(|entry| { let dir_entry = entry.ok()?; let relative_path = dir_entry diff --git a/compiler/crates/relay-compiler/src/file_source/watchman_file_source.rs b/compiler/crates/relay-compiler/src/file_source/watchman_file_source.rs index 9bb046b549f06..c4382a5c06e5b 100644 --- a/compiler/crates/relay-compiler/src/file_source/watchman_file_source.rs +++ b/compiler/crates/relay-compiler/src/file_source/watchman_file_source.rs @@ -20,9 +20,8 @@ use relay_saved_state_loader::SavedStateLoader; pub use watchman_client::prelude::Clock; use watchman_client::prelude::*; -use super::watchman_query_builder::get_all_roots; -use super::watchman_query_builder::get_watchman_expr; use super::FileSourceResult; +use super::watchman_query_builder::get_watchman_expr; use crate::compiler_state::CompilerState; use crate::config::Config; use crate::errors::Error; @@ -233,9 +232,11 @@ impl WatchmanFileSource { let since = Some(scm_since.clone()); let root = self.resolved_root.clone(); + let saved_state_query_timer = perf_logger_event.start("saved_state_info_query_time"); let saved_state_result = query_file_result(&self.config, &self.client, &root, since, true) .await .map_err(|_| "query failed")?; + perf_logger_event.stop(saved_state_query_timer); let since = Some(scm_since.clone()); let config = Arc::clone(&self.config); @@ -292,9 +293,12 @@ impl WatchmanFileSource { } // Then await the changed files query. + let saved_state_await_changed_files_time = + perf_logger_event.start("saved_state_await_changed_files_time"); let file_source_result = changed_files_result_future .await .map_err(|_| "query failed")??; + perf_logger_event.stop(saved_state_await_changed_files_time); compiler_state .pending_file_source_changes @@ -305,17 +309,19 @@ impl WatchmanFileSource { if let Some(update_compiler_state_from_saved_state) = &self.config.update_compiler_state_from_saved_state { + let update_compiler_state_from_saved_state_time = + perf_logger_event.start("update_compiler_state_from_saved_state_time"); update_compiler_state_from_saved_state(&mut compiler_state, &self.config); + perf_logger_event.stop(update_compiler_state_from_saved_state_time); } - if let Err(parse_error) = perf_logger_event.time("merge_file_source_changes", || { + match perf_logger_event.time("merge_file_source_changes", || { let result = compiler_state.merge_file_source_changes(&self.config, perf_logger, true); perf_logger_event.stop(try_saved_state_event); result }) { - Ok(Err(parse_error)) - } else { - Ok(Ok(compiler_state)) + Err(parse_error) => Ok(Err(parse_error)), + _ => Ok(Ok(compiler_state)), } } } @@ -345,7 +351,8 @@ async fn query_file_result( ..Default::default() } } else { - let query_roots = get_all_roots(config) + let query_roots = config + .get_all_roots() .into_iter() .map(PathGeneratorElement::RecursivePath) .collect(); diff --git a/compiler/crates/relay-compiler/src/file_source/watchman_query_builder.rs b/compiler/crates/relay-compiler/src/file_source/watchman_query_builder.rs index ef34780cb284a..ad6f19f0744ee 100644 --- a/compiler/crates/relay-compiler/src/file_source/watchman_query_builder.rs +++ b/compiler/crates/relay-compiler/src/file_source/watchman_query_builder.rs @@ -14,7 +14,6 @@ use watchman_client::prelude::*; use crate::compiler_state::ProjectSet; use crate::config::Config; -use crate::config::SchemaLocation; type FnvIndexMap = IndexMap; @@ -22,19 +21,33 @@ pub fn get_watchman_expr(config: &Config) -> Expr { let mut sources_conditions = vec![expr_any(get_sources_dir_exprs(config, &config.sources))]; // not excluded by any glob if !config.excludes.is_empty() { - sources_conditions.push(Expr::Not(Box::new(expr_any( - config - .excludes - .iter() - .map(|item| { - Expr::Match(MatchTerm { - glob: item.into(), - wholename: true, - ..Default::default() - }) - }) - .collect(), - )))); + let mut excludes = vec![]; + let mut exclude_negates = vec![]; + for exclude in config.excludes.iter() { + if let Some(negate) = exclude.strip_prefix("!") { + exclude_negates.push(Expr::Match(MatchTerm { + glob: negate.into(), + wholename: true, + ..Default::default() + })); + } else { + excludes.push(Expr::Match(MatchTerm { + glob: exclude.into(), + wholename: true, + ..Default::default() + })); + } + } + + let mut excluded_expr = expr_any(excludes); + if !exclude_negates.is_empty() { + excluded_expr = Expr::All(vec![ + excluded_expr, + Expr::Not(Box::new(expr_any(exclude_negates))), + ]); + } + + sources_conditions.push(Expr::Not(Box::new(excluded_expr))); } let sources_expr = Expr::All(sources_conditions); @@ -45,30 +58,30 @@ pub fn get_watchman_expr(config: &Config) -> Expr { expressions.push(expr_any(generated_sources_dir_exprs)); } - let output_dir_paths = get_output_dir_paths(config); + let output_dir_paths = config.get_output_dir_paths(); if !output_dir_paths.is_empty() { let output_dir_expr = expr_files_in_dirs(output_dir_paths); expressions.push(output_dir_expr); } - let schema_file_paths = get_schema_file_paths(config); + let schema_file_paths = config.get_schema_file_paths(); if !schema_file_paths.is_empty() { let schema_file_expr = Expr::Name(NameTerm { - paths: get_schema_file_paths(config), + paths: config.get_schema_file_paths(), wholename: true, }); expressions.push(schema_file_expr); } - let schema_dir_paths = get_schema_dir_paths(config); + let schema_dir_paths = config.get_schema_dir_paths(); if !schema_dir_paths.is_empty() { let schema_dir_expr = expr_graphql_files_in_dirs(schema_dir_paths); expressions.push(schema_dir_expr); } - let extension_roots = get_extension_roots(config); + let extension_roots = config.get_extension_roots(); if !extension_roots.is_empty() { - let extensions_expr = expr_graphql_files_in_dirs(extension_roots); + let extensions_expr = expr_graphql_file_or_dir_contents(extension_roots); expressions.push(extensions_expr); } @@ -123,98 +136,6 @@ fn get_project_file_ext_expr(typegen_language: TypegenLanguage) -> Expr { }) } -/// Compute all root paths that we need to query Watchman with. All files -/// relevant to the compiler should be in these directories. -pub fn get_all_roots(config: &Config) -> Vec { - let source_roots = get_source_roots(config); - let extra_sources_roots = get_generated_sources_roots(config); - let output_roots = get_output_dir_paths(config); - let extension_roots = get_extension_roots(config); - let schema_file_roots = get_schema_file_roots(config); - let schema_dir_roots = get_schema_dir_paths(config); - unify_roots( - source_roots - .into_iter() - .chain(extra_sources_roots) - .chain(output_roots) - .chain(extension_roots) - .chain(schema_file_roots) - .chain(schema_dir_roots) - .collect(), - ) -} - -/// Returns all root directories of JS source files for the config. -fn get_source_roots(config: &Config) -> Vec { - config.sources.keys().cloned().collect() -} - -/// Returns all root directories of JS source files for the config. -fn get_generated_sources_roots(config: &Config) -> Vec { - config.generated_sources.keys().cloned().collect() -} - -/// Returns all root directories of GraphQL schema extension files for the -/// config. -fn get_extension_roots(config: &Config) -> Vec { - config - .projects - .values() - .flat_map(|project_config| project_config.schema_extensions.iter().cloned()) - .collect() -} - -/// Returns all output and extra artifact output directories for the config. -fn get_output_dir_paths(config: &Config) -> Vec { - let output_dirs = config - .projects - .values() - .filter_map(|project_config| project_config.output.clone()); - - let extra_artifact_output_dirs = config - .projects - .values() - .filter_map(|project_config| project_config.extra_artifacts_output.clone()); - - output_dirs.chain(extra_artifact_output_dirs).collect() -} - -/// Returns all paths that contain GraphQL schema files for the config. -fn get_schema_file_paths(config: &Config) -> Vec { - config - .projects - .values() - .filter_map(|project_config| match &project_config.schema_location { - SchemaLocation::File(schema_file) => Some(schema_file.clone()), - SchemaLocation::Directory(_) => None, - }) - .collect() -} - -/// Returns all GraphQL schema directories for the config. -fn get_schema_dir_paths(config: &Config) -> Vec { - config - .projects - .values() - .filter_map(|project_config| match &project_config.schema_location { - SchemaLocation::File(_) => None, - SchemaLocation::Directory(schema_dir) => Some(schema_dir.clone()), - }) - .collect() -} - -/// Returns root directories that contain GraphQL schema files. -fn get_schema_file_roots(config: &Config) -> impl Iterator { - get_schema_file_paths(config) - .into_iter() - .map(|schema_path| { - schema_path - .parent() - .expect("A schema in the project root directory is currently not supported.") - .to_owned() - }) -} - fn expr_files_in_dirs(roots: Vec) -> Expr { expr_any( roots @@ -226,13 +147,27 @@ fn expr_files_in_dirs(roots: Vec) -> Expr { fn expr_graphql_files_in_dirs(roots: Vec) -> Expr { Expr::All(vec![ - // ending in *.graphql + // ending in *.graphql or *.gql Expr::Suffix(vec!["graphql".into(), "gql".into()]), // in one of the extension directories expr_files_in_dirs(roots), ]) } +// Expression to get all graphql items by path or path of containing folder. +fn expr_graphql_file_or_dir_contents(paths: Vec) -> Expr { + Expr::All(vec![ + Expr::Suffix(vec!["graphql".into(), "gql".into()]), + Expr::Any(vec![ + Expr::Name(NameTerm { + paths: paths.clone(), + wholename: true, + }), + expr_files_in_dirs(paths), + ]), + ]) +} + /// Helper to create an `anyof` expression if multiple items are passed or just /// return the expression for a single item input `Vec`. /// Panics for empty expressions. These are not valid in Watchman. We could @@ -245,47 +180,3 @@ fn expr_any(expressions: Vec) -> Expr { _ => Expr::Any(expressions), } } - -/// Finds the roots of a set of paths. This filters any paths -/// that are a subdirectory of other paths in the input. -fn unify_roots(mut paths: Vec) -> Vec { - paths.sort(); - let mut roots = Vec::new(); - for path in paths { - match roots.last() { - Some(prev) if path.starts_with(prev) => { - // skip - } - _ => { - roots.push(path); - } - } - } - roots -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn test_unify_roots() { - assert_eq!(unify_roots(vec![]).len(), 0); - assert_eq!( - unify_roots(vec!["Apps".into(), "Libraries".into()]), - &[PathBuf::from("Apps"), PathBuf::from("Libraries")] - ); - assert_eq!( - unify_roots(vec!["Apps".into(), "Apps/Foo".into()]), - &[PathBuf::from("Apps")] - ); - assert_eq!( - unify_roots(vec!["Apps/Foo".into(), "Apps".into()]), - &[PathBuf::from("Apps")] - ); - assert_eq!( - unify_roots(vec!["Foo".into(), "Foo2".into()]), - &[PathBuf::from("Foo"), PathBuf::from("Foo2"),] - ); - } -} diff --git a/compiler/crates/relay-compiler/src/get_programs.rs b/compiler/crates/relay-compiler/src/get_programs.rs index dc5d0a7d4e50c..d3c3617ca3dd2 100644 --- a/compiler/crates/relay-compiler/src/get_programs.rs +++ b/compiler/crates/relay-compiler/src/get_programs.rs @@ -5,16 +5,18 @@ * LICENSE file in the root directory of this source tree. */ +use std::collections::HashMap; use std::sync::Arc; use std::sync::Mutex; use common::PerfLogger; +use relay_config::ProjectName; use relay_transforms::Programs; +use crate::NoopArtifactWriter; use crate::compiler::Compiler; use crate::compiler_state::CompilerState; use crate::config::Config; -use crate::NoopArtifactWriter; /// Asynchronously compiles Relay programs and returns them along with the compiler state and configuration. /// @@ -24,19 +26,24 @@ use crate::NoopArtifactWriter; pub async fn get_programs( mut config: Config, perf_logger: Arc, -) -> (Vec>, CompilerState, Arc) { - let raw_programs: Arc>>> = Arc::new(Mutex::new(vec![])); +) -> ( + HashMap>, + CompilerState, + Arc, +) { + let raw_programs: Arc>>> = + Arc::new(Mutex::new(HashMap::new())); let raw_programs_cloned = raw_programs.clone(); config.compile_everything = true; config.generate_virtual_id_file_name = None; config.artifact_writer = Box::new(NoopArtifactWriter); config.generate_extra_artifacts = Some(Box::new( - move |_config, _project_config, _schema, programs, _artifacts| { + move |_config, project_config, _schema, programs, _artifacts| { raw_programs_cloned .lock() .unwrap() - .push(Arc::new(programs.clone())); + .insert(project_config.name, Arc::new(programs.clone())); vec![] }, )); @@ -52,7 +59,7 @@ pub async fn get_programs( }; let programs = { let guard = raw_programs.lock().unwrap(); - if guard.len() < 1 { + if guard.is_empty() { eprintln!("Failed to extract program from compiler state"); std::process::exit(1); } diff --git a/compiler/crates/relay-compiler/src/graphql_asts.rs b/compiler/crates/relay-compiler/src/graphql_asts.rs index b0dce6f0f13de..a37133ebfab9d 100644 --- a/compiler/crates/relay-compiler/src/graphql_asts.rs +++ b/compiler/crates/relay-compiler/src/graphql_asts.rs @@ -5,15 +5,15 @@ * LICENSE file in the root directory of this source tree. */ -use std::collections::hash_map::Entry; use std::collections::HashSet; +use std::collections::hash_map::Entry; use std::path::Path; use std::path::PathBuf; use std::sync::Arc; -use common::sync::ParallelIterator; use common::Diagnostic; use common::SourceLocationKey; +use common::sync::ParallelIterator; use dependency_analyzer::ExecutableDefinitionNameSet; use fnv::FnvHashMap; use graphql_ir::ExecutableDefinitionName; diff --git a/compiler/crates/relay-compiler/src/lib.rs b/compiler/crates/relay-compiler/src/lib.rs index 2aabce08e8173..a399afe7c1968 100644 --- a/compiler/crates/relay-compiler/src/lib.rs +++ b/compiler/crates/relay-compiler/src/lib.rs @@ -26,6 +26,12 @@ pub mod status_reporter; mod utils; pub use artifact_map::ArtifactSourceKey; +pub use build_project::AdditionalValidations; +pub use build_project::Artifact; +pub use build_project::ArtifactContent; +pub use build_project::ArtifactGeneratedTypes; +pub use build_project::BuildProjectFailure; +pub use build_project::SourceHashes; pub use build_project::artifact_writer::ArtifactFileWriter; pub use build_project::artifact_writer::ArtifactValidationWriter; pub use build_project::artifact_writer::ArtifactWriter; @@ -41,12 +47,6 @@ pub use build_project::source_control_for_root; pub use build_project::transform_program; pub use build_project::validate; pub use build_project::validate_program; -pub use build_project::AdditionalValidations; -pub use build_project::Artifact; -pub use build_project::ArtifactContent; -pub use build_project::ArtifactGeneratedTypes; -pub use build_project::BuildProjectFailure; -pub use build_project::SourceHashes; pub use config::ConfigFile; pub use config::ConfigFileProject; pub use config::FileSourceKind; @@ -56,7 +56,6 @@ pub use config::PersistConfig; pub use config::ProjectConfig; pub use config::RemotePersistConfig; pub use config::SchemaLocation; -pub use file_source::source_for_location; pub use file_source::ExternalFileSourceResult; pub use file_source::File; pub use file_source::FileCategorizer; @@ -68,6 +67,7 @@ pub use file_source::FileSourceSubscriptionNextChange; pub use file_source::FsSourceReader; pub use file_source::SourceControlUpdateStatus; pub use file_source::SourceReader; +pub use file_source::source_for_location; pub use get_programs::get_programs; pub use graphql_asts::GraphQLAsts; pub use operation_persister::LocalPersister; diff --git a/compiler/crates/relay-compiler/src/operation_persister/local_persister.rs b/compiler/crates/relay-compiler/src/operation_persister/local_persister.rs index e8ce1e41de19e..84e55c8730404 100644 --- a/compiler/crates/relay-compiler/src/operation_persister/local_persister.rs +++ b/compiler/crates/relay-compiler/src/operation_persister/local_persister.rs @@ -20,8 +20,8 @@ use sha1::Digest; use sha1::Sha1; use sha2::Sha256; -use crate::config::ArtifactForPersister; use crate::OperationPersister; +use crate::config::ArtifactForPersister; /// A local persister that stores GraphQL documents in a file on disk. /// diff --git a/compiler/crates/relay-compiler/src/operation_persister/remote_persister.rs b/compiler/crates/relay-compiler/src/operation_persister/remote_persister.rs index 13578bd845eaa..bcd77c28796d6 100644 --- a/compiler/crates/relay-compiler/src/operation_persister/remote_persister.rs +++ b/compiler/crates/relay-compiler/src/operation_persister/remote_persister.rs @@ -6,13 +6,13 @@ */ use async_trait::async_trait; -use persist_query::persist; use persist_query::PersistError; +use persist_query::persist; use relay_config::RemotePersistConfig; use tokio::sync::Semaphore; -use crate::config::ArtifactForPersister; use crate::OperationPersister; +use crate::config::ArtifactForPersister; /// A remote persister that sends GraphQL documents to a server for persistence. /// diff --git a/compiler/crates/relay-compiler/src/status_reporter.rs b/compiler/crates/relay-compiler/src/status_reporter.rs index 1970db1da02ba..8d05afca9c32f 100644 --- a/compiler/crates/relay-compiler/src/status_reporter.rs +++ b/compiler/crates/relay-compiler/src/status_reporter.rs @@ -10,6 +10,8 @@ //! This module contains two implementations of the `StatusReporter` trait: //! * `ConsoleStatusReporter`: Reports the status to the console using the `log` crate. //! * `JSONStatusReporter`: Reports the status to a JSON file using the `serde_json` crate. +use std::fs::OpenOptions; +use std::io::Write; use std::path::PathBuf; use common::Diagnostic; @@ -19,11 +21,11 @@ use log::error; use log::info; use log::warn; +use crate::FsSourceReader; +use crate::SourceReader; use crate::errors::BuildProjectError; use crate::errors::Error; use crate::source_for_location; -use crate::FsSourceReader; -use crate::SourceReader; pub trait StatusReporter { fn build_starts(&self); @@ -78,6 +80,9 @@ impl ConsoleStatusReporter { match severity { DiagnosticSeverity::ERROR => error!("{}", output), DiagnosticSeverity::WARNING => warn!("{}", output), + DiagnosticSeverity::HINT => { + // Opting to omit, not emit, hints in the CLI output. + } _ => info!("{}", output), } } @@ -157,22 +162,76 @@ impl StatusReporter for ConsoleStatusReporter { } } -pub struct JSONStatusReporter; +pub struct JSONStatusReporter { + path: Option, + base_reporter: Box, +} + +impl JSONStatusReporter { + pub fn new( + path: Option, + base_reporter: Box, + ) -> Self { + Self { + path, + base_reporter, + } + } +} impl StatusReporter for JSONStatusReporter { fn build_starts(&self) {} fn build_completes(&self, diagnostics: &[Diagnostic]) { - println!( - "{{\"completed\":true,\"diagnostics\":{}}}", - serde_json::to_string(diagnostics).unwrap() - ); + match &self.path { + Some(path) => { + self.base_reporter.build_completes(diagnostics); + let mut file = OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open(path) + .unwrap(); + writeln!( + file, + "{{\"completed\":true,\"diagnostics\":{}}}", + serde_json::to_string(diagnostics).unwrap() + ) + .unwrap(); + } + None => { + println!( + "{{\"completed\":true,\"diagnostics\":{}}}", + serde_json::to_string(diagnostics).unwrap() + ); + } + } } fn build_errors(&self, error: &Error) { - println!( - "{{\"completed\":false,\"error\":{}}}", - serde_json::to_string(error).unwrap() - ); + match &self.path { + Some(path) => { + self.base_reporter.build_errors(error); + + let mut file = OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open(path) + .unwrap(); + writeln!( + file, + "{{\"completed\":false,\"error\":{}}}", + serde_json::to_string(error).unwrap() + ) + .unwrap(); + } + None => { + println!( + "{{\"completed\":false,\"error\":{}}}", + serde_json::to_string(error).unwrap() + ); + } + } } } diff --git a/compiler/crates/relay-compiler/src/utils.rs b/compiler/crates/relay-compiler/src/utils.rs index 68daded0b0138..4686e7e0a829f 100644 --- a/compiler/crates/relay-compiler/src/utils.rs +++ b/compiler/crates/relay-compiler/src/utils.rs @@ -26,5 +26,6 @@ pub fn get_parser_features(project_config: &ProjectConfig) -> ParserFeatures { } else { FragmentArgumentSyntaxKind::None }, + allow_string_literal_alias: false, } } diff --git a/compiler/crates/relay-compiler/tests/compile_relay_artifacts.rs b/compiler/crates/relay-compiler/tests/compile_relay_artifacts.rs index b4d7bc78476dd..0aa22b3252595 100644 --- a/compiler/crates/relay-compiler/tests/compile_relay_artifacts.rs +++ b/compiler/crates/relay-compiler/tests/compile_relay_artifacts.rs @@ -14,7 +14,6 @@ use common::FeatureFlags; use common::NamedItem; use common::SourceLocationKey; use fixture_tests::Fixture; -use graphql_ir::build_ir_with_extra_features; use graphql_ir::BuilderOptions; use graphql_ir::FragmentDefinition; use graphql_ir::FragmentDefinitionName; @@ -23,26 +22,27 @@ use graphql_ir::OperationDefinition; use graphql_ir::OperationDefinitionName; use graphql_ir::Program; use graphql_ir::RelayMode; +use graphql_ir::build_ir_with_extra_features; use graphql_syntax::parse_executable; use graphql_test_helpers::diagnostics_to_sorted_string; use graphql_text_printer::print_full_operation; use intern::string_key::Intern; +use relay_codegen::JsModuleFormat; use relay_codegen::build_request_params; use relay_codegen::print_fragment; use relay_codegen::print_operation; use relay_codegen::print_request; -use relay_codegen::JsModuleFormat; -use relay_compiler::find_duplicates; -use relay_compiler::validate; use relay_compiler::ConfigFileProject; use relay_compiler::ProjectConfig; +use relay_compiler::find_duplicates; +use relay_compiler::validate; use relay_config::NonNodeIdFieldsConfig; use relay_config::ProjectName; use relay_config::SchemaConfig; use relay_test_schema::get_test_schema; use relay_test_schema::get_test_schema_with_extensions; -use relay_transforms::apply_transforms; use relay_transforms::DIRECTIVE_SPLIT_OPERATION; +use relay_transforms::apply_transforms; pub async fn transform_fixture(fixture: &Fixture<'_>) -> Result { let source_location = SourceLocationKey::standalone(fixture.file_name); @@ -164,6 +164,7 @@ pub async fn transform_fixture(fixture: &Fixture<'_>) -> Result Arc::new(flags) }), js_module_format: config_file_project.js_module_format, + relativize_js_module_paths: config_file_project.relativize_js_module_paths, ..default_project_config } }, diff --git a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/client-3D-resolvers-enabled-client-3D-fragment.expected b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/client-3D-resolvers-enabled-client-3D-fragment.expected index 10b36c368c75b..ffe36e4157469 100644 --- a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/client-3D-resolvers-enabled-client-3D-fragment.expected +++ b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/client-3D-resolvers-enabled-client-3D-fragment.expected @@ -56,6 +56,60 @@ type SpecialUser implements BasicUser @__RelayResolverModel { "language": "flow" } ==================================== OUTPUT =================================== +{ + "kind": "SplitOperation", + "metadata": {}, + "name": "client3DResolversEnabledClient3DFragment_ClientUserFragment$normalization", + "selections": [ + { + "kind": "ClientExtension", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "data", + "storageKey": null + } + ] + } + ] +} + +{ + "kind": "SplitOperation", + "metadata": {}, + "name": "client3DResolversEnabledClient3DFragment_SpecialUserFragment$normalization", + "selections": [ + { + "kind": "ClientExtension", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "data", + "storageKey": null + } + ] + } + ] +} + import JSResource from 'JSResource'; { "fragment": { @@ -174,6 +228,15 @@ import JSResource from 'JSResource'; { "kind": "InlineFragment", "selections": [ + { + "args": null, + "documentName": "client3DResolversEnabledClient3DFragment_clientSideQuery", + "fragmentName": "client3DResolversEnabledClient3DFragment_ClientUserFragment", + "fragmentPropName": "ClientUserFragment", + "kind": "ModuleImport", + "componentModuleProvider": () => JSResource('m#ClientUser.react'), + "operationModuleProvider": () => JSResource('m#client3DResolversEnabledClient3DFragment_ClientUserFragment$normalization.graphql') + }, { "alias": null, "args": null, @@ -188,6 +251,15 @@ import JSResource from 'JSResource'; { "kind": "InlineFragment", "selections": [ + { + "args": null, + "documentName": "client3DResolversEnabledClient3DFragment_clientSideQuery", + "fragmentName": "client3DResolversEnabledClient3DFragment_SpecialUserFragment", + "fragmentPropName": "SpecialUserFragment", + "kind": "ModuleImport", + "componentModuleProvider": () => JSResource('m#SpecialUser.react'), + "operationModuleProvider": () => JSResource('m#client3DResolversEnabledClient3DFragment_SpecialUserFragment$normalization.graphql') + }, { "alias": null, "args": null, @@ -223,6 +295,9 @@ QUERY: Query Text is Empty. +import clientUserRelayModelInstanceResolver from '../ClientUserResolver'; +import ClientUser__id_graphql from './ClientUser__id.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; { "argumentDefinitions": [], "kind": "Fragment", @@ -239,7 +314,7 @@ Query Text is Empty. }, "kind": "RelayResolver", "name": "__relay_model_instance", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./ClientUser__id.graphql'), require('./../ClientUserResolver'), 'id', true), + "resolverModule": resolverDataInjector(ClientUser__id_graphql, clientUserRelayModelInstanceResolver, 'id', true), "path": "__relay_model_instance" } ], @@ -270,6 +345,9 @@ Query Text is Empty. "abstractKey": null } +import specialUserRelayModelInstanceResolver from '../SpecialUserResolver'; +import SpecialUser__id_graphql from './SpecialUser__id.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; { "argumentDefinitions": [], "kind": "Fragment", @@ -286,7 +364,7 @@ Query Text is Empty. }, "kind": "RelayResolver", "name": "__relay_model_instance", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./SpecialUser__id.graphql'), require('./../SpecialUserResolver'), 'id', true), + "resolverModule": resolverDataInjector(SpecialUser__id_graphql, specialUserRelayModelInstanceResolver, 'id', true), "path": "__relay_model_instance" } ], diff --git a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/client_edge_from_client_type_to_client_type_terse.expected b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/client_edge_from_client_type_to_client_type_terse.expected index 9ddd4936a9368..f773ff086c7d0 100644 --- a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/client_edge_from_client_type_to_client_type_terse.expected +++ b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/client_edge_from_client_type_to_client_type_terse.expected @@ -73,8 +73,8 @@ extend type ClientViewer { "name": "ClientType__id" }, "kind": "RelayResolver", - "name": "client_edge", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('ClientType__id.graphql'), require('').ClientType, 'id', true), + "name": "__relay_model_instance", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('ClientType__id.graphql'), require('UserModelResolver').ClientType, 'id', true), "path": "client_viewer.client_edge.__relay_model_instance" } }, diff --git a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/client_edge_from_client_type_to_client_type_terse_live.expected b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/client_edge_from_client_type_to_client_type_terse_live.expected index 68ef6ab876478..695ceeb631560 100644 --- a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/client_edge_from_client_type_to_client_type_terse_live.expected +++ b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/client_edge_from_client_type_to_client_type_terse_live.expected @@ -66,8 +66,8 @@ extend type ClientViewer { "name": "ClientType__id" }, "kind": "RelayLiveResolver", - "name": "client_edge", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('ClientType__id.graphql'), require('').ClientType, 'id', true), + "name": "__relay_model_instance", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('ClientType__id.graphql'), require('UserModelResolver').ClientType, 'id', true), "path": "client_viewer.client_edge.__relay_model_instance" } }, diff --git a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/client_edge_from_client_type_to_client_type_terse_plural.expected b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/client_edge_from_client_type_to_client_type_terse_plural.expected index 92fb7aa085419..331618ec47216 100644 --- a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/client_edge_from_client_type_to_client_type_terse_plural.expected +++ b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/client_edge_from_client_type_to_client_type_terse_plural.expected @@ -73,8 +73,8 @@ extend type ClientViewer { "name": "ClientType__id" }, "kind": "RelayResolver", - "name": "client_edges", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('ClientType__id.graphql'), require('').ClientType, 'id', true), + "name": "__relay_model_instance", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('ClientType__id.graphql'), require('UserModelResolver').ClientType, 'id', true), "path": "client_viewer.client_edges.__relay_model_instance" } }, diff --git a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/client_edge_from_client_type_to_client_type_terse_scalar.expected b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/client_edge_from_client_type_to_client_type_terse_scalar.expected index c98c039017972..d4b96a790cc7b 100644 --- a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/client_edge_from_client_type_to_client_type_terse_scalar.expected +++ b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/client_edge_from_client_type_to_client_type_terse_scalar.expected @@ -65,8 +65,8 @@ extend type ClientViewer { "name": "ClientType__id" }, "kind": "RelayResolver", - "name": "client_edge", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('ClientType__id.graphql'), require('').ClientType, 'id', true), + "name": "__relay_model_instance", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('ClientType__id.graphql'), require('UserModelResolver').ClientType, 'id', true), "path": "client_viewer.client_edge.__relay_model_instance" } }, diff --git a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/fragment-on-non-node-fetchable-type.expected b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/fragment-on-non-node-fetchable-type.expected index 94c3498c75c31..9b1407df008bc 100644 --- a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/fragment-on-non-node-fetchable-type.expected +++ b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/fragment-on-non-node-fetchable-type.expected @@ -150,13 +150,6 @@ fragment fragmentOnNonNodeFetchableType_ProfilePicture on User { "name": "fetch_id", "storageKey": null }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "__token", - "storageKey": null - }, { "alias": null, "args": null, @@ -204,7 +197,6 @@ fragment fragmentOnNonNodeFetchableType_RefetchableFragment on NonNodeStory { id } fetch_id - __token } @@ -293,13 +285,6 @@ fragment fragmentOnNonNodeFetchableType_RefetchableFragment on NonNodeStory { "kind": "ScalarField", "name": "fetch_id", "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "__token", - "storageKey": null } ], "type": "NonNodeStory", diff --git a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/fragment-on-query-commonjs-relativize-disabled.expected b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/fragment-on-query-commonjs-relativize-disabled.expected new file mode 100644 index 0000000000000..08e8cc5270d41 --- /dev/null +++ b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/fragment-on-query-commonjs-relativize-disabled.expected @@ -0,0 +1,294 @@ +==================================== INPUT ==================================== +fragment fragmentOnQueryCommonjsRelativizeDisabled_RefetchableFragment on Query + @refetchable(queryName: "RefetchableFragmentQuery") + @argumentDefinitions(id: {type: "ID!"}) { + node(id: $id) { + ... on User { + id + name + ...fragmentOnQueryCommonjsRelativizeDisabled_ProfilePicture + } + } +} + +fragment fragmentOnQueryCommonjsRelativizeDisabled_ProfilePicture on User { + profilePicture(size: $size) { + uri + } +} + +%project_config% +{ + "jsModuleFormat": "commonjs", + "language": "flow" +} +==================================== OUTPUT =================================== +{ + "fragment": { + "argumentDefinitions": [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "id" + }, + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "size" + } + ], + "kind": "Fragment", + "metadata": null, + "name": "RefetchableFragmentQuery", + "selections": [ + { + "args": [ + { + "kind": "Variable", + "name": "id", + "variableName": "id" + } + ], + "kind": "FragmentSpread", + "name": "fragmentOnQueryCommonjsRelativizeDisabled_RefetchableFragment" + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "id" + }, + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "size" + } + ], + "kind": "Operation", + "name": "RefetchableFragmentQuery", + "selections": [ + { + "alias": null, + "args": [ + { + "kind": "Variable", + "name": "id", + "variableName": "id" + } + ], + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + }, + { + "kind": "InlineFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + }, + { + "alias": null, + "args": [ + { + "kind": "Variable", + "name": "size", + "variableName": "size" + } + ], + "concreteType": "Image", + "kind": "LinkedField", + "name": "profilePicture", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "uri", + "storageKey": null + } + ], + "storageKey": null + } + ], + "type": "User", + "abstractKey": null + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "31dcd57f42128b8d975e6224202a17c9", + "id": null, + "metadata": {}, + "name": "RefetchableFragmentQuery", + "operationKind": "query", + "text": null + } +} + +QUERY: + +query RefetchableFragmentQuery( + $id: ID! + $size: [Int] +) { + ...fragmentOnQueryCommonjsRelativizeDisabled_RefetchableFragment_1Bmzm5 +} + +fragment fragmentOnQueryCommonjsRelativizeDisabled_ProfilePicture on User { + profilePicture(size: $size) { + uri + } +} + +fragment fragmentOnQueryCommonjsRelativizeDisabled_RefetchableFragment_1Bmzm5 on Query { + node(id: $id) { + __typename + ... on User { + id + name + ...fragmentOnQueryCommonjsRelativizeDisabled_ProfilePicture + } + id + } +} + + +{ + "argumentDefinitions": [ + { + "kind": "RootArgument", + "name": "size" + } + ], + "kind": "Fragment", + "metadata": null, + "name": "fragmentOnQueryCommonjsRelativizeDisabled_ProfilePicture", + "selections": [ + { + "alias": null, + "args": [ + { + "kind": "Variable", + "name": "size", + "variableName": "size" + } + ], + "concreteType": "Image", + "kind": "LinkedField", + "name": "profilePicture", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "uri", + "storageKey": null + } + ], + "storageKey": null + } + ], + "type": "User", + "abstractKey": null +} + +import RefetchableFragmentQuery_graphql from './RefetchableFragmentQuery.graphql'; +{ + "argumentDefinitions": [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "id" + }, + { + "kind": "RootArgument", + "name": "size" + } + ], + "kind": "Fragment", + "metadata": { + "refetch": { + "connection": null, + "fragmentPathInResult": [], + "operation": RefetchableFragmentQuery_graphql + } + }, + "name": "fragmentOnQueryCommonjsRelativizeDisabled_RefetchableFragment", + "selections": [ + { + "alias": null, + "args": [ + { + "kind": "Variable", + "name": "id", + "variableName": "id" + } + ], + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + { + "kind": "InlineFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + }, + { + "args": null, + "kind": "FragmentSpread", + "name": "fragmentOnQueryCommonjsRelativizeDisabled_ProfilePicture" + } + ], + "type": "User", + "abstractKey": null + } + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null +} diff --git a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/fragment-on-query-commonjs-relativize-disabled.graphql b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/fragment-on-query-commonjs-relativize-disabled.graphql new file mode 100644 index 0000000000000..56b608e1d49e0 --- /dev/null +++ b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/fragment-on-query-commonjs-relativize-disabled.graphql @@ -0,0 +1,23 @@ +fragment fragmentOnQueryCommonjsRelativizeDisabled_RefetchableFragment on Query + @refetchable(queryName: "RefetchableFragmentQuery") + @argumentDefinitions(id: {type: "ID!"}) { + node(id: $id) { + ... on User { + id + name + ...fragmentOnQueryCommonjsRelativizeDisabled_ProfilePicture + } + } +} + +fragment fragmentOnQueryCommonjsRelativizeDisabled_ProfilePicture on User { + profilePicture(size: $size) { + uri + } +} + +%project_config% +{ + "jsModuleFormat": "commonjs", + "language": "flow" +} diff --git a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/fragment-on-query-commonjs.expected b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/fragment-on-query-commonjs.expected new file mode 100644 index 0000000000000..3c9222882ae91 --- /dev/null +++ b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/fragment-on-query-commonjs.expected @@ -0,0 +1,294 @@ +==================================== INPUT ==================================== +fragment fragmentOnQueryCommonjs_RefetchableFragment on Query + @refetchable(queryName: "RefetchableFragmentQuery") + @argumentDefinitions(id: {type: "ID!"}) { + node(id: $id) { + ... on User { + id + name + ...fragmentOnQueryCommonjs_ProfilePicture + } + } +} + +fragment fragmentOnQueryCommonjs_ProfilePicture on User { + profilePicture(size: $size) { + uri + } +} + +%project_config% +{ + "jsModuleFormat": "commonjs", + "language": "flow" +} +==================================== OUTPUT =================================== +{ + "fragment": { + "argumentDefinitions": [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "id" + }, + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "size" + } + ], + "kind": "Fragment", + "metadata": null, + "name": "RefetchableFragmentQuery", + "selections": [ + { + "args": [ + { + "kind": "Variable", + "name": "id", + "variableName": "id" + } + ], + "kind": "FragmentSpread", + "name": "fragmentOnQueryCommonjs_RefetchableFragment" + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "id" + }, + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "size" + } + ], + "kind": "Operation", + "name": "RefetchableFragmentQuery", + "selections": [ + { + "alias": null, + "args": [ + { + "kind": "Variable", + "name": "id", + "variableName": "id" + } + ], + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + }, + { + "kind": "InlineFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + }, + { + "alias": null, + "args": [ + { + "kind": "Variable", + "name": "size", + "variableName": "size" + } + ], + "concreteType": "Image", + "kind": "LinkedField", + "name": "profilePicture", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "uri", + "storageKey": null + } + ], + "storageKey": null + } + ], + "type": "User", + "abstractKey": null + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "31dcd57f42128b8d975e6224202a17c9", + "id": null, + "metadata": {}, + "name": "RefetchableFragmentQuery", + "operationKind": "query", + "text": null + } +} + +QUERY: + +query RefetchableFragmentQuery( + $id: ID! + $size: [Int] +) { + ...fragmentOnQueryCommonjs_RefetchableFragment_1Bmzm5 +} + +fragment fragmentOnQueryCommonjs_ProfilePicture on User { + profilePicture(size: $size) { + uri + } +} + +fragment fragmentOnQueryCommonjs_RefetchableFragment_1Bmzm5 on Query { + node(id: $id) { + __typename + ... on User { + id + name + ...fragmentOnQueryCommonjs_ProfilePicture + } + id + } +} + + +{ + "argumentDefinitions": [ + { + "kind": "RootArgument", + "name": "size" + } + ], + "kind": "Fragment", + "metadata": null, + "name": "fragmentOnQueryCommonjs_ProfilePicture", + "selections": [ + { + "alias": null, + "args": [ + { + "kind": "Variable", + "name": "size", + "variableName": "size" + } + ], + "concreteType": "Image", + "kind": "LinkedField", + "name": "profilePicture", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "uri", + "storageKey": null + } + ], + "storageKey": null + } + ], + "type": "User", + "abstractKey": null +} + +import RefetchableFragmentQuery_graphql from './RefetchableFragmentQuery.graphql'; +{ + "argumentDefinitions": [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "id" + }, + { + "kind": "RootArgument", + "name": "size" + } + ], + "kind": "Fragment", + "metadata": { + "refetch": { + "connection": null, + "fragmentPathInResult": [], + "operation": RefetchableFragmentQuery_graphql + } + }, + "name": "fragmentOnQueryCommonjs_RefetchableFragment", + "selections": [ + { + "alias": null, + "args": [ + { + "kind": "Variable", + "name": "id", + "variableName": "id" + } + ], + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + { + "kind": "InlineFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + }, + { + "args": null, + "kind": "FragmentSpread", + "name": "fragmentOnQueryCommonjs_ProfilePicture" + } + ], + "type": "User", + "abstractKey": null + } + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null +} diff --git a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/fragment-on-query-commonjs.graphql b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/fragment-on-query-commonjs.graphql new file mode 100644 index 0000000000000..fe948d1357373 --- /dev/null +++ b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/fragment-on-query-commonjs.graphql @@ -0,0 +1,23 @@ +fragment fragmentOnQueryCommonjs_RefetchableFragment on Query + @refetchable(queryName: "RefetchableFragmentQuery") + @argumentDefinitions(id: {type: "ID!"}) { + node(id: $id) { + ... on User { + id + name + ...fragmentOnQueryCommonjs_ProfilePicture + } + } +} + +fragment fragmentOnQueryCommonjs_ProfilePicture on User { + profilePicture(size: $size) { + uri + } +} + +%project_config% +{ + "jsModuleFormat": "commonjs", + "language": "flow" +} diff --git a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/prefetchable-pagination-query-with-conflicting-args.invalid.expected b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/prefetchable-pagination-query-with-conflicting-args.invalid.expected new file mode 100644 index 0000000000000..2cb3d0efa4b9b --- /dev/null +++ b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/prefetchable-pagination-query-with-conflicting-args.invalid.expected @@ -0,0 +1,45 @@ +==================================== INPUT ==================================== +# expected-to-throw + +query prefetchablePaginationQueryWithConflictingArgsQuery($site: String) { + node(id: "x") { + ...prefetchablePaginationQueryWithConflictingArgs_FirstFragment @arguments(site: $site) + } +} + +fragment prefetchablePaginationQueryWithConflictingArgs_FirstFragment on Node + @refetchable(queryName: "RefetchableFragmentQuery") + @argumentDefinitions( + count: {type: "Int", defaultValue: 10} + cursor: {type: "ID"} + site: {type: "String"} + ) { + id + ...prefetchablePaginationQueryWithConflictingArgs_SecondFragment + ... on User { + name + friends(after: $cursor, first: $count) + @connection(key: "PaginationFragment_friends", prefetchable_pagination: true) { + edges { + node { + id + # local $site + url(site: $site) + } + } + } + } +} + +fragment prefetchablePaginationQueryWithConflictingArgs_SecondFragment on Node { + # global $site + p2: url(site: $site) +} +==================================== ERROR ==================================== +✖︎ Fragment variable `$site` conflicts with a global variable generated by the @refetchable generated query + + prefetchable-pagination-query-with-conflicting-args.invalid.graphql:14:5 + 13 │ cursor: {type: "ID"} + 14 │ site: {type: "String"} + │ ^^^^ + 15 │ ) { diff --git a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/prefetchable-pagination-query-with-conflicting-args.invalid.graphql b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/prefetchable-pagination-query-with-conflicting-args.invalid.graphql new file mode 100644 index 0000000000000..c16cbd882625e --- /dev/null +++ b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/prefetchable-pagination-query-with-conflicting-args.invalid.graphql @@ -0,0 +1,36 @@ +# expected-to-throw + +query prefetchablePaginationQueryWithConflictingArgsQuery($site: String) { + node(id: "x") { + ...prefetchablePaginationQueryWithConflictingArgs_FirstFragment @arguments(site: $site) + } +} + +fragment prefetchablePaginationQueryWithConflictingArgs_FirstFragment on Node + @refetchable(queryName: "RefetchableFragmentQuery") + @argumentDefinitions( + count: {type: "Int", defaultValue: 10} + cursor: {type: "ID"} + site: {type: "String"} + ) { + id + ...prefetchablePaginationQueryWithConflictingArgs_SecondFragment + ... on User { + name + friends(after: $cursor, first: $count) + @connection(key: "PaginationFragment_friends", prefetchable_pagination: true) { + edges { + node { + id + # local $site + url(site: $site) + } + } + } + } +} + +fragment prefetchablePaginationQueryWithConflictingArgs_SecondFragment on Node { + # global $site + p2: url(site: $site) +} diff --git a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/prefetchable-pagination-query-without-conflicting-args.expected b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/prefetchable-pagination-query-without-conflicting-args.expected new file mode 100644 index 0000000000000..4c550f610761a --- /dev/null +++ b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/prefetchable-pagination-query-without-conflicting-args.expected @@ -0,0 +1,1125 @@ +==================================== INPUT ==================================== +query prefetchablePaginationQueryWithoutConflictingArgsQuery($site: String) { + node(id: "x") { + ...prefetchablePaginationQueryWithoutConflictingArgs_FirstFragment @arguments(site: $site) + ...prefetchablePaginationQueryWithoutConflictingArgs_SecondFragment @arguments(site: $site) + } +} + +fragment prefetchablePaginationQueryWithoutConflictingArgs_FirstFragment on Page + @refetchable(queryName: "RefetchableFragmentQuery") + @argumentDefinitions( + count: {type: "Int", defaultValue: 10} + cursor: {type: "ID"} + site: {type: "String"} + ) { + name + friends(after: $cursor, first: $count) + @connection(key: "PaginationFragment_friends", prefetchable_pagination: true) { + edges { + node { + id + url(site: $site) + } + } + } +} + +fragment prefetchablePaginationQueryWithoutConflictingArgs_SecondFragment on User + @argumentDefinitions( + count: {type: "Int", defaultValue: 10} + cursor: {type: "ID"} + site: {type: "String"} + ) { + id + friends(after: $cursor, first: $count) + @connection(key: "PaginationFragment2_friends") { + edges { + node { + id + url(site: $site) + } + } + } +} +==================================== OUTPUT =================================== +{ + "fragment": { + "argumentDefinitions": [ + { + "defaultValue": 10, + "kind": "LocalArgument", + "name": "count" + }, + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "cursor" + }, + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "id" + }, + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "site" + } + ], + "kind": "Fragment", + "metadata": null, + "name": "RefetchableFragmentQuery", + "selections": [ + { + "alias": null, + "args": [ + { + "kind": "Variable", + "name": "id", + "variableName": "id" + } + ], + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + { + "args": [ + { + "kind": "Variable", + "name": "count", + "variableName": "count" + }, + { + "kind": "Variable", + "name": "cursor", + "variableName": "cursor" + }, + { + "kind": "Variable", + "name": "site", + "variableName": "site" + } + ], + "kind": "FragmentSpread", + "name": "prefetchablePaginationQueryWithoutConflictingArgs_FirstFragment" + } + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [ + { + "defaultValue": 10, + "kind": "LocalArgument", + "name": "count" + }, + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "cursor" + }, + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "site" + }, + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "id" + } + ], + "kind": "Operation", + "name": "RefetchableFragmentQuery", + "selections": [ + { + "alias": null, + "args": [ + { + "kind": "Variable", + "name": "id", + "variableName": "id" + } + ], + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + }, + { + "kind": "InlineFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + }, + { + "alias": null, + "args": [ + { + "kind": "Variable", + "name": "after", + "variableName": "cursor" + }, + { + "kind": "Variable", + "name": "first", + "variableName": "count" + } + ], + "concreteType": "FriendsConnection", + "kind": "LinkedField", + "name": "friends", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "FriendsEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + }, + { + "alias": null, + "args": [ + { + "kind": "Variable", + "name": "site", + "variableName": "site" + } + ], + "kind": "ScalarField", + "name": "url", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null + } + ], + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "cursor", + "storageKey": null + } + ], + "storageKey": null + }, + { + "alias": null, + "args": null, + "concreteType": "PageInfo", + "kind": "LinkedField", + "name": "pageInfo", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "endCursor", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "hasNextPage", + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": null + }, + { + "alias": null, + "args": [ + { + "kind": "Variable", + "name": "after", + "variableName": "cursor" + }, + { + "kind": "Variable", + "name": "first", + "variableName": "count" + } + ], + "filters": null, + "handle": "connection", + "key": "PaginationFragment_friends", + "kind": "LinkedHandle", + "name": "friends" + } + ], + "type": "Page", + "abstractKey": null + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "31dcd57f42128b8d975e6224202a17c9", + "id": null, + "metadata": {}, + "name": "RefetchableFragmentQuery", + "operationKind": "query", + "text": null + } +} + +QUERY: + +query RefetchableFragmentQuery( + $count: Int = 10 + $cursor: ID + $site: String + $id: ID! +) { + node(id: $id) { + __typename + ...prefetchablePaginationQueryWithoutConflictingArgs_FirstFragment_1Xo9Zp + id + } +} + +fragment prefetchablePaginationQueryWithoutConflictingArgs_FirstFragment_1Xo9Zp on Page { + name + friends(after: $cursor, first: $count) { + edges { + ...prefetchablePaginationQueryWithoutConflictingArgs_FirstFragment__edges_1Xo9Zp + } + pageInfo { + endCursor + hasNextPage + } + } + id +} + +fragment prefetchablePaginationQueryWithoutConflictingArgs_FirstFragment__edges_1Xo9Zp on FriendsEdge { + node { + id + url(site: $site) + __typename + } + cursor +} + + +{ + "fragment": { + "argumentDefinitions": [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "site" + } + ], + "kind": "Fragment", + "metadata": null, + "name": "prefetchablePaginationQueryWithoutConflictingArgsQuery", + "selections": [ + { + "alias": null, + "args": [ + { + "kind": "Literal", + "name": "id", + "value": "x" + } + ], + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + { + "args": [ + { + "kind": "Variable", + "name": "site", + "variableName": "site" + } + ], + "kind": "FragmentSpread", + "name": "prefetchablePaginationQueryWithoutConflictingArgs_FirstFragment" + }, + { + "args": [ + { + "kind": "Variable", + "name": "site", + "variableName": "site" + } + ], + "kind": "FragmentSpread", + "name": "prefetchablePaginationQueryWithoutConflictingArgs_SecondFragment" + } + ], + "storageKey": "node(id:\"x\")" + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "site" + } + ], + "kind": "Operation", + "name": "prefetchablePaginationQueryWithoutConflictingArgsQuery", + "selections": [ + { + "alias": null, + "args": [ + { + "kind": "Literal", + "name": "id", + "value": "x" + } + ], + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + }, + { + "kind": "InlineFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + }, + { + "alias": null, + "args": [ + { + "kind": "Literal", + "name": "first", + "value": 10 + } + ], + "concreteType": "FriendsConnection", + "kind": "LinkedField", + "name": "friends", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "FriendsEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + }, + { + "alias": null, + "args": [ + { + "kind": "Variable", + "name": "site", + "variableName": "site" + } + ], + "kind": "ScalarField", + "name": "url", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null + } + ], + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "cursor", + "storageKey": null + } + ], + "storageKey": null + }, + { + "alias": null, + "args": null, + "concreteType": "PageInfo", + "kind": "LinkedField", + "name": "pageInfo", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "endCursor", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "hasNextPage", + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": "friends(first:10)" + }, + { + "alias": null, + "args": [ + { + "kind": "Literal", + "name": "first", + "value": 10 + } + ], + "filters": null, + "handle": "connection", + "key": "PaginationFragment_friends", + "kind": "LinkedHandle", + "name": "friends" + } + ], + "type": "Page", + "abstractKey": null + }, + { + "kind": "InlineFragment", + "selections": [ + { + "alias": null, + "args": [ + { + "kind": "Literal", + "name": "first", + "value": 10 + } + ], + "concreteType": "FriendsConnection", + "kind": "LinkedField", + "name": "friends", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "FriendsEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + }, + { + "alias": null, + "args": [ + { + "kind": "Variable", + "name": "site", + "variableName": "site" + } + ], + "kind": "ScalarField", + "name": "url", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null + } + ], + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "cursor", + "storageKey": null + } + ], + "storageKey": null + }, + { + "alias": null, + "args": null, + "concreteType": "PageInfo", + "kind": "LinkedField", + "name": "pageInfo", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "endCursor", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "hasNextPage", + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": "friends(first:10)" + }, + { + "alias": null, + "args": [ + { + "kind": "Literal", + "name": "first", + "value": 10 + } + ], + "filters": null, + "handle": "connection", + "key": "PaginationFragment2_friends", + "kind": "LinkedHandle", + "name": "friends" + } + ], + "type": "User", + "abstractKey": null + } + ], + "storageKey": "node(id:\"x\")" + } + ] + }, + "params": { + "cacheID": "61508c3bf1b78d9d4b7d6609870e28da", + "id": null, + "metadata": {}, + "name": "prefetchablePaginationQueryWithoutConflictingArgsQuery", + "operationKind": "query", + "text": null + } +} + +QUERY: + +query prefetchablePaginationQueryWithoutConflictingArgsQuery( + $site: String +) { + node(id: "x") { + __typename + ...prefetchablePaginationQueryWithoutConflictingArgs_FirstFragment_QaI6y + ...prefetchablePaginationQueryWithoutConflictingArgs_SecondFragment_QaI6y + id + } +} + +fragment prefetchablePaginationQueryWithoutConflictingArgs_FirstFragment_QaI6y on Page { + name + friends(first: 10) { + edges { + ...prefetchablePaginationQueryWithoutConflictingArgs_FirstFragment__edges_2VKHkd + } + pageInfo { + endCursor + hasNextPage + } + } + id +} + +fragment prefetchablePaginationQueryWithoutConflictingArgs_FirstFragment__edges_2VKHkd on FriendsEdge { + node { + id + url(site: $site) + __typename + } + cursor +} + +fragment prefetchablePaginationQueryWithoutConflictingArgs_SecondFragment_QaI6y on User { + id + friends(first: 10) { + edges { + node { + id + url(site: $site) + __typename + } + cursor + } + pageInfo { + endCursor + hasNextPage + } + } +} + + +{ + "argumentDefinitions": [ + { + "defaultValue": 10, + "kind": "LocalArgument", + "name": "count" + }, + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "cursor" + }, + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "site" + } + ], + "kind": "Fragment", + "metadata": { + "connection": [ + { + "count": "count", + "cursor": "cursor", + "direction": "forward", + "path": [ + "friends" + ] + } + ], + "refetch": { + "connection": { + "forward": { + "count": "count", + "cursor": "cursor" + }, + "backward": null, + "path": [ + "friends" + ] + }, + "fragmentPathInResult": [ + "node" + ], + "operation": require('RefetchableFragmentQuery.graphql'), + "identifierInfo": { + "identifierField": "id", + "identifierQueryVariableName": "id" + }, + "edgesFragment": require('prefetchablePaginationQueryWithoutConflictingArgs_FirstFragment__edges.graphql') + } + }, + "name": "prefetchablePaginationQueryWithoutConflictingArgs_FirstFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + }, + { + "alias": "friends", + "args": null, + "concreteType": "FriendsConnection", + "kind": "LinkedField", + "name": "__PaginationFragment_friends_connection", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "FriendsEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + { + "args": [ + { + "kind": "Variable", + "name": "count", + "variableName": "count" + }, + { + "kind": "Variable", + "name": "cursor", + "variableName": "cursor" + }, + { + "kind": "Variable", + "name": "site", + "variableName": "site" + } + ], + "kind": "FragmentSpread", + "name": "prefetchablePaginationQueryWithoutConflictingArgs_FirstFragment__edges" + } + ], + "storageKey": null + }, + { + "alias": null, + "args": null, + "concreteType": "PageInfo", + "kind": "LinkedField", + "name": "pageInfo", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "endCursor", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "hasNextPage", + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + } + ], + "type": "Page", + "abstractKey": null +} + +{ + "argumentDefinitions": [ + { + "defaultValue": 10, + "kind": "LocalArgument", + "name": "count" + }, + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "cursor" + }, + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "site" + } + ], + "kind": "Fragment", + "metadata": { + "plural": true + }, + "name": "prefetchablePaginationQueryWithoutConflictingArgs_FirstFragment__edges", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + }, + { + "alias": null, + "args": [ + { + "kind": "Variable", + "name": "site", + "variableName": "site" + } + ], + "kind": "ScalarField", + "name": "url", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null + } + ], + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "cursor", + "storageKey": null + } + ], + "type": "FriendsEdge", + "abstractKey": null +} + +{ + "argumentDefinitions": [ + { + "defaultValue": 10, + "kind": "LocalArgument", + "name": "count" + }, + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "cursor" + }, + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "site" + } + ], + "kind": "Fragment", + "metadata": { + "connection": [ + { + "count": "count", + "cursor": "cursor", + "direction": "forward", + "path": [ + "friends" + ] + } + ] + }, + "name": "prefetchablePaginationQueryWithoutConflictingArgs_SecondFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + }, + { + "alias": "friends", + "args": null, + "concreteType": "FriendsConnection", + "kind": "LinkedField", + "name": "__PaginationFragment2_friends_connection", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "FriendsEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + }, + { + "alias": null, + "args": [ + { + "kind": "Variable", + "name": "site", + "variableName": "site" + } + ], + "kind": "ScalarField", + "name": "url", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null + } + ], + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "cursor", + "storageKey": null + } + ], + "storageKey": null + }, + { + "alias": null, + "args": null, + "concreteType": "PageInfo", + "kind": "LinkedField", + "name": "pageInfo", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "endCursor", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "hasNextPage", + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": null + } + ], + "type": "User", + "abstractKey": null +} diff --git a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/prefetchable-pagination-query-without-conflicting-args.graphql b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/prefetchable-pagination-query-without-conflicting-args.graphql new file mode 100644 index 0000000000000..cab3ddda2e68c --- /dev/null +++ b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/prefetchable-pagination-query-without-conflicting-args.graphql @@ -0,0 +1,43 @@ +query prefetchablePaginationQueryWithoutConflictingArgsQuery($site: String) { + node(id: "x") { + ...prefetchablePaginationQueryWithoutConflictingArgs_FirstFragment @arguments(site: $site) + ...prefetchablePaginationQueryWithoutConflictingArgs_SecondFragment @arguments(site: $site) + } +} + +fragment prefetchablePaginationQueryWithoutConflictingArgs_FirstFragment on Page + @refetchable(queryName: "RefetchableFragmentQuery") + @argumentDefinitions( + count: {type: "Int", defaultValue: 10} + cursor: {type: "ID"} + site: {type: "String"} + ) { + name + friends(after: $cursor, first: $count) + @connection(key: "PaginationFragment_friends", prefetchable_pagination: true) { + edges { + node { + id + url(site: $site) + } + } + } +} + +fragment prefetchablePaginationQueryWithoutConflictingArgs_SecondFragment on User + @argumentDefinitions( + count: {type: "Int", defaultValue: 10} + cursor: {type: "ID"} + site: {type: "String"} + ) { + id + friends(after: $cursor, first: $count) + @connection(key: "PaginationFragment2_friends") { + edges { + node { + id + url(site: $site) + } + } + } +} diff --git a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/prefetchable-refetchable-fragment-with-connection.expected b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/prefetchable-refetchable-fragment-with-connection.expected index eaa1126e760cb..8de231abb0395 100644 --- a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/prefetchable-refetchable-fragment-with-connection.expected +++ b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/prefetchable-refetchable-fragment-with-connection.expected @@ -293,7 +293,7 @@ fragment prefetchableRefetchableFragmentWithConnection_PaginationFragment_1G22uz name friends(after: $cursor, first: $count) { edges { - ...prefetchableRefetchableFragmentWithConnection_PaginationFragment__edges + ...prefetchableRefetchableFragmentWithConnection_PaginationFragment__edges_1G22uz } pageInfo { endCursor @@ -303,7 +303,7 @@ fragment prefetchableRefetchableFragmentWithConnection_PaginationFragment_1G22uz } } -fragment prefetchableRefetchableFragmentWithConnection_PaginationFragment__edges on FriendsEdge { +fragment prefetchableRefetchableFragmentWithConnection_PaginationFragment__edges_1G22uz on FriendsEdge { node { id __typename @@ -395,7 +395,18 @@ fragment prefetchableRefetchableFragmentWithConnection_PaginationFragment__edges "plural": true, "selections": [ { - "args": null, + "args": [ + { + "kind": "Variable", + "name": "count", + "variableName": "count" + }, + { + "kind": "Variable", + "name": "cursor", + "variableName": "cursor" + } + ], "kind": "FragmentSpread", "name": "prefetchableRefetchableFragmentWithConnection_PaginationFragment__edges" } @@ -440,7 +451,18 @@ fragment prefetchableRefetchableFragmentWithConnection_PaginationFragment__edges } { - "argumentDefinitions": [], + "argumentDefinitions": [ + { + "defaultValue": 10, + "kind": "LocalArgument", + "name": "count" + }, + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "cursor" + } + ], "kind": "Fragment", "metadata": { "plural": true diff --git a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/provided-variable-directive-commonjs-relativize-disabled.expected b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/provided-variable-directive-commonjs-relativize-disabled.expected new file mode 100644 index 0000000000000..4b7f610525727 --- /dev/null +++ b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/provided-variable-directive-commonjs-relativize-disabled.expected @@ -0,0 +1,239 @@ +==================================== INPUT ==================================== +query providedVariableDirectiveCommonjsRelativizeDisabledQuery { + me { + ...providedVariableDirectiveCommonjsRelativizeDisabledFragment + } +} + +fragment providedVariableDirectiveCommonjsRelativizeDisabledFragment on User + @argumentDefinitions( + condA: {type: "Boolean!", provider: "bareProvider" }, + condB: {type: "Boolean!", provider: "./relProvider" }, + condC: {type: "Boolean!", provider: "../parentProvider" }, + ) { + username @include(if: $condA) + name @include(if: $condB) + alternate_name @include(if: $condC) +} + +%project_config% +{ + "jsModuleFormat": "commonjs", + "relativizeJsModulePaths": false, + "language": "flow" +} +==================================== OUTPUT =================================== +import condC_provider from '../../parentProvider'; +import condB_provider from '.././relProvider'; +import condA_provider from 'bareProvider'; +{ + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "providedVariableDirectiveCommonjsRelativizeDisabledQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "me", + "plural": false, + "selections": [ + { + "args": null, + "kind": "FragmentSpread", + "name": "providedVariableDirectiveCommonjsRelativizeDisabledFragment" + } + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "__relay_internal__pv__bareProvider" + }, + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "__relay_internal__pv__relProvider" + }, + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "__relay_internal__pv__parentProvider" + } + ], + "kind": "Operation", + "name": "providedVariableDirectiveCommonjsRelativizeDisabledQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "me", + "plural": false, + "selections": [ + { + "condition": "__relay_internal__pv__bareProvider", + "kind": "Condition", + "passingValue": true, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "username", + "storageKey": null + } + ] + }, + { + "condition": "__relay_internal__pv__relProvider", + "kind": "Condition", + "passingValue": true, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + } + ] + }, + { + "condition": "__relay_internal__pv__parentProvider", + "kind": "Condition", + "passingValue": true, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "alternate_name", + "storageKey": null + } + ] + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "39a6590f6b52a674c0c593ed4494a130", + "id": null, + "metadata": {}, + "name": "providedVariableDirectiveCommonjsRelativizeDisabledQuery", + "operationKind": "query", + "text": null, + "providedVariables": { + "__relay_internal__pv__bareProvider": condA_provider, + "__relay_internal__pv__relProvider": condB_provider, + "__relay_internal__pv__parentProvider": condC_provider + } + } +} + +QUERY: + +query providedVariableDirectiveCommonjsRelativizeDisabledQuery( + $__relay_internal__pv__bareProvider: Boolean! + $__relay_internal__pv__relProvider: Boolean! + $__relay_internal__pv__parentProvider: Boolean! +) { + me { + ...providedVariableDirectiveCommonjsRelativizeDisabledFragment + id + } +} + +fragment providedVariableDirectiveCommonjsRelativizeDisabledFragment on User { + username @include(if: $__relay_internal__pv__bareProvider) + name @include(if: $__relay_internal__pv__relProvider) + alternate_name @include(if: $__relay_internal__pv__parentProvider) +} + + +{ + "argumentDefinitions": [ + { + "kind": "RootArgument", + "name": "__relay_internal__pv__bareProvider" + }, + { + "kind": "RootArgument", + "name": "__relay_internal__pv__parentProvider" + }, + { + "kind": "RootArgument", + "name": "__relay_internal__pv__relProvider" + } + ], + "kind": "Fragment", + "metadata": null, + "name": "providedVariableDirectiveCommonjsRelativizeDisabledFragment", + "selections": [ + { + "condition": "__relay_internal__pv__bareProvider", + "kind": "Condition", + "passingValue": true, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "username", + "storageKey": null + } + ] + }, + { + "condition": "__relay_internal__pv__relProvider", + "kind": "Condition", + "passingValue": true, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + } + ] + }, + { + "condition": "__relay_internal__pv__parentProvider", + "kind": "Condition", + "passingValue": true, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "alternate_name", + "storageKey": null + } + ] + } + ], + "type": "User", + "abstractKey": null +} diff --git a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/provided-variable-directive-commonjs-relativize-disabled.graphql b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/provided-variable-directive-commonjs-relativize-disabled.graphql new file mode 100644 index 0000000000000..72d64ecb72f44 --- /dev/null +++ b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/provided-variable-directive-commonjs-relativize-disabled.graphql @@ -0,0 +1,23 @@ +query providedVariableDirectiveCommonjsRelativizeDisabledQuery { + me { + ...providedVariableDirectiveCommonjsRelativizeDisabledFragment + } +} + +fragment providedVariableDirectiveCommonjsRelativizeDisabledFragment on User + @argumentDefinitions( + condA: {type: "Boolean!", provider: "bareProvider" }, + condB: {type: "Boolean!", provider: "./relProvider" }, + condC: {type: "Boolean!", provider: "../parentProvider" }, + ) { + username @include(if: $condA) + name @include(if: $condB) + alternate_name @include(if: $condC) +} + +%project_config% +{ + "jsModuleFormat": "commonjs", + "relativizeJsModulePaths": false, + "language": "flow" +} diff --git a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/provided-variable-directive-commonjs-relativize-enabled.expected b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/provided-variable-directive-commonjs-relativize-enabled.expected new file mode 100644 index 0000000000000..0f1445d439e48 --- /dev/null +++ b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/provided-variable-directive-commonjs-relativize-enabled.expected @@ -0,0 +1,239 @@ +==================================== INPUT ==================================== +query providedVariableDirectiveCommonjsRelativizeEnabledQuery { + me { + ...providedVariableDirectiveCommonjsRelativizeEnabledFragment + } +} + +fragment providedVariableDirectiveCommonjsRelativizeEnabledFragment on User + @argumentDefinitions( + condA: {type: "Boolean!", provider: "bareProvider" }, + condB: {type: "Boolean!", provider: "./relProvider" }, + condC: {type: "Boolean!", provider: "../parentProvider" }, + ) { + username @include(if: $condA) + name @include(if: $condB) + alternate_name @include(if: $condC) +} + +%project_config% +{ + "jsModuleFormat": "commonjs", + "relativizeJsModulePaths": true, + "language": "flow" +} +==================================== OUTPUT =================================== +import condC_provider from '../../parentProvider'; +import condB_provider from '.././relProvider'; +import condA_provider from '../bareProvider'; +{ + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "providedVariableDirectiveCommonjsRelativizeEnabledQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "me", + "plural": false, + "selections": [ + { + "args": null, + "kind": "FragmentSpread", + "name": "providedVariableDirectiveCommonjsRelativizeEnabledFragment" + } + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "__relay_internal__pv__bareProvider" + }, + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "__relay_internal__pv__relProvider" + }, + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "__relay_internal__pv__parentProvider" + } + ], + "kind": "Operation", + "name": "providedVariableDirectiveCommonjsRelativizeEnabledQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "me", + "plural": false, + "selections": [ + { + "condition": "__relay_internal__pv__bareProvider", + "kind": "Condition", + "passingValue": true, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "username", + "storageKey": null + } + ] + }, + { + "condition": "__relay_internal__pv__relProvider", + "kind": "Condition", + "passingValue": true, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + } + ] + }, + { + "condition": "__relay_internal__pv__parentProvider", + "kind": "Condition", + "passingValue": true, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "alternate_name", + "storageKey": null + } + ] + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "76e93fe5c27fdd1b1af17f4e3e9b4d0d", + "id": null, + "metadata": {}, + "name": "providedVariableDirectiveCommonjsRelativizeEnabledQuery", + "operationKind": "query", + "text": null, + "providedVariables": { + "__relay_internal__pv__bareProvider": condA_provider, + "__relay_internal__pv__relProvider": condB_provider, + "__relay_internal__pv__parentProvider": condC_provider + } + } +} + +QUERY: + +query providedVariableDirectiveCommonjsRelativizeEnabledQuery( + $__relay_internal__pv__bareProvider: Boolean! + $__relay_internal__pv__relProvider: Boolean! + $__relay_internal__pv__parentProvider: Boolean! +) { + me { + ...providedVariableDirectiveCommonjsRelativizeEnabledFragment + id + } +} + +fragment providedVariableDirectiveCommonjsRelativizeEnabledFragment on User { + username @include(if: $__relay_internal__pv__bareProvider) + name @include(if: $__relay_internal__pv__relProvider) + alternate_name @include(if: $__relay_internal__pv__parentProvider) +} + + +{ + "argumentDefinitions": [ + { + "kind": "RootArgument", + "name": "__relay_internal__pv__bareProvider" + }, + { + "kind": "RootArgument", + "name": "__relay_internal__pv__parentProvider" + }, + { + "kind": "RootArgument", + "name": "__relay_internal__pv__relProvider" + } + ], + "kind": "Fragment", + "metadata": null, + "name": "providedVariableDirectiveCommonjsRelativizeEnabledFragment", + "selections": [ + { + "condition": "__relay_internal__pv__bareProvider", + "kind": "Condition", + "passingValue": true, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "username", + "storageKey": null + } + ] + }, + { + "condition": "__relay_internal__pv__relProvider", + "kind": "Condition", + "passingValue": true, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + } + ] + }, + { + "condition": "__relay_internal__pv__parentProvider", + "kind": "Condition", + "passingValue": true, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "alternate_name", + "storageKey": null + } + ] + } + ], + "type": "User", + "abstractKey": null +} diff --git a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/provided-variable-directive-commonjs-relativize-enabled.graphql b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/provided-variable-directive-commonjs-relativize-enabled.graphql new file mode 100644 index 0000000000000..c01131176e2a5 --- /dev/null +++ b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/provided-variable-directive-commonjs-relativize-enabled.graphql @@ -0,0 +1,23 @@ +query providedVariableDirectiveCommonjsRelativizeEnabledQuery { + me { + ...providedVariableDirectiveCommonjsRelativizeEnabledFragment + } +} + +fragment providedVariableDirectiveCommonjsRelativizeEnabledFragment on User + @argumentDefinitions( + condA: {type: "Boolean!", provider: "bareProvider" }, + condB: {type: "Boolean!", provider: "./relProvider" }, + condC: {type: "Boolean!", provider: "../parentProvider" }, + ) { + username @include(if: $condA) + name @include(if: $condB) + alternate_name @include(if: $condC) +} + +%project_config% +{ + "jsModuleFormat": "commonjs", + "relativizeJsModulePaths": true, + "language": "flow" +} diff --git a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/provided-variable-directive-commonjs.expected b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/provided-variable-directive-commonjs.expected new file mode 100644 index 0000000000000..c5e59bc30b478 --- /dev/null +++ b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/provided-variable-directive-commonjs.expected @@ -0,0 +1,238 @@ +==================================== INPUT ==================================== +query providedVariableDirectiveCommonjsRelativizeDefaultQuery { + me { + ...providedVariableDirectiveCommonjsRelativizeDefaultFragment + } +} + +fragment providedVariableDirectiveCommonjsRelativizeDefaultFragment on User + @argumentDefinitions( + condA: {type: "Boolean!", provider: "bareProvider" }, + condB: {type: "Boolean!", provider: "./relProvider" }, + condC: {type: "Boolean!", provider: "../parentProvider" }, + ) { + username @include(if: $condA) + name @include(if: $condB) + alternate_name @include(if: $condC) +} + +%project_config% +{ + "jsModuleFormat": "commonjs", + "language": "flow" +} +==================================== OUTPUT =================================== +import condC_provider from '../../parentProvider'; +import condB_provider from '.././relProvider'; +import condA_provider from '../bareProvider'; +{ + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "providedVariableDirectiveCommonjsRelativizeDefaultQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "me", + "plural": false, + "selections": [ + { + "args": null, + "kind": "FragmentSpread", + "name": "providedVariableDirectiveCommonjsRelativizeDefaultFragment" + } + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "__relay_internal__pv__bareProvider" + }, + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "__relay_internal__pv__relProvider" + }, + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "__relay_internal__pv__parentProvider" + } + ], + "kind": "Operation", + "name": "providedVariableDirectiveCommonjsRelativizeDefaultQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "me", + "plural": false, + "selections": [ + { + "condition": "__relay_internal__pv__bareProvider", + "kind": "Condition", + "passingValue": true, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "username", + "storageKey": null + } + ] + }, + { + "condition": "__relay_internal__pv__relProvider", + "kind": "Condition", + "passingValue": true, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + } + ] + }, + { + "condition": "__relay_internal__pv__parentProvider", + "kind": "Condition", + "passingValue": true, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "alternate_name", + "storageKey": null + } + ] + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "f458fb2591f6867a48f155ee137dbc65", + "id": null, + "metadata": {}, + "name": "providedVariableDirectiveCommonjsRelativizeDefaultQuery", + "operationKind": "query", + "text": null, + "providedVariables": { + "__relay_internal__pv__bareProvider": condA_provider, + "__relay_internal__pv__relProvider": condB_provider, + "__relay_internal__pv__parentProvider": condC_provider + } + } +} + +QUERY: + +query providedVariableDirectiveCommonjsRelativizeDefaultQuery( + $__relay_internal__pv__bareProvider: Boolean! + $__relay_internal__pv__relProvider: Boolean! + $__relay_internal__pv__parentProvider: Boolean! +) { + me { + ...providedVariableDirectiveCommonjsRelativizeDefaultFragment + id + } +} + +fragment providedVariableDirectiveCommonjsRelativizeDefaultFragment on User { + username @include(if: $__relay_internal__pv__bareProvider) + name @include(if: $__relay_internal__pv__relProvider) + alternate_name @include(if: $__relay_internal__pv__parentProvider) +} + + +{ + "argumentDefinitions": [ + { + "kind": "RootArgument", + "name": "__relay_internal__pv__bareProvider" + }, + { + "kind": "RootArgument", + "name": "__relay_internal__pv__parentProvider" + }, + { + "kind": "RootArgument", + "name": "__relay_internal__pv__relProvider" + } + ], + "kind": "Fragment", + "metadata": null, + "name": "providedVariableDirectiveCommonjsRelativizeDefaultFragment", + "selections": [ + { + "condition": "__relay_internal__pv__bareProvider", + "kind": "Condition", + "passingValue": true, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "username", + "storageKey": null + } + ] + }, + { + "condition": "__relay_internal__pv__relProvider", + "kind": "Condition", + "passingValue": true, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + } + ] + }, + { + "condition": "__relay_internal__pv__parentProvider", + "kind": "Condition", + "passingValue": true, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "alternate_name", + "storageKey": null + } + ] + } + ], + "type": "User", + "abstractKey": null +} diff --git a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/provided-variable-directive-commonjs.graphql b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/provided-variable-directive-commonjs.graphql new file mode 100644 index 0000000000000..6e3502befa884 --- /dev/null +++ b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/provided-variable-directive-commonjs.graphql @@ -0,0 +1,22 @@ +query providedVariableDirectiveCommonjsRelativizeDefaultQuery { + me { + ...providedVariableDirectiveCommonjsRelativizeDefaultFragment + } +} + +fragment providedVariableDirectiveCommonjsRelativizeDefaultFragment on User + @argumentDefinitions( + condA: {type: "Boolean!", provider: "bareProvider" }, + condB: {type: "Boolean!", provider: "./relProvider" }, + condC: {type: "Boolean!", provider: "../parentProvider" }, + ) { + username @include(if: $condA) + name @include(if: $condB) + alternate_name @include(if: $condC) +} + +%project_config% +{ + "jsModuleFormat": "commonjs", + "language": "flow" +} diff --git a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/refetchable-fragment-on-node-and-fetchable-arg.expected b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/refetchable-fragment-on-node-and-fetchable-arg.expected index 4e8fe2c933413..120dd981e118d 100644 --- a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/refetchable-fragment-on-node-and-fetchable-arg.expected +++ b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/refetchable-fragment-on-node-and-fetchable-arg.expected @@ -82,13 +82,6 @@ fragment refetchableFragmentOnNodeAndFetchableArg_RefetchableFragment on Fetchab "kind": "ScalarField", "name": "fetch_id", "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "__token", - "storageKey": null } ], "storageKey": null @@ -119,7 +112,6 @@ query RefetchableFragmentQuery( fragment refetchableFragmentOnNodeAndFetchableArg_RefetchableFragment on FetchableType { id fetch_id - __token } @@ -154,13 +146,6 @@ fragment refetchableFragmentOnNodeAndFetchableArg_RefetchableFragment on Fetchab "kind": "ScalarField", "name": "fetch_id", "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "__token", - "storageKey": null } ], "type": "FetchableType", diff --git a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/refetchable-fragment-on-node-and-fetchable.expected b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/refetchable-fragment-on-node-and-fetchable.expected index 460c33e7373dc..6b06a9dd96626 100644 --- a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/refetchable-fragment-on-node-and-fetchable.expected +++ b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/refetchable-fragment-on-node-and-fetchable.expected @@ -84,13 +84,6 @@ fragment refetchableFragmentOnNodeAndFetchable_RefetchableFragment on FetchableT "kind": "ScalarField", "name": "fetch_id", "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "__token", - "storageKey": null } ], "storageKey": null @@ -121,7 +114,6 @@ query RefetchableFragmentQuery( fragment refetchableFragmentOnNodeAndFetchable_RefetchableFragment on FetchableType { id fetch_id - __token } @@ -156,13 +148,6 @@ fragment refetchableFragmentOnNodeAndFetchable_RefetchableFragment on FetchableT "kind": "ScalarField", "name": "fetch_id", "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "__token", - "storageKey": null } ], "type": "FetchableType", diff --git a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/refetchable-fragment-with-connection-es-modules.expected b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/refetchable-fragment-with-connection-es-modules.expected index e6e18c31fcfff..26c85b56ab0ec 100644 --- a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/refetchable-fragment-with-connection-es-modules.expected +++ b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/refetchable-fragment-with-connection-es-modules.expected @@ -21,7 +21,6 @@ fragment refetchableFragmentWithConnectionEsModules_PaginationFragment on Node %project_config% { - "eagerEsModules": true, "language": "flow" } ==================================== OUTPUT =================================== diff --git a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/refetchable-fragment-with-connection-es-modules.graphql b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/refetchable-fragment-with-connection-es-modules.graphql index 1884b68042ebe..4c2808fa15dc9 100644 --- a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/refetchable-fragment-with-connection-es-modules.graphql +++ b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/refetchable-fragment-with-connection-es-modules.graphql @@ -20,6 +20,5 @@ fragment refetchableFragmentWithConnectionEsModules_PaginationFragment on Node %project_config% { - "eagerEsModules": true, "language": "flow" } diff --git a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/refetchable-with-arguments.invalid.expected b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/refetchable-with-arguments.invalid.expected deleted file mode 100644 index 216e330f65e64..0000000000000 --- a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/refetchable-with-arguments.invalid.expected +++ /dev/null @@ -1,30 +0,0 @@ -==================================== INPUT ==================================== -# expected-to-throw - -query refetchableWithArgumentsQuery($site: String) { - node(id: "x") { - ...refetchableWithArgumentsF1 - } -} - -fragment refetchableWithArgumentsF1 on Node - @argumentDefinitions(site: {type: "String"}) - @refetchable(queryName: "RefetchableWithArgumentsRefetchQuery") -{ - # local $site - p1: url(site: $site) - ...refetchableWithArgumentsF2 -} - -fragment refetchableWithArgumentsF2 on Node { - # global $site - p2: url(site: $site) -} -==================================== ERROR ==================================== -✖︎ Fragment variable `$site` conflicts with a global variable generated by the @refetchable generated query - - refetchable-with-arguments.invalid.graphql:10:24 - 9 │ fragment refetchableWithArgumentsF1 on Node - 10 │ @argumentDefinitions(site: {type: "String"}) - │ ^^^^ - 11 │ @refetchable(queryName: "RefetchableWithArgumentsRefetchQuery") diff --git a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/refetchable-with-arguments.invalid.graphql b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/refetchable-with-arguments.invalid.graphql deleted file mode 100644 index 95a00bfe3171e..0000000000000 --- a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/refetchable-with-arguments.invalid.graphql +++ /dev/null @@ -1,21 +0,0 @@ -# expected-to-throw - -query refetchableWithArgumentsQuery($site: String) { - node(id: "x") { - ...refetchableWithArgumentsF1 - } -} - -fragment refetchableWithArgumentsF1 on Node - @argumentDefinitions(site: {type: "String"}) - @refetchable(queryName: "RefetchableWithArgumentsRefetchQuery") -{ - # local $site - p1: url(site: $site) - ...refetchableWithArgumentsF2 -} - -fragment refetchableWithArgumentsF2 on Node { - # global $site - p2: url(site: $site) -} diff --git a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/relay-resolver-es-modules.expected b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/relay-resolver-es-modules.expected index 7e9bfdf972ed7..0ae153bc01089 100644 --- a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/relay-resolver-es-modules.expected +++ b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/relay-resolver-es-modules.expected @@ -20,7 +20,6 @@ extend type User { %project_config% { - "eagerEsModules": true, "language": "flow", "jsModuleFormat": "haste" } diff --git a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/relay-resolver-es-modules.graphql b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/relay-resolver-es-modules.graphql index 3c095b987958f..a9e0fee06a98a 100644 --- a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/relay-resolver-es-modules.graphql +++ b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/relay-resolver-es-modules.graphql @@ -19,7 +19,6 @@ extend type User { %project_config% { - "eagerEsModules": true, "language": "flow", "jsModuleFormat": "haste" } diff --git a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/relay-test-operation.expected b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/relay-test-operation.expected new file mode 100644 index 0000000000000..6a413e8d76ba0 --- /dev/null +++ b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/relay-test-operation.expected @@ -0,0 +1,154 @@ +==================================== INPUT ==================================== +query relayTestOperation_TestQuery @relay_test_operation(emitRawText: true) { + viewer { + actor { + name + } + } +} +==================================== OUTPUT =================================== +{ + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "relayTestOperation_TestQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "Viewer", + "kind": "LinkedField", + "name": "viewer", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": null, + "kind": "LinkedField", + "name": "actor", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [], + "kind": "Operation", + "name": "relayTestOperation_TestQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "Viewer", + "kind": "LinkedField", + "name": "viewer", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": null, + "kind": "LinkedField", + "name": "actor", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "bcef98ee9908f55df74d310f9cfd2ee6", + "id": null, + "metadata": { + "relayTestingSelectionTypeInfo": { + "viewer": { + "enumValues": null, + "nullable": true, + "plural": false, + "type": "Viewer" + }, + "viewer.actor": { + "enumValues": null, + "nullable": true, + "plural": false, + "type": "Actor" + }, + "viewer.actor.__typename": { + "enumValues": null, + "nullable": false, + "plural": false, + "type": "String" + }, + "viewer.actor.id": { + "enumValues": null, + "nullable": false, + "plural": false, + "type": "ID" + }, + "viewer.actor.name": { + "enumValues": null, + "nullable": true, + "plural": false, + "type": "String" + } + } + }, + "name": "relayTestOperation_TestQuery", + "operationKind": "query", + "text": "query relayTestOperation_TestQuery @relay_test_operation(emitRawText: true) {\n viewer {\n actor {\n name\n }\n }\n}\n" + } +} + +QUERY: + +query relayTestOperation_TestQuery { + viewer { + actor { + __typename + name + id + } + } +} diff --git a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/relay-test-operation.graphql b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/relay-test-operation.graphql new file mode 100644 index 0000000000000..d2c0a96580d54 --- /dev/null +++ b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/relay-test-operation.graphql @@ -0,0 +1,7 @@ +query relayTestOperation_TestQuery @relay_test_operation(emitRawText: true) { + viewer { + actor { + name + } + } +} diff --git a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/updatable-fragment-spread-with-dangerously-unaliased-fixme.expected b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/updatable-fragment-spread-with-dangerously-unaliased-fixme.expected new file mode 100644 index 0000000000000..b53de0eb6ae2a --- /dev/null +++ b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/updatable-fragment-spread-with-dangerously-unaliased-fixme.expected @@ -0,0 +1,108 @@ +==================================== INPUT ==================================== +query updatableFragmentSpreadWithDangerouslyUnaliasedFixmeQuery { + me { + ...updatableFragmentSpreadWithDangerouslyUnaliasedFixme_user @dangerously_unaliased_fixme + } +} + +fragment updatableFragmentSpreadWithDangerouslyUnaliasedFixme_user on User @updatable { + firstName +} +==================================== OUTPUT =================================== +{ + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "updatableFragmentSpreadWithDangerouslyUnaliasedFixmeQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "me", + "plural": false, + "selections": [ + { + "args": null, + "kind": "FragmentSpread", + "name": "updatableFragmentSpreadWithDangerouslyUnaliasedFixme_user" + } + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [], + "kind": "Operation", + "name": "updatableFragmentSpreadWithDangerouslyUnaliasedFixmeQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "me", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "9c1625f473e47737ba5ee76d36681bd4", + "id": null, + "metadata": {}, + "name": "updatableFragmentSpreadWithDangerouslyUnaliasedFixmeQuery", + "operationKind": "query", + "text": null + } +} + +QUERY: + +query updatableFragmentSpreadWithDangerouslyUnaliasedFixmeQuery { + me { + __typename + id + } +} + + +{ + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "updatableFragmentSpreadWithDangerouslyUnaliasedFixme_user", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "firstName", + "storageKey": null + } + ], + "type": "User", + "abstractKey": null +} diff --git a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/updatable-fragment-spread-with-dangerously-unaliased-fixme.graphql b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/updatable-fragment-spread-with-dangerously-unaliased-fixme.graphql new file mode 100644 index 0000000000000..19dc838b70fdc --- /dev/null +++ b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/updatable-fragment-spread-with-dangerously-unaliased-fixme.graphql @@ -0,0 +1,9 @@ +query updatableFragmentSpreadWithDangerouslyUnaliasedFixmeQuery { + me { + ...updatableFragmentSpreadWithDangerouslyUnaliasedFixme_user @dangerously_unaliased_fixme + } +} + +fragment updatableFragmentSpreadWithDangerouslyUnaliasedFixme_user on User @updatable { + firstName +} diff --git a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/updatable-fragment-spread-with-defer.invalid.expected b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/updatable-fragment-spread-with-defer.invalid.expected index 3809713de3858..2e855edc9ee08 100644 --- a/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/updatable-fragment-spread-with-defer.invalid.expected +++ b/compiler/crates/relay-compiler/tests/compile_relay_artifacts/fixtures/updatable-fragment-spread-with-defer.invalid.expected @@ -12,8 +12,8 @@ fragment updatableFragmentSpreadWithDefer_user on User @updatable { ==================================== ERROR ==================================== ✖︎ Directives are not allowed on spreads of updatable fragments. - updatable-fragment-spread-with-defer.invalid.graphql:4:8 + updatable-fragment-spread-with-defer.invalid.graphql:4:46 3 │ me { 4 │ ...updatableFragmentSpreadWithDefer_user @defer - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ ^^^^^^ 5 │ } diff --git a/compiler/crates/relay-compiler/tests/compile_relay_artifacts_test.rs b/compiler/crates/relay-compiler/tests/compile_relay_artifacts_test.rs index e5b2bfea3f8aa..cda842e6e73d5 100644 --- a/compiler/crates/relay-compiler/tests/compile_relay_artifacts_test.rs +++ b/compiler/crates/relay-compiler/tests/compile_relay_artifacts_test.rs @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<220a462d1e8062621d9ac165e8d6b30d>> + * @generated SignedSource<<67aa79681f4c3c1406944dd059174dc7>> */ mod compile_relay_artifacts; @@ -677,6 +677,20 @@ async fn fragment_on_query() { test_fixture(transform_fixture, file!(), "fragment-on-query.graphql", "compile_relay_artifacts/fixtures/fragment-on-query.expected", input, expected).await; } +#[tokio::test] +async fn fragment_on_query_commonjs() { + let input = include_str!("compile_relay_artifacts/fixtures/fragment-on-query-commonjs.graphql"); + let expected = include_str!("compile_relay_artifacts/fixtures/fragment-on-query-commonjs.expected"); + test_fixture(transform_fixture, file!(), "fragment-on-query-commonjs.graphql", "compile_relay_artifacts/fixtures/fragment-on-query-commonjs.expected", input, expected).await; +} + +#[tokio::test] +async fn fragment_on_query_commonjs_relativize_disabled() { + let input = include_str!("compile_relay_artifacts/fixtures/fragment-on-query-commonjs-relativize-disabled.graphql"); + let expected = include_str!("compile_relay_artifacts/fixtures/fragment-on-query-commonjs-relativize-disabled.expected"); + test_fixture(transform_fixture, file!(), "fragment-on-query-commonjs-relativize-disabled.graphql", "compile_relay_artifacts/fixtures/fragment-on-query-commonjs-relativize-disabled.expected", input, expected).await; +} + #[tokio::test] async fn fragment_on_query_with_cycle_invalid() { let input = include_str!("compile_relay_artifacts/fixtures/fragment-on-query-with-cycle.invalid.graphql"); @@ -992,6 +1006,20 @@ async fn plural_fragment() { test_fixture(transform_fixture, file!(), "plural-fragment.graphql", "compile_relay_artifacts/fixtures/plural-fragment.expected", input, expected).await; } +#[tokio::test] +async fn prefetchable_pagination_query_with_conflicting_args_invalid() { + let input = include_str!("compile_relay_artifacts/fixtures/prefetchable-pagination-query-with-conflicting-args.invalid.graphql"); + let expected = include_str!("compile_relay_artifacts/fixtures/prefetchable-pagination-query-with-conflicting-args.invalid.expected"); + test_fixture(transform_fixture, file!(), "prefetchable-pagination-query-with-conflicting-args.invalid.graphql", "compile_relay_artifacts/fixtures/prefetchable-pagination-query-with-conflicting-args.invalid.expected", input, expected).await; +} + +#[tokio::test] +async fn prefetchable_pagination_query_without_conflicting_args() { + let input = include_str!("compile_relay_artifacts/fixtures/prefetchable-pagination-query-without-conflicting-args.graphql"); + let expected = include_str!("compile_relay_artifacts/fixtures/prefetchable-pagination-query-without-conflicting-args.expected"); + test_fixture(transform_fixture, file!(), "prefetchable-pagination-query-without-conflicting-args.graphql", "compile_relay_artifacts/fixtures/prefetchable-pagination-query-without-conflicting-args.expected", input, expected).await; +} + #[tokio::test] async fn prefetchable_refetchable_fragment_with_connection() { let input = include_str!("compile_relay_artifacts/fixtures/prefetchable-refetchable-fragment-with-connection.graphql"); @@ -1013,6 +1041,27 @@ async fn provided_variable_directive() { test_fixture(transform_fixture, file!(), "provided-variable-directive.graphql", "compile_relay_artifacts/fixtures/provided-variable-directive.expected", input, expected).await; } +#[tokio::test] +async fn provided_variable_directive_commonjs() { + let input = include_str!("compile_relay_artifacts/fixtures/provided-variable-directive-commonjs.graphql"); + let expected = include_str!("compile_relay_artifacts/fixtures/provided-variable-directive-commonjs.expected"); + test_fixture(transform_fixture, file!(), "provided-variable-directive-commonjs.graphql", "compile_relay_artifacts/fixtures/provided-variable-directive-commonjs.expected", input, expected).await; +} + +#[tokio::test] +async fn provided_variable_directive_commonjs_relativize_disabled() { + let input = include_str!("compile_relay_artifacts/fixtures/provided-variable-directive-commonjs-relativize-disabled.graphql"); + let expected = include_str!("compile_relay_artifacts/fixtures/provided-variable-directive-commonjs-relativize-disabled.expected"); + test_fixture(transform_fixture, file!(), "provided-variable-directive-commonjs-relativize-disabled.graphql", "compile_relay_artifacts/fixtures/provided-variable-directive-commonjs-relativize-disabled.expected", input, expected).await; +} + +#[tokio::test] +async fn provided_variable_directive_commonjs_relativize_enabled() { + let input = include_str!("compile_relay_artifacts/fixtures/provided-variable-directive-commonjs-relativize-enabled.graphql"); + let expected = include_str!("compile_relay_artifacts/fixtures/provided-variable-directive-commonjs-relativize-enabled.expected"); + test_fixture(transform_fixture, file!(), "provided-variable-directive-commonjs-relativize-enabled.graphql", "compile_relay_artifacts/fixtures/provided-variable-directive-commonjs-relativize-enabled.expected", input, expected).await; +} + #[tokio::test] async fn provided_variable_in_fragment() { let input = include_str!("compile_relay_artifacts/fixtures/provided-variable-in-fragment.graphql"); @@ -1286,13 +1335,6 @@ async fn refetchable_with_arguments_conflicting_invalid() { test_fixture(transform_fixture, file!(), "refetchable-with-arguments-conflicting.invalid.graphql", "compile_relay_artifacts/fixtures/refetchable-with-arguments-conflicting.invalid.expected", input, expected).await; } -#[tokio::test] -async fn refetchable_with_arguments_invalid() { - let input = include_str!("compile_relay_artifacts/fixtures/refetchable-with-arguments.invalid.graphql"); - let expected = include_str!("compile_relay_artifacts/fixtures/refetchable-with-arguments.invalid.expected"); - test_fixture(transform_fixture, file!(), "refetchable-with-arguments.invalid.graphql", "compile_relay_artifacts/fixtures/refetchable-with-arguments.invalid.expected", input, expected).await; -} - #[tokio::test] async fn relay_client_id_field() { let input = include_str!("compile_relay_artifacts/fixtures/relay-client-id-field.graphql"); @@ -1552,6 +1594,13 @@ async fn relay_resolvers_with_different_field_args_are_not_merged() { test_fixture(transform_fixture, file!(), "relay-resolvers-with-different-field-args-are-not-merged.graphql", "compile_relay_artifacts/fixtures/relay-resolvers-with-different-field-args-are-not-merged.expected", input, expected).await; } +#[tokio::test] +async fn relay_test_operation() { + let input = include_str!("compile_relay_artifacts/fixtures/relay-test-operation.graphql"); + let expected = include_str!("compile_relay_artifacts/fixtures/relay-test-operation.expected"); + test_fixture(transform_fixture, file!(), "relay-test-operation.graphql", "compile_relay_artifacts/fixtures/relay-test-operation.expected", input, expected).await; +} + #[tokio::test] async fn required_argument_not_passed_default_value() { let input = include_str!("compile_relay_artifacts/fixtures/required_argument_not_passed_default_value.graphql"); @@ -2042,6 +2091,13 @@ async fn updatable_fragment_spread() { test_fixture(transform_fixture, file!(), "updatable-fragment-spread.graphql", "compile_relay_artifacts/fixtures/updatable-fragment-spread.expected", input, expected).await; } +#[tokio::test] +async fn updatable_fragment_spread_with_dangerously_unaliased_fixme() { + let input = include_str!("compile_relay_artifacts/fixtures/updatable-fragment-spread-with-dangerously-unaliased-fixme.graphql"); + let expected = include_str!("compile_relay_artifacts/fixtures/updatable-fragment-spread-with-dangerously-unaliased-fixme.expected"); + test_fixture(transform_fixture, file!(), "updatable-fragment-spread-with-dangerously-unaliased-fixme.graphql", "compile_relay_artifacts/fixtures/updatable-fragment-spread-with-dangerously-unaliased-fixme.expected", input, expected).await; +} + #[tokio::test] async fn updatable_fragment_spread_with_defer_invalid() { let input = include_str!("compile_relay_artifacts/fixtures/updatable-fragment-spread-with-defer.invalid.graphql"); diff --git a/compiler/crates/relay-compiler/tests/compile_relay_artifacts_with_custom_id.rs b/compiler/crates/relay-compiler/tests/compile_relay_artifacts_with_custom_id.rs index ce0331fe70f6d..08feee65776ea 100644 --- a/compiler/crates/relay-compiler/tests/compile_relay_artifacts_with_custom_id.rs +++ b/compiler/crates/relay-compiler/tests/compile_relay_artifacts_with_custom_id.rs @@ -13,7 +13,6 @@ use common::FeatureFlags; use common::NamedItem; use common::SourceLocationKey; use fixture_tests::Fixture; -use graphql_ir::build_ir_with_extra_features; use graphql_ir::BuilderOptions; use graphql_ir::FragmentDefinition; use graphql_ir::FragmentDefinitionName; @@ -22,23 +21,24 @@ use graphql_ir::OperationDefinition; use graphql_ir::OperationDefinitionName; use graphql_ir::Program; use graphql_ir::RelayMode; +use graphql_ir::build_ir_with_extra_features; use graphql_syntax::parse_executable; use graphql_test_helpers::diagnostics_to_sorted_string; use graphql_text_printer::print_full_operation; use intern::string_key::Intern; +use relay_codegen::JsModuleFormat; use relay_codegen::build_request_params; use relay_codegen::print_fragment; use relay_codegen::print_operation; use relay_codegen::print_request; -use relay_codegen::JsModuleFormat; -use relay_compiler::validate; use relay_compiler::ProjectConfig; +use relay_compiler::validate; use relay_config::ProjectName; use relay_config::SchemaConfig; use relay_test_schema::get_test_schema_with_custom_id; use relay_test_schema::get_test_schema_with_custom_id_with_extensions; -use relay_transforms::apply_transforms; use relay_transforms::DIRECTIVE_SPLIT_OPERATION; +use relay_transforms::apply_transforms; pub async fn transform_fixture(fixture: &Fixture<'_>) -> Result { let source_location = SourceLocationKey::standalone(fixture.file_name); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration.rs b/compiler/crates/relay-compiler/tests/relay_compiler_integration.rs index ba8b031b6413d..be057e905b323 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration.rs +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration.rs @@ -16,18 +16,18 @@ use futures_util::FutureExt; use graphql_cli::DiagnosticPrinter; use graphql_test_helpers::ProjectFixture; use graphql_test_helpers::TestDir; -use relay_compiler::build_project::generate_extra_artifacts::default_generate_extra_artifacts_fn; -use relay_compiler::compiler::Compiler; -use relay_compiler::config::Config; -use relay_compiler::errors::BuildProjectError; -use relay_compiler::errors::Error; -use relay_compiler::source_for_location; use relay_compiler::FileSourceKind; use relay_compiler::FsSourceReader; use relay_compiler::LocalPersister; use relay_compiler::OperationPersister; use relay_compiler::RemotePersister; use relay_compiler::SourceReader; +use relay_compiler::build_project::generate_extra_artifacts::default_generate_extra_artifacts_fn; +use relay_compiler::compiler::Compiler; +use relay_compiler::config::Config; +use relay_compiler::errors::BuildProjectError; +use relay_compiler::errors::Error; +use relay_compiler::source_for_location; use relay_config::PersistConfig; pub async fn transform_fixture(fixture: &Fixture<'_>) -> Result { diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_mutation_extension.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_mutation_extension.expected index 451ebd4e0671a..3764bd7abbfd4 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_mutation_extension.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_mutation_extension.expected @@ -8,7 +8,6 @@ graphql`mutation barMutation { { "language": "flow", "schema": "./schema.graphql", - "eagerEsModules": true, "schemaExtensions": [ "./extensions.graphql" ] diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_mutation_extension.input b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_mutation_extension.input index 52a836f4c7cf9..15b2a70d772da 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_mutation_extension.input +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_mutation_extension.input @@ -7,7 +7,6 @@ graphql`mutation barMutation { { "language": "flow", "schema": "./schema.graphql", - "eagerEsModules": true, "schemaExtensions": [ "./extensions.graphql" ] diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_mutation_resolver.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_mutation_resolver.expected index 6bd7b71eee9c8..6bdae630c902e 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_mutation_resolver.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_mutation_resolver.expected @@ -13,7 +13,6 @@ graphql`mutation barMutation { { "language": "flow", "schema": "./schema.graphql", - "eagerEsModules": true, "featureFlags": { "enable_relay_resolver_mutations": true } @@ -31,7 +30,7 @@ type Query { ==================================== OUTPUT =================================== //- __generated__/barMutation.graphql.js /** - * SignedSource<<7f1de50339842bab9cecc72c6fbc076f>> + * SignedSource<<9c253a512cbd9b531ef5c5c5feb60982>> * @flow * @lightSyntaxTransform * @nogrep @@ -57,7 +56,7 @@ export type barMutation = {| |}; */ -import {foo_mutation as mutationFooMutationResolver} from './../foo'; +import {foo_mutation as mutationFooMutationResolver} from '../foo'; var node/*: ClientRequest*/ = { "fragment": { diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_mutation_resolver.input b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_mutation_resolver.input index a438f2400a648..8760136cea00c 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_mutation_resolver.input +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_mutation_resolver.input @@ -12,7 +12,6 @@ graphql`mutation barMutation { { "language": "flow", "schema": "./schema.graphql", - "eagerEsModules": true, "featureFlags": { "enable_relay_resolver_mutations": true } diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_mutation_resolver_different_mutation_ok.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_mutation_resolver_different_mutation_ok.expected index aca31017723c0..fcdb007e845fb 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_mutation_resolver_different_mutation_ok.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_mutation_resolver_different_mutation_ok.expected @@ -17,9 +17,11 @@ graphql`mutation barMutation { { "language": "flow", "schema": "./schema.graphql", - "eagerEsModules": true, "featureFlags": { - "enable_relay_resolver_mutations": true + "enable_relay_resolver_mutations": true, + "allow_output_type_resolvers": { + "kind": "enabled" + } }, "schemaExtensions": [ "./extensions.graphql" @@ -105,7 +107,7 @@ export default node; //- __generated__/barMutation.graphql.js /** - * SignedSource<<6f79ca8d7718498c480388c5b22a5130>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -131,7 +133,7 @@ export type barMutation = {| |}; */ -import {baz_mutation as notCalledMutationBazMutationResolver} from './../foo'; +import {baz_mutation as notCalledMutationBazMutationResolver} from '../foo'; var node/*: ClientRequest*/ = { "fragment": { diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_mutation_resolver_different_mutation_ok.input b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_mutation_resolver_different_mutation_ok.input index 19045c738c146..0aa031e77edad 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_mutation_resolver_different_mutation_ok.input +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_mutation_resolver_different_mutation_ok.input @@ -16,9 +16,11 @@ graphql`mutation barMutation { { "language": "flow", "schema": "./schema.graphql", - "eagerEsModules": true, "featureFlags": { - "enable_relay_resolver_mutations": true + "enable_relay_resolver_mutations": true, + "allow_output_type_resolvers": { + "kind": "enabled" + } }, "schemaExtensions": [ "./extensions.graphql" diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_mutation_resolver_invalid_disabled.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_mutation_resolver_invalid_disabled.expected index 6591cac6c1870..5d339db2a8006 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_mutation_resolver_invalid_disabled.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_mutation_resolver_invalid_disabled.expected @@ -13,7 +13,11 @@ graphql`mutation barMutation { { "language": "flow", "schema": "./schema.graphql", - "eagerEsModules": true + "featureFlags": { + "allow_output_type_resolvers": { + "kind": "enabled" + } + } } //- schema.graphql diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_mutation_resolver_invalid_disabled.input b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_mutation_resolver_invalid_disabled.input index 821ce7cb09945..4e89220fe58ea 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_mutation_resolver_invalid_disabled.input +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_mutation_resolver_invalid_disabled.input @@ -12,7 +12,11 @@ graphql`mutation barMutation { { "language": "flow", "schema": "./schema.graphql", - "eagerEsModules": true + "featureFlags": { + "allow_output_type_resolvers": { + "kind": "enabled" + } + } } //- schema.graphql diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_mutation_resolver_invalid_nonscalar.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_mutation_resolver_invalid_nonscalar.expected index 8dc5efb8b9690..40bff0e1a4f46 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_mutation_resolver_invalid_nonscalar.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_mutation_resolver_invalid_nonscalar.expected @@ -29,9 +29,11 @@ graphql`mutation barMutation { { "language": "flow", "schema": "./schema.graphql", - "eagerEsModules": true, "featureFlags": { - "enable_relay_resolver_mutations": true + "enable_relay_resolver_mutations": true, + "allow_output_type_resolvers": { + "kind": "enabled" + } }, "schemaExtensions": [ "./extensions.graphql" diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_mutation_resolver_invalid_nonscalar.input b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_mutation_resolver_invalid_nonscalar.input index 5048694f6c0c1..192dac4bdc14f 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_mutation_resolver_invalid_nonscalar.input +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_mutation_resolver_invalid_nonscalar.input @@ -28,9 +28,11 @@ graphql`mutation barMutation { { "language": "flow", "schema": "./schema.graphql", - "eagerEsModules": true, "featureFlags": { - "enable_relay_resolver_mutations": true + "enable_relay_resolver_mutations": true, + "allow_output_type_resolvers": { + "kind": "enabled" + } }, "schemaExtensions": [ "./extensions.graphql" diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_schema_extension_in_throw_on_field_error.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_schema_extension_in_throw_on_field_error.expected new file mode 100644 index 0000000000000..82b314391ac04 --- /dev/null +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_schema_extension_in_throw_on_field_error.expected @@ -0,0 +1,34 @@ +==================================== INPUT ==================================== +//- foo.js +graphql`query fooQuery @throwOnFieldError { + client_field +}` + +//- relay.config.json +{ + "language": "flow", + "jsModuleFormat": "haste", + "schema": "schema.graphql", + "schemaExtensions": [ + "schema-extensions" + ] +} + +//- schema.graphql +type Query { + greeting: String +} + +//- schema-extensions/extension.graphql + +extend type Query { + client_field: String +} +==================================== OUTPUT =================================== +✖︎ Expected client-defined field within `@throwOnFieldError` to be annotated with `@catch`. Accessing an unset field is treated as a field error, but Relay cannot guarantee that client field will be set before they are read. Add `@catch` to explicitly handle the case where the field is unset. + + foo.js:2:3 + 1 │ query fooQuery @throwOnFieldError { + 2 │ client_field + │ ^^^^^^^^^^^^ + 3 │ } diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_schema_extension_in_throw_on_field_error.input b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_schema_extension_in_throw_on_field_error.input new file mode 100644 index 0000000000000..a279496d2b914 --- /dev/null +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_schema_extension_in_throw_on_field_error.input @@ -0,0 +1,25 @@ +//- foo.js +graphql`query fooQuery @throwOnFieldError { + client_field +}` + +//- relay.config.json +{ + "language": "flow", + "jsModuleFormat": "haste", + "schema": "schema.graphql", + "schemaExtensions": [ + "schema-extensions" + ] +} + +//- schema.graphql +type Query { + greeting: String +} + +//- schema-extensions/extension.graphql + +extend type Query { + client_field: String +} diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_schema_extension_interface_uses_resolver_type.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_schema_extension_interface_uses_resolver_type.expected index 9f7c9ad4829ec..ddc05697eedb3 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_schema_extension_interface_uses_resolver_type.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/client_schema_extension_interface_uses_resolver_type.expected @@ -54,7 +54,7 @@ interface Worker { ==================================== OUTPUT =================================== //- __generated__/Admin____relay_model_instance.graphql.js /** - * SignedSource<<2a569a157aaf5519c1f05d904b6d6d26>> + * SignedSource<<5a24c49663dd9955391fbdeb829a767e>> * @flow * @lightSyntaxTransform * @nogrep @@ -86,6 +86,10 @@ export type Admin____relay_model_instance$key = { }; */ +import {Admin as adminRelayModelInstanceResolver} from 'Admin'; +import Admin__id_graphql from 'Admin__id.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -102,7 +106,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "__relay_model_instance", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('Admin__id.graphql'), require('Admin').Admin, 'id', true), + "resolverModule": resolverDataInjector(Admin__id_graphql, adminRelayModelInstanceResolver, 'id', true), "path": "__relay_model_instance" } ], @@ -110,14 +114,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< Admin____relay_model_instance$fragmentType, Admin____relay_model_instance$data, >*/); //- __generated__/Admin__id.graphql.js /** - * SignedSource<<29acfbf1d6f559b8b77e9cd1f35218c0>> + * SignedSource<<78e97d08242ee5ff28fb2c5bf850cd81>> * @flow * @lightSyntaxTransform * @nogrep @@ -165,14 +169,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< Admin__id$fragmentType, Admin__id$data, >*/); //- __generated__/PersonComponentQuery.graphql.js /** - * SignedSource<<678979a6fbd285c60deb8d498debb4ca>> + * SignedSource<<2d07c1150de22f2bad1b3141700c5cc0>> * @flow * @lightSyntaxTransform * @nogrep @@ -214,6 +218,13 @@ export type PersonComponentQuery = {| |}; */ +import {Admin as adminRelayModelInstanceResolver} from 'Admin'; +import {admin as adminAdminResolver} from 'Admin'; +import {boss as queryBossResolver} from 'Admin'; +import Admin____relay_model_instance_graphql from 'Admin____relay_model_instance.graphql'; +import Admin__id_graphql from 'Admin__id.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ClientRequest*/ = (function(){ var v0 = { "args": null, @@ -258,8 +269,8 @@ return { "args": null, "fragment": (v0/*: any*/), "kind": "RelayResolver", - "name": "boss", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('Admin__id.graphql'), require('Admin').Admin, 'id', true), + "name": "__relay_model_instance", + "resolverModule": resolverDataInjector(Admin__id_graphql, adminRelayModelInstanceResolver, 'id', true), "path": "boss.__relay_model_instance" } }, @@ -269,7 +280,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "boss", - "resolverModule": require('Admin').boss, + "resolverModule": queryBossResolver, "path": "boss" }, "linkedField": { @@ -289,8 +300,8 @@ return { "args": null, "fragment": (v0/*: any*/), "kind": "RelayResolver", - "name": "admin", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('Admin__id.graphql'), require('Admin').Admin, 'id', true), + "name": "__relay_model_instance", + "resolverModule": resolverDataInjector(Admin__id_graphql, adminRelayModelInstanceResolver, 'id', true), "path": "boss.admin.__relay_model_instance" } }, @@ -304,7 +315,7 @@ return { }, "kind": "RelayResolver", "name": "admin", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('Admin____relay_model_instance.graphql'), require('Admin').admin, '__relay_model_instance', true), + "resolverModule": resolverDataInjector(Admin____relay_model_instance_graphql, adminAdminResolver, '__relay_model_instance', true), "path": "boss.admin" }, "linkedField": (v3/*: any*/) @@ -392,7 +403,7 @@ return { (node/*: any*/).hash = "25c3cfb12f96f3cf4479f3effad2e911"; -module.exports = ((node/*: any*/)/*: ClientQuery< +export default ((node/*: any*/)/*: ClientQuery< PersonComponentQuery$variables, PersonComponentQuery$data, >*/); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/custom_scalar_variable_default_arg.invalid.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/custom_scalar_variable_default_arg.invalid.expected index 8c64ff8cda4e1..2d658171ce787 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/custom_scalar_variable_default_arg.invalid.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/custom_scalar_variable_default_arg.invalid.expected @@ -14,7 +14,6 @@ graphql`query fooCustomScalarLiteralArgQuery($arg: CustomScalarType! = "foobar", { "language": "flow", "schema": "./schema.graphql", - "eagerEsModules": true, "featureFlags": { "enable_relay_resolver_mutations": true, "enable_strict_custom_scalars": true diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/custom_scalar_variable_default_arg.invalid.input b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/custom_scalar_variable_default_arg.invalid.input index de9c236a060b8..b5e6216fc7a28 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/custom_scalar_variable_default_arg.invalid.input +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/custom_scalar_variable_default_arg.invalid.input @@ -13,7 +13,6 @@ graphql`query fooCustomScalarLiteralArgQuery($arg: CustomScalarType! = "foobar", { "language": "flow", "schema": "./schema.graphql", - "eagerEsModules": true, "featureFlags": { "enable_relay_resolver_mutations": true, "enable_strict_custom_scalars": true diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/custom_scalar_variable_default_arg_non_strict.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/custom_scalar_variable_default_arg_non_strict.expected index d6a4282f0c499..d62665fafae34 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/custom_scalar_variable_default_arg_non_strict.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/custom_scalar_variable_default_arg_non_strict.expected @@ -14,7 +14,6 @@ graphql`query fooCustomScalarLiteralArgQuery($arg: CustomScalarType = "foobar", { "language": "flow", "schema": "./schema.graphql", - "eagerEsModules": true, "featureFlags": { "enable_relay_resolver_mutations": true }, diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/custom_scalar_variable_default_arg_non_strict.input b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/custom_scalar_variable_default_arg_non_strict.input index 99f3bb7ce6977..ebc68dbb280f0 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/custom_scalar_variable_default_arg_non_strict.input +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/custom_scalar_variable_default_arg_non_strict.input @@ -13,7 +13,6 @@ graphql`query fooCustomScalarLiteralArgQuery($arg: CustomScalarType = "foobar", { "language": "flow", "schema": "./schema.graphql", - "eagerEsModules": true, "featureFlags": { "enable_relay_resolver_mutations": true }, diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/error_handling_fragment.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/error_handling_fragment.expected index 4e044fa88b48c..cfd5644610b28 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/error_handling_fragment.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/error_handling_fragment.expected @@ -17,7 +17,7 @@ type User { name: String } ==================================== OUTPUT =================================== //- __generated__/foo.graphql.js /** - * SignedSource<> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -64,7 +64,7 @@ var node/*: ReaderFragment*/ = { (node/*: any*/).hash = "c846248549175d6d05faa3bd13697146"; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< foo$fragmentType, foo$data, >*/); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/error_handling_query.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/error_handling_query.expected index 669cf838b6380..8f76bcfe6859a 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/error_handling_query.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/error_handling_query.expected @@ -19,7 +19,7 @@ type User { name: String } ==================================== OUTPUT =================================== //- __generated__/fooQuery.graphql.js /** - * SignedSource<<3fedea69ae18a5e38707e31bf973e7af>> + * SignedSource<<9b5cf0134e02913bc0d65df99f7ff0c3>> * @flow * @lightSyntaxTransform * @nogrep @@ -96,7 +96,7 @@ return { (node/*: any*/).hash = "5837704043bd9bdb31bb77ca3ed3856e"; -module.exports = ((node/*: any*/)/*: Query< +export default ((node/*: any*/)/*: Query< fooQuery$variables, fooQuery$data, >*/); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/exec_resolvers_directive_with_root_fragment.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/exec_resolvers_directive_with_root_fragment.expected new file mode 100644 index 0000000000000..2ea58bb4d56e2 --- /dev/null +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/exec_resolvers_directive_with_root_fragment.expected @@ -0,0 +1,180 @@ +==================================== INPUT ==================================== +//- User_foo.js +/** + * @RelayResolver User.foo: RelayResolverValue + * @rootFragment UserFooFragment + */ +graphql`fragment UserFooFragment on User { + bar +}` + +//- User_bar.js +/** + * @RelayResolver User.bar: RelayResolverValue + */ + +//- relay.config.json +{ + "language": "flow", + "jsModuleFormat": "haste", + "schema": "schema.graphql", + "featureFlags": { + "enable_exec_time_resolvers_directive": true + }, + "resolversSchemaModule": { + "path": "__generated__/ResolversSchemaModule.js" + } +} + +//- schema.graphql +type Query { me: User } +type User { name: String } +==================================== OUTPUT =================================== +//- __generated__/ResolversSchemaModule.js +/** + * SignedSource<> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { SchemaResolvers } from 'ReactiveQueryExecutor'; +import type { ResolverFunction, NormalizationSplitOperation } from 'relay-runtime'; + +*/ + +import UserFooFragment$normalization from 'UserFooFragment$normalization.graphql'; +import {bar as userBarResolver} from 'User_bar'; +import {foo as userFooResolver} from 'User_foo'; + +var schema_resolvers/*: SchemaResolvers*/ = { + "User": { + "bar": { + "resolverFunction": userBarResolver, + "rootFragment": null + }, + "foo": { + "resolverFunction": userFooResolver, + "rootFragment": UserFooFragment$normalization + } + } +}; + +export default schema_resolvers; + +//- __generated__/UserFooFragment$normalization.graphql.js +/** + * SignedSource<> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { NormalizationSplitOperation } from 'relay-runtime'; + +*/ + +import {bar as userBarResolver} from 'User_bar'; + +var node/*: NormalizationSplitOperation*/ = { + "kind": "SplitOperation", + "metadata": {}, + "name": "UserFooFragment$normalization", + "selections": [ + { + "kind": "ClientExtension", + "selections": [ + { + "name": "bar", + "args": null, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": true, + "resolverInfo": { + "resolverFunction": userBarResolver, + "rootFragment": null + } + } + ] + } + ] +}; + +(node/*: any*/).hash = "285ee53d00b8def775c9e1ed756743bf"; + +export default node; + +//- __generated__/UserFooFragment.graphql.js +/** + * SignedSource<<843aee583dbf963998562b8559b43846>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { Fragment, ReaderFragment } from 'relay-runtime'; +import type { FragmentType } from "relay-runtime"; +import {bar as userBarResolverType} from "User_bar"; +// Type assertion validating that `userBarResolverType` resolver is correctly implemented. +// A type error here indicates that the type signature of the resolver module is incorrect. +(userBarResolverType: () => ?mixed); +declare export opaque type UserFooFragment$fragmentType: FragmentType; +export type UserFooFragment$data = {| + +bar: ?ReturnType, + +$fragmentType: UserFooFragment$fragmentType, +|}; +export type UserFooFragment$key = { + +$data?: UserFooFragment$data, + +$fragmentSpreads: UserFooFragment$fragmentType, + ... +}; +*/ + +import {bar as userBarResolver} from 'User_bar'; + +var node/*: ReaderFragment*/ = { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "UserFooFragment", + "selections": [ + { + "kind": "ClientExtension", + "selections": [ + { + "alias": null, + "args": null, + "fragment": null, + "kind": "RelayResolver", + "name": "bar", + "resolverModule": userBarResolver, + "path": "bar" + } + ] + } + ], + "type": "User", + "abstractKey": null +}; + +(node/*: any*/).hash = "285ee53d00b8def775c9e1ed756743bf"; + +export default ((node/*: any*/)/*: Fragment< + UserFooFragment$fragmentType, + UserFooFragment$data, +>*/); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/exec_resolvers_directive_with_root_fragment.input b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/exec_resolvers_directive_with_root_fragment.input new file mode 100644 index 0000000000000..46e61f3377de0 --- /dev/null +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/exec_resolvers_directive_with_root_fragment.input @@ -0,0 +1,30 @@ +//- User_foo.js +/** + * @RelayResolver User.foo: RelayResolverValue + * @rootFragment UserFooFragment + */ +graphql`fragment UserFooFragment on User { + bar +}` + +//- User_bar.js +/** + * @RelayResolver User.bar: RelayResolverValue + */ + +//- relay.config.json +{ + "language": "flow", + "jsModuleFormat": "haste", + "schema": "schema.graphql", + "featureFlags": { + "enable_exec_time_resolvers_directive": true + }, + "resolversSchemaModule": { + "path": "__generated__/ResolversSchemaModule.js" + } +} + +//- schema.graphql +type Query { me: User } +type User { name: String } diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/extra_in_single_file_config.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/extra_in_single_file_config.expected new file mode 100644 index 0000000000000..d2157fb0067a2 --- /dev/null +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/extra_in_single_file_config.expected @@ -0,0 +1,64 @@ +==================================== INPUT ==================================== +//- foo.js +graphql` + fragment foo on User { + name + }`; + +//- relay.config.json +{ + "language": "typescript", + "schema": "./schema.graphql", + "extra": { + "is_metadata": true, + "tags": ["free-form", "unstructured"] + } +} + +//- schema.graphql +type Query { me: User } +type User { name: String } +==================================== OUTPUT =================================== +//- __generated__/foo.graphql.ts +/** + * SignedSource<> + * @lightSyntaxTransform + * @nogrep + */ + +/* tslint:disable */ +/* eslint-disable */ +// @ts-nocheck + +import { ReaderFragment } from 'relay-runtime'; +import { FragmentRefs } from "relay-runtime"; +export type foo$data = { + readonly name: string | null | undefined; + readonly " $fragmentType": "foo"; +}; +export type foo$key = { + readonly " $data"?: foo$data; + readonly " $fragmentSpreads": FragmentRefs<"foo">; +}; + +const node: ReaderFragment = { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "foo", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + } + ], + "type": "User", + "abstractKey": null +}; + +(node as any).hash = "01d5e51e4b7ff55557834f125c21745d"; + +export default node; diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/extra_in_single_file_config.input b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/extra_in_single_file_config.input new file mode 100644 index 0000000000000..e42d677faea4f --- /dev/null +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/extra_in_single_file_config.input @@ -0,0 +1,19 @@ +//- foo.js +graphql` + fragment foo on User { + name + }`; + +//- relay.config.json +{ + "language": "typescript", + "schema": "./schema.graphql", + "extra": { + "is_metadata": true, + "tags": ["free-form", "unstructured"] + } +} + +//- schema.graphql +type Query { me: User } +type User { name: String } diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/fragment_alias_nested_in_inline_fragment.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/fragment_alias_nested_in_inline_fragment.expected index ec06195dcc8ac..12ad19d8f5c0f 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/fragment_alias_nested_in_inline_fragment.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/fragment_alias_nested_in_inline_fragment.expected @@ -32,7 +32,7 @@ type User { name: String } ==================================== OUTPUT =================================== //- __generated__/foo.graphql.js /** - * SignedSource<<3ffa1f4ea14c9dc9914907c99445fb9f>> + * SignedSource<<0492052b84cb7ca7f7e46fbb59fa504d>> * @flow * @lightSyntaxTransform * @nogrep @@ -89,14 +89,14 @@ var node/*: ReaderFragment*/ = { (node/*: any*/).hash = "cb852f428534e4d18b622320fecaa807"; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< foo$fragmentType, foo$data, >*/); //- __generated__/fooInner.graphql.js /** - * SignedSource<<75e048a7d2500a23d76cc69843f4aae3>> + * SignedSource<<5eab1f300c9a4d65cc8dc272ba13f88a>> * @flow * @lightSyntaxTransform * @nogrep @@ -141,14 +141,14 @@ var node/*: ReaderFragment*/ = { (node/*: any*/).hash = "56025f0f9c913622266e88aa8587934e"; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< fooInner$fragmentType, fooInner$data, >*/); //- __generated__/fooQuery.graphql.js /** - * SignedSource<> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -238,7 +238,7 @@ var node/*: ConcreteRequest*/ = { (node/*: any*/).hash = "c7ea9fa69ee8b2305ff517551fc1cc17"; -module.exports = ((node/*: any*/)/*: Query< +export default ((node/*: any*/)/*: Query< fooQuery$variables, fooQuery$data, >*/); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/live_resolver_implements_interface_field.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/live_resolver_implements_interface_field.expected index cbefa75ec643f..609bd121e95f6 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/live_resolver_implements_interface_field.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/live_resolver_implements_interface_field.expected @@ -52,7 +52,7 @@ interface IPerson { ==================================== OUTPUT =================================== //- __generated__/Admin____relay_model_instance.graphql.js /** - * SignedSource<<43fd74ebd2b94a456ec3c8657edf2868>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -84,6 +84,10 @@ export type Admin____relay_model_instance$key = { }; */ +import {Admin as adminRelayModelInstanceResolver} from 'AdminTypeResolvers'; +import Admin__id_graphql from 'Admin__id.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -100,7 +104,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayLiveResolver", "name": "__relay_model_instance", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('Admin__id.graphql'), require('AdminTypeResolvers').Admin, 'id', true), + "resolverModule": resolverDataInjector(Admin__id_graphql, adminRelayModelInstanceResolver, 'id', true), "path": "__relay_model_instance" } ], @@ -108,14 +112,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< Admin____relay_model_instance$fragmentType, Admin____relay_model_instance$data, >*/); //- __generated__/Admin__id.graphql.js /** - * SignedSource<<29acfbf1d6f559b8b77e9cd1f35218c0>> + * SignedSource<<78e97d08242ee5ff28fb2c5bf850cd81>> * @flow * @lightSyntaxTransform * @nogrep @@ -163,14 +167,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< Admin__id$fragmentType, Admin__id$data, >*/); //- __generated__/PersonComponentFragment.graphql.js /** - * SignedSource<> + * SignedSource<<35b6c94634075e96f33e1c33da81c60d>> * @flow * @lightSyntaxTransform * @nogrep @@ -195,6 +199,12 @@ export type PersonComponentFragment$key = { }; */ +import {name as adminNameResolver} from 'AdminTypeResolvers'; +import Admin____relay_model_instance_graphql from 'Admin____relay_model_instance.graphql'; +import {name as userNameResolver} from 'UserTypeResolvers'; +import User____relay_model_instance_graphql from 'User____relay_model_instance.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -217,7 +227,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "name", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('Admin____relay_model_instance.graphql'), require('AdminTypeResolvers').name, '__relay_model_instance', true), + "resolverModule": resolverDataInjector(Admin____relay_model_instance_graphql, adminNameResolver, '__relay_model_instance', true), "path": "name" } ], @@ -237,7 +247,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayLiveResolver", "name": "name", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('User____relay_model_instance.graphql'), require('UserTypeResolvers').name, '__relay_model_instance', true), + "resolverModule": resolverDataInjector(User____relay_model_instance_graphql, userNameResolver, '__relay_model_instance', true), "path": "name" } ], @@ -253,14 +263,14 @@ var node/*: ReaderFragment*/ = { (node/*: any*/).hash = "a57dd30bd59412781e9566e1553e2d70"; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< PersonComponentFragment$fragmentType, PersonComponentFragment$data, >*/); //- __generated__/User____relay_model_instance.graphql.js /** - * SignedSource<<5b1064cbbcdf6ad4c972e722067e79b2>> + * SignedSource<<1d7f7276af9b68a8321f13626bfca7aa>> * @flow * @lightSyntaxTransform * @nogrep @@ -292,6 +302,10 @@ export type User____relay_model_instance$key = { }; */ +import {User as userRelayModelInstanceResolver} from 'UserTypeResolvers'; +import User__id_graphql from 'User__id.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -308,7 +322,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayLiveResolver", "name": "__relay_model_instance", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('User__id.graphql'), require('UserTypeResolvers').User, 'id', true), + "resolverModule": resolverDataInjector(User__id_graphql, userRelayModelInstanceResolver, 'id', true), "path": "__relay_model_instance" } ], @@ -316,14 +330,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< User____relay_model_instance$fragmentType, User____relay_model_instance$data, >*/); //- __generated__/User__id.graphql.js /** - * SignedSource<<0a0f39eb34bfc882d28378a0b05b3c17>> + * SignedSource<<56388778b313654f6700e4bfa73599a2>> * @flow * @lightSyntaxTransform * @nogrep @@ -371,7 +385,7 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< User__id$fragmentType, User__id$data, >*/); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/multiple_resolvers_on_interface_of_all_strong_model_type.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/multiple_resolvers_on_interface_of_all_strong_model_type.expected index 47609bd8d3eef..1dcde374e51dc 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/multiple_resolvers_on_interface_of_all_strong_model_type.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/multiple_resolvers_on_interface_of_all_strong_model_type.expected @@ -61,7 +61,7 @@ interface IActor { ==================================== OUTPUT =================================== //- __generated__/Admin____relay_model_instance.graphql.js /** - * SignedSource<<7fbe3989595c55397f4bcc5c81ec30b2>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -93,6 +93,10 @@ export type Admin____relay_model_instance$key = { }; */ +import {Admin as adminRelayModelInstanceResolver} from 'AdminTypeResolvers'; +import Admin__id_graphql from 'Admin__id.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -109,7 +113,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "__relay_model_instance", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('Admin__id.graphql'), require('AdminTypeResolvers').Admin, 'id', true), + "resolverModule": resolverDataInjector(Admin__id_graphql, adminRelayModelInstanceResolver, 'id', true), "path": "__relay_model_instance" } ], @@ -117,14 +121,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< Admin____relay_model_instance$fragmentType, Admin____relay_model_instance$data, >*/); //- __generated__/Admin__id.graphql.js /** - * SignedSource<<29acfbf1d6f559b8b77e9cd1f35218c0>> + * SignedSource<<78e97d08242ee5ff28fb2c5bf850cd81>> * @flow * @lightSyntaxTransform * @nogrep @@ -172,14 +176,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< Admin__id$fragmentType, Admin__id$data, >*/); //- __generated__/PersonComponentQuery.graphql.js /** - * SignedSource<> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -220,6 +224,14 @@ export type PersonComponentQuery = {| |}; */ +import Admin____relay_model_instance_graphql from 'Admin____relay_model_instance.graphql'; +import {description as adminDescriptionResolver} from 'IActorResolvers'; +import {description as userDescriptionResolver} from 'IActorResolvers'; +import {name as adminNameResolver} from 'IPersonResolvers'; +import {name as userNameResolver} from 'IPersonResolvers'; +import User____relay_model_instance_graphql from 'User____relay_model_instance.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ClientRequest*/ = (function(){ var v0 = { "args": null, @@ -272,7 +284,7 @@ return { "fragment": (v0/*: any*/), "kind": "RelayResolver", "name": "name", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('Admin____relay_model_instance.graphql'), require('IPersonResolvers').name, '__relay_model_instance', true), + "resolverModule": resolverDataInjector(Admin____relay_model_instance_graphql, adminNameResolver, '__relay_model_instance', true), "path": "person.name" } ], @@ -288,7 +300,7 @@ return { "fragment": (v1/*: any*/), "kind": "RelayResolver", "name": "name", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('User____relay_model_instance.graphql'), require('IPersonResolvers').name, '__relay_model_instance', true), + "resolverModule": resolverDataInjector(User____relay_model_instance_graphql, userNameResolver, '__relay_model_instance', true), "path": "person.name" } ], @@ -320,7 +332,7 @@ return { "fragment": (v0/*: any*/), "kind": "RelayResolver", "name": "description", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('Admin____relay_model_instance.graphql'), require('IActorResolvers').description, '__relay_model_instance', true), + "resolverModule": resolverDataInjector(Admin____relay_model_instance_graphql, adminDescriptionResolver, '__relay_model_instance', true), "path": "actor.description" } ], @@ -336,7 +348,7 @@ return { "fragment": (v1/*: any*/), "kind": "RelayResolver", "name": "description", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('User____relay_model_instance.graphql'), require('IActorResolvers').description, '__relay_model_instance', true), + "resolverModule": resolverDataInjector(User____relay_model_instance_graphql, userDescriptionResolver, '__relay_model_instance', true), "path": "actor.description" } ], @@ -380,7 +392,7 @@ return { "storageKey": null, "isOutputType": true, "resolverInfo": { - "resolverFunction": require('IPersonResolvers').name, + "resolverFunction": adminNameResolver, "rootFragment": null } } @@ -398,7 +410,7 @@ return { "storageKey": null, "isOutputType": true, "resolverInfo": { - "resolverFunction": require('IPersonResolvers').name, + "resolverFunction": userNameResolver, "rootFragment": null } } @@ -434,7 +446,7 @@ return { "storageKey": null, "isOutputType": true, "resolverInfo": { - "resolverFunction": require('IActorResolvers').description, + "resolverFunction": adminDescriptionResolver, "rootFragment": null } } @@ -452,7 +464,7 @@ return { "storageKey": null, "isOutputType": true, "resolverInfo": { - "resolverFunction": require('IActorResolvers').description, + "resolverFunction": userDescriptionResolver, "rootFragment": null } } @@ -481,14 +493,14 @@ return { (node/*: any*/).hash = "0ed0cac8f6cfaba728cb6608b7840d57"; -module.exports = ((node/*: any*/)/*: ClientQuery< +export default ((node/*: any*/)/*: ClientQuery< PersonComponentQuery$variables, PersonComponentQuery$data, >*/); //- __generated__/User____relay_model_instance.graphql.js /** - * SignedSource<<7ffabc2a97c3589cbfd20a23b3b608ca>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -520,6 +532,10 @@ export type User____relay_model_instance$key = { }; */ +import {User as userRelayModelInstanceResolver} from 'UserTypeResolvers'; +import User__id_graphql from 'User__id.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -536,7 +552,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "__relay_model_instance", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('User__id.graphql'), require('UserTypeResolvers').User, 'id', true), + "resolverModule": resolverDataInjector(User__id_graphql, userRelayModelInstanceResolver, 'id', true), "path": "__relay_model_instance" } ], @@ -544,14 +560,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< User____relay_model_instance$fragmentType, User____relay_model_instance$data, >*/); //- __generated__/User__id.graphql.js /** - * SignedSource<<0a0f39eb34bfc882d28378a0b05b3c17>> + * SignedSource<<56388778b313654f6700e4bfa73599a2>> * @flow * @lightSyntaxTransform * @nogrep @@ -599,7 +615,7 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< User__id$fragmentType, User__id$data, >*/); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/multiple_resolvers_returns_interfaces_of_all_strong_model_type.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/multiple_resolvers_returns_interfaces_of_all_strong_model_type.expected index e34a93f4c904b..d3badc65d03b2 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/multiple_resolvers_returns_interfaces_of_all_strong_model_type.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/multiple_resolvers_returns_interfaces_of_all_strong_model_type.expected @@ -70,7 +70,7 @@ interface IActor { ==================================== OUTPUT =================================== //- __generated__/Admin____relay_model_instance.graphql.js /** - * SignedSource<<7fbe3989595c55397f4bcc5c81ec30b2>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -102,6 +102,10 @@ export type Admin____relay_model_instance$key = { }; */ +import {Admin as adminRelayModelInstanceResolver} from 'AdminTypeResolvers'; +import Admin__id_graphql from 'Admin__id.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -118,7 +122,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "__relay_model_instance", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('Admin__id.graphql'), require('AdminTypeResolvers').Admin, 'id', true), + "resolverModule": resolverDataInjector(Admin__id_graphql, adminRelayModelInstanceResolver, 'id', true), "path": "__relay_model_instance" } ], @@ -126,14 +130,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< Admin____relay_model_instance$fragmentType, Admin____relay_model_instance$data, >*/); //- __generated__/Admin__id.graphql.js /** - * SignedSource<<29acfbf1d6f559b8b77e9cd1f35218c0>> + * SignedSource<<78e97d08242ee5ff28fb2c5bf850cd81>> * @flow * @lightSyntaxTransform * @nogrep @@ -181,14 +185,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< Admin__id$fragmentType, Admin__id$data, >*/); //- __generated__/PersonComponentQuery.graphql.js /** - * SignedSource<> + * SignedSource<<412e97e1ab4e0e6ee91f6302ee11a247>> * @flow * @lightSyntaxTransform * @nogrep @@ -215,6 +219,14 @@ export type PersonComponentQuery = {| |}; */ +import {description as adminDescriptionResolver} from 'AdminTypeResolvers'; +import {name as adminNameResolver} from 'AdminTypeResolvers'; +import Admin____relay_model_instance_graphql from 'Admin____relay_model_instance.graphql'; +import {description as userDescriptionResolver} from 'UserTypeResolvers'; +import {name as userNameResolver} from 'UserTypeResolvers'; +import User____relay_model_instance_graphql from 'User____relay_model_instance.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ClientRequest*/ = (function(){ var v0 = { "args": null, @@ -267,7 +279,7 @@ return { "fragment": (v0/*: any*/), "kind": "RelayResolver", "name": "name", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('Admin____relay_model_instance.graphql'), require('AdminTypeResolvers').name, '__relay_model_instance', true), + "resolverModule": resolverDataInjector(Admin____relay_model_instance_graphql, adminNameResolver, '__relay_model_instance', true), "path": "person.name" } ], @@ -283,7 +295,7 @@ return { "fragment": (v1/*: any*/), "kind": "RelayResolver", "name": "name", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('User____relay_model_instance.graphql'), require('UserTypeResolvers').name, '__relay_model_instance', true), + "resolverModule": resolverDataInjector(User____relay_model_instance_graphql, userNameResolver, '__relay_model_instance', true), "path": "person.name" } ], @@ -315,7 +327,7 @@ return { "fragment": (v0/*: any*/), "kind": "RelayResolver", "name": "description", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('Admin____relay_model_instance.graphql'), require('AdminTypeResolvers').description, '__relay_model_instance', true), + "resolverModule": resolverDataInjector(Admin____relay_model_instance_graphql, adminDescriptionResolver, '__relay_model_instance', true), "path": "actor.description" } ], @@ -331,7 +343,7 @@ return { "fragment": (v1/*: any*/), "kind": "RelayResolver", "name": "description", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('User____relay_model_instance.graphql'), require('UserTypeResolvers').description, '__relay_model_instance', true), + "resolverModule": resolverDataInjector(User____relay_model_instance_graphql, userDescriptionResolver, '__relay_model_instance', true), "path": "actor.description" } ], @@ -375,7 +387,7 @@ return { "storageKey": null, "isOutputType": true, "resolverInfo": { - "resolverFunction": require('AdminTypeResolvers').name, + "resolverFunction": adminNameResolver, "rootFragment": null } } @@ -393,7 +405,7 @@ return { "storageKey": null, "isOutputType": true, "resolverInfo": { - "resolverFunction": require('UserTypeResolvers').name, + "resolverFunction": userNameResolver, "rootFragment": null } } @@ -429,7 +441,7 @@ return { "storageKey": null, "isOutputType": true, "resolverInfo": { - "resolverFunction": require('AdminTypeResolvers').description, + "resolverFunction": adminDescriptionResolver, "rootFragment": null } } @@ -447,7 +459,7 @@ return { "storageKey": null, "isOutputType": true, "resolverInfo": { - "resolverFunction": require('UserTypeResolvers').description, + "resolverFunction": userDescriptionResolver, "rootFragment": null } } @@ -476,14 +488,14 @@ return { (node/*: any*/).hash = "0ed0cac8f6cfaba728cb6608b7840d57"; -module.exports = ((node/*: any*/)/*: ClientQuery< +export default ((node/*: any*/)/*: ClientQuery< PersonComponentQuery$variables, PersonComponentQuery$data, >*/); //- __generated__/User____relay_model_instance.graphql.js /** - * SignedSource<<7ffabc2a97c3589cbfd20a23b3b608ca>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -515,6 +527,10 @@ export type User____relay_model_instance$key = { }; */ +import {User as userRelayModelInstanceResolver} from 'UserTypeResolvers'; +import User__id_graphql from 'User__id.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -531,7 +547,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "__relay_model_instance", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('User__id.graphql'), require('UserTypeResolvers').User, 'id', true), + "resolverModule": resolverDataInjector(User__id_graphql, userRelayModelInstanceResolver, 'id', true), "path": "__relay_model_instance" } ], @@ -539,14 +555,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< User____relay_model_instance$fragmentType, User____relay_model_instance$data, >*/); //- __generated__/User__id.graphql.js /** - * SignedSource<<0a0f39eb34bfc882d28378a0b05b3c17>> + * SignedSource<<56388778b313654f6700e4bfa73599a2>> * @flow * @lightSyntaxTransform * @nogrep @@ -594,7 +610,7 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< User__id$fragmentType, User__id$data, >*/); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/prefetchable_refetchable_pagination.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/prefetchable_refetchable_pagination.expected index 08aa5a971b5a5..1bb5963397b92 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/prefetchable_refetchable_pagination.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/prefetchable_refetchable_pagination.expected @@ -61,7 +61,7 @@ type Page { ==================================== OUTPUT =================================== //- __generated__/RefetchableFragmentQuery.graphql.js /** - * SignedSource<<19c135fed3c2860b79d3e6208b163e1a>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -269,14 +269,14 @@ return { (node/*: any*/).hash = "dfac7950191066a5d1e0d99a67f503b7"; -module.exports = ((node/*: any*/)/*: Query< +export default ((node/*: any*/)/*: Query< RefetchableFragmentQuery$variables, RefetchableFragmentQuery$data, >*/); //- __generated__/foo.graphql.js /** - * SignedSource<<3963af2dff879eeff47ec478ec74e048>> + * SignedSource<<09be7413452a486cdf0270241b87e69a>> * @flow * @lightSyntaxTransform * @nogrep @@ -312,6 +312,9 @@ export type foo$key = { }; */ +import RefetchableFragmentQuery_graphql from 'RefetchableFragmentQuery.graphql'; +import foo__edges_graphql from 'foo__edges.graphql'; + var node/*: ReaderFragment*/ = (function(){ var v0 = [ "posts" @@ -349,8 +352,8 @@ return { "fragmentPathInResult": [ "viewer" ], - "operation": require('RefetchableFragmentQuery.graphql'), - "edgesFragment": require('foo__edges.graphql') + "operation": RefetchableFragmentQuery_graphql, + "edgesFragment": foo__edges_graphql } }, "name": "foo", @@ -415,7 +418,7 @@ return { (node/*: any*/).hash = "dfac7950191066a5d1e0d99a67f503b7"; -module.exports = ((node/*: any*/)/*: PrefetchableRefetchableFragment< +export default ((node/*: any*/)/*: PrefetchableRefetchableFragment< foo$fragmentType, foo$data, foo__edges$data, @@ -424,7 +427,7 @@ module.exports = ((node/*: any*/)/*: PrefetchableRefetchableFragment< //- __generated__/fooQuery.graphql.js /** - * SignedSource<<18e66ec469beae13e529e6c78e5c3e04>> + * SignedSource<<0692b813f39e023e6ae14977764bd0af>> * @flow * @lightSyntaxTransform * @nogrep @@ -631,14 +634,14 @@ return { (node/*: any*/).hash = "5b2116a3cb677e65d08c3df8b486f670"; -module.exports = ((node/*: any*/)/*: Query< +export default ((node/*: any*/)/*: Query< fooQuery$variables, fooQuery$data, >*/); //- __generated__/foo__edges.graphql.js /** - * SignedSource<> + * SignedSource<<535ff662cfd1f9edd945c6fafbfa0aff>> * @flow * @lightSyntaxTransform * @nogrep @@ -712,7 +715,7 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< foo__edges$fragmentType, foo__edges$data, >*/); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/preloadable_query_flow.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/preloadable_query_flow.expected index 2b08a3cf1f33a..845cbfde88a9b 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/preloadable_query_flow.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/preloadable_query_flow.expected @@ -9,9 +9,8 @@ graphql` { "language": "flow", "schema": "./schema.graphql", - "eagerEsModules": true, - "persistConfig": { - "file": "./operations.json" + "persistConfig": { + "file": "./operations.json" } } diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/preloadable_query_flow.input b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/preloadable_query_flow.input index 4d16ef06ba05b..d567b09fd12ec 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/preloadable_query_flow.input +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/preloadable_query_flow.input @@ -8,9 +8,8 @@ graphql` { "language": "flow", "schema": "./schema.graphql", - "eagerEsModules": true, - "persistConfig": { - "file": "./operations.json" + "persistConfig": { + "file": "./operations.json" } } diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/preloadable_query_javascript.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/preloadable_query_javascript.expected index 64b42ed3cbcb9..01f5d3bac064e 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/preloadable_query_javascript.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/preloadable_query_javascript.expected @@ -9,9 +9,8 @@ graphql` { "language": "javascript", "schema": "./schema.graphql", - "eagerEsModules": true, - "persistConfig": { - "file": "./operations.json" + "persistConfig": { + "file": "./operations.json" } } diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/preloadable_query_javascript.input b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/preloadable_query_javascript.input index 3f9b4d9e869d0..02de822bede28 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/preloadable_query_javascript.input +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/preloadable_query_javascript.input @@ -8,9 +8,8 @@ graphql` { "language": "javascript", "schema": "./schema.graphql", - "eagerEsModules": true, - "persistConfig": { - "file": "./operations.json" + "persistConfig": { + "file": "./operations.json" } } diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/preloadable_query_typescript.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/preloadable_query_typescript.expected index 40e6cf9c1f716..ad4710a0a6e3b 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/preloadable_query_typescript.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/preloadable_query_typescript.expected @@ -9,9 +9,8 @@ graphql` { "language": "typescript", "schema": "./schema.graphql", - "eagerEsModules": true, - "persistConfig": { - "file": "./operations.json" + "persistConfig": { + "file": "./operations.json" } } diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/preloadable_query_typescript.input b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/preloadable_query_typescript.input index 676812f950e8c..27a077ea015a7 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/preloadable_query_typescript.input +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/preloadable_query_typescript.input @@ -8,9 +8,8 @@ graphql` { "language": "typescript", "schema": "./schema.graphql", - "eagerEsModules": true, - "persistConfig": { - "file": "./operations.json" + "persistConfig": { + "file": "./operations.json" } } diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/relay_resolvers_in_throw_on_field_error.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/relay_resolvers_in_throw_on_field_error.expected new file mode 100644 index 0000000000000..7d000fec575d3 --- /dev/null +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/relay_resolvers_in_throw_on_field_error.expected @@ -0,0 +1,301 @@ +==================================== INPUT ==================================== +//- foo.js +graphql`query fooQuery @throwOnFieldError { + # This does not need @catch because it is a resolver + clientUser { + # This does not need @catch because it was generated by + # a strong resolver + id + } +}` + +/** + * @RelayResolver ClientUser + * A strong type + */ + + /** + * @RelayResolver Query.clientUser: ClientUser + * Resolver that returns ClientUser + */ + +//- relay.config.json +{ + "language": "flow", + "jsModuleFormat": "haste", + "schema": "schema.graphql", + "schemaExtensions": [ + "schema-extensions" + ] +} + +//- schema.graphql +type Query { + greeting: String +} + +//- schema-extensions/extension.graphql + +extend type Query { + client_field: String +} +==================================== OUTPUT =================================== +//- __generated__/ClientUser____relay_model_instance.graphql.js +/** + * SignedSource<> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { Fragment, ReaderFragment } from 'relay-runtime'; +import type { ClientUser__id$data } from "ClientUser__id.graphql"; +import type { FragmentType } from "relay-runtime"; +import {ClientUser as clientUserRelayModelInstanceResolverType} from "foo"; +// Type assertion validating that `clientUserRelayModelInstanceResolverType` resolver is correctly implemented. +// A type error here indicates that the type signature of the resolver module is incorrect. +(clientUserRelayModelInstanceResolverType: ( + id: ClientUser__id$data['id'], +) => mixed); +declare export opaque type ClientUser____relay_model_instance$fragmentType: FragmentType; +export type ClientUser____relay_model_instance$data = {| + +__relay_model_instance: $NonMaybeType>, + +$fragmentType: ClientUser____relay_model_instance$fragmentType, +|}; +export type ClientUser____relay_model_instance$key = { + +$data?: ClientUser____relay_model_instance$data, + +$fragmentSpreads: ClientUser____relay_model_instance$fragmentType, + ... +}; +*/ + +import ClientUser__id_graphql from 'ClientUser__id.graphql'; +import {ClientUser as clientUserRelayModelInstanceResolver} from 'foo'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + +var node/*: ReaderFragment*/ = { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "ClientUser____relay_model_instance", + "selections": [ + { + "alias": null, + "args": null, + "fragment": { + "args": null, + "kind": "FragmentSpread", + "name": "ClientUser__id" + }, + "kind": "RelayResolver", + "name": "__relay_model_instance", + "resolverModule": resolverDataInjector(ClientUser__id_graphql, clientUserRelayModelInstanceResolver, 'id', true), + "path": "__relay_model_instance" + } + ], + "type": "ClientUser", + "abstractKey": null +}; + +export default ((node/*: any*/)/*: Fragment< + ClientUser____relay_model_instance$fragmentType, + ClientUser____relay_model_instance$data, +>*/); + +//- __generated__/ClientUser__id.graphql.js +/** + * SignedSource<<002edcab96dd03234c41444688f0adcf>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { Fragment, ReaderFragment } from 'relay-runtime'; +import type { FragmentType } from "relay-runtime"; +declare export opaque type ClientUser__id$fragmentType: FragmentType; +export type ClientUser__id$data = {| + +id: string, + +$fragmentType: ClientUser__id$fragmentType, +|}; +export type ClientUser__id$key = { + +$data?: ClientUser__id$data, + +$fragmentSpreads: ClientUser__id$fragmentType, + ... +}; +*/ + +var node/*: ReaderFragment*/ = { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "ClientUser__id", + "selections": [ + { + "kind": "ClientExtension", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + } + ] + } + ], + "type": "ClientUser", + "abstractKey": null +}; + +export default ((node/*: any*/)/*: Fragment< + ClientUser__id$fragmentType, + ClientUser__id$data, +>*/); + +//- __generated__/fooQuery.graphql.js +/** + * SignedSource<<6ba4104661e695807aff3bb181147e4a>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ClientRequest, ClientQuery } from 'relay-runtime'; +import type { DataID } from "relay-runtime"; +import {clientUser as queryClientUserResolverType} from "foo"; +// Type assertion validating that `queryClientUserResolverType` resolver is correctly implemented. +// A type error here indicates that the type signature of the resolver module is incorrect. +(queryClientUserResolverType: () => ?{| + +id: DataID, +|}); +export type fooQuery$variables = {||}; +export type fooQuery$data = {| + +clientUser: ?{| + +id: string, + |}, +|}; +export type fooQuery = {| + response: fooQuery$data, + variables: fooQuery$variables, +|}; +*/ + +import ClientUser__id_graphql from 'ClientUser__id.graphql'; +import {ClientUser as clientUserRelayModelInstanceResolver} from 'foo'; +import {clientUser as queryClientUserResolver} from 'foo'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + +var node/*: ClientRequest*/ = (function(){ +var v0 = { + "alias": null, + "args": null, + "concreteType": "ClientUser", + "kind": "LinkedField", + "name": "clientUser", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + } + ], + "storageKey": null +}; +return { + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": { + "hasClientEdges": true, + "throwOnFieldError": true + }, + "name": "fooQuery", + "selections": [ + { + "kind": "ClientEdgeToClientObject", + "concreteType": "ClientUser", + "modelResolvers": { + "ClientUser": { + "alias": null, + "args": null, + "fragment": { + "args": null, + "kind": "FragmentSpread", + "name": "ClientUser__id" + }, + "kind": "RelayResolver", + "name": "__relay_model_instance", + "resolverModule": resolverDataInjector(ClientUser__id_graphql, clientUserRelayModelInstanceResolver, 'id', true), + "path": "clientUser.__relay_model_instance" + } + }, + "backingField": { + "alias": null, + "args": null, + "fragment": null, + "kind": "RelayResolver", + "name": "clientUser", + "resolverModule": queryClientUserResolver, + "path": "clientUser" + }, + "linkedField": (v0/*: any*/) + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [], + "kind": "Operation", + "name": "fooQuery", + "selections": [ + { + "kind": "ClientEdgeToClientObject", + "backingField": { + "name": "clientUser", + "args": null, + "fragment": null, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": false + }, + "linkedField": (v0/*: any*/) + } + ] + }, + "params": { + "cacheID": "b4419e844fe6e6b6a0c48c1d2e9e7fc8", + "id": null, + "metadata": {}, + "name": "fooQuery", + "operationKind": "query", + "text": null + } +}; +})(); + +(node/*: any*/).hash = "a96f54d4f51f87ef55b7e2d6bc0c31be"; + +export default ((node/*: any*/)/*: ClientQuery< + fooQuery$variables, + fooQuery$data, +>*/); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/relay_resolvers_in_throw_on_field_error.input b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/relay_resolvers_in_throw_on_field_error.input new file mode 100644 index 0000000000000..3de2325be46a4 --- /dev/null +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/relay_resolvers_in_throw_on_field_error.input @@ -0,0 +1,40 @@ +//- foo.js +graphql`query fooQuery @throwOnFieldError { + # This does not need @catch because it is a resolver + clientUser { + # This does not need @catch because it was generated by + # a strong resolver + id + } +}` + +/** + * @RelayResolver ClientUser + * A strong type + */ + + /** + * @RelayResolver Query.clientUser: ClientUser + * Resolver that returns ClientUser + */ + +//- relay.config.json +{ + "language": "flow", + "jsModuleFormat": "haste", + "schema": "schema.graphql", + "schemaExtensions": [ + "schema-extensions" + ] +} + +//- schema.graphql +type Query { + greeting: String +} + +//- schema-extensions/extension.graphql + +extend type Query { + client_field: String +} diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/repro_dangerously_unaliased_changes_output_before.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/repro_dangerously_unaliased_changes_output_before.expected index dad4e6395ee4f..957ce1ecbe42f 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/repro_dangerously_unaliased_changes_output_before.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/repro_dangerously_unaliased_changes_output_before.expected @@ -36,6 +36,9 @@ query ModuleNameQuery { "featureFlags": { "disable_deduping_common_structures_in_artifacts": { "kind": "enabled" + }, + "enforce_fragment_alias_where_ambiguous": { + "kind": "disabled" } } } diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/repro_dangerously_unaliased_changes_output_before.input b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/repro_dangerously_unaliased_changes_output_before.input index d589100d44364..cb3228688909f 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/repro_dangerously_unaliased_changes_output_before.input +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/repro_dangerously_unaliased_changes_output_before.input @@ -35,6 +35,9 @@ query ModuleNameQuery { "featureFlags": { "disable_deduping_common_structures_in_artifacts": { "kind": "enabled" + }, + "enforce_fragment_alias_where_ambiguous": { + "kind": "disabled" } } } diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/required_conditional_field.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/required_conditional_field.expected index 356f6971c27b2..00fd9372f563d 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/required_conditional_field.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/required_conditional_field.expected @@ -17,7 +17,7 @@ type User { name: String } ==================================== OUTPUT =================================== //- __generated__/foo.graphql.js /** - * SignedSource<<1b92de4aca2edabc15fd10c89b3ba0d0>> + * SignedSource<<3d6dbdca4b6b8b28019ed046a5f9581a>> * @flow * @lightSyntaxTransform * @nogrep @@ -78,7 +78,7 @@ var node/*: ReaderFragment*/ = { (node/*: any*/).hash = "9315d3c00a7787d5cc3f0930cf1ee1a5"; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< foo$fragmentType, foo$data, >*/); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_on_interface.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_on_interface.expected index a353383e8b8bb..50c7be39d41a0 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_on_interface.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_on_interface.expected @@ -49,7 +49,7 @@ interface IPerson { ==================================== OUTPUT =================================== //- __generated__/Admin____relay_model_instance.graphql.js /** - * SignedSource<<7fbe3989595c55397f4bcc5c81ec30b2>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -81,6 +81,10 @@ export type Admin____relay_model_instance$key = { }; */ +import {Admin as adminRelayModelInstanceResolver} from 'AdminTypeResolvers'; +import Admin__id_graphql from 'Admin__id.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -97,7 +101,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "__relay_model_instance", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('Admin__id.graphql'), require('AdminTypeResolvers').Admin, 'id', true), + "resolverModule": resolverDataInjector(Admin__id_graphql, adminRelayModelInstanceResolver, 'id', true), "path": "__relay_model_instance" } ], @@ -105,14 +109,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< Admin____relay_model_instance$fragmentType, Admin____relay_model_instance$data, >*/); //- __generated__/Admin__id.graphql.js /** - * SignedSource<<29acfbf1d6f559b8b77e9cd1f35218c0>> + * SignedSource<<78e97d08242ee5ff28fb2c5bf850cd81>> * @flow * @lightSyntaxTransform * @nogrep @@ -160,14 +164,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< Admin__id$fragmentType, Admin__id$data, >*/); //- __generated__/PersonComponentFragment.graphql.js /** - * SignedSource<<281289c09e9f11b0f0fdf7dd357dbe36>> + * SignedSource<<6a9121eabc585aa4a04d8e1049221b52>> * @flow * @lightSyntaxTransform * @nogrep @@ -192,6 +196,12 @@ export type PersonComponentFragment$key = { }; */ +import {name as adminNameResolver} from 'AdminTypeResolvers'; +import Admin____relay_model_instance_graphql from 'Admin____relay_model_instance.graphql'; +import {name as userNameResolver} from 'UserTypeResolvers'; +import User____relay_model_instance_graphql from 'User____relay_model_instance.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -214,7 +224,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "name", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('Admin____relay_model_instance.graphql'), require('AdminTypeResolvers').name, '__relay_model_instance', true), + "resolverModule": resolverDataInjector(Admin____relay_model_instance_graphql, adminNameResolver, '__relay_model_instance', true), "path": "name" } ], @@ -234,7 +244,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "name", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('User____relay_model_instance.graphql'), require('UserTypeResolvers').name, '__relay_model_instance', true), + "resolverModule": resolverDataInjector(User____relay_model_instance_graphql, userNameResolver, '__relay_model_instance', true), "path": "name" } ], @@ -250,14 +260,14 @@ var node/*: ReaderFragment*/ = { (node/*: any*/).hash = "a57dd30bd59412781e9566e1553e2d70"; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< PersonComponentFragment$fragmentType, PersonComponentFragment$data, >*/); //- __generated__/User____relay_model_instance.graphql.js /** - * SignedSource<<7ffabc2a97c3589cbfd20a23b3b608ca>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -289,6 +299,10 @@ export type User____relay_model_instance$key = { }; */ +import {User as userRelayModelInstanceResolver} from 'UserTypeResolvers'; +import User__id_graphql from 'User__id.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -305,7 +319,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "__relay_model_instance", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('User__id.graphql'), require('UserTypeResolvers').User, 'id', true), + "resolverModule": resolverDataInjector(User__id_graphql, userRelayModelInstanceResolver, 'id', true), "path": "__relay_model_instance" } ], @@ -313,14 +327,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< User____relay_model_instance$fragmentType, User____relay_model_instance$data, >*/); //- __generated__/User__id.graphql.js /** - * SignedSource<<0a0f39eb34bfc882d28378a0b05b3c17>> + * SignedSource<<56388778b313654f6700e4bfa73599a2>> * @flow * @lightSyntaxTransform * @nogrep @@ -368,7 +382,7 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< User__id$fragmentType, User__id$data, >*/); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_on_interface_of_all_strong_model_type.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_on_interface_of_all_strong_model_type.expected index 15b512be7435a..578165e56e6ee 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_on_interface_of_all_strong_model_type.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_on_interface_of_all_strong_model_type.expected @@ -46,7 +46,7 @@ interface IPerson { ==================================== OUTPUT =================================== //- __generated__/Admin____relay_model_instance.graphql.js /** - * SignedSource<<7fbe3989595c55397f4bcc5c81ec30b2>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -78,6 +78,10 @@ export type Admin____relay_model_instance$key = { }; */ +import {Admin as adminRelayModelInstanceResolver} from 'AdminTypeResolvers'; +import Admin__id_graphql from 'Admin__id.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -94,7 +98,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "__relay_model_instance", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('Admin__id.graphql'), require('AdminTypeResolvers').Admin, 'id', true), + "resolverModule": resolverDataInjector(Admin__id_graphql, adminRelayModelInstanceResolver, 'id', true), "path": "__relay_model_instance" } ], @@ -102,14 +106,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< Admin____relay_model_instance$fragmentType, Admin____relay_model_instance$data, >*/); //- __generated__/Admin__id.graphql.js /** - * SignedSource<<29acfbf1d6f559b8b77e9cd1f35218c0>> + * SignedSource<<78e97d08242ee5ff28fb2c5bf850cd81>> * @flow * @lightSyntaxTransform * @nogrep @@ -157,14 +161,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< Admin__id$fragmentType, Admin__id$data, >*/); //- __generated__/PersonComponentFragment.graphql.js /** - * SignedSource<<4ea82f388eeb21ecbc79f404917c194e>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -197,6 +201,12 @@ export type PersonComponentFragment$key = { }; */ +import Admin____relay_model_instance_graphql from 'Admin____relay_model_instance.graphql'; +import {name as adminNameResolver} from 'IPersonResolvers'; +import {name as userNameResolver} from 'IPersonResolvers'; +import User____relay_model_instance_graphql from 'User____relay_model_instance.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -219,7 +229,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "name", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('Admin____relay_model_instance.graphql'), require('IPersonResolvers').name, '__relay_model_instance', true), + "resolverModule": resolverDataInjector(Admin____relay_model_instance_graphql, adminNameResolver, '__relay_model_instance', true), "path": "name" } ], @@ -239,7 +249,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "name", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('User____relay_model_instance.graphql'), require('IPersonResolvers').name, '__relay_model_instance', true), + "resolverModule": resolverDataInjector(User____relay_model_instance_graphql, userNameResolver, '__relay_model_instance', true), "path": "name" } ], @@ -255,14 +265,14 @@ var node/*: ReaderFragment*/ = { (node/*: any*/).hash = "a57dd30bd59412781e9566e1553e2d70"; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< PersonComponentFragment$fragmentType, PersonComponentFragment$data, >*/); //- __generated__/User____relay_model_instance.graphql.js /** - * SignedSource<<7ffabc2a97c3589cbfd20a23b3b608ca>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -294,6 +304,10 @@ export type User____relay_model_instance$key = { }; */ +import {User as userRelayModelInstanceResolver} from 'UserTypeResolvers'; +import User__id_graphql from 'User__id.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -310,7 +324,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "__relay_model_instance", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('User__id.graphql'), require('UserTypeResolvers').User, 'id', true), + "resolverModule": resolverDataInjector(User__id_graphql, userRelayModelInstanceResolver, 'id', true), "path": "__relay_model_instance" } ], @@ -318,14 +332,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< User____relay_model_instance$fragmentType, User____relay_model_instance$data, >*/); //- __generated__/User__id.graphql.js /** - * SignedSource<<0a0f39eb34bfc882d28378a0b05b3c17>> + * SignedSource<<56388778b313654f6700e4bfa73599a2>> * @flow * @lightSyntaxTransform * @nogrep @@ -373,7 +387,7 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< User__id$fragmentType, User__id$data, >*/); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_on_interface_of_all_strong_model_type_including_cse.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_on_interface_of_all_strong_model_type_including_cse.expected index 399d75857dca0..4b6c934884b84 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_on_interface_of_all_strong_model_type_including_cse.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_on_interface_of_all_strong_model_type_including_cse.expected @@ -51,7 +51,7 @@ type Visitor implements IPerson { ==================================== OUTPUT =================================== //- __generated__/Admin____relay_model_instance.graphql.js /** - * SignedSource<<7fbe3989595c55397f4bcc5c81ec30b2>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -83,6 +83,10 @@ export type Admin____relay_model_instance$key = { }; */ +import {Admin as adminRelayModelInstanceResolver} from 'AdminTypeResolvers'; +import Admin__id_graphql from 'Admin__id.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -99,7 +103,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "__relay_model_instance", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('Admin__id.graphql'), require('AdminTypeResolvers').Admin, 'id', true), + "resolverModule": resolverDataInjector(Admin__id_graphql, adminRelayModelInstanceResolver, 'id', true), "path": "__relay_model_instance" } ], @@ -107,14 +111,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< Admin____relay_model_instance$fragmentType, Admin____relay_model_instance$data, >*/); //- __generated__/Admin__id.graphql.js /** - * SignedSource<<29acfbf1d6f559b8b77e9cd1f35218c0>> + * SignedSource<<78e97d08242ee5ff28fb2c5bf850cd81>> * @flow * @lightSyntaxTransform * @nogrep @@ -162,14 +166,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< Admin__id$fragmentType, Admin__id$data, >*/); //- __generated__/PersonComponentFragment.graphql.js /** - * SignedSource<<2707615d1c65ebada666a7a0d138837e>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -202,6 +206,12 @@ export type PersonComponentFragment$key = { }; */ +import Admin____relay_model_instance_graphql from 'Admin____relay_model_instance.graphql'; +import {name as adminNameResolver} from 'IPersonResolvers'; +import {name as userNameResolver} from 'IPersonResolvers'; +import User____relay_model_instance_graphql from 'User____relay_model_instance.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -238,7 +248,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "name", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('Admin____relay_model_instance.graphql'), require('IPersonResolvers').name, '__relay_model_instance', true), + "resolverModule": resolverDataInjector(Admin____relay_model_instance_graphql, adminNameResolver, '__relay_model_instance', true), "path": "name" } ], @@ -258,7 +268,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "name", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('User____relay_model_instance.graphql'), require('IPersonResolvers').name, '__relay_model_instance', true), + "resolverModule": resolverDataInjector(User____relay_model_instance_graphql, userNameResolver, '__relay_model_instance', true), "path": "name" } ], @@ -274,14 +284,14 @@ var node/*: ReaderFragment*/ = { (node/*: any*/).hash = "a57dd30bd59412781e9566e1553e2d70"; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< PersonComponentFragment$fragmentType, PersonComponentFragment$data, >*/); //- __generated__/User____relay_model_instance.graphql.js /** - * SignedSource<<7ffabc2a97c3589cbfd20a23b3b608ca>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -313,6 +323,10 @@ export type User____relay_model_instance$key = { }; */ +import {User as userRelayModelInstanceResolver} from 'UserTypeResolvers'; +import User__id_graphql from 'User__id.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -329,7 +343,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "__relay_model_instance", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('User__id.graphql'), require('UserTypeResolvers').User, 'id', true), + "resolverModule": resolverDataInjector(User__id_graphql, userRelayModelInstanceResolver, 'id', true), "path": "__relay_model_instance" } ], @@ -337,14 +351,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< User____relay_model_instance$fragmentType, User____relay_model_instance$data, >*/); //- __generated__/User__id.graphql.js /** - * SignedSource<<0a0f39eb34bfc882d28378a0b05b3c17>> + * SignedSource<<56388778b313654f6700e4bfa73599a2>> * @flow * @lightSyntaxTransform * @nogrep @@ -392,7 +406,7 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< User__id$fragmentType, User__id$data, >*/); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_on_interface_of_all_strong_model_type_with_root_fragment.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_on_interface_of_all_strong_model_type_with_root_fragment.expected index b586fa6d8befd..d94fd6fa66c87 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_on_interface_of_all_strong_model_type_with_root_fragment.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_on_interface_of_all_strong_model_type_with_root_fragment.expected @@ -49,7 +49,7 @@ type Visitor implements IPerson { ==================================== OUTPUT =================================== //- __generated__/Admin____relay_model_instance.graphql.js /** - * SignedSource<<7fbe3989595c55397f4bcc5c81ec30b2>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -81,6 +81,10 @@ export type Admin____relay_model_instance$key = { }; */ +import {Admin as adminRelayModelInstanceResolver} from 'AdminTypeResolvers'; +import Admin__id_graphql from 'Admin__id.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -97,7 +101,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "__relay_model_instance", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('Admin__id.graphql'), require('AdminTypeResolvers').Admin, 'id', true), + "resolverModule": resolverDataInjector(Admin__id_graphql, adminRelayModelInstanceResolver, 'id', true), "path": "__relay_model_instance" } ], @@ -105,14 +109,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< Admin____relay_model_instance$fragmentType, Admin____relay_model_instance$data, >*/); //- __generated__/Admin__id.graphql.js /** - * SignedSource<<29acfbf1d6f559b8b77e9cd1f35218c0>> + * SignedSource<<78e97d08242ee5ff28fb2c5bf850cd81>> * @flow * @lightSyntaxTransform * @nogrep @@ -160,14 +164,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< Admin__id$fragmentType, Admin__id$data, >*/); //- __generated__/IPersonResolversFragment.graphql.js /** - * SignedSource<> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -198,6 +202,10 @@ export type IPersonResolversFragment$key = { }; */ +import {name as adminNameResolver} from 'IPersonResolvers'; +import {name as userNameResolver} from 'IPersonResolvers'; +import {name as visitorNameResolver} from 'IPersonResolvers'; + var node/*: ReaderFragment*/ = (function(){ var v0 = { "args": null, @@ -222,7 +230,7 @@ return { "fragment": (v0/*: any*/), "kind": "RelayResolver", "name": "name", - "resolverModule": require('IPersonResolvers').name, + "resolverModule": visitorNameResolver, "path": "name" } ], @@ -238,7 +246,7 @@ return { "fragment": (v0/*: any*/), "kind": "RelayResolver", "name": "name", - "resolverModule": require('IPersonResolvers').name, + "resolverModule": adminNameResolver, "path": "name" } ], @@ -254,7 +262,7 @@ return { "fragment": (v0/*: any*/), "kind": "RelayResolver", "name": "name", - "resolverModule": require('IPersonResolvers').name, + "resolverModule": userNameResolver, "path": "name" } ], @@ -271,14 +279,14 @@ return { (node/*: any*/).hash = "ba873f284ce4d50f2e9204a78f11952a"; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< IPersonResolversFragment$fragmentType, IPersonResolversFragment$data, >*/); //- __generated__/User____relay_model_instance.graphql.js /** - * SignedSource<<7ffabc2a97c3589cbfd20a23b3b608ca>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -310,6 +318,10 @@ export type User____relay_model_instance$key = { }; */ +import {User as userRelayModelInstanceResolver} from 'UserTypeResolvers'; +import User__id_graphql from 'User__id.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -326,7 +338,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "__relay_model_instance", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('User__id.graphql'), require('UserTypeResolvers').User, 'id', true), + "resolverModule": resolverDataInjector(User__id_graphql, userRelayModelInstanceResolver, 'id', true), "path": "__relay_model_instance" } ], @@ -334,14 +346,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< User____relay_model_instance$fragmentType, User____relay_model_instance$data, >*/); //- __generated__/User__id.graphql.js /** - * SignedSource<<0a0f39eb34bfc882d28378a0b05b3c17>> + * SignedSource<<56388778b313654f6700e4bfa73599a2>> * @flow * @lightSyntaxTransform * @nogrep @@ -389,7 +401,7 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< User__id$fragmentType, User__id$data, >*/); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_on_interface_of_all_weak_model_type.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_on_interface_of_all_weak_model_type.expected index 7db8b071d696a..7231837754c5c 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_on_interface_of_all_weak_model_type.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_on_interface_of_all_weak_model_type.expected @@ -46,7 +46,7 @@ interface IPerson ==================================== OUTPUT =================================== //- __generated__/Admin____relay_model_instance.graphql.js /** - * SignedSource<<523d097198aa2ffa2a1209e24ac2a337>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -95,14 +95,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< Admin____relay_model_instance$fragmentType, Admin____relay_model_instance$data, >*/); //- __generated__/PersonComponentFragment.graphql.js /** - * SignedSource<<4ea82f388eeb21ecbc79f404917c194e>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -135,6 +135,12 @@ export type PersonComponentFragment$key = { }; */ +import Admin____relay_model_instance_graphql from 'Admin____relay_model_instance.graphql'; +import {name as adminNameResolver} from 'IPersonResolvers'; +import {name as userNameResolver} from 'IPersonResolvers'; +import User____relay_model_instance_graphql from 'User____relay_model_instance.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -157,7 +163,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "name", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('Admin____relay_model_instance.graphql'), require('IPersonResolvers').name, '__relay_model_instance', true), + "resolverModule": resolverDataInjector(Admin____relay_model_instance_graphql, adminNameResolver, '__relay_model_instance', true), "path": "name" } ], @@ -177,7 +183,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "name", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('User____relay_model_instance.graphql'), require('IPersonResolvers').name, '__relay_model_instance', true), + "resolverModule": resolverDataInjector(User____relay_model_instance_graphql, userNameResolver, '__relay_model_instance', true), "path": "name" } ], @@ -193,14 +199,14 @@ var node/*: ReaderFragment*/ = { (node/*: any*/).hash = "a57dd30bd59412781e9566e1553e2d70"; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< PersonComponentFragment$fragmentType, PersonComponentFragment$data, >*/); //- __generated__/User____relay_model_instance.graphql.js /** - * SignedSource<<9a188c26688bb46f65ed80df4ae938c3>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -249,7 +255,7 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< User____relay_model_instance$fragmentType, User____relay_model_instance$data, >*/); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_on_interface_returns_custom_scalar.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_on_interface_returns_custom_scalar.expected index b450a6c4f09eb..3fb3c4a6bde3a 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_on_interface_returns_custom_scalar.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_on_interface_returns_custom_scalar.expected @@ -67,7 +67,7 @@ interface IPerson { ==================================== OUTPUT =================================== //- __generated__/Admin____relay_model_instance.graphql.js /** - * SignedSource<<7fbe3989595c55397f4bcc5c81ec30b2>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -99,6 +99,10 @@ export type Admin____relay_model_instance$key = { }; */ +import {Admin as adminRelayModelInstanceResolver} from 'AdminTypeResolvers'; +import Admin__id_graphql from 'Admin__id.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -115,7 +119,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "__relay_model_instance", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('Admin__id.graphql'), require('AdminTypeResolvers').Admin, 'id', true), + "resolverModule": resolverDataInjector(Admin__id_graphql, adminRelayModelInstanceResolver, 'id', true), "path": "__relay_model_instance" } ], @@ -123,14 +127,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< Admin____relay_model_instance$fragmentType, Admin____relay_model_instance$data, >*/); //- __generated__/Admin__id.graphql.js /** - * SignedSource<<29acfbf1d6f559b8b77e9cd1f35218c0>> + * SignedSource<<78e97d08242ee5ff28fb2c5bf850cd81>> * @flow * @lightSyntaxTransform * @nogrep @@ -178,14 +182,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< Admin__id$fragmentType, Admin__id$data, >*/); //- __generated__/PersonComponentFragment.graphql.js /** - * SignedSource<<5a351d5a9beb2093277e17f6efa94c15>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -211,6 +215,12 @@ export type PersonComponentFragment$key = { }; */ +import {someComplexObject as adminSomeComplexObjectResolver} from 'AdminTypeResolvers'; +import Admin____relay_model_instance_graphql from 'Admin____relay_model_instance.graphql'; +import {someComplexObject as userSomeComplexObjectResolver} from 'UserTypeResolvers'; +import User____relay_model_instance_graphql from 'User____relay_model_instance.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -233,7 +243,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "someComplexObject", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('Admin____relay_model_instance.graphql'), require('AdminTypeResolvers').someComplexObject, '__relay_model_instance', true), + "resolverModule": resolverDataInjector(Admin____relay_model_instance_graphql, adminSomeComplexObjectResolver, '__relay_model_instance', true), "path": "someComplexObject" } ], @@ -253,7 +263,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "someComplexObject", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('User____relay_model_instance.graphql'), require('UserTypeResolvers').someComplexObject, '__relay_model_instance', true), + "resolverModule": resolverDataInjector(User____relay_model_instance_graphql, userSomeComplexObjectResolver, '__relay_model_instance', true), "path": "someComplexObject" } ], @@ -269,14 +279,14 @@ var node/*: ReaderFragment*/ = { (node/*: any*/).hash = "d8e63d57ea12bd6248ca0a69e440f37c"; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< PersonComponentFragment$fragmentType, PersonComponentFragment$data, >*/); //- __generated__/User____relay_model_instance.graphql.js /** - * SignedSource<<7ffabc2a97c3589cbfd20a23b3b608ca>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -308,6 +318,10 @@ export type User____relay_model_instance$key = { }; */ +import {User as userRelayModelInstanceResolver} from 'UserTypeResolvers'; +import User__id_graphql from 'User__id.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -324,7 +338,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "__relay_model_instance", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('User__id.graphql'), require('UserTypeResolvers').User, 'id', true), + "resolverModule": resolverDataInjector(User__id_graphql, userRelayModelInstanceResolver, 'id', true), "path": "__relay_model_instance" } ], @@ -332,14 +346,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< User____relay_model_instance$fragmentType, User____relay_model_instance$data, >*/); //- __generated__/User__id.graphql.js /** - * SignedSource<<0a0f39eb34bfc882d28378a0b05b3c17>> + * SignedSource<<56388778b313654f6700e4bfa73599a2>> * @flow * @lightSyntaxTransform * @nogrep @@ -387,7 +401,7 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< User__id$fragmentType, User__id$data, >*/); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_client_schema_extension_enum.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_client_schema_extension_enum.expected index 464acdee66d56..9e2859951d643 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_client_schema_extension_enum.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_client_schema_extension_enum.expected @@ -45,7 +45,7 @@ enum Status { ==================================== OUTPUT =================================== //- __generated__/SomeComponentFragment.graphql.js /** - * SignedSource<> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -78,6 +78,10 @@ export type SomeComponentFragment$key = { }; */ +import {status as userStatusResolver} from 'UserTypeResolvers'; +import User____relay_model_instance_graphql from 'User____relay_model_instance.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -94,7 +98,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "status", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('User____relay_model_instance.graphql'), require('UserTypeResolvers').status, '__relay_model_instance', true), + "resolverModule": resolverDataInjector(User____relay_model_instance_graphql, userStatusResolver, '__relay_model_instance', true), "path": "status" } ], @@ -104,14 +108,14 @@ var node/*: ReaderFragment*/ = { (node/*: any*/).hash = "6bb04088a9e45bd235fc92a69a2f3ef5"; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< SomeComponentFragment$fragmentType, SomeComponentFragment$data, >*/); //- __generated__/User____relay_model_instance.graphql.js /** - * SignedSource<<7ffabc2a97c3589cbfd20a23b3b608ca>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -143,6 +147,10 @@ export type User____relay_model_instance$key = { }; */ +import {User as userRelayModelInstanceResolver} from 'UserTypeResolvers'; +import User__id_graphql from 'User__id.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -159,7 +167,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "__relay_model_instance", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('User__id.graphql'), require('UserTypeResolvers').User, 'id', true), + "resolverModule": resolverDataInjector(User__id_graphql, userRelayModelInstanceResolver, 'id', true), "path": "__relay_model_instance" } ], @@ -167,14 +175,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< User____relay_model_instance$fragmentType, User____relay_model_instance$data, >*/); //- __generated__/User__id.graphql.js /** - * SignedSource<<0a0f39eb34bfc882d28378a0b05b3c17>> + * SignedSource<<56388778b313654f6700e4bfa73599a2>> * @flow * @lightSyntaxTransform * @nogrep @@ -222,7 +230,7 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< User__id$fragmentType, User__id$data, >*/); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_enum_with_enum_suffix.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_enum_with_enum_suffix.expected index 831f9cb88faa5..c3aa2f66925b7 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_enum_with_enum_suffix.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_enum_with_enum_suffix.expected @@ -46,7 +46,7 @@ enum Status { ==================================== OUTPUT =================================== //- __generated__/SomeComponentFragment.graphql.js /** - * SignedSource<> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -79,6 +79,10 @@ export type SomeComponentFragment$key = { }; */ +import {status as userStatusResolver} from 'UserTypeResolvers'; +import User____relay_model_instance_graphql from 'User____relay_model_instance.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -95,7 +99,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "status", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('User____relay_model_instance.graphql'), require('UserTypeResolvers').status, '__relay_model_instance', true), + "resolverModule": resolverDataInjector(User____relay_model_instance_graphql, userStatusResolver, '__relay_model_instance', true), "path": "status" } ], @@ -105,14 +109,14 @@ var node/*: ReaderFragment*/ = { (node/*: any*/).hash = "6bb04088a9e45bd235fc92a69a2f3ef5"; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< SomeComponentFragment$fragmentType, SomeComponentFragment$data, >*/); //- __generated__/User____relay_model_instance.graphql.js /** - * SignedSource<<7ffabc2a97c3589cbfd20a23b3b608ca>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -144,6 +148,10 @@ export type User____relay_model_instance$key = { }; */ +import {User as userRelayModelInstanceResolver} from 'UserTypeResolvers'; +import User__id_graphql from 'User__id.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -160,7 +168,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "__relay_model_instance", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('User__id.graphql'), require('UserTypeResolvers').User, 'id', true), + "resolverModule": resolverDataInjector(User__id_graphql, userRelayModelInstanceResolver, 'id', true), "path": "__relay_model_instance" } ], @@ -168,14 +176,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< User____relay_model_instance$fragmentType, User____relay_model_instance$data, >*/); //- __generated__/User__id.graphql.js /** - * SignedSource<<0a0f39eb34bfc882d28378a0b05b3c17>> + * SignedSource<<56388778b313654f6700e4bfa73599a2>> * @flow * @lightSyntaxTransform * @nogrep @@ -223,7 +231,7 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< User__id$fragmentType, User__id$data, >*/); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_interface_of_all_live_model_type.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_interface_of_all_live_model_type.expected index 28bfd4f81360d..2b0ad2acf2949 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_interface_of_all_live_model_type.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_interface_of_all_live_model_type.expected @@ -57,7 +57,7 @@ interface IPerson { ==================================== OUTPUT =================================== //- __generated__/Admin____relay_model_instance.graphql.js /** - * SignedSource<<43fd74ebd2b94a456ec3c8657edf2868>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -89,6 +89,10 @@ export type Admin____relay_model_instance$key = { }; */ +import {Admin as adminRelayModelInstanceResolver} from 'AdminTypeResolvers'; +import Admin__id_graphql from 'Admin__id.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -105,7 +109,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayLiveResolver", "name": "__relay_model_instance", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('Admin__id.graphql'), require('AdminTypeResolvers').Admin, 'id', true), + "resolverModule": resolverDataInjector(Admin__id_graphql, adminRelayModelInstanceResolver, 'id', true), "path": "__relay_model_instance" } ], @@ -113,14 +117,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< Admin____relay_model_instance$fragmentType, Admin____relay_model_instance$data, >*/); //- __generated__/Admin__id.graphql.js /** - * SignedSource<<29acfbf1d6f559b8b77e9cd1f35218c0>> + * SignedSource<<78e97d08242ee5ff28fb2c5bf850cd81>> * @flow * @lightSyntaxTransform * @nogrep @@ -168,14 +172,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< Admin__id$fragmentType, Admin__id$data, >*/); //- __generated__/QueryComponentQuery.graphql.js /** - * SignedSource<> + * SignedSource<<59fc6b33f73b794b7e7e25239be84303>> * @flow * @lightSyntaxTransform * @nogrep @@ -207,6 +211,17 @@ export type QueryComponentQuery = {| |}; */ +import {Admin as adminRelayModelInstanceResolver} from 'AdminTypeResolvers'; +import {name as adminNameResolver} from 'AdminTypeResolvers'; +import Admin____relay_model_instance_graphql from 'Admin____relay_model_instance.graphql'; +import Admin__id_graphql from 'Admin__id.graphql'; +import {person as queryPersonResolver} from 'QueryResolvers'; +import {User as userRelayModelInstanceResolver} from 'UserTypeResolvers'; +import {name as userNameResolver} from 'UserTypeResolvers'; +import User____relay_model_instance_graphql from 'User____relay_model_instance.graphql'; +import User__id_graphql from 'User__id.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ClientRequest*/ = (function(){ var v0 = { "alias": null, @@ -240,8 +255,8 @@ return { "name": "Admin__id" }, "kind": "RelayLiveResolver", - "name": "person", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('Admin__id.graphql'), require('AdminTypeResolvers').Admin, 'id', true), + "name": "__relay_model_instance", + "resolverModule": resolverDataInjector(Admin__id_graphql, adminRelayModelInstanceResolver, 'id', true), "path": "person.__relay_model_instance" }, "User": { @@ -253,8 +268,8 @@ return { "name": "User__id" }, "kind": "RelayLiveResolver", - "name": "person", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('User__id.graphql'), require('UserTypeResolvers').User, 'id', true), + "name": "__relay_model_instance", + "resolverModule": resolverDataInjector(User__id_graphql, userRelayModelInstanceResolver, 'id', true), "path": "person.__relay_model_instance" } }, @@ -264,7 +279,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "person", - "resolverModule": require('QueryResolvers').person, + "resolverModule": queryPersonResolver, "path": "person" }, "linkedField": { @@ -288,7 +303,7 @@ return { }, "kind": "RelayResolver", "name": "name", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('Admin____relay_model_instance.graphql'), require('AdminTypeResolvers').name, '__relay_model_instance', true), + "resolverModule": resolverDataInjector(Admin____relay_model_instance_graphql, adminNameResolver, '__relay_model_instance', true), "path": "person.name" } ], @@ -308,7 +323,7 @@ return { }, "kind": "RelayResolver", "name": "name", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('User____relay_model_instance.graphql'), require('UserTypeResolvers').name, '__relay_model_instance', true), + "resolverModule": resolverDataInjector(User____relay_model_instance_graphql, userNameResolver, '__relay_model_instance', true), "path": "person.name" } ], @@ -442,14 +457,14 @@ return { (node/*: any*/).hash = "cc7b67152b1dce33f04a61bea084084f"; -module.exports = ((node/*: any*/)/*: ClientQuery< +export default ((node/*: any*/)/*: ClientQuery< QueryComponentQuery$variables, QueryComponentQuery$data, >*/); //- __generated__/User____relay_model_instance.graphql.js /** - * SignedSource<<5b1064cbbcdf6ad4c972e722067e79b2>> + * SignedSource<<1d7f7276af9b68a8321f13626bfca7aa>> * @flow * @lightSyntaxTransform * @nogrep @@ -481,6 +496,10 @@ export type User____relay_model_instance$key = { }; */ +import {User as userRelayModelInstanceResolver} from 'UserTypeResolvers'; +import User__id_graphql from 'User__id.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -497,7 +516,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayLiveResolver", "name": "__relay_model_instance", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('User__id.graphql'), require('UserTypeResolvers').User, 'id', true), + "resolverModule": resolverDataInjector(User__id_graphql, userRelayModelInstanceResolver, 'id', true), "path": "__relay_model_instance" } ], @@ -505,14 +524,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< User____relay_model_instance$fragmentType, User____relay_model_instance$data, >*/); //- __generated__/User__id.graphql.js /** - * SignedSource<<0a0f39eb34bfc882d28378a0b05b3c17>> + * SignedSource<<56388778b313654f6700e4bfa73599a2>> * @flow * @lightSyntaxTransform * @nogrep @@ -560,7 +579,7 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< User__id$fragmentType, User__id$data, >*/); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_interface_of_all_strong_model_type.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_interface_of_all_strong_model_type.expected index f6758dae84bdb..5f2094fdb6e9e 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_interface_of_all_strong_model_type.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_interface_of_all_strong_model_type.expected @@ -55,7 +55,7 @@ interface IPerson { ==================================== OUTPUT =================================== //- __generated__/Admin____relay_model_instance.graphql.js /** - * SignedSource<<7fbe3989595c55397f4bcc5c81ec30b2>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -87,6 +87,10 @@ export type Admin____relay_model_instance$key = { }; */ +import {Admin as adminRelayModelInstanceResolver} from 'AdminTypeResolvers'; +import Admin__id_graphql from 'Admin__id.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -103,7 +107,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "__relay_model_instance", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('Admin__id.graphql'), require('AdminTypeResolvers').Admin, 'id', true), + "resolverModule": resolverDataInjector(Admin__id_graphql, adminRelayModelInstanceResolver, 'id', true), "path": "__relay_model_instance" } ], @@ -111,14 +115,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< Admin____relay_model_instance$fragmentType, Admin____relay_model_instance$data, >*/); //- __generated__/Admin__id.graphql.js /** - * SignedSource<<29acfbf1d6f559b8b77e9cd1f35218c0>> + * SignedSource<<78e97d08242ee5ff28fb2c5bf850cd81>> * @flow * @lightSyntaxTransform * @nogrep @@ -166,14 +170,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< Admin__id$fragmentType, Admin__id$data, >*/); //- __generated__/PersonComponentQuery.graphql.js /** - * SignedSource<> + * SignedSource<<5973012c2c6a92f1f47b2fc3b4a4e212>> * @flow * @lightSyntaxTransform * @nogrep @@ -205,6 +209,17 @@ export type PersonComponentQuery = {| |}; */ +import {Admin as adminRelayModelInstanceResolver} from 'AdminTypeResolvers'; +import {name as adminNameResolver} from 'AdminTypeResolvers'; +import Admin____relay_model_instance_graphql from 'Admin____relay_model_instance.graphql'; +import Admin__id_graphql from 'Admin__id.graphql'; +import {person as queryPersonResolver} from 'QueryResolvers'; +import {User as userRelayModelInstanceResolver} from 'UserTypeResolvers'; +import {name as userNameResolver} from 'UserTypeResolvers'; +import User____relay_model_instance_graphql from 'User____relay_model_instance.graphql'; +import User__id_graphql from 'User__id.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ClientRequest*/ = (function(){ var v0 = { "alias": null, @@ -238,8 +253,8 @@ return { "name": "Admin__id" }, "kind": "RelayResolver", - "name": "person", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('Admin__id.graphql'), require('AdminTypeResolvers').Admin, 'id', true), + "name": "__relay_model_instance", + "resolverModule": resolverDataInjector(Admin__id_graphql, adminRelayModelInstanceResolver, 'id', true), "path": "person.__relay_model_instance" }, "User": { @@ -251,8 +266,8 @@ return { "name": "User__id" }, "kind": "RelayResolver", - "name": "person", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('User__id.graphql'), require('UserTypeResolvers').User, 'id', true), + "name": "__relay_model_instance", + "resolverModule": resolverDataInjector(User__id_graphql, userRelayModelInstanceResolver, 'id', true), "path": "person.__relay_model_instance" } }, @@ -262,7 +277,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "person", - "resolverModule": require('QueryResolvers').person, + "resolverModule": queryPersonResolver, "path": "person" }, "linkedField": { @@ -286,7 +301,7 @@ return { }, "kind": "RelayResolver", "name": "name", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('Admin____relay_model_instance.graphql'), require('AdminTypeResolvers').name, '__relay_model_instance', true), + "resolverModule": resolverDataInjector(Admin____relay_model_instance_graphql, adminNameResolver, '__relay_model_instance', true), "path": "person.name" } ], @@ -306,7 +321,7 @@ return { }, "kind": "RelayResolver", "name": "name", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('User____relay_model_instance.graphql'), require('UserTypeResolvers').name, '__relay_model_instance', true), + "resolverModule": resolverDataInjector(User____relay_model_instance_graphql, userNameResolver, '__relay_model_instance', true), "path": "person.name" } ], @@ -440,14 +455,14 @@ return { (node/*: any*/).hash = "6f362dca4e4d03f5759795a4ce89dee2"; -module.exports = ((node/*: any*/)/*: ClientQuery< +export default ((node/*: any*/)/*: ClientQuery< PersonComponentQuery$variables, PersonComponentQuery$data, >*/); //- __generated__/User____relay_model_instance.graphql.js /** - * SignedSource<<7ffabc2a97c3589cbfd20a23b3b608ca>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -479,6 +494,10 @@ export type User____relay_model_instance$key = { }; */ +import {User as userRelayModelInstanceResolver} from 'UserTypeResolvers'; +import User__id_graphql from 'User__id.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -495,7 +514,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "__relay_model_instance", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('User__id.graphql'), require('UserTypeResolvers').User, 'id', true), + "resolverModule": resolverDataInjector(User__id_graphql, userRelayModelInstanceResolver, 'id', true), "path": "__relay_model_instance" } ], @@ -503,14 +522,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< User____relay_model_instance$fragmentType, User____relay_model_instance$data, >*/); //- __generated__/User__id.graphql.js /** - * SignedSource<<0a0f39eb34bfc882d28378a0b05b3c17>> + * SignedSource<<56388778b313654f6700e4bfa73599a2>> * @flow * @lightSyntaxTransform * @nogrep @@ -558,7 +577,7 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< User__id$fragmentType, User__id$data, >*/); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_interface_of_all_strong_model_type_including_cse.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_interface_of_all_strong_model_type_including_cse.expected index 5de176d4401a0..5ea06491b714a 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_interface_of_all_strong_model_type_including_cse.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_interface_of_all_strong_model_type_including_cse.expected @@ -60,7 +60,7 @@ type Visitor implements IPerson { ==================================== OUTPUT =================================== //- __generated__/Admin____relay_model_instance.graphql.js /** - * SignedSource<<7fbe3989595c55397f4bcc5c81ec30b2>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -92,6 +92,10 @@ export type Admin____relay_model_instance$key = { }; */ +import {Admin as adminRelayModelInstanceResolver} from 'AdminTypeResolvers'; +import Admin__id_graphql from 'Admin__id.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -108,7 +112,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "__relay_model_instance", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('Admin__id.graphql'), require('AdminTypeResolvers').Admin, 'id', true), + "resolverModule": resolverDataInjector(Admin__id_graphql, adminRelayModelInstanceResolver, 'id', true), "path": "__relay_model_instance" } ], @@ -116,14 +120,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< Admin____relay_model_instance$fragmentType, Admin____relay_model_instance$data, >*/); //- __generated__/Admin__id.graphql.js /** - * SignedSource<<29acfbf1d6f559b8b77e9cd1f35218c0>> + * SignedSource<<78e97d08242ee5ff28fb2c5bf850cd81>> * @flow * @lightSyntaxTransform * @nogrep @@ -171,14 +175,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< Admin__id$fragmentType, Admin__id$data, >*/); //- __generated__/PersonComponentQuery.graphql.js /** - * SignedSource<<3a0314e8be13b513500cffb4650eab31>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -210,6 +214,17 @@ export type PersonComponentQuery = {| |}; */ +import {Admin as adminRelayModelInstanceResolver} from 'AdminTypeResolvers'; +import {name as adminNameResolver} from 'AdminTypeResolvers'; +import Admin____relay_model_instance_graphql from 'Admin____relay_model_instance.graphql'; +import Admin__id_graphql from 'Admin__id.graphql'; +import {person as queryPersonResolver} from 'QueryResolvers'; +import {User as userRelayModelInstanceResolver} from 'UserTypeResolvers'; +import {name as userNameResolver} from 'UserTypeResolvers'; +import User____relay_model_instance_graphql from 'User____relay_model_instance.graphql'; +import User__id_graphql from 'User__id.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ClientRequest*/ = (function(){ var v0 = { "kind": "InlineFragment", @@ -257,8 +272,8 @@ return { "name": "Admin__id" }, "kind": "RelayResolver", - "name": "person", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('Admin__id.graphql'), require('AdminTypeResolvers').Admin, 'id', true), + "name": "__relay_model_instance", + "resolverModule": resolverDataInjector(Admin__id_graphql, adminRelayModelInstanceResolver, 'id', true), "path": "person.__relay_model_instance" }, "User": { @@ -270,8 +285,8 @@ return { "name": "User__id" }, "kind": "RelayResolver", - "name": "person", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('User__id.graphql'), require('UserTypeResolvers').User, 'id', true), + "name": "__relay_model_instance", + "resolverModule": resolverDataInjector(User__id_graphql, userRelayModelInstanceResolver, 'id', true), "path": "person.__relay_model_instance" } }, @@ -281,7 +296,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "person", - "resolverModule": require('QueryResolvers').person, + "resolverModule": queryPersonResolver, "path": "person" }, "linkedField": { @@ -306,7 +321,7 @@ return { }, "kind": "RelayResolver", "name": "name", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('Admin____relay_model_instance.graphql'), require('AdminTypeResolvers').name, '__relay_model_instance', true), + "resolverModule": resolverDataInjector(Admin____relay_model_instance_graphql, adminNameResolver, '__relay_model_instance', true), "path": "person.name" } ], @@ -326,7 +341,7 @@ return { }, "kind": "RelayResolver", "name": "name", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('User____relay_model_instance.graphql'), require('UserTypeResolvers').name, '__relay_model_instance', true), + "resolverModule": resolverDataInjector(User____relay_model_instance_graphql, userNameResolver, '__relay_model_instance', true), "path": "person.name" } ], @@ -461,14 +476,14 @@ return { (node/*: any*/).hash = "6f362dca4e4d03f5759795a4ce89dee2"; -module.exports = ((node/*: any*/)/*: ClientQuery< +export default ((node/*: any*/)/*: ClientQuery< PersonComponentQuery$variables, PersonComponentQuery$data, >*/); //- __generated__/User____relay_model_instance.graphql.js /** - * SignedSource<<7ffabc2a97c3589cbfd20a23b3b608ca>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -500,6 +515,10 @@ export type User____relay_model_instance$key = { }; */ +import {User as userRelayModelInstanceResolver} from 'UserTypeResolvers'; +import User__id_graphql from 'User__id.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -516,7 +535,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "__relay_model_instance", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('User__id.graphql'), require('UserTypeResolvers').User, 'id', true), + "resolverModule": resolverDataInjector(User__id_graphql, userRelayModelInstanceResolver, 'id', true), "path": "__relay_model_instance" } ], @@ -524,14 +543,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< User____relay_model_instance$fragmentType, User____relay_model_instance$data, >*/); //- __generated__/User__id.graphql.js /** - * SignedSource<<0a0f39eb34bfc882d28378a0b05b3c17>> + * SignedSource<<56388778b313654f6700e4bfa73599a2>> * @flow * @lightSyntaxTransform * @nogrep @@ -579,7 +598,7 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< User__id$fragmentType, User__id$data, >*/); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_interface_of_all_weak_model_type.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_interface_of_all_weak_model_type.expected index be3111fd5c4e7..b697cbebd4ba3 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_interface_of_all_weak_model_type.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_interface_of_all_weak_model_type.expected @@ -57,7 +57,7 @@ interface IPerson { ==================================== OUTPUT =================================== //- __generated__/Admin____relay_model_instance.graphql.js /** - * SignedSource<<523d097198aa2ffa2a1209e24ac2a337>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -106,14 +106,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< Admin____relay_model_instance$fragmentType, Admin____relay_model_instance$data, >*/); //- __generated__/QueryComponentQuery.graphql.js /** - * SignedSource<<4c12cb9f690c4ba88d309a8e9f747867>> + * SignedSource<<1e557ef442e027c83b6204650e80e65f>> * @flow * @lightSyntaxTransform * @nogrep @@ -142,6 +142,14 @@ export type QueryComponentQuery = {| |}; */ +import {name as adminNameResolver} from 'AdminTypeResolvers'; +import Admin____relay_model_instance_graphql from 'Admin____relay_model_instance.graphql'; +import {person as queryPersonResolver} from 'QueryResolvers'; +import Query__person$normalization_graphql from 'Query__person$normalization.graphql'; +import {name as userNameResolver} from 'UserTypeResolvers'; +import User____relay_model_instance_graphql from 'User____relay_model_instance.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ClientRequest*/ = { "fragment": { "argumentDefinitions": [], @@ -161,13 +169,13 @@ var node/*: ClientRequest*/ = { "fragment": null, "kind": "RelayResolver", "name": "person", - "resolverModule": require('QueryResolvers').person, + "resolverModule": queryPersonResolver, "path": "person", "normalizationInfo": { "kind": "OutputType", "concreteType": null, "plural": false, - "normalizationNode": require('Query__person$normalization.graphql') + "normalizationNode": Query__person$normalization_graphql } }, "linkedField": { @@ -191,7 +199,7 @@ var node/*: ClientRequest*/ = { }, "kind": "RelayResolver", "name": "name", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('Admin____relay_model_instance.graphql'), require('AdminTypeResolvers').name, '__relay_model_instance', true), + "resolverModule": resolverDataInjector(Admin____relay_model_instance_graphql, adminNameResolver, '__relay_model_instance', true), "path": "person.name" } ], @@ -211,7 +219,7 @@ var node/*: ClientRequest*/ = { }, "kind": "RelayResolver", "name": "name", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('User____relay_model_instance.graphql'), require('UserTypeResolvers').name, '__relay_model_instance', true), + "resolverModule": resolverDataInjector(User____relay_model_instance_graphql, userNameResolver, '__relay_model_instance', true), "path": "person.name" } ], @@ -241,7 +249,7 @@ var node/*: ClientRequest*/ = { "storageKey": null, "isOutputType": true, "resolverInfo": { - "resolverFunction": require('QueryResolvers').person, + "resolverFunction": queryPersonResolver, "rootFragment": null } }, @@ -270,7 +278,7 @@ var node/*: ClientRequest*/ = { "storageKey": null, "isOutputType": true, "resolverInfo": { - "resolverFunction": require('AdminTypeResolvers').name, + "resolverFunction": adminNameResolver, "rootFragment": null } } @@ -288,7 +296,7 @@ var node/*: ClientRequest*/ = { "storageKey": null, "isOutputType": true, "resolverInfo": { - "resolverFunction": require('UserTypeResolvers').name, + "resolverFunction": userNameResolver, "rootFragment": null } } @@ -314,14 +322,14 @@ var node/*: ClientRequest*/ = { (node/*: any*/).hash = "cc7b67152b1dce33f04a61bea084084f"; -module.exports = ((node/*: any*/)/*: ClientQuery< +export default ((node/*: any*/)/*: ClientQuery< QueryComponentQuery$variables, QueryComponentQuery$data, >*/); //- __generated__/Query__person$normalization.graphql.js /** - * SignedSource<> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -389,11 +397,11 @@ return { }; })(); -module.exports = node; +export default node; //- __generated__/User____relay_model_instance.graphql.js /** - * SignedSource<<9a188c26688bb46f65ed80df4ae938c3>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -442,7 +450,7 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< User____relay_model_instance$fragmentType, User____relay_model_instance$data, >*/); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_interface_of_live_and_non_live_strong_model_type.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_interface_of_live_and_non_live_strong_model_type.expected index 077c8b5868038..cb6c078a74875 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_interface_of_live_and_non_live_strong_model_type.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_interface_of_live_and_non_live_strong_model_type.expected @@ -56,7 +56,7 @@ interface IPerson { ==================================== OUTPUT =================================== //- __generated__/Admin____relay_model_instance.graphql.js /** - * SignedSource<<7fbe3989595c55397f4bcc5c81ec30b2>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -88,6 +88,10 @@ export type Admin____relay_model_instance$key = { }; */ +import {Admin as adminRelayModelInstanceResolver} from 'AdminTypeResolvers'; +import Admin__id_graphql from 'Admin__id.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -104,7 +108,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "__relay_model_instance", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('Admin__id.graphql'), require('AdminTypeResolvers').Admin, 'id', true), + "resolverModule": resolverDataInjector(Admin__id_graphql, adminRelayModelInstanceResolver, 'id', true), "path": "__relay_model_instance" } ], @@ -112,14 +116,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< Admin____relay_model_instance$fragmentType, Admin____relay_model_instance$data, >*/); //- __generated__/Admin__id.graphql.js /** - * SignedSource<<29acfbf1d6f559b8b77e9cd1f35218c0>> + * SignedSource<<78e97d08242ee5ff28fb2c5bf850cd81>> * @flow * @lightSyntaxTransform * @nogrep @@ -167,14 +171,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< Admin__id$fragmentType, Admin__id$data, >*/); //- __generated__/QueryComponentQuery.graphql.js /** - * SignedSource<<13ee77f43acb40f084c4d45dd85f37d8>> + * SignedSource<<7a365938eef61835fc62a24e5d3b799b>> * @flow * @lightSyntaxTransform * @nogrep @@ -206,6 +210,17 @@ export type QueryComponentQuery = {| |}; */ +import {Admin as adminRelayModelInstanceResolver} from 'AdminTypeResolvers'; +import {name as adminNameResolver} from 'AdminTypeResolvers'; +import Admin____relay_model_instance_graphql from 'Admin____relay_model_instance.graphql'; +import Admin__id_graphql from 'Admin__id.graphql'; +import {person as queryPersonResolver} from 'QueryResolvers'; +import {User as userRelayModelInstanceResolver} from 'UserTypeResolvers'; +import {name as userNameResolver} from 'UserTypeResolvers'; +import User____relay_model_instance_graphql from 'User____relay_model_instance.graphql'; +import User__id_graphql from 'User__id.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ClientRequest*/ = (function(){ var v0 = { "alias": null, @@ -239,8 +254,8 @@ return { "name": "Admin__id" }, "kind": "RelayResolver", - "name": "person", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('Admin__id.graphql'), require('AdminTypeResolvers').Admin, 'id', true), + "name": "__relay_model_instance", + "resolverModule": resolverDataInjector(Admin__id_graphql, adminRelayModelInstanceResolver, 'id', true), "path": "person.__relay_model_instance" }, "User": { @@ -252,8 +267,8 @@ return { "name": "User__id" }, "kind": "RelayLiveResolver", - "name": "person", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('User__id.graphql'), require('UserTypeResolvers').User, 'id', true), + "name": "__relay_model_instance", + "resolverModule": resolverDataInjector(User__id_graphql, userRelayModelInstanceResolver, 'id', true), "path": "person.__relay_model_instance" } }, @@ -263,7 +278,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "person", - "resolverModule": require('QueryResolvers').person, + "resolverModule": queryPersonResolver, "path": "person" }, "linkedField": { @@ -287,7 +302,7 @@ return { }, "kind": "RelayResolver", "name": "name", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('Admin____relay_model_instance.graphql'), require('AdminTypeResolvers').name, '__relay_model_instance', true), + "resolverModule": resolverDataInjector(Admin____relay_model_instance_graphql, adminNameResolver, '__relay_model_instance', true), "path": "person.name" } ], @@ -307,7 +322,7 @@ return { }, "kind": "RelayResolver", "name": "name", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('User____relay_model_instance.graphql'), require('UserTypeResolvers').name, '__relay_model_instance', true), + "resolverModule": resolverDataInjector(User____relay_model_instance_graphql, userNameResolver, '__relay_model_instance', true), "path": "person.name" } ], @@ -441,14 +456,14 @@ return { (node/*: any*/).hash = "cc7b67152b1dce33f04a61bea084084f"; -module.exports = ((node/*: any*/)/*: ClientQuery< +export default ((node/*: any*/)/*: ClientQuery< QueryComponentQuery$variables, QueryComponentQuery$data, >*/); //- __generated__/User____relay_model_instance.graphql.js /** - * SignedSource<<5b1064cbbcdf6ad4c972e722067e79b2>> + * SignedSource<<1d7f7276af9b68a8321f13626bfca7aa>> * @flow * @lightSyntaxTransform * @nogrep @@ -480,6 +495,10 @@ export type User____relay_model_instance$key = { }; */ +import {User as userRelayModelInstanceResolver} from 'UserTypeResolvers'; +import User__id_graphql from 'User__id.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -496,7 +515,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayLiveResolver", "name": "__relay_model_instance", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('User__id.graphql'), require('UserTypeResolvers').User, 'id', true), + "resolverModule": resolverDataInjector(User__id_graphql, userRelayModelInstanceResolver, 'id', true), "path": "__relay_model_instance" } ], @@ -504,14 +523,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< User____relay_model_instance$fragmentType, User____relay_model_instance$data, >*/); //- __generated__/User__id.graphql.js /** - * SignedSource<<0a0f39eb34bfc882d28378a0b05b3c17>> + * SignedSource<<56388778b313654f6700e4bfa73599a2>> * @flow * @lightSyntaxTransform * @nogrep @@ -559,7 +578,7 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< User__id$fragmentType, User__id$data, >*/); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_plural_server_type.invalid.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_plural_server_type.invalid.expected new file mode 100644 index 0000000000000..fe16dfdc119e7 --- /dev/null +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_plural_server_type.invalid.expected @@ -0,0 +1,42 @@ +==================================== INPUT ==================================== +//- PersonComponent.js +graphql`query PersonComponentQuery { + plural_server @waterfall { + name + } +}` + +//- QueryResolvers.js +/** + * @RelayResolver Query.plural_server: [User] + */ + +//- relay.config.json +{ + "language": "flow", + "jsModuleFormat": "haste", + "schema": "schema.graphql" +} + +//- schema.graphql +type Query { + greeting: String + node(id: ID!): Node +} + +interface Node { + id: ID! +} + +type User implements Node { + id: ID! + name: String +} +==================================== OUTPUT =================================== +✖︎ Unexpected Relay Resolver returning plual edge to type defined on the server. Relay Resolvers do not curretly support returning plural edges to server types. As a work around, consider defining a plural edge to a client type which has a singular edge to the server type. + + QueryResolvers.js:2:25 + 1 │ * + 2 │ * @RelayResolver Query.plural_server: [User] + │ ^^^^^^^^^^^^^ + 3 │ diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_plural_server_type.invalid.input b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_plural_server_type.invalid.input new file mode 100644 index 0000000000000..65199a9a5f064 --- /dev/null +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_plural_server_type.invalid.input @@ -0,0 +1,33 @@ +//- PersonComponent.js +graphql`query PersonComponentQuery { + plural_server @waterfall { + name + } +}` + +//- QueryResolvers.js +/** + * @RelayResolver Query.plural_server: [User] + */ + +//- relay.config.json +{ + "language": "flow", + "jsModuleFormat": "haste", + "schema": "schema.graphql" +} + +//- schema.graphql +type Query { + greeting: String + node(id: ID!): Node +} + +interface Node { + id: ID! +} + +type User implements Node { + id: ID! + name: String +} diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_union_of_cse.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_union_of_cse.expected index bb4bff2ad5b06..56dfe64ba798a 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_union_of_cse.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_union_of_cse.expected @@ -57,7 +57,7 @@ union Person = User | Admin ==================================== OUTPUT =================================== //- __generated__/PersonComponentQuery.graphql.js /** - * SignedSource<<5482af7faa6804c7e8a9331a2d410982>> + * SignedSource<<140d4139905c84426daa58015486e9c1>> * @flow * @lightSyntaxTransform * @nogrep @@ -97,6 +97,10 @@ export type PersonComponentQuery = {| |}; */ +import {name as adminNameResolver} from 'AdminTypeResolvers'; +import {person as queryPersonResolver} from 'QueryResolvers'; +import {name as userNameResolver} from 'UserTypeResolvers'; + var node/*: ClientRequest*/ = (function(){ var v0 = { "alias": null, @@ -124,7 +128,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "person", - "resolverModule": require('QueryResolvers').person, + "resolverModule": queryPersonResolver, "path": "person" }, "linkedField": { @@ -144,7 +148,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "name", - "resolverModule": require('UserTypeResolvers').name, + "resolverModule": userNameResolver, "path": "person.name" } ], @@ -160,7 +164,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "name", - "resolverModule": require('AdminTypeResolvers').name, + "resolverModule": adminNameResolver, "path": "person.name" } ], @@ -190,7 +194,7 @@ return { "storageKey": null, "isOutputType": false, "resolverInfo": { - "resolverFunction": require('QueryResolvers').person, + "resolverFunction": queryPersonResolver, "rootFragment": null } }, @@ -219,7 +223,7 @@ return { "storageKey": null, "isOutputType": true, "resolverInfo": { - "resolverFunction": require('UserTypeResolvers').name, + "resolverFunction": userNameResolver, "rootFragment": null } }, @@ -238,7 +242,7 @@ return { "storageKey": null, "isOutputType": true, "resolverInfo": { - "resolverFunction": require('AdminTypeResolvers').name, + "resolverFunction": adminNameResolver, "rootFragment": null } }, @@ -266,7 +270,7 @@ return { (node/*: any*/).hash = "6c9de2c954253987b712fc422952bea1"; -module.exports = ((node/*: any*/)/*: ClientQuery< +export default ((node/*: any*/)/*: ClientQuery< PersonComponentQuery$variables, PersonComponentQuery$data, >*/); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_union_of_strong_resolver.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_union_of_strong_resolver.expected index 5257d7382bf8c..389537fb99389 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_union_of_strong_resolver.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_union_of_strong_resolver.expected @@ -58,7 +58,7 @@ union Person = User | Admin ==================================== OUTPUT =================================== //- __generated__/Admin____relay_model_instance.graphql.js /** - * SignedSource<<7fbe3989595c55397f4bcc5c81ec30b2>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -90,6 +90,10 @@ export type Admin____relay_model_instance$key = { }; */ +import {Admin as adminRelayModelInstanceResolver} from 'AdminTypeResolvers'; +import Admin__id_graphql from 'Admin__id.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -106,7 +110,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "__relay_model_instance", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('Admin__id.graphql'), require('AdminTypeResolvers').Admin, 'id', true), + "resolverModule": resolverDataInjector(Admin__id_graphql, adminRelayModelInstanceResolver, 'id', true), "path": "__relay_model_instance" } ], @@ -114,14 +118,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< Admin____relay_model_instance$fragmentType, Admin____relay_model_instance$data, >*/); //- __generated__/Admin__id.graphql.js /** - * SignedSource<<29acfbf1d6f559b8b77e9cd1f35218c0>> + * SignedSource<<78e97d08242ee5ff28fb2c5bf850cd81>> * @flow * @lightSyntaxTransform * @nogrep @@ -169,14 +173,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< Admin__id$fragmentType, Admin__id$data, >*/); //- __generated__/PersonComponentQuery.graphql.js /** - * SignedSource<> + * SignedSource<<477be90f6daa26fda63c82e18f24e7e6>> * @flow * @lightSyntaxTransform * @nogrep @@ -222,6 +226,17 @@ export type PersonComponentQuery = {| |}; */ +import {Admin as adminRelayModelInstanceResolver} from 'AdminTypeResolvers'; +import {name as adminNameResolver} from 'AdminTypeResolvers'; +import Admin____relay_model_instance_graphql from 'Admin____relay_model_instance.graphql'; +import Admin__id_graphql from 'Admin__id.graphql'; +import {person as queryPersonResolver} from 'QueryResolvers'; +import {User as userRelayModelInstanceResolver} from 'UserTypeResolvers'; +import {name as userNameResolver} from 'UserTypeResolvers'; +import User____relay_model_instance_graphql from 'User____relay_model_instance.graphql'; +import User__id_graphql from 'User__id.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ClientRequest*/ = (function(){ var v0 = { "alias": null, @@ -255,8 +270,8 @@ return { "name": "Admin__id" }, "kind": "RelayResolver", - "name": "person", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('Admin__id.graphql'), require('AdminTypeResolvers').Admin, 'id', true), + "name": "__relay_model_instance", + "resolverModule": resolverDataInjector(Admin__id_graphql, adminRelayModelInstanceResolver, 'id', true), "path": "person.__relay_model_instance" }, "User": { @@ -268,8 +283,8 @@ return { "name": "User__id" }, "kind": "RelayResolver", - "name": "person", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('User__id.graphql'), require('UserTypeResolvers').User, 'id', true), + "name": "__relay_model_instance", + "resolverModule": resolverDataInjector(User__id_graphql, userRelayModelInstanceResolver, 'id', true), "path": "person.__relay_model_instance" } }, @@ -279,7 +294,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "person", - "resolverModule": require('QueryResolvers').person, + "resolverModule": queryPersonResolver, "path": "person" }, "linkedField": { @@ -303,7 +318,7 @@ return { }, "kind": "RelayResolver", "name": "name", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('User____relay_model_instance.graphql'), require('UserTypeResolvers').name, '__relay_model_instance', true), + "resolverModule": resolverDataInjector(User____relay_model_instance_graphql, userNameResolver, '__relay_model_instance', true), "path": "person.name" } ], @@ -323,7 +338,7 @@ return { }, "kind": "RelayResolver", "name": "name", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('Admin____relay_model_instance.graphql'), require('AdminTypeResolvers').name, '__relay_model_instance', true), + "resolverModule": resolverDataInjector(Admin____relay_model_instance_graphql, adminNameResolver, '__relay_model_instance', true), "path": "person.name" } ], @@ -458,14 +473,14 @@ return { (node/*: any*/).hash = "6c9de2c954253987b712fc422952bea1"; -module.exports = ((node/*: any*/)/*: ClientQuery< +export default ((node/*: any*/)/*: ClientQuery< PersonComponentQuery$variables, PersonComponentQuery$data, >*/); //- __generated__/User____relay_model_instance.graphql.js /** - * SignedSource<<7ffabc2a97c3589cbfd20a23b3b608ca>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -497,6 +512,10 @@ export type User____relay_model_instance$key = { }; */ +import {User as userRelayModelInstanceResolver} from 'UserTypeResolvers'; +import User__id_graphql from 'User__id.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -513,7 +532,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "__relay_model_instance", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('User__id.graphql'), require('UserTypeResolvers').User, 'id', true), + "resolverModule": resolverDataInjector(User__id_graphql, userRelayModelInstanceResolver, 'id', true), "path": "__relay_model_instance" } ], @@ -521,14 +540,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< User____relay_model_instance$fragmentType, User____relay_model_instance$data, >*/); //- __generated__/User__id.graphql.js /** - * SignedSource<<0a0f39eb34bfc882d28378a0b05b3c17>> + * SignedSource<<56388778b313654f6700e4bfa73599a2>> * @flow * @lightSyntaxTransform * @nogrep @@ -576,7 +595,7 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< User__id$fragmentType, User__id$data, >*/); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_weak_client_schema_type.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_weak_client_schema_type.expected new file mode 100644 index 0000000000000..469ca1d58b561 --- /dev/null +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_weak_client_schema_type.expected @@ -0,0 +1,39 @@ +==================================== INPUT ==================================== +//- PersonComponent.js +graphql`query PersonComponentQuery { + some_resolver_field { + __typename + } +}` + +//- Resolvers.js +/** + * @RelayResolver Query.some_resolver_field: SomeType + */ + +//- relay.config.json +{ + "language": "typescript", + "schema": "schema.graphql", + "schemaExtensions": [ + "schema-extensions" + ] +} + +//- schema.graphql +type Query { + some_field: Boolean +} + +//- schema-extensions/extension.graphql +type SomeType { + name: String +} +==================================== OUTPUT =================================== +✖︎ Relay Resolvers that return weak types defined in client schema extensions are not supported. Prefer defining the return type using a `@weak` Relay Resolver type: https://relay.dev/docs/next/guides/relay-resolvers/defining-types/#defining-a-weak-type + + Resolvers.js:2:25 + 1 │ * + 2 │ * @RelayResolver Query.some_resolver_field: SomeType + │ ^^^^^^^^^^^^^^^^^^^ + 3 │ diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_weak_client_schema_type.input b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_weak_client_schema_type.input new file mode 100644 index 0000000000000..bb65b2ee3cd63 --- /dev/null +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_returns_weak_client_schema_type.input @@ -0,0 +1,30 @@ +//- PersonComponent.js +graphql`query PersonComponentQuery { + some_resolver_field { + __typename + } +}` + +//- Resolvers.js +/** + * @RelayResolver Query.some_resolver_field: SomeType + */ + +//- relay.config.json +{ + "language": "typescript", + "schema": "schema.graphql", + "schemaExtensions": [ + "schema-extensions" + ] +} + +//- schema.graphql +type Query { + some_field: Boolean +} + +//- schema-extensions/extension.graphql +type SomeType { + name: String +} diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_semantic_non_null_custom_scalar.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_semantic_non_null_custom_scalar.expected index 91439b72b641b..3612f8ea92122 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_semantic_non_null_custom_scalar.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_semantic_non_null_custom_scalar.expected @@ -66,7 +66,7 @@ type User { name: String } ==================================== OUTPUT =================================== //- __generated__/ResolversSchemaModule.js /** - * SignedSource<<983b85d6af01334176c1f903eecd63de>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -82,36 +82,43 @@ import type { ResolverFunction, NormalizationSplitOperation } from 'relay-runtim */ +import UserFooFragment$normalization from 'UserFooFragment$normalization.graphql'; +import {bar as userBarResolver} from 'User_bar'; +import {bar_live as userBarLiveResolver} from 'User_bar'; +import {bar_live_plural as userBarLivePluralResolver} from 'User_bar'; +import {bar_plural as userBarPluralResolver} from 'User_bar'; +import {foo as userFooResolver} from 'User_foo'; + var schema_resolvers/*: SchemaResolvers*/ = { "User": { "bar": { - "resolverFunction": require('User_bar').bar, + "resolverFunction": userBarResolver, "rootFragment": null }, "bar_live": { - "resolverFunction": require('User_bar').bar_live, + "resolverFunction": userBarLiveResolver, "rootFragment": null }, "bar_live_plural": { - "resolverFunction": require('User_bar').bar_live_plural, + "resolverFunction": userBarLivePluralResolver, "rootFragment": null }, "bar_plural": { - "resolverFunction": require('User_bar').bar_plural, + "resolverFunction": userBarPluralResolver, "rootFragment": null }, "foo": { - "resolverFunction": require('User_foo').foo, - "rootFragment": require('UserFooFragment$normalization.graphql') + "resolverFunction": userFooResolver, + "rootFragment": UserFooFragment$normalization } } }; -module.exports = schema_resolvers; +export default schema_resolvers; //- __generated__/UserFooFragment$normalization.graphql.js /** - * SignedSource<<9720117be72c47f602a12640a544c268>> + * SignedSource<<2ff85acb606b5f7522f512b09e7b74b0>> * @flow * @lightSyntaxTransform * @nogrep @@ -126,6 +133,11 @@ import type { NormalizationSplitOperation } from 'relay-runtime'; */ +import {bar as userBarResolver} from 'User_bar'; +import {bar_live as userBarLiveResolver} from 'User_bar'; +import {bar_live_plural as userBarLivePluralResolver} from 'User_bar'; +import {bar_plural as userBarPluralResolver} from 'User_bar'; + var node/*: NormalizationSplitOperation*/ = { "kind": "SplitOperation", "metadata": {}, @@ -141,7 +153,7 @@ var node/*: NormalizationSplitOperation*/ = { "storageKey": null, "isOutputType": true, "resolverInfo": { - "resolverFunction": require('User_bar').bar, + "resolverFunction": userBarResolver, "rootFragment": null } }, @@ -152,7 +164,7 @@ var node/*: NormalizationSplitOperation*/ = { "storageKey": null, "isOutputType": true, "resolverInfo": { - "resolverFunction": require('User_bar').bar_live, + "resolverFunction": userBarLiveResolver, "rootFragment": null } }, @@ -163,7 +175,7 @@ var node/*: NormalizationSplitOperation*/ = { "storageKey": null, "isOutputType": true, "resolverInfo": { - "resolverFunction": require('User_bar').bar_plural, + "resolverFunction": userBarPluralResolver, "rootFragment": null } }, @@ -174,7 +186,7 @@ var node/*: NormalizationSplitOperation*/ = { "storageKey": null, "isOutputType": true, "resolverInfo": { - "resolverFunction": require('User_bar').bar_live_plural, + "resolverFunction": userBarLivePluralResolver, "rootFragment": null } } @@ -185,11 +197,11 @@ var node/*: NormalizationSplitOperation*/ = { (node/*: any*/).hash = "85fc07b6873f5892801afc1cb3e30ea1"; -module.exports = node; +export default node; //- __generated__/UserFooFragment.graphql.js /** - * SignedSource<<46fde794f6313b1e7b8d607cfe8a711d>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -234,6 +246,11 @@ export type UserFooFragment$key = { }; */ +import {bar as userBarResolver} from 'User_bar'; +import {bar_live as userBarLiveResolver} from 'User_bar'; +import {bar_live_plural as userBarLivePluralResolver} from 'User_bar'; +import {bar_plural as userBarPluralResolver} from 'User_bar'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -251,7 +268,7 @@ var node/*: ReaderFragment*/ = { "fragment": null, "kind": "RelayResolver", "name": "bar", - "resolverModule": require('User_bar').bar, + "resolverModule": userBarResolver, "path": "bar" }, { @@ -260,7 +277,7 @@ var node/*: ReaderFragment*/ = { "fragment": null, "kind": "RelayLiveResolver", "name": "bar_live", - "resolverModule": require('User_bar').bar_live, + "resolverModule": userBarLiveResolver, "path": "bar_live" }, { @@ -269,7 +286,7 @@ var node/*: ReaderFragment*/ = { "fragment": null, "kind": "RelayResolver", "name": "bar_plural", - "resolverModule": require('User_bar').bar_plural, + "resolverModule": userBarPluralResolver, "path": "bar_plural" }, { @@ -278,7 +295,7 @@ var node/*: ReaderFragment*/ = { "fragment": null, "kind": "RelayLiveResolver", "name": "bar_live_plural", - "resolverModule": require('User_bar').bar_live_plural, + "resolverModule": userBarLivePluralResolver, "path": "bar_live_plural" } ] @@ -290,7 +307,7 @@ var node/*: ReaderFragment*/ = { (node/*: any*/).hash = "85fc07b6873f5892801afc1cb3e30ea1"; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< UserFooFragment$fragmentType, UserFooFragment$data, >*/); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_semantic_non_null_live.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_semantic_non_null_live.expected index 3644d33e2f0f7..d21412b3c923c 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_semantic_non_null_live.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_semantic_non_null_live.expected @@ -36,7 +36,7 @@ type User { name: String } ==================================== OUTPUT =================================== //- __generated__/ResolversSchemaModule.js /** - * SignedSource<> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -52,24 +52,28 @@ import type { ResolverFunction, NormalizationSplitOperation } from 'relay-runtim */ +import UserFooFragment$normalization from 'UserFooFragment$normalization.graphql'; +import {bar as userBarResolver} from 'User_bar'; +import {foo as userFooResolver} from 'User_foo'; + var schema_resolvers/*: SchemaResolvers*/ = { "User": { "bar": { - "resolverFunction": require('User_bar').bar, + "resolverFunction": userBarResolver, "rootFragment": null }, "foo": { - "resolverFunction": require('User_foo').foo, - "rootFragment": require('UserFooFragment$normalization.graphql') + "resolverFunction": userFooResolver, + "rootFragment": UserFooFragment$normalization } } }; -module.exports = schema_resolvers; +export default schema_resolvers; //- __generated__/UserFooFragment$normalization.graphql.js /** - * SignedSource<<4dcc21fbaeaa2b274cc36b53f0b0f77a>> + * SignedSource<<541a461c62bf43d8ebed22633ed4faa5>> * @flow * @lightSyntaxTransform * @nogrep @@ -84,6 +88,8 @@ import type { NormalizationSplitOperation } from 'relay-runtime'; */ +import {bar as userBarResolver} from 'User_bar'; + var node/*: NormalizationSplitOperation*/ = { "kind": "SplitOperation", "metadata": {}, @@ -99,7 +105,7 @@ var node/*: NormalizationSplitOperation*/ = { "storageKey": null, "isOutputType": true, "resolverInfo": { - "resolverFunction": require('User_bar').bar, + "resolverFunction": userBarResolver, "rootFragment": null } } @@ -110,11 +116,11 @@ var node/*: NormalizationSplitOperation*/ = { (node/*: any*/).hash = "c571977071bef677ed9b7926d2dad022"; -module.exports = node; +export default node; //- __generated__/UserFooFragment.graphql.js /** - * SignedSource<> + * SignedSource<<6e1fa2a8a1af2105085fd039e387d453>> * @flow * @lightSyntaxTransform * @nogrep @@ -143,6 +149,8 @@ export type UserFooFragment$key = { }; */ +import {bar as userBarResolver} from 'User_bar'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -160,7 +168,7 @@ var node/*: ReaderFragment*/ = { "fragment": null, "kind": "RelayLiveResolver", "name": "bar", - "resolverModule": require('User_bar').bar, + "resolverModule": userBarResolver, "path": "bar" } ] @@ -172,7 +180,7 @@ var node/*: ReaderFragment*/ = { (node/*: any*/).hash = "c571977071bef677ed9b7926d2dad022"; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< UserFooFragment$fragmentType, UserFooFragment$data, >*/); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_semantic_non_null_plural.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_semantic_non_null_plural.expected index 0516535649ef1..ee8d1aa036030 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_semantic_non_null_plural.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_semantic_non_null_plural.expected @@ -35,7 +35,7 @@ type User { name: String } ==================================== OUTPUT =================================== //- __generated__/ResolversSchemaModule.js /** - * SignedSource<> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -51,24 +51,28 @@ import type { ResolverFunction, NormalizationSplitOperation } from 'relay-runtim */ +import UserFooFragment$normalization from 'UserFooFragment$normalization.graphql'; +import {bar as userBarResolver} from 'User_bar'; +import {foo as userFooResolver} from 'User_foo'; + var schema_resolvers/*: SchemaResolvers*/ = { "User": { "bar": { - "resolverFunction": require('User_bar').bar, + "resolverFunction": userBarResolver, "rootFragment": null }, "foo": { - "resolverFunction": require('User_foo').foo, - "rootFragment": require('UserFooFragment$normalization.graphql') + "resolverFunction": userFooResolver, + "rootFragment": UserFooFragment$normalization } } }; -module.exports = schema_resolvers; +export default schema_resolvers; //- __generated__/UserFooFragment$normalization.graphql.js /** - * SignedSource<> + * SignedSource<<55a6356e918a5748630210a3c7203b52>> * @flow * @lightSyntaxTransform * @nogrep @@ -83,6 +87,8 @@ import type { NormalizationSplitOperation } from 'relay-runtime'; */ +import {bar as userBarResolver} from 'User_bar'; + var node/*: NormalizationSplitOperation*/ = { "kind": "SplitOperation", "metadata": {}, @@ -98,7 +104,7 @@ var node/*: NormalizationSplitOperation*/ = { "storageKey": null, "isOutputType": true, "resolverInfo": { - "resolverFunction": require('User_bar').bar, + "resolverFunction": userBarResolver, "rootFragment": null } } @@ -109,11 +115,11 @@ var node/*: NormalizationSplitOperation*/ = { (node/*: any*/).hash = "c571977071bef677ed9b7926d2dad022"; -module.exports = node; +export default node; //- __generated__/UserFooFragment.graphql.js /** - * SignedSource<> + * SignedSource<<98c012b16ac8accaf2aef828243ec830>> * @flow * @lightSyntaxTransform * @nogrep @@ -142,6 +148,8 @@ export type UserFooFragment$key = { }; */ +import {bar as userBarResolver} from 'User_bar'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -159,7 +167,7 @@ var node/*: ReaderFragment*/ = { "fragment": null, "kind": "RelayResolver", "name": "bar", - "resolverModule": require('User_bar').bar, + "resolverModule": userBarResolver, "path": "bar" } ] @@ -171,7 +179,7 @@ var node/*: ReaderFragment*/ = { (node/*: any*/).hash = "c571977071bef677ed9b7926d2dad022"; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< UserFooFragment$fragmentType, UserFooFragment$data, >*/); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_semantic_non_null_plural_live.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_semantic_non_null_plural_live.expected index 1829e619fa764..20f9f9d036db4 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_semantic_non_null_plural_live.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_semantic_non_null_plural_live.expected @@ -36,7 +36,7 @@ type User { name: String } ==================================== OUTPUT =================================== //- __generated__/ResolversSchemaModule.js /** - * SignedSource<> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -52,24 +52,28 @@ import type { ResolverFunction, NormalizationSplitOperation } from 'relay-runtim */ +import UserFooFragment$normalization from 'UserFooFragment$normalization.graphql'; +import {bar as userBarResolver} from 'User_bar'; +import {foo as userFooResolver} from 'User_foo'; + var schema_resolvers/*: SchemaResolvers*/ = { "User": { "bar": { - "resolverFunction": require('User_bar').bar, + "resolverFunction": userBarResolver, "rootFragment": null }, "foo": { - "resolverFunction": require('User_foo').foo, - "rootFragment": require('UserFooFragment$normalization.graphql') + "resolverFunction": userFooResolver, + "rootFragment": UserFooFragment$normalization } } }; -module.exports = schema_resolvers; +export default schema_resolvers; //- __generated__/UserFooFragment$normalization.graphql.js /** - * SignedSource<<4dcc21fbaeaa2b274cc36b53f0b0f77a>> + * SignedSource<<541a461c62bf43d8ebed22633ed4faa5>> * @flow * @lightSyntaxTransform * @nogrep @@ -84,6 +88,8 @@ import type { NormalizationSplitOperation } from 'relay-runtime'; */ +import {bar as userBarResolver} from 'User_bar'; + var node/*: NormalizationSplitOperation*/ = { "kind": "SplitOperation", "metadata": {}, @@ -99,7 +105,7 @@ var node/*: NormalizationSplitOperation*/ = { "storageKey": null, "isOutputType": true, "resolverInfo": { - "resolverFunction": require('User_bar').bar, + "resolverFunction": userBarResolver, "rootFragment": null } } @@ -110,11 +116,11 @@ var node/*: NormalizationSplitOperation*/ = { (node/*: any*/).hash = "c571977071bef677ed9b7926d2dad022"; -module.exports = node; +export default node; //- __generated__/UserFooFragment.graphql.js /** - * SignedSource<<07549999ce219b691cd421ba43c8497a>> + * SignedSource<<72a5ba9899acd0ebef6422e1c255bd1c>> * @flow * @lightSyntaxTransform * @nogrep @@ -143,6 +149,8 @@ export type UserFooFragment$key = { }; */ +import {bar as userBarResolver} from 'User_bar'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -160,7 +168,7 @@ var node/*: ReaderFragment*/ = { "fragment": null, "kind": "RelayLiveResolver", "name": "bar", - "resolverModule": require('User_bar').bar, + "resolverModule": userBarResolver, "path": "bar" } ] @@ -172,7 +180,7 @@ var node/*: ReaderFragment*/ = { (node/*: any*/).hash = "c571977071bef677ed9b7926d2dad022"; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< UserFooFragment$fragmentType, UserFooFragment$data, >*/); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_semantic_non_null_relayresolvervalue.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_semantic_non_null_relayresolvervalue.expected index cd04b70ca2556..e157f9f117a7c 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_semantic_non_null_relayresolvervalue.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_semantic_non_null_relayresolvervalue.expected @@ -53,7 +53,7 @@ type User { name: String } ==================================== OUTPUT =================================== //- __generated__/ResolversSchemaModule.js /** - * SignedSource<<983b85d6af01334176c1f903eecd63de>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -69,36 +69,43 @@ import type { ResolverFunction, NormalizationSplitOperation } from 'relay-runtim */ +import UserFooFragment$normalization from 'UserFooFragment$normalization.graphql'; +import {bar as userBarResolver} from 'User_bar'; +import {bar_live as userBarLiveResolver} from 'User_bar'; +import {bar_live_plural as userBarLivePluralResolver} from 'User_bar'; +import {bar_plural as userBarPluralResolver} from 'User_bar'; +import {foo as userFooResolver} from 'User_foo'; + var schema_resolvers/*: SchemaResolvers*/ = { "User": { "bar": { - "resolverFunction": require('User_bar').bar, + "resolverFunction": userBarResolver, "rootFragment": null }, "bar_live": { - "resolverFunction": require('User_bar').bar_live, + "resolverFunction": userBarLiveResolver, "rootFragment": null }, "bar_live_plural": { - "resolverFunction": require('User_bar').bar_live_plural, + "resolverFunction": userBarLivePluralResolver, "rootFragment": null }, "bar_plural": { - "resolverFunction": require('User_bar').bar_plural, + "resolverFunction": userBarPluralResolver, "rootFragment": null }, "foo": { - "resolverFunction": require('User_foo').foo, - "rootFragment": require('UserFooFragment$normalization.graphql') + "resolverFunction": userFooResolver, + "rootFragment": UserFooFragment$normalization } } }; -module.exports = schema_resolvers; +export default schema_resolvers; //- __generated__/UserFooFragment$normalization.graphql.js /** - * SignedSource<<9720117be72c47f602a12640a544c268>> + * SignedSource<<2ff85acb606b5f7522f512b09e7b74b0>> * @flow * @lightSyntaxTransform * @nogrep @@ -113,6 +120,11 @@ import type { NormalizationSplitOperation } from 'relay-runtime'; */ +import {bar as userBarResolver} from 'User_bar'; +import {bar_live as userBarLiveResolver} from 'User_bar'; +import {bar_live_plural as userBarLivePluralResolver} from 'User_bar'; +import {bar_plural as userBarPluralResolver} from 'User_bar'; + var node/*: NormalizationSplitOperation*/ = { "kind": "SplitOperation", "metadata": {}, @@ -128,7 +140,7 @@ var node/*: NormalizationSplitOperation*/ = { "storageKey": null, "isOutputType": true, "resolverInfo": { - "resolverFunction": require('User_bar').bar, + "resolverFunction": userBarResolver, "rootFragment": null } }, @@ -139,7 +151,7 @@ var node/*: NormalizationSplitOperation*/ = { "storageKey": null, "isOutputType": true, "resolverInfo": { - "resolverFunction": require('User_bar').bar_live, + "resolverFunction": userBarLiveResolver, "rootFragment": null } }, @@ -150,7 +162,7 @@ var node/*: NormalizationSplitOperation*/ = { "storageKey": null, "isOutputType": true, "resolverInfo": { - "resolverFunction": require('User_bar').bar_plural, + "resolverFunction": userBarPluralResolver, "rootFragment": null } }, @@ -161,7 +173,7 @@ var node/*: NormalizationSplitOperation*/ = { "storageKey": null, "isOutputType": true, "resolverInfo": { - "resolverFunction": require('User_bar').bar_live_plural, + "resolverFunction": userBarLivePluralResolver, "rootFragment": null } } @@ -172,11 +184,11 @@ var node/*: NormalizationSplitOperation*/ = { (node/*: any*/).hash = "85fc07b6873f5892801afc1cb3e30ea1"; -module.exports = node; +export default node; //- __generated__/UserFooFragment.graphql.js /** - * SignedSource<> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -220,6 +232,11 @@ export type UserFooFragment$key = { }; */ +import {bar as userBarResolver} from 'User_bar'; +import {bar_live as userBarLiveResolver} from 'User_bar'; +import {bar_live_plural as userBarLivePluralResolver} from 'User_bar'; +import {bar_plural as userBarPluralResolver} from 'User_bar'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -237,7 +254,7 @@ var node/*: ReaderFragment*/ = { "fragment": null, "kind": "RelayResolver", "name": "bar", - "resolverModule": require('User_bar').bar, + "resolverModule": userBarResolver, "path": "bar" }, { @@ -246,7 +263,7 @@ var node/*: ReaderFragment*/ = { "fragment": null, "kind": "RelayLiveResolver", "name": "bar_live", - "resolverModule": require('User_bar').bar_live, + "resolverModule": userBarLiveResolver, "path": "bar_live" }, { @@ -255,7 +272,7 @@ var node/*: ReaderFragment*/ = { "fragment": null, "kind": "RelayResolver", "name": "bar_plural", - "resolverModule": require('User_bar').bar_plural, + "resolverModule": userBarPluralResolver, "path": "bar_plural" }, { @@ -264,7 +281,7 @@ var node/*: ReaderFragment*/ = { "fragment": null, "kind": "RelayLiveResolver", "name": "bar_live_plural", - "resolverModule": require('User_bar').bar_live_plural, + "resolverModule": userBarLivePluralResolver, "path": "bar_live_plural" } ] @@ -276,7 +293,7 @@ var node/*: ReaderFragment*/ = { (node/*: any*/).hash = "85fc07b6873f5892801afc1cb3e30ea1"; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< UserFooFragment$fragmentType, UserFooFragment$data, >*/); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_semantic_non_null_relayresolvervalue_disabled.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_semantic_non_null_relayresolvervalue_disabled.expected index 6aa048480f9f5..0c9e46211ffb3 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_semantic_non_null_relayresolvervalue_disabled.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_semantic_non_null_relayresolvervalue_disabled.expected @@ -53,7 +53,7 @@ type User { name: String } ==================================== OUTPUT =================================== //- __generated__/ResolversSchemaModule.js /** - * SignedSource<<983b85d6af01334176c1f903eecd63de>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -69,36 +69,43 @@ import type { ResolverFunction, NormalizationSplitOperation } from 'relay-runtim */ +import UserFooFragment$normalization from 'UserFooFragment$normalization.graphql'; +import {bar as userBarResolver} from 'User_bar'; +import {bar_live as userBarLiveResolver} from 'User_bar'; +import {bar_live_plural as userBarLivePluralResolver} from 'User_bar'; +import {bar_plural as userBarPluralResolver} from 'User_bar'; +import {foo as userFooResolver} from 'User_foo'; + var schema_resolvers/*: SchemaResolvers*/ = { "User": { "bar": { - "resolverFunction": require('User_bar').bar, + "resolverFunction": userBarResolver, "rootFragment": null }, "bar_live": { - "resolverFunction": require('User_bar').bar_live, + "resolverFunction": userBarLiveResolver, "rootFragment": null }, "bar_live_plural": { - "resolverFunction": require('User_bar').bar_live_plural, + "resolverFunction": userBarLivePluralResolver, "rootFragment": null }, "bar_plural": { - "resolverFunction": require('User_bar').bar_plural, + "resolverFunction": userBarPluralResolver, "rootFragment": null }, "foo": { - "resolverFunction": require('User_foo').foo, - "rootFragment": require('UserFooFragment$normalization.graphql') + "resolverFunction": userFooResolver, + "rootFragment": UserFooFragment$normalization } } }; -module.exports = schema_resolvers; +export default schema_resolvers; //- __generated__/UserFooFragment$normalization.graphql.js /** - * SignedSource<> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -113,6 +120,11 @@ import type { NormalizationSplitOperation } from 'relay-runtime'; */ +import {bar as userBarResolver} from 'User_bar'; +import {bar_live as userBarLiveResolver} from 'User_bar'; +import {bar_live_plural as userBarLivePluralResolver} from 'User_bar'; +import {bar_plural as userBarPluralResolver} from 'User_bar'; + var node/*: NormalizationSplitOperation*/ = { "kind": "SplitOperation", "metadata": {}, @@ -128,7 +140,7 @@ var node/*: NormalizationSplitOperation*/ = { "storageKey": null, "isOutputType": true, "resolverInfo": { - "resolverFunction": require('User_bar').bar, + "resolverFunction": userBarResolver, "rootFragment": null } }, @@ -139,7 +151,7 @@ var node/*: NormalizationSplitOperation*/ = { "storageKey": null, "isOutputType": true, "resolverInfo": { - "resolverFunction": require('User_bar').bar_live, + "resolverFunction": userBarLiveResolver, "rootFragment": null } }, @@ -150,7 +162,7 @@ var node/*: NormalizationSplitOperation*/ = { "storageKey": null, "isOutputType": true, "resolverInfo": { - "resolverFunction": require('User_bar').bar_plural, + "resolverFunction": userBarPluralResolver, "rootFragment": null } }, @@ -161,7 +173,7 @@ var node/*: NormalizationSplitOperation*/ = { "storageKey": null, "isOutputType": true, "resolverInfo": { - "resolverFunction": require('User_bar').bar_live_plural, + "resolverFunction": userBarLivePluralResolver, "rootFragment": null } } @@ -172,11 +184,11 @@ var node/*: NormalizationSplitOperation*/ = { (node/*: any*/).hash = "64a6f93fd7f3b03cced910bd568d74f0"; -module.exports = node; +export default node; //- __generated__/UserFooFragment.graphql.js /** - * SignedSource<<4f80ed9d7cde6b607f5e3173957a4d1d>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -220,6 +232,11 @@ export type UserFooFragment$key = { }; */ +import {bar as userBarResolver} from 'User_bar'; +import {bar_live as userBarLiveResolver} from 'User_bar'; +import {bar_live_plural as userBarLivePluralResolver} from 'User_bar'; +import {bar_plural as userBarPluralResolver} from 'User_bar'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -235,7 +252,7 @@ var node/*: ReaderFragment*/ = { "fragment": null, "kind": "RelayResolver", "name": "bar", - "resolverModule": require('User_bar').bar, + "resolverModule": userBarResolver, "path": "bar" }, { @@ -244,7 +261,7 @@ var node/*: ReaderFragment*/ = { "fragment": null, "kind": "RelayLiveResolver", "name": "bar_live", - "resolverModule": require('User_bar').bar_live, + "resolverModule": userBarLiveResolver, "path": "bar_live" }, { @@ -253,7 +270,7 @@ var node/*: ReaderFragment*/ = { "fragment": null, "kind": "RelayResolver", "name": "bar_plural", - "resolverModule": require('User_bar').bar_plural, + "resolverModule": userBarPluralResolver, "path": "bar_plural" }, { @@ -262,7 +279,7 @@ var node/*: ReaderFragment*/ = { "fragment": null, "kind": "RelayLiveResolver", "name": "bar_live_plural", - "resolverModule": require('User_bar').bar_live_plural, + "resolverModule": userBarLivePluralResolver, "path": "bar_live_plural" } ] @@ -274,7 +291,7 @@ var node/*: ReaderFragment*/ = { (node/*: any*/).hash = "64a6f93fd7f3b03cced910bd568d74f0"; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< UserFooFragment$fragmentType, UserFooFragment$data, >*/); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_semantic_non_null_scalar.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_semantic_non_null_scalar.expected index 44ae876d5d657..22c2ce15dfc72 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_semantic_non_null_scalar.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolver_semantic_non_null_scalar.expected @@ -35,7 +35,7 @@ type User { name: String } ==================================== OUTPUT =================================== //- __generated__/ResolversSchemaModule.js /** - * SignedSource<> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -51,24 +51,28 @@ import type { ResolverFunction, NormalizationSplitOperation } from 'relay-runtim */ +import UserFooFragment$normalization from 'UserFooFragment$normalization.graphql'; +import {bar as userBarResolver} from 'User_bar'; +import {foo as userFooResolver} from 'User_foo'; + var schema_resolvers/*: SchemaResolvers*/ = { "User": { "bar": { - "resolverFunction": require('User_bar').bar, + "resolverFunction": userBarResolver, "rootFragment": null }, "foo": { - "resolverFunction": require('User_foo').foo, - "rootFragment": require('UserFooFragment$normalization.graphql') + "resolverFunction": userFooResolver, + "rootFragment": UserFooFragment$normalization } } }; -module.exports = schema_resolvers; +export default schema_resolvers; //- __generated__/UserFooFragment$normalization.graphql.js /** - * SignedSource<> + * SignedSource<<55a6356e918a5748630210a3c7203b52>> * @flow * @lightSyntaxTransform * @nogrep @@ -83,6 +87,8 @@ import type { NormalizationSplitOperation } from 'relay-runtime'; */ +import {bar as userBarResolver} from 'User_bar'; + var node/*: NormalizationSplitOperation*/ = { "kind": "SplitOperation", "metadata": {}, @@ -98,7 +104,7 @@ var node/*: NormalizationSplitOperation*/ = { "storageKey": null, "isOutputType": true, "resolverInfo": { - "resolverFunction": require('User_bar').bar, + "resolverFunction": userBarResolver, "rootFragment": null } } @@ -109,11 +115,11 @@ var node/*: NormalizationSplitOperation*/ = { (node/*: any*/).hash = "c571977071bef677ed9b7926d2dad022"; -module.exports = node; +export default node; //- __generated__/UserFooFragment.graphql.js /** - * SignedSource<> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -142,6 +148,8 @@ export type UserFooFragment$key = { }; */ +import {bar as userBarResolver} from 'User_bar'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -159,7 +167,7 @@ var node/*: ReaderFragment*/ = { "fragment": null, "kind": "RelayResolver", "name": "bar", - "resolverModule": require('User_bar').bar, + "resolverModule": userBarResolver, "path": "bar" } ] @@ -171,7 +179,7 @@ var node/*: ReaderFragment*/ = { (node/*: any*/).hash = "c571977071bef677ed9b7926d2dad022"; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< UserFooFragment$fragmentType, UserFooFragment$data, >*/); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolvers_non_nullable.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolvers_non_nullable.expected index a5393a5580343..1269dd13c6b5a 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolvers_non_nullable.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolvers_non_nullable.expected @@ -33,7 +33,7 @@ type User { name: String } ==================================== OUTPUT =================================== //- __generated__/ResolversSchemaModule.js /** - * SignedSource<> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -49,24 +49,28 @@ import type { ResolverFunction, NormalizationSplitOperation } from 'relay-runtim */ +import UserFooFragment$normalization from 'UserFooFragment$normalization.graphql'; +import {bar as userBarResolver} from 'User_bar'; +import {foo as userFooResolver} from 'User_foo'; + var schema_resolvers/*: SchemaResolvers*/ = { "User": { "bar": { - "resolverFunction": require('User_bar').bar, + "resolverFunction": userBarResolver, "rootFragment": null }, "foo": { - "resolverFunction": require('User_foo').foo, - "rootFragment": require('UserFooFragment$normalization.graphql') + "resolverFunction": userFooResolver, + "rootFragment": UserFooFragment$normalization } } }; -module.exports = schema_resolvers; +export default schema_resolvers; //- __generated__/UserFooFragment$normalization.graphql.js /** - * SignedSource<<79043ca4ab6c3a1b54e21b82211b593c>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -81,6 +85,8 @@ import type { NormalizationSplitOperation } from 'relay-runtime'; */ +import {bar as userBarResolver} from 'User_bar'; + var node/*: NormalizationSplitOperation*/ = { "kind": "SplitOperation", "metadata": {}, @@ -96,7 +102,7 @@ var node/*: NormalizationSplitOperation*/ = { "storageKey": null, "isOutputType": true, "resolverInfo": { - "resolverFunction": require('User_bar').bar, + "resolverFunction": userBarResolver, "rootFragment": null } } @@ -107,11 +113,11 @@ var node/*: NormalizationSplitOperation*/ = { (node/*: any*/).hash = "285ee53d00b8def775c9e1ed756743bf"; -module.exports = node; +export default node; //- __generated__/UserFooFragment.graphql.js /** - * SignedSource<<2d571ceea775fac44fa1fceb53824891>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -140,6 +146,8 @@ export type UserFooFragment$key = { }; */ +import {bar as userBarResolver} from 'User_bar'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -155,7 +163,7 @@ var node/*: ReaderFragment*/ = { "fragment": null, "kind": "RelayResolver", "name": "bar", - "resolverModule": require('User_bar').bar, + "resolverModule": userBarResolver, "path": "bar" } ] @@ -167,7 +175,7 @@ var node/*: ReaderFragment*/ = { (node/*: any*/).hash = "285ee53d00b8def775c9e1ed756743bf"; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< UserFooFragment$fragmentType, UserFooFragment$data, >*/); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolvers_schema_module.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolvers_schema_module.expected index 68e06574604cd..6a18e321fa498 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolvers_schema_module.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolvers_schema_module.expected @@ -32,7 +32,7 @@ type User { name: String } ==================================== OUTPUT =================================== //- __generated__/ResolversSchemaModule.js /** - * SignedSource<> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -48,24 +48,28 @@ import type { ResolverFunction, NormalizationSplitOperation } from 'relay-runtim */ +import UserFooFragment$normalization from 'UserFooFragment$normalization.graphql'; +import {bar as userBarResolver} from 'User_bar'; +import {foo as userFooResolver} from 'User_foo'; + var schema_resolvers/*: SchemaResolvers*/ = { "User": { "bar": { - "resolverFunction": require('User_bar').bar, + "resolverFunction": userBarResolver, "rootFragment": null }, "foo": { - "resolverFunction": require('User_foo').foo, - "rootFragment": require('UserFooFragment$normalization.graphql') + "resolverFunction": userFooResolver, + "rootFragment": UserFooFragment$normalization } } }; -module.exports = schema_resolvers; +export default schema_resolvers; //- __generated__/UserFooFragment$normalization.graphql.js /** - * SignedSource<<79043ca4ab6c3a1b54e21b82211b593c>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -80,6 +84,8 @@ import type { NormalizationSplitOperation } from 'relay-runtime'; */ +import {bar as userBarResolver} from 'User_bar'; + var node/*: NormalizationSplitOperation*/ = { "kind": "SplitOperation", "metadata": {}, @@ -95,7 +101,7 @@ var node/*: NormalizationSplitOperation*/ = { "storageKey": null, "isOutputType": true, "resolverInfo": { - "resolverFunction": require('User_bar').bar, + "resolverFunction": userBarResolver, "rootFragment": null } } @@ -106,11 +112,11 @@ var node/*: NormalizationSplitOperation*/ = { (node/*: any*/).hash = "285ee53d00b8def775c9e1ed756743bf"; -module.exports = node; +export default node; //- __generated__/UserFooFragment.graphql.js /** - * SignedSource<<8cc1f9903984d3c06d796d4524cf1c23>> + * SignedSource<<843aee583dbf963998562b8559b43846>> * @flow * @lightSyntaxTransform * @nogrep @@ -139,6 +145,8 @@ export type UserFooFragment$key = { }; */ +import {bar as userBarResolver} from 'User_bar'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -154,7 +162,7 @@ var node/*: ReaderFragment*/ = { "fragment": null, "kind": "RelayResolver", "name": "bar", - "resolverModule": require('User_bar').bar, + "resolverModule": userBarResolver, "path": "bar" } ] @@ -166,7 +174,7 @@ var node/*: ReaderFragment*/ = { (node/*: any*/).hash = "285ee53d00b8def775c9e1ed756743bf"; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< UserFooFragment$fragmentType, UserFooFragment$data, >*/); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolvers_schema_module_apply_to_normalization_ast.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolvers_schema_module_apply_to_normalization_ast.expected index 1bda3061356f5..65f42340c908d 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolvers_schema_module_apply_to_normalization_ast.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolvers_schema_module_apply_to_normalization_ast.expected @@ -33,7 +33,7 @@ type User { name: String } ==================================== OUTPUT =================================== //- __generated__/ResolversSchemaModule.js /** - * SignedSource<> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -49,24 +49,28 @@ import type { ResolverFunction, NormalizationSplitOperation } from 'relay-runtim */ +import UserFooFragment$normalization from 'UserFooFragment$normalization.graphql'; +import {bar as userBarResolver} from 'User_bar'; +import {foo as userFooResolver} from 'User_foo'; + var schema_resolvers/*: SchemaResolvers*/ = { "User": { "bar": { - "resolverFunction": require('User_bar').bar, + "resolverFunction": userBarResolver, "rootFragment": null }, "foo": { - "resolverFunction": require('User_foo').foo, - "rootFragment": require('UserFooFragment$normalization.graphql') + "resolverFunction": userFooResolver, + "rootFragment": UserFooFragment$normalization } } }; -module.exports = schema_resolvers; +export default schema_resolvers; //- __generated__/UserFooFragment$normalization.graphql.js /** - * SignedSource<<0dc5f1959c406808557590206bee9173>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -104,11 +108,11 @@ var node/*: NormalizationSplitOperation*/ = { (node/*: any*/).hash = "285ee53d00b8def775c9e1ed756743bf"; -module.exports = node; +export default node; //- __generated__/UserFooFragment.graphql.js /** - * SignedSource<<8cc1f9903984d3c06d796d4524cf1c23>> + * SignedSource<<843aee583dbf963998562b8559b43846>> * @flow * @lightSyntaxTransform * @nogrep @@ -137,6 +141,8 @@ export type UserFooFragment$key = { }; */ +import {bar as userBarResolver} from 'User_bar'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -152,7 +158,7 @@ var node/*: ReaderFragment*/ = { "fragment": null, "kind": "RelayResolver", "name": "bar", - "resolverModule": require('User_bar').bar, + "resolverModule": userBarResolver, "path": "bar" } ] @@ -164,7 +170,7 @@ var node/*: ReaderFragment*/ = { (node/*: any*/).hash = "285ee53d00b8def775c9e1ed756743bf"; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< UserFooFragment$fragmentType, UserFooFragment$data, >*/); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolvers_with_context_javascript.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolvers_with_context_javascript.expected index 9626a389b81fc..2fd48c9f75d1d 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolvers_with_context_javascript.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolvers_with_context_javascript.expected @@ -31,7 +31,7 @@ type User { name: String } ==================================== OUTPUT =================================== //- __generated__/UserFooFragment.graphql.js /** - * SignedSource<> + * SignedSource<<4bd80eb7588ca19df0cc55cf857d4650>> * @lightSyntaxTransform * @nogrep */ @@ -40,6 +40,8 @@ type User { name: String } 'use strict'; +import {bar as userBarResolver} from 'User_bar'; + var node = { "argumentDefinitions": [], "kind": "Fragment", @@ -55,7 +57,7 @@ var node = { "fragment": null, "kind": "RelayResolver", "name": "bar", - "resolverModule": require('User_bar').bar, + "resolverModule": userBarResolver, "path": "bar" } ] @@ -67,4 +69,4 @@ var node = { node.hash = "285ee53d00b8def775c9e1ed756743bf"; -module.exports = node; +export default node; diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolvers_with_context_package_import.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolvers_with_context_package_import.expected index bf8e6e17b4011..5e7d1c442262e 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolvers_with_context_package_import.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolvers_with_context_package_import.expected @@ -31,7 +31,7 @@ type User { name: String } ==================================== OUTPUT =================================== //- __generated__/UserFooFragment.graphql.js /** - * SignedSource<<109c452d9b0ac94c8474a71548d2bf85>> + * SignedSource<<574f063c4f501846c8f536abc3be1e4a>> * @flow * @lightSyntaxTransform * @nogrep @@ -64,6 +64,8 @@ export type UserFooFragment$key = { }; */ +import {bar as userBarResolver} from 'User_bar'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -79,7 +81,7 @@ var node/*: ReaderFragment*/ = { "fragment": null, "kind": "RelayResolver", "name": "bar", - "resolverModule": require('User_bar').bar, + "resolverModule": userBarResolver, "path": "bar" } ] @@ -91,7 +93,7 @@ var node/*: ReaderFragment*/ = { (node/*: any*/).hash = "285ee53d00b8def775c9e1ed756743bf"; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< UserFooFragment$fragmentType, UserFooFragment$data, >*/); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolvers_with_context_path_import.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolvers_with_context_path_import.expected index 41edea0b817fd..f2014a605fe93 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolvers_with_context_path_import.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/resolvers_with_context_path_import.expected @@ -31,7 +31,7 @@ type User { name: String } ==================================== OUTPUT =================================== //- __generated__/UserFooFragment.graphql.js /** - * SignedSource<<9b9fdc86e3eff5eb9d79a0480d5ee62c>> + * SignedSource<<21d0ba98d4859103c9815fa8222cb7c0>> * @flow * @lightSyntaxTransform * @nogrep @@ -64,6 +64,8 @@ export type UserFooFragment$key = { }; */ +import {bar as userBarResolver} from 'User_bar'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -79,7 +81,7 @@ var node/*: ReaderFragment*/ = { "fragment": null, "kind": "RelayResolver", "name": "bar", - "resolverModule": require('User_bar').bar, + "resolverModule": userBarResolver, "path": "bar" } ] @@ -91,7 +93,7 @@ var node/*: ReaderFragment*/ = { (node/*: any*/).hash = "285ee53d00b8def775c9e1ed756743bf"; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< UserFooFragment$fragmentType, UserFooFragment$data, >*/); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/semantic_null_require_bubble_to_required_parent.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/semantic_null_require_bubble_to_required_parent.expected index cc9bef1208460..2efc9226b6f7e 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/semantic_null_require_bubble_to_required_parent.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/semantic_null_require_bubble_to_required_parent.expected @@ -25,7 +25,7 @@ type Person { ==================================== OUTPUT =================================== //- __generated__/PersonComponentFragment.graphql.js /** - * SignedSource<<72c3714ccc77e5f3810f7c07bbc9c9b8>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -93,7 +93,7 @@ var node/*: ReaderFragment*/ = { (node/*: any*/).hash = "98e50ccb4d4bee0618374c55c0a2c4a8"; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< PersonComponentFragment$fragmentType, PersonComponentFragment$data, >*/); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/semantic_null_require_bubbling.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/semantic_null_require_bubbling.expected index 2ea416bb39dda..f9a6bfd4ee629 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/semantic_null_require_bubbling.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/semantic_null_require_bubbling.expected @@ -24,7 +24,7 @@ type Person { ==================================== OUTPUT =================================== //- __generated__/PersonComponentFragment.graphql.js /** - * SignedSource<<7d314480e3c0070e01eb2fd380d77189>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -88,7 +88,7 @@ var node/*: ReaderFragment*/ = { (node/*: any*/).hash = "76fce3bfedd135a11ccda0abc265eb69"; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< PersonComponentFragment$fragmentType, PersonComponentFragment$data, >*/); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/spread_interface_fragment_on_concrete_type.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/spread_interface_fragment_on_concrete_type.expected index 5b7049e61cbe3..9cfe1d4a8a4ab 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/spread_interface_fragment_on_concrete_type.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/spread_interface_fragment_on_concrete_type.expected @@ -54,7 +54,7 @@ interface IPerson { ==================================== OUTPUT =================================== //- __generated__/Admin____relay_model_instance.graphql.js /** - * SignedSource<<7fbe3989595c55397f4bcc5c81ec30b2>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -86,6 +86,10 @@ export type Admin____relay_model_instance$key = { }; */ +import {Admin as adminRelayModelInstanceResolver} from 'AdminTypeResolvers'; +import Admin__id_graphql from 'Admin__id.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -102,7 +106,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "__relay_model_instance", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('Admin__id.graphql'), require('AdminTypeResolvers').Admin, 'id', true), + "resolverModule": resolverDataInjector(Admin__id_graphql, adminRelayModelInstanceResolver, 'id', true), "path": "__relay_model_instance" } ], @@ -110,14 +114,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< Admin____relay_model_instance$fragmentType, Admin____relay_model_instance$data, >*/); //- __generated__/Admin__id.graphql.js /** - * SignedSource<<29acfbf1d6f559b8b77e9cd1f35218c0>> + * SignedSource<<78e97d08242ee5ff28fb2c5bf850cd81>> * @flow * @lightSyntaxTransform * @nogrep @@ -165,14 +169,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< Admin__id$fragmentType, Admin__id$data, >*/); //- __generated__/PersonComponentFragment.graphql.js /** - * SignedSource<<08d1886c2f48001c3ee34ede8433c26d>> + * SignedSource<<28cc9ec418ed43ba73463255cb2ebc92>> * @flow * @lightSyntaxTransform * @nogrep @@ -197,6 +201,12 @@ export type PersonComponentFragment$key = $ReadOnlyArray<{ }>; */ +import {name as adminNameResolver} from 'AdminTypeResolvers'; +import Admin____relay_model_instance_graphql from 'Admin____relay_model_instance.graphql'; +import {name as userNameResolver} from 'UserTypeResolvers'; +import User____relay_model_instance_graphql from 'User____relay_model_instance.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -221,7 +231,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "name", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('Admin____relay_model_instance.graphql'), require('AdminTypeResolvers').name, '__relay_model_instance', true), + "resolverModule": resolverDataInjector(Admin____relay_model_instance_graphql, adminNameResolver, '__relay_model_instance', true), "path": "name" } ], @@ -241,7 +251,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "name", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('User____relay_model_instance.graphql'), require('UserTypeResolvers').name, '__relay_model_instance', true), + "resolverModule": resolverDataInjector(User____relay_model_instance_graphql, userNameResolver, '__relay_model_instance', true), "path": "name" } ], @@ -257,14 +267,14 @@ var node/*: ReaderFragment*/ = { (node/*: any*/).hash = "8be134328a2e066b0a25f03aa1bd8468"; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< PersonComponentFragment$fragmentType, PersonComponentFragment$data, >*/); //- __generated__/SpreadInterfaceFragmentOnConcreteTypeComponentFragment.graphql.js /** - * SignedSource<> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -308,14 +318,14 @@ var node/*: ReaderFragment*/ = { (node/*: any*/).hash = "fdd82cf9f0e06b12fd7652c210d56984"; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< SpreadInterfaceFragmentOnConcreteTypeComponentFragment$fragmentType, SpreadInterfaceFragmentOnConcreteTypeComponentFragment$data, >*/); //- __generated__/User____relay_model_instance.graphql.js /** - * SignedSource<<7ffabc2a97c3589cbfd20a23b3b608ca>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -347,6 +357,10 @@ export type User____relay_model_instance$key = { }; */ +import {User as userRelayModelInstanceResolver} from 'UserTypeResolvers'; +import User__id_graphql from 'User__id.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -363,7 +377,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "__relay_model_instance", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('User__id.graphql'), require('UserTypeResolvers').User, 'id', true), + "resolverModule": resolverDataInjector(User__id_graphql, userRelayModelInstanceResolver, 'id', true), "path": "__relay_model_instance" } ], @@ -371,14 +385,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< User____relay_model_instance$fragmentType, User____relay_model_instance$data, >*/); //- __generated__/User__id.graphql.js /** - * SignedSource<<0a0f39eb34bfc882d28378a0b05b3c17>> + * SignedSource<<56388778b313654f6700e4bfa73599a2>> * @flow * @lightSyntaxTransform * @nogrep @@ -426,7 +440,7 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< User__id$fragmentType, User__id$data, >*/); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/spread_interface_fragment_on_weak_concrete_type.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/spread_interface_fragment_on_weak_concrete_type.expected index 80671dd60984d..e83043157ac55 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/spread_interface_fragment_on_weak_concrete_type.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/spread_interface_fragment_on_weak_concrete_type.expected @@ -64,7 +64,7 @@ interface IPerson { ==================================== OUTPUT =================================== //- __generated__/Admin____relay_model_instance.graphql.js /** - * SignedSource<<523d097198aa2ffa2a1209e24ac2a337>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -113,14 +113,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< Admin____relay_model_instance$fragmentType, Admin____relay_model_instance$data, >*/); //- __generated__/PersonComponentFragment.graphql.js /** - * SignedSource<<281289c09e9f11b0f0fdf7dd357dbe36>> + * SignedSource<<6a9121eabc585aa4a04d8e1049221b52>> * @flow * @lightSyntaxTransform * @nogrep @@ -145,6 +145,12 @@ export type PersonComponentFragment$key = { }; */ +import {name as adminNameResolver} from 'AdminTypeResolvers'; +import Admin____relay_model_instance_graphql from 'Admin____relay_model_instance.graphql'; +import {name as userNameResolver} from 'UserTypeResolvers'; +import User____relay_model_instance_graphql from 'User____relay_model_instance.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -167,7 +173,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "name", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('Admin____relay_model_instance.graphql'), require('AdminTypeResolvers').name, '__relay_model_instance', true), + "resolverModule": resolverDataInjector(Admin____relay_model_instance_graphql, adminNameResolver, '__relay_model_instance', true), "path": "name" } ], @@ -187,7 +193,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "name", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('User____relay_model_instance.graphql'), require('UserTypeResolvers').name, '__relay_model_instance', true), + "resolverModule": resolverDataInjector(User____relay_model_instance_graphql, userNameResolver, '__relay_model_instance', true), "path": "name" } ], @@ -203,14 +209,14 @@ var node/*: ReaderFragment*/ = { (node/*: any*/).hash = "a57dd30bd59412781e9566e1553e2d70"; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< PersonComponentFragment$fragmentType, PersonComponentFragment$data, >*/); //- __generated__/UserQueryComponentQuery.graphql.js /** - * SignedSource<<097b4c59a2a87918f0d913802fcac7cb>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -240,6 +246,10 @@ export type UserQueryComponentQuery = {| |}; */ +import {name as adminNameResolver} from 'AdminTypeResolvers'; +import {name as userNameResolver} from 'UserTypeResolvers'; +import {user as queryUserResolver} from 'UserTypeResolvers'; + var node/*: ClientRequest*/ = { "fragment": { "argumentDefinitions": [], @@ -259,7 +269,7 @@ var node/*: ClientRequest*/ = { "fragment": null, "kind": "RelayResolver", "name": "user", - "resolverModule": require('UserTypeResolvers').user, + "resolverModule": queryUserResolver, "path": "user", "normalizationInfo": { "kind": "WeakModel", @@ -303,7 +313,7 @@ var node/*: ClientRequest*/ = { "storageKey": null, "isOutputType": true, "resolverInfo": { - "resolverFunction": require('UserTypeResolvers').user, + "resolverFunction": queryUserResolver, "rootFragment": null } }, @@ -328,7 +338,7 @@ var node/*: ClientRequest*/ = { "storageKey": null, "isOutputType": true, "resolverInfo": { - "resolverFunction": require('AdminTypeResolvers').name, + "resolverFunction": adminNameResolver, "rootFragment": null } } @@ -346,7 +356,7 @@ var node/*: ClientRequest*/ = { "storageKey": null, "isOutputType": true, "resolverInfo": { - "resolverFunction": require('UserTypeResolvers').name, + "resolverFunction": userNameResolver, "rootFragment": null } } @@ -382,14 +392,14 @@ var node/*: ClientRequest*/ = { (node/*: any*/).hash = "69ab3801e24e1f83ade42fd674df9ce7"; -module.exports = ((node/*: any*/)/*: ClientQuery< +export default ((node/*: any*/)/*: ClientQuery< UserQueryComponentQuery$variables, UserQueryComponentQuery$data, >*/); //- __generated__/User____relay_model_instance.graphql.js /** - * SignedSource<<9a188c26688bb46f65ed80df4ae938c3>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -438,7 +448,7 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< User____relay_model_instance$fragmentType, User____relay_model_instance$data, >*/); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/spread_multiple_interface_fragments_on_concrete_type.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/spread_multiple_interface_fragments_on_concrete_type.expected index 8ca0188f59301..f32712d1bac06 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/spread_multiple_interface_fragments_on_concrete_type.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/spread_multiple_interface_fragments_on_concrete_type.expected @@ -66,7 +66,7 @@ interface IActor { ==================================== OUTPUT =================================== //- __generated__/ActorComponentFragment.graphql.js /** - * SignedSource<<29a6721ae972d24abc6fd33ed4cae7bd>> + * SignedSource<<8ecf1496c9be81fba75d8d8bf501e19b>> * @flow * @lightSyntaxTransform * @nogrep @@ -99,6 +99,12 @@ export type ActorComponentFragment$key = { }; */ +import Admin____relay_model_instance_graphql from 'Admin____relay_model_instance.graphql'; +import {description as adminDescriptionResolver} from 'IActorResolvers'; +import {description as userDescriptionResolver} from 'IActorResolvers'; +import User____relay_model_instance_graphql from 'User____relay_model_instance.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -121,7 +127,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "description", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('Admin____relay_model_instance.graphql'), require('IActorResolvers').description, '__relay_model_instance', true), + "resolverModule": resolverDataInjector(Admin____relay_model_instance_graphql, adminDescriptionResolver, '__relay_model_instance', true), "path": "description" } ], @@ -141,7 +147,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "description", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('User____relay_model_instance.graphql'), require('IActorResolvers').description, '__relay_model_instance', true), + "resolverModule": resolverDataInjector(User____relay_model_instance_graphql, userDescriptionResolver, '__relay_model_instance', true), "path": "description" } ], @@ -157,14 +163,14 @@ var node/*: ReaderFragment*/ = { (node/*: any*/).hash = "44e992843e41c24ce4cc28290f4c04c3"; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< ActorComponentFragment$fragmentType, ActorComponentFragment$data, >*/); //- __generated__/Admin____relay_model_instance.graphql.js /** - * SignedSource<<7fbe3989595c55397f4bcc5c81ec30b2>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -196,6 +202,10 @@ export type Admin____relay_model_instance$key = { }; */ +import {Admin as adminRelayModelInstanceResolver} from 'AdminTypeResolvers'; +import Admin__id_graphql from 'Admin__id.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -212,7 +222,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "__relay_model_instance", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('Admin__id.graphql'), require('AdminTypeResolvers').Admin, 'id', true), + "resolverModule": resolverDataInjector(Admin__id_graphql, adminRelayModelInstanceResolver, 'id', true), "path": "__relay_model_instance" } ], @@ -220,14 +230,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< Admin____relay_model_instance$fragmentType, Admin____relay_model_instance$data, >*/); //- __generated__/Admin__id.graphql.js /** - * SignedSource<<29acfbf1d6f559b8b77e9cd1f35218c0>> + * SignedSource<<78e97d08242ee5ff28fb2c5bf850cd81>> * @flow * @lightSyntaxTransform * @nogrep @@ -275,14 +285,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< Admin__id$fragmentType, Admin__id$data, >*/); //- __generated__/PersonComponentFragment.graphql.js /** - * SignedSource<<4ea82f388eeb21ecbc79f404917c194e>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -315,6 +325,12 @@ export type PersonComponentFragment$key = { }; */ +import Admin____relay_model_instance_graphql from 'Admin____relay_model_instance.graphql'; +import {name as adminNameResolver} from 'IPersonResolvers'; +import {name as userNameResolver} from 'IPersonResolvers'; +import User____relay_model_instance_graphql from 'User____relay_model_instance.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -337,7 +353,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "name", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('Admin____relay_model_instance.graphql'), require('IPersonResolvers').name, '__relay_model_instance', true), + "resolverModule": resolverDataInjector(Admin____relay_model_instance_graphql, adminNameResolver, '__relay_model_instance', true), "path": "name" } ], @@ -357,7 +373,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "name", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('User____relay_model_instance.graphql'), require('IPersonResolvers').name, '__relay_model_instance', true), + "resolverModule": resolverDataInjector(User____relay_model_instance_graphql, userNameResolver, '__relay_model_instance', true), "path": "name" } ], @@ -373,14 +389,14 @@ var node/*: ReaderFragment*/ = { (node/*: any*/).hash = "a57dd30bd59412781e9566e1553e2d70"; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< PersonComponentFragment$fragmentType, PersonComponentFragment$data, >*/); //- __generated__/SpreadInterfaceFragmentOnConcreteTypeComponentFragment.graphql.js /** - * SignedSource<<290409f030b9f36f1caae1e20cc4b354>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -430,14 +446,14 @@ var node/*: ReaderFragment*/ = { (node/*: any*/).hash = "e2b46497d73b1e146810ae14681ce015"; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< SpreadInterfaceFragmentOnConcreteTypeComponentFragment$fragmentType, SpreadInterfaceFragmentOnConcreteTypeComponentFragment$data, >*/); //- __generated__/User____relay_model_instance.graphql.js /** - * SignedSource<<7ffabc2a97c3589cbfd20a23b3b608ca>> + * SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -469,6 +485,10 @@ export type User____relay_model_instance$key = { }; */ +import {User as userRelayModelInstanceResolver} from 'UserTypeResolvers'; +import User__id_graphql from 'User__id.graphql'; +import {resolverDataInjector} from 'relay-runtime/experimental'; + var node/*: ReaderFragment*/ = { "argumentDefinitions": [], "kind": "Fragment", @@ -485,7 +505,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "__relay_model_instance", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('User__id.graphql'), require('UserTypeResolvers').User, 'id', true), + "resolverModule": resolverDataInjector(User__id_graphql, userRelayModelInstanceResolver, 'id', true), "path": "__relay_model_instance" } ], @@ -493,14 +513,14 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< User____relay_model_instance$fragmentType, User____relay_model_instance$data, >*/); //- __generated__/User__id.graphql.js /** - * SignedSource<<0a0f39eb34bfc882d28378a0b05b3c17>> + * SignedSource<<56388778b313654f6700e4bfa73599a2>> * @flow * @lightSyntaxTransform * @nogrep @@ -548,7 +568,7 @@ var node/*: ReaderFragment*/ = { "abstractKey": null }; -module.exports = ((node/*: any*/)/*: Fragment< +export default ((node/*: any*/)/*: Fragment< User__id$fragmentType, User__id$data, >*/); diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/typescript_resolver_type_import.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/typescript_resolver_type_import.expected index 7e58e6d06fe95..9f1774800337e 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/typescript_resolver_type_import.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/typescript_resolver_type_import.expected @@ -12,8 +12,7 @@ graphql`fragment barFragment on User { //- relay.config.json { "language": "typescript", - "schema": "./schema.graphql", - "eagerEsModules": true + "schema": "./schema.graphql" } //- schema.graphql @@ -22,7 +21,7 @@ type User { name: String } ==================================== OUTPUT =================================== //- __generated__/barFragment.graphql.ts /** - * SignedSource<<0f778584db7b20b3491a3ed42c61cdc1>> + * SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -46,7 +45,7 @@ export type barFragment$key = { readonly " $fragmentSpreads": FragmentRefs<"barFragment">; }; -import {foo as userFooResolver} from './../foo'; +import {foo as userFooResolver} from '../foo'; const node: ReaderFragment = { "argumentDefinitions": [], diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/typescript_resolver_type_import.input b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/typescript_resolver_type_import.input index cddc98ea38529..7cd79d104ed37 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/typescript_resolver_type_import.input +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/typescript_resolver_type_import.input @@ -11,8 +11,7 @@ graphql`fragment barFragment on User { //- relay.config.json { "language": "typescript", - "schema": "./schema.graphql", - "eagerEsModules": true + "schema": "./schema.graphql" } //- schema.graphql diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/typescript_resolver_with_context.expected b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/typescript_resolver_with_context.expected index d54ede81350af..6bfb9f87fb101 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/typescript_resolver_with_context.expected +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/typescript_resolver_with_context.expected @@ -15,7 +15,6 @@ graphql`fragment barFragment on User { { "language": "typescript", "schema": "./schema.graphql", - "eagerEsModules": true, "resolverContextType" : { "package": "@test/package", "name": "ITestResolverContextType" } } @@ -25,7 +24,7 @@ type User { name: String } ==================================== OUTPUT =================================== //- __generated__/barFragment.graphql.ts /** - * SignedSource<<7c2c6939d2e5610ed47a154e9cab2dfd>> + * SignedSource<<047bd9f880ccc0b27af29416cc627e8b>> * @lightSyntaxTransform * @nogrep */ @@ -53,7 +52,7 @@ export type barFragment$key = { readonly " $fragmentSpreads": FragmentRefs<"barFragment">; }; -import {foo as userFooResolver} from './../foo'; +import {foo as userFooResolver} from '../foo'; const node: ReaderFragment = { "argumentDefinitions": [], diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/typescript_resolver_with_context.input b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/typescript_resolver_with_context.input index e4936b3b9facf..9c1ba6212f038 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/typescript_resolver_with_context.input +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration/fixtures/typescript_resolver_with_context.input @@ -14,7 +14,6 @@ graphql`fragment barFragment on User { { "language": "typescript", "schema": "./schema.graphql", - "eagerEsModules": true, "resolverContextType" : { "package": "@test/package", "name": "ITestResolverContextType" } } diff --git a/compiler/crates/relay-compiler/tests/relay_compiler_integration_test.rs b/compiler/crates/relay-compiler/tests/relay_compiler_integration_test.rs index db820da036835..d04b7fd5be143 100644 --- a/compiler/crates/relay-compiler/tests/relay_compiler_integration_test.rs +++ b/compiler/crates/relay-compiler/tests/relay_compiler_integration_test.rs @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<44313b4ad0ca41fac58e6a820c78a157>> + * @generated SignedSource<> */ mod relay_compiler_integration; @@ -47,6 +47,13 @@ async fn client_mutation_resolver_invalid_nonscalar() { test_fixture(transform_fixture, file!(), "client_mutation_resolver_invalid_nonscalar.input", "relay_compiler_integration/fixtures/client_mutation_resolver_invalid_nonscalar.expected", input, expected).await; } +#[tokio::test] +async fn client_schema_extension_in_throw_on_field_error() { + let input = include_str!("relay_compiler_integration/fixtures/client_schema_extension_in_throw_on_field_error.input"); + let expected = include_str!("relay_compiler_integration/fixtures/client_schema_extension_in_throw_on_field_error.expected"); + test_fixture(transform_fixture, file!(), "client_schema_extension_in_throw_on_field_error.input", "relay_compiler_integration/fixtures/client_schema_extension_in_throw_on_field_error.expected", input, expected).await; +} + #[tokio::test] async fn client_schema_extension_interface_uses_resolver_type() { let input = include_str!("relay_compiler_integration/fixtures/client_schema_extension_interface_uses_resolver_type.input"); @@ -82,6 +89,20 @@ async fn error_handling_query() { test_fixture(transform_fixture, file!(), "error_handling_query.input", "relay_compiler_integration/fixtures/error_handling_query.expected", input, expected).await; } +#[tokio::test] +async fn exec_resolvers_directive_with_root_fragment() { + let input = include_str!("relay_compiler_integration/fixtures/exec_resolvers_directive_with_root_fragment.input"); + let expected = include_str!("relay_compiler_integration/fixtures/exec_resolvers_directive_with_root_fragment.expected"); + test_fixture(transform_fixture, file!(), "exec_resolvers_directive_with_root_fragment.input", "relay_compiler_integration/fixtures/exec_resolvers_directive_with_root_fragment.expected", input, expected).await; +} + +#[tokio::test] +async fn extra_in_single_file_config() { + let input = include_str!("relay_compiler_integration/fixtures/extra_in_single_file_config.input"); + let expected = include_str!("relay_compiler_integration/fixtures/extra_in_single_file_config.expected"); + test_fixture(transform_fixture, file!(), "extra_in_single_file_config.input", "relay_compiler_integration/fixtures/extra_in_single_file_config.expected", input, expected).await; +} + #[tokio::test] async fn fragment_alias_nested_in_inline_fragment() { let input = include_str!("relay_compiler_integration/fixtures/fragment_alias_nested_in_inline_fragment.input"); @@ -145,6 +166,13 @@ async fn preloadable_query_typescript() { test_fixture(transform_fixture, file!(), "preloadable_query_typescript.input", "relay_compiler_integration/fixtures/preloadable_query_typescript.expected", input, expected).await; } +#[tokio::test] +async fn relay_resolvers_in_throw_on_field_error() { + let input = include_str!("relay_compiler_integration/fixtures/relay_resolvers_in_throw_on_field_error.input"); + let expected = include_str!("relay_compiler_integration/fixtures/relay_resolvers_in_throw_on_field_error.expected"); + test_fixture(transform_fixture, file!(), "relay_resolvers_in_throw_on_field_error.input", "relay_compiler_integration/fixtures/relay_resolvers_in_throw_on_field_error.expected", input, expected).await; +} + #[tokio::test] async fn repro_dangerously_unaliased_changes_output_after() { let input = include_str!("relay_compiler_integration/fixtures/repro_dangerously_unaliased_changes_output_after.input"); @@ -264,6 +292,13 @@ async fn resolver_returns_interface_of_live_and_non_live_strong_model_type() { test_fixture(transform_fixture, file!(), "resolver_returns_interface_of_live_and_non_live_strong_model_type.input", "relay_compiler_integration/fixtures/resolver_returns_interface_of_live_and_non_live_strong_model_type.expected", input, expected).await; } +#[tokio::test] +async fn resolver_returns_plural_server_type_invalid() { + let input = include_str!("relay_compiler_integration/fixtures/resolver_returns_plural_server_type.invalid.input"); + let expected = include_str!("relay_compiler_integration/fixtures/resolver_returns_plural_server_type.invalid.expected"); + test_fixture(transform_fixture, file!(), "resolver_returns_plural_server_type.invalid.input", "relay_compiler_integration/fixtures/resolver_returns_plural_server_type.invalid.expected", input, expected).await; +} + #[tokio::test] async fn resolver_returns_union_of_cse() { let input = include_str!("relay_compiler_integration/fixtures/resolver_returns_union_of_cse.input"); @@ -292,6 +327,13 @@ async fn resolver_returns_union_of_weak_resolver() { test_fixture(transform_fixture, file!(), "resolver_returns_union_of_weak_resolver.input", "relay_compiler_integration/fixtures/resolver_returns_union_of_weak_resolver.expected", input, expected).await; } +#[tokio::test] +async fn resolver_returns_weak_client_schema_type() { + let input = include_str!("relay_compiler_integration/fixtures/resolver_returns_weak_client_schema_type.input"); + let expected = include_str!("relay_compiler_integration/fixtures/resolver_returns_weak_client_schema_type.expected"); + test_fixture(transform_fixture, file!(), "resolver_returns_weak_client_schema_type.input", "relay_compiler_integration/fixtures/resolver_returns_weak_client_schema_type.expected", input, expected).await; +} + #[tokio::test] async fn resolver_semantic_non_null_custom_scalar() { let input = include_str!("relay_compiler_integration/fixtures/resolver_semantic_non_null_custom_scalar.input"); diff --git a/compiler/crates/relay-compiler/tests/relay_config_schema_json_test.rs b/compiler/crates/relay-compiler/tests/relay_config_schema_json_test.rs index 2e297cbb91250..93c6473a9cd38 100644 --- a/compiler/crates/relay-compiler/tests/relay_config_schema_json_test.rs +++ b/compiler/crates/relay-compiler/tests/relay_config_schema_json_test.rs @@ -5,8 +5,8 @@ * LICENSE file in the root directory of this source tree. */ -use fixture_tests::assert_file_contains; use fixture_tests::WORKSPACE_ROOT; +use fixture_tests::assert_file_contains; use relay_compiler::ConfigFile; #[tokio::test] diff --git a/compiler/crates/relay-config/Cargo.toml b/compiler/crates/relay-config/Cargo.toml index c9630a4f6391c..f932ee0a8a381 100644 --- a/compiler/crates/relay-config/Cargo.toml +++ b/compiler/crates/relay-config/Cargo.toml @@ -4,7 +4,7 @@ name = "relay-config" version = "0.0.0" authors = ["Facebook"] -edition = "2021" +edition = "2024" repository = "https://github.com/facebook/relay" license = "MIT" @@ -12,11 +12,11 @@ license = "MIT" common = { path = "../common" } fnv = "1.0" globset = { version = "0.4.13", features = ["serde1"] } -indexmap = { version = "2.2.6", features = ["arbitrary", "rayon", "serde"] } +indexmap = { version = "2.9.0", features = ["arbitrary", "rayon", "serde"] } intern = { path = "../intern" } pathdiff = "0.2" -regex = "1.9.2" -schemars = { version = "0.8.21", features = ["indexmap2"] } -serde = { version = "1.0.185", features = ["derive", "rc"] } -serde_json = { version = "1.0.132", features = ["float_roundtrip", "unbounded_depth"] } -strum = { version = "0.26.2", features = ["derive"] } +regex = "1.11.1" +schemars = { version = "1.0.4", features = ["indexmap2"] } +serde = { version = "1.0.219", features = ["derive", "rc"] } +serde_json = { version = "1.0.140", features = ["alloc", "float_roundtrip", "unbounded_depth"] } +strum = { version = "0.27.1", features = ["derive"] } diff --git a/compiler/crates/relay-config/src/lib.rs b/compiler/crates/relay-config/src/lib.rs index 52ba87c73ec9d..fd78ea4742a09 100644 --- a/compiler/crates/relay-config/src/lib.rs +++ b/compiler/crates/relay-config/src/lib.rs @@ -25,8 +25,8 @@ pub use defer_stream_interface::DeferStreamInterface; pub use diagnostic_report_config::DiagnosticLevel; pub use diagnostic_report_config::DiagnosticReportConfig; pub use js_module_format::JsModuleFormat; -pub use module_import_config::DynamicModuleProvider; pub use module_import_config::ModuleImportConfig; +pub use module_import_config::ModuleProvider; pub use module_import_config::Surface; pub use non_node_id_fields_config::NonNodeIdFieldsConfig; pub use project_config::ExtraArtifactsConfig; diff --git a/compiler/crates/relay-config/src/module_import_config.rs b/compiler/crates/relay-config/src/module_import_config.rs index 95fcbf12bba25..7bb5d05dce111 100644 --- a/compiler/crates/relay-config/src/module_import_config.rs +++ b/compiler/crates/relay-config/src/module_import_config.rs @@ -17,7 +17,11 @@ pub struct ModuleImportConfig { /// Defines the custom import statement to be generated on the /// `ModuleImport` node in ASTs, used for dynamically loading /// components at runtime. - pub dynamic_module_provider: Option, + pub dynamic_module_provider: Option, + /// Defines the custom import statement to be generated for the + /// `operationModuleProvider` function on the `NormalizationModuleImport` + /// node in ASTs. Used in exec time client 3D. + pub operation_module_provider: Option, /// Defines the surface upon which @module is enabled. pub surface: Option, } @@ -34,7 +38,7 @@ pub struct ModuleImportConfig { JsonSchema )] #[serde(tag = "mode")] -pub enum DynamicModuleProvider { +pub enum ModuleProvider { /// Generates a module provider using JSResource JSResource, /// Generates a custom JS import, Use `<$module>` as the placeholder diff --git a/compiler/crates/relay-config/src/project_config.rs b/compiler/crates/relay-config/src/project_config.rs index 55aa6d740ec27..68bace19e16b7 100644 --- a/compiler/crates/relay-config/src/project_config.rs +++ b/compiler/crates/relay-config/src/project_config.rs @@ -6,9 +6,9 @@ */ use std::fmt; +use std::path::MAIN_SEPARATOR; use std::path::Path; use std::path::PathBuf; -use std::path::MAIN_SEPARATOR; use std::sync::Arc; use common::DirectiveName; @@ -24,22 +24,22 @@ use intern::string_key::Intern; use intern::string_key::StringKey; use regex::Regex; use schemars::JsonSchema; -use serde::de::Error; use serde::Deserialize; use serde::Deserializer; use serde::Serialize; +use serde::de::Error; use serde_json::Value; +use crate::JsModuleFormat; +use crate::ProjectName; +use crate::TypegenConfig; +use crate::TypegenLanguage; use crate::connection_interface::ConnectionInterface; use crate::defer_stream_interface::DeferStreamInterface; use crate::diagnostic_report_config::DiagnosticReportConfig; use crate::module_import_config::ModuleImportConfig; use crate::non_node_id_fields_config::NonNodeIdFieldsConfig; use crate::resolvers_schema_module_config::ResolversSchemaModuleConfig; -use crate::JsModuleFormat; -use crate::ProjectName; -use crate::TypegenConfig; -use crate::TypegenLanguage; type FnvIndexMap = IndexMap; @@ -220,6 +220,10 @@ pub struct SchemaConfig { /// The name of the directive indicating fields that cannot be selected #[serde(default = "default_unselectable_directive_name")] pub unselectable_directive_name: DirectiveName, + + /// If we should select __token field on fetchable types + #[serde(default = "default_enable_token_field")] + pub enable_token_field: bool, } fn default_node_interface_id_field() -> StringKey { @@ -234,6 +238,10 @@ fn default_unselectable_directive_name() -> DirectiveName { DirectiveName("unselectable".intern()) } +fn default_enable_token_field() -> bool { + false +} + impl Default for SchemaConfig { fn default() -> Self { Self { @@ -243,6 +251,7 @@ impl Default for SchemaConfig { node_interface_id_variable_name: default_node_interface_id_variable_name(), non_node_id_fields: None, unselectable_directive_name: default_unselectable_directive_name(), + enable_token_field: default_enable_token_field(), } } } @@ -309,6 +318,9 @@ pub struct ProjectConfig { pub input_unions: Option>, /// Custom function to get the path for an artifact. pub get_custom_path_for_artifact: Option, + /// Treats JS module paths as relative to './' when true, and leaves JS + /// module paths unmodified when false. + pub relativize_js_module_paths: bool, } impl Default for ProjectConfig { @@ -342,6 +354,7 @@ impl Default for ProjectConfig { codegen_command: Default::default(), input_unions: Default::default(), get_custom_path_for_artifact: None, + relativize_js_module_paths: true, } } } @@ -377,6 +390,7 @@ impl Debug for ProjectConfig { codegen_command, input_unions, get_custom_path_for_artifact: _, + relativize_js_module_paths, } = self; f.debug_struct("ProjectConfig") .field("name", name) @@ -406,12 +420,14 @@ impl Debug for ProjectConfig { .field("resolvers_schema_module", resolvers_schema_module) .field("codegen_command", codegen_command) .field("input_unions", input_unions) + .field("relativize_js_module_paths", relativize_js_module_paths) .finish() } } impl ProjectConfig { - /// This function will create a correct path for an artifact based on the project configuration + /// Gets the correct path for a generated artifact based on its originating source file's + /// location, and the project's configuration. pub fn create_path_for_artifact( &self, source_file: SourceLocationKey, @@ -441,6 +457,7 @@ impl ProjectConfig { } } + /// Generates a path for an artifact file based on a definition name and its location. pub fn artifact_path_for_definition( &self, definition_name: WithLocation>, @@ -465,6 +482,7 @@ impl ProjectConfig { } } + /// Generates a path for an artifact file that is specific to the programming language being used. pub fn path_for_language_specific_artifact( &self, source_file: SourceLocationKey, diff --git a/compiler/crates/relay-config/src/project_name.rs b/compiler/crates/relay-config/src/project_name.rs index 8b384556b2fc5..d88f31517a824 100644 --- a/compiler/crates/relay-config/src/project_name.rs +++ b/compiler/crates/relay-config/src/project_name.rs @@ -7,9 +7,9 @@ use std::fmt; +use intern::Lookup; use intern::string_key::Intern; use intern::string_key::StringKey; -use intern::Lookup; use schemars::JsonSchema; use serde::Deserialize; use serde::Deserializer; diff --git a/compiler/crates/relay-config/src/typegen_config.rs b/compiler/crates/relay-config/src/typegen_config.rs index ec872b0c0d025..22027f69b1fa6 100644 --- a/compiler/crates/relay-config/src/typegen_config.rs +++ b/compiler/crates/relay-config/src/typegen_config.rs @@ -54,23 +54,34 @@ impl TypegenLanguage { } } +/// Defines a custom GraphQL +/// descrbing a custom scalar. #[derive(Debug, Serialize, Deserialize, Clone, JsonSchema, Hash, PartialEq, Eq)] #[serde(untagged)] pub enum CustomType { + /// A string representing the name of a custom type. e.g. "string" or "number" Name(StringKey), + /// A module which defines the custom type. e.g. { "name": "MyCustomType", "path": "./Types.ts" } Path(CustomTypeImport), } +/// Defines a module path and export name of the Flow or TypeScript type +/// descrbing a GraphQL custom scalar. #[derive(Debug, Serialize, Deserialize, Clone, JsonSchema, Hash, PartialEq, Eq)] pub struct CustomTypeImport { + /// The name under which the type is exported from the module pub name: StringKey, + /// The path to the module relative to the project root pub path: PathBuf, } +/// Describes the type to import and use as the context for Relay Resolvers. #[derive(Debug, Serialize, Deserialize, Clone, JsonSchema)] #[serde(untagged)] pub enum ResolverContextTypeInput { + /// The type imported using a relative path Path(ResolverContextTypeInputPath), + /// The type imported using a named package Package(ResolverContextTypeInputPackage), } @@ -137,8 +148,9 @@ pub struct TypegenConfig { #[serde(default)] pub no_future_proof_enums: bool, - /// This option enables emitting es modules artifacts. - #[serde(default)] + /// This option enables opting out of emitting es modules artifacts. When + /// set to false, Relay will emit CommonJS modules. + #[serde(default = "get_true")] pub eager_es_modules: bool, /// Keep the previous compiler behavior by outputting an union @@ -151,11 +163,15 @@ pub struct TypegenConfig { /// {"name:: "MyErrorName", "path": "../src/MyError"} pub custom_error_type: Option, - /// Indicates the type to import and use as the context for live resolvers. + /// Indicates the type to import and use as the context for Relay Resolvers. #[serde(default)] pub resolver_context_type: Option, } +fn get_true() -> bool { + true +} + impl Default for TypegenConfig { fn default() -> Self { TypegenConfig { diff --git a/compiler/crates/relay-docblock/Cargo.toml b/compiler/crates/relay-docblock/Cargo.toml index 782aa4cb03a86..915d0b49b2428 100644 --- a/compiler/crates/relay-docblock/Cargo.toml +++ b/compiler/crates/relay-docblock/Cargo.toml @@ -4,7 +4,7 @@ name = "relay-docblock" version = "0.0.0" authors = ["Facebook"] -edition = "2021" +edition = "2024" repository = "https://github.com/facebook/relay" license = "MIT" @@ -24,12 +24,12 @@ errors = { path = "../errors" } graphql-ir = { path = "../graphql-ir" } graphql-syntax = { path = "../graphql-syntax" } intern = { path = "../intern" } -lazy_static = "1.4" +lazy_static = "1.5" relay-config = { path = "../relay-config" } relay-schema = { path = "../relay-schema" } schema = { path = "../schema" } -serde = { version = "1.0.185", features = ["derive", "rc"] } -thiserror = "1.0.64" +serde = { version = "1.0.219", features = ["derive", "rc"] } +thiserror = "2.0.12" [dev-dependencies] extract-graphql = { path = "../extract-graphql" } @@ -37,4 +37,4 @@ fixture-tests = { path = "../fixture-tests" } graphql-cli = { path = "../graphql-cli" } graphql-test-helpers = { path = "../graphql-test-helpers" } relay-test-schema = { path = "../relay-test-schema" } -tokio = { version = "1.41.0", features = ["full", "test-util", "tracing"] } +tokio = { version = "1.46.1", features = ["full", "test-util", "tracing"] } diff --git a/compiler/crates/relay-docblock/src/docblock_ir.rs b/compiler/crates/relay-docblock/src/docblock_ir.rs index 686a7fe362f9b..a0136616e9141 100644 --- a/compiler/crates/relay-docblock/src/docblock_ir.rs +++ b/compiler/crates/relay-docblock/src/docblock_ir.rs @@ -14,19 +14,14 @@ use common::NamedItem; use common::SourceLocationKey; use common::Span; use common::WithLocation; -use docblock_shared::ResolverSourceHash; use docblock_shared::ARGUMENT_DEFINITIONS; use docblock_shared::ARGUMENT_TYPE; use docblock_shared::DEFAULT_VALUE; use docblock_shared::KEY_RESOLVER_ID_FIELD; use docblock_shared::PROVIDER_ARG_NAME; -use graphql_ir::reexport::StringKey; +use docblock_shared::ResolverSourceHash; use graphql_ir::FragmentDefinitionName; -use graphql_syntax::parse_field_definition; -use graphql_syntax::parse_field_definition_stub; -use graphql_syntax::parse_identifier; -use graphql_syntax::parse_identifier_and_implements_interfaces; -use graphql_syntax::parse_type; +use graphql_ir::reexport::StringKey; use graphql_syntax::ConstantValue; use graphql_syntax::ExecutableDefinition; use graphql_syntax::FieldDefinition; @@ -38,10 +33,21 @@ use graphql_syntax::StringNode; use graphql_syntax::Token; use graphql_syntax::TokenKind; use graphql_syntax::TypeAnnotation; -use intern::string_key::Intern; +use graphql_syntax::parse_field_definition; +use graphql_syntax::parse_field_definition_stub; +use graphql_syntax::parse_identifier; +use graphql_syntax::parse_identifier_and_implements_interfaces; +use graphql_syntax::parse_type; use intern::Lookup; +use intern::string_key::Intern; use relay_config::ProjectName; +use crate::DocblockIr; +use crate::LegacyVerboseResolverIr; +use crate::On; +use crate::ParseOptions; +use crate::ResolverFieldDocblockIr; +use crate::ResolverTypeDocblockIr; use crate::errors::ErrorMessagesWithData; use crate::errors::IrParsingErrorMessages; use crate::ir::Argument; @@ -54,12 +60,6 @@ use crate::ir::UnpopulatedIrField; use crate::ir::WeakObjectIr; use crate::untyped_representation::AllowedFieldName; use crate::untyped_representation::UntypedDocblockRepresentation; -use crate::DocblockIr; -use crate::LegacyVerboseResolverIr; -use crate::On; -use crate::ParseOptions; -use crate::ResolverFieldDocblockIr; -use crate::ResolverTypeDocblockIr; pub(crate) fn parse_docblock_ir( project_name: &ProjectName, @@ -404,6 +404,7 @@ fn parse_terse_relay_resolver_ir( fragment_arguments, source_hash, type_confirmed: false, + property_lookup_name: None, }) } diff --git a/compiler/crates/relay-docblock/src/errors.rs b/compiler/crates/relay-docblock/src/errors.rs index b782474acede0..0fce133794f9b 100644 --- a/compiler/crates/relay-docblock/src/errors.rs +++ b/compiler/crates/relay-docblock/src/errors.rs @@ -12,9 +12,9 @@ use intern::string_key::StringKey; use schema::suggestion_list::did_you_mean; use thiserror::Error; -use crate::untyped_representation::AllowedFieldName; use crate::ON_INTERFACE_FIELD; use crate::ON_TYPE_FIELD; +use crate::untyped_representation::AllowedFieldName; #[derive( Clone, @@ -203,6 +203,11 @@ pub enum SchemaValidationErrorMessages { resolver_field_name: StringKey, actual_return_type: StringKey, }, + + #[error( + "Relay Resolvers that return weak types defined in client schema extensions are not supported. Prefer defining the return type using a `@weak` Relay Resolver type: https://relay.dev/docs/next/guides/relay-resolvers/defining-types/#defining-a-weak-type" + )] + ClientEdgeToClientWeakType, } #[derive( diff --git a/compiler/crates/relay-docblock/src/ir.rs b/compiler/crates/relay-docblock/src/ir.rs index c31c728ffa778..d54afbf27166d 100644 --- a/compiler/crates/relay-docblock/src/ir.rs +++ b/compiler/crates/relay-docblock/src/ir.rs @@ -18,7 +18,6 @@ use common::NamedItem; use common::ObjectName; use common::Span; use common::WithLocation; -use docblock_shared::ResolverSourceHash; use docblock_shared::FRAGMENT_KEY_ARGUMENT_NAME; use docblock_shared::GENERATED_FRAGMENT_ARGUMENT_NAME; use docblock_shared::HAS_OUTPUT_TYPE_ARGUMENT_NAME; @@ -28,11 +27,14 @@ use docblock_shared::INJECT_FRAGMENT_DATA_ARGUMENT_NAME; use docblock_shared::LIVE_ARGUMENT_NAME; use docblock_shared::RELAY_RESOLVER_DIRECTIVE_NAME; use docblock_shared::RELAY_RESOLVER_MODEL_DIRECTIVE_NAME; +use docblock_shared::RELAY_RESOLVER_MODEL_GENERATED_ID_FIELD_DIRECTIVE_NAME; use docblock_shared::RELAY_RESOLVER_MODEL_INSTANCE_FIELD; use docblock_shared::RELAY_RESOLVER_SOURCE_HASH; use docblock_shared::RELAY_RESOLVER_SOURCE_HASH_VALUE; use docblock_shared::RELAY_RESOLVER_WEAK_OBJECT_DIRECTIVE; +use docblock_shared::RESOLVER_PROPERTY_LOOKUP_NAME; use docblock_shared::RESOLVER_VALUE_SCALAR_NAME; +use docblock_shared::ResolverSourceHash; use docblock_shared::TYPE_CONFIRMED_ARGUMENT_NAME; use graphql_ir::FragmentDefinitionName; use graphql_syntax::BooleanNode; @@ -65,13 +67,13 @@ use relay_config::SchemaConfig; use relay_schema::CUSTOM_SCALAR_DIRECTIVE_NAME; use relay_schema::EXPORT_NAME_CUSTOM_SCALAR_ARGUMENT_NAME; use relay_schema::PATH_CUSTOM_SCALAR_ARGUMENT_NAME; -use schema::suggestion_list::GraphQLSuggestions; use schema::InterfaceID; use schema::Object; use schema::ObjectID; use schema::SDLSchema; use schema::Schema; use schema::Type; +use schema::suggestion_list::GraphQLSuggestions; use crate::errors::ErrorMessagesWithData; use crate::errors::SchemaValidationErrorMessages; @@ -373,6 +375,7 @@ trait ResolverIr: Sized { fn source_hash(&self) -> ResolverSourceHash; fn semantic_non_null(&self) -> Option; fn type_confirmed(&self) -> bool; + fn property_lookup_name(&self) -> Option>; fn to_graphql_schema_ast( self, @@ -514,7 +517,13 @@ trait ResolverIr: Sized { } } } - + let property_lookup = self.property_lookup_name(); + if let Some(property_lookup) = property_lookup { + arguments.push(string_argument( + RESOLVER_PROPERTY_LOOKUP_NAME.0, + property_lookup, + )); + } let schema = project_config.schema; if let Some(output_type) = self.output_type() { @@ -821,6 +830,7 @@ pub struct TerseRelayResolverIr { /// Indicates that the extraction method used has already validated that the /// implementaiton matches the GraphQL types. pub type_confirmed: bool, + pub property_lookup_name: Option>, } impl ResolverIr for TerseRelayResolverIr { @@ -926,6 +936,10 @@ impl ResolverIr for TerseRelayResolverIr { fn type_confirmed(&self) -> bool { self.type_confirmed } + + fn property_lookup_name(&self) -> Option> { + self.property_lookup_name + } } impl ResolverTypeDefinitionIr for TerseRelayResolverIr { @@ -1118,6 +1132,10 @@ impl ResolverIr for LegacyVerboseResolverIr { fn type_confirmed(&self) -> bool { false } + + fn property_lookup_name(&self) -> Option> { + None + } } impl ResolverTypeDefinitionIr for LegacyVerboseResolverIr { @@ -1177,7 +1195,14 @@ impl StrongObjectIr { exclamation: dummy_token(span), })), arguments: None, - directives: vec![], + directives: vec![ConstantDirective { + span, + at: dummy_token(span), + name: string_key_as_identifier( + RELAY_RESOLVER_MODEL_GENERATED_ID_FIELD_DIRECTIVE_NAME.0, + ), + arguments: None, + }], description: None, hack_source: None, span, @@ -1267,6 +1292,10 @@ impl ResolverIr for StrongObjectIr { fn type_confirmed(&self) -> bool { self.type_confirmed } + + fn property_lookup_name(&self) -> Option> { + None + } } /// Relay Resolver docblock representing a "model" type for a weak object @@ -1456,6 +1485,10 @@ impl ResolverIr for WeakObjectIr { fn type_confirmed(&self) -> bool { self.type_confirmed } + + fn property_lookup_name(&self) -> Option> { + None + } } fn string_argument(name: StringKey, value: WithLocation) -> ConstantArgument { diff --git a/compiler/crates/relay-docblock/src/untyped_representation.rs b/compiler/crates/relay-docblock/src/untyped_representation.rs index bf209a3f9db7a..bc0f9c0b9ea63 100644 --- a/compiler/crates/relay-docblock/src/untyped_representation.rs +++ b/compiler/crates/relay-docblock/src/untyped_representation.rs @@ -5,8 +5,8 @@ * LICENSE file in the root directory of this source tree. */ -use std::collections::hash_map::Entry; use std::collections::HashMap; +use std::collections::hash_map::Entry; use std::fmt::Display; use std::hash::Hash; @@ -17,8 +17,6 @@ use docblock_syntax::DocblockAST; use docblock_syntax::DocblockSection; use graphql_ir::reexport::StringKey; -use crate::errors::UntypedRepresentationErrorMessages; -use crate::ir::IrField; use crate::DEPRECATED_FIELD; use crate::EDGE_TO_FIELD; use crate::EMPTY_STRING; @@ -31,6 +29,8 @@ use crate::RELAY_RESOLVER_FIELD; use crate::ROOT_FRAGMENT_FIELD; use crate::SEMANTIC_NON_NULL_FIELD; use crate::WEAK_FIELD; +use crate::errors::UntypedRepresentationErrorMessages; +use crate::ir::IrField; /// All fields which are allowed in RelayResolver docblocks. #[derive( diff --git a/compiler/crates/relay-docblock/src/validate_resolver_schema.rs b/compiler/crates/relay-docblock/src/validate_resolver_schema.rs index 9c6ed78ddbb82..a9eed70e6ea9b 100644 --- a/compiler/crates/relay-docblock/src/validate_resolver_schema.rs +++ b/compiler/crates/relay-docblock/src/validate_resolver_schema.rs @@ -7,14 +7,16 @@ use common::Diagnostic; use common::DiagnosticsResult; +use common::FeatureFlag; use common::FeatureFlags; use common::NamedItem; +use docblock_shared::HAS_OUTPUT_TYPE_ARGUMENT_NAME; use docblock_shared::KEY_RESOLVER_ID_FIELD; use docblock_shared::RELAY_RESOLVER_DIRECTIVE_NAME; use docblock_shared::RELAY_RESOLVER_MODEL_DIRECTIVE_NAME; use docblock_shared::RELAY_RESOLVER_WEAK_OBJECT_DIRECTIVE; -use errors::try2; use errors::try_all; +use errors::try3; use schema::Object; use schema::SDLSchema; use schema::Schema; @@ -27,8 +29,9 @@ pub fn validate_resolver_schema( schema: &SDLSchema, feature_flags: &FeatureFlags, ) -> DiagnosticsResult<()> { - try2( + try3( validate_strong_resolver_types(schema), + validate_output_type_resolver_types(schema, &feature_flags.allow_output_type_resolvers), validate_mutation_resolvers(schema, feature_flags.enable_relay_resolver_mutations), )?; @@ -50,6 +53,41 @@ fn validate_strong_resolver_types(schema: &SDLSchema) -> DiagnosticsResult<()> { Ok(()) } +fn validate_output_type_resolver_types( + schema: &SDLSchema, + allow_output_type_resolvers: &FeatureFlag, +) -> DiagnosticsResult<()> { + try_all(schema.fields().map(|field| -> DiagnosticsResult<()> { + if let Some(resolver_directive) = field.directives.named(*RELAY_RESOLVER_DIRECTIVE_NAME) { + if resolver_directive + .arguments + .named(*HAS_OUTPUT_TYPE_ARGUMENT_NAME) + .is_some() + { + if let Some(obj_id) = field.type_.inner().get_object_id() { + if schema + .object(obj_id) + .directives + .named(*RELAY_RESOLVER_WEAK_OBJECT_DIRECTIVE) + .is_none() + { + if !allow_output_type_resolvers.is_enabled_for(field.name.item) { + return DiagnosticsResult::Err(vec![Diagnostic::error( + SchemaValidationErrorMessages::ClientEdgeToClientWeakType, + field.name.location, + )]); + } + } + } + } + }; + + Ok(()) + }))?; + + Ok(()) +} + fn object_is_strong_model_type(object: &Object) -> bool { if !object.is_extension { return false; @@ -125,7 +163,7 @@ fn is_valid_mutation_resolver_return_type(type_: &TypeReference) -> bool { TypeReference::NonNull(non_null_type) => { // note: this should be unreachable since we already disallow relay resolvers to return non-nullable types // - implement this anyway in case that changes in the future - return is_valid_mutation_resolver_return_type(non_null_type.as_ref()); + is_valid_mutation_resolver_return_type(non_null_type.as_ref()) } } } diff --git a/compiler/crates/relay-docblock/tests/parse.rs b/compiler/crates/relay-docblock/tests/parse.rs index df04c5a0f0cd1..e730bb9cfbf78 100644 --- a/compiler/crates/relay-docblock/tests/parse.rs +++ b/compiler/crates/relay-docblock/tests/parse.rs @@ -12,12 +12,12 @@ use docblock_syntax::parse_docblock; use extract_graphql::JavaScriptSourceFeature; use fixture_tests::Fixture; use graphql_cli::DiagnosticPrinter; -use graphql_syntax::parse_executable; use graphql_syntax::ExecutableDefinition; +use graphql_syntax::parse_executable; use intern::string_key::Intern; use relay_config::ProjectName; -use relay_docblock::parse_docblock_ast; use relay_docblock::ParseOptions; +use relay_docblock::parse_docblock_ast; pub async fn transform_fixture(fixture: &Fixture<'_>) -> Result { let js_features = extract_graphql::extract(fixture.content); diff --git a/compiler/crates/relay-docblock/tests/parse/fixtures/terse-relay-resolver-args.expected b/compiler/crates/relay-docblock/tests/parse/fixtures/terse-relay-resolver-args.expected index 98ec348e24bfd..adb2ec5cdcbec 100644 --- a/compiler/crates/relay-docblock/tests/parse/fixtures/terse-relay-resolver-args.expected +++ b/compiler/crates/relay-docblock/tests/parse/fixtures/terse-relay-resolver-args.expected @@ -128,6 +128,7 @@ Field( "ff0b47b51f0011ae9def59af3e3792a3", ), type_confirmed: false, + property_lookup_name: None, }, ), ) diff --git a/compiler/crates/relay-docblock/tests/parse/fixtures/terse-relay-resolver-disallow-non-nullable-list-item.expected b/compiler/crates/relay-docblock/tests/parse/fixtures/terse-relay-resolver-disallow-non-nullable-list-item.expected index 23601aad555b3..a9482f9f75107 100644 --- a/compiler/crates/relay-docblock/tests/parse/fixtures/terse-relay-resolver-disallow-non-nullable-list-item.expected +++ b/compiler/crates/relay-docblock/tests/parse/fixtures/terse-relay-resolver-disallow-non-nullable-list-item.expected @@ -85,6 +85,7 @@ Field( "12e28b8739ff3ab018186a28de3ca726", ), type_confirmed: false, + property_lookup_name: None, }, ), ) diff --git a/compiler/crates/relay-docblock/tests/parse/fixtures/terse-relay-resolver-non-nullable.expected b/compiler/crates/relay-docblock/tests/parse/fixtures/terse-relay-resolver-non-nullable.expected index 356d89db6d081..fd91ed50f3db5 100644 --- a/compiler/crates/relay-docblock/tests/parse/fixtures/terse-relay-resolver-non-nullable.expected +++ b/compiler/crates/relay-docblock/tests/parse/fixtures/terse-relay-resolver-non-nullable.expected @@ -74,6 +74,7 @@ Field( "ac789e28bceef3eeaab77ae5203f43a6", ), type_confirmed: false, + property_lookup_name: None, }, ), ) diff --git a/compiler/crates/relay-docblock/tests/parse/fixtures/terse-relay-resolver-semantic-non-null.expected b/compiler/crates/relay-docblock/tests/parse/fixtures/terse-relay-resolver-semantic-non-null.expected index f7dc9d7978a15..f40b2d5120fdd 100644 --- a/compiler/crates/relay-docblock/tests/parse/fixtures/terse-relay-resolver-semantic-non-null.expected +++ b/compiler/crates/relay-docblock/tests/parse/fixtures/terse-relay-resolver-semantic-non-null.expected @@ -114,6 +114,7 @@ Field( "ba2a3b6d7c4294fef33f921df3b20065", ), type_confirmed: false, + property_lookup_name: None, }, ), ) diff --git a/compiler/crates/relay-docblock/tests/parse/fixtures/terse-relay-resolver.expected b/compiler/crates/relay-docblock/tests/parse/fixtures/terse-relay-resolver.expected index eced4a153ac1e..e0f413dae3f19 100644 --- a/compiler/crates/relay-docblock/tests/parse/fixtures/terse-relay-resolver.expected +++ b/compiler/crates/relay-docblock/tests/parse/fixtures/terse-relay-resolver.expected @@ -80,6 +80,7 @@ Field( "0a9950ad1f952f5777b27604738fcf91", ), type_confirmed: false, + property_lookup_name: None, }, ), ) diff --git a/compiler/crates/relay-docblock/tests/to_schema.rs b/compiler/crates/relay-docblock/tests/to_schema.rs index 9669b8e3f0993..00c43d4a41239 100644 --- a/compiler/crates/relay-docblock/tests/to_schema.rs +++ b/compiler/crates/relay-docblock/tests/to_schema.rs @@ -10,19 +10,19 @@ use std::sync::Arc; use common::DiagnosticsResult; use common::FeatureFlag; use common::SourceLocationKey; -use docblock_syntax::parse_docblock; use docblock_syntax::DocblockSource; +use docblock_syntax::parse_docblock; use extract_graphql::JavaScriptSourceFeature; use fixture_tests::Fixture; -use graphql_syntax::parse_executable; use graphql_syntax::ExecutableDefinition; +use graphql_syntax::parse_executable; use graphql_test_helpers::diagnostics_to_sorted_string; use intern::string_key::Intern; use relay_config::ProjectName; +use relay_docblock::ParseOptions; use relay_docblock::extend_schema_with_resolver_type_system_definition; use relay_docblock::parse_docblock_ast; use relay_docblock::validate_resolver_schema; -use relay_docblock::ParseOptions; use relay_test_schema::get_test_schema_with_extensions; use schema::SDLSchema; @@ -39,8 +39,7 @@ pub async fn transform_fixture(fixture: &Fixture<'_>) -> Result let executable_documents = js_features .iter() - .enumerate() - .filter_map(|(_, source)| match source { + .filter_map(|source| match source { JavaScriptSourceFeature::GraphQL(source) => Some( parse_executable(&source.text_source().text, SourceLocationKey::Generated) .map_err(|diagnostics| { diff --git a/compiler/crates/relay-docblock/tests/to_schema/fixtures/legacy-relay-resolver-with-root-fragment-on-model.expected b/compiler/crates/relay-docblock/tests/to_schema/fixtures/legacy-relay-resolver-with-root-fragment-on-model.expected index f3a1b885e0429..13481f35cc57f 100644 --- a/compiler/crates/relay-docblock/tests/to_schema/fixtures/legacy-relay-resolver-with-root-fragment-on-model.expected +++ b/compiler/crates/relay-docblock/tests/to_schema/fixtures/legacy-relay-resolver-with-root-fragment-on-model.expected @@ -26,7 +26,7 @@ graphql` ` ==================================== OUTPUT =================================== type MyType @__RelayResolverModel { - id: ID! + id: ID! @__RelayResolverModelGeneratedIDField __relay_model_instance: RelayResolverValue! @relay_resolver(fragment_name: "MyType__id", generated_fragment: true, inject_fragment_data: "id", import_name: "MyType", import_path: "/path/to/test/fixture/legacy-relay-resolver-with-root-fragment-on-model.js") @resolver_source_hash(value: "b81f253a757aaba36955be6d8e224c2a") @unselectable(reason: "This field is intended only for Relay's internal use") } diff --git a/compiler/crates/relay-docblock/tests/to_schema/fixtures/relay-resolver-strong-object-with-implements-interface-non-interface.expected b/compiler/crates/relay-docblock/tests/to_schema/fixtures/relay-resolver-strong-object-with-implements-interface-non-interface.expected index 7ba9b9b2738fc..5a69e3acd90db 100644 --- a/compiler/crates/relay-docblock/tests/to_schema/fixtures/relay-resolver-strong-object-with-implements-interface-non-interface.expected +++ b/compiler/crates/relay-docblock/tests/to_schema/fixtures/relay-resolver-strong-object-with-implements-interface-non-interface.expected @@ -32,4 +32,4 @@ interface IFoo { ℹ︎ the other type is defined here :1:1 -Internal error: Unable to print source, start index (19563) out of range. +Internal error: Unable to print source, start index (19544) out of range. diff --git a/compiler/crates/relay-docblock/tests/to_schema/fixtures/relay-resolver-strong-object-with-implements.expected b/compiler/crates/relay-docblock/tests/to_schema/fixtures/relay-resolver-strong-object-with-implements.expected index 5c2c1ede03bd3..170f1af4b2075 100644 --- a/compiler/crates/relay-docblock/tests/to_schema/fixtures/relay-resolver-strong-object-with-implements.expected +++ b/compiler/crates/relay-docblock/tests/to_schema/fixtures/relay-resolver-strong-object-with-implements.expected @@ -20,6 +20,6 @@ interface IFoo { ` ==================================== OUTPUT =================================== type ClientUser implements IFoo @__RelayResolverModel { - id: ID! + id: ID! @__RelayResolverModelGeneratedIDField __relay_model_instance: RelayResolverValue! @relay_resolver(fragment_name: "ClientUser__id", generated_fragment: true, inject_fragment_data: "id", import_name: "ClientUser", import_path: "/path/to/test/fixture/relay-resolver-strong-object-with-implements.js") @resolver_source_hash(value: "76be3b85f11135352a0d3a5726418956") @unselectable(reason: "This field is intended only for Relay's internal use") } diff --git a/compiler/crates/relay-docblock/tests/to_schema/fixtures/relay-resolver-strong-object-with-multiple-implements.expected b/compiler/crates/relay-docblock/tests/to_schema/fixtures/relay-resolver-strong-object-with-multiple-implements.expected index 9c25186d33d87..365179a1937dd 100644 --- a/compiler/crates/relay-docblock/tests/to_schema/fixtures/relay-resolver-strong-object-with-multiple-implements.expected +++ b/compiler/crates/relay-docblock/tests/to_schema/fixtures/relay-resolver-strong-object-with-multiple-implements.expected @@ -26,6 +26,6 @@ interface IBar { ` ==================================== OUTPUT =================================== type ClientUser implements IFoo & IBar @__RelayResolverModel { - id: ID! + id: ID! @__RelayResolverModelGeneratedIDField __relay_model_instance: RelayResolverValue! @relay_resolver(fragment_name: "ClientUser__id", generated_fragment: true, inject_fragment_data: "id", import_name: "ClientUser", import_path: "/path/to/test/fixture/relay-resolver-strong-object-with-multiple-implements.js") @resolver_source_hash(value: "1b7346b6155a43514be2946721ff59fb") @unselectable(reason: "This field is intended only for Relay's internal use") } diff --git a/compiler/crates/relay-docblock/tests/to_schema/fixtures/relay-resolver-strong-object.expected b/compiler/crates/relay-docblock/tests/to_schema/fixtures/relay-resolver-strong-object.expected index 4ef88ea2cb954..b549ce206784c 100644 --- a/compiler/crates/relay-docblock/tests/to_schema/fixtures/relay-resolver-strong-object.expected +++ b/compiler/crates/relay-docblock/tests/to_schema/fixtures/relay-resolver-strong-object.expected @@ -11,6 +11,6 @@ */ ==================================== OUTPUT =================================== type ClientUser @__RelayResolverModel { - id: ID! + id: ID! @__RelayResolverModelGeneratedIDField __relay_model_instance: RelayResolverValue! @relay_resolver(fragment_name: "ClientUser__id", generated_fragment: true, inject_fragment_data: "id", import_name: "ClientUser", import_path: "/path/to/test/fixture/relay-resolver-strong-object.js") @resolver_source_hash(value: "b1c8ae1937aed7425f5a87a4762ad83d") @unselectable(reason: "This field is intended only for Relay's internal use") } diff --git a/compiler/crates/relay-docblock/tests/to_schema/fixtures/terse-relay-resolver-with-output-type.expected b/compiler/crates/relay-docblock/tests/to_schema/fixtures/terse-relay-resolver-with-output-type.invalid.expected similarity index 57% rename from compiler/crates/relay-docblock/tests/to_schema/fixtures/terse-relay-resolver-with-output-type.expected rename to compiler/crates/relay-docblock/tests/to_schema/fixtures/terse-relay-resolver-with-output-type.invalid.expected index 09202a14a709a..aa18b96ced1db 100644 --- a/compiler/crates/relay-docblock/tests/to_schema/fixtures/terse-relay-resolver-with-output-type.expected +++ b/compiler/crates/relay-docblock/tests/to_schema/fixtures/terse-relay-resolver-with-output-type.invalid.expected @@ -6,6 +6,8 @@ * LICENSE file in the root directory of this source tree. */ +// expected-to-throw + /** * @RelayResolver User.favorite_page: ClientPage * @rootFragment myRootFragment @@ -29,7 +31,11 @@ graphql` foo: String } ` -==================================== OUTPUT =================================== -extend type User { - favorite_page: ClientPage @relay_resolver(has_output_type: true, import_name: "favorite_page", import_path: "/path/to/test/fixture/terse-relay-resolver-with-output-type.js", fragment_name: "myRootFragment") @resolver_source_hash(value: "6debf0d3b679b66e8d0c58dbdb4d422d") -} +==================================== ERROR ==================================== +✖︎ Relay Resolvers that return weak types defined in client schema extensions are not supported. Prefer defining the return type using a `@weak` Relay Resolver type: https://relay.dev/docs/next/guides/relay-resolvers/defining-types/#defining-a-weak-type + + /path/to/test/fixture/terse-relay-resolver-with-output-type.invalid.js:2:24 + 1 │ * + 2 │ * @RelayResolver User.favorite_page: ClientPage + │ ^^^^^^^^^^^^^ + 3 │ * @rootFragment myRootFragment diff --git a/compiler/crates/relay-docblock/tests/to_schema/fixtures/terse-relay-resolver-with-output-type.js b/compiler/crates/relay-docblock/tests/to_schema/fixtures/terse-relay-resolver-with-output-type.invalid.js similarity index 96% rename from compiler/crates/relay-docblock/tests/to_schema/fixtures/terse-relay-resolver-with-output-type.js rename to compiler/crates/relay-docblock/tests/to_schema/fixtures/terse-relay-resolver-with-output-type.invalid.js index 87940daaae533..b509600011890 100644 --- a/compiler/crates/relay-docblock/tests/to_schema/fixtures/terse-relay-resolver-with-output-type.js +++ b/compiler/crates/relay-docblock/tests/to_schema/fixtures/terse-relay-resolver-with-output-type.invalid.js @@ -5,6 +5,8 @@ * LICENSE file in the root directory of this source tree. */ +// expected-to-throw + /** * @RelayResolver User.favorite_page: ClientPage * @rootFragment myRootFragment diff --git a/compiler/crates/relay-docblock/tests/to_schema/fixtures/terse-relay-resolver-with-root-fragment-on-model.expected b/compiler/crates/relay-docblock/tests/to_schema/fixtures/terse-relay-resolver-with-root-fragment-on-model.expected index ac003540d6f49..ed4f871311cd0 100644 --- a/compiler/crates/relay-docblock/tests/to_schema/fixtures/terse-relay-resolver-with-root-fragment-on-model.expected +++ b/compiler/crates/relay-docblock/tests/to_schema/fixtures/terse-relay-resolver-with-root-fragment-on-model.expected @@ -22,7 +22,7 @@ graphql` ` ==================================== OUTPUT =================================== type MyType @__RelayResolverModel { - id: ID! + id: ID! @__RelayResolverModelGeneratedIDField __relay_model_instance: RelayResolverValue! @relay_resolver(fragment_name: "MyType__id", generated_fragment: true, inject_fragment_data: "id", import_name: "MyType", import_path: "/path/to/test/fixture/terse-relay-resolver-with-root-fragment-on-model.js") @resolver_source_hash(value: "b81f253a757aaba36955be6d8e224c2a") @unselectable(reason: "This field is intended only for Relay's internal use") } diff --git a/compiler/crates/relay-docblock/tests/to_schema_test.rs b/compiler/crates/relay-docblock/tests/to_schema_test.rs index 8e46b4bcb49a5..de2560932a942 100644 --- a/compiler/crates/relay-docblock/tests/to_schema_test.rs +++ b/compiler/crates/relay-docblock/tests/to_schema_test.rs @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<6de39460e884e377d74e072feff9f740>> + * @generated SignedSource<<884f289d16217b8cae932ebeb6a182f2>> */ mod to_schema; @@ -251,10 +251,10 @@ async fn terse_relay_resolver_union() { } #[tokio::test] -async fn terse_relay_resolver_with_output_type() { - let input = include_str!("to_schema/fixtures/terse-relay-resolver-with-output-type.js"); - let expected = include_str!("to_schema/fixtures/terse-relay-resolver-with-output-type.expected"); - test_fixture(transform_fixture, file!(), "terse-relay-resolver-with-output-type.js", "to_schema/fixtures/terse-relay-resolver-with-output-type.expected", input, expected).await; +async fn terse_relay_resolver_with_output_type_invalid() { + let input = include_str!("to_schema/fixtures/terse-relay-resolver-with-output-type.invalid.js"); + let expected = include_str!("to_schema/fixtures/terse-relay-resolver-with-output-type.invalid.expected"); + test_fixture(transform_fixture, file!(), "terse-relay-resolver-with-output-type.invalid.js", "to_schema/fixtures/terse-relay-resolver-with-output-type.invalid.expected", input, expected).await; } #[tokio::test] diff --git a/compiler/crates/relay-lsp/Cargo.toml b/compiler/crates/relay-lsp/Cargo.toml index 873106de93069..5e78e00c50673 100644 --- a/compiler/crates/relay-lsp/Cargo.toml +++ b/compiler/crates/relay-lsp/Cargo.toml @@ -4,7 +4,7 @@ name = "relay-lsp" version = "0.0.0" authors = ["Facebook"] -edition = "2021" +edition = "2024" repository = "https://github.com/facebook/relay" license = "MIT" @@ -18,7 +18,7 @@ crossbeam = "0.8" dashmap = { version = "5.5.3", features = ["rayon", "serde"] } docblock-shared = { path = "../docblock-shared" } docblock-syntax = { path = "../docblock-syntax" } -dunce = "1.0.2" +dunce = "1.0.5" extract-graphql = { path = "../extract-graphql" } env_logger = "0.7" fnv = "1.0" @@ -27,9 +27,9 @@ graphql-syntax = { path = "../graphql-syntax" } graphql-text-printer = { path = "../graphql-text-printer" } graphql-watchman = { path = "../graphql-watchman" } intern = { path = "../intern" } -itertools = "0.13.0" -lazy_static = "1.4" -log = { version = "0.4.22", features = ["kv_unstable"] } +itertools = "0.14.0" +lazy_static = "1.5" +log = { version = "0.4.27", features = ["kv_unstable", "kv_unstable_std"] } lsp-server = "0.7.2" lsp-types = "0.94.1" percent-encoding = "2.1" @@ -44,9 +44,9 @@ schema = { path = "../schema" } schema-diff = { path = "../schema-diff" } schema-documentation = { path = "../schema-documentation" } schema-print = { path = "../schema-print" } -serde = { version = "1.0.185", features = ["derive", "rc"] } -serde_json = { version = "1.0.132", features = ["float_roundtrip", "unbounded_depth"] } -tokio = { version = "1.41.0", features = ["full", "test-util", "tracing"] } +serde = { version = "1.0.219", features = ["derive", "rc"] } +serde_json = { version = "1.0.140", features = ["alloc", "float_roundtrip", "unbounded_depth"] } +tokio = { version = "1.46.1", features = ["full", "test-util", "tracing"] } [dev-dependencies] fixture-tests = { path = "../fixture-tests" } diff --git a/compiler/crates/relay-lsp/src/client.rs b/compiler/crates/relay-lsp/src/client.rs index 9006f15f65e89..c33c744460e14 100644 --- a/compiler/crates/relay-lsp/src/client.rs +++ b/compiler/crates/relay-lsp/src/client.rs @@ -17,13 +17,13 @@ use lsp_server::Request as ServerRequest; #[cfg(test)] use lsp_server::RequestId; #[cfg(test)] -use lsp_types::request::Initialize; -#[cfg(test)] -use lsp_types::request::Request; -#[cfg(test)] use lsp_types::InitializeParams; #[cfg(test)] use lsp_types::InitializedParams; +#[cfg(test)] +use lsp_types::request::Initialize; +#[cfg(test)] +use lsp_types::request::Request; #[cfg(test)] pub fn initialize(client: &Connection, params: &InitializeParams, request_id: u64) { diff --git a/compiler/crates/relay-lsp/src/code_action.rs b/compiler/crates/relay-lsp/src/code_action.rs index 0226aa2300831..18bbb74d9d830 100644 --- a/compiler/crates/relay-lsp/src/code_action.rs +++ b/compiler/crates/relay-lsp/src/code_action.rs @@ -11,15 +11,13 @@ use std::collections::HashMap; use std::collections::HashSet; use common::Span; +use create_name_suggestion::DefinitionNameSuffix; use create_name_suggestion::create_default_name; use create_name_suggestion::create_default_name_with_index; use create_name_suggestion::create_impactful_name; use create_name_suggestion::create_name_wrapper; -use create_name_suggestion::DefinitionNameSuffix; use graphql_syntax::ExecutableDefinition; use intern::Lookup; -use lsp_types::request::CodeActionRequest; -use lsp_types::request::Request; use lsp_types::CodeAction; use lsp_types::CodeActionOrCommand; use lsp_types::Diagnostic; @@ -29,6 +27,8 @@ use lsp_types::TextDocumentPositionParams; use lsp_types::TextEdit; use lsp_types::Url; use lsp_types::WorkspaceEdit; +use lsp_types::request::CodeActionRequest; +use lsp_types::request::Request; use resolution_path::FragmentDefinitionPath; use resolution_path::IdentParent; use resolution_path::IdentPath; @@ -44,7 +44,7 @@ use crate::lsp_runtime_error::LSPRuntimeResult; use crate::server::GlobalState; use crate::utils::is_file_uri_in_dir; -pub(crate) fn on_code_action( +pub fn on_code_action( state: &impl GlobalState, params: ::Params, ) -> LSPRuntimeResult<::Result> { diff --git a/compiler/crates/relay-lsp/src/code_action/create_name_suggestion.rs b/compiler/crates/relay-lsp/src/code_action/create_name_suggestion.rs index 6d1f621ad462b..74e3b0788b96f 100644 --- a/compiler/crates/relay-lsp/src/code_action/create_name_suggestion.rs +++ b/compiler/crates/relay-lsp/src/code_action/create_name_suggestion.rs @@ -205,10 +205,10 @@ fn create_impactful_part() -> String { mod tests { use std::collections::HashSet; + use super::DefinitionNameSuffix; use super::create_default_name; use super::create_default_name_with_index; use super::create_name_wrapper; - use super::DefinitionNameSuffix; #[test] fn test_create_default_name() { @@ -330,8 +330,8 @@ mod tests { #[cfg(windows)] #[cfg(test)] mod tests { - use super::create_default_name; use super::DefinitionNameSuffix; + use super::create_default_name; #[test] fn test_create_default_name() { diff --git a/compiler/crates/relay-lsp/src/completion.rs b/compiler/crates/relay-lsp/src/completion.rs index 3f09db9a8c1b1..e287e93bfa210 100644 --- a/compiler/crates/relay-lsp/src/completion.rs +++ b/compiler/crates/relay-lsp/src/completion.rs @@ -13,12 +13,12 @@ use common::Named; use common::NamedItem; use common::Span; use fnv::FnvHashSet; +use graphql_ir::DIRECTIVE_ARGUMENTS; use graphql_ir::FragmentDefinitionName; use graphql_ir::OperationDefinitionName; use graphql_ir::Program; use graphql_ir::VariableDefinition; use graphql_ir::VariableName; -use graphql_ir::DIRECTIVE_ARGUMENTS; use graphql_syntax::Argument; use graphql_syntax::ConstantValue; use graphql_syntax::Directive; @@ -34,12 +34,9 @@ use graphql_syntax::ScalarField; use graphql_syntax::Selection; use graphql_syntax::TokenKind; use graphql_syntax::Value; -use intern::string_key::StringKey; use intern::Lookup; +use intern::string_key::StringKey; use log::debug; -use lsp_types::request::Completion; -use lsp_types::request::Request; -use lsp_types::request::ResolveCompletionItem; use lsp_types::CompletionItem; use lsp_types::CompletionItemKind; use lsp_types::CompletionResponse; @@ -47,6 +44,9 @@ use lsp_types::Documentation; use lsp_types::InsertTextFormat; use lsp_types::MarkupContent; use lsp_types::MarkupKind; +use lsp_types::request::Completion; +use lsp_types::request::Request; +use lsp_types::request::ResolveCompletionItem; use schema::Argument as SchemaArgument; use schema::Directive as SchemaDirective; use schema::InputObject; @@ -58,12 +58,12 @@ use schema::Type; use schema::TypeReference; use schema::TypeWithFields; +use crate::LSPRuntimeError; +use crate::SchemaDocumentation; use crate::lsp_runtime_error::LSPRuntimeResult; use crate::node_resolution_info::TypePath; use crate::node_resolution_info::TypePathItem; use crate::server::GlobalState; -use crate::LSPRuntimeError; -use crate::SchemaDocumentation; #[derive(Debug, Clone)] pub enum CompletionKind { diff --git a/compiler/crates/relay-lsp/src/completion/test.rs b/compiler/crates/relay-lsp/src/completion/test.rs index 5f2d8f40ec84e..6c402d7349e24 100644 --- a/compiler/crates/relay-lsp/src/completion/test.rs +++ b/compiler/crates/relay-lsp/src/completion/test.rs @@ -11,8 +11,8 @@ use std::sync::Arc; use common::SourceLocationKey; use common::Span; -use graphql_ir::build; use graphql_ir::Program; +use graphql_ir::build; use graphql_syntax::parse_executable; use graphql_syntax::parse_executable_with_error_recovery; use intern::string_key::Intern; diff --git a/compiler/crates/relay-lsp/src/diagnostic_reporter.rs b/compiler/crates/relay-lsp/src/diagnostic_reporter.rs index 7836d690fe1fa..f918aa222f03d 100644 --- a/compiler/crates/relay-lsp/src/diagnostic_reporter.rs +++ b/compiler/crates/relay-lsp/src/diagnostic_reporter.rs @@ -9,20 +9,18 @@ use std::path::Path; use std::path::PathBuf; -use common::get_diagnostics_data; use common::Diagnostic as CompilerDiagnostic; use common::DiagnosticRelatedInformation; use common::Location; use common::TextSource; +use common::get_diagnostics_data; use crossbeam::channel::Sender; -use dashmap::mapref::entry::Entry; use dashmap::DashMap; +use dashmap::mapref::entry::Entry; use dunce::canonicalize; use extract_graphql::JavaScriptSourceFeature; use lsp_server::Message; use lsp_server::Notification as ServerNotification; -use lsp_types::notification::Notification; -use lsp_types::notification::PublishDiagnostics; use lsp_types::Diagnostic; use lsp_types::DiagnosticRelatedInformation as LspDiagnosticRelatedInformation; use lsp_types::DiagnosticSeverity; @@ -32,11 +30,13 @@ use lsp_types::Position; use lsp_types::PublishDiagnosticsParams; use lsp_types::Range; use lsp_types::Url; +use lsp_types::notification::Notification; +use lsp_types::notification::PublishDiagnostics; +use relay_compiler::FsSourceReader; +use relay_compiler::SourceReader; use relay_compiler::errors::BuildProjectError; use relay_compiler::errors::Error; use relay_compiler::source_for_location; -use relay_compiler::FsSourceReader; -use relay_compiler::SourceReader; use crate::lsp_process_error::LSPProcessResult; @@ -367,8 +367,8 @@ mod tests { use lsp_types::Range; use relay_compiler::SourceReader; - use super::is_sub_range; use super::DiagnosticReporter; + use super::is_sub_range; struct MockSourceReader(String); diff --git a/compiler/crates/relay-lsp/src/docblock_resolution_info.rs b/compiler/crates/relay-lsp/src/docblock_resolution_info.rs index 77b14a51f070e..be405a5d08428 100644 --- a/compiler/crates/relay-lsp/src/docblock_resolution_info.rs +++ b/compiler/crates/relay-lsp/src/docblock_resolution_info.rs @@ -6,8 +6,8 @@ */ use common::Span; -use graphql_ir::reexport::StringKey; use graphql_ir::FragmentDefinitionName; +use graphql_ir::reexport::StringKey; use relay_docblock::DocblockIr; use relay_docblock::On; use relay_docblock::ResolverFieldDocblockIr; diff --git a/compiler/crates/relay-lsp/src/explore_schema_for_type.rs b/compiler/crates/relay-lsp/src/explore_schema_for_type.rs index c5fe081278b59..8ee5306859b4f 100644 --- a/compiler/crates/relay-lsp/src/explore_schema_for_type.rs +++ b/compiler/crates/relay-lsp/src/explore_schema_for_type.rs @@ -15,7 +15,7 @@ use crate::lsp_runtime_error::LSPRuntimeError; use crate::lsp_runtime_error::LSPRuntimeResult; use crate::server::GlobalState; -mod types; +pub(crate) mod types; pub(crate) struct ExploreSchemaForType {} diff --git a/compiler/crates/relay-lsp/src/explore_schema_for_type/types.rs b/compiler/crates/relay-lsp/src/explore_schema_for_type/types.rs index 37f995bc85a42..6ceb07f9772a1 100644 --- a/compiler/crates/relay-lsp/src/explore_schema_for_type/types.rs +++ b/compiler/crates/relay-lsp/src/explore_schema_for_type/types.rs @@ -5,8 +5,8 @@ * LICENSE file in the root directory of this source tree. */ -use intern::string_key::StringKey; use intern::Lookup; +use intern::string_key::StringKey; use schema::DirectiveValue; use schema::EnumID; use schema::FieldID; diff --git a/compiler/crates/relay-lsp/src/find_field_usages.rs b/compiler/crates/relay-lsp/src/find_field_usages.rs index dbd92fab78258..6a54f10f04aff 100644 --- a/compiler/crates/relay-lsp/src/find_field_usages.rs +++ b/compiler/crates/relay-lsp/src/find_field_usages.rs @@ -31,10 +31,10 @@ use schema::Type; use serde::Deserialize; use serde::Serialize; -use crate::location::transform_relay_location_on_disk_to_lsp_location; -use crate::server::GlobalState; use crate::LSPRuntimeError; use crate::LSPRuntimeResult; +use crate::location::transform_relay_location_on_disk_to_lsp_location; +use crate::server::GlobalState; // This implementation of FindFieldUsages find matching fields in: // - exact type matches diff --git a/compiler/crates/relay-lsp/src/goto_definition.rs b/compiler/crates/relay-lsp/src/goto_definition.rs index 3e9cc86211c21..ef433214eadae 100644 --- a/compiler/crates/relay-lsp/src/goto_definition.rs +++ b/compiler/crates/relay-lsp/src/goto_definition.rs @@ -19,10 +19,10 @@ use intern::string_key::Intern; use intern::string_key::StringKey; use log::error; use log::info; -use lsp_types::request::GotoDefinition; -use lsp_types::request::Request; use lsp_types::GotoDefinitionResponse; use lsp_types::Url; +use lsp_types::request::GotoDefinition; +use lsp_types::request::Request; use schema::SDLSchema; use schema::Schema; use schema::Type; @@ -32,13 +32,13 @@ use serde::Serialize; use self::goto_docblock_definition::get_docblock_definition_description; use self::goto_graphql_definition::get_graphql_definition_description; use self::goto_graphql_definition::get_graphql_schema_definition_description; +use crate::FieldDefinitionSourceInfo; +use crate::FieldSchemaInfo; +use crate::LSPExtraDataProvider; use crate::location::transform_relay_location_on_disk_to_lsp_location; use crate::lsp_runtime_error::LSPRuntimeError; use crate::lsp_runtime_error::LSPRuntimeResult; use crate::server::GlobalState; -use crate::FieldDefinitionSourceInfo; -use crate::FieldSchemaInfo; -use crate::LSPExtraDataProvider; /// A concrete description of a GraphQL definition that a user would like to goto. pub enum DefinitionDescription { @@ -202,6 +202,7 @@ fn locate_type_definition( GotoDefinitionResponse::Scalar(get_location( &source_info.file_path, source_info.line_number, + source_info.column_number, )?) } else { return Err(LSPRuntimeError::ExpectedError); @@ -318,6 +319,7 @@ fn locate_field_definition( return Ok(GotoDefinitionResponse::Scalar(get_location( &source_info.file_path, source_info.line_number, + source_info.column_number, )?)); } else { error!( @@ -345,10 +347,14 @@ fn locate_field_definition( .map_err(|_| LSPRuntimeError::ExpectedError) } -fn get_location(path: &str, line: u64) -> Result { +fn get_location( + path: &str, + line: u64, + column: u64, +) -> Result { let start = lsp_types::Position { line: line as u32, - character: 0, + character: column as u32, }; let range = lsp_types::Range { start, end: start }; diff --git a/compiler/crates/relay-lsp/src/goto_definition/goto_docblock_definition.rs b/compiler/crates/relay-lsp/src/goto_definition/goto_docblock_definition.rs index d809a697010d8..025dcfeceb970 100644 --- a/compiler/crates/relay-lsp/src/goto_definition/goto_docblock_definition.rs +++ b/compiler/crates/relay-lsp/src/goto_definition/goto_docblock_definition.rs @@ -9,10 +9,10 @@ use common::Span; use relay_docblock::DocblockIr; use super::DefinitionDescription; -use crate::docblock_resolution_info::create_docblock_resolution_info; +use crate::LSPRuntimeResult; use crate::docblock_resolution_info::DocblockResolutionInfo; +use crate::docblock_resolution_info::create_docblock_resolution_info; use crate::lsp_runtime_error::LSPRuntimeError; -use crate::LSPRuntimeResult; pub fn get_docblock_definition_description( docblock_ir: &DocblockIr, diff --git a/compiler/crates/relay-lsp/src/graphql_tools.rs b/compiler/crates/relay-lsp/src/graphql_tools.rs index 7d955c7031a2c..3f106e198f8c4 100644 --- a/compiler/crates/relay-lsp/src/graphql_tools.rs +++ b/compiler/crates/relay-lsp/src/graphql_tools.rs @@ -11,7 +11,6 @@ use std::sync::Arc; use common::DirectiveName; use common::PerfLogger; use common::SourceLocationKey; -use graphql_ir::build_ir_with_extra_features; use graphql_ir::BuilderOptions; use graphql_ir::ExecutableDefinition; use graphql_ir::FragmentDefinition; @@ -21,27 +20,28 @@ use graphql_ir::OperationDefinition; use graphql_ir::OperationDefinitionName; use graphql_ir::Program; use graphql_ir::Selection; +use graphql_ir::build_ir_with_extra_features; use graphql_syntax::parse_executable_with_error_recovery_and_parser_features; use graphql_text_printer::print_full_operation; use intern::string_key::Intern; use intern::string_key::StringKey; -use lsp_types::request::Request; use lsp_types::Url; +use lsp_types::request::Request; +use relay_compiler::ProjectName; use relay_compiler::config::ProjectConfig; use relay_compiler::get_parser_features; -use relay_compiler::ProjectName; -use relay_transforms::apply_transforms; use relay_transforms::CustomTransformsConfig; use relay_transforms::Programs; +use relay_transforms::apply_transforms; use schema::SDLSchema; use schema_documentation::SchemaDocumentation; use serde::Deserialize; use serde::Serialize; +use crate::LSPRuntimeError; use crate::lsp_runtime_error::LSPRuntimeResult; use crate::server::GlobalState; use crate::server::LSPState; -use crate::LSPRuntimeError; pub(crate) enum GraphQLExecuteQuery {} @@ -188,27 +188,28 @@ fn build_operation_ir_with_fragments( ) .map_err(|errors| format!("{:?}", errors))?; - if let Some(operation) = ir.iter().find_map(|item| { + match ir.iter().find_map(|item| { if let ExecutableDefinition::Operation(operation) = item { Some(Arc::new(operation.clone())) } else { None } }) { - let fragments = ir - .iter() - .filter_map(|item| { - if let ExecutableDefinition::Fragment(fragment) = item { - Some(Arc::new(fragment.clone())) - } else { - None - } - }) - .collect::>(); + Some(operation) => { + let fragments = ir + .iter() + .filter_map(|item| { + if let ExecutableDefinition::Fragment(fragment) = item { + Some(Arc::new(fragment.clone())) + } else { + None + } + }) + .collect::>(); - Ok((operation, fragments)) - } else { - Err("Unable to find an operation.".to_string()) + Ok((operation, fragments)) + } + _ => Err("Unable to find an operation.".to_string()), } } @@ -256,8 +257,8 @@ pub(crate) fn get_query_text< let operation_name = operation.name.item.0; let program = state.get_program(&project_name.into())?; - let query_text = - if let Some(program) = get_operation_only_program(operation, fragments, &program) { + let query_text = match get_operation_only_program(operation, fragments, &program) { + Some(program) => { let programs = transform_program( project_config, Arc::new(program), @@ -271,9 +272,9 @@ pub(crate) fn get_query_text< .map_err(LSPRuntimeError::UnexpectedError)?; print_full_operation_text(programs, operation_name).unwrap_or(original_text) - } else { - original_text - }; + } + _ => original_text, + }; Ok(query_text) } diff --git a/compiler/crates/relay-lsp/src/hover.rs b/compiler/crates/relay-lsp/src/hover.rs index b81e595cb8d7f..92599a3907f1e 100644 --- a/compiler/crates/relay-lsp/src/hover.rs +++ b/compiler/crates/relay-lsp/src/hover.rs @@ -8,10 +8,10 @@ //! Utilities for providing the hover feature use intern::Lookup; -use lsp_types::request::HoverRequest; -use lsp_types::request::Request; use lsp_types::LanguageString; use lsp_types::MarkedString; +use lsp_types::request::HoverRequest; +use lsp_types::request::Request; use resolution_path::ResolvePosition; use serde::Serialize; @@ -114,9 +114,9 @@ fn get_open_schema_explorer_command_link( fn get_open_schema_explorer_command(params: &GraphQLSchemaExplorerParams<'_>) -> String { // see https://docs.rs/percent-encoding/2.1.0/percent_encoding/ - use percent_encoding::utf8_percent_encode; use percent_encoding::AsciiSet; use percent_encoding::CONTROLS; + use percent_encoding::utf8_percent_encode; const FRAGMENT: AsciiSet = CONTROLS.add(b' ').add(b'"').add(b'<').add(b'>').add(b'`'); diff --git a/compiler/crates/relay-lsp/src/hover/with_resolution_path.rs b/compiler/crates/relay-lsp/src/hover/with_resolution_path.rs index 45472063f49f8..48c39ccf751d7 100644 --- a/compiler/crates/relay-lsp/src/hover/with_resolution_path.rs +++ b/compiler/crates/relay-lsp/src/hover/with_resolution_path.rs @@ -15,10 +15,10 @@ use graphql_syntax::FragmentDefinition; use graphql_syntax::Identifier; use graphql_syntax::OperationDefinition; use graphql_syntax::VariableDefinition; -use graphql_text_printer::print_value; use graphql_text_printer::PrinterOptions; -use intern::string_key::StringKey; +use graphql_text_printer::print_value; use intern::Lookup; +use intern::string_key::StringKey; use lsp_types::Hover; use lsp_types::HoverContents; use lsp_types::MarkedString; @@ -68,9 +68,9 @@ use schema::Schema; use schema_documentation::SchemaDocumentation; use schema_print::print_directive; -use crate::hover::get_open_schema_explorer_command_link; -use crate::hover::GraphQLSchemaExplorerParams; use crate::LSPExtraDataProvider; +use crate::hover::GraphQLSchemaExplorerParams; +use crate::hover::get_open_schema_explorer_command_link; /// Enum, that allows us to adjust content of the hover /// tooltip based on the consumer type (Relay, GraphQL) diff --git a/compiler/crates/relay-lsp/src/inlay_hints.rs b/compiler/crates/relay-lsp/src/inlay_hints.rs index e096696661fd6..e2842e795c059 100644 --- a/compiler/crates/relay-lsp/src/inlay_hints.rs +++ b/compiler/crates/relay-lsp/src/inlay_hints.rs @@ -14,20 +14,20 @@ use graphql_ir::InlineFragment; use graphql_ir::Program; use graphql_ir::Visitor; use intern::string_key::StringKey; -use lsp_types::request::InlayHintRequest; -use lsp_types::request::Request; use lsp_types::InlayHint; use lsp_types::InlayHintLabel; use lsp_types::InlayHintTooltip; use lsp_types::MarkupContent; +use lsp_types::request::InlayHintRequest; +use lsp_types::request::Request; use schema::SDLSchema; use schema::Schema; +use crate::LSPRuntimeError; use crate::lsp_runtime_error::LSPRuntimeResult; -use crate::server::build_ir_for_lsp; use crate::server::GlobalState; +use crate::server::build_ir_for_lsp; use crate::utils::is_file_uri_in_dir; -use crate::LSPRuntimeError; pub fn on_inlay_hint_request( state: &impl GlobalState, diff --git a/compiler/crates/relay-lsp/src/lib.rs b/compiler/crates/relay-lsp/src/lib.rs index b486a3b52d38c..57e4fb71467e7 100644 --- a/compiler/crates/relay-lsp/src/lib.rs +++ b/compiler/crates/relay-lsp/src/lib.rs @@ -33,6 +33,7 @@ mod shutdown; mod status_reporter; pub mod status_updater; pub mod text_documents; +pub mod type_information; pub mod utils; use std::path::Path; use std::sync::Arc; @@ -126,11 +127,11 @@ pub fn diagnostics_to_code_actions( let mut all_actions = vec![]; for param in published_params { for diagnostic in param.diagnostics { - if let Some(action) = get_code_actions_from_diagnostic(¶m.uri, diagnostic) - .unwrap() - .first() - { - all_actions.push(action.clone()); + if let Some(actions) = get_code_actions_from_diagnostic(¶m.uri, diagnostic) { + // If there are multiple actions for a diagnostic, we only send the first one. + if let Some(action) = actions.first() { + all_actions.push(action.clone()); + } } } } diff --git a/compiler/crates/relay-lsp/src/lsp_extra_data_provider.rs b/compiler/crates/relay-lsp/src/lsp_extra_data_provider.rs index d40b9af135553..bba4414d34110 100644 --- a/compiler/crates/relay-lsp/src/lsp_extra_data_provider.rs +++ b/compiler/crates/relay-lsp/src/lsp_extra_data_provider.rs @@ -12,6 +12,7 @@ use serde::Serialize; pub struct FieldDefinitionSourceInfo { pub file_path: String, pub line_number: u64, + pub column_number: u64, pub is_local: bool, } diff --git a/compiler/crates/relay-lsp/src/node_resolution_info.rs b/compiler/crates/relay-lsp/src/node_resolution_info.rs index 69d8e654da3fd..aee7f13805df5 100644 --- a/compiler/crates/relay-lsp/src/node_resolution_info.rs +++ b/compiler/crates/relay-lsp/src/node_resolution_info.rs @@ -308,9 +308,9 @@ mod test { use graphql_syntax::parse_executable; use intern::string_key::Intern; - use super::create_node_resolution_info; use super::NodeKind; use super::NodeResolutionInfo; + use super::create_node_resolution_info; fn parse_and_get_node_info(source: &str, pos: u32) -> NodeResolutionInfo { let document = diff --git a/compiler/crates/relay-lsp/src/print_operation.rs b/compiler/crates/relay-lsp/src/print_operation.rs index 7ccad74ac0f19..f44af32e2f33b 100644 --- a/compiler/crates/relay-lsp/src/print_operation.rs +++ b/compiler/crates/relay-lsp/src/print_operation.rs @@ -6,8 +6,8 @@ */ use graphql_ir::OperationDefinitionName; -use lsp_types::request::Request; use lsp_types::TextDocumentPositionParams; +use lsp_types::request::Request; use serde::Deserialize; use serde::Serialize; diff --git a/compiler/crates/relay-lsp/src/references.rs b/compiler/crates/relay-lsp/src/references.rs index 0fa55c73a3caa..8a0895a3604f2 100644 --- a/compiler/crates/relay-lsp/src/references.rs +++ b/compiler/crates/relay-lsp/src/references.rs @@ -15,14 +15,15 @@ use graphql_ir::FragmentSpread; use graphql_ir::Program; use graphql_ir::Visitor; use intern::string_key::StringKey; +use lsp_types::Location as LSPLocation; use lsp_types::request::References; use lsp_types::request::Request; -use lsp_types::Location as LSPLocation; use relay_docblock::DocblockIr; use relay_docblock::On; use relay_docblock::ResolverFieldDocblockIr; use schema::Schema; +use crate::FeatureResolutionInfo; use crate::docblock_resolution_info::DocblockResolutionInfo; use crate::find_field_usages::find_field_locations; use crate::find_field_usages::get_usages; @@ -31,7 +32,6 @@ use crate::lsp_runtime_error::LSPRuntimeError; use crate::lsp_runtime_error::LSPRuntimeResult; use crate::node_resolution_info::NodeKind; use crate::server::GlobalState; -use crate::FeatureResolutionInfo; fn get_references_response( feature_resolution_info: FeatureResolutionInfo, diff --git a/compiler/crates/relay-lsp/src/rename.rs b/compiler/crates/relay-lsp/src/rename.rs index a4a8dd158e19b..0f55108b6b21d 100644 --- a/compiler/crates/relay-lsp/src/rename.rs +++ b/compiler/crates/relay-lsp/src/rename.rs @@ -24,13 +24,13 @@ use graphql_syntax::OperationDefinition; use intern::string_key::Intern; use intern::string_key::StringKey; use lazy_static::lazy_static; -use lsp_types::request::PrepareRenameRequest; -use lsp_types::request::Rename; -use lsp_types::request::Request; use lsp_types::PrepareRenameResponse; use lsp_types::TextEdit; use lsp_types::Url; use lsp_types::WorkspaceEdit; +use lsp_types::request::PrepareRenameRequest; +use lsp_types::request::Rename; +use lsp_types::request::Request; use rayon::prelude::IntoParallelRefIterator; use rayon::prelude::ParallelIterator; use resolution_path::ArgumentParent; @@ -50,11 +50,11 @@ use resolution_path::VariableDefinitionPath; use resolution_path::VariableIdentifierParent; use resolution_path::VariableIdentifierPath; -use crate::location::transform_relay_location_on_disk_to_lsp_location; use crate::Feature; use crate::GlobalState; use crate::LSPRuntimeError; use crate::LSPRuntimeResult; +use crate::location::transform_relay_location_on_disk_to_lsp_location; lazy_static! { static ref ARGUMENTS_DIRECTIVE: StringKey = "arguments".intern(); diff --git a/compiler/crates/relay-lsp/src/resolved_types_at_location.rs b/compiler/crates/relay-lsp/src/resolved_types_at_location.rs index d402b1ff805ca..e54aa5695897a 100644 --- a/compiler/crates/relay-lsp/src/resolved_types_at_location.rs +++ b/compiler/crates/relay-lsp/src/resolved_types_at_location.rs @@ -5,19 +5,19 @@ * LICENSE file in the root directory of this source tree. */ -use lsp_types::request::Request; use lsp_types::Position; use lsp_types::TextDocumentIdentifier; use lsp_types::TextDocumentPositionParams; use lsp_types::Url; +use lsp_types::request::Request; use schema::Schema; use serde::Deserialize; use serde::Serialize; +use crate::FeatureResolutionInfo; use crate::lsp_runtime_error::LSPRuntimeError; use crate::lsp_runtime_error::LSPRuntimeResult; use crate::server::GlobalState; -use crate::FeatureResolutionInfo; pub(crate) enum ResolvedTypesAtLocation {} diff --git a/compiler/crates/relay-lsp/src/search_schema_items.rs b/compiler/crates/relay-lsp/src/search_schema_items.rs index 13e7c5e110749..15e20fba040c6 100644 --- a/compiler/crates/relay-lsp/src/search_schema_items.rs +++ b/compiler/crates/relay-lsp/src/search_schema_items.rs @@ -6,8 +6,8 @@ */ use graphql_ir::reexport::StringKey; -use intern::string_key::Intern; use intern::Lookup; +use intern::string_key::Intern; use lsp_types::request::Request; use schema::Schema; use schema_documentation::SchemaDocumentation; diff --git a/compiler/crates/relay-lsp/src/server.rs b/compiler/crates/relay-lsp/src/server.rs index e9684fa0cca84..932cfd5a615aa 100644 --- a/compiler/crates/relay-lsp/src/server.rs +++ b/compiler/crates/relay-lsp/src/server.rs @@ -19,8 +19,8 @@ use common::PerfLogEvent; use common::PerfLogger; use crossbeam::channel::Receiver; use crossbeam::select; -use heartbeat::on_heartbeat; use heartbeat::HeartbeatRequest; +use heartbeat::on_heartbeat; use log::debug; pub use lsp_notification_dispatch::LSPNotificationDispatch; pub use lsp_request_dispatch::LSPRequestDispatch; @@ -30,10 +30,19 @@ use lsp_server::Message; use lsp_server::Notification; use lsp_server::Response as ServerResponse; use lsp_server::ResponseError; -pub use lsp_state::build_ir_for_lsp; pub use lsp_state::GlobalState; pub use lsp_state::LSPState; pub use lsp_state::Schemas; +pub use lsp_state::build_ir_for_lsp; +use lsp_types::CodeActionOptions; +use lsp_types::CodeActionProviderCapability; +use lsp_types::CompletionOptions; +use lsp_types::InitializeParams; +use lsp_types::RenameOptions; +use lsp_types::ServerCapabilities; +use lsp_types::TextDocumentSyncCapability; +use lsp_types::TextDocumentSyncKind; +use lsp_types::WorkDoneProgressOptions; use lsp_types::notification::Cancel; use lsp_types::notification::DidChangeTextDocument; use lsp_types::notification::DidCloseTextDocument; @@ -50,45 +59,38 @@ use lsp_types::request::References; use lsp_types::request::Rename; use lsp_types::request::ResolveCompletionItem; use lsp_types::request::Shutdown; -use lsp_types::CodeActionProviderCapability; -use lsp_types::CompletionOptions; -use lsp_types::InitializeParams; -use lsp_types::RenameOptions; -use lsp_types::ServerCapabilities; -use lsp_types::TextDocumentSyncCapability; -use lsp_types::TextDocumentSyncKind; -use lsp_types::WorkDoneProgressOptions; -use relay_compiler::config::Config; use relay_compiler::NoopArtifactWriter; +use relay_compiler::config::Config; use schema_documentation::SchemaDocumentation; use schema_documentation::SchemaDocumentationLoader; use self::task_queue::TaskProcessor; +pub use crate::LSPExtraDataProvider; use crate::code_action::on_code_action; use crate::completion::on_completion; use crate::completion::on_resolve_completion_item; -use crate::explore_schema_for_type::on_explore_schema_for_type; use crate::explore_schema_for_type::ExploreSchemaForType; -use crate::find_field_usages::on_find_field_usages; +use crate::explore_schema_for_type::on_explore_schema_for_type; use crate::find_field_usages::FindFieldUsages; +use crate::find_field_usages::on_find_field_usages; +use crate::goto_definition::GetSourceLocationOfTypeDefinition; use crate::goto_definition::on_get_source_location_of_type_definition; use crate::goto_definition::on_goto_definition; -use crate::goto_definition::GetSourceLocationOfTypeDefinition; -use crate::graphql_tools::on_graphql_execute_query; use crate::graphql_tools::GraphQLExecuteQuery; +use crate::graphql_tools::on_graphql_execute_query; use crate::hover::on_hover; use crate::inlay_hints::on_inlay_hint_request; use crate::lsp_process_error::LSPProcessResult; use crate::lsp_runtime_error::LSPRuntimeError; -use crate::print_operation::on_print_operation; use crate::print_operation::PrintOperation; +use crate::print_operation::on_print_operation; use crate::references::on_references; use crate::rename::on_prepare_rename; use crate::rename::on_rename; -use crate::resolved_types_at_location::on_get_resolved_types_at_location; use crate::resolved_types_at_location::ResolvedTypesAtLocation; -use crate::search_schema_items::on_search_schema_items; +use crate::resolved_types_at_location::on_get_resolved_types_at_location; use crate::search_schema_items::SearchSchemaItems; +use crate::search_schema_items::on_search_schema_items; use crate::server::lsp_state::handle_lsp_state_tasks; use crate::server::lsp_state_resources::LSPStateResources; use crate::server::task_queue::TaskQueue; @@ -100,7 +102,8 @@ use crate::text_documents::on_did_change_text_document; use crate::text_documents::on_did_close_text_document; use crate::text_documents::on_did_open_text_document; use crate::text_documents::on_did_save_text_document; -pub use crate::LSPExtraDataProvider; +use crate::type_information::TypeInformation; +use crate::type_information::on_type_information; /// Initializes an LSP connection, handling the `initialize` message and `initialized` notification /// handshake. @@ -132,7 +135,10 @@ pub fn initialize(connection: &Connection) -> LSPProcessResult hover_provider: Some(lsp_types::HoverProviderCapability::Simple(true)), definition_provider: Some(lsp_types::OneOf::Left(true)), references_provider: Some(lsp_types::OneOf::Left(true)), - code_action_provider: Some(CodeActionProviderCapability::Simple(true)), + code_action_provider: Some(CodeActionProviderCapability::Options(CodeActionOptions { + code_action_kinds: Some(vec![lsp_types::CodeActionKind::QUICKFIX]), + ..Default::default() + })), inlay_hint_provider: Some(lsp_types::OneOf::Left(true)), ..Default::default() }; @@ -262,6 +268,7 @@ fn dispatch_request(request: lsp_server::Request, lsp_state: &impl GlobalState) let request = LSPRequestDispatch::new(request, lsp_state) .on_request_sync::(on_get_resolved_types_at_location)? .on_request_sync::(on_search_schema_items)? + .on_request_sync::(on_type_information)? .on_request_sync::(on_explore_schema_for_type)? .on_request_sync::( on_get_source_location_of_type_definition, diff --git a/compiler/crates/relay-lsp/src/server/lsp_notification_dispatch.rs b/compiler/crates/relay-lsp/src/server/lsp_notification_dispatch.rs index c81999a8792e8..0916f5a4418bb 100644 --- a/compiler/crates/relay-lsp/src/server/lsp_notification_dispatch.rs +++ b/compiler/crates/relay-lsp/src/server/lsp_notification_dispatch.rs @@ -65,11 +65,11 @@ mod test { use std::sync::atomic::AtomicI32; use std::sync::atomic::Ordering; + use lsp_types::LogMessageParams; + use lsp_types::MessageType; use lsp_types::notification::LogMessage; use lsp_types::notification::Notification; use lsp_types::notification::TelemetryEvent; - use lsp_types::LogMessageParams; - use lsp_types::MessageType; use super::LSPNotificationDispatch; use crate::lsp_runtime_error::LSPRuntimeResult; diff --git a/compiler/crates/relay-lsp/src/server/lsp_request_dispatch.rs b/compiler/crates/relay-lsp/src/server/lsp_request_dispatch.rs index 2b6f7a1e4765b..6c0a3895ee4cd 100644 --- a/compiler/crates/relay-lsp/src/server/lsp_request_dispatch.rs +++ b/compiler/crates/relay-lsp/src/server/lsp_request_dispatch.rs @@ -122,13 +122,13 @@ mod test { use std::sync::atomic::AtomicI32; use std::sync::atomic::Ordering; - use lsp_types::request::GotoDefinition; - use lsp_types::request::HoverRequest; - use lsp_types::request::Request; use lsp_types::Position; use lsp_types::TextDocumentIdentifier; use lsp_types::TextDocumentPositionParams; use lsp_types::Url; + use lsp_types::request::GotoDefinition; + use lsp_types::request::HoverRequest; + use lsp_types::request::Request; use super::LSPRequestDispatch; use crate::lsp_runtime_error::LSPRuntimeResult; diff --git a/compiler/crates/relay-lsp/src/server/lsp_state.rs b/compiler/crates/relay-lsp/src/server/lsp_state.rs index 57ae809eac3ca..504d3506cc4fe 100644 --- a/compiler/crates/relay-lsp/src/server/lsp_state.rs +++ b/compiler/crates/relay-lsp/src/server/lsp_state.rs @@ -15,21 +15,21 @@ use common::SourceLocationKey; use common::Span; use crossbeam::channel::SendError; use crossbeam::channel::Sender; -use dashmap::mapref::entry::Entry; use dashmap::DashMap; +use dashmap::mapref::entry::Entry; use docblock_syntax::parse_docblock; use extract_graphql::JavaScriptSourceFeature; use fnv::FnvBuildHasher; -use graphql_ir::build_ir_with_extra_features; use graphql_ir::BuilderOptions; use graphql_ir::FragmentVariablesSemantic; use graphql_ir::OperationDefinitionName; use graphql_ir::Program; use graphql_ir::RelayMode; -use graphql_syntax::parse_executable_with_error_recovery_and_parser_features; +use graphql_ir::build_ir_with_extra_features; use graphql_syntax::ExecutableDefinition; use graphql_syntax::ExecutableDocument; use graphql_syntax::GraphQLSource; +use graphql_syntax::parse_executable_with_error_recovery_and_parser_features; use graphql_text_printer::print_full_operation; use intern::string_key::Intern; use intern::string_key::StringKey; @@ -39,16 +39,15 @@ use lsp_types::Diagnostic; use lsp_types::Range; use lsp_types::TextDocumentPositionParams; use lsp_types::Url; -use relay_compiler::config::Config; -use relay_compiler::get_parser_features; use relay_compiler::FileCategorizer; use relay_compiler::FileGroup; use relay_compiler::ProjectName; -use relay_docblock::parse_docblock_ast; +use relay_compiler::config::Config; +use relay_compiler::get_parser_features; use relay_docblock::ParseOptions; +use relay_docblock::parse_docblock_ast; use relay_transforms::apply_transforms; use relay_transforms::deprecated_fields_for_executable_definition; -use relay_transforms::disallow_required_on_non_null_field_for_executable_definition; use schema::SDLSchema; use schema_documentation::CombinedSchemaDocumentation; use schema_documentation::SchemaDocumentation; @@ -56,6 +55,12 @@ use schema_documentation::SchemaDocumentationLoader; use tokio::sync::Notify; use super::task_queue::TaskScheduler; +use crate::ContentConsumerType; +use crate::DocblockNode; +use crate::Feature; +use crate::FeatureResolutionInfo; +use crate::LSPExtraDataProvider; +use crate::LSPRuntimeError; use crate::diagnostic_reporter::DiagnosticReporter; use crate::docblock_resolution_info::create_docblock_resolution_info; use crate::graphql_tools::get_operation_only_program; @@ -67,12 +72,6 @@ use crate::utils::extract_executable_definitions_from_text_document; use crate::utils::extract_feature_from_text; use crate::utils::get_file_group_from_uri; use crate::utils::get_project_name_from_file_group; -use crate::ContentConsumerType; -use crate::DocblockNode; -use crate::Feature; -use crate::FeatureResolutionInfo; -use crate::LSPExtraDataProvider; -use crate::LSPRuntimeError; pub type Schemas = Arc, FnvBuildHasher>>; pub type SourcePrograms = Arc>; @@ -274,17 +273,11 @@ impl Err(BuildProjectFailure::Error( + BuildProjectError::ValidationErrors { + errors: diagnostics, + project_name: project_config.name, + }, + )), + // Compilation-blocking validation errors + Err(diagnostics) => Err(BuildProjectFailure::Error( + BuildProjectError::ValidationErrors { + errors: diagnostics, + project_name: project_config.name, + }, + )), + }?; Ok(()) } diff --git a/compiler/crates/relay-lsp/src/server/task_queue.rs b/compiler/crates/relay-lsp/src/server/task_queue.rs index 78ecc43d6293a..11426e9f346b9 100644 --- a/compiler/crates/relay-lsp/src/server/task_queue.rs +++ b/compiler/crates/relay-lsp/src/server/task_queue.rs @@ -10,9 +10,9 @@ use std::thread; use std::thread::JoinHandle; use std::time::Instant; -use crossbeam::channel::unbounded; use crossbeam::channel::Receiver; use crossbeam::channel::Sender; +use crossbeam::channel::unbounded; use log::debug; pub struct TaskQueue { diff --git a/compiler/crates/relay-lsp/src/status_updater.rs b/compiler/crates/relay-lsp/src/status_updater.rs index 6daf3f3c08bec..b13489218e968 100644 --- a/compiler/crates/relay-lsp/src/status_updater.rs +++ b/compiler/crates/relay-lsp/src/status_updater.rs @@ -14,12 +14,12 @@ use crossbeam::channel::Sender; use lsp_server::Message; use lsp_server::Notification as ServerNotification; use lsp_server::Request as ServerRequest; -use lsp_types::notification::Notification; -use lsp_types::notification::ShowMessage; -use lsp_types::request::Request; use lsp_types::MessageActionItem; use lsp_types::MessageType; use lsp_types::ShowMessageParams; +use lsp_types::notification::Notification; +use lsp_types::notification::ShowMessage; +use lsp_types::request::Request; use serde::Deserialize; use serde::Serialize; diff --git a/compiler/crates/relay-lsp/src/text_documents.rs b/compiler/crates/relay-lsp/src/text_documents.rs index 4cc195a51a177..ab1fd0c74763f 100644 --- a/compiler/crates/relay-lsp/src/text_documents.rs +++ b/compiler/crates/relay-lsp/src/text_documents.rs @@ -7,15 +7,15 @@ //! Utilities related to LSP text document syncing +use lsp_types::DidChangeTextDocumentParams; +use lsp_types::DidOpenTextDocumentParams; +use lsp_types::TextDocumentItem; use lsp_types::notification::Cancel; use lsp_types::notification::DidChangeTextDocument; use lsp_types::notification::DidCloseTextDocument; use lsp_types::notification::DidOpenTextDocument; use lsp_types::notification::DidSaveTextDocument; use lsp_types::notification::Notification; -use lsp_types::DidChangeTextDocumentParams; -use lsp_types::DidOpenTextDocumentParams; -use lsp_types::TextDocumentItem; use crate::lsp_runtime_error::LSPRuntimeResult; use crate::server::GlobalState; diff --git a/compiler/crates/relay-lsp/src/type_information.rs b/compiler/crates/relay-lsp/src/type_information.rs new file mode 100644 index 0000000000000..62b8eb5de100f --- /dev/null +++ b/compiler/crates/relay-lsp/src/type_information.rs @@ -0,0 +1,87 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +//! Type information LSP request + +use intern::Lookup; +use intern::string_key::Intern; +use lsp_types::Url; +use lsp_types::request::Request; +use schema::Schema; +use serde::Deserialize; +use serde::Serialize; + +use crate::LSPRuntimeError; +use crate::explore_schema_for_type::types; +use crate::lsp_runtime_error::LSPRuntimeResult; +use crate::server::GlobalState; + +pub(crate) fn on_type_information( + lsp_state: &impl GlobalState, + params: TypeInformationParams, +) -> LSPRuntimeResult { + let Ok(project_name) = lsp_state.extract_project_name_from_url(¶ms.uri) else { + return Err(LSPRuntimeError::UnexpectedError(format!( + "Unable to extract Relay GraphQL project from uri: {:?}", + params.uri + ))); + }; + + let type_name = ¶ms.type_name; + + let schema = lsp_state.get_schema(&project_name)?; + + let type_ = if params.type_name == "Query" { + schema.query_type() + } else if params.type_name == "Subscription" { + schema.subscription_type() + } else if params.type_name == "Mutation" { + schema.mutation_type() + } else { + schema.get_type(type_name.intern()) + }; + + let Some(type_) = type_ else { + return Err(LSPRuntimeError::UnexpectedError(format!( + "Unable to find type information for {type_name}", + ))); + }; + + let schema_item = types::get_full_schema_explorer_type_reference( + type_, + ¶ms.type_name, + &schema, + &lsp_state.get_schema_documentation(project_name.lookup()), + &None, + None, + ); + + Ok(TypeInformationResponse { schema_item }) +} + +pub(crate) enum TypeInformation {} + +#[derive(Deserialize, Serialize, Debug)] +#[serde(rename_all = "camelCase")] +pub(crate) struct TypeInformationParams { + pub uri: Url, + pub type_name: String, +} + +#[derive(Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub(crate) struct TypeInformationResponse { + schema_item: crate::explore_schema_for_type::types::SchemaExplorerTypeReference< + crate::explore_schema_for_type::types::SchemaExplorerSchemaType, + >, +} + +impl Request for TypeInformation { + type Params = TypeInformationParams; + type Result = TypeInformationResponse; + const METHOD: &'static str = "relay/typeInformation"; +} diff --git a/compiler/crates/relay-lsp/src/utils.rs b/compiler/crates/relay-lsp/src/utils.rs index f382ce73e6f6e..09b86299e667c 100644 --- a/compiler/crates/relay-lsp/src/utils.rs +++ b/compiler/crates/relay-lsp/src/utils.rs @@ -14,26 +14,26 @@ use common::TextSource; use dashmap::DashMap; use docblock_syntax::parse_docblock; use extract_graphql::JavaScriptSourceFeature; -use graphql_syntax::parse_executable_with_error_recovery_and_parser_features; use graphql_syntax::ExecutableDefinition; use graphql_syntax::GraphQLSource; use graphql_syntax::ParserFeatures; +use graphql_syntax::parse_executable_with_error_recovery_and_parser_features; use intern::string_key::StringKey; use log::debug; use lsp_types::Position; use lsp_types::TextDocumentPositionParams; use lsp_types::Url; -use relay_compiler::config::Config; -use relay_compiler::get_parser_features; use relay_compiler::FileCategorizer; use relay_compiler::FileGroup; use relay_compiler::ProjectConfig; -use relay_docblock::parse_docblock_ast; +use relay_compiler::config::Config; +use relay_compiler::get_parser_features; use relay_docblock::ParseOptions; +use relay_docblock::parse_docblock_ast; +use crate::Feature; use crate::lsp_runtime_error::LSPRuntimeError; use crate::lsp_runtime_error::LSPRuntimeResult; -use crate::Feature; pub fn is_file_uri_in_dir(root_dir: PathBuf, file_uri: &Url) -> bool { let file_path_result = file_uri.to_file_path(); diff --git a/compiler/crates/relay-lsp/tests/find_field_usages.rs b/compiler/crates/relay-lsp/tests/find_field_usages.rs index 6f1c4318f03e8..5039383b22af0 100644 --- a/compiler/crates/relay-lsp/tests/find_field_usages.rs +++ b/compiler/crates/relay-lsp/tests/find_field_usages.rs @@ -7,9 +7,9 @@ use common::SourceLocationKey; use fixture_tests::Fixture; +use graphql_ir::Program; use graphql_ir::build; use graphql_ir::reexport::Intern; -use graphql_ir::Program; use graphql_syntax::parse_executable; use relay_lsp::find_field_usages; use relay_test_schema::get_test_schema; diff --git a/compiler/crates/relay-lsp/tests/hover.rs b/compiler/crates/relay-lsp/tests/hover.rs index 0b3e0b7f04855..18f7497609abc 100644 --- a/compiler/crates/relay-lsp/tests/hover.rs +++ b/compiler/crates/relay-lsp/tests/hover.rs @@ -10,16 +10,16 @@ use std::sync::Arc; use common::SourceLocationKey; use common::Span; use fixture_tests::Fixture; +use graphql_ir::Program; use graphql_ir::build; use graphql_ir::reexport::Intern; -use graphql_ir::Program; use graphql_syntax::parse_executable; use graphql_test_helpers::diagnostics_to_sorted_string; use lsp_types::HoverContents; use lsp_types::MarkedString; -use relay_lsp::hover::get_hover; use relay_lsp::ContentConsumerType; use relay_lsp::DummyExtraDataProvider; +use relay_lsp::hover::get_hover; use relay_test_schema::get_test_schema; use relay_test_schema::get_test_schema_with_extensions; use resolution_path::ResolvePosition; diff --git a/compiler/crates/relay-lsp/tests/rename.rs b/compiler/crates/relay-lsp/tests/rename.rs index 5f6f3b6bf1634..df83006324995 100644 --- a/compiler/crates/relay-lsp/tests/rename.rs +++ b/compiler/crates/relay-lsp/tests/rename.rs @@ -11,16 +11,16 @@ use common::Location; use common::SourceLocationKey; use common::Span; use fixture_tests::Fixture; -use graphql_ir::build; use graphql_ir::Program; -use graphql_syntax::parse_executable_with_features; +use graphql_ir::build; use graphql_syntax::FragmentArgumentSyntaxKind; use graphql_syntax::ParserFeatures; +use graphql_syntax::parse_executable_with_features; use graphql_test_helpers::diagnostics_to_sorted_string; use itertools::Itertools; +use relay_lsp::Feature; use relay_lsp::rename::create_rename_request; use relay_lsp::rename::get_locations_for_rename; -use relay_lsp::Feature; use relay_test_schema::get_test_schema; pub async fn transform_fixture(fixture: &Fixture<'_>) -> Result { @@ -34,6 +34,7 @@ pub async fn transform_fixture(fixture: &Fixture<'_>) -> Result ParserFeatures { fragment_argument_capability: FragmentArgumentSyntaxKind::SpreadArgumentsAndFragmentVariableDefinitions, + allow_string_literal_alias: false, }, ) .map_err(|diagnostics| diagnostics_to_sorted_string(&source, &diagnostics))?; @@ -70,7 +71,7 @@ fn rename_locations(locations: Vec, source: &str) -> String { let start = (span.start as i32 + offset) as usize; let end = (span.end as i32 + offset) as usize; - source_with_renames.replace_range(start..end, &renamed_key); + source_with_renames.replace_range(start..end, renamed_key); let original_length = end as i32 - start as i32; offset += renamed_key_length - original_length; diff --git a/compiler/crates/relay-saved-state-loader/Cargo.toml b/compiler/crates/relay-saved-state-loader/Cargo.toml index 6c68f1e55365f..7ca8119e9695c 100644 --- a/compiler/crates/relay-saved-state-loader/Cargo.toml +++ b/compiler/crates/relay-saved-state-loader/Cargo.toml @@ -4,10 +4,10 @@ name = "relay-saved-state-loader" version = "0.0.0" authors = ["Facebook"] -edition = "2021" +edition = "2024" repository = "https://github.com/facebook/relay" license = "MIT" [dependencies] -async-trait = "0.1.71" -serde_bser = "0.3" +async-trait = "0.1.86" +serde_bser = "0.4" diff --git a/compiler/crates/relay-schema-generation/Cargo.toml b/compiler/crates/relay-schema-generation/Cargo.toml index f0b2cbd9b341d..6e9e59b424505 100644 --- a/compiler/crates/relay-schema-generation/Cargo.toml +++ b/compiler/crates/relay-schema-generation/Cargo.toml @@ -4,7 +4,7 @@ name = "relay-schema-generation" version = "0.0.0" authors = ["Facebook"] -edition = "2021" +edition = "2024" repository = "https://github.com/facebook/relay" license = "MIT" @@ -27,15 +27,15 @@ graphql-syntax = { path = "../graphql-syntax" } hermes_comments = { git = "https://github.com/facebook/hermes.git" } hermes_estree = { git = "https://github.com/facebook/hermes.git" } hermes_parser = { git = "https://github.com/facebook/hermes.git" } -indexmap = { version = "2.2.6", features = ["arbitrary", "rayon", "serde"] } +indexmap = { version = "2.9.0", features = ["arbitrary", "rayon", "serde"] } intern = { path = "../intern" } -lazy_static = "1.4" +lazy_static = "1.5" relay-config = { path = "../relay-config" } relay-docblock = { path = "../relay-docblock" } -rustc-hash = "1.1.0" +rustc-hash = "2.1.1" schema-extractor = { path = "../schema-extractor" } -serde = { version = "1.0.185", features = ["derive", "rc"] } -thiserror = "1.0.64" +serde = { version = "1.0.219", features = ["derive", "rc"] } +thiserror = "2.0.12" [dev-dependencies] extract-graphql = { path = "../extract-graphql" } @@ -43,4 +43,4 @@ fixture-tests = { path = "../fixture-tests" } graphql-cli = { path = "../graphql-cli" } graphql-test-helpers = { path = "../graphql-test-helpers" } relay-test-schema = { path = "../relay-test-schema" } -tokio = { version = "1.41.0", features = ["full", "test-util", "tracing"] } +tokio = { version = "1.46.1", features = ["full", "test-util", "tracing"] } diff --git a/compiler/crates/relay-schema-generation/src/errors.rs b/compiler/crates/relay-schema-generation/src/errors.rs index 7e98c02ee4611..315426aa6fcb9 100644 --- a/compiler/crates/relay-schema-generation/src/errors.rs +++ b/compiler/crates/relay-schema-generation/src/errors.rs @@ -98,4 +98,12 @@ pub enum SchemaGenerationError { module_name: StringKey, import_type: JSImportType, }, + #[error( + "The key of a property lookup resolver defined with @gqlField must be an identifier (not a string or computed value)." + )] + ExpectedPropertyLookupToBeIdentifer, + #[error( + "This field is a property lookup but has an import for a GraphQL fragment used in the resolver. This is not allowed." + )] + ExpectedResolverFunctionWithRootFragment, } diff --git a/compiler/crates/relay-schema-generation/src/find_property_lookup_resolvers.rs b/compiler/crates/relay-schema-generation/src/find_property_lookup_resolvers.rs new file mode 100644 index 0000000000000..b7bf1525916d7 --- /dev/null +++ b/compiler/crates/relay-schema-generation/src/find_property_lookup_resolvers.rs @@ -0,0 +1,121 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#![deny(warnings)] +#![deny(clippy::all)] + +use ::intern::string_key::Intern; +use ::intern::string_key::StringKey; +use common::Diagnostic; +use common::Location; +use common::SourceLocationKey; +use common::Span; +use common::WithLocation; +use docblock_shared::ResolverSourceHash; +use docblock_syntax::DocblockAST; +use docblock_syntax::parse_docblock; +use fnv::FnvHashMap; +use hermes_estree::ObjectTypePropertyKey; +use hermes_estree::Range; +use hermes_estree::SourceRange; +use hermes_estree::Visitor; + +use crate::FieldDefinitionInfo; +use crate::SchemaGenerationError; +use crate::UnresolvedFieldDefinition; +use crate::get_deprecated; +use crate::get_description; + +fn source_range_to_span(source_range: SourceRange) -> Span { + Span { + start: source_range.start, + end: source_range.end, + } +} + +pub struct PropertyVisitor<'a> { + pub location: SourceLocationKey, + source_hash: ResolverSourceHash, + pub errors: Vec, + entity_name: WithLocation, + resolver_node_ranges: &'a FnvHashMap, + pub field_definitions: Vec, +} + +impl<'a> PropertyVisitor<'a> { + pub fn new( + source_module_path: &str, + source_hash: ResolverSourceHash, + entity_name: WithLocation, + resolver_node_ranges: &'a FnvHashMap, + ) -> Self { + Self { + location: SourceLocationKey::standalone(source_module_path), + source_hash, + errors: vec![], + entity_name, + resolver_node_ranges, + field_definitions: vec![], + } + } +} + +impl Visitor<'_> for PropertyVisitor<'_> { + fn visit_object_type_property(&mut self, ast: &'_ hermes_estree::ObjectTypeProperty) { + if self.resolver_node_ranges.contains_key(&ast.range) { + let field_name = match &ast.key { + ObjectTypePropertyKey::Identifier(id) => WithLocation::from_span( + self.location, + source_range_to_span(id.range), + id.name.clone().intern(), + ), + ObjectTypePropertyKey::_Literal(lit) => { + self.errors.push(Diagnostic::error( + SchemaGenerationError::ExpectedPropertyLookupToBeIdentifer, + Location::new(self.location, source_range_to_span(lit.range())), + )); + return; + } + }; + let (comment, comment_range) = self.resolver_node_ranges.get(&ast.range).unwrap(); + let docblock = match parse_docblock(comment, self.location) { + Ok(docblock) => docblock, + Err(err) => { + self.errors.extend(err); + return; + } + }; + let description = match get_description(&docblock, *comment_range) { + Ok(description) => description, + Err(err) => { + self.errors.extend(err); + return; + } + }; + let deprecated = get_deprecated(&docblock); + let alias = get_aliased_field_name(&docblock); + let field_definition = UnresolvedFieldDefinition { + field_name: alias.unwrap_or(field_name), + entity_name: Some(self.entity_name), + return_type: ast.value.clone(), + source_hash: self.source_hash, + description, + deprecated, + entity_type: None, + field_info: FieldDefinitionInfo::PropertyLookupInfo { + property_name: field_name, + }, + }; + self.field_definitions.push(field_definition); + } + } +} + +fn get_aliased_field_name(docblock: &DocblockAST) -> Option> { + let aliased_field = docblock.find_field("gqlField".intern()); + aliased_field.and_then(|f| f.field_value) +} diff --git a/compiler/crates/relay-schema-generation/src/find_resolver_imports.rs b/compiler/crates/relay-schema-generation/src/find_resolver_imports.rs index 0387d14b651da..4fdf4cf739545 100644 --- a/compiler/crates/relay-schema-generation/src/find_resolver_imports.rs +++ b/compiler/crates/relay-schema-generation/src/find_resolver_imports.rs @@ -17,15 +17,15 @@ use common::Diagnostic; use common::DiagnosticsResult; use common::Location; use common::SourceLocationKey; +use hermes_estree::_Literal; use hermes_estree::Declaration; use hermes_estree::ImportDeclarationSpecifier; use hermes_estree::Visitor; -use hermes_estree::_Literal; use rustc_hash::FxHashMap; use serde::Serialize; -use crate::to_location; use crate::SchemaGenerationError; +use crate::to_location; pub type JSModules = FxHashMap; pub struct ImportExportVisitor { imports: JSModules, diff --git a/compiler/crates/relay-schema-generation/src/lib.rs b/compiler/crates/relay-schema-generation/src/lib.rs index 50eb81d761b39..d60a8783eb828 100644 --- a/compiler/crates/relay-schema-generation/src/lib.rs +++ b/compiler/crates/relay-schema-generation/src/lib.rs @@ -10,19 +10,20 @@ #![deny(clippy::all)] mod errors; +mod find_property_lookup_resolvers; mod find_resolver_imports; -use std::collections::hash_map::Entry; use std::collections::HashSet; +use std::collections::hash_map::Entry; use std::path::Path; use std::path::PathBuf; use std::str::FromStr; use ::errors::try_all; +use ::intern::Lookup; use ::intern::intern; use ::intern::string_key::Intern; use ::intern::string_key::StringKey; -use ::intern::Lookup; use common::Diagnostic; use common::DiagnosticsResult; use common::Location; @@ -30,17 +31,18 @@ use common::ScalarName; use common::SourceLocationKey; use common::Span; use common::WithLocation; -use docblock_shared::ResolverSourceHash; use docblock_shared::DEPRECATED_FIELD; -use docblock_syntax::parse_docblock; +use docblock_shared::ResolverSourceHash; use docblock_syntax::DocblockAST; use docblock_syntax::DocblockSection; +use docblock_syntax::parse_docblock; use errors::SchemaGenerationError; use find_resolver_imports::ImportExportVisitor; use find_resolver_imports::JSImportType; use find_resolver_imports::ModuleResolution; use find_resolver_imports::ModuleResolutionKey; use fnv::FnvBuildHasher; +use fnv::FnvHashMap; use graphql_ir::FragmentDefinitionName; use graphql_syntax::ConstantArgument; use graphql_syntax::ConstantDirective; @@ -58,6 +60,7 @@ use graphql_syntax::StringNode; use graphql_syntax::Token; use graphql_syntax::TokenKind; use graphql_syntax::TypeAnnotation; +use hermes_comments::AttachedComments; use hermes_comments::find_nodes_after_comments; use hermes_estree::Declaration; use hermes_estree::FlowTypeAnnotation; @@ -71,10 +74,11 @@ use hermes_estree::Range; use hermes_estree::SourceRange; use hermes_estree::TypeAlias; use hermes_estree::TypeAnnotationEnum; -use hermes_parser::parse; +use hermes_estree::Visitor; use hermes_parser::ParseResult; use hermes_parser::ParserDialect; use hermes_parser::ParserFlags; +use hermes_parser::parse; use indexmap::IndexMap; use lazy_static::lazy_static; use relay_config::CustomType; @@ -91,6 +95,8 @@ use relay_docblock::WeakObjectIr; use rustc_hash::FxHashMap; use schema_extractor::SchemaExtractor; +use crate::find_property_lookup_resolvers::PropertyVisitor; + pub static LIVE_FLOW_TYPE_NAME: &str = "LiveState"; type FnvIndexMap = IndexMap; @@ -134,17 +140,27 @@ pub struct RelayResolverExtractor { custom_scalar_map: FnvIndexMap, } +enum FieldDefinitionInfo { + ResolverFunctionInfo { + arguments: Option, + is_live: Option, + root_fragment: Option<(WithLocation, Vec)>, + }, + PropertyLookupInfo { + // If an alias is used, this may differ from the field name + property_name: WithLocation, + }, +} + struct UnresolvedFieldDefinition { entity_name: Option>, field_name: WithLocation, return_type: FlowTypeAnnotation, - arguments: Option, source_hash: ResolverSourceHash, - is_live: Option, description: Option>, deprecated: Option, - root_fragment: Option<(WithLocation, Vec)>, entity_type: Option>, + field_info: FieldDefinitionInfo, } impl Default for RelayResolverExtractor { @@ -233,6 +249,15 @@ impl RelayResolverExtractor { let module_resolution = import_export_visitor.get_module_resolution(&ast)?; let attached_comments = find_nodes_after_comments(&ast, &comments); + let (gql_field_comments, attached_comments): (AttachedComments<'_>, AttachedComments<'_>) = + attached_comments + .into_iter() + .partition(|(comment, _, _, _)| comment.contains("@gqlField")); + + let gql_comments = + FnvHashMap::from_iter(gql_field_comments.into_iter().map( + |(comment, comment_range, _, node_range)| (node_range, (comment, comment_range)), + )); let result = try_all( attached_comments @@ -280,13 +305,15 @@ impl RelayResolverExtractor { entity_name, field_name: name, return_type, - arguments, source_hash, - is_live, description, deprecated, - root_fragment: None, entity_type: None, + field_info: FieldDefinitionInfo::ResolverFunctionInfo { + arguments, + is_live, + root_fragment: None, + }, }, )? } else { @@ -305,6 +332,25 @@ impl RelayResolverExtractor { type_alias, }) => { let name = resolver_value.field_value.unwrap_or(field_name); + let mut prop_visitor = PropertyVisitor::new( + source_module_path, + source_hash, + name, + &gql_comments, + ); + prop_visitor.visit_flow_type_annotation(&type_alias); + if !prop_visitor.errors.is_empty() { + return Err(prop_visitor.errors); + } + let field_definitions: Vec<( + UnresolvedFieldDefinition, + SourceLocationKey, + )> = prop_visitor + .field_definitions + .into_iter() + .map(|def| (def, prop_visitor.location)) + .collect(); + self.add_weak_type_definition( name, type_alias, @@ -312,7 +358,8 @@ impl RelayResolverExtractor { source_module_path, description, false, - )? + )?; + self.unresolved_field_definitions.extend(field_definitions); } } Ok(()) @@ -379,27 +426,51 @@ impl RelayResolverExtractor { // Special case: we attach the field to the `Query` type when there is no entity WithLocation::new(field.field_name.location, intern!("Query")) }; - let arguments = if let Some(args) = field.arguments { - Some(flow_type_to_field_arguments( - source_location, - &self.custom_scalar_map, - &args, - module_resolution, - &self.type_definitions, - )?) - } else { - None + let property_lookup_name = match field.field_info { + FieldDefinitionInfo::PropertyLookupInfo { property_name } => { + Some(property_name) + } + FieldDefinitionInfo::ResolverFunctionInfo { .. } => None, }; - if let (Some(field_arguments), Some((root_fragment, fragment_arguments))) = - (&arguments, &field.root_fragment) - { - relay_docblock::validate_fragment_arguments( - source_location, - field_arguments, - root_fragment.location.source_location(), - fragment_arguments, - )?; - } + let (arguments, is_live, (root_fragment, fragment_arguments)) = + match field.field_info { + FieldDefinitionInfo::ResolverFunctionInfo { + arguments, + is_live, + root_fragment, + } => { + let args = if let Some(args) = arguments { + Some(flow_type_to_field_arguments( + source_location, + &self.custom_scalar_map, + &args, + module_resolution, + &self.type_definitions, + )?) + } else { + None + }; + + if let ( + Some(field_arguments), + Some((root_fragment, fragment_arguments)), + ) = (&args, &root_fragment) + { + relay_docblock::validate_fragment_arguments( + source_location, + field_arguments, + root_fragment.location.source_location(), + fragment_arguments, + )?; + } + + (args, is_live, root_fragment.unzip()) + } + FieldDefinitionInfo::PropertyLookupInfo { .. } => { + (None, None, (None, None)) + } + }; + let live = is_live.map(|loc| UnpopulatedIrField { key_location: loc }); let description_node = field.description.map(|desc| StringNode { token: Token { span: desc.location.span(), @@ -425,10 +496,6 @@ impl RelayResolverExtractor { hack_source: None, span: field.field_name.location.span(), }; - let live = field - .is_live - .map(|loc| UnpopulatedIrField { key_location: loc }); - let (root_fragment, fragment_arguments) = field.root_fragment.unzip(); self.resolved_field_definitions.push(TerseRelayResolverIr { field: field_definition, type_, @@ -443,6 +510,7 @@ impl RelayResolverExtractor { field.field_name.location, ), type_confirmed: true, + property_lookup_name, }); Ok(()) }), @@ -489,8 +557,23 @@ impl RelayResolverExtractor { ); let fragment_arguments = relay_docblock::extract_fragment_arguments(&fragment_definition).transpose()?; - field_definition.root_fragment = - Some((fragment, fragment_arguments.unwrap_or(vec![]))); + field_definition.field_info = match field_definition.field_info { + FieldDefinitionInfo::ResolverFunctionInfo { + arguments, + is_live, + root_fragment: _, + } => FieldDefinitionInfo::ResolverFunctionInfo { + arguments, + is_live, + root_fragment: Some((fragment, fragment_arguments.unwrap_or(vec![]))), + }, + FieldDefinitionInfo::PropertyLookupInfo { .. } => { + return Err(vec![Diagnostic::error( + SchemaGenerationError::ExpectedResolverFunctionWithRootFragment, + entity_name.location, + )]); + } + } } } self.unresolved_field_definitions @@ -623,17 +706,17 @@ impl RelayResolverExtractor { entity_name: Some(name), field_name, return_type: field_type.clone(), - arguments: None, source_hash, - is_live: None, description, deprecated: None, - root_fragment: None, entity_type: Some( weak_object .type_name .name_with_location(weak_object.location.source_location()), ), + field_info: FieldDefinitionInfo::PropertyLookupInfo { + property_name: field_name, + }, }, self.current_location, )); diff --git a/compiler/crates/relay-schema-generation/tests/docblock.rs b/compiler/crates/relay-schema-generation/tests/docblock.rs index 49c605bc49459..7ec416fb58c69 100644 --- a/compiler/crates/relay-schema-generation/tests/docblock.rs +++ b/compiler/crates/relay-schema-generation/tests/docblock.rs @@ -21,14 +21,14 @@ use graphql_cli::DiagnosticPrinter; use graphql_syntax::ExecutableDefinition; use graphql_test_helpers::ProjectFixture; use indexmap::IndexMap; -use intern::string_key::Intern; use intern::Lookup; +use intern::string_key::Intern; use relay_config::CustomType; use relay_config::CustomTypeImport; use relay_config::ProjectName; -use relay_docblock::extend_schema_with_resolver_type_system_definition; use relay_docblock::DocblockIr; use relay_docblock::ResolverFieldDocblockIr; +use relay_docblock::extend_schema_with_resolver_type_system_definition; use relay_schema_generation::RelayResolverExtractor; use relay_test_schema::get_test_schema_with_extensions; diff --git a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/arguments.expected b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/arguments.expected index 465a0a0e176fa..2ff923ce9cb4f 100644 --- a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/arguments.expected +++ b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/arguments.expected @@ -123,6 +123,7 @@ Field( "9eb0075d1ca5f38ce3ae8a364b4fb1be", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -236,6 +237,7 @@ Field( "9eb0075d1ca5f38ce3ae8a364b4fb1be", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -276,7 +278,7 @@ Type( ), ) type Cat @__RelayResolverModel { - id: ID! + id: ID! @__RelayResolverModelGeneratedIDField __relay_model_instance: RelayResolverValue! @relay_resolver(generated_fragment: true, fragment_name: "Cat__id", import_name: "Cat", import_path: "module.js", inject_fragment_data: "id") @resolver_source_hash(value: "9eb0075d1ca5f38ce3ae8a364b4fb1be") @unselectable(reason: "This field is intended only for Relay's internal use") } diff --git a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/built-in-scalar-id.expected b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/built-in-scalar-id.expected index 37c048ee40ca4..e1cb06c3f6109 100644 --- a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/built-in-scalar-id.expected +++ b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/built-in-scalar-id.expected @@ -86,6 +86,7 @@ Field( "a9744194670e59356ad37913eff9defe", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -126,6 +127,6 @@ Type( ), ) type Cat @__RelayResolverModel { - id: ID! + id: ID! @__RelayResolverModelGeneratedIDField __relay_model_instance: RelayResolverValue! @relay_resolver(generated_fragment: true, fragment_name: "Cat__id", import_name: "Cat", import_path: "Cat.js", inject_fragment_data: "id") @resolver_source_hash(value: "a9744194670e59356ad37913eff9defe") @unselectable(reason: "This field is intended only for Relay's internal use") } diff --git a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/conflicting-type-definitions.error.expected b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/conflicting-type-definitions.error.expected index 382f083978619..2310a856619c1 100644 --- a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/conflicting-type-definitions.error.expected +++ b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/conflicting-type-definitions.error.expected @@ -81,7 +81,7 @@ Type( ), ) type Cat @__RelayResolverModel { - id: ID! + id: ID! @__RelayResolverModelGeneratedIDField __relay_model_instance: RelayResolverValue! @relay_resolver(generated_fragment: true, fragment_name: "Cat__id", import_name: "Cat", import_path: "Cat.js", inject_fragment_data: "id") @resolver_source_hash(value: "1b432d1f1ef72adb427857abb24a5068") @unselectable(reason: "This field is intended only for Relay's internal use") } @@ -90,31 +90,31 @@ Type( WeakObjectType( WeakObjectIr { type_name: Identifier { - span: 104:111, + span: 39:46, token: Token { - span: 104:111, + span: 39:46, kind: Identifier, }, value: "WeakCat", }, - rhs_location: AnotherWeakCat.js:104:111, + rhs_location: WeakCat.js:39:46, description: None, hack_source: None, deprecated: None, - location: AnotherWeakCat.js:104:111, + location: WeakCat.js:39:46, implements_interfaces: [], source_hash: ResolverSourceHash( - "fbdfc15b898dadb276e02cb59997fd3e", + "d87c48cc6e55a86fc69fd0d1f73d6bea", ), type_confirmed: true, }, ), ) -scalar WeakCatModel @__RelayCustomScalar(path: "AnotherWeakCat.js", export_name: "WeakCat") +scalar WeakCatModel @__RelayCustomScalar(path: "WeakCat.js", export_name: "WeakCat") type WeakCat @__RelayResolverModel @RelayOutputType @__RelayWeakObject { - __relay_model_instance: WeakCatModel! @resolver_source_hash(value: "fbdfc15b898dadb276e02cb59997fd3e") @unselectable(reason: "This field is intended only for Relay's internal use") + __relay_model_instance: WeakCatModel! @resolver_source_hash(value: "d87c48cc6e55a86fc69fd0d1f73d6bea") @unselectable(reason: "This field is intended only for Relay's internal use") } @@ -137,8 +137,8 @@ type WeakCat @__RelayResolverModel @RelayOutputType @__RelayWeakObject { ✖︎ Duplicate definition for type 'WeakCatModel'. - WeakCat.js:5:13 - 4 │ */ - 5 │ export type WeakCat = { + AnotherWeakCat.js:6:13 + 5 │ */ + 6 │ export type WeakCat = { │ ^^^^^^^ - 6 │ name: string + 7 │ name: string diff --git a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/custom-scalar-global-shadow.expected b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/custom-scalar-global-shadow.expected index 481e8a722d434..f221cabf3ef1f 100644 --- a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/custom-scalar-global-shadow.expected +++ b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/custom-scalar-global-shadow.expected @@ -76,6 +76,7 @@ Field( "b34abe1ebe87f22729175fd53c228fc1", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -116,7 +117,7 @@ Type( ), ) type Cat @__RelayResolverModel { - id: ID! + id: ID! @__RelayResolverModelGeneratedIDField __relay_model_instance: RelayResolverValue! @relay_resolver(generated_fragment: true, fragment_name: "Cat__id", import_name: "Cat", import_path: "Cat.js", inject_fragment_data: "id") @resolver_source_hash(value: "b34abe1ebe87f22729175fd53c228fc1") @unselectable(reason: "This field is intended only for Relay's internal use") } diff --git a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/custom-scalar-global.expected b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/custom-scalar-global.expected index 7d46be9b25603..2c7cdf1596219 100644 --- a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/custom-scalar-global.expected +++ b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/custom-scalar-global.expected @@ -68,6 +68,7 @@ Field( "dfc8ee95a857f2fd4e31a1f955c041c9", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -108,6 +109,6 @@ Type( ), ) type Cat @__RelayResolverModel { - id: ID! + id: ID! @__RelayResolverModelGeneratedIDField __relay_model_instance: RelayResolverValue! @relay_resolver(generated_fragment: true, fragment_name: "Cat__id", import_name: "Cat", import_path: "Cat.js", inject_fragment_data: "id") @resolver_source_hash(value: "dfc8ee95a857f2fd4e31a1f955c041c9") @unselectable(reason: "This field is intended only for Relay's internal use") } diff --git a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/custom-scalar.expected b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/custom-scalar.expected index 5efbe1fd2ba4c..ff103e6eb0493 100644 --- a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/custom-scalar.expected +++ b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/custom-scalar.expected @@ -80,6 +80,7 @@ Field( "69e6ff1ceadd103da3b3b9a805681c7c", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -180,6 +181,7 @@ Field( "69e6ff1ceadd103da3b3b9a805681c7c", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -220,6 +222,6 @@ Type( ), ) type Cat @__RelayResolverModel { - id: ID! + id: ID! @__RelayResolverModelGeneratedIDField __relay_model_instance: RelayResolverValue! @relay_resolver(generated_fragment: true, fragment_name: "Cat__id", import_name: "Cat", import_path: "Cat.js", inject_fragment_data: "id") @resolver_source_hash(value: "69e6ff1ceadd103da3b3b9a805681c7c") @unselectable(reason: "This field is intended only for Relay's internal use") } diff --git a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/deprecated.expected b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/deprecated.expected index 485a24add8aa7..39dc40283ed98 100644 --- a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/deprecated.expected +++ b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/deprecated.expected @@ -97,6 +97,7 @@ Field( "dcf10bac1041d7f30f36c2bd311d01b8", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -155,6 +156,7 @@ Field( "dcf10bac1041d7f30f36c2bd311d01b8", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -200,6 +202,6 @@ Type( ), ) type Cat @__RelayResolverModel { - id: ID! + id: ID! @__RelayResolverModelGeneratedIDField __relay_model_instance: RelayResolverValue! @relay_resolver(generated_fragment: true, fragment_name: "Cat__id", import_name: "Cat", import_path: "module.js", inject_fragment_data: "id") @resolver_source_hash(value: "dcf10bac1041d7f30f36c2bd311d01b8") @unselectable(reason: "This field is intended only for Relay's internal use") } diff --git a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/description.expected b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/description.expected index 5cbae24a345cd..7122f0c700bf7 100644 --- a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/description.expected +++ b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/description.expected @@ -93,6 +93,7 @@ Field( "77d8976fca517fd3aea7ae4517ed9579", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -153,6 +154,7 @@ Field( "77d8976fca517fd3aea7ae4517ed9579", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -193,7 +195,7 @@ Type( ), ) type Cat @__RelayResolverModel { - id: ID! + id: ID! @__RelayResolverModelGeneratedIDField __relay_model_instance: RelayResolverValue! @relay_resolver(generated_fragment: true, fragment_name: "Cat__id", import_name: "Cat", import_path: "module.js", inject_fragment_data: "id") @resolver_source_hash(value: "77d8976fca517fd3aea7ae4517ed9579") @unselectable(reason: "This field is intended only for Relay's internal use") } diff --git a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/idof.expected b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/idof.expected index a8e5a47a4f008..e2aa27e3813a3 100644 --- a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/idof.expected +++ b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/idof.expected @@ -92,6 +92,7 @@ Field( "08165d712e1fcab6840e997daaebf69e", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -144,6 +145,7 @@ Field( "08165d712e1fcab6840e997daaebf69e", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -184,7 +186,7 @@ Type( ), ) type Cat @__RelayResolverModel { - id: ID! + id: ID! @__RelayResolverModelGeneratedIDField __relay_model_instance: RelayResolverValue! @relay_resolver(generated_fragment: true, fragment_name: "Cat__id", import_name: "Cat", import_path: "module.js", inject_fragment_data: "id") @resolver_source_hash(value: "08165d712e1fcab6840e997daaebf69e") @unselectable(reason: "This field is intended only for Relay's internal use") } @@ -221,7 +223,7 @@ Type( ), ) type Dog @__RelayResolverModel { - id: ID! + id: ID! @__RelayResolverModelGeneratedIDField __relay_model_instance: RelayResolverValue! @relay_resolver(generated_fragment: true, fragment_name: "Dog__id", import_name: "Dog", import_path: "module.js", inject_fragment_data: "id") @resolver_source_hash(value: "08165d712e1fcab6840e997daaebf69e") @unselectable(reason: "This field is intended only for Relay's internal use") } diff --git a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/live.expected b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/live.expected index 8cd74bef370cf..b6e547ec4452f 100644 --- a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/live.expected +++ b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/live.expected @@ -78,6 +78,7 @@ Field( "088060a75d59266ad0547d9a0312562f", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -118,6 +119,6 @@ Type( ), ) type Cat @__RelayResolverModel { - id: ID! + id: ID! @__RelayResolverModelGeneratedIDField __relay_model_instance: RelayResolverValue! @relay_resolver(generated_fragment: true, fragment_name: "Cat__id", import_name: "Cat", import_path: "module.js", inject_fragment_data: "id") @resolver_source_hash(value: "088060a75d59266ad0547d9a0312562f") @unselectable(reason: "This field is intended only for Relay's internal use") } diff --git a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/optional-strong-type.expected b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/optional-strong-type.expected index 58d385f156830..9d7ad332eb784 100644 --- a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/optional-strong-type.expected +++ b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/optional-strong-type.expected @@ -57,7 +57,7 @@ Type( ), ) type Cat @__RelayResolverModel { - id: ID! + id: ID! @__RelayResolverModelGeneratedIDField __relay_model_instance: RelayResolverValue! @relay_resolver(generated_fragment: true, fragment_name: "Cat__id", import_name: "Cat", import_path: "Cat.js", inject_fragment_data: "id") @resolver_source_hash(value: "4325ac2bef4354788b5ccb1c5f5ffd53") @unselectable(reason: "This field is intended only for Relay's internal use") } diff --git a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/plural-optional.expected b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/plural-optional.expected index 8c6364f36b285..1782535abdc4a 100644 --- a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/plural-optional.expected +++ b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/plural-optional.expected @@ -92,6 +92,7 @@ Field( "78b9d79fcbb849dee2ae7a1d6d275e22", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -144,6 +145,7 @@ Field( "78b9d79fcbb849dee2ae7a1d6d275e22", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -184,6 +186,6 @@ Type( ), ) type Cat @__RelayResolverModel { - id: ID! + id: ID! @__RelayResolverModelGeneratedIDField __relay_model_instance: RelayResolverValue! @relay_resolver(generated_fragment: true, fragment_name: "Cat__id", import_name: "Cat", import_path: "Cat.js", inject_fragment_data: "id") @resolver_source_hash(value: "b0ce1a838dad74fce5422c930d8d9fc3") @unselectable(reason: "This field is intended only for Relay's internal use") } diff --git a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/primitive-types.expected b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/primitive-types.expected index 46f88febe4003..3afe56053829b 100644 --- a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/primitive-types.expected +++ b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/primitive-types.expected @@ -91,6 +91,7 @@ Field( "41bd781893c904ec9352255c4f6e974e", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -143,6 +144,7 @@ Field( "41bd781893c904ec9352255c4f6e974e", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -195,6 +197,7 @@ Field( "41bd781893c904ec9352255c4f6e974e", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -247,6 +250,7 @@ Field( "41bd781893c904ec9352255c4f6e974e", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -287,6 +291,6 @@ Type( ), ) type Cat @__RelayResolverModel { - id: ID! + id: ID! @__RelayResolverModelGeneratedIDField __relay_model_instance: RelayResolverValue! @relay_resolver(generated_fragment: true, fragment_name: "Cat__id", import_name: "Cat", import_path: "module.js", inject_fragment_data: "id") @resolver_source_hash(value: "41bd781893c904ec9352255c4f6e974e") @unselectable(reason: "This field is intended only for Relay's internal use") } diff --git a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/property-lookup.expected b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/property-lookup.expected new file mode 100644 index 0000000000000..b3e24f2fd4190 --- /dev/null +++ b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/property-lookup.expected @@ -0,0 +1,383 @@ +==================================== INPUT ==================================== +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +//- module.js + +/** + * @RelayResolver + */ +export type Cat = { + /** + * @gqlField + * + * This is the full name of the cat + */ + name: string, + /** + * @gqlField + */ + color: string, + /** + * @gqlField + * @deprecated Just use name instead + */ + first_name: number, + /** + * @gqlField other_name + */ + alias: number, +}; +==================================== OUTPUT =================================== +Field( + TerseRelayResolver( + TerseRelayResolverIr { + field: FieldDefinition { + name: Identifier { + span: 17:27, + token: Token { + span: 17:27, + kind: Identifier, + }, + value: "other_name", + }, + type_: Named( + NamedTypeAnnotation { + name: Identifier { + span: 504:510, + token: Token { + span: 504:510, + kind: Identifier, + }, + value: "Float", + }, + }, + ), + arguments: None, + directives: [], + description: None, + hack_source: None, + span: 17:27, + }, + type_: WithLocation { + location: module.js:231:234, + item: "Cat", + }, + root_fragment: None, + deprecated: None, + semantic_non_null: Some( + ConstantDirective { + span: 17:27, + at: Token { + span: 0:0, + kind: Empty, + }, + name: Identifier { + span: 17:27, + token: Token { + span: 0:0, + kind: Empty, + }, + value: "semanticNonNull", + }, + arguments: None, + }, + ), + live: None, + location: module.js:17:27, + fragment_arguments: None, + source_hash: ResolverSourceHash( + "6f05c358daee6821384cbf6c03fcb703", + ), + type_confirmed: true, + property_lookup_name: Some( + WithLocation { + location: module.js:497:502, + item: "alias", + }, + ), + }, + ), +) +extend type Cat { + other_name: Float @relay_resolver(fragment_name: "Cat____relay_model_instance", generated_fragment: true, inject_fragment_data: "__relay_model_instance", type_confirmed: true, has_output_type: true, import_name: "other_name", import_path: "module.js", property_lookup_name: "alias") @resolver_source_hash(value: "6f05c358daee6821384cbf6c03fcb703") @semanticNonNull +} + + +Field( + TerseRelayResolver( + TerseRelayResolverIr { + field: FieldDefinition { + name: Identifier { + span: 311:315, + token: Token { + span: 311:315, + kind: Identifier, + }, + value: "name", + }, + type_: Named( + NamedTypeAnnotation { + name: Identifier { + span: 317:323, + token: Token { + span: 317:323, + kind: Identifier, + }, + value: "String", + }, + }, + ), + arguments: None, + directives: [], + description: Some( + StringNode { + token: Token { + span: 241:308, + kind: Empty, + }, + value: "\nThis is the full name of the cat", + }, + ), + hack_source: None, + span: 311:315, + }, + type_: WithLocation { + location: module.js:231:234, + item: "Cat", + }, + root_fragment: None, + deprecated: None, + semantic_non_null: Some( + ConstantDirective { + span: 311:315, + at: Token { + span: 0:0, + kind: Empty, + }, + name: Identifier { + span: 311:315, + token: Token { + span: 0:0, + kind: Empty, + }, + value: "semanticNonNull", + }, + arguments: None, + }, + ), + live: None, + location: module.js:311:315, + fragment_arguments: None, + source_hash: ResolverSourceHash( + "6f05c358daee6821384cbf6c03fcb703", + ), + type_confirmed: true, + property_lookup_name: Some( + WithLocation { + location: module.js:311:315, + item: "name", + }, + ), + }, + ), +) +extend type Cat { + name: String @relay_resolver(fragment_name: "Cat____relay_model_instance", generated_fragment: true, inject_fragment_data: "__relay_model_instance", type_confirmed: true, has_output_type: true, import_name: "name", import_path: "module.js", property_lookup_name: "name") @resolver_source_hash(value: "6f05c358daee6821384cbf6c03fcb703") @semanticNonNull +} + + +Field( + TerseRelayResolver( + TerseRelayResolverIr { + field: FieldDefinition { + name: Identifier { + span: 354:359, + token: Token { + span: 354:359, + kind: Identifier, + }, + value: "color", + }, + type_: Named( + NamedTypeAnnotation { + name: Identifier { + span: 361:367, + token: Token { + span: 361:367, + kind: Identifier, + }, + value: "String", + }, + }, + ), + arguments: None, + directives: [], + description: None, + hack_source: None, + span: 354:359, + }, + type_: WithLocation { + location: module.js:231:234, + item: "Cat", + }, + root_fragment: None, + deprecated: None, + semantic_non_null: Some( + ConstantDirective { + span: 354:359, + at: Token { + span: 0:0, + kind: Empty, + }, + name: Identifier { + span: 354:359, + token: Token { + span: 0:0, + kind: Empty, + }, + value: "semanticNonNull", + }, + arguments: None, + }, + ), + live: None, + location: module.js:354:359, + fragment_arguments: None, + source_hash: ResolverSourceHash( + "6f05c358daee6821384cbf6c03fcb703", + ), + type_confirmed: true, + property_lookup_name: Some( + WithLocation { + location: module.js:354:359, + item: "color", + }, + ), + }, + ), +) +extend type Cat { + color: String @relay_resolver(fragment_name: "Cat____relay_model_instance", generated_fragment: true, inject_fragment_data: "__relay_model_instance", type_confirmed: true, has_output_type: true, import_name: "color", import_path: "module.js", property_lookup_name: "color") @resolver_source_hash(value: "6f05c358daee6821384cbf6c03fcb703") @semanticNonNull +} + + +Field( + TerseRelayResolver( + TerseRelayResolverIr { + field: FieldDefinition { + name: Identifier { + span: 437:447, + token: Token { + span: 437:447, + kind: Identifier, + }, + value: "first_name", + }, + type_: Named( + NamedTypeAnnotation { + name: Identifier { + span: 449:455, + token: Token { + span: 449:455, + kind: Identifier, + }, + value: "Float", + }, + }, + ), + arguments: None, + directives: [], + description: None, + hack_source: None, + span: 437:447, + }, + type_: WithLocation { + location: module.js:231:234, + item: "Cat", + }, + root_fragment: None, + deprecated: Some( + PopulatedIrField( + PopulatedIrField { + key_location: module.js:23:33, + value: WithLocation { + location: module.js:34:55, + item: "Just use name instead", + }, + }, + ), + ), + semantic_non_null: Some( + ConstantDirective { + span: 437:447, + at: Token { + span: 0:0, + kind: Empty, + }, + name: Identifier { + span: 437:447, + token: Token { + span: 0:0, + kind: Empty, + }, + value: "semanticNonNull", + }, + arguments: None, + }, + ), + live: None, + location: module.js:437:447, + fragment_arguments: None, + source_hash: ResolverSourceHash( + "6f05c358daee6821384cbf6c03fcb703", + ), + type_confirmed: true, + property_lookup_name: Some( + WithLocation { + location: module.js:437:447, + item: "first_name", + }, + ), + }, + ), +) +extend type Cat { + first_name: Float @relay_resolver(fragment_name: "Cat____relay_model_instance", generated_fragment: true, inject_fragment_data: "__relay_model_instance", type_confirmed: true, has_output_type: true, import_name: "first_name", import_path: "module.js", property_lookup_name: "first_name") @resolver_source_hash(value: "6f05c358daee6821384cbf6c03fcb703") @deprecated(reason: "Just use name instead") @semanticNonNull +} + + +Type( + WeakObjectType( + WeakObjectIr { + type_name: Identifier { + span: 231:234, + token: Token { + span: 231:234, + kind: Identifier, + }, + value: "Cat", + }, + rhs_location: module.js:231:234, + description: None, + hack_source: None, + deprecated: None, + location: module.js:231:234, + implements_interfaces: [], + source_hash: ResolverSourceHash( + "6f05c358daee6821384cbf6c03fcb703", + ), + type_confirmed: true, + }, + ), +) +scalar CatModel @__RelayCustomScalar(path: "module.js", export_name: "Cat") + + +type Cat @__RelayResolverModel @RelayOutputType @__RelayWeakObject { + __relay_model_instance: CatModel! @resolver_source_hash(value: "6f05c358daee6821384cbf6c03fcb703") @unselectable(reason: "This field is intended only for Relay's internal use") +} diff --git a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/property-lookup.input b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/property-lookup.input new file mode 100644 index 0000000000000..89f302ccd2bb8 --- /dev/null +++ b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/property-lookup.input @@ -0,0 +1,33 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +//- module.js + +/** + * @RelayResolver + */ +export type Cat = { + /** + * @gqlField + * + * This is the full name of the cat + */ + name: string, + /** + * @gqlField + */ + color: string, + /** + * @gqlField + * @deprecated Just use name instead + */ + first_name: number, + /** + * @gqlField other_name + */ + alias: number, +}; diff --git a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/resolver-functions-on-Query.expected b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/resolver-functions-on-Query.expected index 6185f9d154df7..28ba45fe6cf49 100644 --- a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/resolver-functions-on-Query.expected +++ b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/resolver-functions-on-Query.expected @@ -62,6 +62,7 @@ Field( "38a7a82b77c64472c7bf872babfeece3", ), type_confirmed: true, + property_lookup_name: None, }, ), ) diff --git a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/return-non-optional-type.expected b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/return-non-optional-type.expected index d4d58f898a575..5c18580a4a78b 100644 --- a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/return-non-optional-type.expected +++ b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/return-non-optional-type.expected @@ -171,6 +171,7 @@ Field( "b97d5d4b012e01a0c3070bd919062a36", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -240,6 +241,7 @@ Field( "b97d5d4b012e01a0c3070bd919062a36", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -322,6 +324,7 @@ Field( "b97d5d4b012e01a0c3070bd919062a36", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -396,6 +399,7 @@ Field( "b97d5d4b012e01a0c3070bd919062a36", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -461,6 +465,7 @@ Field( "b97d5d4b012e01a0c3070bd919062a36", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -530,6 +535,7 @@ Field( "b97d5d4b012e01a0c3070bd919062a36", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -599,6 +605,7 @@ Field( "b97d5d4b012e01a0c3070bd919062a36", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -672,6 +679,7 @@ Field( "b97d5d4b012e01a0c3070bd919062a36", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -763,6 +771,7 @@ Field( "b97d5d4b012e01a0c3070bd919062a36", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -858,6 +867,7 @@ Field( "b97d5d4b012e01a0c3070bd919062a36", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -944,6 +954,7 @@ Field( "b97d5d4b012e01a0c3070bd919062a36", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -984,7 +995,7 @@ Type( ), ) type Cat @__RelayResolverModel { - id: ID! + id: ID! @__RelayResolverModelGeneratedIDField __relay_model_instance: RelayResolverValue! @relay_resolver(generated_fragment: true, fragment_name: "Cat__id", import_name: "Cat", import_path: "module.js", inject_fragment_data: "id") @resolver_source_hash(value: "b97d5d4b012e01a0c3070bd919062a36") @unselectable(reason: "This field is intended only for Relay's internal use") } diff --git a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/return-optional-strong-object.expected b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/return-optional-strong-object.expected index fa1d85bb329fa..3593788185dff 100644 --- a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/return-optional-strong-object.expected +++ b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/return-optional-strong-object.expected @@ -72,6 +72,7 @@ Field( "2cd51ab90c9beb1c509ff624a8ba6609", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -112,6 +113,6 @@ Type( ), ) type Cat @__RelayResolverModel { - id: ID! + id: ID! @__RelayResolverModelGeneratedIDField __relay_model_instance: RelayResolverValue! @relay_resolver(generated_fragment: true, fragment_name: "Cat__id", import_name: "Cat", import_path: "Cat.js", inject_fragment_data: "id") @resolver_source_hash(value: "b0ce1a838dad74fce5422c930d8d9fc3") @unselectable(reason: "This field is intended only for Relay's internal use") } diff --git a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/return-optional-weak-object.expected b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/return-optional-weak-object.expected index e845d15160543..87b2e96c4aa4a 100644 --- a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/return-optional-weak-object.expected +++ b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/return-optional-weak-object.expected @@ -73,6 +73,7 @@ Field( "1b4f4f49bebe8b72971595382a3f9b57", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -113,7 +114,7 @@ Type( ), ) type Cat @__RelayResolverModel { - id: ID! + id: ID! @__RelayResolverModelGeneratedIDField __relay_model_instance: RelayResolverValue! @relay_resolver(generated_fragment: true, fragment_name: "Cat__id", import_name: "Cat", import_path: "module.js", inject_fragment_data: "id") @resolver_source_hash(value: "1b4f4f49bebe8b72971595382a3f9b57") @unselectable(reason: "This field is intended only for Relay's internal use") } diff --git a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/return-relay-resolver-value.expected b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/return-relay-resolver-value.expected index f7da81a67fb75..35786ab6a6937 100644 --- a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/return-relay-resolver-value.expected +++ b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/return-relay-resolver-value.expected @@ -101,6 +101,7 @@ Field( "fc15c065174264428a3632fe9cf329d6", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -153,6 +154,7 @@ Field( "fc15c065174264428a3632fe9cf329d6", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -205,6 +207,7 @@ Field( "fc15c065174264428a3632fe9cf329d6", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -245,6 +248,6 @@ Type( ), ) type Cat @__RelayResolverModel { - id: ID! + id: ID! @__RelayResolverModelGeneratedIDField __relay_model_instance: RelayResolverValue! @relay_resolver(generated_fragment: true, fragment_name: "Cat__id", import_name: "Cat", import_path: "module.js", inject_fragment_data: "id") @resolver_source_hash(value: "fc15c065174264428a3632fe9cf329d6") @unselectable(reason: "This field is intended only for Relay's internal use") } diff --git a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/root-fragment-arguments.expected b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/root-fragment-arguments.expected index 82a9234e42270..6e1d5a961519e 100644 --- a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/root-fragment-arguments.expected +++ b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/root-fragment-arguments.expected @@ -113,6 +113,7 @@ Field( "d534da5736dd3e656f91a8593a50b413", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -153,6 +154,6 @@ Type( ), ) type Cat @__RelayResolverModel { - id: ID! + id: ID! @__RelayResolverModelGeneratedIDField __relay_model_instance: RelayResolverValue! @relay_resolver(generated_fragment: true, fragment_name: "Cat__id", import_name: "Cat", import_path: "module.js", inject_fragment_data: "id") @resolver_source_hash(value: "d534da5736dd3e656f91a8593a50b413") @unselectable(reason: "This field is intended only for Relay's internal use") } diff --git a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/root-fragment.expected b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/root-fragment.expected index 03dc1d05543f5..f0140b089c6ad 100644 --- a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/root-fragment.expected +++ b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/root-fragment.expected @@ -88,6 +88,7 @@ Field( "0707351e18761dc811f8a249f18a13d8", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -128,6 +129,6 @@ Type( ), ) type Cat @__RelayResolverModel { - id: ID! + id: ID! @__RelayResolverModelGeneratedIDField __relay_model_instance: RelayResolverValue! @relay_resolver(generated_fragment: true, fragment_name: "Cat__id", import_name: "Cat", import_path: "module.js", inject_fragment_data: "id") @resolver_source_hash(value: "0707351e18761dc811f8a249f18a13d8") @unselectable(reason: "This field is intended only for Relay's internal use") } diff --git a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/single-module.expected b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/single-module.expected index 4fb7e58e25b52..e05bba62abaad 100644 --- a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/single-module.expected +++ b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/single-module.expected @@ -77,6 +77,7 @@ Field( "7bbe33db29d93d787719b8c25219f5a0", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -129,6 +130,7 @@ Field( "7bbe33db29d93d787719b8c25219f5a0", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -169,6 +171,6 @@ Type( ), ) type Cat @__RelayResolverModel { - id: ID! + id: ID! @__RelayResolverModelGeneratedIDField __relay_model_instance: RelayResolverValue! @relay_resolver(generated_fragment: true, fragment_name: "Cat__id", import_name: "Cat", import_path: "module.js", inject_fragment_data: "id") @resolver_source_hash(value: "7bbe33db29d93d787719b8c25219f5a0") @unselectable(reason: "This field is intended only for Relay's internal use") } diff --git a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/strong-type-define-flow-opaque-within.expected b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/strong-type-define-flow-opaque-within.expected index 35583b2f6f095..370871257f047 100644 --- a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/strong-type-define-flow-opaque-within.expected +++ b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/strong-type-define-flow-opaque-within.expected @@ -81,6 +81,7 @@ Field( "7bd696850a88d12b6ebff3689599d699", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -133,6 +134,7 @@ Field( "179d2d09d0726f713ad290a603cc35c6", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -173,6 +175,6 @@ Type( ), ) type Cat @__RelayResolverModel { - id: ID! + id: ID! @__RelayResolverModelGeneratedIDField __relay_model_instance: RelayResolverValue! @relay_resolver(generated_fragment: true, fragment_name: "Cat__id", import_name: "Cat", import_path: "Cat.js", inject_fragment_data: "id") @resolver_source_hash(value: "7bd696850a88d12b6ebff3689599d699") @unselectable(reason: "This field is intended only for Relay's internal use") } diff --git a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/strong-type-define-flow-within.expected b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/strong-type-define-flow-within.expected index b47abb3e9657d..1a4d2ee9e082c 100644 --- a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/strong-type-define-flow-within.expected +++ b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/strong-type-define-flow-within.expected @@ -81,6 +81,7 @@ Field( "8416f4ce9b514fc153597a144f614204", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -133,6 +134,7 @@ Field( "179d2d09d0726f713ad290a603cc35c6", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -173,6 +175,6 @@ Type( ), ) type Cat @__RelayResolverModel { - id: ID! + id: ID! @__RelayResolverModelGeneratedIDField __relay_model_instance: RelayResolverValue! @relay_resolver(generated_fragment: true, fragment_name: "Cat__id", import_name: "Cat", import_path: "Cat.js", inject_fragment_data: "id") @resolver_source_hash(value: "8416f4ce9b514fc153597a144f614204") @unselectable(reason: "This field is intended only for Relay's internal use") } diff --git a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/unsupported-type.error.expected b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/unsupported-type.error.expected index 4df4336e0644c..b28ace5aa25de 100644 --- a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/unsupported-type.error.expected +++ b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/unsupported-type.error.expected @@ -57,7 +57,7 @@ Type( ), ) type Cat @__RelayResolverModel { - id: ID! + id: ID! @__RelayResolverModelGeneratedIDField __relay_model_instance: RelayResolverValue! @relay_resolver(generated_fragment: true, fragment_name: "Cat__id", import_name: "Cat", import_path: "module.js", inject_fragment_data: "id") @resolver_source_hash(value: "dbaa81766321d75c0da7fa6c70895562") @unselectable(reason: "This field is intended only for Relay's internal use") } diff --git a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/weak-object.expected b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/weak-object.expected index 8f0d5ff663338..fa310e1647dc5 100644 --- a/compiler/crates/relay-schema-generation/tests/docblock/fixtures/weak-object.expected +++ b/compiler/crates/relay-schema-generation/tests/docblock/fixtures/weak-object.expected @@ -88,6 +88,7 @@ Field( "67480ced829a656aa50d7f407d9529d0", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -140,6 +141,7 @@ Field( "67480ced829a656aa50d7f407d9529d0", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -192,6 +194,7 @@ Field( "67480ced829a656aa50d7f407d9529d0", ), type_confirmed: true, + property_lookup_name: None, }, ), ) @@ -232,7 +235,7 @@ Type( ), ) type Cat @__RelayResolverModel { - id: ID! + id: ID! @__RelayResolverModelGeneratedIDField __relay_model_instance: RelayResolverValue! @relay_resolver(generated_fragment: true, fragment_name: "Cat__id", import_name: "Cat", import_path: "module.js", inject_fragment_data: "id") @resolver_source_hash(value: "67480ced829a656aa50d7f407d9529d0") @unselectable(reason: "This field is intended only for Relay's internal use") } diff --git a/compiler/crates/relay-schema-generation/tests/docblock_test.rs b/compiler/crates/relay-schema-generation/tests/docblock_test.rs index b3bf9139d1499..7c063fb841f72 100644 --- a/compiler/crates/relay-schema-generation/tests/docblock_test.rs +++ b/compiler/crates/relay-schema-generation/tests/docblock_test.rs @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<37bc46ba9cbfc9f4ef7b6b98faef30d0>> + * @generated SignedSource<> */ mod docblock; @@ -138,6 +138,13 @@ async fn primitive_types() { test_fixture(transform_fixture, file!(), "primitive-types.input", "docblock/fixtures/primitive-types.expected", input, expected).await; } +#[tokio::test] +async fn property_lookup() { + let input = include_str!("docblock/fixtures/property-lookup.input"); + let expected = include_str!("docblock/fixtures/property-lookup.expected"); + test_fixture(transform_fixture, file!(), "property-lookup.input", "docblock/fixtures/property-lookup.expected", input, expected).await; +} + #[tokio::test] async fn resolver_functions_on_query() { let input = include_str!("docblock/fixtures/resolver-functions-on-Query.input"); diff --git a/compiler/crates/relay-schema-generation/tests/extract.rs b/compiler/crates/relay-schema-generation/tests/extract.rs index 8b72a1b0ceee9..a6a31074fcfea 100644 --- a/compiler/crates/relay-schema-generation/tests/extract.rs +++ b/compiler/crates/relay-schema-generation/tests/extract.rs @@ -13,9 +13,9 @@ use graphql_cli::DiagnosticPrinter; use hermes_comments::find_nodes_after_comments; use hermes_estree::IntoFunction; use hermes_estree::Node; -use hermes_parser::parse; use hermes_parser::ParserDialect; use hermes_parser::ParserFlags; +use hermes_parser::parse; use relay_schema_generation::RelayResolverExtractor; pub async fn transform_fixture(fixture: &Fixture<'_>) -> Result { diff --git a/compiler/crates/relay-schema/Cargo.toml b/compiler/crates/relay-schema/Cargo.toml index 9cf972fb34e59..1191517fbd5da 100644 --- a/compiler/crates/relay-schema/Cargo.toml +++ b/compiler/crates/relay-schema/Cargo.toml @@ -4,7 +4,7 @@ name = "relay-schema" version = "0.0.0" authors = ["Facebook"] -edition = "2021" +edition = "2024" repository = "https://github.com/facebook/relay" license = "MIT" @@ -13,5 +13,5 @@ common = { path = "../common" } docblock-shared = { path = "../docblock-shared" } graphql-syntax = { path = "../graphql-syntax" } intern = { path = "../intern" } -lazy_static = "1.4" +lazy_static = "1.5" schema = { path = "../schema" } diff --git a/compiler/crates/relay-schema/src/relay-extensions.graphql b/compiler/crates/relay-schema/src/relay-extensions.graphql index e0020c5852f5d..383eb9cf865e4 100644 --- a/compiler/crates/relay-schema/src/relay-extensions.graphql +++ b/compiler/crates/relay-schema/src/relay-extensions.graphql @@ -5,6 +5,7 @@ directive @relay_test_operation( DO_NOT_USE_use_in_production: Boolean = false @static + emitRawText: Boolean = false @static ) on QUERY | MUTATION | SUBSCRIPTION """ @@ -26,7 +27,7 @@ directive @no_inline(raw_response_type: Boolean) on FRAGMENT_DEFINITION A directive added to queries and fragments which causes the Relay client to throw if reading a field that has an error. Relay will also honor the @semanticNonNull -direcitve on fields read from that query or fragment. Emitted types for such +directive on fields read from that query or fragment. Emitted types for such fields will be non-null. Requires the `experimental_emit_semantic_nullability_types` typegen configuration to be enabled. @@ -404,4 +405,4 @@ If added to a query, resolvers in that query to run at exec-time, rather than re This means the resolvers are run when the query data is requested rather than when the query is used (i.e. when the network request is made instead of at render time). """ -directive @exec_time_resolvers on QUERY +directive @exec_time_resolvers(enabledProvider: String) on QUERY diff --git a/compiler/crates/relay-test-schema/Cargo.toml b/compiler/crates/relay-test-schema/Cargo.toml index 53279f59bd1f2..179beb5475398 100644 --- a/compiler/crates/relay-test-schema/Cargo.toml +++ b/compiler/crates/relay-test-schema/Cargo.toml @@ -4,12 +4,12 @@ name = "relay-test-schema" version = "0.0.0" authors = ["Facebook"] -edition = "2021" +edition = "2024" repository = "https://github.com/facebook/relay" license = "MIT" [dependencies] common = { path = "../common" } -lazy_static = "1.4" +lazy_static = "1.5" relay-schema = { path = "../relay-schema" } schema = { path = "../schema" } diff --git a/compiler/crates/relay-test-schema/src/testschema.graphql b/compiler/crates/relay-test-schema/src/testschema.graphql index 0d049e5efbdcd..303c71deb757a 100644 --- a/compiler/crates/relay-test-schema/src/testschema.graphql +++ b/compiler/crates/relay-test-schema/src/testschema.graphql @@ -14,7 +14,7 @@ directive @credentials( directive @live_query(polling_interval: Int, config_id: String) on QUERY -directive @live(config_id: String) on FIELD +directive @live on FIELD directive @customDirective(level: Int!) on FIELD | FRAGMENT_SPREAD @@ -959,7 +959,7 @@ type PlainUserRenderer { } union UserNameRenderer = - PlainUserNameRenderer + | PlainUserNameRenderer | MarkdownUserNameRenderer | CustomNameRenderer diff --git a/compiler/crates/relay-test-schema/src/testschema_with_custom_id.graphql b/compiler/crates/relay-test-schema/src/testschema_with_custom_id.graphql index 5c30aad299b27..596c9dc482651 100644 --- a/compiler/crates/relay-test-schema/src/testschema_with_custom_id.graphql +++ b/compiler/crates/relay-test-schema/src/testschema_with_custom_id.graphql @@ -933,7 +933,7 @@ type PlainUserRenderer { } union UserNameRenderer = - PlainUserNameRenderer + | PlainUserNameRenderer | MarkdownUserNameRenderer | CustomNameRenderer diff --git a/compiler/crates/relay-transforms/Cargo.toml b/compiler/crates/relay-transforms/Cargo.toml index 9db7b10355f0f..0ff32ae4f5053 100644 --- a/compiler/crates/relay-transforms/Cargo.toml +++ b/compiler/crates/relay-transforms/Cargo.toml @@ -1,10 +1,10 @@ -# @generated by autocargo from //relay/oss/crates/relay-transforms:[apply_fragment_arguments_test,assignable_directive_test,assignable_fragment_spread_test,catch_directive_test,client_edges_test,client_extensions_test,declarative_connection_test,disallow_typename_on_root_test,fragment_alias_directive_test,generate_data_driven_dependency_metadata_test,generate_live_query_metadata_test,generate_relay_resolvers_operations_for_nested_objects_test,graphql-client_extensions_abstract_types-test,graphql-defer_stream-test,graphql-disallow_non_node_id_fields-test,graphql-disallow_required_on_non_null_field-test,graphql-disallow_reserved_aliases-test,graphql-disallowreadtime_features_in_mutations-test,graphql-flatten-test,graphql-generate_id_field-test,graphql-generate_typename-test,graphql-inline_fragments-test,graphql-mask-test,graphql-match-client-only-test,graphql-match-client-resolver-test,graphql-match-test,graphql-node_identifier-test,graphql-refetchable_fragment_test,graphql-skip_client_extensions-test,graphql-skip_redundant_nodes-test,graphql-skip_unreachable_nodes-test,graphql-sort_selections-test,graphql-subscription_transform-test,graphql-validate_deprecated_fields_test,graphql-validate_fragment_alias_conflict-test,graphql-validate_module_names-test,graphql-validate_relay_directives-test,graphql-validate_required_arguments_test,graphql-validate_server_only_directives-test,graphql-validate_unused_variables-test,inline_data_fragment_test,provided-variable-fragment-transform-test,relay-actor-change-test,relay-transforms,relay_resolvers_abstract_types_test,relay_resolvers_test,relay_test_operation_test,required_directive_test,skip_unused_variables_test,transform_connections_test,updatable_directive_test,updatable_fragment_spread_test,validate_connections_schema_test,validate_connections_test,validate_global_variable_names_test,validate_global_variables-test,validate_no_double_underscore_alias_test,validate_no_unselectable_selections_test,validate_static_args] +# @generated by autocargo from //relay/oss/crates/relay-transforms:[apply_fragment_arguments_test,assignable_directive_test,assignable_fragment_spread_test,catch_directive_test,client_edges_test,client_extensions_test,declarative_connection_test,disallow_typename_on_root_test,fragment_alias_directive_test,generate_data_driven_dependency_metadata_test,generate_live_query_metadata_test,generate_relay_resolvers_operations_for_nested_objects_test,graphql-client_extensions_abstract_types-test,graphql-defer_stream-test,graphql-disallow_non_node_id_fields-test,graphql-disallow_required_on_non_null_field-test,graphql-disallow_reserved_aliases-test,graphql-disallowreadtime_features_in_mutations-test,graphql-flatten-test,graphql-generate_id_field-test,graphql-generate_typename-test,graphql-inline_fragments-test,graphql-mask-test,graphql-match-client-only-test,graphql-match-client-resolver-test,graphql-match-test,graphql-node_identifier-test,graphql-refetchable_fragment_test,graphql-skip_client_extensions-test,graphql-skip_redundant_nodes-test,graphql-skip_unreachable_nodes-test,graphql-sort_selections-test,graphql-subscription_transform-test,graphql-validate_deprecated_fields_test,graphql-validate_fragment_alias_conflict-test,graphql-validate_module_names-test,graphql-validate_relay_directives-test,graphql-validate_required_arguments_test,graphql-validate_server_only_directives-test,graphql-validate_unused_variables-test,inline_data_fragment_test,provided-variable-fragment-transform-test,relay-actor-change-test,relay-transforms,relay_resolvers_abstract_types_test,relay_resolvers_test,relay_test_operation_test,required_directive_test,skip_unused_variables_test,transform_connections_test,updatable_directive_test,updatable_fragment_spread_test,validate-client-schema-extensions-use-catch-test,validate_connections_schema_test,validate_connections_test,validate_global_variable_names_test,validate_global_variables-test,validate_no_double_underscore_alias_test,validate_no_unselectable_selections_test,validate_static_args] [package] name = "relay-transforms" version = "0.0.0" authors = ["Facebook"] -edition = "2021" +edition = "2024" repository = "https://github.com/facebook/relay" license = "MIT" @@ -134,22 +134,22 @@ graphql-ir = { path = "../graphql-ir" } graphql-ir-validations = { path = "../graphql-ir-validations" } graphql-syntax = { path = "../graphql-syntax" } graphql-text-printer = { path = "../graphql-text-printer" } -indexmap = { version = "2.2.6", features = ["arbitrary", "rayon", "serde"] } +indexmap = { version = "2.9.0", features = ["arbitrary", "rayon", "serde"] } intern = { path = "../intern" } -itertools = "0.13.0" -lazy_static = "1.4" +itertools = "0.14.0" +lazy_static = "1.5" parking_lot = { version = "0.12.1", features = ["send_guard"] } -regex = "1.9.2" +regex = "1.11.1" relay-config = { path = "../relay-config" } relay-schema = { path = "../relay-schema" } -rustc-hash = "1.1.0" +rustc-hash = "2.1.1" schema = { path = "../schema" } -serde = { version = "1.0.185", features = ["derive", "rc"] } -thiserror = "1.0.64" +serde = { version = "1.0.219", features = ["derive", "rc"] } +thiserror = "2.0.12" [dev-dependencies] fixture-tests = { path = "../fixture-tests" } graphql-cli = { path = "../graphql-cli" } graphql-test-helpers = { path = "../graphql-test-helpers" } relay-test-schema = { path = "../relay-test-schema" } -tokio = { version = "1.41.0", features = ["full", "test-util", "tracing"] } +tokio = { version = "1.46.1", features = ["full", "test-util", "tracing"] } diff --git a/compiler/crates/relay-transforms/src/apply_fragment_arguments.rs b/compiler/crates/relay-transforms/src/apply_fragment_arguments.rs index 3ad1f666e900e..48156ef2995f7 100644 --- a/compiler/crates/relay-transforms/src/apply_fragment_arguments.rs +++ b/compiler/crates/relay-transforms/src/apply_fragment_arguments.rs @@ -18,9 +18,6 @@ use common::Location; use common::NamedItem; use common::SourceLocationKey; use common::WithLocation; -use graphql_ir::associated_data_impl; -use graphql_ir::transform_list; -use graphql_ir::transform_list_multi; use graphql_ir::Condition; use graphql_ir::ConditionValue; use graphql_ir::ConstantValue; @@ -29,7 +26,6 @@ use graphql_ir::FragmentDefinition; use graphql_ir::FragmentDefinitionName; use graphql_ir::FragmentDefinitionNameMap; use graphql_ir::FragmentDefinitionNameSet; -use graphql_ir::FragmentSignature; use graphql_ir::FragmentSpread; use graphql_ir::InlineFragment; use graphql_ir::OperationDefinition; @@ -45,24 +41,28 @@ use graphql_ir::Value; use graphql_ir::Variable; use graphql_ir::VariableDefinition; use graphql_ir::VariableName; +use graphql_ir::associated_data_impl; +use graphql_ir::transform_list; +use graphql_ir::transform_list_multi; use graphql_syntax::OperationKind; use intern::string_key::Intern; +use intern::string_key::StringKey; use intern::string_key::StringKeyIndexMap; use intern::string_key::StringKeyMap; use itertools::Itertools; -use scope::format_local_variable; use scope::Scope; +use scope::format_local_variable; use thiserror::Error; use super::get_applied_fragment_name; -use crate::match_::SplitOperationMetadata; +use crate::RawResponseGenerationMode; +use crate::RelayResolverMetadata; use crate::match_::DIRECTIVE_SPLIT_OPERATION; -use crate::no_inline::is_raw_response_type_enabled; +use crate::match_::SplitOperationMetadata; use crate::no_inline::NO_INLINE_DIRECTIVE_NAME; use crate::no_inline::PARENT_DOCUMENTS_ARG; +use crate::no_inline::is_raw_response_type_enabled; use crate::util::get_normalization_operation_name; -use crate::RawResponseGenerationMode; -use crate::RelayResolverMetadata; /// A transform that converts a set of documents containing fragments/fragment /// spreads *with* arguments to one where all arguments have been inlined. This @@ -169,7 +169,7 @@ struct ApplyFragmentArgumentsTransform<'flags, 'program, 'base_fragments> { split_operations: StringKeyMap<(Option, ProvidedVariablesMap)>, } -impl Transformer for ApplyFragmentArgumentsTransform<'_, '_, '_> { +impl Transformer<'_> for ApplyFragmentArgumentsTransform<'_, '_, '_> { const NAME: &'static str = "ApplyFragmentArgumentsTransform"; const VISIT_ARGUMENTS: bool = true; const VISIT_DIRECTIVES: bool = true; @@ -190,7 +190,22 @@ impl Transformer for ApplyFragmentArgumentsTransform<'_, '_, '_> { // this transform does not add the SplitOperation directive, so this // should be equal to checking whether the result is a split operation self.provided_variables.clear(); - transform_result + + match transform_result { + Transformed::Keep => Transformed::Keep, + Transformed::Replace(new_operation) => Transformed::Replace(new_operation), + Transformed::Delete => { + self.errors.push(Diagnostic::error( + ValidationMessage::EmptySelectionsInDocument { + document: "query", + name: operation.name.item.0, + }, + operation.name.location, + )); + + Transformed::Delete + } + } } else { let mut add_provided_variables = |new_operation: &mut OperationDefinition| { new_operation.variable_definitions.append( @@ -211,7 +226,17 @@ impl Transformer for ApplyFragmentArgumentsTransform<'_, '_, '_> { add_provided_variables(&mut new_operation); Transformed::Replace(new_operation) } - Transformed::Delete => Transformed::Delete, + Transformed::Delete => { + self.errors.push(Diagnostic::error( + ValidationMessage::EmptySelectionsInDocument { + document: "query", + name: operation.name.item.0, + }, + operation.name.location, + )); + + Transformed::Delete + } } } } @@ -299,12 +324,7 @@ impl Transformer for ApplyFragmentArgumentsTransform<'_, '_, '_> { fragment.name.location, FragmentDefinitionName(normalization_name), ), - signature: Some(FragmentSignature { - name: fragment.name, - variable_definitions: fragment.variable_definitions.clone(), - type_condition: fragment.type_condition, - directives: fragment.directives.clone(), - }), + signature: Some(fragment.as_ref().into()), })); // If the fragment type is abstract, we need to ensure that it's only evaluated at runtime if the // type of the object matches the fragment's type condition. Rather than reimplement type refinement @@ -323,23 +343,19 @@ impl Transformer for ApplyFragmentArgumentsTransform<'_, '_, '_> { } } - if let Some(applied_fragment) = self.apply_fragment(spread, fragment) { - let directives = self - .transform_directives(&spread.directives) - .replace_or_else(|| spread.directives.clone()); - Transformed::Replace(Selection::FragmentSpread(Arc::new(FragmentSpread { - fragment: applied_fragment.name, - arguments: Vec::new(), - directives, - signature: Some(FragmentSignature { - name: applied_fragment.name, - variable_definitions: applied_fragment.variable_definitions.clone(), - type_condition: applied_fragment.type_condition, - directives: applied_fragment.directives.clone(), - }), - }))) - } else { - Transformed::Delete + match self.apply_fragment(spread, fragment) { + Some(applied_fragment) => { + let directives = self + .transform_directives(&spread.directives) + .replace_or_else(|| spread.directives.clone()); + Transformed::Replace(Selection::FragmentSpread(Arc::new(FragmentSpread { + fragment: applied_fragment.name, + arguments: Vec::new(), + directives, + signature: Some(applied_fragment.as_ref().into()), + }))) + } + _ => Transformed::Delete, } } @@ -741,4 +757,14 @@ enum ValidationMessage { ProvidedVariableIncompatibleWithArguments { original_definition_name: VariableName, }, + #[error( + "After applying transforms to the {document} `{name}` selections of \ + the `{name}` that would be sent to the server are empty. \ + This is likely due to the use of `@skip`/`@include` directives with \ + constant values that remove all selections in the {document}. " + )] + EmptySelectionsInDocument { + name: StringKey, + document: &'static str, + }, } diff --git a/compiler/crates/relay-transforms/src/apply_transforms.rs b/compiler/crates/relay-transforms/src/apply_transforms.rs index e2800c176e5ce..b2ab379292414 100644 --- a/compiler/crates/relay-transforms/src/apply_transforms.rs +++ b/compiler/crates/relay-transforms/src/apply_transforms.rs @@ -7,20 +7,21 @@ use std::sync::Arc; -use common::sync::try_join; use common::DiagnosticsResult; use common::DirectiveName; use common::PerfLogEvent; use common::PerfLogger; +use common::sync::try_join; use graphql_ir::FragmentDefinitionNameSet; use graphql_ir::Program; +use raw_text::set_raw_text; use relay_config::ProjectConfig; use validate_operation_variables::ValidateVariablesOptions; use super::*; +use crate::apply_custom_transforms::CustomTransformsConfig; use crate::apply_custom_transforms::apply_after_custom_transforms; use crate::apply_custom_transforms::apply_before_custom_transforms; -use crate::apply_custom_transforms::CustomTransformsConfig; use crate::assignable_fragment_spread::annotate_updatable_fragment_spreads; use crate::assignable_fragment_spread::replace_updatable_fragment_spreads; use crate::client_extensions_abstract_types::client_extensions_abstract_types; @@ -152,8 +153,11 @@ fn apply_common_transforms( let log_event = perf_logger.create_event("apply_common_transforms"); log_event.string("project", project_config.name.to_string()); + // raw_text stores the operation text before transforms and should be executed first + let mut program = log_event.time("raw_text", || set_raw_text(&program))?; + let custom_transforms = &custom_transforms_config.and_then(|c| c.common_transforms.as_ref()); - let mut program = apply_before_custom_transforms( + program = apply_before_custom_transforms( &program, custom_transforms, project_config, diff --git a/compiler/crates/relay-transforms/src/assignable_fragment_spread/annotate_updatable_fragment_spreads.rs b/compiler/crates/relay-transforms/src/assignable_fragment_spread/annotate_updatable_fragment_spreads.rs index 7679638e2d095..351b665446bc2 100644 --- a/compiler/crates/relay-transforms/src/assignable_fragment_spread/annotate_updatable_fragment_spreads.rs +++ b/compiler/crates/relay-transforms/src/assignable_fragment_spread/annotate_updatable_fragment_spreads.rs @@ -39,7 +39,7 @@ struct AnnotateUpdatableFragmentSpreads<'s> { program: &'s Program, } -impl<'s> Transformer for AnnotateUpdatableFragmentSpreads<'s> { +impl Transformer<'_> for AnnotateUpdatableFragmentSpreads<'_> { const NAME: &'static str = "AnnotateUpdatableFragmentSpreads"; const VISIT_ARGUMENTS: bool = false; const VISIT_DIRECTIVES: bool = false; diff --git a/compiler/crates/relay-transforms/src/assignable_fragment_spread/replace_updatable_fragment_spreads.rs b/compiler/crates/relay-transforms/src/assignable_fragment_spread/replace_updatable_fragment_spreads.rs index de9a2df3253b1..5ff7e837af240 100644 --- a/compiler/crates/relay-transforms/src/assignable_fragment_spread/replace_updatable_fragment_spreads.rs +++ b/compiler/crates/relay-transforms/src/assignable_fragment_spread/replace_updatable_fragment_spreads.rs @@ -31,7 +31,7 @@ struct ReplaceAssignableFragmentSpreads<'s> { program: &'s Program, } -impl<'s> Transformer for ReplaceAssignableFragmentSpreads<'s> { +impl Transformer<'_> for ReplaceAssignableFragmentSpreads<'_> { const NAME: &'static str = "ReplaceAssignableFragmentSpreads"; const VISIT_ARGUMENTS: bool = false; const VISIT_DIRECTIVES: bool = false; diff --git a/compiler/crates/relay-transforms/src/assignable_fragment_spread/transform_assignable_fragment_spreads_in_regular_queries.rs b/compiler/crates/relay-transforms/src/assignable_fragment_spread/transform_assignable_fragment_spreads_in_regular_queries.rs index 1edaf971de21b..dd03c7e224c47 100644 --- a/compiler/crates/relay-transforms/src/assignable_fragment_spread/transform_assignable_fragment_spreads_in_regular_queries.rs +++ b/compiler/crates/relay-transforms/src/assignable_fragment_spread/transform_assignable_fragment_spreads_in_regular_queries.rs @@ -23,14 +23,15 @@ use graphql_ir::ScalarField; use graphql_ir::Selection; use graphql_ir::Transformed; use graphql_ir::Transformer; -use intern::string_key::Intern; use intern::Lookup; +use intern::string_key::Intern; use schema::Schema; -use super::ensure_discriminated_union_is_created; -use super::errors::ValidationMessage; use super::ASSIGNABLE_DIRECTIVE; use super::UPDATABLE_DIRECTIVE; +use super::ensure_discriminated_union_is_created; +use super::errors::ValidationMessage; +use crate::fragment_alias_directive::FRAGMENT_DANGEROUSLY_UNALIAS_DIRECTIVE_NAME; pub fn transform_assignable_fragment_spreads_in_regular_queries( program: &Program, @@ -58,7 +59,7 @@ struct AssignableFragmentSpread<'s> { path: Vec, } -impl<'s> AssignableFragmentSpread<'s> { +impl AssignableFragmentSpread<'_> { /// 1. Validate that the assignable fragment does not have @skip/@defer, and /// is not within an inline fragment with directives, and is nested in a linked field /// 2. Mark the enclosing linked field as containing an assignable fragment spread. @@ -137,7 +138,7 @@ enum ValidGeneratedFlowType { Any, } -impl<'s> Transformer for AssignableFragmentSpread<'s> { +impl Transformer<'_> for AssignableFragmentSpread<'_> { const NAME: &'static str = "AssignableFragmentTransform"; const VISIT_ARGUMENTS: bool = false; const VISIT_DIRECTIVES: bool = false; @@ -208,8 +209,14 @@ impl<'s> Transformer for AssignableFragmentSpread<'s> { return Transformed::Keep; } + let dissallowed_directives = fragment_spread + .directives + .iter() + .filter(|directive| directive.name.item != *FRAGMENT_DANGEROUSLY_UNALIAS_DIRECTIVE_NAME) + .collect::>(); + // Assignable fragments cannot have directives, but we error only on the first one - if let Some(directive) = fragment_spread.directives.first() { + if let Some(directive) = dissallowed_directives.first() { self.errors.push(Diagnostic::error( ValidationMessage::AssignableFragmentSpreadNoOtherDirectives { disallowed_directive_name: directive.name.item.0, diff --git a/compiler/crates/relay-transforms/src/assignable_fragment_spread/transform_assignable_fragment_spreads_in_updatable_queries.rs b/compiler/crates/relay-transforms/src/assignable_fragment_spread/transform_assignable_fragment_spreads_in_updatable_queries.rs index 0c258d90f3339..a03f52ccd5648 100644 --- a/compiler/crates/relay-transforms/src/assignable_fragment_spread/transform_assignable_fragment_spreads_in_updatable_queries.rs +++ b/compiler/crates/relay-transforms/src/assignable_fragment_spread/transform_assignable_fragment_spreads_in_updatable_queries.rs @@ -11,7 +11,6 @@ use common::DirectiveName; use common::Location; use common::NamedItem; use common::WithLocation; -use graphql_ir::associated_data_impl; use graphql_ir::Directive; use graphql_ir::FragmentDefinition; use graphql_ir::FragmentSpread; @@ -20,6 +19,7 @@ use graphql_ir::Program; use graphql_ir::Selection; use graphql_ir::Transformed; use graphql_ir::Transformer; +use graphql_ir::associated_data_impl; use intern::string_key::Intern; use intern::string_key::StringKey; use lazy_static::lazy_static; @@ -71,7 +71,7 @@ struct AssignableFragmentSpreadForUpdatable<'s> { program: &'s Program, } -impl<'s> Transformer for AssignableFragmentSpreadForUpdatable<'s> { +impl Transformer<'_> for AssignableFragmentSpreadForUpdatable<'_> { const NAME: &'static str = "AssignableFragmentTransformForUpdatable"; const VISIT_ARGUMENTS: bool = false; const VISIT_DIRECTIVES: bool = false; diff --git a/compiler/crates/relay-transforms/src/assignable_fragment_spread/validate_assignable_directive.rs b/compiler/crates/relay-transforms/src/assignable_fragment_spread/validate_assignable_directive.rs index 5d99b1ae552b1..7327857b3de1e 100644 --- a/compiler/crates/relay-transforms/src/assignable_fragment_spread/validate_assignable_directive.rs +++ b/compiler/crates/relay-transforms/src/assignable_fragment_spread/validate_assignable_directive.rs @@ -17,8 +17,8 @@ use intern::string_key::Intern; use lazy_static::lazy_static; use schema::Schema; -use super::ValidationMessage; use super::ASSIGNABLE_DIRECTIVE; +use super::ValidationMessage; lazy_static! { static ref ALLOW_LISTED_DIRECTIVES: Vec = vec![ @@ -36,7 +36,7 @@ struct AssignableDirective<'a> { program: &'a Program, } -impl<'a> Validator for AssignableDirective<'a> { +impl Validator for AssignableDirective<'_> { const NAME: &'static str = "AssignableDirective"; const VALIDATE_ARGUMENTS: bool = false; const VALIDATE_DIRECTIVES: bool = false; diff --git a/compiler/crates/relay-transforms/src/assignable_fragment_spread/validate_updatable_directive.rs b/compiler/crates/relay-transforms/src/assignable_fragment_spread/validate_updatable_directive.rs index 1ac3160bb579f..dc3fda0470c0e 100644 --- a/compiler/crates/relay-transforms/src/assignable_fragment_spread/validate_updatable_directive.rs +++ b/compiler/crates/relay-transforms/src/assignable_fragment_spread/validate_updatable_directive.rs @@ -32,13 +32,15 @@ use intern::string_key::StringKey; use lazy_static::lazy_static; use schema::Schema; -use super::ValidationMessage; use super::ASSIGNABLE_DIRECTIVE; use super::UPDATABLE_DIRECTIVE; +use super::ValidationMessage; +use crate::fragment_alias_directive::FRAGMENT_DANGEROUSLY_UNALIAS_DIRECTIVE_NAME; lazy_static! { static ref ALLOW_LISTED_DIRECTIVES: Vec = vec![ *UPDATABLE_DIRECTIVE, + *FRAGMENT_DANGEROUSLY_UNALIAS_DIRECTIVE_NAME, // TODO have a global list of directives...? DirectiveName("fb_owner".intern()), ]; @@ -266,7 +268,7 @@ impl<'a> UpdatableDirective<'a> { } } -impl<'a> Validator for UpdatableDirective<'a> { +impl Validator for UpdatableDirective<'_> { const NAME: &'static str = "UpdatableDirective"; const VALIDATE_ARGUMENTS: bool = false; const VALIDATE_DIRECTIVES: bool = true; diff --git a/compiler/crates/relay-transforms/src/assignable_fragment_spread/validate_updatable_fragment_spread.rs b/compiler/crates/relay-transforms/src/assignable_fragment_spread/validate_updatable_fragment_spread.rs index fc9c1372068f8..ab9ff73cbea57 100644 --- a/compiler/crates/relay-transforms/src/assignable_fragment_spread/validate_updatable_fragment_spread.rs +++ b/compiler/crates/relay-transforms/src/assignable_fragment_spread/validate_updatable_fragment_spread.rs @@ -19,9 +19,10 @@ use schema::Schema; use schema::Type; use schema::TypeReference; -use super::ensure_discriminated_union_is_created; use super::ValidationMessage; +use super::ensure_discriminated_union_is_created; use crate::UPDATABLE_DIRECTIVE; +use crate::fragment_alias_directive::FRAGMENT_DANGEROUSLY_UNALIAS_DIRECTIVE_NAME; pub fn validate_updatable_fragment_spread(program: &Program) -> DiagnosticsResult<()> { UpdatableFragmentSpread { @@ -56,7 +57,7 @@ struct UpdatableFragmentSpread<'a> { path: Vec, } -impl<'a> UpdatableFragmentSpread<'a> { +impl UpdatableFragmentSpread<'_> { /// Validate many conditions for spreads of updatable fragments: /// * the fragment spread contains no directives /// * there is no @if or @skip between the linked field and the fragment spread @@ -88,10 +89,15 @@ impl<'a> UpdatableFragmentSpread<'a> { ) -> DiagnosticsResult<()> { let mut errors = vec![]; - if !fragment_spread.directives.is_empty() { + let invalid_directive = fragment_spread + .directives + .iter() + .find(|directive| directive.name.item != *FRAGMENT_DANGEROUSLY_UNALIAS_DIRECTIVE_NAME); + + if let Some(directive) = invalid_directive { errors.push(Diagnostic::error( ValidationMessage::UpdatableFragmentSpreadNoDirectives, - fragment_spread.fragment.location, + directive.location, )); } @@ -108,7 +114,7 @@ impl<'a> UpdatableFragmentSpread<'a> { } encountered_inline_fragment = true; } - PathItem::LinkedField(ref mut linked_field_path_item) => { + PathItem::LinkedField(linked_field_path_item) => { encountered_linked_field = true; if !encountered_inline_fragment { @@ -183,7 +189,7 @@ impl<'a> UpdatableFragmentSpread<'a> { } } -impl<'a> Validator for UpdatableFragmentSpread<'a> { +impl Validator for UpdatableFragmentSpread<'_> { const NAME: &'static str = "UpdatableFragmentSpread"; const VALIDATE_ARGUMENTS: bool = false; const VALIDATE_DIRECTIVES: bool = false; diff --git a/compiler/crates/relay-transforms/src/catch_directive.rs b/compiler/crates/relay-transforms/src/catch_directive.rs index d188f938317a5..7f70149e3f0c1 100644 --- a/compiler/crates/relay-transforms/src/catch_directive.rs +++ b/compiler/crates/relay-transforms/src/catch_directive.rs @@ -13,7 +13,6 @@ use common::Diagnostic; use common::DiagnosticsResult; use common::DirectiveName; use common::NamedItem; -use graphql_ir::associated_data_impl; use graphql_ir::Directive; use graphql_ir::FragmentDefinition; use graphql_ir::InlineFragment; @@ -24,6 +23,7 @@ use graphql_ir::ScalarField; use graphql_ir::Selection; use graphql_ir::Transformed; use graphql_ir::Transformer; +use graphql_ir::associated_data_impl; use intern::intern; use lazy_static::lazy_static; mod catchable_node; @@ -31,10 +31,10 @@ mod validation_message; use self::catchable_node::CatchMetadata; use self::catchable_node::CatchableNode; -use crate::catch_directive::validation_message::ValidationMessage; -use crate::catch_directive::validation_message::ValidationMessageWithData; use crate::FragmentAliasMetadata; use crate::REQUIRED_DIRECTIVE_NAME; +use crate::catch_directive::validation_message::ValidationMessage; +use crate::catch_directive::validation_message::ValidationMessageWithData; lazy_static! { pub static ref CATCH_DIRECTIVE_NAME: DirectiveName = DirectiveName(intern!("catch")); @@ -137,7 +137,7 @@ impl<'program> CatchDirective<'program> { } } -impl<'s> Transformer for CatchDirective<'s> { +impl Transformer<'_> for CatchDirective<'_> { const NAME: &'static str = "CatchDirectiveTransform"; const VISIT_ARGUMENTS: bool = false; const VISIT_DIRECTIVES: bool = false; diff --git a/compiler/crates/relay-transforms/src/client_edges.rs b/compiler/crates/relay-transforms/src/client_edges.rs index 384f62ada4d92..17cc29fb53757 100644 --- a/compiler/crates/relay-transforms/src/client_edges.rs +++ b/compiler/crates/relay-transforms/src/client_edges.rs @@ -16,10 +16,8 @@ use common::NamedItem; use common::ObjectName; use common::WithLocation; use docblock_shared::HAS_OUTPUT_TYPE_ARGUMENT_NAME; -use docblock_shared::LIVE_ARGUMENT_NAME; use docblock_shared::RELAY_RESOLVER_DIRECTIVE_NAME; use docblock_shared::RELAY_RESOLVER_MODEL_INSTANCE_FIELD; -use graphql_ir::associated_data_impl; use graphql_ir::Argument; use graphql_ir::ConstantValue; use graphql_ir::Directive; @@ -37,27 +35,32 @@ use graphql_ir::Selection; use graphql_ir::Transformed; use graphql_ir::Transformer; use graphql_ir::Value; +use graphql_ir::associated_data_impl; use graphql_syntax::OperationKind; +use intern::Lookup; use intern::string_key::Intern; use intern::string_key::StringKey; use intern::string_key::StringKeyMap; -use intern::Lookup; use lazy_static::lazy_static; use relay_config::ProjectConfig; use relay_schema::definitions::ResolverType; use schema::DirectiveValue; +use schema::FieldID; use schema::ObjectID; use schema::Schema; use schema::Type; use super::ValidationMessageWithData; -use crate::refetchable_fragment::RefetchableFragment; -use crate::refetchable_fragment::REFETCHABLE_NAME; -use crate::relay_resolvers::get_bool_argument_is_true; -use crate::RequiredMetadataDirective; -use crate::ValidationMessage; use crate::CHILDREN_CAN_BUBBLE_METADATA_KEY; use crate::REQUIRED_DIRECTIVE_NAME; +use crate::RequiredMetadataDirective; +use crate::ValidationMessage; +use crate::match_::MATCH_CONSTANTS; +use crate::refetchable_fragment::REFETCHABLE_NAME; +use crate::refetchable_fragment::RefetchableFragment; +use crate::relay_resolvers::ResolverInfo; +use crate::relay_resolvers::get_bool_argument_is_true; +use crate::relay_resolvers::get_resolver_info; lazy_static! { // This gets attached to the generated query @@ -90,8 +93,9 @@ associated_data_impl!(ClientEdgeMetadataDirective); #[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct ClientEdgeModelResolver { + pub model_field_id: FieldID, pub type_name: WithLocation, - pub is_live: bool, + pub resolver_info: ResolverInfo, } /// Metadata directive attached to generated queries @@ -103,7 +107,7 @@ associated_data_impl!(ClientEdgeGeneratedQueryMetadataDirective); pub struct ClientEdgeMetadata<'a> { /// The field which defines the graph relationship (currently always a Resolver) - pub backing_field: Selection, + pub backing_field: &'a Selection, /// Models the client edge field and its selections pub linked_field: &'a LinkedField, /// Additional metadata about the client edge @@ -132,14 +136,10 @@ impl<'a> ClientEdgeMetadata<'a> { fragment.selections.len() == 2, "Expected Client Edge inline fragment to have exactly two selections. This is a bug in the Relay compiler." ); - let mut backing_field = fragment - .selections.first() - .expect("Client Edge inline fragments have exactly two selections").clone(); - let backing_field_directives = backing_field.directives().iter().filter(|directive| - directive.name.item != RequiredMetadataDirective::directive_name() - ).cloned().collect(); - backing_field.set_directives(backing_field_directives); + let backing_field = fragment + .selections.first() + .expect("Client Edge inline fragments have exactly two selections"); let linked_field = match fragment.selections.get(1) { Some(Selection::LinkedField(linked_field)) => linked_field, @@ -317,6 +317,7 @@ impl<'program, 'pc> ClientEdgesTransform<'program, 'pc> { *REQUIRED_DIRECTIVE_NAME, *CHILDREN_CAN_BUBBLE_METADATA_KEY, RequiredMetadataDirective::directive_name(), + MATCH_CONSTANTS.match_directive_name, ]; let other_directives = directives @@ -468,17 +469,19 @@ impl<'program, 'pc> ClientEdgesTransform<'program, 'pc> { .schema .named_field(model, *RELAY_RESOLVER_MODEL_INSTANCE_FIELD)?; let model_field = self.program.schema.field(model_field_id); - let resolver_directive = model_field.directives.named(*RELAY_RESOLVER_DIRECTIVE_NAME); - let is_live = resolver_directive.map_or(false, |resolver_directive| { - resolver_directive - .arguments - .iter() - .any(|arg| arg.name.0 == LIVE_ARGUMENT_NAME.0) - }); - Some(ClientEdgeModelResolver { - type_name: object.name, - is_live, - }) + get_resolver_info(&self.program.schema, model_field, object.name.location) + .and_then(|resolver_info_result| match resolver_info_result { + Ok(resolver_info) => Some(resolver_info), + Err(diagnstics) => { + self.errors.extend(diagnstics); + None + } + }) + .map(|resolver_info| ClientEdgeModelResolver { + model_field_id, + type_name: object.name, + resolver_info, + }) } fn get_edge_to_server_object_metadata_directive( @@ -488,6 +491,12 @@ impl<'program, 'pc> ClientEdgesTransform<'program, 'pc> { waterfall_directive: Option<&Directive>, selections: Vec, ) -> ClientEdgeMetadataDirective { + if field_type.type_.is_list() { + self.errors.push(Diagnostic::error( + ValidationMessage::ClientEdgeToServerObjectList, + field_type.name.location, + )); + } // Client Edges to server objects must be annotated with @waterfall if waterfall_directive.is_none() { self.errors.push(Diagnostic::error_with_data( @@ -606,7 +615,20 @@ fn create_inline_fragment_for_client_edge( } let transformed_field = Arc::new(LinkedField { + selections: selections.clone(), + ..field.clone() + }); + + let backing_field_directives = field + .directives() + .iter() + .filter(|directive| directive.name.item != RequiredMetadataDirective::directive_name()) + .cloned() + .collect(); + + let backing_field = Arc::new(LinkedField { selections, + directives: backing_field_directives, ..field.clone() }); @@ -615,14 +637,14 @@ fn create_inline_fragment_for_client_edge( directives: inline_fragment_directives, selections: vec![ // NOTE: This creates 2^H selecitons where H is the depth of nested client edges + Selection::LinkedField(Arc::clone(&backing_field)), Selection::LinkedField(Arc::clone(&transformed_field)), - Selection::LinkedField(transformed_field), ], spread_location: Location::generated(), } } -impl Transformer for ClientEdgesTransform<'_, '_> { +impl Transformer<'_> for ClientEdgesTransform<'_, '_> { const NAME: &'static str = "ClientEdgesTransform"; const VISIT_ARGUMENTS: bool = false; const VISIT_DIRECTIVES: bool = false; @@ -716,7 +738,7 @@ pub fn remove_client_edge_selections(program: &Program) -> DiagnosticsResult for ClientEdgesCleanupTransform { const NAME: &'static str = "ClientEdgesCleanupTransform"; const VISIT_ARGUMENTS: bool = false; const VISIT_DIRECTIVES: bool = false; @@ -727,7 +749,7 @@ impl Transformer for ClientEdgesCleanupTransform { let new_selection = metadata.backing_field; Transformed::Replace( - self.transform_selection(&new_selection) + self.transform_selection(new_selection) .unwrap_or_else(|| new_selection.clone()), ) } diff --git a/compiler/crates/relay-transforms/src/client_extensions.rs b/compiler/crates/relay-transforms/src/client_extensions.rs index 0f888de844799..7ea037532537d 100644 --- a/compiler/crates/relay-transforms/src/client_extensions.rs +++ b/compiler/crates/relay-transforms/src/client_extensions.rs @@ -9,7 +9,6 @@ use std::sync::Arc; use common::DirectiveName; use common::Location; -use common::PointerAddress; use common::WithLocation; use fnv::FnvHashMap; use graphql_ir::Directive; @@ -37,7 +36,7 @@ pub fn client_extensions(program: &Program) -> Program { .replace_or_else(|| program.clone()) } -type Seen = FnvHashMap>; +type Seen<'a> = FnvHashMap<&'a Selection, Transformed>; lazy_static! { pub static ref CLIENT_EXTENSION_DIRECTIVE_NAME: DirectiveName = @@ -46,7 +45,7 @@ lazy_static! { struct ClientExtensionsTransform<'program> { program: &'program Program, - seen: Seen, + seen: Seen<'program>, } impl<'program> ClientExtensionsTransform<'program> { @@ -60,7 +59,7 @@ impl<'program> ClientExtensionsTransform<'program> { fn transform_client_edge( &mut self, fragment: &InlineFragment, - metadata: ClientEdgeMetadata<'_>, + metadata: ClientEdgeMetadata<'program>, ) -> Transformed { // Backing field should always be a RelayResolver. We can't wrap a // resolver. If its a scalar (no fragment) we don't need to wrap @@ -74,7 +73,7 @@ impl<'program> ClientExtensionsTransform<'program> { // fields of the client edge get wrapped with a client extension inline // fragment or deleted. let backing_field = self - .transform_selection(&metadata.backing_field) + .transform_selection(metadata.backing_field) .unwrap_or_else(|| metadata.backing_field.clone()); let selections = self @@ -88,14 +87,14 @@ impl<'program> ClientExtensionsTransform<'program> { } } -impl Transformer for ClientExtensionsTransform<'_> { +impl<'a> Transformer<'a> for ClientExtensionsTransform<'a> { const NAME: &'static str = "ClientExtensionsTransform"; const VISIT_ARGUMENTS: bool = false; const VISIT_DIRECTIVES: bool = false; fn transform_selections( &mut self, - selections: &[Selection], + selections: &'a [Selection], ) -> TransformedValue> { if selections.is_empty() { return TransformedValue::Keep; @@ -152,19 +151,20 @@ impl Transformer for ClientExtensionsTransform<'_> { } } - fn transform_selection(&mut self, selection: &Selection) -> Transformed { - let key = PointerAddress::new(selection); - if let Some(prev) = self.seen.get(&key) { - prev.clone() - } else { - self.seen.insert(key, Transformed::Keep); - let result = self.default_transform_selection(selection); - self.seen.insert(key, result.clone()); - result + fn transform_selection(&mut self, selection: &'a Selection) -> Transformed { + if let Some(prev) = self.seen.get(selection) { + return prev.clone(); } + + let result = self.default_transform_selection(selection); + self.seen.insert(selection, result.clone()); + result } - fn transform_inline_fragment(&mut self, fragment: &InlineFragment) -> Transformed { + fn transform_inline_fragment( + &mut self, + fragment: &'a InlineFragment, + ) -> Transformed { // Client Edges are modeled in the IR as inline fragments. If we // traverse into those fragments, and pull its contents out into a // separate inline fragment (without this directive) we will have lost @@ -183,7 +183,7 @@ impl Transformer for ClientExtensionsTransform<'_> { self.default_transform_inline_fragment(fragment) } - fn transform_linked_field(&mut self, field: &LinkedField) -> Transformed { + fn transform_linked_field(&mut self, field: &'a LinkedField) -> Transformed { if self .program .schema diff --git a/compiler/crates/relay-transforms/src/client_extensions_abstract_types.rs b/compiler/crates/relay-transforms/src/client_extensions_abstract_types.rs index 754f832c6e99e..cd8c7f2d6ca1d 100644 --- a/compiler/crates/relay-transforms/src/client_extensions_abstract_types.rs +++ b/compiler/crates/relay-transforms/src/client_extensions_abstract_types.rs @@ -5,12 +5,11 @@ * LICENSE file in the root directory of this source tree. */ -use std::collections::hash_map::Entry; use std::collections::HashMap; +use std::collections::hash_map::Entry; use std::mem; use std::sync::Arc; -use graphql_ir::associated_data_impl; use graphql_ir::FragmentDefinition; use graphql_ir::FragmentDefinitionName; use graphql_ir::FragmentSpread; @@ -20,6 +19,7 @@ use graphql_ir::Program; use graphql_ir::Selection; use graphql_ir::Transformed; use graphql_ir::Transformer; +use graphql_ir::associated_data_impl; use intern::string_key::StringKey; use intern::string_key::StringKeySet; use schema::ObjectID; @@ -174,7 +174,7 @@ impl<'program> ClientExtensionsAbstactTypesTransform<'program> { } } -impl Transformer for ClientExtensionsAbstactTypesTransform<'_> { +impl Transformer<'_> for ClientExtensionsAbstactTypesTransform<'_> { const NAME: &'static str = "ClientExtensionsAbstactTypesTransform"; const VISIT_ARGUMENTS: bool = false; const VISIT_DIRECTIVES: bool = false; diff --git a/compiler/crates/relay-transforms/src/connections.rs b/compiler/crates/relay-transforms/src/connections.rs index 29c2eb212c0f9..d1200a521486c 100644 --- a/compiler/crates/relay-transforms/src/connections.rs +++ b/compiler/crates/relay-transforms/src/connections.rs @@ -8,6 +8,8 @@ mod connection_constants; mod connection_util; pub use connection_constants::ConnectionConstants; +pub use connection_util::ConnectionMetadata; +pub use connection_util::ConnectionMetadataDirective; pub use connection_util::assert_connection_selections; pub use connection_util::build_connection_metadata; pub use connection_util::build_edge_selections; @@ -15,6 +17,4 @@ pub use connection_util::build_page_info_selections; pub use connection_util::extract_connection_directive; pub use connection_util::extract_connection_metadata_from_directive; pub use connection_util::get_default_filters; -pub use connection_util::ConnectionMetadata; -pub use connection_util::ConnectionMetadataDirective; pub use relay_config::ConnectionInterface; diff --git a/compiler/crates/relay-transforms/src/connections/connection_util.rs b/compiler/crates/relay-transforms/src/connections/connection_util.rs index 054ad6ef3478f..5566d771f5fd4 100644 --- a/compiler/crates/relay-transforms/src/connections/connection_util.rs +++ b/compiler/crates/relay-transforms/src/connections/connection_util.rs @@ -8,12 +8,12 @@ use common::Location; use common::NamedItem; use common::WithLocation; -use graphql_ir::associated_data_impl; use graphql_ir::Directive; use graphql_ir::InlineFragment; use graphql_ir::LinkedField; use graphql_ir::ScalarField; use graphql_ir::Selection; +use graphql_ir::associated_data_impl; use intern::string_key::StringKey; use schema::SDLSchema; use schema::Schema; diff --git a/compiler/crates/relay-transforms/src/debug_transform.rs b/compiler/crates/relay-transforms/src/debug_transform.rs index 06f0e3d5714b3..65d275dca6b6c 100644 --- a/compiler/crates/relay-transforms/src/debug_transform.rs +++ b/compiler/crates/relay-transforms/src/debug_transform.rs @@ -15,9 +15,9 @@ use graphql_ir::OperationDefinition; use graphql_ir::Program; use graphql_ir::Transformed; use graphql_ir::Transformer; +use graphql_text_printer::PrinterOptions; use graphql_text_printer::print_fragment; use graphql_text_printer::print_operation; -use graphql_text_printer::PrinterOptions; use schema::SDLSchema; /// A transform for debugging purpose only. Insert it to `apply_transforms`, and @@ -40,7 +40,7 @@ const PRINTER_OPTIONS: PrinterOptions = PrinterOptions { debug_directive_data: false, }; -impl Transformer for DebugTransform { +impl Transformer<'_> for DebugTransform { const NAME: &'static str = "DebugTransform"; const VISIT_ARGUMENTS: bool = false; const VISIT_DIRECTIVES: bool = false; diff --git a/compiler/crates/relay-transforms/src/declarative_connection.rs b/compiler/crates/relay-transforms/src/declarative_connection.rs index 012e31ff8cc4e..bc8d5103c742b 100644 --- a/compiler/crates/relay-transforms/src/declarative_connection.rs +++ b/compiler/crates/relay-transforms/src/declarative_connection.rs @@ -24,17 +24,17 @@ use graphql_ir::Transformer; use intern::string_key::Intern; use intern::string_key::StringKey; use lazy_static::lazy_static; -use schema::suggestion_list::did_you_mean; -use schema::suggestion_list::GraphQLSuggestions; use schema::SDLSchema; use schema::Schema; use schema::Type; use schema::TypeWithFields; +use schema::suggestion_list::GraphQLSuggestions; +use schema::suggestion_list::did_you_mean; use thiserror::Error; use crate::connections::ConnectionInterface; -use crate::handle_fields::build_handle_field_directive; use crate::handle_fields::HandleFieldDirectiveValues; +use crate::handle_fields::build_handle_field_directive; pub fn transform_declarative_connection( program: &Program, @@ -102,7 +102,7 @@ impl<'a> DeclarativeConnectionMutationTransform<'a> { } } -impl Transformer for DeclarativeConnectionMutationTransform<'_> { +impl Transformer<'_> for DeclarativeConnectionMutationTransform<'_> { const NAME: &'static str = "DeclarativeConnectionMutationTransform"; const VISIT_ARGUMENTS: bool = false; const VISIT_DIRECTIVES: bool = false; @@ -294,7 +294,7 @@ impl Transformer for DeclarativeConnectionMutationTransform<'_> { let is_not_object_type = self .schema .get_type(edge_typename_value) - .map_or(true, |edge_type| !edge_type.is_object()); + .is_none_or(|edge_type| !edge_type.is_object()); if is_not_object_type { let suggestions = GraphQLSuggestions::new(self.schema); diff --git a/compiler/crates/relay-transforms/src/defer_stream.rs b/compiler/crates/relay-transforms/src/defer_stream.rs index 825c527b591cc..51bfafd69c23f 100644 --- a/compiler/crates/relay-transforms/src/defer_stream.rs +++ b/compiler/crates/relay-transforms/src/defer_stream.rs @@ -35,9 +35,9 @@ use graphql_ir::Selection; use graphql_ir::Transformed; use graphql_ir::Transformer; use graphql_ir::Value; +use intern::Lookup; use intern::string_key::Intern; use intern::string_key::StringKey; -use intern::Lookup; use relay_config::DeferStreamInterface; use schema::Schema; use thiserror::Error; @@ -275,7 +275,7 @@ impl DeferStreamTransform<'_> { } } -impl<'s> Transformer for DeferStreamTransform<'s> { +impl Transformer<'_> for DeferStreamTransform<'_> { const NAME: &'static str = "DeferStreamTransform"; const VISIT_ARGUMENTS: bool = false; const VISIT_DIRECTIVES: bool = false; diff --git a/compiler/crates/relay-transforms/src/errors.rs b/compiler/crates/relay-transforms/src/errors.rs index 759d73b972c31..86d604dd8ab8b 100644 --- a/compiler/crates/relay-transforms/src/errors.rs +++ b/compiler/crates/relay-transforms/src/errors.rs @@ -131,6 +131,12 @@ pub enum ValidationMessage { name: StringKey, type_name: ObjectName, }, + + #[error( + "Unexpected Relay Resolver returning plual edge to type defined on the server. Relay Resolvers do not curretly support returning plural edges to server types. As a work around, consider defining a plural edge to a client type which has a singular edge to the server type." + )] + ClientEdgeToServerObjectList, + #[error("Invalid directive combination. @alias may not be combined with other directives.")] FragmentAliasIncompatibleDirective, diff --git a/compiler/crates/relay-transforms/src/flatten.rs b/compiler/crates/relay-transforms/src/flatten.rs index aa1b897c8e6be..8002223512793 100644 --- a/compiler/crates/relay-transforms/src/flatten.rs +++ b/compiler/crates/relay-transforms/src/flatten.rs @@ -7,14 +7,12 @@ use std::sync::Arc; -use common::sync::*; use common::Diagnostic; use common::DiagnosticsResult; use common::NamedItem; use common::PointerAddress; +use common::sync::*; use fnv::FnvHashMap; -use graphql_ir::node_identifier::LocationAgnosticPartialEq; -use graphql_ir::node_identifier::NodeIdentifier; use graphql_ir::Condition; use graphql_ir::Directive; use graphql_ir::FragmentDefinition; @@ -25,19 +23,21 @@ use graphql_ir::OperationDefinition; use graphql_ir::Program; use graphql_ir::Selection; use graphql_ir::TransformedValue; +use graphql_ir::node_identifier::LocationAgnosticPartialEq; +use graphql_ir::node_identifier::NodeIdentifier; use parking_lot::Mutex; use parking_lot::RwLock; use schema::SDLSchema; use schema::Schema; use schema::Type; -use crate::handle_fields::HANDLER_ARG_NAME; -use crate::handle_fields::KEY_ARG_NAME; -use crate::util::is_relay_custom_inline_fragment_directive; -use crate::util::CustomMetadataDirectives; use crate::ModuleMetadata; use crate::RelayLocationAgnosticBehavior; use crate::ValidationMessage; +use crate::handle_fields::HANDLER_ARG_NAME; +use crate::handle_fields::KEY_ARG_NAME; +use crate::util::CustomMetadataDirectives; +use crate::util::is_relay_custom_inline_fragment_directive; type SeenLinkedFields = Arc>>>>; type SeenInlineFragments = diff --git a/compiler/crates/relay-transforms/src/fragment_alias_directive.rs b/compiler/crates/relay-transforms/src/fragment_alias_directive.rs index d22674a60c2ae..2e1a0ea9f0bfc 100644 --- a/compiler/crates/relay-transforms/src/fragment_alias_directive.rs +++ b/compiler/crates/relay-transforms/src/fragment_alias_directive.rs @@ -14,8 +14,6 @@ use common::DirectiveName; use common::FeatureFlag; use common::NamedItem; use common::WithLocation; -use graphql_ir::associated_data_impl; -use graphql_ir::transform_list; use graphql_ir::Condition; use graphql_ir::FragmentDefinition; use graphql_ir::FragmentSpread; @@ -27,16 +25,18 @@ use graphql_ir::Selection; use graphql_ir::Transformed; use graphql_ir::TransformedValue; use graphql_ir::Transformer; +use graphql_ir::associated_data_impl; +use graphql_ir::transform_list; use intern::string_key::Intern; use intern::string_key::StringKey; use lazy_static::lazy_static; use schema::Schema; use schema::Type; +use crate::MATCH_CONSTANTS; use crate::RelayDirective; use crate::ValidationMessage; use crate::ValidationMessageWithData; -use crate::MATCH_CONSTANTS; lazy_static! { pub static ref FRAGMENT_ALIAS_DIRECTIVE_NAME: DirectiveName = DirectiveName("alias".intern()); @@ -79,7 +79,7 @@ pub fn remove_aliased_inline_fragments(program: &Program) -> Program { struct AliasedInlineFragmentRemovalTransform {} -impl Transformer for AliasedInlineFragmentRemovalTransform { +impl Transformer<'_> for AliasedInlineFragmentRemovalTransform { const NAME: &'static str = "AliasedInlineFragmentRemovalTransform"; const VISIT_ARGUMENTS: bool = false; const VISIT_DIRECTIVES: bool = false; @@ -192,7 +192,7 @@ impl<'program> FragmentAliasTransform<'program> { .expect("I believe we have already validated that all fragments exist"); let fragment_is_plural = - RelayDirective::find(&fragment.directives).map_or(false, |directive| directive.plural); + RelayDirective::find(&fragment.directives).is_some_and(|directive| directive.plural); if fragment_is_plural && self.parent_type.expect("expect parent type").is_plural { // Plural fragments handle their own nullability when read. However, @@ -257,7 +257,7 @@ impl<'program> FragmentAliasTransform<'program> { } } -impl Transformer for FragmentAliasTransform<'_> { +impl Transformer<'_> for FragmentAliasTransform<'_> { const NAME: &'static str = "NamedFragmentSpreadsTransform"; const VISIT_ARGUMENTS: bool = false; const VISIT_DIRECTIVES: bool = false; @@ -414,7 +414,7 @@ impl Transformer for FragmentAliasTransform<'_> { match spread.alias() { Ok(Some(alias)) => { let fragment_is_plural = RelayDirective::find(&fragment.directives) - .map_or(false, |directive| directive.plural); + .is_some_and(|directive| directive.plural); let parent_type = self .parent_type diff --git a/compiler/crates/relay-transforms/src/generate_data_driven_dependency_metadata.rs b/compiler/crates/relay-transforms/src/generate_data_driven_dependency_metadata.rs index 8277252b7789f..1a870bb5408e7 100644 --- a/compiler/crates/relay-transforms/src/generate_data_driven_dependency_metadata.rs +++ b/compiler/crates/relay-transforms/src/generate_data_driven_dependency_metadata.rs @@ -5,7 +5,6 @@ * LICENSE file in the root directory of this source tree. */ -use graphql_ir::associated_data_impl; use graphql_ir::Directive; use graphql_ir::FragmentDefinition; use graphql_ir::OperationDefinition; @@ -13,6 +12,7 @@ use graphql_ir::Program; use graphql_ir::Selection; use graphql_ir::Transformed; use graphql_ir::Transformer; +use graphql_ir::associated_data_impl; use intern::string_key::StringKey; use intern::string_key::StringKeyMap; use itertools::Itertools; @@ -20,8 +20,8 @@ use schema::Schema; use schema::Type; use schema::TypeReference; -use crate::util::get_normalization_fragment_filename; use crate::ModuleMetadata; +use crate::util::get_normalization_fragment_filename; #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct RelayDataDrivenDependencyMetadata { @@ -149,11 +149,23 @@ impl<'s> GenerateDataDrivenDependencyMetadata<'s> { .program .schema .get_type_name(processing_item.parent_type.inner()); - if !module_metadata.read_time_resolvers { - module_entries - .entry(id) - .and_modify(|module_entry| { - module_entry.branches.insert( + module_entries + .entry(id) + .and_modify(|module_entry| { + module_entry.branches.insert( + type_name, + Branch { + component, + fragment: get_normalization_fragment_filename( + fragment_name, + ), + }, + ); + }) + .or_insert(ModuleEntry { + branches: { + let mut map = StringKeyMap::default(); + map.insert( type_name, Branch { component, @@ -162,24 +174,10 @@ impl<'s> GenerateDataDrivenDependencyMetadata<'s> { ), }, ); - }) - .or_insert(ModuleEntry { - branches: { - let mut map = StringKeyMap::default(); - map.insert( - type_name, - Branch { - component, - fragment: get_normalization_fragment_filename( - fragment_name, - ), - }, - ); - map - }, - plural: processing_item.plural, - }); - } + map + }, + plural: processing_item.plural, + }); } } Selection::Condition(condition) => { @@ -228,7 +226,7 @@ struct ProcessingItem<'a> { selections: &'a [Selection], } -impl<'s> Transformer for GenerateDataDrivenDependencyMetadata<'s> { +impl Transformer<'_> for GenerateDataDrivenDependencyMetadata<'_> { const NAME: &'static str = "GenerateDataDrivenDependencyMetadata"; const VISIT_ARGUMENTS: bool = false; const VISIT_DIRECTIVES: bool = false; diff --git a/compiler/crates/relay-transforms/src/generate_id_field.rs b/compiler/crates/relay-transforms/src/generate_id_field.rs index 5d54604348ff7..34bb1b49501e5 100644 --- a/compiler/crates/relay-transforms/src/generate_id_field.rs +++ b/compiler/crates/relay-transforms/src/generate_id_field.rs @@ -5,8 +5,8 @@ * LICENSE file in the root directory of this source tree. */ -use std::collections::hash_map::Entry; use std::collections::HashMap; +use std::collections::hash_map::Entry; use std::sync::Arc; use common::Location; @@ -52,7 +52,7 @@ struct NodeInterface { id_field: FieldID, } -impl<'s> Transformer for GenerateIDFieldTransform<'s> { +impl Transformer<'_> for GenerateIDFieldTransform<'_> { const NAME: &'static str = "GenerateIDFieldTransform"; const VISIT_ARGUMENTS: bool = false; const VISIT_DIRECTIVES: bool = false; diff --git a/compiler/crates/relay-transforms/src/generate_live_query_metadata.rs b/compiler/crates/relay-transforms/src/generate_live_query_metadata.rs index 608eccec06f39..9a53be84f83d4 100644 --- a/compiler/crates/relay-transforms/src/generate_live_query_metadata.rs +++ b/compiler/crates/relay-transforms/src/generate_live_query_metadata.rs @@ -25,7 +25,6 @@ use graphql_ir::Transformer; use graphql_ir::Value; use graphql_syntax::OperationKind; use intern::string_key::Intern; -use itertools::Itertools; use lazy_static::lazy_static; use thiserror::Error; @@ -57,7 +56,7 @@ struct GenerateLiveQueryMetadata { errors: Vec, } -impl Transformer for GenerateLiveQueryMetadata { +impl Transformer<'_> for GenerateLiveQueryMetadata { const NAME: &'static str = "GenerateLiveQueryMetadata"; const VISIT_ARGUMENTS: bool = false; const VISIT_DIRECTIVES: bool = false; @@ -152,22 +151,13 @@ impl Transformer for GenerateLiveQueryMetadata { if live_directives.is_empty() { return Transformed::Keep; } - // We allow multiple `@live` selections in a query but don't allow multiple `config_id`s. Because - // it confuses `method` header formation. `config_id` on `@live` is only used for `@live_query` - // use case migration, which translates into exactly 1 `@live` selection. - let config_id = live_directives - .iter() - .filter_map(|dir| dir.arguments.named(*CONFIG_ID_ARG)) - .at_most_one() - .unwrap() - .and_then(|arg| arg.value.item.get_string_literal()) - .unwrap_or_else(|| StringKey::from_str("").unwrap()); - next_directives.push(create_metadata_directive( *LIVE_METADATA_KEY, ConstantValue::Object(vec![ConstantArgument { name: WithLocation::generated(*CONFIG_ID_ARG), - value: WithLocation::generated(ConstantValue::String(config_id)), + value: WithLocation::generated(ConstantValue::String( + StringKey::from_str("").unwrap(), + )), }]), )); } diff --git a/compiler/crates/relay-transforms/src/generate_relay_resolvers_model_fragments.rs b/compiler/crates/relay-transforms/src/generate_relay_resolvers_model_fragments.rs index 0790fc3c63b88..1bba621617a92 100644 --- a/compiler/crates/relay-transforms/src/generate_relay_resolvers_model_fragments.rs +++ b/compiler/crates/relay-transforms/src/generate_relay_resolvers_model_fragments.rs @@ -9,18 +9,18 @@ use std::sync::Arc; use common::NamedItem; use common::WithLocation; -use docblock_shared::ResolverSourceHash; use docblock_shared::RELAY_RESOLVER_MODEL_DIRECTIVE_NAME; use docblock_shared::RELAY_RESOLVER_MODEL_INSTANCE_FIELD; use docblock_shared::RELAY_RESOLVER_SOURCE_HASH; use docblock_shared::RELAY_RESOLVER_SOURCE_HASH_VALUE; -use graphql_ir::associated_data_impl; +use docblock_shared::ResolverSourceHash; use graphql_ir::Directive; use graphql_ir::FragmentDefinition; use graphql_ir::FragmentDefinitionName; use graphql_ir::Program; use graphql_ir::ScalarField; use graphql_ir::Selection; +use graphql_ir::associated_data_impl; use intern::string_key::Intern; use relay_config::ProjectName; use relay_config::SchemaConfig; diff --git a/compiler/crates/relay-transforms/src/generate_relay_resolvers_operations_for_nested_objects.rs b/compiler/crates/relay-transforms/src/generate_relay_resolvers_operations_for_nested_objects.rs index 941b086a73655..d63cf7eca66e2 100644 --- a/compiler/crates/relay-transforms/src/generate_relay_resolvers_operations_for_nested_objects.rs +++ b/compiler/crates/relay-transforms/src/generate_relay_resolvers_operations_for_nested_objects.rs @@ -36,13 +36,13 @@ use schema::SDLSchema; use schema::Schema; use schema::Type; +use crate::RESOLVER_BELONGS_TO_BASE_SCHEMA_DIRECTIVE; +use crate::SplitOperationMetadata; +use crate::ValidationMessage; use crate::generate_relay_resolvers_model_fragments::directives_with_artifact_source; use crate::get_normalization_operation_name; use crate::match_::RawResponseGenerationMode; use crate::relay_resolvers::get_bool_argument_is_true; -use crate::SplitOperationMetadata; -use crate::ValidationMessage; -use crate::RESOLVER_BELONGS_TO_BASE_SCHEMA_DIRECTIVE; fn generate_fat_selections_from_type( schema: &SDLSchema, diff --git a/compiler/crates/relay-transforms/src/generate_relay_resolvers_root_fragment_split_operation.rs b/compiler/crates/relay-transforms/src/generate_relay_resolvers_root_fragment_split_operation.rs index b9e1941c0f547..b611fb9fa90c7 100644 --- a/compiler/crates/relay-transforms/src/generate_relay_resolvers_root_fragment_split_operation.rs +++ b/compiler/crates/relay-transforms/src/generate_relay_resolvers_root_fragment_split_operation.rs @@ -7,25 +7,29 @@ use std::sync::Arc; +use ::intern::intern; +use ::intern::string_key::Intern; use common::DiagnosticsResult; +use common::DirectiveName; use common::NamedItem; +use common::WithLocation; use docblock_shared::RELAY_RESOLVER_DIRECTIVE_NAME; -use graphql_ir::associated_data_impl; +use graphql_ir::Directive; use graphql_ir::ExecutableDefinition; use graphql_ir::FragmentDefinition; use graphql_ir::FragmentDefinitionName; use graphql_ir::OperationDefinition; use graphql_ir::OperationDefinitionName; use graphql_ir::Program; +use graphql_ir::associated_data_impl; use graphql_syntax::OperationKind; -use intern::string_key::Intern; use rustc_hash::FxHashSet; use schema::SDLSchema; +use crate::RESOLVER_BELONGS_TO_BASE_SCHEMA_DIRECTIVE; +use crate::SplitOperationMetadata; use crate::get_normalization_operation_name; use crate::get_resolver_fragment_dependency_name; -use crate::SplitOperationMetadata; -use crate::RESOLVER_BELONGS_TO_BASE_SCHEMA_DIRECTIVE; #[derive(Clone, Debug, PartialEq, Eq, Hash)] struct IsResolverRootFragment(); @@ -52,6 +56,15 @@ pub fn generate_relay_resolvers_root_fragment_split_operation( raw_response_type_generation_mode: None, } .into(), + Directive { + name: WithLocation::new( + fragment.name.location, + DirectiveName(intern!("exec_time_resolvers")), + ), + arguments: vec![], + data: None, + location: fragment.name.location, + }, ], selections: fragment.selections.clone(), kind: OperationKind::Query, diff --git a/compiler/crates/relay-transforms/src/generate_typename.rs b/compiler/crates/relay-transforms/src/generate_typename.rs index b78e3241a2f25..f3b88b9a7a3f7 100644 --- a/compiler/crates/relay-transforms/src/generate_typename.rs +++ b/compiler/crates/relay-transforms/src/generate_typename.rs @@ -9,7 +9,6 @@ use std::sync::Arc; use common::DirectiveName; use common::Location; -use common::PointerAddress; use common::WithLocation; use fnv::FnvHashMap; use graphql_ir::Directive; @@ -47,14 +46,11 @@ pub fn generate_typename(program: &Program, is_for_codegen: bool) -> Program { .replace_or_else(|| program.clone()) } -// Note on correctness: the PointerAddress here is calculated from addresses of the input -// context. Because those value are still referenced, that memory cannot be freed/ -// reused for the lifetime of the transform. -type Seen = FnvHashMap>; +type Seen<'a> = FnvHashMap<&'a InlineFragment, Transformed>; struct GenerateTypenameTransform<'s> { program: &'s Program, - seen: Seen, + seen: Seen<'s>, is_for_codegen: bool, parent_type: Option, } @@ -70,14 +66,14 @@ impl<'s> GenerateTypenameTransform<'s> { } } -impl<'s> Transformer for GenerateTypenameTransform<'s> { +impl<'s> Transformer<'s> for GenerateTypenameTransform<'s> { const NAME: &'static str = "GenerateTypenameTransform"; const VISIT_ARGUMENTS: bool = false; const VISIT_DIRECTIVES: bool = false; fn transform_operation( &mut self, - operation: &OperationDefinition, + operation: &'s OperationDefinition, ) -> Transformed { self.parent_type = Some(operation.type_); self.default_transform_operation(operation) @@ -85,7 +81,7 @@ impl<'s> Transformer for GenerateTypenameTransform<'s> { fn transform_fragment( &mut self, - fragment: &FragmentDefinition, + fragment: &'s FragmentDefinition, ) -> Transformed { self.parent_type = Some(fragment.type_condition); let schema = &self.program.schema; @@ -115,7 +111,7 @@ impl<'s> Transformer for GenerateTypenameTransform<'s> { } } - fn transform_linked_field(&mut self, field: &LinkedField) -> Transformed { + fn transform_linked_field(&mut self, field: &'s LinkedField) -> Transformed { let schema = &self.program.schema; let field_definition = schema.field(field.definition.item); let parent_type = self.parent_type; @@ -154,12 +150,14 @@ impl<'s> Transformer for GenerateTypenameTransform<'s> { } } - fn transform_inline_fragment(&mut self, fragment: &InlineFragment) -> Transformed { - let key = PointerAddress::new(fragment); - if let Some(prev) = self.seen.get(&key) { + fn transform_inline_fragment( + &mut self, + fragment: &'s InlineFragment, + ) -> Transformed { + if let Some(prev) = self.seen.get(fragment) { return prev.clone(); } - self.seen.insert(key, Transformed::Delete); + self.seen.insert(fragment, Transformed::Delete); let parent_type = self.parent_type; if fragment.type_condition.is_some() { self.parent_type = fragment.type_condition; @@ -204,7 +202,7 @@ impl<'s> Transformer for GenerateTypenameTransform<'s> { }))) } }; - self.seen.insert(key, result.clone()); + self.seen.insert(fragment, result.clone()); result } diff --git a/compiler/crates/relay-transforms/src/handle_fields.rs b/compiler/crates/relay-transforms/src/handle_fields.rs index 8dcb55d469472..3d22775bec334 100644 --- a/compiler/crates/relay-transforms/src/handle_fields.rs +++ b/compiler/crates/relay-transforms/src/handle_fields.rs @@ -9,15 +9,15 @@ mod handle_field_transform; mod handle_field_util; pub use handle_field_transform::handle_field_transform; -pub use handle_field_util::build_handle_field_directive; -pub use handle_field_util::build_handle_field_directive_from_connection_directive; -pub use handle_field_util::extract_handle_field_directive_args_for_connection; -pub use handle_field_util::extract_handle_field_directives; -pub use handle_field_util::extract_values_from_handle_field_directive; -pub use handle_field_util::HandleFieldDirectiveValues; pub use handle_field_util::CONNECTION_HANDLER_ARG_NAME; pub use handle_field_util::DYNAMIC_KEY_ARG_NAME; pub use handle_field_util::FILTERS_ARG_NAME; -pub use handle_field_util::HANDLER_ARG_NAME; pub use handle_field_util::HANDLE_FIELD_DIRECTIVE_NAME; +pub use handle_field_util::HANDLER_ARG_NAME; +pub use handle_field_util::HandleFieldDirectiveValues; pub use handle_field_util::KEY_ARG_NAME; +pub use handle_field_util::build_handle_field_directive; +pub use handle_field_util::build_handle_field_directive_from_connection_directive; +pub use handle_field_util::extract_handle_field_directive_args_for_connection; +pub use handle_field_util::extract_handle_field_directives; +pub use handle_field_util::extract_values_from_handle_field_directive; diff --git a/compiler/crates/relay-transforms/src/handle_fields/handle_field_transform.rs b/compiler/crates/relay-transforms/src/handle_fields/handle_field_transform.rs index 27c997c5950a6..88312f059f337 100644 --- a/compiler/crates/relay-transforms/src/handle_fields/handle_field_transform.rs +++ b/compiler/crates/relay-transforms/src/handle_fields/handle_field_transform.rs @@ -58,7 +58,7 @@ impl HandleFieldTransform { } } -impl Transformer for HandleFieldTransform { +impl Transformer<'_> for HandleFieldTransform { const NAME: &'static str = "HandleFieldTransform"; const VISIT_ARGUMENTS: bool = false; const VISIT_DIRECTIVES: bool = false; diff --git a/compiler/crates/relay-transforms/src/handle_fields/handle_field_util.rs b/compiler/crates/relay-transforms/src/handle_fields/handle_field_util.rs index b839ecc2892fe..3b1d642f4a12c 100644 --- a/compiler/crates/relay-transforms/src/handle_fields/handle_field_util.rs +++ b/compiler/crates/relay-transforms/src/handle_fields/handle_field_util.rs @@ -46,8 +46,8 @@ pub struct HandleFieldDirectiveValues { } /// We have two handler keys, "handler" in connection, and "handle" in everywhere else -/// Speicific helpers are created separately for connection - +/// Specific helpers are created separately for connection +/// /// Helper to extract handle field arguments that are present /// on the directive, without any validation or assumption of /// correctness of values. diff --git a/compiler/crates/relay-transforms/src/inline_data_fragment.rs b/compiler/crates/relay-transforms/src/inline_data_fragment.rs index 63dae3c9929a4..498f63ddd59ee 100644 --- a/compiler/crates/relay-transforms/src/inline_data_fragment.rs +++ b/compiler/crates/relay-transforms/src/inline_data_fragment.rs @@ -13,7 +13,6 @@ use common::DirectiveName; use common::Location; use common::NamedItem; use common::WithLocation; -use graphql_ir::associated_data_impl; use graphql_ir::Argument; use graphql_ir::Directive; use graphql_ir::FragmentDefinitionName; @@ -24,6 +23,7 @@ use graphql_ir::Selection; use graphql_ir::Transformed; use graphql_ir::Transformer; use graphql_ir::VariableDefinition; +use graphql_ir::associated_data_impl; use intern::string_key::Intern; use lazy_static::lazy_static; use thiserror::Error; @@ -97,7 +97,7 @@ pub struct InlineDirectiveMetadata { } associated_data_impl!(InlineDirectiveMetadata); -impl<'s> Transformer for InlineDataFragmentsTransform<'s> { +impl Transformer<'_> for InlineDataFragmentsTransform<'_> { const NAME: &'static str = "InlineDataFragmentsTransform"; const VISIT_ARGUMENTS: bool = false; const VISIT_DIRECTIVES: bool = false; diff --git a/compiler/crates/relay-transforms/src/inline_fragments.rs b/compiler/crates/relay-transforms/src/inline_fragments.rs index 9a9382dac3094..886a8bb7c0191 100644 --- a/compiler/crates/relay-transforms/src/inline_fragments.rs +++ b/compiler/crates/relay-transforms/src/inline_fragments.rs @@ -10,8 +10,6 @@ use std::sync::Arc; use common::Location; use fnv::FnvHashMap; -use graphql_ir::node_identifier::LocationAgnosticHash; -use graphql_ir::node_identifier::LocationAgnosticPartialEq; use graphql_ir::FragmentDefinition; use graphql_ir::FragmentSpread; use graphql_ir::InlineFragment; @@ -20,6 +18,8 @@ use graphql_ir::ScalarField; use graphql_ir::Selection; use graphql_ir::Transformed; use graphql_ir::Transformer; +use graphql_ir::node_identifier::LocationAgnosticHash; +use graphql_ir::node_identifier::LocationAgnosticPartialEq; use crate::NoInlineFragmentSpreadMetadata; use crate::RelayLocationAgnosticBehavior; @@ -119,7 +119,7 @@ impl<'s> InlineFragmentsTransform<'s> { } } -impl<'s> Transformer for InlineFragmentsTransform<'s> { +impl Transformer<'_> for InlineFragmentsTransform<'_> { const NAME: &'static str = "InlineFragmentsTransform"; const VISIT_ARGUMENTS: bool = false; const VISIT_DIRECTIVES: bool = false; diff --git a/compiler/crates/relay-transforms/src/lib.rs b/compiler/crates/relay-transforms/src/lib.rs index 580be9e785a5f..597b456fb11b5 100644 --- a/compiler/crates/relay-transforms/src/lib.rs +++ b/compiler/crates/relay-transforms/src/lib.rs @@ -46,6 +46,7 @@ mod murmurhash; mod no_inline; mod preloadable_directive; mod provided_variable_fragment_transform; +pub mod raw_text; mod refetchable_fragment; mod relay_actor_change; mod relay_directive; @@ -76,10 +77,10 @@ mod validations; use std::collections::HashMap; use std::collections::HashSet; -use intern::string_key::StringKey; use intern::BuildIdHasher; -pub use metadata_directive::create_metadata_directive; +use intern::string_key::StringKey; pub use metadata_directive::INTERNAL_METADATA_DIRECTIVE; +pub use metadata_directive::create_metadata_directive; /// Name of an executable operation type OperationName = StringKey; @@ -92,118 +93,119 @@ pub use apply_custom_transforms::BaseFragmentNames; pub use apply_custom_transforms::CustomTransform; pub use apply_custom_transforms::CustomTransforms; pub use apply_custom_transforms::CustomTransformsConfig; -pub use apply_fragment_arguments::apply_fragment_arguments; pub use apply_fragment_arguments::NoInlineFragmentSpreadMetadata; -pub use apply_transforms::apply_transforms; +pub use apply_fragment_arguments::apply_fragment_arguments; pub use apply_transforms::Programs; +pub use apply_transforms::apply_transforms; +pub use assignable_fragment_spread::ASSIGNABLE_DIRECTIVE; +pub use assignable_fragment_spread::ASSIGNABLE_DIRECTIVE_FOR_TYPEGEN; +pub use assignable_fragment_spread::TypeConditionInfo; +pub use assignable_fragment_spread::UPDATABLE_DIRECTIVE; +pub use assignable_fragment_spread::UPDATABLE_DIRECTIVE_FOR_TYPEGEN; pub use assignable_fragment_spread::transform_assignable_fragment_spreads_in_regular_queries; pub use assignable_fragment_spread::transform_assignable_fragment_spreads_in_updatable_queries; pub use assignable_fragment_spread::validate_assignable_directive; pub use assignable_fragment_spread::validate_updatable_directive; pub use assignable_fragment_spread::validate_updatable_fragment_spread; -pub use assignable_fragment_spread::TypeConditionInfo; -pub use assignable_fragment_spread::ASSIGNABLE_DIRECTIVE; -pub use assignable_fragment_spread::ASSIGNABLE_DIRECTIVE_FOR_TYPEGEN; -pub use assignable_fragment_spread::UPDATABLE_DIRECTIVE; -pub use assignable_fragment_spread::UPDATABLE_DIRECTIVE_FOR_TYPEGEN; -pub use catch_directive::catch_directive; +pub use catch_directive::CATCH_DIRECTIVE_NAME; pub use catch_directive::CatchMetadataDirective; pub use catch_directive::CatchTo; -pub use catch_directive::CATCH_DIRECTIVE_NAME; pub use catch_directive::NULL_TO; pub use catch_directive::RESULT_TO; pub use catch_directive::TO_ARGUMENT; -pub use client_edges::client_edges; -pub use client_edges::remove_client_edge_selections; +pub use catch_directive::catch_directive; +pub use client_edges::CLIENT_EDGE_SOURCE_NAME; +pub use client_edges::CLIENT_EDGE_WATERFALL_DIRECTIVE_NAME; pub use client_edges::ClientEdgeGeneratedQueryMetadataDirective; pub use client_edges::ClientEdgeMetadata; pub use client_edges::ClientEdgeMetadataDirective; pub use client_edges::ClientEdgeModelResolver; -pub use client_edges::CLIENT_EDGE_SOURCE_NAME; -pub use client_edges::CLIENT_EDGE_WATERFALL_DIRECTIVE_NAME; -pub use client_extensions::client_extensions; +pub use client_edges::client_edges; +pub use client_edges::remove_client_edge_selections; pub use client_extensions::CLIENT_EXTENSION_DIRECTIVE_NAME; -pub use client_extensions_abstract_types::client_extensions_abstract_types; +pub use client_extensions::client_extensions; pub use client_extensions_abstract_types::ClientExtensionAbstractTypeMetadataDirective; -pub use connections::extract_connection_metadata_from_directive; +pub use client_extensions_abstract_types::client_extensions_abstract_types; pub use connections::ConnectionConstants; pub use connections::ConnectionInterface; pub use connections::ConnectionMetadata; +pub use connections::extract_connection_metadata_from_directive; pub use debug_transform::debug_transform; pub use declarative_connection::transform_declarative_connection; -pub use defer_stream::transform_defer_stream; pub use defer_stream::DeferDirective; pub use defer_stream::StreamDirective; +pub use defer_stream::transform_defer_stream; pub use directive_finder::DirectiveFinder; pub use flatten::flatten; +pub use fragment_alias_directive::FragmentAliasMetadata; pub use fragment_alias_directive::fragment_alias_directive; pub use fragment_alias_directive::remove_aliased_inline_fragments; -pub use fragment_alias_directive::FragmentAliasMetadata; -pub use generate_data_driven_dependency_metadata::generate_data_driven_dependency_metadata; pub use generate_data_driven_dependency_metadata::RelayDataDrivenDependencyMetadata; +pub use generate_data_driven_dependency_metadata::generate_data_driven_dependency_metadata; pub use generate_id_field::generate_id_field; pub use generate_live_query_metadata::generate_live_query_metadata; pub use generate_relay_resolvers_model_fragments::ArtifactSourceKeyData; pub use generate_relay_resolvers_operations_for_nested_objects::generate_relay_resolvers_operations_for_nested_objects; pub use generate_relay_resolvers_root_fragment_split_operation::annotate_resolver_root_fragments; pub use generate_relay_resolvers_root_fragment_split_operation::generate_relay_resolvers_root_fragment_split_operation; -pub use generate_typename::generate_typename; pub use generate_typename::TYPE_DISCRIMINATOR_DIRECTIVE_NAME; +pub use generate_typename::generate_typename; pub use handle_fields::extract_handle_field_directives; pub use handle_fields::extract_values_from_handle_field_directive; pub use handle_fields::handle_field_transform; pub use hash_arguments::hash_arguments; -pub use inline_data_fragment::inline_data_fragment; -pub use inline_data_fragment::InlineDirectiveMetadata; pub use inline_data_fragment::INLINE_DIRECTIVE_NAME; +pub use inline_data_fragment::InlineDirectiveMetadata; +pub use inline_data_fragment::inline_data_fragment; pub use inline_fragments::inline_fragments; pub use inline_fragments::inline_fragments_keep_fragments; pub use mask::mask; -pub use match_::split_module_import; -pub use match_::transform_match; -pub use match_::transform_subscriptions; +pub use match_::DIRECTIVE_SPLIT_OPERATION; +pub use match_::MATCH_CONSTANTS; pub use match_::ModuleMetadata; pub use match_::RawResponseGenerationMode; pub use match_::SplitOperationMetadata; -pub use match_::DIRECTIVE_SPLIT_OPERATION; -pub use match_::MATCH_CONSTANTS; +pub use match_::split_module_import; +pub use match_::transform_match; +pub use match_::transform_subscriptions; pub use no_inline::NO_INLINE_DIRECTIVE_NAME; pub use preloadable_directive::is_operation_preloadable; pub use preloadable_directive::should_generate_hack_preloader; pub use provided_variable_fragment_transform::provided_variable_fragment_transform; -pub use refetchable_fragment::transform_refetchable_fragment; -pub use refetchable_fragment::RefetchableDerivedFromMetadata; -pub use refetchable_fragment::RefetchableMetadata; pub use refetchable_fragment::CONSTANTS as REFETCHABLE_CONSTANTS; pub use refetchable_fragment::REFETCHABLE_NAME; -pub use relay_actor_change::relay_actor_change_transform; +pub use refetchable_fragment::RefetchableDerivedFromMetadata; +pub use refetchable_fragment::RefetchableMetadata; +pub use refetchable_fragment::transform_refetchable_fragment; pub use relay_actor_change::RELAY_ACTOR_CHANGE_DIRECTIVE_FOR_CODEGEN; +pub use relay_actor_change::relay_actor_change_transform; pub use relay_directive::RelayDirective; pub use relay_node_identifier::RelayLocationAgnosticBehavior; -pub use relay_resolvers::get_resolver_fragment_dependency_name; -pub use relay_resolvers::relay_resolvers; -pub use relay_resolvers::resolver_type_import_alias; pub use relay_resolvers::FragmentDataInjectionMode; pub use relay_resolvers::RelayResolverMetadata; pub use relay_resolvers::ResolverOutputTypeInfo; +pub use relay_resolvers::get_resolver_fragment_dependency_name; +pub use relay_resolvers::relay_resolvers; +pub use relay_resolvers::resolver_type_import_alias; pub use relay_resolvers_abstract_types::relay_resolvers_abstract_types; -pub use remove_base_fragments::remove_base_fragments; pub use remove_base_fragments::RESOLVER_BELONGS_TO_BASE_SCHEMA_DIRECTIVE; -pub use required_directive::required_directive; -pub use required_directive::RequiredAction; -pub use required_directive::RequiredMetadataDirective; +pub use remove_base_fragments::remove_base_fragments; pub use required_directive::ACTION_ARGUMENT; pub use required_directive::CHILDREN_CAN_BUBBLE_METADATA_KEY; pub use required_directive::REQUIRED_DIRECTIVE_NAME; pub use rescript_relay_generate_typename::rescript_relay_generate_typename; pub use rescript_relay_inline_codesplit::rescript_relay_inline_codesplit; pub use rescript_relay_transform_codesplit::rescript_relay_transform_codesplit; +pub use required_directive::RequiredAction; +pub use required_directive::RequiredMetadataDirective; pub use required_directive::THROW_ACTION; +pub use required_directive::required_directive; +pub use root_variables::VariableMapEntry; pub use skip_client_directives::skip_client_directives; pub use skip_client_extensions::skip_client_extensions; pub use skip_null_arguments_transform::skip_null_arguments_transform; -pub use skip_redundant_nodes::skip_redundant_nodes; pub use skip_redundant_nodes::SkipRedundantNodesTransform; +pub use skip_redundant_nodes::skip_redundant_nodes; pub use skip_split_operation::skip_split_operation; pub use skip_unreachable_node::skip_unreachable_node_loose; pub use skip_unreachable_node::skip_unreachable_node_strict; @@ -216,8 +218,8 @@ pub use util::generate_abstract_type_refinement_key; pub use util::get_normalization_fragment_filename; pub use util::get_normalization_operation_name; pub use util::remove_directive; -pub use validate_operation_variables::validate_operation_variables; pub use validate_operation_variables::ValidateVariablesOptions; +pub use validate_operation_variables::validate_operation_variables; pub use validations::*; pub use crate::errors::ValidationMessage; diff --git a/compiler/crates/relay-transforms/src/mask.rs b/compiler/crates/relay-transforms/src/mask.rs index 1739c730b8a5b..f26b24da9f5ec 100644 --- a/compiler/crates/relay-transforms/src/mask.rs +++ b/compiler/crates/relay-transforms/src/mask.rs @@ -20,8 +20,8 @@ use graphql_ir::Transformed; use graphql_ir::Transformer; use graphql_ir::VariableDefinition; use graphql_ir::VariableName; -use indexmap::map::Entry; use indexmap::IndexMap; +use indexmap::map::Entry; use schema::Schema; use crate::relay_directive::RelayDirective; @@ -80,7 +80,7 @@ impl<'s> Mask<'s> { } } -impl<'s> Transformer for Mask<'s> { +impl Transformer<'_> for Mask<'_> { const NAME: &'static str = "MaskTransform"; const VISIT_ARGUMENTS: bool = false; const VISIT_DIRECTIVES: bool = false; diff --git a/compiler/crates/relay-transforms/src/match_.rs b/compiler/crates/relay-transforms/src/match_.rs index 46eec27a757a3..5ab1bfc1452e9 100644 --- a/compiler/crates/relay-transforms/src/match_.rs +++ b/compiler/crates/relay-transforms/src/match_.rs @@ -15,10 +15,10 @@ mod validation_message; pub use constants::MATCH_CONSTANTS; pub use hash_supported_argument::hash_supported_argument; -pub use match_transform::transform_match; pub use match_transform::ModuleMetadata; +pub use match_transform::transform_match; pub use split_module_import::split_module_import; +pub use split_operation_metadata::DIRECTIVE_SPLIT_OPERATION; pub use split_operation_metadata::RawResponseGenerationMode; pub use split_operation_metadata::SplitOperationMetadata; -pub use split_operation_metadata::DIRECTIVE_SPLIT_OPERATION; pub use subscription_transform::transform_subscriptions; diff --git a/compiler/crates/relay-transforms/src/match_/hash_supported_argument.rs b/compiler/crates/relay-transforms/src/match_/hash_supported_argument.rs index 1c7ad5e55cab0..efabb6c5c1a66 100644 --- a/compiler/crates/relay-transforms/src/match_/hash_supported_argument.rs +++ b/compiler/crates/relay-transforms/src/match_/hash_supported_argument.rs @@ -17,8 +17,8 @@ use graphql_ir::Selection; use graphql_ir::Transformed; use graphql_ir::Transformer; use graphql_ir::Value; -use intern::string_key::Intern; use intern::Lookup; +use intern::string_key::Intern; use schema::SDLSchema; use schema::Schema; use schema::TypeReference; @@ -45,7 +45,7 @@ struct HashSupportedArgumentTransform<'a> { errors: Vec, } -impl<'a> Transformer for HashSupportedArgumentTransform<'a> { +impl Transformer<'_> for HashSupportedArgumentTransform<'_> { const NAME: &'static str = "HashSupportedArgumentTransform"; const VISIT_ARGUMENTS: bool = false; @@ -110,7 +110,7 @@ impl<'a> Transformer for HashSupportedArgumentTransform<'a> { } } -impl<'a> HashSupportedArgumentTransform<'a> { +impl HashSupportedArgumentTransform<'_> { /// Returns true iff the field is supplied with a `supported` arg and that /// arg has a type of `[string]` (potentially non-nullable somewhere). fn has_match_supported_arg(&self, field: &LinkedField) -> bool { diff --git a/compiler/crates/relay-transforms/src/match_/match_transform.rs b/compiler/crates/relay-transforms/src/match_/match_transform.rs index 6aad76daece78..1bce50f9f85b0 100644 --- a/compiler/crates/relay-transforms/src/match_/match_transform.rs +++ b/compiler/crates/relay-transforms/src/match_/match_transform.rs @@ -10,9 +10,9 @@ use std::hash::Hash; use std::hash::Hasher; use std::sync::Arc; +use ::intern::Lookup; use ::intern::string_key::Intern; use ::intern::string_key::StringKey; -use ::intern::Lookup; use common::ArgumentName; use common::Diagnostic; use common::DiagnosticsResult; @@ -24,7 +24,6 @@ use common::WithLocation; use docblock_shared::RELAY_RESOLVER_MODEL_DIRECTIVE_NAME; use fnv::FnvBuildHasher; use fnv::FnvHashMap; -use graphql_ir::associated_data_impl; use graphql_ir::Argument; use graphql_ir::ConstantValue; use graphql_ir::Directive; @@ -45,6 +44,7 @@ use graphql_ir::Transformed; use graphql_ir::TransformedValue; use graphql_ir::Transformer; use graphql_ir::Value; +use graphql_ir::associated_data_impl; use indexmap::IndexSet; use relay_config::DeferStreamInterface; use relay_config::ModuleImportConfig; @@ -58,13 +58,13 @@ use schema::TypeReference; use schema::UnionID; use super::validation_message::ValidationMessage; +use crate::FragmentAliasMetadata; +use crate::INLINE_DIRECTIVE_NAME; use crate::fragment_alias_directive::FRAGMENT_ALIAS_DIRECTIVE_NAME; use crate::match_::MATCH_CONSTANTS; use crate::no_inline::attach_no_inline_directives_to_fragments; use crate::no_inline::validate_required_no_inline_directive; -use crate::util::get_normalization_operation_name; -use crate::FragmentAliasMetadata; -use crate::INLINE_DIRECTIVE_NAME; +use crate::util::get_normalization_fragment_filename; /// Transform and validate @match and @module pub fn transform_match( @@ -602,11 +602,10 @@ impl<'program, 'flag> MatchTransform<'program, 'flag> { module_directive.name.location, )]; - let mut normalization_name = get_normalization_operation_name(spread.fragment.item.0); - normalization_name.push_str(".graphql"); + let normalization_name = get_normalization_fragment_filename(spread.fragment.item); let mut operation_field_arguments = vec![build_string_literal_argument( MATCH_CONSTANTS.js_field_module_arg, - normalization_name.intern(), + normalization_name, module_directive.name.location, )]; @@ -865,7 +864,7 @@ impl<'program, 'flag> MatchTransform<'program, 'flag> { } } -impl Transformer for MatchTransform<'_, '_> { +impl Transformer<'_> for MatchTransform<'_, '_> { const NAME: &'static str = "MatchTransform"; const VISIT_ARGUMENTS: bool = false; const VISIT_DIRECTIVES: bool = false; @@ -1104,12 +1103,23 @@ fn validate_parent_type_of_fragment_with_read_time_resolver( transform.relay_resolver_model_unions.insert(id); } Type::Object(id) => { - return Err(Diagnostic::error( - ValidationMessage::InvalidModuleOnConcreteParentType { - object_name: transform.program.schema.object(id).name.item, - }, - spread.fragment.location, - )); + let object_has_read_time_resolver = transform + .program + .schema + .object(id) + .directives + .named(*RELAY_RESOLVER_MODEL_DIRECTIVE_NAME) + .is_some(); + + if !object_has_read_time_resolver { + return Err(Diagnostic::error( + ValidationMessage::MissingRelayResolverModelForObject { + spread_name: spread.fragment.item, + object: transform.program.schema.object(id).name.item, + }, + spread.fragment.location, + )); + } } Type::Enum(_) | Type::Scalar(_) | Type::InputObject(_) => { panic!( diff --git a/compiler/crates/relay-transforms/src/match_/split_module_import.rs b/compiler/crates/relay-transforms/src/match_/split_module_import.rs index 2ef03fcbde0ca..4238d7ed95fce 100644 --- a/compiler/crates/relay-transforms/src/match_/split_module_import.rs +++ b/compiler/crates/relay-transforms/src/match_/split_module_import.rs @@ -7,7 +7,9 @@ use std::sync::Arc; +use common::DirectiveName; use common::WithLocation; +use graphql_ir::Directive; use graphql_ir::FragmentDefinitionNameSet; use graphql_ir::InlineFragment; use graphql_ir::OperationDefinition; @@ -22,10 +24,10 @@ use intern::string_key::Intern; use intern::string_key::StringKeyMap; use schema::Schema; -use super::SplitOperationMetadata; use super::MATCH_CONSTANTS; -use crate::util::get_normalization_operation_name; +use super::SplitOperationMetadata; use crate::ModuleMetadata; +use crate::util::get_normalization_operation_name; pub fn split_module_import( program: &Program, @@ -70,7 +72,7 @@ impl<'program, 'base_fragment_names> SplitModuleImportTransform<'program, 'base_ } } -impl Transformer for SplitModuleImportTransform<'_, '_> { +impl Transformer<'_> for SplitModuleImportTransform<'_, '_> { const NAME: &'static str = "SplitModuleImportTransform"; const VISIT_ARGUMENTS: bool = false; const VISIT_DIRECTIVES: bool = false; @@ -104,10 +106,6 @@ impl Transformer for SplitModuleImportTransform<'_, '_> { if self .base_fragment_names .contains(&module_metadata.fragment_name) - // We do not need to generate normalization files for fragments that are - // resolved entirely by read time resolver models on the client side when - // client 3D support for read time resolvers is enabled. - || module_metadata.read_time_resolvers { return self.default_transform_inline_fragment(fragment); } @@ -137,6 +135,20 @@ impl Transformer for SplitModuleImportTransform<'_, '_> { }) .cloned() .collect(); + let operation_directives: Vec = + if module_metadata.read_time_resolvers { + vec![Directive { + name: WithLocation::new( + module_metadata.fragment_source_location, + DirectiveName("exec_time_resolvers".intern()), + ), + arguments: vec![], + data: None, + location: module_metadata.fragment_source_location, + }] + } else { + vec![] + }; ( SplitOperationMetadata { derived_from: Some(module_metadata.fragment_name), @@ -151,7 +163,7 @@ impl Transformer for SplitModuleImportTransform<'_, '_> { ), type_: parent_type, variable_definitions: vec![], - directives: vec![], + directives: operation_directives, selections: next_selections, kind: OperationKind::Query, generated: false, diff --git a/compiler/crates/relay-transforms/src/match_/split_operation_metadata.rs b/compiler/crates/relay-transforms/src/match_/split_operation_metadata.rs index a92a09cc78fab..5a0567fb9dd7b 100644 --- a/compiler/crates/relay-transforms/src/match_/split_operation_metadata.rs +++ b/compiler/crates/relay-transforms/src/match_/split_operation_metadata.rs @@ -10,9 +10,9 @@ use std::hash::Hash; use common::ArgumentName; use common::DirectiveName; use common::Location; -use graphql_ir::associated_data_impl; use graphql_ir::ExecutableDefinitionName; use graphql_ir::FragmentDefinitionName; +use graphql_ir::associated_data_impl; use intern::string_key::Intern; use lazy_static::lazy_static; use rustc_hash::FxHashSet; diff --git a/compiler/crates/relay-transforms/src/match_/subscription_transform.rs b/compiler/crates/relay-transforms/src/match_/subscription_transform.rs index fd2aeac02b47b..bf4cd96f362f5 100644 --- a/compiler/crates/relay-transforms/src/match_/subscription_transform.rs +++ b/compiler/crates/relay-transforms/src/match_/subscription_transform.rs @@ -25,16 +25,16 @@ use graphql_ir::Transformed; use graphql_ir::Transformer; use graphql_ir::Value; use graphql_syntax::OperationKind; -use intern::string_key::Intern; use intern::Lookup; +use intern::string_key::Intern; use schema::FieldID; use schema::Schema; use schema::Type; use schema::TypeReference; +use crate::ModuleMetadata; use crate::match_::MATCH_CONSTANTS; use crate::util::get_normalization_operation_name; -use crate::ModuleMetadata; pub fn transform_subscriptions(program: &Program) -> DiagnosticsResult { let mut transformer = SubscriptionTransform::new(program); @@ -242,7 +242,7 @@ struct ValidFieldResult<'operation> { fragment_spread: &'operation FragmentSpread, } -impl Transformer for SubscriptionTransform<'_> { +impl Transformer<'_> for SubscriptionTransform<'_> { const NAME: &'static str = "SubscriptionTransform"; const VISIT_ARGUMENTS: bool = false; const VISIT_DIRECTIVES: bool = false; diff --git a/compiler/crates/relay-transforms/src/match_/validation_message.rs b/compiler/crates/relay-transforms/src/match_/validation_message.rs index 7b103fe08b6c1..594c8d106d2e7 100644 --- a/compiler/crates/relay-transforms/src/match_/validation_message.rs +++ b/compiler/crates/relay-transforms/src/match_/validation_message.rs @@ -137,7 +137,10 @@ pub enum ValidationMessage { }, #[error( - "@module was used on a fragment with a concrete parent type: '{object_name}'. The parent type should be an interface or union." + "Invalid fragment spread '...{spread_name}'. Object '{object}' should be backed by a relay resolver model." )] - InvalidModuleOnConcreteParentType { object_name: ObjectName }, + MissingRelayResolverModelForObject { + spread_name: FragmentDefinitionName, + object: ObjectName, + }, } diff --git a/compiler/crates/relay-transforms/src/no_inline.rs b/compiler/crates/relay-transforms/src/no_inline.rs index c52d3fcda6066..c3815f696c079 100644 --- a/compiler/crates/relay-transforms/src/no_inline.rs +++ b/compiler/crates/relay-transforms/src/no_inline.rs @@ -27,8 +27,8 @@ use intern::string_key::Intern; use intern::string_key::StringKey; use lazy_static::lazy_static; -use crate::ValidationMessage; use crate::MATCH_CONSTANTS; +use crate::ValidationMessage; lazy_static! { pub static ref NO_INLINE_DIRECTIVE_NAME: DirectiveName = DirectiveName("no_inline".intern()); @@ -138,7 +138,7 @@ impl<'f, 'p> RequiredNoInlineValidator<'f, 'p> { } } -impl<'f, 'p> Validator for RequiredNoInlineValidator<'f, 'p> { +impl Validator for RequiredNoInlineValidator<'_, '_> { const NAME: &'static str = "RequiredNoInlineValidator"; const VALIDATE_ARGUMENTS: bool = false; const VALIDATE_DIRECTIVES: bool = false; diff --git a/compiler/crates/relay-transforms/src/provided_variable_fragment_transform.rs b/compiler/crates/relay-transforms/src/provided_variable_fragment_transform.rs index 3ca3b351ffacf..3c9c484f795aa 100644 --- a/compiler/crates/relay-transforms/src/provided_variable_fragment_transform.rs +++ b/compiler/crates/relay-transforms/src/provided_variable_fragment_transform.rs @@ -36,10 +36,10 @@ use crate::util::format_provided_variable_name; /// This transform applies provided variables in each fragment. /// - Rename all uses of provided variables (in values) -/// \[provided_variable_name\] --> __pv__\[module_name\] +/// \[provided_variable_name\] --> __pv__\[module_name\] /// - Remove provided variables from (local) argument definitions /// - Add provided variables to list of used global variables -/// +/// /// apply_fragment_arguments depends on provide_variable_fragment_transform pub fn provided_variable_fragment_transform(program: &Program) -> DiagnosticsResult { let mut transform = ProvidedVariableFragmentTransform::new(&program.schema); @@ -190,7 +190,7 @@ impl<'schema> ProvidedVariableFragmentTransform<'schema> { } } -impl<'schema> Transformer for ProvidedVariableFragmentTransform<'schema> { +impl Transformer<'_> for ProvidedVariableFragmentTransform<'_> { const NAME: &'static str = "ProvidedVariableFragmentTransform"; const VISIT_ARGUMENTS: bool = true; const VISIT_DIRECTIVES: bool = true; diff --git a/compiler/crates/relay-transforms/src/raw_text.rs b/compiler/crates/relay-transforms/src/raw_text.rs new file mode 100644 index 0000000000000..dc7241ec59ae8 --- /dev/null +++ b/compiler/crates/relay-transforms/src/raw_text.rs @@ -0,0 +1,106 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +use ::intern::string_key::Intern; +use common::ArgumentName; +use common::DiagnosticsResult; +use common::DirectiveName; +use common::Location; +use common::NamedItem; +use common::WithLocation; +use graphql_ir::Argument; +use graphql_ir::ConstantValue; +use graphql_ir::Directive; +use graphql_ir::OperationDefinition; +use graphql_ir::Program; +use graphql_ir::Transformed; +use graphql_ir::Transformer; +use graphql_ir::Value; +use graphql_text_printer::OperationPrinter; +use intern::intern; +use lazy_static::lazy_static; + +use crate::test_operation_metadata::EMIT_RAW_TEXT_ARG; +use crate::test_operation_metadata::TEST_OPERATION_DIRECTIVE; + +lazy_static! { + pub static ref RAW_TEXT_DIRECTIVE_NAME: DirectiveName = DirectiveName(intern!("rawText")); +} + +struct RawTextTransform<'program> { + printer: OperationPrinter<'program>, +} + +impl<'program> RawTextTransform<'program> { + fn new(program: &'program Program) -> Self { + Self { + printer: OperationPrinter::new(program, Default::default()), + } + } +} + +pub fn set_raw_text(program: &Program) -> DiagnosticsResult { + let mut transform = RawTextTransform::new(program); + let next_program = transform + .transform_program(program) + .replace_or_else(|| program.clone()); + Ok(next_program) +} + +impl Transformer<'_> for RawTextTransform<'_> { + const NAME: &'static str = "RawTextTransform"; + + const VISIT_ARGUMENTS: bool = false; + const VISIT_DIRECTIVES: bool = false; + + fn transform_operation( + &mut self, + operation: &OperationDefinition, + ) -> Transformed { + if operation + .directives + .named(*TEST_OPERATION_DIRECTIVE) + .and_then(|directive| directive.arguments.named(*EMIT_RAW_TEXT_ARG)) + .is_some() + { + let raw_text = self.printer.print(operation); + + let mut next_directives = operation.directives.clone(); + next_directives.push(create_raw_text_directive(&raw_text)); + Transformed::Replace(OperationDefinition { + directives: next_directives, + ..operation.clone() + }) + } else { + Transformed::Keep + } + } +} + +pub fn create_raw_text_directive(raw_text: &String) -> Directive { + Directive { + name: WithLocation::generated(*RAW_TEXT_DIRECTIVE_NAME), + arguments: vec![Argument { + name: WithLocation::generated(ArgumentName(intern!("rawText"))), + value: WithLocation::generated(Value::Constant(ConstantValue::String( + raw_text.intern(), + ))), + }], + data: None, + location: Location::generated(), + } +} + +pub fn get_raw_text_value(op: &OperationDefinition) -> Option { + op.directives + .named(*RAW_TEXT_DIRECTIVE_NAME) + .and_then(|dir| dir.arguments.named(ArgumentName(intern!("rawText")))) + .and_then(|arg| match &arg.value.item { + Value::Constant(ConstantValue::String(s)) => Some(s.to_string()), + _ => None, + }) +} diff --git a/compiler/crates/relay-transforms/src/refetchable_fragment.rs b/compiler/crates/relay-transforms/src/refetchable_fragment.rs index e4fa37c74e3a6..0b2a746c71e56 100644 --- a/compiler/crates/relay-transforms/src/refetchable_fragment.rs +++ b/compiler/crates/relay-transforms/src/refetchable_fragment.rs @@ -41,17 +41,18 @@ use relay_config::ProjectConfig; use relay_config::SchemaConfig; use schema::SDLSchema; use schema::Schema; +pub use utils::CONSTANTS; pub use utils::RefetchableDerivedFromMetadata; pub use utils::RefetchableMetadata; -pub use utils::CONSTANTS; +pub use utils::build_used_global_variables; use utils::*; use viewer_query_generator::VIEWER_QUERY_GENERATOR; -use self::refetchable_directive::RefetchableDirective; pub use self::refetchable_directive::REFETCHABLE_NAME; +use self::refetchable_directive::RefetchableDirective; use self::validation_message::ValidationMessage; -use crate::connections::extract_connection_metadata_from_directive; use crate::connections::ConnectionConstants; +use crate::connections::extract_connection_metadata_from_directive; use crate::relay_directive::PLURAL_ARG_NAME; use crate::relay_directive::RELAY_DIRECTIVE_NAME; use crate::root_variables::InferVariablesVisitor; diff --git a/compiler/crates/relay-transforms/src/refetchable_fragment/fetchable_query_generator.rs b/compiler/crates/relay-transforms/src/refetchable_fragment/fetchable_query_generator.rs index 04b9af3eae1bf..5611df01ed3d6 100644 --- a/compiler/crates/relay-transforms/src/refetchable_fragment/fetchable_query_generator.rs +++ b/compiler/crates/relay-transforms/src/refetchable_fragment/fetchable_query_generator.rs @@ -30,17 +30,17 @@ use schema::SDLSchema; use schema::Schema; use schema::Type; +use super::CONSTANTS; +use super::QueryGenerator; +use super::RefetchRoot; +use super::RefetchableIdentifierInfo; +use super::RefetchableMetadata; use super::build_fragment_metadata_as_directive; use super::build_fragment_spread; use super::build_operation_variable_definitions; use super::build_used_global_variables; use super::uses_prefetchable_pagination_in_connection; use super::validation_message::ValidationMessage; -use super::QueryGenerator; -use super::RefetchRoot; -use super::RefetchableIdentifierInfo; -use super::RefetchableMetadata; -use super::CONSTANTS; use crate::root_variables::VariableMap; fn build_refetch_operation( @@ -61,6 +61,11 @@ fn build_refetch_operation( let (fetch_field_id, id_arg) = get_fetch_field_id_and_id_arg(fragment, schema, query_type, fetch_field_name)?; + let fetch_token_field = match schema_config.enable_token_field { + true => Some(schema.fetch_token_field()), + false => None, + }; + let fragment = Arc::new(FragmentDefinition { name: fragment.name, variable_definitions: fragment.variable_definitions.clone(), @@ -87,7 +92,7 @@ fn build_refetch_operation( selections: enforce_selections_with_id_field( fragment, identifier_field_id, - schema.fetch_token_field(), + fetch_token_field, ), }); let mut variable_definitions = build_operation_variable_definitions(&fragment); @@ -227,7 +232,7 @@ fn has_field(selections: &[Selection], field_id: FieldID) -> bool { fn enforce_selections_with_id_field( fragment: &FragmentDefinition, identifier_field_id: FieldID, - fetch_token_field_id: FieldID, + fetch_token_field_id: Option, ) -> Vec { let mut next_selections = fragment.selections.clone(); if !has_field(&next_selections, identifier_field_id) { @@ -238,14 +243,16 @@ fn enforce_selections_with_id_field( directives: vec![], }))); } - if !has_field(&next_selections, fetch_token_field_id) { - next_selections.push(Selection::ScalarField(Arc::new(ScalarField { - alias: None, - definition: WithLocation::generated(fetch_token_field_id), - arguments: vec![], - directives: vec![], - }))); - } + if let Some(fetch_token_field_id) = fetch_token_field_id { + if !has_field(&next_selections, fetch_token_field_id) { + next_selections.push(Selection::ScalarField(Arc::new(ScalarField { + alias: None, + definition: WithLocation::new(fragment.name.location, fetch_token_field_id), + arguments: vec![], + directives: vec![], + }))); + } + }; next_selections } diff --git a/compiler/crates/relay-transforms/src/refetchable_fragment/node_query_generator.rs b/compiler/crates/relay-transforms/src/refetchable_fragment/node_query_generator.rs index 6c4f5bde4dd67..9f81ce917e426 100644 --- a/compiler/crates/relay-transforms/src/refetchable_fragment/node_query_generator.rs +++ b/compiler/crates/relay-transforms/src/refetchable_fragment/node_query_generator.rs @@ -33,17 +33,17 @@ use schema::SDLSchema; use schema::Schema; use schema::Type; +use super::CONSTANTS; +use super::QueryGenerator; +use super::RefetchRoot; +use super::RefetchableIdentifierInfo; +use super::RefetchableMetadata; use super::build_fragment_metadata_as_directive; use super::build_fragment_spread; use super::build_operation_variable_definitions; use super::build_used_global_variables; use super::uses_prefetchable_pagination_in_connection; use super::validation_message::ValidationMessage; -use super::QueryGenerator; -use super::RefetchRoot; -use super::RefetchableIdentifierInfo; -use super::RefetchableMetadata; -use super::CONSTANTS; use crate::root_variables::VariableMap; fn build_refetch_operation( diff --git a/compiler/crates/relay-transforms/src/refetchable_fragment/query_query_generator.rs b/compiler/crates/relay-transforms/src/refetchable_fragment/query_query_generator.rs index 0186d29555441..d1fdb93757ce5 100644 --- a/compiler/crates/relay-transforms/src/refetchable_fragment/query_query_generator.rs +++ b/compiler/crates/relay-transforms/src/refetchable_fragment/query_query_generator.rs @@ -14,14 +14,14 @@ use relay_config::SchemaConfig; use schema::SDLSchema; use schema::Schema; +use super::QueryGenerator; +use super::RefetchRoot; +use super::RefetchableMetadata; use super::build_fragment_metadata_as_directive; use super::build_fragment_spread; use super::build_operation_variable_definitions; use super::build_used_global_variables; use super::uses_prefetchable_pagination_in_connection; -use super::QueryGenerator; -use super::RefetchRoot; -use super::RefetchableMetadata; use crate::root_variables::VariableMap; fn build_refetch_operation( diff --git a/compiler/crates/relay-transforms/src/refetchable_fragment/refetchable_directive.rs b/compiler/crates/relay-transforms/src/refetchable_fragment/refetchable_directive.rs index 994a8c56061f8..792234d6835ce 100644 --- a/compiler/crates/relay-transforms/src/refetchable_fragment/refetchable_directive.rs +++ b/compiler/crates/relay-transforms/src/refetchable_fragment/refetchable_directive.rs @@ -16,10 +16,10 @@ use graphql_ir::ConstantValue; use graphql_ir::Directive; use graphql_ir::OperationDefinitionName; use graphql_ir::Value; -use graphql_text_printer::print_value; use graphql_text_printer::PrinterOptions; -use intern::string_key::Intern; +use graphql_text_printer::print_value; use intern::Lookup; +use intern::string_key::Intern; use lazy_static::lazy_static; use schema::SDLSchema; diff --git a/compiler/crates/relay-transforms/src/refetchable_fragment/utils.rs b/compiler/crates/relay-transforms/src/refetchable_fragment/utils.rs index 5c141fec573bc..bd3a74e98453f 100644 --- a/compiler/crates/relay-transforms/src/refetchable_fragment/utils.rs +++ b/compiler/crates/relay-transforms/src/refetchable_fragment/utils.rs @@ -13,12 +13,10 @@ use common::DiagnosticsResult; use common::DirectiveName; use common::NamedItem; use common::WithLocation; -use graphql_ir::associated_data_impl; use graphql_ir::Argument; use graphql_ir::Directive; use graphql_ir::FragmentDefinition; use graphql_ir::FragmentDefinitionName; -use graphql_ir::FragmentSignature; use graphql_ir::FragmentSpread; use graphql_ir::OperationDefinitionName; use graphql_ir::ProvidedVariableMetadata; @@ -26,6 +24,7 @@ use graphql_ir::Selection; use graphql_ir::Value; use graphql_ir::Variable; use graphql_ir::VariableDefinition; +use graphql_ir::associated_data_impl; use intern::string_key::Intern; use intern::string_key::StringKey; use lazy_static::lazy_static; @@ -88,12 +87,7 @@ pub fn build_fragment_spread(fragment: &FragmentDefinition) -> Selection { ), }) .collect(), - signature: Some(FragmentSignature { - name: fragment.name, - variable_definitions: fragment.variable_definitions.clone(), - type_condition: fragment.type_condition, - directives: fragment.directives.clone(), - }), + signature: Some(fragment.into()), })) } diff --git a/compiler/crates/relay-transforms/src/refetchable_fragment/viewer_query_generator.rs b/compiler/crates/relay-transforms/src/refetchable_fragment/viewer_query_generator.rs index b98d32187fea1..380bd8ba29cc0 100644 --- a/compiler/crates/relay-transforms/src/refetchable_fragment/viewer_query_generator.rs +++ b/compiler/crates/relay-transforms/src/refetchable_fragment/viewer_query_generator.rs @@ -20,16 +20,16 @@ use schema::SDLSchema; use schema::Schema; use schema::Type; +use super::CONSTANTS; +use super::QueryGenerator; +use super::RefetchRoot; +use super::RefetchableMetadata; use super::build_fragment_metadata_as_directive; use super::build_fragment_spread; use super::build_operation_variable_definitions; use super::build_used_global_variables; use super::uses_prefetchable_pagination_in_connection; use super::validation_message::ValidationMessage; -use super::QueryGenerator; -use super::RefetchRoot; -use super::RefetchableMetadata; -use super::CONSTANTS; use crate::root_variables::VariableMap; fn build_refetch_operation( diff --git a/compiler/crates/relay-transforms/src/relay_actor_change.rs b/compiler/crates/relay-transforms/src/relay_actor_change.rs index 708adf8f3a50e..69daf8117cec3 100644 --- a/compiler/crates/relay-transforms/src/relay_actor_change.rs +++ b/compiler/crates/relay-transforms/src/relay_actor_change.rs @@ -70,7 +70,7 @@ impl<'program, 'feature> ActorChangeTransform<'program, 'feature> { } } -impl<'program, 'feature> Transformer for ActorChangeTransform<'program, 'feature> { +impl Transformer<'_> for ActorChangeTransform<'_, '_> { const NAME: &'static str = "ActorChangeTransform"; const VISIT_ARGUMENTS: bool = false; const VISIT_DIRECTIVES: bool = false; diff --git a/compiler/crates/relay-transforms/src/relay_node_identifier.rs b/compiler/crates/relay-transforms/src/relay_node_identifier.rs index 360f0c9459bc7..ae2470754b41c 100644 --- a/compiler/crates/relay-transforms/src/relay_node_identifier.rs +++ b/compiler/crates/relay-transforms/src/relay_node_identifier.rs @@ -8,8 +8,8 @@ use common::DirectiveName; use graphql_ir::node_identifier::LocationAgnosticBehavior; -use crate::util::CustomMetadataDirectives; use crate::ModuleMetadata; +use crate::util::CustomMetadataDirectives; #[derive(Clone)] pub struct RelayLocationAgnosticBehavior; diff --git a/compiler/crates/relay-transforms/src/relay_resolvers.rs b/compiler/crates/relay-transforms/src/relay_resolvers.rs index b87844d8ebde7..4a2910c393c7f 100644 --- a/compiler/crates/relay-transforms/src/relay_resolvers.rs +++ b/compiler/crates/relay-transforms/src/relay_resolvers.rs @@ -22,13 +22,12 @@ use docblock_shared::INJECT_FRAGMENT_DATA_ARGUMENT_NAME; use docblock_shared::LIVE_ARGUMENT_NAME; use docblock_shared::RELAY_RESOLVER_DIRECTIVE_NAME; use docblock_shared::RELAY_RESOLVER_WEAK_OBJECT_DIRECTIVE; +use docblock_shared::RESOLVER_PROPERTY_LOOKUP_NAME; use docblock_shared::TYPE_CONFIRMED_ARGUMENT_NAME; -use graphql_ir::associated_data_impl; use graphql_ir::Argument; use graphql_ir::Directive; use graphql_ir::Field as IrField; use graphql_ir::FragmentDefinitionName; -use graphql_ir::FragmentSignature; use graphql_ir::FragmentSpread; use graphql_ir::InlineFragment; use graphql_ir::LinkedField; @@ -39,11 +38,12 @@ use graphql_ir::Selection; use graphql_ir::Transformed; use graphql_ir::Transformer; use graphql_ir::VariableName; +use graphql_ir::associated_data_impl; use graphql_syntax::BooleanNode; use graphql_syntax::ConstantValue; +use intern::Lookup; use intern::string_key::Intern; use intern::string_key::StringKey; -use intern::Lookup; use relay_config::ProjectName; use schema::ArgumentValue; use schema::Field; @@ -53,13 +53,13 @@ use schema::Schema; use schema::Type; use super::ValidationMessage; -use crate::generate_relay_resolvers_operations_for_nested_objects::generate_name_for_nested_object_operation; -use crate::ClientEdgeMetadata; -use crate::FragmentAliasMetadata; -use crate::RequiredMetadataDirective; use crate::CHILDREN_CAN_BUBBLE_METADATA_KEY; use crate::CLIENT_EDGE_WATERFALL_DIRECTIVE_NAME; +use crate::ClientEdgeMetadata; +use crate::FragmentAliasMetadata; use crate::REQUIRED_DIRECTIVE_NAME; +use crate::RequiredMetadataDirective; +use crate::generate_relay_resolvers_operations_for_nested_objects::generate_name_for_nested_object_operation; /// Transform Relay Resolver fields. This is done in two passes. /// @@ -104,11 +104,17 @@ impl ResolverOutputTypeInfo { } } -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum FragmentDataInjectionMode { Field { name: StringKey, is_required: bool }, // TODO: Add Support for FullData } +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum ResolverSchemaGenType { + ResolverModule, + PropertyLookup { property_name: StringKey }, +} + #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct RelayResolverFieldMetadata { field_parent_type: StringKey, @@ -120,6 +126,7 @@ pub struct RelayResolverFieldMetadata { live: bool, output_type_info: ResolverOutputTypeInfo, type_confirmed: bool, + resolver_type: ResolverSchemaGenType, } associated_data_impl!(RelayResolverFieldMetadata); @@ -140,6 +147,7 @@ pub struct RelayResolverMetadata { FragmentDataInjectionMode, )>, pub type_confirmed: bool, + pub resolver_type: ResolverSchemaGenType, } associated_data_impl!(RelayResolverMetadata); @@ -253,6 +261,7 @@ impl<'program> RelayResolverSpreadTransform<'program> { ) }), type_confirmed: field_metadata.type_confirmed, + resolver_type: field_metadata.resolver_type, }; let mut new_directives: Vec = vec![resolver_metadata.into()]; @@ -266,12 +275,7 @@ impl<'program> RelayResolverSpreadTransform<'program> { Selection::FragmentSpread(Arc::new(FragmentSpread { fragment: fragment_definition.name, arguments: fragment_arguments, - signature: Some(FragmentSignature { - name: fragment_definition.name, - variable_definitions: fragment_definition.variable_definitions.clone(), - type_condition: fragment_definition.type_condition, - directives: fragment_definition.directives.clone(), - }), + signature: Some(fragment_definition.as_ref().into()), directives: new_directives, })) } else { @@ -286,7 +290,7 @@ impl<'program> RelayResolverSpreadTransform<'program> { } } -impl<'program> Transformer for RelayResolverSpreadTransform<'program> { +impl Transformer<'_> for RelayResolverSpreadTransform<'_> { const NAME: &'static str = "RelayResolversSpreadTransform"; const VISIT_ARGUMENTS: bool = false; const VISIT_DIRECTIVES: bool = false; @@ -312,7 +316,7 @@ impl<'program> Transformer for RelayResolverSpreadTransform<'program> { match ClientEdgeMetadata::find(fragment) { Some(client_edge_metadata) => { let backing_id_field = self - .transform_selection(&client_edge_metadata.backing_field) + .transform_selection(client_edge_metadata.backing_field) .unwrap_or_else(|| client_edge_metadata.backing_field.clone()); let selections_field = self @@ -395,7 +399,8 @@ impl<'program> RelayResolverFieldTransform<'program> { live, has_output_type, fragment_data_injection_mode, - type_confirmed + type_confirmed, + resolver_type, }) => { let mut non_required_directives = field.directives().iter().filter(|directive| { @@ -404,6 +409,7 @@ impl<'program> RelayResolverFieldTransform<'program> { && directive.name.item != *REQUIRED_DIRECTIVE_NAME && directive.name.item != *CHILDREN_CAN_BUBBLE_METADATA_KEY && directive.name.item != *CLIENT_EDGE_WATERFALL_DIRECTIVE_NAME + && directive.name.item != crate::match_::MATCH_CONSTANTS.match_directive_name }); if let Some(directive) = non_required_directives.next() { self.errors.push(Diagnostic::error( @@ -493,7 +499,8 @@ impl<'program> RelayResolverFieldTransform<'program> { live, output_type_info, fragment_data_injection_mode, - type_confirmed + type_confirmed, + resolver_type, }; let mut directives: Vec = field.directives().to_vec(); @@ -512,7 +519,7 @@ impl<'program> RelayResolverFieldTransform<'program> { } } -impl Transformer for RelayResolverFieldTransform<'_> { +impl Transformer<'_> for RelayResolverFieldTransform<'_> { const NAME: &'static str = "RelayResolversFieldTransform"; const VISIT_ARGUMENTS: bool = false; const VISIT_DIRECTIVES: bool = false; @@ -569,7 +576,7 @@ impl Transformer for RelayResolverFieldTransform<'_> { let transformed = match ClientEdgeMetadata::find(fragment) { Some(client_edge_metadata) => { let backing_id_field = self - .transform_selection(&client_edge_metadata.backing_field) + .transform_selection(client_edge_metadata.backing_field) .unwrap_or_else(|| client_edge_metadata.backing_field.clone()); let field_name = client_edge_metadata @@ -602,15 +609,16 @@ impl Transformer for RelayResolverFieldTransform<'_> { } } -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct ResolverInfo { pub fragment_name: Option, - fragment_data_injection_mode: Option, + pub fragment_data_injection_mode: Option, pub import_path: StringKey, pub import_name: Option, pub live: bool, has_output_type: bool, - type_confirmed: bool, + pub type_confirmed: bool, + pub resolver_type: ResolverSchemaGenType, } pub fn get_resolver_info( @@ -645,6 +653,13 @@ pub fn get_resolver_info( .ok(); let type_confirmed = get_bool_argument_is_true(arguments, *TYPE_CONFIRMED_ARGUMENT_NAME); + let resolver_type = + match get_argument_value(arguments, *RESOLVER_PROPERTY_LOOKUP_NAME, error_location) + .ok() + { + Some(property_name) => ResolverSchemaGenType::PropertyLookup { property_name }, + None => ResolverSchemaGenType::ResolverModule, + }; Ok(ResolverInfo { fragment_name, @@ -675,6 +690,7 @@ pub fn get_resolver_info( } }), type_confirmed, + resolver_type, }) }) } diff --git a/compiler/crates/relay-transforms/src/relay_resolvers_abstract_types.rs b/compiler/crates/relay-transforms/src/relay_resolvers_abstract_types.rs index f2d89db6f034b..604a7f80ae91b 100644 --- a/compiler/crates/relay-transforms/src/relay_resolvers_abstract_types.rs +++ b/compiler/crates/relay-transforms/src/relay_resolvers_abstract_types.rs @@ -17,7 +17,6 @@ use common::NamedItem; use common::WithLocation; use docblock_shared::RELAY_RESOLVER_DIRECTIVE_NAME; use docblock_shared::ROOT_FRAGMENT_FIELD; -use graphql_ir::transform_list; use graphql_ir::Condition; use graphql_ir::FragmentDefinition; use graphql_ir::InlineFragment; @@ -28,6 +27,7 @@ use graphql_ir::Selection; use graphql_ir::Transformed; use graphql_ir::TransformedValue; use graphql_ir::Transformer; +use graphql_ir::transform_list; use schema::FieldID; use schema::InterfaceID; use schema::Schema; @@ -349,7 +349,7 @@ impl RelayResolverAbstractTypesTransform<'_> { } } -impl Transformer for RelayResolverAbstractTypesTransform<'_> { +impl Transformer<'_> for RelayResolverAbstractTypesTransform<'_> { const NAME: &'static str = "RelayResolverAbstractTypesTransform"; const VISIT_ARGUMENTS: bool = false; const VISIT_DIRECTIVES: bool = false; diff --git a/compiler/crates/relay-transforms/src/remove_base_fragments.rs b/compiler/crates/relay-transforms/src/remove_base_fragments.rs index 8361fcad729e9..1f260817b6519 100644 --- a/compiler/crates/relay-transforms/src/remove_base_fragments.rs +++ b/compiler/crates/relay-transforms/src/remove_base_fragments.rs @@ -47,7 +47,7 @@ struct StripBaseFragmentsTransform<'a> { base_fragment_names: &'a FragmentDefinitionNameSet, } -impl<'a> Transformer for StripBaseFragmentsTransform<'a> { +impl Transformer<'_> for StripBaseFragmentsTransform<'_> { const NAME: &'static str = "StripBaseFragmentsTransform"; const VISIT_ARGUMENTS: bool = false; const VISIT_DIRECTIVES: bool = false; diff --git a/compiler/crates/relay-transforms/src/required_directive.rs b/compiler/crates/relay-transforms/src/required_directive.rs index 0c3b49ae2be43..4b59c6568db17 100644 --- a/compiler/crates/relay-transforms/src/required_directive.rs +++ b/compiler/crates/relay-transforms/src/required_directive.rs @@ -19,7 +19,6 @@ use common::DirectiveName; use common::Location; use common::NamedItem; use common::WithLocation; -use graphql_ir::associated_data_impl; use graphql_ir::Directive; use graphql_ir::Field; use graphql_ir::FragmentDefinition; @@ -33,10 +32,11 @@ use graphql_ir::Selection; use graphql_ir::Transformed; use graphql_ir::TransformedValue; use graphql_ir::Transformer; +use graphql_ir::associated_data_impl; +use intern::Lookup; use intern::string_key::Intern; use intern::string_key::StringKey; use intern::string_key::StringKeyMap; -use intern::Lookup; use lazy_static::lazy_static; use requireable_field::RequireableField; use requireable_field::RequiredMetadata; @@ -319,7 +319,7 @@ impl<'program> RequiredDirective<'program> { } } -impl<'s> Transformer for RequiredDirective<'s> { +impl Transformer<'_> for RequiredDirective<'_> { const NAME: &'static str = "RequiredDirectiveTransform"; const VISIT_ARGUMENTS: bool = false; const VISIT_DIRECTIVES: bool = false; @@ -592,21 +592,25 @@ struct RequiredDirectiveVisitor<'s> { visited_fragments: FragmentDefinitionNameMap, } -impl<'s> DirectiveFinder for RequiredDirectiveVisitor<'s> { +impl DirectiveFinder for RequiredDirectiveVisitor<'_> { fn visit_directive(&self, directive: &Directive) -> bool { directive.name.item == *REQUIRED_DIRECTIVE_NAME } fn visit_fragment_spread(&mut self, fragment_spread: &graphql_ir::FragmentSpread) -> bool { - let fragment = self - .program - .fragment(fragment_spread.fragment.item) - .unwrap(); - self.visit_fragment(fragment) + let fragment = self.program.fragment(fragment_spread.fragment.item); + if let Some(frag) = fragment { + self.visit_fragment(frag) + } else { + // Could not find fragment spread. This can happen if we are running + // this transform via LSP validation where we only validate a single + // tagged template literal in isolation. + false + } } } -impl<'s> RequiredDirectiveVisitor<'s> { +impl RequiredDirectiveVisitor<'_> { fn visit_fragment(&mut self, fragment: &FragmentDefinition) -> bool { if let Some(val) = self.visited_fragments.get(&fragment.name.item) { return *val; diff --git a/compiler/crates/relay-transforms/src/required_directive/requireable_field.rs b/compiler/crates/relay-transforms/src/required_directive/requireable_field.rs index f8a28aa58e8a3..571f0e37aebe2 100644 --- a/compiler/crates/relay-transforms/src/required_directive/requireable_field.rs +++ b/compiler/crates/relay-transforms/src/required_directive/requireable_field.rs @@ -16,9 +16,9 @@ use graphql_ir::ScalarField; use intern::string_key::StringKey; use schema::SDLSchema; -use super::validation_message::RequiredDirectiveValidationMessage; use super::ACTION_ARGUMENT; use super::REQUIRED_DIRECTIVE_NAME; +use super::validation_message::RequiredDirectiveValidationMessage; use crate::RequiredAction; #[derive(Clone, Copy)] diff --git a/compiler/crates/relay-transforms/src/rescript_relay_generate_typename.rs b/compiler/crates/relay-transforms/src/rescript_relay_generate_typename.rs index 1df8994d6cf4f..364febd92e1ba 100644 --- a/compiler/crates/relay-transforms/src/rescript_relay_generate_typename.rs +++ b/compiler/crates/relay-transforms/src/rescript_relay_generate_typename.rs @@ -58,7 +58,7 @@ impl<'s> RescriptRelayGenerateTypenameTransform<'s> { } } -impl<'s> Transformer for RescriptRelayGenerateTypenameTransform<'s> { +impl<'s> Transformer<'_> for RescriptRelayGenerateTypenameTransform<'s> { const NAME: &'static str = "RescriptRelayGenerateTypenameTransform"; const VISIT_ARGUMENTS: bool = false; const VISIT_DIRECTIVES: bool = false; diff --git a/compiler/crates/relay-transforms/src/rescript_relay_inline_codesplit.rs b/compiler/crates/relay-transforms/src/rescript_relay_inline_codesplit.rs index bb994dc5db679..22d6aa415d891 100644 --- a/compiler/crates/relay-transforms/src/rescript_relay_inline_codesplit.rs +++ b/compiler/crates/relay-transforms/src/rescript_relay_inline_codesplit.rs @@ -64,7 +64,7 @@ impl<'s> RescriptRelayInlineCodesplitTransform<'s> { } } -impl<'s> Transformer for RescriptRelayInlineCodesplitTransform<'s> { +impl<'s> Transformer<'_> for RescriptRelayInlineCodesplitTransform<'s> { const NAME: &'static str = "RescriptRelayInlineAutoCodesplitTransform"; const VISIT_ARGUMENTS: bool = false; const VISIT_DIRECTIVES: bool = true; diff --git a/compiler/crates/relay-transforms/src/rescript_relay_transform_codesplit.rs b/compiler/crates/relay-transforms/src/rescript_relay_transform_codesplit.rs index 1ca2c4a5a03b9..84ab9f484e299 100644 --- a/compiler/crates/relay-transforms/src/rescript_relay_transform_codesplit.rs +++ b/compiler/crates/relay-transforms/src/rescript_relay_transform_codesplit.rs @@ -46,7 +46,7 @@ impl<'s> RescriptRelayTransformCodesplitTransform<'s> { // All inline fragments will be flattened into the top-most one in an internal Relay transform that runs after this // (flatten.rs). So, we need to make sure all relevant directives are copied onto the inline fragment that'll remain, // or else they're gone in the inline transform and we can't figure out what to code split. -impl<'s> Transformer for RescriptRelayTransformCodesplitTransform<'s> { +impl<'s> Transformer<'_> for RescriptRelayTransformCodesplitTransform<'s> { const NAME: &'static str = "RescriptRelayTransformAutoCodesplitTransform"; const VISIT_ARGUMENTS: bool = false; const VISIT_DIRECTIVES: bool = true; diff --git a/compiler/crates/relay-transforms/src/root_variables.rs b/compiler/crates/relay-transforms/src/root_variables.rs index 9882b76ca4239..7f24b4ab1f276 100644 --- a/compiler/crates/relay-transforms/src/root_variables.rs +++ b/compiler/crates/relay-transforms/src/root_variables.rs @@ -29,13 +29,19 @@ use super::RelayResolverMetadata; use crate::no_inline::NO_INLINE_DIRECTIVE_NAME; pub type VariableMap = HashMap; -type Visited = FragmentDefinitionNameMap; +pub enum VariableMapEntry { + // The variables for this entry are still being populated as a result of a + // cyclic fragment dependency. + Pending, + Populated(VariableMap), +} +pub type Visited = FragmentDefinitionNameMap; pub struct InferVariablesVisitor<'program> { /// Cache fragments as they are transformed to avoid duplicate processing. /// Because @argument values don't matter (only variable names/types), /// each reachable fragment only has to be checked once. - visited_fragments: Visited, + pub visited_fragments: Visited, program: &'program Program, } @@ -87,6 +93,7 @@ struct VariablesVisitor<'a, 'b> { program: &'a Program, local_variables: HashSet, transitive_local_variables: &'b HashSet, + cycle_detected: bool, errors: Vec, } @@ -103,6 +110,7 @@ impl<'a, 'b> VariablesVisitor<'a, 'b> { program, local_variables, transitive_local_variables, + cycle_detected: false, errors: Default::default(), } } @@ -116,15 +124,27 @@ impl VariablesVisitor<'_, '_> { /// to reanalyze variable usage. fn infer_fragment_variables(&mut self, fragment: &FragmentDefinition) -> VariableMap { if let Some(map) = self.visited_fragments.get(&fragment.name.item) { - map.clone() + match map { + VariableMapEntry::Pending => { + // We have encountered a cycle! For the purposes of the + // parent traversal, we can safely return an empty map since + // the Pending fragment is already being processed and its + // variables will get added. + // + // However, this traversal is invalid, since it does not + // include the dependencies of the Pending fragment. We + // record this here, and consumers of this visitor can + // check if a cycle was encountered and avoid caching + // results which encountered cycles. + self.cycle_detected = true; + Default::default() + } + VariableMapEntry::Populated(map) => map.clone(), + } } else { - // Break cycles by initially caching a version that is empty. - // If the current fragment is reached again, it won't have any - // root variables to add to its parents. The traversal below will - // find any root variables and update the cached version of the - // fragment. + // Populate the cache with a pending entry so we can detect if we encounter a cycle. self.visited_fragments - .insert(fragment.name.item, Default::default()); + .insert(fragment.name.item, VariableMapEntry::Pending); // Avoid collecting local variables usages as root variables let local_variables = fragment @@ -151,8 +171,20 @@ impl VariablesVisitor<'_, '_> { ); visitor.visit_fragment(fragment); let result = visitor.variable_map; - self.visited_fragments - .insert(fragment.name.item, result.clone()); + + if visitor.cycle_detected { + // If a cycle was detected, the result may be missing some + // variables, so we won't cache it. Instead, we'll clean up our + // Pending flag and force other callers to do their own traversal. + self.visited_fragments.remove(&fragment.name.item); + } else { + // If no cycle was detected, this result can safely be reused by + // other traversals, so we'll cache it for later use. + self.visited_fragments.insert( + fragment.name.item, + VariableMapEntry::Populated(result.clone()), + ); + } result } } @@ -208,7 +240,7 @@ impl VariablesVisitor<'_, '_> { } } -impl<'a, 'b> Visitor for VariablesVisitor<'a, 'b> { +impl Visitor for VariablesVisitor<'_, '_> { const NAME: &'static str = "VariablesVisitor"; const VISIT_ARGUMENTS: bool = true; const VISIT_DIRECTIVES: bool = true; diff --git a/compiler/crates/relay-transforms/src/skip_client_directives.rs b/compiler/crates/relay-transforms/src/skip_client_directives.rs index 426c721d4701a..bb5eb8d901ee1 100644 --- a/compiler/crates/relay-transforms/src/skip_client_directives.rs +++ b/compiler/crates/relay-transforms/src/skip_client_directives.rs @@ -28,7 +28,7 @@ impl<'s> SkipClientDirectives<'s> { } } -impl<'s> Transformer for SkipClientDirectives<'s> { +impl Transformer<'_> for SkipClientDirectives<'_> { const NAME: &'static str = "SkipClientDirectives"; const VISIT_ARGUMENTS: bool = false; const VISIT_DIRECTIVES: bool = true; diff --git a/compiler/crates/relay-transforms/src/skip_client_extensions.rs b/compiler/crates/relay-transforms/src/skip_client_extensions.rs index fa36a2c5ca59b..96bf216d74986 100644 --- a/compiler/crates/relay-transforms/src/skip_client_extensions.rs +++ b/compiler/crates/relay-transforms/src/skip_client_extensions.rs @@ -40,7 +40,7 @@ impl<'s> SkipClientExtensionsTransform<'s> { } } -impl<'s> SkipClientExtensionsTransform<'s> { +impl SkipClientExtensionsTransform<'_> { fn is_client_directive(&self, name: DirectiveName) -> bool { // Return true if: // - directive is a custom internal directive used to hold @@ -52,7 +52,7 @@ impl<'s> SkipClientExtensionsTransform<'s> { } } -impl<'s> Transformer for SkipClientExtensionsTransform<'s> { +impl Transformer<'_> for SkipClientExtensionsTransform<'_> { const NAME: &'static str = "SkipClientExtensionsTransform"; const VISIT_ARGUMENTS: bool = false; const VISIT_DIRECTIVES: bool = true; diff --git a/compiler/crates/relay-transforms/src/skip_null_arguments_transform.rs b/compiler/crates/relay-transforms/src/skip_null_arguments_transform.rs index ad07af9da387c..d26dc068e79ee 100644 --- a/compiler/crates/relay-transforms/src/skip_null_arguments_transform.rs +++ b/compiler/crates/relay-transforms/src/skip_null_arguments_transform.rs @@ -24,7 +24,7 @@ pub fn skip_null_arguments_transform(program: &Program) -> Program { struct SkipNullArgumentsTransform; -impl Transformer for SkipNullArgumentsTransform { +impl Transformer<'_> for SkipNullArgumentsTransform { const NAME: &'static str = "SkipNullArgumentsTransform"; const VISIT_ARGUMENTS: bool = true; const VISIT_DIRECTIVES: bool = true; diff --git a/compiler/crates/relay-transforms/src/skip_redundant_nodes.rs b/compiler/crates/relay-transforms/src/skip_redundant_nodes.rs index 5edf4509a1631..49cb1db4f89d1 100644 --- a/compiler/crates/relay-transforms/src/skip_redundant_nodes.rs +++ b/compiler/crates/relay-transforms/src/skip_redundant_nodes.rs @@ -7,11 +7,10 @@ use std::sync::Arc; -use common::sync::*; use common::NamedItem; use common::PointerAddress; +use common::sync::*; use dashmap::DashMap; -use graphql_ir::node_identifier::NodeIdentifier; use graphql_ir::Condition; use graphql_ir::FragmentDefinition; use graphql_ir::InlineFragment; @@ -21,12 +20,13 @@ use graphql_ir::Program; use graphql_ir::Selection; use graphql_ir::Transformed; use graphql_ir::TransformedValue; +use graphql_ir::node_identifier::NodeIdentifier; use relay_config::DeferStreamInterface; use schema::SDLSchema; -use crate::util::is_relay_custom_inline_fragment_directive; use crate::ClientEdgeMetadataDirective; use crate::RelayLocationAgnosticBehavior; +use crate::util::is_relay_custom_inline_fragment_directive; /** * A transform that removes redundant fields and fragment spreads. Redundancy is diff --git a/compiler/crates/relay-transforms/src/skip_split_operation.rs b/compiler/crates/relay-transforms/src/skip_split_operation.rs index 1e0db52280d29..0bfa592315cc9 100644 --- a/compiler/crates/relay-transforms/src/skip_split_operation.rs +++ b/compiler/crates/relay-transforms/src/skip_split_operation.rs @@ -25,7 +25,7 @@ pub fn skip_split_operation(program: &Program) -> Program { pub struct SkipSplitOperation; -impl Transformer for SkipSplitOperation { +impl Transformer<'_> for SkipSplitOperation { const NAME: &'static str = "SkipSplitOperationTransform"; const VISIT_ARGUMENTS: bool = false; const VISIT_DIRECTIVES: bool = false; diff --git a/compiler/crates/relay-transforms/src/skip_unreachable_node.rs b/compiler/crates/relay-transforms/src/skip_unreachable_node.rs index 8c776df688baf..1a1992be890a3 100644 --- a/compiler/crates/relay-transforms/src/skip_unreachable_node.rs +++ b/compiler/crates/relay-transforms/src/skip_unreachable_node.rs @@ -10,7 +10,6 @@ use std::sync::Arc; use common::Diagnostic; use common::DiagnosticsResult; use common::NamedItem; -use graphql_ir::transform_list_multi; use graphql_ir::Condition; use graphql_ir::ConditionValue; use graphql_ir::ConstantValue; @@ -27,6 +26,7 @@ use graphql_ir::TransformedMulti; use graphql_ir::TransformedValue; use graphql_ir::Transformer; use graphql_ir::Value; +use graphql_ir::transform_list_multi; use intern::string_key::StringKey; use relay_config::DeferStreamInterface; use thiserror::Error; @@ -86,7 +86,7 @@ pub struct SkipUnreachableNodeTransform<'s> { defer_stream_interface: DeferStreamInterface, } -impl<'s> Transformer for SkipUnreachableNodeTransform<'s> { +impl Transformer<'_> for SkipUnreachableNodeTransform<'_> { const NAME: &'static str = "SkipUnreachableNodeTransform"; const VISIT_ARGUMENTS: bool = false; const VISIT_DIRECTIVES: bool = false; diff --git a/compiler/crates/relay-transforms/src/skip_updatable_queries.rs b/compiler/crates/relay-transforms/src/skip_updatable_queries.rs index d153a21110836..64355553118e0 100644 --- a/compiler/crates/relay-transforms/src/skip_updatable_queries.rs +++ b/compiler/crates/relay-transforms/src/skip_updatable_queries.rs @@ -30,7 +30,7 @@ impl<'s> SkipUpdatableQueries<'s> { } } -impl<'s> Transformer for SkipUpdatableQueries<'s> { +impl Transformer<'_> for SkipUpdatableQueries<'_> { const NAME: &'static str = "SkipUpdatableQueriesTransform"; const VISIT_ARGUMENTS: bool = false; const VISIT_DIRECTIVES: bool = true; diff --git a/compiler/crates/relay-transforms/src/sort_selections.rs b/compiler/crates/relay-transforms/src/sort_selections.rs index a1e53532499f4..4b5553990dfb8 100644 --- a/compiler/crates/relay-transforms/src/sort_selections.rs +++ b/compiler/crates/relay-transforms/src/sort_selections.rs @@ -9,7 +9,6 @@ use std::cmp::Ordering; use std::collections::HashMap; use common::PointerAddress; -use graphql_ir::transform_list; use graphql_ir::Field; use graphql_ir::FragmentDefinition; use graphql_ir::OperationDefinition; @@ -18,6 +17,7 @@ use graphql_ir::Selection; use graphql_ir::Transformed; use graphql_ir::TransformedValue; use graphql_ir::Transformer; +use graphql_ir::transform_list; type Seen = HashMap>; @@ -43,7 +43,7 @@ impl<'s> SortSelectionsTransform<'s> { } } -impl Transformer for SortSelectionsTransform<'_> { +impl Transformer<'_> for SortSelectionsTransform<'_> { const NAME: &'static str = "SortSelectionsTransform"; const VISIT_ARGUMENTS: bool = false; const VISIT_DIRECTIVES: bool = false; diff --git a/compiler/crates/relay-transforms/src/test_operation_metadata.rs b/compiler/crates/relay-transforms/src/test_operation_metadata.rs index 4d0a2fb1691b5..7803c69f226f9 100644 --- a/compiler/crates/relay-transforms/src/test_operation_metadata.rs +++ b/compiler/crates/relay-transforms/src/test_operation_metadata.rs @@ -33,12 +33,12 @@ use schema::SDLSchema; use schema::Schema; use schema::Type; -use crate::create_metadata_directive; -use crate::ValidationMessage; use crate::DIRECTIVE_SPLIT_OPERATION; +use crate::ValidationMessage; +use crate::create_metadata_directive; lazy_static! { - static ref TEST_OPERATION_DIRECTIVE: DirectiveName = + pub static ref TEST_OPERATION_DIRECTIVE: DirectiveName = DirectiveName("relay_test_operation".intern()); static ref TEST_OPERATION_METADATA_KEY: ArgumentName = ArgumentName("relayTestingSelectionTypeInfo".intern()); @@ -48,6 +48,7 @@ lazy_static! { static ref TYPE_KEY: ArgumentName = ArgumentName("type".intern()); static ref DO_NOT_USE_USE_IN_PRODUCTION_ARG: ArgumentName = ArgumentName("DO_NOT_USE_use_in_production".intern()); + pub static ref EMIT_RAW_TEXT_ARG: ArgumentName = ArgumentName("emitRawText".intern()); } /// Transforms the @relay_test_operation directive to @__metadata thats printed @@ -86,7 +87,7 @@ impl<'a> GenerateTestOperationMetadata<'a> { } } -impl<'a> Transformer for GenerateTestOperationMetadata<'a> { +impl Transformer<'_> for GenerateTestOperationMetadata<'_> { const NAME: &'static str = "GenerateTestOperationMetadata"; const VISIT_ARGUMENTS: bool = false; const VISIT_DIRECTIVES: bool = false; @@ -103,7 +104,7 @@ impl<'a> Transformer for GenerateTestOperationMetadata<'a> { && test_operation_directive .arguments .named(*DO_NOT_USE_USE_IN_PRODUCTION_ARG) - .map_or(true, |arg| { + .is_none_or(|arg| { if let Value::Constant(ConstantValue::Boolean(arg_value)) = arg.value.item { diff --git a/compiler/crates/relay-transforms/src/transform_connections.rs b/compiler/crates/relay-transforms/src/transform_connections.rs index e55657c9be39e..6fc07c6562a35 100644 --- a/compiler/crates/relay-transforms/src/transform_connections.rs +++ b/compiler/crates/relay-transforms/src/transform_connections.rs @@ -7,10 +7,10 @@ use std::sync::Arc; +use ::intern::Lookup; use ::intern::intern; use ::intern::string_key::Intern; use ::intern::string_key::StringKey; -use ::intern::Lookup; use common::ArgumentName; use common::Location; use common::NamedItem; @@ -26,35 +26,40 @@ use graphql_ir::InlineFragment; use graphql_ir::LinkedField; use graphql_ir::OperationDefinition; use graphql_ir::Program; +use graphql_ir::ProvidedVariableMetadata; use graphql_ir::Selection; use graphql_ir::Transformed; use graphql_ir::Transformer; use graphql_ir::Value; use graphql_ir::Value::Constant; +use graphql_ir::Variable; +use graphql_ir::VariableDefinition; use relay_config::DeferStreamInterface; use schema::Schema; +use crate::connections::ConnectionConstants; +use crate::connections::ConnectionInterface; +use crate::connections::ConnectionMetadata; +use crate::connections::ConnectionMetadataDirective; use crate::connections::assert_connection_selections; use crate::connections::build_connection_metadata; use crate::connections::build_edge_selections; use crate::connections::build_page_info_selections; use crate::connections::extract_connection_directive; use crate::connections::get_default_filters; -use crate::connections::ConnectionConstants; -use crate::connections::ConnectionInterface; -use crate::connections::ConnectionMetadata; -use crate::connections::ConnectionMetadataDirective; -use crate::handle_fields::build_handle_field_directive_from_connection_directive; use crate::handle_fields::KEY_ARG_NAME; +use crate::handle_fields::build_handle_field_directive_from_connection_directive; +use crate::refetchable_fragment::build_used_global_variables; use crate::relay_directive::PLURAL_ARG_NAME; use crate::relay_directive::RELAY_DIRECTIVE_NAME; +use crate::root_variables::InferVariablesVisitor; pub fn transform_connections( program: &Program, connection_interface: &ConnectionInterface, defer_stream_interface: &DeferStreamInterface, - // Does not do other validaiton and transforms only extract prefetchable pagination - // fragment. Needed for generating correct types in the typegen pipeline. + // Determines whether or not to skip other validation and transforms and only extract prefetchable + // pagination fragment. Needed for generating correct types in the typegen pipeline. only_extract_prefetchable_pagination_fragment: bool, ) -> Program { let mut transform = ConnectionTransform::new( @@ -78,6 +83,7 @@ struct ConnectionTransform<'s> { current_path: Option>, current_connection_metadata: Vec, current_document_name: StringKey, + fragment_variable_definitions: Vec, program: &'s Program, defer_stream_interface: &'s DeferStreamInterface, edge_fragments: Vec>, @@ -97,6 +103,7 @@ impl<'s> ConnectionTransform<'s> { current_path: None, current_document_name: connection_interface.cursor, // Set an arbitrary value to avoid Option current_connection_metadata: Vec::new(), + fragment_variable_definitions: Vec::new(), program, defer_stream_interface, edge_fragments: vec![], @@ -346,15 +353,15 @@ impl<'s> ConnectionTransform<'s> { let fields = std::mem::take(&mut edges_field_to_maybe_fragmentify.selections); let location = edges_field_to_maybe_fragmentify.alias_or_name_location(); - let edges_fragment = Arc::new(FragmentDefinition { + let mut edges_fragment_without_used_global_variables = FragmentDefinition { name: WithLocation::new( location, FragmentDefinitionName( format!("{}__edges", self.current_document_name).intern(), ), ), - variable_definitions: vec![], //TODO: Do we need variable_definitions?, - used_global_variables: vec![], //TODO: Do we need used_global_variables?, + variable_definitions: self.fragment_variable_definitions.clone(), + used_global_variables: vec![], type_condition: edge_type, directives: vec![Directive { name: WithLocation::new(location, *RELAY_DIRECTIVE_NAME), @@ -369,12 +376,42 @@ impl<'s> ConnectionTransform<'s> { location, }], selections: fields, - }); + }; + + // Determine used_global_variables for the edges fragment. + let variables_map = InferVariablesVisitor::new(self.program) + .infer_fragment_variables( + &edges_fragment_without_used_global_variables, + ); + edges_fragment_without_used_global_variables.used_global_variables = + build_used_global_variables( + &variables_map, + &edges_fragment_without_used_global_variables.variable_definitions, + ) + .unwrap_or_default(); + + let edges_fragment = Arc::new(edges_fragment_without_used_global_variables); edges_field_to_maybe_fragmentify.selections.push( Selection::FragmentSpread(Arc::new(FragmentSpread { fragment: edges_fragment.name, - arguments: vec![], + arguments: edges_fragment + .variable_definitions + .iter() + .filter(|def| { + ProvidedVariableMetadata::find(&def.directives).is_none() + }) + .map(|var| Argument { + name: var.name.map(|x| ArgumentName(x.0)), + value: WithLocation::new( + var.name.location, + Value::Variable(Variable { + name: var.name, + type_: var.type_.clone(), + }), + ), + }) + .collect(), signature: None, directives: vec![], })), @@ -476,7 +513,7 @@ impl<'s> ConnectionTransform<'s> { } } -impl<'s> Transformer for ConnectionTransform<'s> { +impl Transformer<'_> for ConnectionTransform<'_> { const NAME: &'static str = "ConnectionTransform"; const VISIT_ARGUMENTS: bool = false; const VISIT_DIRECTIVES: bool = false; @@ -516,6 +553,7 @@ impl<'s> Transformer for ConnectionTransform<'s> { self.current_document_name = fragment.name.item.0; self.current_path = Some(Vec::new()); self.current_connection_metadata = Vec::new(); + self.fragment_variable_definitions = fragment.variable_definitions.clone(); let transformed = self.default_transform_fragment(fragment); if self.current_connection_metadata.is_empty() { diff --git a/compiler/crates/relay-transforms/src/unwrap_custom_directive_selection.rs b/compiler/crates/relay-transforms/src/unwrap_custom_directive_selection.rs index db4a18d100d0f..cc636f0b8dcb9 100644 --- a/compiler/crates/relay-transforms/src/unwrap_custom_directive_selection.rs +++ b/compiler/crates/relay-transforms/src/unwrap_custom_directive_selection.rs @@ -41,7 +41,7 @@ impl UnwrapCustomDirectiveSelection { } } -impl Transformer for UnwrapCustomDirectiveSelection { +impl Transformer<'_> for UnwrapCustomDirectiveSelection { const NAME: &'static str = "UnwrapCustomDirectiveSelection"; const VISIT_ARGUMENTS: bool = false; const VISIT_DIRECTIVES: bool = false; diff --git a/compiler/crates/relay-transforms/src/util.rs b/compiler/crates/relay-transforms/src/util.rs index 1689f9a3205e3..8d5fcd2e8a357 100644 --- a/compiler/crates/relay-transforms/src/util.rs +++ b/compiler/crates/relay-transforms/src/util.rs @@ -6,42 +6,43 @@ */ use common::DirectiveName; +use graphql_ir::ARGUMENT_DEFINITION; use graphql_ir::Argument; use graphql_ir::Directive; use graphql_ir::FragmentDefinitionName; use graphql_ir::ProvidedVariableMetadata; +use graphql_ir::UNUSED_LOCAL_VARIABLE_DEPRECATED; use graphql_ir::Value; use graphql_ir::VariableName; -use graphql_ir::ARGUMENT_DEFINITION; -use graphql_ir::UNUSED_LOCAL_VARIABLE_DEPRECATED; +use intern::Lookup; use intern::string_key::Intern; use intern::string_key::StringKey; -use intern::Lookup; use lazy_static::lazy_static; use regex::Regex; use schema::SDLSchema; use schema::Schema; use schema::Type; +use crate::ClientEdgeGeneratedQueryMetadataDirective; +use crate::ClientEdgeMetadataDirective; +use crate::DIRECTIVE_SPLIT_OPERATION; +use crate::FragmentAliasMetadata; +use crate::INTERNAL_METADATA_DIRECTIVE; +use crate::ModuleMetadata; +use crate::RefetchableDerivedFromMetadata; +use crate::RelayResolverMetadata; +use crate::RequiredMetadataDirective; use crate::catch_directive::CATCH_DIRECTIVE_NAME; use crate::client_extensions::CLIENT_EXTENSION_DIRECTIVE_NAME; use crate::connections::ConnectionMetadataDirective; use crate::fragment_alias_directive::FRAGMENT_DANGEROUSLY_UNALIAS_DIRECTIVE_NAME; use crate::handle_fields::HANDLE_FIELD_DIRECTIVE_NAME; use crate::inline_data_fragment::InlineDirectiveMetadata; +use crate::raw_text::RAW_TEXT_DIRECTIVE_NAME; use crate::refetchable_fragment::RefetchableMetadata; use crate::relay_actor_change::RELAY_ACTOR_CHANGE_DIRECTIVE_FOR_CODEGEN; use crate::required_directive::CHILDREN_CAN_BUBBLE_METADATA_KEY; use crate::required_directive::REQUIRED_DIRECTIVE_NAME; -use crate::ClientEdgeGeneratedQueryMetadataDirective; -use crate::ClientEdgeMetadataDirective; -use crate::FragmentAliasMetadata; -use crate::ModuleMetadata; -use crate::RefetchableDerivedFromMetadata; -use crate::RelayResolverMetadata; -use crate::RequiredMetadataDirective; -use crate::DIRECTIVE_SPLIT_OPERATION; -use crate::INTERNAL_METADATA_DIRECTIVE; /// This function will return a new Vec[...] of directives, /// where one will be missing. The one with `remove_directive_name` name @@ -86,7 +87,7 @@ pub fn extract_variable_name(argument: Option<&Argument>) -> Option { } lazy_static! { - static ref CUSTOM_METADATA_DIRECTIVES: [DirectiveName; 20] = [ + static ref CUSTOM_METADATA_DIRECTIVES: [DirectiveName; 21] = [ *CATCH_DIRECTIVE_NAME, *CLIENT_EXTENSION_DIRECTIVE_NAME, ConnectionMetadataDirective::directive_name(), @@ -107,6 +108,7 @@ lazy_static! { *RELAY_ACTOR_CHANGE_DIRECTIVE_FOR_CODEGEN, ProvidedVariableMetadata::directive_name(), FragmentAliasMetadata::directive_name(), + *RAW_TEXT_DIRECTIVE_NAME, ]; static ref DIRECTIVES_SKIPPED_IN_NODE_IDENTIFIER: [DirectiveName; 10] = [ *CATCH_DIRECTIVE_NAME, diff --git a/compiler/crates/relay-transforms/src/validate_operation_variables.rs b/compiler/crates/relay-transforms/src/validate_operation_variables.rs index fa342075aee32..76bb17be8965d 100644 --- a/compiler/crates/relay-transforms/src/validate_operation_variables.rs +++ b/compiler/crates/relay-transforms/src/validate_operation_variables.rs @@ -69,7 +69,7 @@ impl<'s> ValidateOperationVariables<'s> { /// Refines the argument definitions for operations to remove unused arguments /// due to statically pruned conditional branches (e.g. because of overriding /// a variable used in `@include()` to be false). -impl<'s> Transformer for ValidateOperationVariables<'s> { +impl Transformer<'_> for ValidateOperationVariables<'_> { const NAME: &'static str = "ValidateOperationVariables"; const VISIT_ARGUMENTS: bool = false; const VISIT_DIRECTIVES: bool = false; diff --git a/compiler/crates/relay-transforms/src/validations.rs b/compiler/crates/relay-transforms/src/validations.rs index 8ef39eeebfbf4..2210b84fc5a6f 100644 --- a/compiler/crates/relay-transforms/src/validations.rs +++ b/compiler/crates/relay-transforms/src/validations.rs @@ -13,6 +13,7 @@ mod disallow_required_on_non_null_field; mod disallow_reserved_aliases; mod disallow_typename_on_root; mod rescript_relay_disallow_invalid_names; +mod validate_client_schema_extensions_use_catch; mod validate_connections; mod validate_exhaustive_directive; mod validate_fragment_alias_conflict; @@ -36,10 +37,10 @@ pub use disallow_circular_no_inline_fragments::disallow_circular_no_inline_fragm pub use disallow_non_node_id_fields::disallow_non_node_id_fields; pub use disallow_readtime_features_in_mutations::disallow_readtime_features_in_mutations; pub use disallow_required_on_non_null_field::disallow_required_on_non_null_field; -pub use disallow_required_on_non_null_field::disallow_required_on_non_null_field_for_executable_definition; pub use disallow_reserved_aliases::disallow_reserved_aliases; pub use disallow_typename_on_root::disallow_typename_on_root; pub use rescript_relay_disallow_invalid_names::rescript_relay_disallow_invalid_names; +pub use validate_client_schema_extensions_use_catch::validate_client_schema_extensions_use_catch; pub use validate_connections::validate_connections; pub use validate_exhaustive_directive::validate_exhaustive_directive; pub use validate_fragment_alias_conflict::validate_fragment_alias_conflict; @@ -56,4 +57,5 @@ pub use validate_resolver_fragments::validate_resolver_fragments; pub use validate_server_only_directives::validate_server_only_directives; pub use validate_static_args::validate_static_args; pub use validate_unused_fragment_variables::validate_unused_fragment_variables; +pub use validate_unused_variables::ValidateUnusedVariables; pub use validate_unused_variables::validate_unused_variables; diff --git a/compiler/crates/relay-transforms/src/validations/deprecated_fields.rs b/compiler/crates/relay-transforms/src/validations/deprecated_fields.rs index e6843b2ee02e6..1266cbc04a1dd 100644 --- a/compiler/crates/relay-transforms/src/validations/deprecated_fields.rs +++ b/compiler/crates/relay-transforms/src/validations/deprecated_fields.rs @@ -105,7 +105,7 @@ impl<'a> DeprecatedFields<'a> { // While the individual methods return a diagnostic, since using deprecated fields are not errors per-se, we reserve // returning an `Err` for cases where we are unable to correctly check. // Deprecation warnings are collected in `self.warnings`. -impl<'a> Validator for DeprecatedFields<'a> { +impl Validator for DeprecatedFields<'_> { const NAME: &'static str = "DeprecatedFields"; const VALIDATE_ARGUMENTS: bool = false; const VALIDATE_DIRECTIVES: bool = true; diff --git a/compiler/crates/relay-transforms/src/validations/disallow_non_node_id_fields.rs b/compiler/crates/relay-transforms/src/validations/disallow_non_node_id_fields.rs index 8fcb2692eddc6..f560a5a1bf5d8 100644 --- a/compiler/crates/relay-transforms/src/validations/disallow_non_node_id_fields.rs +++ b/compiler/crates/relay-transforms/src/validations/disallow_non_node_id_fields.rs @@ -110,7 +110,7 @@ fn validate_all_id_fields( .type_ .inner() .get_scalar_id() - .map_or(false, |scalar_id| { + .is_some_and(|scalar_id| { !NON_STRING_SCALARS.contains(&program.schema.scalar(scalar_id).name.item) }) }; diff --git a/compiler/crates/relay-transforms/src/validations/disallow_required_on_non_null_field.rs b/compiler/crates/relay-transforms/src/validations/disallow_required_on_non_null_field.rs index 6017c726a131b..1d5a0ea8589e4 100644 --- a/compiler/crates/relay-transforms/src/validations/disallow_required_on_non_null_field.rs +++ b/compiler/crates/relay-transforms/src/validations/disallow_required_on_non_null_field.rs @@ -5,6 +5,7 @@ * LICENSE file in the root directory of this source tree. */ +use std::collections::HashMap; use std::sync::Arc; use ::errors::try_all; @@ -12,23 +13,24 @@ use common::Diagnostic; use common::DiagnosticTag; use common::DiagnosticsResult; use common::DirectiveName; +use common::Location; use common::NamedItem; use errors::try2; -use graphql_ir::reexport::Intern; -use graphql_ir::ExecutableDefinition; use graphql_ir::Field; use graphql_ir::FragmentDefinition; use graphql_ir::Program; use graphql_ir::Selection; use graphql_ir::Validator; +use graphql_ir::reexport::Intern; +use intern::string_key::StringKey; use lazy_static::lazy_static; use schema::SDLSchema; use schema::Schema; -use crate::ValidationMessageWithData; use crate::CATCH_DIRECTIVE_NAME; use crate::CHILDREN_CAN_BUBBLE_METADATA_KEY; use crate::REQUIRED_DIRECTIVE_NAME; +use crate::ValidationMessageWithData; lazy_static! { static ref SEMANTIC_NON_NULL_DIRECTIVE: DirectiveName = @@ -37,31 +39,24 @@ lazy_static! { DirectiveName("throwOnFieldError".intern()); } -pub fn disallow_required_on_non_null_field( - schema: &Arc, - program: &Program, -) -> DiagnosticsResult> { - let mut validator = DisallowRequiredOnNonNullField::new(schema); +pub fn disallow_required_on_non_null_field(program: &Program) -> DiagnosticsResult<()> { + let mut validator = DisallowRequiredOnNonNullField::new(&program.schema); validator.validate_program(program)?; - Ok(validator.warnings) -} -pub fn disallow_required_on_non_null_field_for_executable_definition( - schema: &Arc, - definition: &ExecutableDefinition, -) -> DiagnosticsResult> { - let mut validator = DisallowRequiredOnNonNullField::new(schema); - - match definition { - ExecutableDefinition::Fragment(fragment) => validator.validate_fragment(fragment), - ExecutableDefinition::Operation(operation) => validator.validate_operation(operation), - }?; - Ok(validator.warnings) + if validator.warnings.is_empty() { + Ok(()) + } else { + Err(validator.warnings) + } } +type FieldPath = Vec; + struct DisallowRequiredOnNonNullField<'a> { schema: &'a Arc, warnings: Vec, + path: FieldPath, + modifiable_fields: HashMap, } impl<'a> DisallowRequiredOnNonNullField<'a> { @@ -69,9 +64,39 @@ impl<'a> DisallowRequiredOnNonNullField<'a> { Self { schema, warnings: vec![], + path: vec![], + modifiable_fields: HashMap::new(), } } + // Tracks field required-directive removal eligibility. If a field's required directive, + // is not removable, it remains not removable forever. Otherwise it is removable and we + // accumulate the locations where it can be removed... unless it becomes not removable + // later on. + fn update_field_action(&mut self, new_action: Action) { + let mut path = self.path.clone(); + path.reverse(); + let existing_action = self.modifiable_fields.get(&path); + self.modifiable_fields.insert( + path, + match existing_action { + None => new_action, + Some(Action::NotRemovable) => { + // already not removable, keep it that way + Action::NotRemovable + } + Some(Action::Removable(list)) => match new_action { + Action::NotRemovable => Action::NotRemovable, + Action::Removable(new_list) => { + let mut new_list = new_list.clone(); + new_list.extend(list.clone()); + Action::Removable(new_list) + } + }, + }, + ); + } + fn validate_required_field( &mut self, field: &Arc, @@ -93,11 +118,10 @@ impl<'a> DisallowRequiredOnNonNullField<'a> { .type_ .is_non_null() { - self.warnings.push(Diagnostic::hint_with_data( - ValidationMessageWithData::RequiredOnNonNull, - required_directive.unwrap().location, - vec![DiagnosticTag::UNNECESSARY], - )); + self.update_field_action(Action::Removable(vec![Message { + message: ValidationMessageWithData::RequiredOnNonNull, + location: required_directive.unwrap().location, + }])); } else if self .schema .field(field.definition().item) @@ -106,11 +130,12 @@ impl<'a> DisallowRequiredOnNonNullField<'a> { .is_some() { // @required on a semantically-non-null field is unnecessary - self.warnings.push(Diagnostic::hint_with_data( - ValidationMessageWithData::RequiredOnSemanticNonNull, - required_directive.unwrap().location, - vec![DiagnosticTag::UNNECESSARY], - )); + self.update_field_action(Action::Removable(vec![Message { + message: ValidationMessageWithData::RequiredOnSemanticNonNull, + location: required_directive.unwrap().location, + }])); + } else { + self.update_field_action(Action::NotRemovable); } Ok(()) @@ -137,42 +162,93 @@ impl<'a> DisallowRequiredOnNonNullField<'a> { None => self.validate_required_field(linked_field, errors_are_caught), }; + self.path.push(linked_field.alias_or_name(self.schema)); + let selection_result = self.validate_selection_fields(&linked_field.selections, errors_are_caught); try2(field_result, selection_result)?; + self.path.pop(); Ok(()) } Selection::ScalarField(scalar_field) => { self.validate_required_field(scalar_field, errors_are_caught) } + Selection::InlineFragment(fragment) => { + if let Ok(Some(alias)) = fragment.alias(self.schema) { + self.path.push(alias.item); + let result = + self.validate_selection_fields(&fragment.selections, errors_are_caught); + self.path.pop(); + result + } else { + self.validate_selection_fields(&fragment.selections, errors_are_caught) + } + } _ => Ok(()), }))?; Ok(()) } + + fn modifiable_fields_to_warnings(&mut self) { + for (_, action) in self.modifiable_fields.iter() { + match action { + Action::NotRemovable => {} + Action::Removable(message_list) => { + for message in message_list { + self.warnings.push(Diagnostic::hint_with_data( + message.message.clone(), + message.location, + vec![DiagnosticTag::UNNECESSARY], + )); + } + } + } + } + } } + impl Validator for DisallowRequiredOnNonNullField<'_> { const NAME: &'static str = "disallow_required_on_non_null_field"; const VALIDATE_ARGUMENTS: bool = false; const VALIDATE_DIRECTIVES: bool = false; fn validate_fragment(&mut self, fragment: &FragmentDefinition) -> DiagnosticsResult<()> { + self.modifiable_fields.clear(); let throw_on_field_error_directive = fragment.directives.named(*THROW_ON_FIELD_ERROR_DIRECTIVE); let has_throw_on_field_error_directive = throw_on_field_error_directive.is_some(); - self.validate_selection_fields(&fragment.selections, has_throw_on_field_error_directive) + let ret = self + .validate_selection_fields(&fragment.selections, has_throw_on_field_error_directive); + self.modifiable_fields_to_warnings(); + ret } fn validate_operation( &mut self, operation: &graphql_ir::OperationDefinition, ) -> DiagnosticsResult<()> { + self.modifiable_fields.clear(); let throw_on_field_error_directive = operation.directives.named(*THROW_ON_FIELD_ERROR_DIRECTIVE); let has_throw_on_field_error_directive = throw_on_field_error_directive.is_some(); - self.validate_selection_fields(&operation.selections, has_throw_on_field_error_directive) + let result = self + .validate_selection_fields(&operation.selections, has_throw_on_field_error_directive); + self.modifiable_fields_to_warnings(); + result } } + +#[derive(Clone, Debug)] +struct Message { + location: Location, + message: ValidationMessageWithData, +} + +enum Action { + NotRemovable, + Removable(Vec), +} diff --git a/compiler/crates/relay-transforms/src/validations/validate_client_schema_extensions_use_catch.rs b/compiler/crates/relay-transforms/src/validations/validate_client_schema_extensions_use_catch.rs new file mode 100644 index 0000000000000..d18fa55217fe9 --- /dev/null +++ b/compiler/crates/relay-transforms/src/validations/validate_client_schema_extensions_use_catch.rs @@ -0,0 +1,168 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +use std::sync::Arc; + +use common::Diagnostic; +use common::DiagnosticsResult; +use common::DirectiveName; +use common::NamedItem; +use docblock_shared::RELAY_RESOLVER_DIRECTIVE_NAME; +use docblock_shared::RELAY_RESOLVER_MODEL_GENERATED_ID_FIELD_DIRECTIVE_NAME; +use graphql_ir::Field; +use graphql_ir::FragmentDefinition; +use graphql_ir::Program; +use graphql_ir::Validator; +use graphql_ir::reexport::Intern; +use lazy_static::lazy_static; +use schema::Field as SchemaField; +use schema::SDLSchema; +use schema::Schema; +use thiserror::Error; + +use crate::CATCH_DIRECTIVE_NAME; +lazy_static! { + static ref THROW_ON_FIELD_ERROR_DIRECTIVE: DirectiveName = + DirectiveName("throwOnFieldError".intern()); +} + +/// Within @throwOnFieldError, we treat missing data as an error. However, client schema extensions +/// have no practical way to ensure that a field has been set before it is read. This means we must +/// handle the case where a field is missing data gracefully. This validator ensures that all +/// such client schema extension fields are wrapped in a @catch directive. +/// +/// Note that within a linked client schema extension, we can't use @catch because it is not +pub fn validate_client_schema_extensions_use_catch(program: &Program) -> DiagnosticsResult<()> { + let mut validator = EnsureCatch::new(&program.schema); + validator.validate_program(program) +} + +struct EnsureCatch<'a> { + schema: &'a Arc, +} + +impl<'a> EnsureCatch<'a> { + fn new(schema: &'a Arc) -> Self { + Self { schema } + } + + fn is_non_resolver_extension(&self, field: &SchemaField) -> bool { + if !field.is_extension { + return false; + } + // Relay Resolvers are added in such a way that they are marked as extensions, but they don't + // need to be wrapped in @catch since they are guaranteed by Relay Runtime to be set. + if field + .directives + .named(*RELAY_RESOLVER_DIRECTIVE_NAME) + .is_some() + { + return false; + } + + // When defining a strong resolver, Relay will create an id field. These fields + // appear as generarated fields but the Relay runtime ensures they are always set. + if field + .directives + .named(*RELAY_RESOLVER_MODEL_GENERATED_ID_FIELD_DIRECTIVE_NAME) + .is_some() + { + return false; + } + + // Special fields like __id and __typename can be ignored since their existence + // is guaranteed by Relay. They can be identified by not having a parent type. + if field.parent_type.is_none() { + return false; + } + + // The field is a client schema extension that Relay cannot guarantee is set. + true + } +} +impl Validator for EnsureCatch<'_> { + const NAME: &'static str = "validate_client_schema_extensions_use_catch"; + const VALIDATE_ARGUMENTS: bool = false; + const VALIDATE_DIRECTIVES: bool = false; + + fn validate_scalar_field(&mut self, field: &graphql_ir::ScalarField) -> DiagnosticsResult<()> { + let field_definition = self.schema.field(field.definition.item); + if !self.is_non_resolver_extension(field_definition) { + return Ok(()); + } + match field.directives.named(*CATCH_DIRECTIVE_NAME) { + Some(_) => Ok(()), + None => Err(vec![Diagnostic::error( + ValidationMessage::ClientSchemaExtenstionWithoutCatch, + field.alias_or_name_location(), + )]), + } + } + + fn validate_linked_field(&mut self, field: &graphql_ir::LinkedField) -> DiagnosticsResult<()> { + match field.directives.named(*CATCH_DIRECTIVE_NAME) { + Some(_) => Ok(()), + None => { + let field_definition = self.schema.field(field.definition.item); + if self.is_non_resolver_extension(field_definition) { + Err(vec![Diagnostic::error( + ValidationMessage::ClientSchemaExtenstionWithoutCatch, + field.alias_or_name_location(), + )]) + } else { + self.default_validate_linked_field(field) + } + } + } + } + + fn validate_inline_fragment( + &mut self, + fragment: &graphql_ir::InlineFragment, + ) -> DiagnosticsResult<()> { + match fragment.directives.named(*CATCH_DIRECTIVE_NAME) { + Some(_) => Ok(()), + None => self.default_validate_inline_fragment(fragment), + } + } + + fn validate_fragment(&mut self, fragment: &FragmentDefinition) -> DiagnosticsResult<()> { + match fragment.directives.named(*THROW_ON_FIELD_ERROR_DIRECTIVE) { + Some(_) => self.default_validate_fragment(fragment), + None => Ok(()), + } + } + + fn validate_operation( + &mut self, + operation: &graphql_ir::OperationDefinition, + ) -> DiagnosticsResult<()> { + match operation.directives.named(*THROW_ON_FIELD_ERROR_DIRECTIVE) { + Some(_) => self.default_validate_operation(operation), + None => Ok(()), + } + } +} + +#[derive( + Clone, + Debug, + Error, + Eq, + PartialEq, + Ord, + PartialOrd, + Hash, + serde::Serialize +)] +#[serde(tag = "type")] +enum ValidationMessage { + #[error( + "Expected client-defined field within `@throwOnFieldError` to be annotated with `@catch`. Accessing an unset field is treated as a field error, but Relay cannot guarantee that client field will be set before they are read. Add `@catch` to explicitly handle the case where the field is unset." + )] + ClientSchemaExtenstionWithoutCatch, +} diff --git a/compiler/crates/relay-transforms/src/validations/validate_connections.rs b/compiler/crates/relay-transforms/src/validations/validate_connections.rs index 3edc7065024c5..916df8bcca2a7 100644 --- a/compiler/crates/relay-transforms/src/validations/validate_connections.rs +++ b/compiler/crates/relay-transforms/src/validations/validate_connections.rs @@ -20,21 +20,21 @@ use graphql_ir::Selection; use graphql_ir::ValidationMessage; use graphql_ir::Validator; use graphql_ir::Value; -use intern::string_key::StringKey; use intern::Lookup; +use intern::string_key::StringKey; use schema::Field; use schema::Schema; use schema::Type; use schema::TypeReference; -use crate::connections::extract_connection_directive; use crate::connections::ConnectionConstants; use crate::connections::ConnectionInterface; -use crate::handle_fields::extract_handle_field_directive_args_for_connection; +use crate::connections::extract_connection_directive; use crate::handle_fields::CONNECTION_HANDLER_ARG_NAME; use crate::handle_fields::DYNAMIC_KEY_ARG_NAME; use crate::handle_fields::FILTERS_ARG_NAME; use crate::handle_fields::KEY_ARG_NAME; +use crate::handle_fields::extract_handle_field_directive_args_for_connection; pub fn validate_connections( program: &Program, @@ -599,7 +599,7 @@ impl<'s> ConnectionValidation<'s> { } } -impl<'s> Validator for ConnectionValidation<'s> { +impl Validator for ConnectionValidation<'_> { const NAME: &'static str = "ConnectionValidation"; const VALIDATE_ARGUMENTS: bool = false; const VALIDATE_DIRECTIVES: bool = false; diff --git a/compiler/crates/relay-transforms/src/validations/validate_fragment_alias_conflict.rs b/compiler/crates/relay-transforms/src/validations/validate_fragment_alias_conflict.rs index 2d74fa7277795..0874eb237bfb4 100644 --- a/compiler/crates/relay-transforms/src/validations/validate_fragment_alias_conflict.rs +++ b/compiler/crates/relay-transforms/src/validations/validate_fragment_alias_conflict.rs @@ -5,8 +5,8 @@ * LICENSE file in the root directory of this source tree. */ -use std::collections::hash_map::Entry; use std::collections::HashMap; +use std::collections::hash_map::Entry; use std::mem; use std::sync::Arc; @@ -113,7 +113,7 @@ impl<'s> ValidateFragmentAliasConflict<'s> { } } -impl<'a> Validator for ValidateFragmentAliasConflict<'a> { +impl Validator for ValidateFragmentAliasConflict<'_> { const NAME: &'static str = "ValidateFragmentAliasConflict"; const VALIDATE_ARGUMENTS: bool = false; diff --git a/compiler/crates/relay-transforms/src/validations/validate_global_variables.rs b/compiler/crates/relay-transforms/src/validations/validate_global_variables.rs index 977323ec34ac8..75887f6c4e726 100644 --- a/compiler/crates/relay-transforms/src/validations/validate_global_variables.rs +++ b/compiler/crates/relay-transforms/src/validations/validate_global_variables.rs @@ -15,8 +15,8 @@ use graphql_ir::ValidationMessage; use graphql_ir::Validator; use intern::Lookup; -use crate::root_variables::InferVariablesVisitor; use crate::DIRECTIVE_SPLIT_OPERATION; +use crate::root_variables::InferVariablesVisitor; pub fn validate_global_variables(program: &Program) -> DiagnosticsResult<()> { ValidateGlobalVariables::new(program).validate_program(program) diff --git a/compiler/crates/relay-transforms/src/validations/validate_module_names/extract_module_name.rs b/compiler/crates/relay-transforms/src/validations/validate_module_names/extract_module_name.rs index 84e3921f18309..42de6385c0b0d 100644 --- a/compiler/crates/relay-transforms/src/validations/validate_module_names/extract_module_name.rs +++ b/compiler/crates/relay-transforms/src/validations/validate_module_names/extract_module_name.rs @@ -31,7 +31,7 @@ pub fn extract_module_name(path: &str) -> Option { fn get_final_non_index_js_segment(path: &Path) -> Option<&str> { let file_stem = path.file_stem()?.to_str(); - if file_stem.map_or(false, |f| f == "index" || f.starts_with("index.")) { + if file_stem.is_some_and(|f| f == "index" || f.starts_with("index.")) { let mut iter = path.iter(); iter.next_back()?; iter.next_back().and_then(|os_str| os_str.to_str()) diff --git a/compiler/crates/relay-transforms/src/validations/validate_no_double_underscore_alias.rs b/compiler/crates/relay-transforms/src/validations/validate_no_double_underscore_alias.rs index b519190545606..fb9e79b6f7da0 100644 --- a/compiler/crates/relay-transforms/src/validations/validate_no_double_underscore_alias.rs +++ b/compiler/crates/relay-transforms/src/validations/validate_no_double_underscore_alias.rs @@ -14,8 +14,8 @@ use graphql_ir::Program; use graphql_ir::ScalarField; use graphql_ir::ValidationMessage; use graphql_ir::Validator; -use intern::string_key::StringKey; use intern::Lookup; +use intern::string_key::StringKey; pub fn validate_no_double_underscore_alias(program: &Program) -> DiagnosticsResult<()> { let mut validator = ValidateNoDoubleUnderscoreAlias {}; diff --git a/compiler/crates/relay-transforms/src/validations/validate_no_inline_with_raw_response_type.rs b/compiler/crates/relay-transforms/src/validations/validate_no_inline_with_raw_response_type.rs index 2c52b0a18d905..1f49786ed95af 100644 --- a/compiler/crates/relay-transforms/src/validations/validate_no_inline_with_raw_response_type.rs +++ b/compiler/crates/relay-transforms/src/validations/validate_no_inline_with_raw_response_type.rs @@ -20,9 +20,9 @@ use graphql_ir::Program; use graphql_ir::ValidationMessage; use graphql_ir::Validator; -use crate::no_inline::is_raw_response_type_enabled; use crate::no_inline::NO_INLINE_DIRECTIVE_NAME; use crate::no_inline::RAW_RESPONSE_TYPE_NAME; +use crate::no_inline::is_raw_response_type_enabled; /// To generate full raw response types, we need to also generate raw response types for /// @no_inline fragment normalization files. So raw_response_type argument is required @@ -50,7 +50,7 @@ impl<'a> NoInlineRawResponseTypeValidator<'a> { } } -impl<'a> Validator for NoInlineRawResponseTypeValidator<'a> { +impl Validator for NoInlineRawResponseTypeValidator<'_> { const NAME: &'static str = "NoInlineRawResponseTypeValidator"; const VALIDATE_ARGUMENTS: bool = false; const VALIDATE_DIRECTIVES: bool = false; diff --git a/compiler/crates/relay-transforms/src/validations/validate_no_unselectable_selections.rs b/compiler/crates/relay-transforms/src/validations/validate_no_unselectable_selections.rs index 77cacb0e020aa..4bd00307fa150 100644 --- a/compiler/crates/relay-transforms/src/validations/validate_no_unselectable_selections.rs +++ b/compiler/crates/relay-transforms/src/validations/validate_no_unselectable_selections.rs @@ -90,7 +90,7 @@ impl<'a, 'b> UnselectableSelections<'a, 'b> { } } -impl<'a, 'b> Validator for UnselectableSelections<'a, 'b> { +impl Validator for UnselectableSelections<'_, '_> { const NAME: &'static str = "UnselectableSelections"; const VALIDATE_ARGUMENTS: bool = false; const VALIDATE_DIRECTIVES: bool = false; diff --git a/compiler/crates/relay-transforms/src/validations/validate_required_arguments.rs b/compiler/crates/relay-transforms/src/validations/validate_required_arguments.rs index 8d579cad610ca..2aac75ff11334 100644 --- a/compiler/crates/relay-transforms/src/validations/validate_required_arguments.rs +++ b/compiler/crates/relay-transforms/src/validations/validate_required_arguments.rs @@ -13,6 +13,7 @@ use common::WithLocation; use errors::validate; use graphql_ir::Argument; use graphql_ir::Directive; +use graphql_ir::FIXME_FAT_INTERFACE; use graphql_ir::Field; use graphql_ir::FragmentDefinition; use graphql_ir::LinkedField; @@ -21,7 +22,6 @@ use graphql_ir::Program; use graphql_ir::ScalarField; use graphql_ir::ValidationMessage; use graphql_ir::Validator; -use graphql_ir::FIXME_FAT_INTERFACE; use intern::string_key::StringKey; use schema::ArgumentDefinitions; use schema::Schema; diff --git a/compiler/crates/relay-transforms/src/validations/validate_resolver_fragments.rs b/compiler/crates/relay-transforms/src/validations/validate_resolver_fragments.rs index 33616eebb8907..97e82a2e9cddc 100644 --- a/compiler/crates/relay-transforms/src/validations/validate_resolver_fragments.rs +++ b/compiler/crates/relay-transforms/src/validations/validate_resolver_fragments.rs @@ -21,8 +21,8 @@ use graphql_ir::Variable; use schema::SDLSchema; use schema::Schema; -use crate::relay_resolvers::get_argument_value; use crate::ValidationMessage; +use crate::relay_resolvers::get_argument_value; pub fn validate_resolver_fragments(program: &Program) -> DiagnosticsResult<()> { ValidateResolverFragments::new(&program.schema).validate_program(program) diff --git a/compiler/crates/relay-transforms/src/validations/validate_server_only_directives.rs b/compiler/crates/relay-transforms/src/validations/validate_server_only_directives.rs index 7e21398c7192f..8c981f5c0d66d 100644 --- a/compiler/crates/relay-transforms/src/validations/validate_server_only_directives.rs +++ b/compiler/crates/relay-transforms/src/validations/validate_server_only_directives.rs @@ -131,7 +131,7 @@ impl<'s> ServerOnlyDirectivesValidation<'s> { } } -impl<'s> Validator for ServerOnlyDirectivesValidation<'s> { +impl Validator for ServerOnlyDirectivesValidation<'_> { const NAME: &'static str = "ServerOnlyDirectivesValidation"; const VALIDATE_ARGUMENTS: bool = false; const VALIDATE_DIRECTIVES: bool = true; diff --git a/compiler/crates/relay-transforms/src/validations/validate_static_args.rs b/compiler/crates/relay-transforms/src/validations/validate_static_args.rs index 04da0c3f479ec..01b82d51b3f97 100644 --- a/compiler/crates/relay-transforms/src/validations/validate_static_args.rs +++ b/compiler/crates/relay-transforms/src/validations/validate_static_args.rs @@ -53,7 +53,7 @@ impl<'a> StaticArgValidator<'a> { } } -impl<'a> Validator for StaticArgValidator<'a> { +impl Validator for StaticArgValidator<'_> { const NAME: &'static str = "StaticArgValidator"; // Eliding default argument checks as we're overriding specific argument checks for certain types const VALIDATE_ARGUMENTS: bool = false; diff --git a/compiler/crates/relay-transforms/src/validations/validate_unused_fragment_variables.rs b/compiler/crates/relay-transforms/src/validations/validate_unused_fragment_variables.rs index b59a5c12d86cb..9d9b489420ced 100644 --- a/compiler/crates/relay-transforms/src/validations/validate_unused_fragment_variables.rs +++ b/compiler/crates/relay-transforms/src/validations/validate_unused_fragment_variables.rs @@ -13,11 +13,11 @@ use common::NamedItem; use graphql_ir::FragmentDefinition; use graphql_ir::OperationDefinition; use graphql_ir::Program; +use graphql_ir::UNUSED_LOCAL_VARIABLE_DEPRECATED; use graphql_ir::ValidationMessage; use graphql_ir::Validator; use graphql_ir::Variable; use graphql_ir::VariableName; -use graphql_ir::UNUSED_LOCAL_VARIABLE_DEPRECATED; /// Validates that there are no unused fragment variables on fragments. /// diff --git a/compiler/crates/relay-transforms/src/validations/validate_unused_variables.rs b/compiler/crates/relay-transforms/src/validations/validate_unused_variables.rs index 95a7edda47b79..649805984fa2e 100644 --- a/compiler/crates/relay-transforms/src/validations/validate_unused_variables.rs +++ b/compiler/crates/relay-transforms/src/validations/validate_unused_variables.rs @@ -23,12 +23,12 @@ pub fn validate_unused_variables(program: &Program) -> DiagnosticsResult<()> { } pub struct ValidateUnusedVariables<'program> { - visitor: InferVariablesVisitor<'program>, + pub visitor: InferVariablesVisitor<'program>, ignore_directive_name: DirectiveName, } impl<'program> ValidateUnusedVariables<'program> { - fn new(program: &'program Program) -> Self { + pub fn new(program: &'program Program) -> Self { Self { visitor: InferVariablesVisitor::new(program), ignore_directive_name: DirectiveName( diff --git a/compiler/crates/relay-transforms/tests/apply_fragment_arguments/fixtures/empty-selection-constant-include-false-argument.expected b/compiler/crates/relay-transforms/tests/apply_fragment_arguments/fixtures/empty-selection-constant-include-false-argument.expected new file mode 100644 index 0000000000000..6a59c3719fb13 --- /dev/null +++ b/compiler/crates/relay-transforms/tests/apply_fragment_arguments/fixtures/empty-selection-constant-include-false-argument.expected @@ -0,0 +1,20 @@ +==================================== INPUT ==================================== +# expected-to-throw +query EmptyQuery($id: ID!) { + node(id: $id) { + ...Fragment @arguments(cond: false) + } +} + +fragment Fragment on User + @argumentDefinitions(cond: {type: "Boolean!"}) { + lastName @include(if: $cond) +} +==================================== ERROR ==================================== +✖︎ After applying transforms to the query `EmptyQuery` selections of the `EmptyQuery` that would be sent to the server are empty. This is likely due to the use of `@skip`/`@include` directives with constant values that remove all selections in the query. + + empty-selection-constant-include-false-argument.graphql:2:7 + 1 │ # expected-to-throw + 2 │ query EmptyQuery($id: ID!) { + │ ^^^^^^^^^^ + 3 │ node(id: $id) { diff --git a/compiler/crates/relay-transforms/tests/apply_fragment_arguments/fixtures/empty-selection-constant-include-false-argument.graphql b/compiler/crates/relay-transforms/tests/apply_fragment_arguments/fixtures/empty-selection-constant-include-false-argument.graphql new file mode 100644 index 0000000000000..f4fa1bcb931d6 --- /dev/null +++ b/compiler/crates/relay-transforms/tests/apply_fragment_arguments/fixtures/empty-selection-constant-include-false-argument.graphql @@ -0,0 +1,11 @@ +# expected-to-throw +query EmptyQuery($id: ID!) { + node(id: $id) { + ...Fragment @arguments(cond: false) + } +} + +fragment Fragment on User + @argumentDefinitions(cond: {type: "Boolean!"}) { + lastName @include(if: $cond) +} diff --git a/compiler/crates/relay-transforms/tests/apply_fragment_arguments/fixtures/empty-selection-constant-skip-true-argument.expected b/compiler/crates/relay-transforms/tests/apply_fragment_arguments/fixtures/empty-selection-constant-skip-true-argument.expected new file mode 100644 index 0000000000000..ccf1986cb5875 --- /dev/null +++ b/compiler/crates/relay-transforms/tests/apply_fragment_arguments/fixtures/empty-selection-constant-skip-true-argument.expected @@ -0,0 +1,20 @@ +==================================== INPUT ==================================== +# expected-to-throw +query EmptyQuery($id: ID!) { + node(id: $id) { + ...Fragment @arguments(cond: true) + } +} + +fragment Fragment on User + @argumentDefinitions(cond: {type: "Boolean!"}) { + lastName @skip(if: $cond) +} +==================================== ERROR ==================================== +✖︎ After applying transforms to the query `EmptyQuery` selections of the `EmptyQuery` that would be sent to the server are empty. This is likely due to the use of `@skip`/`@include` directives with constant values that remove all selections in the query. + + empty-selection-constant-skip-true-argument.graphql:2:7 + 1 │ # expected-to-throw + 2 │ query EmptyQuery($id: ID!) { + │ ^^^^^^^^^^ + 3 │ node(id: $id) { diff --git a/compiler/crates/relay-transforms/tests/apply_fragment_arguments/fixtures/empty-selection-constant-skip-true-argument.graphql b/compiler/crates/relay-transforms/tests/apply_fragment_arguments/fixtures/empty-selection-constant-skip-true-argument.graphql new file mode 100644 index 0000000000000..a763cf6444a27 --- /dev/null +++ b/compiler/crates/relay-transforms/tests/apply_fragment_arguments/fixtures/empty-selection-constant-skip-true-argument.graphql @@ -0,0 +1,11 @@ +# expected-to-throw +query EmptyQuery($id: ID!) { + node(id: $id) { + ...Fragment @arguments(cond: true) + } +} + +fragment Fragment on User + @argumentDefinitions(cond: {type: "Boolean!"}) { + lastName @skip(if: $cond) +} diff --git a/compiler/crates/relay-transforms/tests/apply_fragment_arguments_test.rs b/compiler/crates/relay-transforms/tests/apply_fragment_arguments_test.rs index 6cd98cd2fec03..cfa5957d0f913 100644 --- a/compiler/crates/relay-transforms/tests/apply_fragment_arguments_test.rs +++ b/compiler/crates/relay-transforms/tests/apply_fragment_arguments_test.rs @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<7a0be220ee0ccec98d9d78d2768944ba>> + * @generated SignedSource<<015f5d0f887cfdca2bff8cd762cc2a55>> */ mod apply_fragment_arguments; @@ -26,6 +26,20 @@ async fn deletes_unreferenced_fragments() { test_fixture(transform_fixture, file!(), "deletes-unreferenced-fragments.graphql", "apply_fragment_arguments/fixtures/deletes-unreferenced-fragments.expected", input, expected).await; } +#[tokio::test] +async fn empty_selection_constant_include_false_argument() { + let input = include_str!("apply_fragment_arguments/fixtures/empty-selection-constant-include-false-argument.graphql"); + let expected = include_str!("apply_fragment_arguments/fixtures/empty-selection-constant-include-false-argument.expected"); + test_fixture(transform_fixture, file!(), "empty-selection-constant-include-false-argument.graphql", "apply_fragment_arguments/fixtures/empty-selection-constant-include-false-argument.expected", input, expected).await; +} + +#[tokio::test] +async fn empty_selection_constant_skip_true_argument() { + let input = include_str!("apply_fragment_arguments/fixtures/empty-selection-constant-skip-true-argument.graphql"); + let expected = include_str!("apply_fragment_arguments/fixtures/empty-selection-constant-skip-true-argument.expected"); + test_fixture(transform_fixture, file!(), "empty-selection-constant-skip-true-argument.graphql", "apply_fragment_arguments/fixtures/empty-selection-constant-skip-true-argument.expected", input, expected).await; +} + #[tokio::test] async fn fragment_include_with_provided_argument() { let input = include_str!("apply_fragment_arguments/fixtures/fragment-include-with-provided-argument.graphql"); diff --git a/compiler/crates/relay-transforms/tests/assignable_directive.rs b/compiler/crates/relay-transforms/tests/assignable_directive.rs index 70a97e65b7639..9ec4973632ceb 100644 --- a/compiler/crates/relay-transforms/tests/assignable_directive.rs +++ b/compiler/crates/relay-transforms/tests/assignable_directive.rs @@ -9,8 +9,8 @@ use std::sync::Arc; use common::SourceLocationKey; use fixture_tests::Fixture; -use graphql_ir::build; use graphql_ir::Program; +use graphql_ir::build; use graphql_syntax::parse_executable; use graphql_test_helpers::diagnostics_to_sorted_string; use relay_test_schema::TEST_SCHEMA; diff --git a/compiler/crates/relay-transforms/tests/assignable_fragment_spread/fixtures/assignable-fragment-spread-with-fixme-directives.expected b/compiler/crates/relay-transforms/tests/assignable_fragment_spread/fixtures/assignable-fragment-spread-with-fixme-directives.expected new file mode 100644 index 0000000000000..e097fcbcbfa55 --- /dev/null +++ b/compiler/crates/relay-transforms/tests/assignable_fragment_spread/fixtures/assignable-fragment-spread-with-fixme-directives.expected @@ -0,0 +1,24 @@ +==================================== INPUT ==================================== +query TestQuery { + node(id: "4") { + ...Assignable_user @dangerously_unaliased_fixme + } +} + +fragment Assignable_user on User @assignable { + __typename +} +==================================== OUTPUT =================================== +query TestQuery { + node(id: "4") { + ... { + ...Assignable_user @dangerously_unaliased_fixme + __typename + __id + } + } +} + +fragment Assignable_user on User @assignable { + __typename +} diff --git a/compiler/crates/relay-transforms/tests/assignable_fragment_spread/fixtures/assignable-fragment-spread-with-fixme-directives.graphql b/compiler/crates/relay-transforms/tests/assignable_fragment_spread/fixtures/assignable-fragment-spread-with-fixme-directives.graphql new file mode 100644 index 0000000000000..543b0b8ca0ae2 --- /dev/null +++ b/compiler/crates/relay-transforms/tests/assignable_fragment_spread/fixtures/assignable-fragment-spread-with-fixme-directives.graphql @@ -0,0 +1,9 @@ +query TestQuery { + node(id: "4") { + ...Assignable_user @dangerously_unaliased_fixme + } +} + +fragment Assignable_user on User @assignable { + __typename +} diff --git a/compiler/crates/relay-transforms/tests/assignable_fragment_spread_test.rs b/compiler/crates/relay-transforms/tests/assignable_fragment_spread_test.rs index bb0b2d9e3640e..39cfb79fad324 100644 --- a/compiler/crates/relay-transforms/tests/assignable_fragment_spread_test.rs +++ b/compiler/crates/relay-transforms/tests/assignable_fragment_spread_test.rs @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<2e19314c2dbf23eef2878bbb537fba0e>> + * @generated SignedSource<<6eb30dc78dfe1878bc1456bffe63bb3c>> */ mod assignable_fragment_spread; @@ -54,6 +54,13 @@ async fn assignable_fragment_spread_with_directives_invalid() { test_fixture(transform_fixture, file!(), "assignable-fragment-spread-with-directives.invalid.graphql", "assignable_fragment_spread/fixtures/assignable-fragment-spread-with-directives.invalid.expected", input, expected).await; } +#[tokio::test] +async fn assignable_fragment_spread_with_fixme_directives() { + let input = include_str!("assignable_fragment_spread/fixtures/assignable-fragment-spread-with-fixme-directives.graphql"); + let expected = include_str!("assignable_fragment_spread/fixtures/assignable-fragment-spread-with-fixme-directives.expected"); + test_fixture(transform_fixture, file!(), "assignable-fragment-spread-with-fixme-directives.graphql", "assignable_fragment_spread/fixtures/assignable-fragment-spread-with-fixme-directives.expected", input, expected).await; +} + #[tokio::test] async fn assignable_fragment_spread_within_inline_fragment() { let input = include_str!("assignable_fragment_spread/fixtures/assignable-fragment-spread-within-inline-fragment.graphql"); diff --git a/compiler/crates/relay-transforms/tests/client_edges.rs b/compiler/crates/relay-transforms/tests/client_edges.rs index 29bdc3af55c52..7d3c34b06d68a 100644 --- a/compiler/crates/relay-transforms/tests/client_edges.rs +++ b/compiler/crates/relay-transforms/tests/client_edges.rs @@ -11,13 +11,13 @@ use common::FeatureFlag; use common::FeatureFlags; use common::SourceLocationKey; use fixture_tests::Fixture; -use graphql_ir::build; use graphql_ir::Program; +use graphql_ir::build; use graphql_syntax::parse_executable; use graphql_test_helpers::diagnostics_to_sorted_string; +use graphql_text_printer::PrinterOptions; use graphql_text_printer::print_fragment; use graphql_text_printer::print_operation; -use graphql_text_printer::PrinterOptions; use relay_config::ProjectConfig; use relay_config::ProjectName; use relay_test_schema::get_test_schema_with_extensions; diff --git a/compiler/crates/relay-transforms/tests/client_edges/fixtures/client-edge-inline-fragment-no-type-condition.expected b/compiler/crates/relay-transforms/tests/client_edges/fixtures/client-edge-inline-fragment-no-type-condition.expected index 08a7779c285da..1995b25884d40 100644 --- a/compiler/crates/relay-transforms/tests/client_edges/fixtures/client-edge-inline-fragment-no-type-condition.expected +++ b/compiler/crates/relay-transforms/tests/client_edges/fixtures/client-edge-inline-fragment-no-type-condition.expected @@ -48,6 +48,7 @@ fragment Foo_user on User { # output_type_info: EdgeTo, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } @waterfall best_friend @waterfall { @@ -76,6 +77,7 @@ fragment Foo_user on User { # output_type_info: EdgeTo, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } @waterfall best_friend @waterfall { diff --git a/compiler/crates/relay-transforms/tests/client_edges/fixtures/client-edge-inline-fragment.expected b/compiler/crates/relay-transforms/tests/client_edges/fixtures/client-edge-inline-fragment.expected index 18240bc4574d2..4e51ee3b61528 100644 --- a/compiler/crates/relay-transforms/tests/client_edges/fixtures/client-edge-inline-fragment.expected +++ b/compiler/crates/relay-transforms/tests/client_edges/fixtures/client-edge-inline-fragment.expected @@ -53,6 +53,7 @@ fragment Foo_node on Node { # output_type_info: EdgeTo, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } @waterfall best_friend @waterfall { @@ -83,6 +84,7 @@ fragment Foo_node on Node { # output_type_info: EdgeTo, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } @waterfall best_friend @waterfall { diff --git a/compiler/crates/relay-transforms/tests/client_edges/fixtures/client-edge-to-client-interface.expected b/compiler/crates/relay-transforms/tests/client_edges/fixtures/client-edge-to-client-interface.expected index 17d4ebdf07413..bc7cc88c835a5 100644 --- a/compiler/crates/relay-transforms/tests/client_edges/fixtures/client-edge-to-client-interface.expected +++ b/compiler/crates/relay-transforms/tests/client_edges/fixtures/client-edge-to-client-interface.expected @@ -38,13 +38,34 @@ fragment Foo_user on User { # unique_id: 0, # model_resolvers: [ # ClientEdgeModelResolver { + # model_field_id: FieldID(528), # type_name: WithLocation { # location: :144:154, # item: ObjectName( # "BestFriend", # ), # }, - # is_live: false, + # resolver_info: ResolverInfo { + # fragment_name: Some( + # FragmentDefinitionName( + # "BestFriend__id", + # ), + # ), + # fragment_data_injection_mode: Some( + # Field { + # name: "id", + # is_required: true, + # }, + # ), + # import_path: "BestFriendResolver", + # import_name: Some( + # "BestFriend", + # ), + # live: false, + # has_output_type: false, + # type_confirmed: false, + # resolver_type: ResolverModule, + # }, # }, # ], # } @@ -61,6 +82,7 @@ fragment Foo_user on User { # output_type_info: EdgeTo, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } best_friend { diff --git a/compiler/crates/relay-transforms/tests/client_edges/fixtures/client-edge-to-client-object.expected b/compiler/crates/relay-transforms/tests/client_edges/fixtures/client-edge-to-client-object.expected index 4f434a5b68d90..65cb672e5767b 100644 --- a/compiler/crates/relay-transforms/tests/client_edges/fixtures/client-edge-to-client-object.expected +++ b/compiler/crates/relay-transforms/tests/client_edges/fixtures/client-edge-to-client-object.expected @@ -47,6 +47,7 @@ fragment Foo_user on User { # output_type_info: EdgeTo, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } best_friend { diff --git a/compiler/crates/relay-transforms/tests/client_edges/fixtures/client-edge-to-client-union.expected b/compiler/crates/relay-transforms/tests/client_edges/fixtures/client-edge-to-client-union.expected index d9be430f4e233..4a7268997de56 100644 --- a/compiler/crates/relay-transforms/tests/client_edges/fixtures/client-edge-to-client-union.expected +++ b/compiler/crates/relay-transforms/tests/client_edges/fixtures/client-edge-to-client-union.expected @@ -32,22 +32,60 @@ fragment FeedbackFragmentType on User { # unique_id: 0, # model_resolvers: [ # ClientEdgeModelResolver { + # model_field_id: FieldID(527), # type_name: WithLocation { - # location: :199:204, + # location: :7:11, # item: ObjectName( - # "Heart", + # "Like", + # ), + # }, + # resolver_info: ResolverInfo { + # fragment_name: Some( + # FragmentDefinitionName( + # "Like__id", + # ), + # ), + # fragment_data_injection_mode: Some( + # Field { + # name: "id", + # is_required: true, + # }, # ), + # import_path: "LikeResolver", + # import_name: None, + # live: false, + # has_output_type: false, + # type_confirmed: false, + # resolver_type: ResolverModule, # }, - # is_live: false, # }, # ClientEdgeModelResolver { + # model_field_id: FieldID(529), # type_name: WithLocation { - # location: :7:11, + # location: :199:204, # item: ObjectName( - # "Like", + # "Heart", + # ), + # }, + # resolver_info: ResolverInfo { + # fragment_name: Some( + # FragmentDefinitionName( + # "Heart__id", + # ), + # ), + # fragment_data_injection_mode: Some( + # Field { + # name: "id", + # is_required: true, + # }, # ), + # import_path: "HeartResolver", + # import_name: None, + # live: false, + # has_output_type: false, + # type_confirmed: false, + # resolver_type: ResolverModule, # }, - # is_live: false, # }, # ], # } @@ -64,6 +102,7 @@ fragment FeedbackFragmentType on User { # output_type_info: EdgeTo, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } feedback_as_union { diff --git a/compiler/crates/relay-transforms/tests/client_edges/fixtures/client-edge-variables.expected b/compiler/crates/relay-transforms/tests/client_edges/fixtures/client-edge-variables.expected index a3a16f6f254ec..5cfd307b1c8c0 100644 --- a/compiler/crates/relay-transforms/tests/client_edges/fixtures/client-edge-variables.expected +++ b/compiler/crates/relay-transforms/tests/client_edges/fixtures/client-edge-variables.expected @@ -40,6 +40,7 @@ fragment Foo_user on User { # output_type_info: EdgeTo, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } @waterfall best_friend @waterfall { diff --git a/compiler/crates/relay-transforms/tests/client_edges/fixtures/client-edge-with-required.expected b/compiler/crates/relay-transforms/tests/client_edges/fixtures/client-edge-with-required.expected index 596fe13d6b5b0..5958457727771 100644 --- a/compiler/crates/relay-transforms/tests/client_edges/fixtures/client-edge-with-required.expected +++ b/compiler/crates/relay-transforms/tests/client_edges/fixtures/client-edge-with-required.expected @@ -44,6 +44,7 @@ fragment Foo_user on User { # output_type_info: EdgeTo, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } @waterfall @required(action: NONE) best_friend @waterfall @required(action: NONE) { diff --git a/compiler/crates/relay-transforms/tests/client_edges/fixtures/client-edge-within-non-client-edge.expected b/compiler/crates/relay-transforms/tests/client_edges/fixtures/client-edge-within-non-client-edge.expected index 5cfc76b0db944..50682f26bb2d6 100644 --- a/compiler/crates/relay-transforms/tests/client_edges/fixtures/client-edge-within-non-client-edge.expected +++ b/compiler/crates/relay-transforms/tests/client_edges/fixtures/client-edge-within-non-client-edge.expected @@ -43,6 +43,7 @@ fragment Foo_user on User { # output_type_info: EdgeTo, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } @waterfall best_friend @waterfall { diff --git a/compiler/crates/relay-transforms/tests/client_edges/fixtures/client-edge.expected b/compiler/crates/relay-transforms/tests/client_edges/fixtures/client-edge.expected index 515a2f6e0db2d..303686c8cdcbb 100644 --- a/compiler/crates/relay-transforms/tests/client_edges/fixtures/client-edge.expected +++ b/compiler/crates/relay-transforms/tests/client_edges/fixtures/client-edge.expected @@ -40,6 +40,7 @@ fragment Foo_user on User { # output_type_info: EdgeTo, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } @waterfall best_friend @waterfall { diff --git a/compiler/crates/relay-transforms/tests/client_edges/fixtures/nested-client-edges-with-variables.expected b/compiler/crates/relay-transforms/tests/client_edges/fixtures/nested-client-edges-with-variables.expected index 94385c5dfe646..bd60b094534a4 100644 --- a/compiler/crates/relay-transforms/tests/client_edges/fixtures/nested-client-edges-with-variables.expected +++ b/compiler/crates/relay-transforms/tests/client_edges/fixtures/nested-client-edges-with-variables.expected @@ -44,6 +44,7 @@ fragment Foo_user on User { # output_type_info: EdgeTo, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } @waterfall best_friend @waterfall { @@ -68,6 +69,7 @@ fragment Foo_user on User { # output_type_info: EdgeTo, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } @waterfall best_friend @waterfall { @@ -128,6 +130,7 @@ fragment RefetchableClientEdgeQuery_Foo_user_best_friend on User @__ClientEdgeGe # output_type_info: EdgeTo, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } @waterfall best_friend @waterfall { diff --git a/compiler/crates/relay-transforms/tests/client_edges/fixtures/nested-client-edges.expected b/compiler/crates/relay-transforms/tests/client_edges/fixtures/nested-client-edges.expected index d0292135b81ba..a5f453518e6c3 100644 --- a/compiler/crates/relay-transforms/tests/client_edges/fixtures/nested-client-edges.expected +++ b/compiler/crates/relay-transforms/tests/client_edges/fixtures/nested-client-edges.expected @@ -42,6 +42,7 @@ fragment Foo_user on User { # output_type_info: EdgeTo, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } @waterfall best_friend @waterfall { @@ -65,6 +66,7 @@ fragment Foo_user on User { # output_type_info: EdgeTo, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } @waterfall best_friend @waterfall { @@ -123,6 +125,7 @@ fragment RefetchableClientEdgeQuery_Foo_user_best_friend on User @__ClientEdgeGe # output_type_info: EdgeTo, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } @waterfall best_friend @waterfall { diff --git a/compiler/crates/relay-transforms/tests/client_edges/fixtures/nested-path-with-alias.expected b/compiler/crates/relay-transforms/tests/client_edges/fixtures/nested-path-with-alias.expected index 6f9a80a231c22..0e4c03b0939c7 100644 --- a/compiler/crates/relay-transforms/tests/client_edges/fixtures/nested-path-with-alias.expected +++ b/compiler/crates/relay-transforms/tests/client_edges/fixtures/nested-path-with-alias.expected @@ -55,6 +55,7 @@ fragment Foo_user on ClientUser { # output_type_info: EdgeTo, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } bff: best_friend { @@ -83,6 +84,7 @@ fragment Foo_user on ClientUser { # output_type_info: EdgeTo, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } bffs_bff: best_friend { diff --git a/compiler/crates/relay-transforms/tests/client_edges/fixtures/nested-path.expected b/compiler/crates/relay-transforms/tests/client_edges/fixtures/nested-path.expected index e0016e0fd3f1c..d9e206772dd9a 100644 --- a/compiler/crates/relay-transforms/tests/client_edges/fixtures/nested-path.expected +++ b/compiler/crates/relay-transforms/tests/client_edges/fixtures/nested-path.expected @@ -53,6 +53,7 @@ fragment Foo_user on ClientUser { # output_type_info: EdgeTo, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } best_friend { @@ -79,6 +80,7 @@ fragment Foo_user on ClientUser { # output_type_info: EdgeTo, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } best_friend { diff --git a/compiler/crates/relay-transforms/tests/client_edges/fixtures/output-type.expected b/compiler/crates/relay-transforms/tests/client_edges/fixtures/output-type.expected index 4e0a473eadf11..97e8d0e996490 100644 --- a/compiler/crates/relay-transforms/tests/client_edges/fixtures/output-type.expected +++ b/compiler/crates/relay-transforms/tests/client_edges/fixtures/output-type.expected @@ -59,6 +59,7 @@ fragment Foo_user on User { # ), # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } best_friend { diff --git a/compiler/crates/relay-transforms/tests/client_extensions.rs b/compiler/crates/relay-transforms/tests/client_extensions.rs index f8bbdd37614a6..841ec33988d50 100644 --- a/compiler/crates/relay-transforms/tests/client_extensions.rs +++ b/compiler/crates/relay-transforms/tests/client_extensions.rs @@ -9,12 +9,12 @@ use std::sync::Arc; use common::SourceLocationKey; use fixture_tests::Fixture; -use graphql_ir::build; use graphql_ir::Program; +use graphql_ir::build; use graphql_syntax::parse_executable; +use graphql_text_printer::PrinterOptions; use graphql_text_printer::print_fragment; use graphql_text_printer::print_operation; -use graphql_text_printer::PrinterOptions; use relay_test_schema::get_test_schema_with_extensions; use relay_transforms::client_extensions; diff --git a/compiler/crates/relay-transforms/tests/client_extensions_abstract_types.rs b/compiler/crates/relay-transforms/tests/client_extensions_abstract_types.rs index ce1428c75bd52..f2264f7754294 100644 --- a/compiler/crates/relay-transforms/tests/client_extensions_abstract_types.rs +++ b/compiler/crates/relay-transforms/tests/client_extensions_abstract_types.rs @@ -9,12 +9,12 @@ use std::sync::Arc; use common::SourceLocationKey; use fixture_tests::Fixture; -use graphql_ir::build; use graphql_ir::Program; +use graphql_ir::build; use graphql_syntax::parse_executable; +use graphql_text_printer::PrinterOptions; use graphql_text_printer::print_fragment; use graphql_text_printer::print_operation; -use graphql_text_printer::PrinterOptions; use relay_test_schema::get_test_schema_with_extensions; use relay_transforms::client_extensions_abstract_types; diff --git a/compiler/crates/relay-transforms/tests/declarative_connection.rs b/compiler/crates/relay-transforms/tests/declarative_connection.rs index 3e4388cbe21df..b105445861e7a 100644 --- a/compiler/crates/relay-transforms/tests/declarative_connection.rs +++ b/compiler/crates/relay-transforms/tests/declarative_connection.rs @@ -8,8 +8,8 @@ use common::FeatureFlags; use fixture_tests::Fixture; use graphql_test_helpers::apply_transform_for_test; -use relay_transforms::transform_declarative_connection; use relay_transforms::ConnectionInterface; +use relay_transforms::transform_declarative_connection; pub async fn transform_fixture(fixture: &Fixture<'_>) -> Result { apply_transform_for_test(fixture, |program| { diff --git a/compiler/crates/relay-transforms/tests/disallow_non_node_id_fields.rs b/compiler/crates/relay-transforms/tests/disallow_non_node_id_fields.rs index a506676d3d866..f4cabc3baee89 100644 --- a/compiler/crates/relay-transforms/tests/disallow_non_node_id_fields.rs +++ b/compiler/crates/relay-transforms/tests/disallow_non_node_id_fields.rs @@ -10,8 +10,8 @@ use std::sync::Arc; use common::SourceLocationKey; use fixture_tests::Fixture; -use graphql_ir::build; use graphql_ir::Program; +use graphql_ir::build; use graphql_syntax::parse_executable; use graphql_test_helpers::diagnostics_to_sorted_string; use intern::intern; diff --git a/compiler/crates/relay-transforms/tests/disallow_readtime_features_in_mutations.rs b/compiler/crates/relay-transforms/tests/disallow_readtime_features_in_mutations.rs index c53344c0b8bc5..9110cc9105987 100644 --- a/compiler/crates/relay-transforms/tests/disallow_readtime_features_in_mutations.rs +++ b/compiler/crates/relay-transforms/tests/disallow_readtime_features_in_mutations.rs @@ -10,8 +10,8 @@ use std::sync::Arc; use common::FeatureFlag; use common::SourceLocationKey; use fixture_tests::Fixture; -use graphql_ir::build; use graphql_ir::Program; +use graphql_ir::build; use graphql_syntax::parse_executable; use graphql_test_helpers::diagnostics_to_sorted_string; use relay_test_schema::get_test_schema_with_extensions; diff --git a/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field.rs b/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field.rs index 253140dccda5b..5664bcfb95cb2 100644 --- a/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field.rs +++ b/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field.rs @@ -9,12 +9,13 @@ use std::sync::Arc; use common::SourceLocationKey; use fixture_tests::Fixture; -use graphql_ir::build; use graphql_ir::Program; +use graphql_ir::build; use graphql_syntax::parse_executable; use graphql_test_helpers::diagnostics_to_sorted_string; use relay_test_schema::get_test_schema_with_extensions; use relay_transforms::disallow_required_on_non_null_field; +use relay_transforms::required_directive; pub async fn transform_fixture(fixture: &Fixture<'_>) -> Result { let parts: Vec<_> = fixture.content.split("%extensions%").collect(); @@ -28,14 +29,14 @@ pub async fn transform_fixture(fixture: &Fixture<'_>) -> Result let ir = build(&schema, &ast.definitions) .map_err(|diagnostics| diagnostics_to_sorted_string(fixture.content, &diagnostics))?; let program = Program::from_definitions(Arc::clone(&schema), ir); - let results = disallow_required_on_non_null_field(&Arc::clone(&schema), &program) - .map_err(|diagnostics| diagnostics_to_sorted_string(fixture.content, &diagnostics))?; + let program = required_directive(&program).unwrap(); + let results = disallow_required_on_non_null_field(&program) + .map_err(|diagnostics| diagnostics_to_sorted_string(fixture.content, &diagnostics)); - Ok(format!( - "OK; warnings: {}", - diagnostics_to_sorted_string(fixture.content, &results) - ) - .to_owned()) + match results { + Ok(_) => Ok("OK".to_owned()), + Err(diagnostics) => Ok(format!("OK; warnings: {}", diagnostics).to_owned()), + } } else { panic!("Expected exactly one %extensions% section marker.") } diff --git a/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field/fixtures/fragment_with_inline_fragment_required_non_null_fields.invalid.expected b/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field/fixtures/fragment_with_inline_fragment_required_non_null_fields.invalid.expected new file mode 100644 index 0000000000000..257e41fc76575 --- /dev/null +++ b/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field/fixtures/fragment_with_inline_fragment_required_non_null_fields.invalid.expected @@ -0,0 +1,19 @@ +==================================== INPUT ==================================== +fragment MyFragment on User @throwOnFieldError { + ... on User { + some_field @required(action: THROW) + } +} + +# %extensions% +extend type User { + some_field: Int! +} +==================================== OUTPUT =================================== +OK; warnings: ℹ Unexpected `@required` directive on a non-null field. This field is already non-null and does not need the `@required` directive. + + fragment_with_inline_fragment_required_non_null_fields.invalid.graphql:3:16 + 2 │ ... on User { + 3 │ some_field @required(action: THROW) + │ ^^^^^^^^^^^^^^^^^^^^^^^^ + 4 │ } diff --git a/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field/fixtures/fragment_with_inline_fragment_required_non_null_fields.invalid.graphql b/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field/fixtures/fragment_with_inline_fragment_required_non_null_fields.invalid.graphql new file mode 100644 index 0000000000000..88356f21fdd99 --- /dev/null +++ b/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field/fixtures/fragment_with_inline_fragment_required_non_null_fields.invalid.graphql @@ -0,0 +1,10 @@ +fragment MyFragment on User @throwOnFieldError { + ... on User { + some_field @required(action: THROW) + } +} + +# %extensions% +extend type User { + some_field: Int! +} diff --git a/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field/fixtures/fragment_with_multiple_inline_fragment_required_fields.expected b/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field/fixtures/fragment_with_multiple_inline_fragment_required_fields.expected new file mode 100644 index 0000000000000..d3323bf5bd5d6 --- /dev/null +++ b/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field/fixtures/fragment_with_multiple_inline_fragment_required_fields.expected @@ -0,0 +1,24 @@ +==================================== INPUT ==================================== +fragment MyFragment on Animal @throwOnFieldError { + ... on Cat { + noise @required(action: THROW) # can't be removed due to Dog's noise also being present + } + ... on Dog { + noise @required(action: THROW) # can't be removed + } +} + +# %extensions% + +interface Animal { + name: String! +} + +type Cat implements Animal { + noise: String! +} +type Dog implements Animal { + noise: String # nullable +} +==================================== OUTPUT =================================== +OK diff --git a/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field/fixtures/fragment_with_multiple_inline_fragment_required_fields.graphql b/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field/fixtures/fragment_with_multiple_inline_fragment_required_fields.graphql new file mode 100644 index 0000000000000..f23685f6da10c --- /dev/null +++ b/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field/fixtures/fragment_with_multiple_inline_fragment_required_fields.graphql @@ -0,0 +1,21 @@ +fragment MyFragment on Animal @throwOnFieldError { + ... on Cat { + noise @required(action: THROW) # can't be removed due to Dog's noise also being present + } + ... on Dog { + noise @required(action: THROW) # can't be removed + } +} + +# %extensions% + +interface Animal { + name: String! +} + +type Cat implements Animal { + noise: String! +} +type Dog implements Animal { + noise: String # nullable +} diff --git a/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field/fixtures/fragment_with_required_semantic_field_no_explicit_errors.expected b/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field/fixtures/fragment_with_required_semantic_field_no_explicit_errors.expected index 863c8ed45bc68..5938d97f31d44 100644 --- a/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field/fixtures/fragment_with_required_semantic_field_no_explicit_errors.expected +++ b/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field/fixtures/fragment_with_required_semantic_field_no_explicit_errors.expected @@ -8,4 +8,4 @@ extend type User { some_field: Int @semanticNonNull } ==================================== OUTPUT =================================== -OK; warnings: +OK diff --git a/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field/fixtures/query_with_required_field.expected b/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field/fixtures/query_with_required_field.expected index 4d54f0cb17573..ee0202ffd8710 100644 --- a/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field/fixtures/query_with_required_field.expected +++ b/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field/fixtures/query_with_required_field.expected @@ -8,4 +8,4 @@ extend type Query { some_field: Int } ==================================== OUTPUT =================================== -OK; warnings: +OK diff --git a/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field/fixtures/query_with_required_field_no_explicit_errors.expected b/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field/fixtures/query_with_required_field_no_explicit_errors.expected index ec111e95ff3ea..1f64e6b35c5eb 100644 --- a/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field/fixtures/query_with_required_field_no_explicit_errors.expected +++ b/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field/fixtures/query_with_required_field_no_explicit_errors.expected @@ -8,4 +8,4 @@ extend type Query { some_field: Int } ==================================== OUTPUT =================================== -OK; warnings: +OK diff --git a/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field/fixtures/query_with_required_semantic_field_no_explicit_errors.expected b/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field/fixtures/query_with_required_semantic_field_no_explicit_errors.expected index 88950494640e4..57e09179d547d 100644 --- a/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field/fixtures/query_with_required_semantic_field_no_explicit_errors.expected +++ b/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field/fixtures/query_with_required_semantic_field_no_explicit_errors.expected @@ -8,4 +8,4 @@ extend type Query { some_field: Int @semanticNonNull } ==================================== OUTPUT =================================== -OK; warnings: +OK diff --git a/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field/fixtures/query_with_semantic_field.expected b/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field/fixtures/query_with_semantic_field.expected index 57c0067cd24d7..0e9b12a81d7e1 100644 --- a/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field/fixtures/query_with_semantic_field.expected +++ b/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field/fixtures/query_with_semantic_field.expected @@ -8,4 +8,4 @@ extend type Query { some_field: Int @semanticNonNull } ==================================== OUTPUT =================================== -OK; warnings: +OK diff --git a/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field/fixtures/query_with_semantic_field_no_explicit_errors.expected b/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field/fixtures/query_with_semantic_field_no_explicit_errors.expected index 817215a17eaa2..d7e2654ae14d2 100644 --- a/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field/fixtures/query_with_semantic_field_no_explicit_errors.expected +++ b/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field/fixtures/query_with_semantic_field_no_explicit_errors.expected @@ -8,4 +8,4 @@ extend type Query { some_field: Int @semanticNonNull } ==================================== OUTPUT =================================== -OK; warnings: +OK diff --git a/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field/fixtures/required_bubbles_to_required_semantic_field.expected b/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field/fixtures/required_bubbles_to_required_semantic_field.expected new file mode 100644 index 0000000000000..bfd2baccfde00 --- /dev/null +++ b/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field/fixtures/required_bubbles_to_required_semantic_field.expected @@ -0,0 +1,17 @@ +==================================== INPUT ==================================== +query MyQuery @throwOnFieldError { + my_field @required(action: LOG) { + some_field @required(action: LOG) + } +} + +# %extensions% +extend type Query { + my_field: MyType @semanticNonNull +} + +type MyType { + some_field: Int +} +==================================== OUTPUT =================================== +OK diff --git a/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field/fixtures/required_bubbles_to_required_semantic_field.graphql b/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field/fixtures/required_bubbles_to_required_semantic_field.graphql new file mode 100644 index 0000000000000..458a60db0a44e --- /dev/null +++ b/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field/fixtures/required_bubbles_to_required_semantic_field.graphql @@ -0,0 +1,14 @@ +query MyQuery @throwOnFieldError { + my_field @required(action: LOG) { + some_field @required(action: LOG) + } +} + +# %extensions% +extend type Query { + my_field: MyType @semanticNonNull +} + +type MyType { + some_field: Int +} diff --git a/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field_test.rs b/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field_test.rs index eecfd83851b00..2fa7810ce5c21 100644 --- a/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field_test.rs +++ b/compiler/crates/relay-transforms/tests/disallow_required_on_non_null_field_test.rs @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<1aeb23bd73757d57be6f0348658290e5>> + * @generated SignedSource<> */ mod disallow_required_on_non_null_field; @@ -12,6 +12,20 @@ mod disallow_required_on_non_null_field; use disallow_required_on_non_null_field::transform_fixture; use fixture_tests::test_fixture; +#[tokio::test] +async fn fragment_with_inline_fragment_required_non_null_fields_invalid() { + let input = include_str!("disallow_required_on_non_null_field/fixtures/fragment_with_inline_fragment_required_non_null_fields.invalid.graphql"); + let expected = include_str!("disallow_required_on_non_null_field/fixtures/fragment_with_inline_fragment_required_non_null_fields.invalid.expected"); + test_fixture(transform_fixture, file!(), "fragment_with_inline_fragment_required_non_null_fields.invalid.graphql", "disallow_required_on_non_null_field/fixtures/fragment_with_inline_fragment_required_non_null_fields.invalid.expected", input, expected).await; +} + +#[tokio::test] +async fn fragment_with_multiple_inline_fragment_required_fields() { + let input = include_str!("disallow_required_on_non_null_field/fixtures/fragment_with_multiple_inline_fragment_required_fields.graphql"); + let expected = include_str!("disallow_required_on_non_null_field/fixtures/fragment_with_multiple_inline_fragment_required_fields.expected"); + test_fixture(transform_fixture, file!(), "fragment_with_multiple_inline_fragment_required_fields.graphql", "disallow_required_on_non_null_field/fixtures/fragment_with_multiple_inline_fragment_required_fields.expected", input, expected).await; +} + #[tokio::test] async fn fragment_with_multiple_required_non_null_fields_invalid() { let input = include_str!("disallow_required_on_non_null_field/fixtures/fragment_with_multiple_required_non_null_fields.invalid.graphql"); @@ -109,3 +123,10 @@ async fn query_with_semantic_field_no_explicit_errors() { let expected = include_str!("disallow_required_on_non_null_field/fixtures/query_with_semantic_field_no_explicit_errors.expected"); test_fixture(transform_fixture, file!(), "query_with_semantic_field_no_explicit_errors.graphql", "disallow_required_on_non_null_field/fixtures/query_with_semantic_field_no_explicit_errors.expected", input, expected).await; } + +#[tokio::test] +async fn required_bubbles_to_required_semantic_field() { + let input = include_str!("disallow_required_on_non_null_field/fixtures/required_bubbles_to_required_semantic_field.graphql"); + let expected = include_str!("disallow_required_on_non_null_field/fixtures/required_bubbles_to_required_semantic_field.expected"); + test_fixture(transform_fixture, file!(), "required_bubbles_to_required_semantic_field.graphql", "disallow_required_on_non_null_field/fixtures/required_bubbles_to_required_semantic_field.expected", input, expected).await; +} diff --git a/compiler/crates/relay-transforms/tests/disallow_reserved_aliases.rs b/compiler/crates/relay-transforms/tests/disallow_reserved_aliases.rs index e4d5e2c1873e5..de546af375781 100644 --- a/compiler/crates/relay-transforms/tests/disallow_reserved_aliases.rs +++ b/compiler/crates/relay-transforms/tests/disallow_reserved_aliases.rs @@ -7,8 +7,8 @@ use common::SourceLocationKey; use fixture_tests::Fixture; -use graphql_ir::build; use graphql_ir::Program; +use graphql_ir::build; use graphql_syntax::parse_executable; use graphql_test_helpers::diagnostics_to_sorted_string; use relay_test_schema::get_test_schema; diff --git a/compiler/crates/relay-transforms/tests/disallow_typename_on_root.rs b/compiler/crates/relay-transforms/tests/disallow_typename_on_root.rs index 4a36780ed53b8..41ecf7fbe1977 100644 --- a/compiler/crates/relay-transforms/tests/disallow_typename_on_root.rs +++ b/compiler/crates/relay-transforms/tests/disallow_typename_on_root.rs @@ -7,8 +7,8 @@ use common::SourceLocationKey; use fixture_tests::Fixture; -use graphql_ir::build; use graphql_ir::Program; +use graphql_ir::build; use graphql_syntax::parse_executable; use graphql_test_helpers::diagnostics_to_sorted_string; use relay_test_schema::get_test_schema; diff --git a/compiler/crates/relay-transforms/tests/flatten.rs b/compiler/crates/relay-transforms/tests/flatten.rs index 822bdd8abced1..4edb61792f64f 100644 --- a/compiler/crates/relay-transforms/tests/flatten.rs +++ b/compiler/crates/relay-transforms/tests/flatten.rs @@ -9,13 +9,13 @@ use std::sync::Arc; use common::SourceLocationKey; use fixture_tests::Fixture; -use graphql_ir::build; use graphql_ir::Program; +use graphql_ir::build; use graphql_syntax::parse_executable; use graphql_test_helpers::diagnostics_to_sorted_string; +use graphql_text_printer::PrinterOptions; use graphql_text_printer::print_fragment; use graphql_text_printer::print_operation; -use graphql_text_printer::PrinterOptions; use relay_test_schema::get_test_schema_with_extensions; use relay_transforms::flatten; diff --git a/compiler/crates/relay-transforms/tests/generate_data_driven_dependency_metadata.rs b/compiler/crates/relay-transforms/tests/generate_data_driven_dependency_metadata.rs index 3e1fe541d487e..f12fd56e2c1a1 100644 --- a/compiler/crates/relay-transforms/tests/generate_data_driven_dependency_metadata.rs +++ b/compiler/crates/relay-transforms/tests/generate_data_driven_dependency_metadata.rs @@ -8,15 +8,16 @@ use common::FeatureFlags; use fixture_tests::Fixture; use graphql_test_helpers::apply_transform_for_test; -use relay_config::DynamicModuleProvider; use relay_config::ModuleImportConfig; +use relay_config::ModuleProvider; use relay_config::Surface; use relay_transforms::generate_data_driven_dependency_metadata; use relay_transforms::transform_match; pub async fn transform_fixture(fixture: &Fixture<'_>) -> Result { let module_import_config = ModuleImportConfig { - dynamic_module_provider: Some(DynamicModuleProvider::JSResource), + dynamic_module_provider: Some(ModuleProvider::JSResource), + operation_module_provider: None, surface: Some(Surface::Resolvers), }; apply_transform_for_test(fixture, |program| { diff --git a/compiler/crates/relay-transforms/tests/generate_data_driven_dependency_metadata/fixtures/client-resolver-3D-module-on-interface.expected b/compiler/crates/relay-transforms/tests/generate_data_driven_dependency_metadata/fixtures/client-resolver-3D-module-on-interface.expected index 083f5b9da9c14..52b543cd3acaa 100644 --- a/compiler/crates/relay-transforms/tests/generate_data_driven_dependency_metadata/fixtures/client-resolver-3D-module-on-interface.expected +++ b/compiler/crates/relay-transforms/tests/generate_data_driven_dependency_metadata/fixtures/client-resolver-3D-module-on-interface.expected @@ -53,7 +53,19 @@ fragment ClientUser_Fragment on ClientUser { } } -fragment NameRendererFragment on Persona { +fragment NameRendererFragment on Persona @__RelayDataDrivenDependencyMetadata +# RelayDataDrivenDependencyMetadata { +# direct_dependencies: Some( +# [ +# ( +# "NameRendererFragment.basicUser", +# "{\"branches\":{\"ClientUser\":{\"component\":\"ClientUser.react\",\"fragment\":\"ClientUser_Fragment$normalization.graphql\"},\"SpecialUser\":{\"component\":\"SpecialUser.react\",\"fragment\":\"SpecialUser_Fragment$normalization.graphql\"}},\"plural\":false}", +# ), +# ], +# ), +# indirect_dependencies: None, +# } + { id basicUser { ... on ClientUser { diff --git a/compiler/crates/relay-transforms/tests/generate_live_query_metadata/fixtures/live_by_at_live_config_id.expected b/compiler/crates/relay-transforms/tests/generate_live_query_metadata/fixtures/live_by_at_live_config_id.expected deleted file mode 100644 index 7bed16f0a387d..0000000000000 --- a/compiler/crates/relay-transforms/tests/generate_live_query_metadata/fixtures/live_by_at_live_config_id.expected +++ /dev/null @@ -1,28 +0,0 @@ -==================================== INPUT ==================================== -query NodeQuery($id: ID!) { - node(id: $id) @live(config_id: "instrumentation_config_id") { - id - ... on User { - birthdate { - day - month - year - } - } - } -} -==================================== OUTPUT =================================== -query NodeQuery( - $id: ID! -) @__metadata(live: {config_id: "instrumentation_config_id"}) { - node(id: $id) @live(config_id: "instrumentation_config_id") { - id - ... on User { - birthdate { - day - month - year - } - } - } -} diff --git a/compiler/crates/relay-transforms/tests/generate_live_query_metadata/fixtures/live_by_at_live_config_id.graphql b/compiler/crates/relay-transforms/tests/generate_live_query_metadata/fixtures/live_by_at_live_config_id.graphql deleted file mode 100644 index 2fab7f3941430..0000000000000 --- a/compiler/crates/relay-transforms/tests/generate_live_query_metadata/fixtures/live_by_at_live_config_id.graphql +++ /dev/null @@ -1,12 +0,0 @@ -query NodeQuery($id: ID!) { - node(id: $id) @live(config_id: "instrumentation_config_id") { - id - ... on User { - birthdate { - day - month - year - } - } - } -} diff --git a/compiler/crates/relay-transforms/tests/generate_live_query_metadata_test.rs b/compiler/crates/relay-transforms/tests/generate_live_query_metadata_test.rs index 3da451aa38495..a3635f18a222b 100644 --- a/compiler/crates/relay-transforms/tests/generate_live_query_metadata_test.rs +++ b/compiler/crates/relay-transforms/tests/generate_live_query_metadata_test.rs @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<<2b8e7c34ba92b84e94641baec91414cd>> */ mod generate_live_query_metadata; @@ -19,13 +19,6 @@ async fn live_by_at_live() { test_fixture(transform_fixture, file!(), "live_by_at_live.graphql", "generate_live_query_metadata/fixtures/live_by_at_live.expected", input, expected).await; } -#[tokio::test] -async fn live_by_at_live_config_id() { - let input = include_str!("generate_live_query_metadata/fixtures/live_by_at_live_config_id.graphql"); - let expected = include_str!("generate_live_query_metadata/fixtures/live_by_at_live_config_id.expected"); - test_fixture(transform_fixture, file!(), "live_by_at_live_config_id.graphql", "generate_live_query_metadata/fixtures/live_by_at_live_config_id.expected", input, expected).await; -} - #[tokio::test] async fn live_by_config_id() { let input = include_str!("generate_live_query_metadata/fixtures/live_by_config_id.graphql"); diff --git a/compiler/crates/relay-transforms/tests/mask.rs b/compiler/crates/relay-transforms/tests/mask.rs index 332b1540011f5..013502646567e 100644 --- a/compiler/crates/relay-transforms/tests/mask.rs +++ b/compiler/crates/relay-transforms/tests/mask.rs @@ -9,11 +9,11 @@ use std::sync::Arc; use common::SourceLocationKey; use fixture_tests::Fixture; -use graphql_ir::build; use graphql_ir::Program; +use graphql_ir::build; use graphql_syntax::parse_executable; -use graphql_text_printer::print_fragment; use graphql_text_printer::PrinterOptions; +use graphql_text_printer::print_fragment; use relay_test_schema::get_test_schema; use relay_transforms::mask; diff --git a/compiler/crates/relay-transforms/tests/match_transform_client_only.rs b/compiler/crates/relay-transforms/tests/match_transform_client_only.rs index 9d88aded5d78a..9f744380efdd1 100644 --- a/compiler/crates/relay-transforms/tests/match_transform_client_only.rs +++ b/compiler/crates/relay-transforms/tests/match_transform_client_only.rs @@ -9,15 +9,16 @@ use common::FeatureFlags; use fixture_tests::Fixture; use graphql_test_helpers::apply_transform_for_test; use relay_config::DeferStreamInterface; -use relay_config::DynamicModuleProvider; use relay_config::ModuleImportConfig; +use relay_config::ModuleProvider; use relay_config::Surface; use relay_transforms::transform_match; pub async fn transform_fixture(fixture: &Fixture<'_>) -> Result { let flags = FeatureFlags::default(); let module_import_config = ModuleImportConfig { - dynamic_module_provider: Some(DynamicModuleProvider::JSResource), + dynamic_module_provider: Some(ModuleProvider::JSResource), + operation_module_provider: None, surface: Some(Surface::All), }; let defer_stream_interface = DeferStreamInterface::default(); diff --git a/compiler/crates/relay-transforms/tests/match_transform_client_resolver.rs b/compiler/crates/relay-transforms/tests/match_transform_client_resolver.rs index 88dd1d5e0b935..eccb5849741a5 100644 --- a/compiler/crates/relay-transforms/tests/match_transform_client_resolver.rs +++ b/compiler/crates/relay-transforms/tests/match_transform_client_resolver.rs @@ -9,15 +9,16 @@ use common::FeatureFlags; use fixture_tests::Fixture; use graphql_test_helpers::apply_transform_for_test; use relay_config::DeferStreamInterface; -use relay_config::DynamicModuleProvider; use relay_config::ModuleImportConfig; +use relay_config::ModuleProvider; use relay_config::Surface; use relay_transforms::transform_match; pub async fn transform_fixture(fixture: &Fixture<'_>) -> Result { let flags = FeatureFlags::default(); let module_import_config = ModuleImportConfig { - dynamic_module_provider: Some(DynamicModuleProvider::JSResource), + dynamic_module_provider: Some(ModuleProvider::JSResource), + operation_module_provider: None, surface: Some(Surface::Resolvers), }; let defer_stream_interface = DeferStreamInterface::default(); diff --git a/compiler/crates/relay-transforms/tests/match_transform_client_resolver/fixtures/client-3D-module-on-concrete-parent-type.invalid.expected b/compiler/crates/relay-transforms/tests/match_transform_client_resolver/fixtures/client-3D-module-on-concrete-parent-type.invalid.expected index 7fc318b9fb52f..6fc095ce8a9bb 100644 --- a/compiler/crates/relay-transforms/tests/match_transform_client_resolver/fixtures/client-3D-module-on-concrete-parent-type.invalid.expected +++ b/compiler/crates/relay-transforms/tests/match_transform_client_resolver/fixtures/client-3D-module-on-concrete-parent-type.invalid.expected @@ -1,5 +1,4 @@ ==================================== INPUT ==================================== -# expected-to-throw fragment NameRendererFragment on Persona { id basicUser { @@ -24,11 +23,37 @@ type BasicUser @__RelayResolverModel { address: String __relay_model_instance: RelayResolverValue @relay_resolver(import_path: "ClientUserResolver", fragment_name: "ClientUser__id", inject_fragment_data: "id") } -==================================== ERROR ==================================== -✖︎ @module was used on a fragment with a concrete parent type: 'BasicUser'. The parent type should be an interface or union. +==================================== OUTPUT =================================== +fragment ClientUser_Fragment on BasicUser { + name + address +} - client-3D-module-on-concrete-parent-type.invalid.graphql:5:8 - 4 │ basicUser { - 5 │ ...ClientUser_Fragment @module(name: "ClientUser.react") - │ ^^^^^^^^^^^^^^^^^^^ - 6 │ } +fragment NameRendererFragment on Persona { + id + basicUser { + ... on BasicUser { + ... on BasicUser @__ModuleMetadata + # ModuleMetadata { + # location: client-3D-module-on-concrete-parent-type.invalid.graphql:89:96, + # key: "NameRendererFragment", + # module_id: "NameRendererFragment.basicUser", + # module_name: "ClientUser.react", + # source_document_name: FragmentDefinitionName( + # FragmentDefinitionName( + # "NameRendererFragment", + # ), + # ), + # read_time_resolvers: true, + # fragment_name: FragmentDefinitionName( + # "ClientUser_Fragment", + # ), + # fragment_source_location: client-3D-module-on-concrete-parent-type.invalid.graphql:139:158, + # no_inline: false, + # } + { + ...ClientUser_Fragment + } + } + } +} diff --git a/compiler/crates/relay-transforms/tests/match_transform_client_resolver/fixtures/client-3D-module-on-concrete-parent-type.invalid.graphql b/compiler/crates/relay-transforms/tests/match_transform_client_resolver/fixtures/client-3D-module-on-concrete-parent-type.invalid.graphql index 21b3267522f90..bc7037d1ece47 100644 --- a/compiler/crates/relay-transforms/tests/match_transform_client_resolver/fixtures/client-3D-module-on-concrete-parent-type.invalid.graphql +++ b/compiler/crates/relay-transforms/tests/match_transform_client_resolver/fixtures/client-3D-module-on-concrete-parent-type.invalid.graphql @@ -1,4 +1,3 @@ -# expected-to-throw fragment NameRendererFragment on Persona { id basicUser { diff --git a/compiler/crates/relay-transforms/tests/node_identifier_test.rs b/compiler/crates/relay-transforms/tests/node_identifier_test.rs index f43f7a30bd647..67cb4a8924609 100644 --- a/compiler/crates/relay-transforms/tests/node_identifier_test.rs +++ b/compiler/crates/relay-transforms/tests/node_identifier_test.rs @@ -6,10 +6,10 @@ */ use common::SourceLocationKey; -use graphql_ir::build; -use graphql_ir::node_identifier::NodeIdentifier; use graphql_ir::ExecutableDefinition; use graphql_ir::Selection; +use graphql_ir::build; +use graphql_ir::node_identifier::NodeIdentifier; use graphql_syntax::parse_executable; use relay_test_schema::TEST_SCHEMA; use relay_transforms::RelayLocationAgnosticBehavior; diff --git a/compiler/crates/relay-transforms/tests/refetchable_fragment.rs b/compiler/crates/relay-transforms/tests/refetchable_fragment.rs index 1bf80be1b247c..62160afc416c6 100644 --- a/compiler/crates/relay-transforms/tests/refetchable_fragment.rs +++ b/compiler/crates/relay-transforms/tests/refetchable_fragment.rs @@ -8,9 +8,10 @@ use fixture_tests::Fixture; use graphql_test_helpers::apply_transform_for_test; use relay_config::DeferStreamInterface; +use relay_config::ProjectConfig; +use relay_transforms::ConnectionInterface; use relay_transforms::transform_connections; use relay_transforms::transform_refetchable_fragment; -use relay_transforms::ConnectionInterface; pub async fn transform_fixture(fixture: &Fixture<'_>) -> Result { apply_transform_for_test(fixture, |program| { @@ -21,12 +22,18 @@ pub async fn transform_fixture(fixture: &Fixture<'_>) -> Result false, ); let base_fragments = Default::default(); - transform_refetchable_fragment( - &program, - &Default::default(), - &base_fragments, - false, - vec![], - ) + let schema_config = if fixture.content.contains("// enable-token-field: true") { + relay_config::SchemaConfig { + enable_token_field: true, + ..Default::default() + } + } else { + Default::default() + }; + let project_config = ProjectConfig { + schema_config, + ..Default::default() + }; + transform_refetchable_fragment(&program, &project_config, &base_fragments, false, vec![]) }) } diff --git a/compiler/crates/relay-transforms/tests/refetchable_fragment/fixtures/fragment-on-non-node-fetchable-type-with-token-field.expected b/compiler/crates/relay-transforms/tests/refetchable_fragment/fixtures/fragment-on-non-node-fetchable-type-with-token-field.expected new file mode 100644 index 0000000000000..33797b14a293b --- /dev/null +++ b/compiler/crates/relay-transforms/tests/refetchable_fragment/fixtures/fragment-on-non-node-fetchable-type-with-token-field.expected @@ -0,0 +1,60 @@ +==================================== INPUT ==================================== +# // enable-token-field: true + +fragment RefetchableFragment on NonNodeStory + @refetchable(queryName: "RefetchableFragmentQuery") { + actor { + ...ProfilePicture + } +} + +fragment ProfilePicture on User { + profilePicture(size: $size) { + uri + } +} +==================================== OUTPUT =================================== +query RefetchableFragmentQuery( + $size: [Int] + $id: ID! +) @__RefetchableDerivedFromMetadata +# RefetchableDerivedFromMetadata( +# FragmentDefinitionName( +# "RefetchableFragment", +# ), +# ) + { + fetch__NonNodeStory(input_fetch_id: $id) { + ...RefetchableFragment + } +} + +fragment ProfilePicture on User { + profilePicture(size: $size) { + uri + } +} + +fragment RefetchableFragment on NonNodeStory @refetchable(queryName: "RefetchableFragmentQuery") @__RefetchableMetadata +# RefetchableMetadata { +# operation_name: OperationDefinitionName( +# "RefetchableFragmentQuery", +# ), +# path: [ +# "fetch__NonNodeStory", +# ], +# identifier_info: Some( +# RefetchableIdentifierInfo { +# identifier_field: "fetch_id", +# identifier_query_variable_name: "id", +# }, +# ), +# is_prefetchable_pagination: false, +# } + { + actor { + ...ProfilePicture + } + fetch_id + __token +} diff --git a/compiler/crates/relay-transforms/tests/refetchable_fragment/fixtures/fragment-on-non-node-fetchable-type-with-token-field.graphql b/compiler/crates/relay-transforms/tests/refetchable_fragment/fixtures/fragment-on-non-node-fetchable-type-with-token-field.graphql new file mode 100644 index 0000000000000..5336685e87e88 --- /dev/null +++ b/compiler/crates/relay-transforms/tests/refetchable_fragment/fixtures/fragment-on-non-node-fetchable-type-with-token-field.graphql @@ -0,0 +1,14 @@ +# // enable-token-field: true + +fragment RefetchableFragment on NonNodeStory + @refetchable(queryName: "RefetchableFragmentQuery") { + actor { + ...ProfilePicture + } +} + +fragment ProfilePicture on User { + profilePicture(size: $size) { + uri + } +} diff --git a/compiler/crates/relay-transforms/tests/refetchable_fragment/fixtures/fragment-on-non-node-fetchable-type.expected b/compiler/crates/relay-transforms/tests/refetchable_fragment/fixtures/fragment-on-non-node-fetchable-type.expected index 078a78736c942..1056b588e0b82 100644 --- a/compiler/crates/relay-transforms/tests/refetchable_fragment/fixtures/fragment-on-non-node-fetchable-type.expected +++ b/compiler/crates/relay-transforms/tests/refetchable_fragment/fixtures/fragment-on-non-node-fetchable-type.expected @@ -54,5 +54,4 @@ fragment RefetchableFragment on NonNodeStory @refetchable(queryName: "Refetchabl ...ProfilePicture } fetch_id - __token } diff --git a/compiler/crates/relay-transforms/tests/refetchable_fragment/fixtures/refetchable-interface-but-no-implementing-types.expected b/compiler/crates/relay-transforms/tests/refetchable_fragment/fixtures/refetchable-interface-but-no-implementing-types.expected index b8974e7f3b0ed..a2f057ebf52ba 100644 --- a/compiler/crates/relay-transforms/tests/refetchable_fragment/fixtures/refetchable-interface-but-no-implementing-types.expected +++ b/compiler/crates/relay-transforms/tests/refetchable_fragment/fixtures/refetchable-interface-but-no-implementing-types.expected @@ -47,5 +47,4 @@ fragment RefetchableFragmentFoo on RefetchableInterfaceFoo @refetchable(queryNam # } { id - __token } diff --git a/compiler/crates/relay-transforms/tests/refetchable_fragment/fixtures/refetchable-interface-some-types-impl-node.expected b/compiler/crates/relay-transforms/tests/refetchable_fragment/fixtures/refetchable-interface-some-types-impl-node.expected index 7f401fadbb1cc..0a2af5ba85e5a 100644 --- a/compiler/crates/relay-transforms/tests/refetchable_fragment/fixtures/refetchable-interface-some-types-impl-node.expected +++ b/compiler/crates/relay-transforms/tests/refetchable_fragment/fixtures/refetchable-interface-some-types-impl-node.expected @@ -54,5 +54,4 @@ fragment RefetchableFragment on RefetchableInterface @refetchable(queryName: "Re # } { id - __token } diff --git a/compiler/crates/relay-transforms/tests/refetchable_fragment/fixtures/refetchable-interface.expected b/compiler/crates/relay-transforms/tests/refetchable_fragment/fixtures/refetchable-interface.expected index 3f1d6beab77ba..dffb237076cd4 100644 --- a/compiler/crates/relay-transforms/tests/refetchable_fragment/fixtures/refetchable-interface.expected +++ b/compiler/crates/relay-transforms/tests/refetchable_fragment/fixtures/refetchable-interface.expected @@ -78,7 +78,6 @@ fragment RefetchableFragment on RefetchableInterface @refetchable(queryName: "Re # } { id - __token } fragment RefetchableFragment2 on RefetchableInterface2 @refetchable(queryName: "RefetchableFragmentQuery2") @__RefetchableMetadata @@ -100,5 +99,4 @@ fragment RefetchableFragment2 on RefetchableInterface2 @refetchable(queryName: " { __typename not_id - __token } diff --git a/compiler/crates/relay-transforms/tests/refetchable_fragment_test.rs b/compiler/crates/relay-transforms/tests/refetchable_fragment_test.rs index 5b2a382ed1b05..ade8bd191fe97 100644 --- a/compiler/crates/relay-transforms/tests/refetchable_fragment_test.rs +++ b/compiler/crates/relay-transforms/tests/refetchable_fragment_test.rs @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<63626487811055e17831fb93aee7ca44>> + * @generated SignedSource<<733f08cd406c19e1403b12a718566e81>> */ mod refetchable_fragment; @@ -68,6 +68,13 @@ async fn fragment_on_non_node_fetchable_type() { test_fixture(transform_fixture, file!(), "fragment-on-non-node-fetchable-type.graphql", "refetchable_fragment/fixtures/fragment-on-non-node-fetchable-type.expected", input, expected).await; } +#[tokio::test] +async fn fragment_on_non_node_fetchable_type_with_token_field() { + let input = include_str!("refetchable_fragment/fixtures/fragment-on-non-node-fetchable-type-with-token-field.graphql"); + let expected = include_str!("refetchable_fragment/fixtures/fragment-on-non-node-fetchable-type-with-token-field.expected"); + test_fixture(transform_fixture, file!(), "fragment-on-non-node-fetchable-type-with-token-field.graphql", "refetchable_fragment/fixtures/fragment-on-non-node-fetchable-type-with-token-field.expected", input, expected).await; +} + #[tokio::test] async fn fragment_on_object_implementing_node_interface() { let input = include_str!("refetchable_fragment/fixtures/fragment-on-object-implementing-node-interface.graphql"); diff --git a/compiler/crates/relay-transforms/tests/relay_resolvers.rs b/compiler/crates/relay-transforms/tests/relay_resolvers.rs index 4b4d73b22857b..a659362f2a7a7 100644 --- a/compiler/crates/relay-transforms/tests/relay_resolvers.rs +++ b/compiler/crates/relay-transforms/tests/relay_resolvers.rs @@ -13,12 +13,12 @@ use common::SourceLocationKey; use common::TextSource; use fixture_tests::Fixture; use graphql_cli::DiagnosticPrinter; -use graphql_ir::build; use graphql_ir::Program; +use graphql_ir::build; use graphql_syntax::parse_executable; +use graphql_text_printer::PrinterOptions; use graphql_text_printer::print_fragment; use graphql_text_printer::print_operation; -use graphql_text_printer::PrinterOptions; use relay_config::ProjectName; use relay_test_schema::get_test_schema_with_located_extensions; use relay_transforms::fragment_alias_directive; diff --git a/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/field-alias.expected b/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/field-alias.expected index da12ad2a18575..09eda975ae018 100644 --- a/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/field-alias.expected +++ b/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/field-alias.expected @@ -34,6 +34,7 @@ fragment Foo_user on User { # output_type_info: Legacy, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } } diff --git a/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/missing-fragment-name.expected b/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/missing-fragment-name.expected index 00987d089eec7..cfb715e559441 100644 --- a/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/missing-fragment-name.expected +++ b/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/missing-fragment-name.expected @@ -22,6 +22,7 @@ fragment Foo_user on User { # output_type_info: Legacy, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } } diff --git a/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/multiple-relay-resolvers.expected b/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/multiple-relay-resolvers.expected index b3d9a0bc35cb0..90cd32ee762db 100644 --- a/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/multiple-relay-resolvers.expected +++ b/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/multiple-relay-resolvers.expected @@ -38,6 +38,7 @@ fragment Foo_user on User { # output_type_info: Legacy, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } ...HobbitNameResolverFragment_name @__RelayResolverMetadata @@ -52,6 +53,7 @@ fragment Foo_user on User { # output_type_info: Legacy, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } } diff --git a/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/nested-relay-resolver.expected b/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/nested-relay-resolver.expected index 211212beb1102..cc0caf7b6a990 100644 --- a/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/nested-relay-resolver.expected +++ b/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/nested-relay-resolver.expected @@ -38,6 +38,7 @@ fragment Foo_user on User { # output_type_info: Legacy, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } } @@ -56,6 +57,7 @@ fragment HobbitNameResolverFragment_name on User { # output_type_info: Legacy, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } } diff --git a/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/relay-resolver-backing-client-edge.expected b/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/relay-resolver-backing-client-edge.expected index 3a29d5cbd70e9..e05c71733b421 100644 --- a/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/relay-resolver-backing-client-edge.expected +++ b/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/relay-resolver-backing-client-edge.expected @@ -36,6 +36,7 @@ fragment Foo_user on User { # output_type_info: EdgeTo, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } } diff --git a/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/relay-resolver-field-and-fragment-arguments.expected b/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/relay-resolver-field-and-fragment-arguments.expected index a86fb9a6b3126..22dbf98d9cbfe 100644 --- a/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/relay-resolver-field-and-fragment-arguments.expected +++ b/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/relay-resolver-field-and-fragment-arguments.expected @@ -44,6 +44,7 @@ fragment Foo_user on User { # output_type_info: Legacy, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } @arguments(fragment_arg: 1) } diff --git a/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/relay-resolver-model.expected b/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/relay-resolver-model.expected index e2992b7896a9b..6a1cb3c58ea33 100644 --- a/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/relay-resolver-model.expected +++ b/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/relay-resolver-model.expected @@ -45,6 +45,7 @@ fragment Foo_user on User { # ), # ), # type_confirmed: false, + # resolver_type: ResolverModule, # } } diff --git a/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/relay-resolver-named-import.expected b/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/relay-resolver-named-import.expected index 27f55a07d24b5..512d6b7b142fd 100644 --- a/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/relay-resolver-named-import.expected +++ b/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/relay-resolver-named-import.expected @@ -34,6 +34,7 @@ fragment Foo_user on User { # output_type_info: Legacy, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } } diff --git a/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/relay-resolver-required.expected b/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/relay-resolver-required.expected index 772bcabe0e929..3b1442955c4cd 100644 --- a/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/relay-resolver-required.expected +++ b/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/relay-resolver-required.expected @@ -32,6 +32,7 @@ fragment Foo_user on User { # output_type_info: Legacy, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } @required(action: THROW) } diff --git a/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/relay-resolver-scalar-field-arguments-with-alias.expected b/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/relay-resolver-scalar-field-arguments-with-alias.expected index 5cea5e6a2feff..00d7b4a050a37 100644 --- a/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/relay-resolver-scalar-field-arguments-with-alias.expected +++ b/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/relay-resolver-scalar-field-arguments-with-alias.expected @@ -40,6 +40,7 @@ fragment Foo_user on User { # output_type_info: Legacy, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } __id @__RelayResolverMetadata @@ -73,6 +74,7 @@ fragment Foo_user on User { # output_type_info: Legacy, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } } diff --git a/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/relay-resolver-scalar-field-arguments.expected b/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/relay-resolver-scalar-field-arguments.expected index b9a10d31df160..0aa4960153838 100644 --- a/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/relay-resolver-scalar-field-arguments.expected +++ b/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/relay-resolver-scalar-field-arguments.expected @@ -39,6 +39,7 @@ fragment Foo_user on User { # output_type_info: Legacy, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } } diff --git a/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/relay-resolver-within-named-inline-fragment.expected b/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/relay-resolver-within-named-inline-fragment.expected index 60e1a3c32fdae..f614823ad06ed 100644 --- a/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/relay-resolver-within-named-inline-fragment.expected +++ b/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/relay-resolver-within-named-inline-fragment.expected @@ -45,6 +45,7 @@ fragment Foo_user on Node { # output_type_info: Legacy, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } } diff --git a/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/relay-resolver.expected b/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/relay-resolver.expected index 08cdd2cd6118e..2a19606b96f3b 100644 --- a/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/relay-resolver.expected +++ b/compiler/crates/relay-transforms/tests/relay_resolvers/fixtures/relay-resolver.expected @@ -32,6 +32,7 @@ fragment Foo_user on User { # output_type_info: Legacy, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } } diff --git a/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types.rs b/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types.rs index 4173d912f7560..782b3c2981c73 100644 --- a/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types.rs +++ b/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types.rs @@ -11,13 +11,13 @@ use common::FeatureFlag; use common::FeatureFlags; use common::SourceLocationKey; use fixture_tests::Fixture; -use graphql_ir::build; use graphql_ir::Program; +use graphql_ir::build; use graphql_syntax::parse_executable; use graphql_test_helpers::diagnostics_to_sorted_string; +use graphql_text_printer::PrinterOptions; use graphql_text_printer::print_fragment; use graphql_text_printer::print_operation; -use graphql_text_printer::PrinterOptions; use relay_config::ProjectName; use relay_test_schema::get_test_schema_with_extensions; use relay_transforms::relay_resolvers; diff --git a/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/condition_on_inline_fragment_without_type_on_interface.expected b/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/condition_on_inline_fragment_without_type_on_interface.expected index bed548d5caf09..01af997409796 100644 --- a/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/condition_on_inline_fragment_without_type_on_interface.expected +++ b/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/condition_on_inline_fragment_without_type_on_interface.expected @@ -40,6 +40,7 @@ fragment conditionOnInlineFragmentWithoutTypeOnInterfaceFragment on Cat { # output_type_info: Legacy, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } __id @__RelayResolverMetadata @@ -54,6 +55,7 @@ fragment conditionOnInlineFragmentWithoutTypeOnInterfaceFragment on Cat { # output_type_info: Legacy, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } } diff --git a/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/conditions_on_nested_selections_on_interface.expected b/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/conditions_on_nested_selections_on_interface.expected index b7812f17151ae..078ec8c6e4d6b 100644 --- a/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/conditions_on_nested_selections_on_interface.expected +++ b/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/conditions_on_nested_selections_on_interface.expected @@ -56,6 +56,7 @@ fragment conditionsOnNestedSelectionsOnInterfaceFragment on Cat { # output_type_info: Legacy, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } } @@ -76,6 +77,7 @@ fragment conditionsOnNestedSelectionsOnInterfaceFragment on Cat { # output_type_info: Legacy, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } } diff --git a/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/conditions_on_selections_on_interface.expected b/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/conditions_on_selections_on_interface.expected index e702b45162fa4..1548e5de1ee5b 100644 --- a/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/conditions_on_selections_on_interface.expected +++ b/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/conditions_on_selections_on_interface.expected @@ -41,6 +41,7 @@ fragment conditionsOnSelectionsOnInterfaceFragment on Cat { # output_type_info: Legacy, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } } @@ -60,6 +61,7 @@ fragment conditionsOnSelectionsOnInterfaceFragment on Cat { # output_type_info: Legacy, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } } diff --git a/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/edge_to_abstract_type.expected b/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/edge_to_abstract_type.expected index 633e163ff44c6..026f747bd126c 100644 --- a/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/edge_to_abstract_type.expected +++ b/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/edge_to_abstract_type.expected @@ -40,6 +40,7 @@ query edgeToAbstractTypeQuery { # output_type_info: Legacy, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } } diff --git a/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/edge_to_abstract_type_with_inline_fragment.expected b/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/edge_to_abstract_type_with_inline_fragment.expected index 87b2a932c794d..3c19f7b50c156 100644 --- a/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/edge_to_abstract_type_with_inline_fragment.expected +++ b/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/edge_to_abstract_type_with_inline_fragment.expected @@ -52,6 +52,7 @@ query edgeToAbstractTypeWithInlineFragmentQuery { # output_type_info: Legacy, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } } diff --git a/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/edge_to_abstract_type_with_inline_fragment_on_abstract_type.expected b/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/edge_to_abstract_type_with_inline_fragment_on_abstract_type.expected index f855013a28b4b..f73aa0f5de4a2 100644 --- a/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/edge_to_abstract_type_with_inline_fragment_on_abstract_type.expected +++ b/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/edge_to_abstract_type_with_inline_fragment_on_abstract_type.expected @@ -48,6 +48,7 @@ query edgeToAbstractTypeWithInlineFragmentOnAbstractTypeQuery { # output_type_info: Legacy, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } } diff --git a/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/extend_server_defined_concrete_type.expected b/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/extend_server_defined_concrete_type.expected index 5ece2fb3f72fe..8b2d3c45b2b72 100644 --- a/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/extend_server_defined_concrete_type.expected +++ b/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/extend_server_defined_concrete_type.expected @@ -29,6 +29,7 @@ fragment extendServerDefinedConcreteTypeFragment on FeedUnit { # output_type_info: Legacy, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } } diff --git a/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/fragment_on_abstract_type_enabled.expected b/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/fragment_on_abstract_type_enabled.expected index 631f0d2468df6..fad3f692e9924 100644 --- a/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/fragment_on_abstract_type_enabled.expected +++ b/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/fragment_on_abstract_type_enabled.expected @@ -41,6 +41,7 @@ fragment fragmentOnAbstractTypeEnabledFragment on Cat { # output_type_info: Legacy, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } } diff --git a/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/inline_fragment_without_type_condition_on_interface.expected b/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/inline_fragment_without_type_condition_on_interface.expected index 9ed95814c0234..2ad4f27e0c066 100644 --- a/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/inline_fragment_without_type_condition_on_interface.expected +++ b/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/inline_fragment_without_type_condition_on_interface.expected @@ -45,6 +45,7 @@ fragment inlineFragmentWithoutTypeConditionOnInterfaceFragment on Cat { # output_type_info: Legacy, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } } diff --git a/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/nested_abstract_type_fragment.expected b/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/nested_abstract_type_fragment.expected index 2e7ea8c7588d7..ef3d396e9c3ef 100644 --- a/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/nested_abstract_type_fragment.expected +++ b/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/nested_abstract_type_fragment.expected @@ -44,6 +44,7 @@ fragment nestedAbstractTypeFragment on Cat { # output_type_info: Legacy, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } } diff --git a/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/nested_abstract_type_query.expected b/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/nested_abstract_type_query.expected index 4995087b1956e..a4002840f889d 100644 --- a/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/nested_abstract_type_query.expected +++ b/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/nested_abstract_type_query.expected @@ -47,6 +47,7 @@ query nestedAbstractTypeQuery { # output_type_info: Legacy, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } } diff --git a/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/nested_abstract_type_selection_on_inline_fragment_without_type.expected b/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/nested_abstract_type_selection_on_inline_fragment_without_type.expected index dec9f8e17d3bd..a4a8f30dd9ecf 100644 --- a/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/nested_abstract_type_selection_on_inline_fragment_without_type.expected +++ b/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/nested_abstract_type_selection_on_inline_fragment_without_type.expected @@ -48,6 +48,7 @@ fragment nestedAbstractTypeSelectionOnInlineFragmentWithoutTypeFragment on Cat { # output_type_info: Legacy, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } } @@ -69,6 +70,7 @@ fragment nestedAbstractTypeSelectionOnInlineFragmentWithoutTypeFragment on Cat { # output_type_info: Legacy, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } } diff --git a/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/nested_condition_on_inline_fragment_on_interface.expected b/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/nested_condition_on_inline_fragment_on_interface.expected index 75889c9bbbb6d..28abf23bb3d98 100644 --- a/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/nested_condition_on_inline_fragment_on_interface.expected +++ b/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/nested_condition_on_inline_fragment_on_interface.expected @@ -45,6 +45,7 @@ fragment nestedConditionOnInlineFragmentOnInterfaceFragment on Cat { # output_type_info: Legacy, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } } diff --git a/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/nested_fragment_spread_on_abstract_type.expected b/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/nested_fragment_spread_on_abstract_type.expected index 19a947bbd80f5..ee34d503aeb97 100644 --- a/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/nested_fragment_spread_on_abstract_type.expected +++ b/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/nested_fragment_spread_on_abstract_type.expected @@ -49,6 +49,7 @@ fragment nestedFragmentSpreadOnAbstractTypeFragment on Cat { # output_type_info: Legacy, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } } @@ -77,6 +78,7 @@ query nestedFragmentSpreadOnAbstractTypeQuery { # output_type_info: Legacy, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } } diff --git a/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/plural_fragment_on_abstract_type.expected b/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/plural_fragment_on_abstract_type.expected index 8ebdee1246ce1..33243d1979b61 100644 --- a/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/plural_fragment_on_abstract_type.expected +++ b/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/plural_fragment_on_abstract_type.expected @@ -38,6 +38,7 @@ fragment pluralFragmentOnAbstractTypeFragment on Cat @relay(plural: true) { # output_type_info: Legacy, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } } diff --git a/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/resolver_field_on_client_interface.expected b/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/resolver_field_on_client_interface.expected index c5f47391ec689..e077f92b61423 100644 --- a/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/resolver_field_on_client_interface.expected +++ b/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/resolver_field_on_client_interface.expected @@ -32,6 +32,7 @@ fragment resolverFieldOnClientInterfaceFragment on Cat { # output_type_info: Legacy, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } } diff --git a/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/resolver_field_on_client_type_implementing_server_interface.expected b/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/resolver_field_on_client_type_implementing_server_interface.expected index b0d07ea933c12..8a35c27417b38 100644 --- a/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/resolver_field_on_client_type_implementing_server_interface.expected +++ b/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/resolver_field_on_client_type_implementing_server_interface.expected @@ -41,6 +41,7 @@ fragment resolverFieldOnClientTypeImplementingServerInterfaceFragment on FeedUni # output_type_info: Legacy, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } } diff --git a/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/spread_fragment_into_interface_on_concrete_type.expected b/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/spread_fragment_into_interface_on_concrete_type.expected index 00597867757cc..573411850f3ed 100644 --- a/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/spread_fragment_into_interface_on_concrete_type.expected +++ b/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/spread_fragment_into_interface_on_concrete_type.expected @@ -45,6 +45,7 @@ fragment spreadFragmentIntoInterfaceOnConcreteTypeFragment on Cat { # output_type_info: Legacy, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } } diff --git a/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/spread_fragment_on_abstract_type.expected b/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/spread_fragment_on_abstract_type.expected index dbd1d1ceb6fb7..8de26f4035500 100644 --- a/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/spread_fragment_on_abstract_type.expected +++ b/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/spread_fragment_on_abstract_type.expected @@ -43,6 +43,7 @@ fragment spreadFragmentOnAbstractTypeFragment on Cat { # output_type_info: Legacy, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } } diff --git a/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/union_types_are_skipped.expected b/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/union_types_are_skipped.expected index dab8e4a969de3..3b60045d1a6e8 100644 --- a/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/union_types_are_skipped.expected +++ b/compiler/crates/relay-transforms/tests/relay_resolvers_abstract_types/fixtures/union_types_are_skipped.expected @@ -36,6 +36,7 @@ fragment spreadFragmentOnAbstractTypeFragment on Cat { # output_type_info: Legacy, # fragment_data_injection_mode: None, # type_confirmed: false, + # resolver_type: ResolverModule, # } } diff --git a/compiler/crates/relay-transforms/tests/relay_test_operation.rs b/compiler/crates/relay-transforms/tests/relay_test_operation.rs index 5645356d8effe..61a83ce7c36c5 100644 --- a/compiler/crates/relay-transforms/tests/relay_test_operation.rs +++ b/compiler/crates/relay-transforms/tests/relay_test_operation.rs @@ -9,13 +9,13 @@ use std::sync::Arc; use common::SourceLocationKey; use fixture_tests::Fixture; -use graphql_ir::build; use graphql_ir::Program; +use graphql_ir::build; use graphql_syntax::parse_executable; use graphql_test_helpers::diagnostics_to_sorted_string; +use graphql_text_printer::PrinterOptions; use graphql_text_printer::print_fragment; use graphql_text_printer::print_operation; -use graphql_text_printer::PrinterOptions; use regex::Regex; use relay_test_schema::get_test_schema_with_extensions; use relay_transforms::generate_test_operation_metadata; diff --git a/compiler/crates/relay-transforms/tests/skip_client_extensions.rs b/compiler/crates/relay-transforms/tests/skip_client_extensions.rs index 2c6491f478659..af145f44b990d 100644 --- a/compiler/crates/relay-transforms/tests/skip_client_extensions.rs +++ b/compiler/crates/relay-transforms/tests/skip_client_extensions.rs @@ -9,12 +9,12 @@ use std::sync::Arc; use common::SourceLocationKey; use fixture_tests::Fixture; -use graphql_ir::build; use graphql_ir::Program; +use graphql_ir::build; use graphql_syntax::parse_executable; +use graphql_text_printer::PrinterOptions; use graphql_text_printer::print_fragment; use graphql_text_printer::print_operation; -use graphql_text_printer::PrinterOptions; use relay_test_schema::get_test_schema_with_extensions; use relay_transforms::skip_client_extensions; diff --git a/compiler/crates/relay-transforms/tests/skip_redundant_nodes.rs b/compiler/crates/relay-transforms/tests/skip_redundant_nodes.rs index c76e0ae33721c..d3aba73baf926 100644 --- a/compiler/crates/relay-transforms/tests/skip_redundant_nodes.rs +++ b/compiler/crates/relay-transforms/tests/skip_redundant_nodes.rs @@ -9,11 +9,11 @@ use std::sync::Arc; use common::SourceLocationKey; use fixture_tests::Fixture; -use graphql_ir::build; use graphql_ir::Program; +use graphql_ir::build; use graphql_syntax::parse_executable; -use graphql_text_printer::print_operation; use graphql_text_printer::PrinterOptions; +use graphql_text_printer::print_operation; use relay_config::DeferStreamInterface; use relay_test_schema::get_test_schema; use relay_test_schema::get_test_schema_with_extensions; diff --git a/compiler/crates/relay-transforms/tests/skip_unreachable_nodes/fixtures/empty-selection-skip-true.expected b/compiler/crates/relay-transforms/tests/skip_unreachable_nodes/fixtures/empty-selection-skip-true.expected new file mode 100644 index 0000000000000..87920f6d75999 --- /dev/null +++ b/compiler/crates/relay-transforms/tests/skip_unreachable_nodes/fixtures/empty-selection-skip-true.expected @@ -0,0 +1,19 @@ +==================================== INPUT ==================================== +# expected-to-throw +query EmptyQuery($id: ID!) { + node(id: $id) { + ...Fragment + } +} + +fragment Fragment on User { + lastName @skip(if: true) +} +==================================== ERROR ==================================== +✖︎ After applying transforms to the query `EmptyQuery` selections of the `EmptyQuery` that would be sent to the server are empty. This is likely due to the use of `@skip`/`@include` directives with constant values that remove all selections in the query. + + empty-selection-skip-true.graphql:2:7 + 1 │ # expected-to-throw + 2 │ query EmptyQuery($id: ID!) { + │ ^^^^^^^^^^ + 3 │ node(id: $id) { diff --git a/compiler/crates/relay-transforms/tests/skip_unreachable_nodes/fixtures/empty-selection-skip-true.graphql b/compiler/crates/relay-transforms/tests/skip_unreachable_nodes/fixtures/empty-selection-skip-true.graphql new file mode 100644 index 0000000000000..85e9321f798dd --- /dev/null +++ b/compiler/crates/relay-transforms/tests/skip_unreachable_nodes/fixtures/empty-selection-skip-true.graphql @@ -0,0 +1,10 @@ +# expected-to-throw +query EmptyQuery($id: ID!) { + node(id: $id) { + ...Fragment + } +} + +fragment Fragment on User { + lastName @skip(if: true) +} \ No newline at end of file diff --git a/compiler/crates/relay-transforms/tests/skip_unreachable_nodes/fixtures/empty-selectoin-include-false.expected b/compiler/crates/relay-transforms/tests/skip_unreachable_nodes/fixtures/empty-selectoin-include-false.expected new file mode 100644 index 0000000000000..0947b852580eb --- /dev/null +++ b/compiler/crates/relay-transforms/tests/skip_unreachable_nodes/fixtures/empty-selectoin-include-false.expected @@ -0,0 +1,19 @@ +==================================== INPUT ==================================== +# expected-to-throw +query EmptyQuery($id: ID!) { + node(id: $id) { + ...Fragment + } +} + +fragment Fragment on User { + lastName @include(if: false) +} +==================================== ERROR ==================================== +✖︎ After applying transforms to the query `EmptyQuery` selections of the `EmptyQuery` that would be sent to the server are empty. This is likely due to the use of `@skip`/`@include` directives with constant values that remove all selections in the query. + + empty-selectoin-include-false.graphql:2:7 + 1 │ # expected-to-throw + 2 │ query EmptyQuery($id: ID!) { + │ ^^^^^^^^^^ + 3 │ node(id: $id) { diff --git a/compiler/crates/relay-transforms/tests/skip_unreachable_nodes/fixtures/empty-selectoin-include-false.graphql b/compiler/crates/relay-transforms/tests/skip_unreachable_nodes/fixtures/empty-selectoin-include-false.graphql new file mode 100644 index 0000000000000..efac3628e1f9c --- /dev/null +++ b/compiler/crates/relay-transforms/tests/skip_unreachable_nodes/fixtures/empty-selectoin-include-false.graphql @@ -0,0 +1,10 @@ +# expected-to-throw +query EmptyQuery($id: ID!) { + node(id: $id) { + ...Fragment + } +} + +fragment Fragment on User { + lastName @include(if: false) +} diff --git a/compiler/crates/relay-transforms/tests/skip_unreachable_nodes_test.rs b/compiler/crates/relay-transforms/tests/skip_unreachable_nodes_test.rs index 2b2ab44cc418b..223e1f10056ca 100644 --- a/compiler/crates/relay-transforms/tests/skip_unreachable_nodes_test.rs +++ b/compiler/crates/relay-transforms/tests/skip_unreachable_nodes_test.rs @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<256a993c631a9ebc61e38ac8a97f397a>> + * @generated SignedSource<<39dd352e5adf4e9487dc822994bb7306>> */ mod skip_unreachable_nodes; @@ -12,6 +12,20 @@ mod skip_unreachable_nodes; use skip_unreachable_nodes::transform_fixture; use fixture_tests::test_fixture; +#[tokio::test] +async fn empty_selection_skip_true() { + let input = include_str!("skip_unreachable_nodes/fixtures/empty-selection-skip-true.graphql"); + let expected = include_str!("skip_unreachable_nodes/fixtures/empty-selection-skip-true.expected"); + test_fixture(transform_fixture, file!(), "empty-selection-skip-true.graphql", "skip_unreachable_nodes/fixtures/empty-selection-skip-true.expected", input, expected).await; +} + +#[tokio::test] +async fn empty_selectoin_include_false() { + let input = include_str!("skip_unreachable_nodes/fixtures/empty-selectoin-include-false.graphql"); + let expected = include_str!("skip_unreachable_nodes/fixtures/empty-selectoin-include-false.expected"); + test_fixture(transform_fixture, file!(), "empty-selectoin-include-false.graphql", "skip_unreachable_nodes/fixtures/empty-selectoin-include-false.expected", input, expected).await; +} + #[tokio::test] async fn keeps_other_fields() { let input = include_str!("skip_unreachable_nodes/fixtures/keeps-other-fields.graphql"); diff --git a/compiler/crates/relay-transforms/tests/skip_unused_variables.rs b/compiler/crates/relay-transforms/tests/skip_unused_variables.rs index 34289b57dfd96..d910f3c68052e 100644 --- a/compiler/crates/relay-transforms/tests/skip_unused_variables.rs +++ b/compiler/crates/relay-transforms/tests/skip_unused_variables.rs @@ -7,8 +7,8 @@ use fixture_tests::Fixture; use graphql_test_helpers::apply_transform_for_test; -use relay_transforms::validate_operation_variables; use relay_transforms::ValidateVariablesOptions; +use relay_transforms::validate_operation_variables; pub async fn transform_fixture(fixture: &Fixture<'_>) -> Result { apply_transform_for_test(fixture, |program| { diff --git a/compiler/crates/relay-transforms/tests/transform_connections.rs b/compiler/crates/relay-transforms/tests/transform_connections.rs index 71746a5bce666..79e64aa658c17 100644 --- a/compiler/crates/relay-transforms/tests/transform_connections.rs +++ b/compiler/crates/relay-transforms/tests/transform_connections.rs @@ -9,18 +9,18 @@ use std::sync::Arc; use common::SourceLocationKey; use fixture_tests::Fixture; -use graphql_ir::build; use graphql_ir::Program; +use graphql_ir::build; use graphql_syntax::parse_executable; use graphql_test_helpers::diagnostics_to_sorted_string; +use graphql_text_printer::PrinterOptions; use graphql_text_printer::print_fragment; use graphql_text_printer::print_operation; -use graphql_text_printer::PrinterOptions; use relay_config::DeferStreamInterface; use relay_test_schema::get_test_schema; +use relay_transforms::ConnectionInterface; use relay_transforms::transform_connections; use relay_transforms::validate_connections; -use relay_transforms::ConnectionInterface; pub async fn transform_fixture(fixture: &Fixture<'_>) -> Result { let source_location = SourceLocationKey::standalone(fixture.file_name); diff --git a/compiler/crates/relay-transforms/tests/updatable_directive.rs b/compiler/crates/relay-transforms/tests/updatable_directive.rs index 4f1ff5e2f2747..1ce097c1bff72 100644 --- a/compiler/crates/relay-transforms/tests/updatable_directive.rs +++ b/compiler/crates/relay-transforms/tests/updatable_directive.rs @@ -9,8 +9,8 @@ use std::sync::Arc; use common::SourceLocationKey; use fixture_tests::Fixture; -use graphql_ir::build; use graphql_ir::Program; +use graphql_ir::build; use graphql_syntax::parse_executable; use graphql_test_helpers::diagnostics_to_sorted_string; use relay_test_schema::get_test_schema; diff --git a/compiler/crates/relay-transforms/tests/updatable_directive/fixtures/dangerously-unaliased-allowed.expected b/compiler/crates/relay-transforms/tests/updatable_directive/fixtures/dangerously-unaliased-allowed.expected new file mode 100644 index 0000000000000..36363b4300ecd --- /dev/null +++ b/compiler/crates/relay-transforms/tests/updatable_directive/fixtures/dangerously-unaliased-allowed.expected @@ -0,0 +1,12 @@ +==================================== INPUT ==================================== +query TestQuery @updatable { + node(id: 4) { + ...FragmentOnUser @dangerously_unaliased_fixme + } +} + +fragment FragmentOnUser on User @assignable { + __typename +} +==================================== OUTPUT =================================== +OK diff --git a/compiler/crates/relay-transforms/tests/updatable_directive/fixtures/dangerously-unaliased-allowed.graphql b/compiler/crates/relay-transforms/tests/updatable_directive/fixtures/dangerously-unaliased-allowed.graphql new file mode 100644 index 0000000000000..70372e457e150 --- /dev/null +++ b/compiler/crates/relay-transforms/tests/updatable_directive/fixtures/dangerously-unaliased-allowed.graphql @@ -0,0 +1,9 @@ +query TestQuery @updatable { + node(id: 4) { + ...FragmentOnUser @dangerously_unaliased_fixme + } +} + +fragment FragmentOnUser on User @assignable { + __typename +} diff --git a/compiler/crates/relay-transforms/tests/updatable_directive_test.rs b/compiler/crates/relay-transforms/tests/updatable_directive_test.rs index de7d40afb6de1..20bbfff12dafd 100644 --- a/compiler/crates/relay-transforms/tests/updatable_directive_test.rs +++ b/compiler/crates/relay-transforms/tests/updatable_directive_test.rs @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<97d52b0fa84f80726d7eb06310a18c0d>> + * @generated SignedSource<<424418c51b45b6086d00947be17a05cd>> */ mod updatable_directive; @@ -33,6 +33,13 @@ async fn client_side_updatable() { test_fixture(transform_fixture, file!(), "client-side-updatable.graphql", "updatable_directive/fixtures/client-side-updatable.expected", input, expected).await; } +#[tokio::test] +async fn dangerously_unaliased_allowed() { + let input = include_str!("updatable_directive/fixtures/dangerously-unaliased-allowed.graphql"); + let expected = include_str!("updatable_directive/fixtures/dangerously-unaliased-allowed.expected"); + test_fixture(transform_fixture, file!(), "dangerously-unaliased-allowed.graphql", "updatable_directive/fixtures/dangerously-unaliased-allowed.expected", input, expected).await; +} + #[tokio::test] async fn directive_fragment_spread_invalid() { let input = include_str!("updatable_directive/fixtures/directive-fragment-spread.invalid.graphql"); diff --git a/compiler/crates/relay-transforms/tests/updatable_fragment_spread.rs b/compiler/crates/relay-transforms/tests/updatable_fragment_spread.rs index 4630b38678d3f..2d340083e4056 100644 --- a/compiler/crates/relay-transforms/tests/updatable_fragment_spread.rs +++ b/compiler/crates/relay-transforms/tests/updatable_fragment_spread.rs @@ -9,8 +9,8 @@ use std::sync::Arc; use common::SourceLocationKey; use fixture_tests::Fixture; -use graphql_ir::build; use graphql_ir::Program; +use graphql_ir::build; use graphql_syntax::parse_executable; use graphql_test_helpers::diagnostics_to_sorted_string; use relay_test_schema::TEST_SCHEMA; diff --git a/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch.rs b/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch.rs new file mode 100644 index 0000000000000..0fe8d8350cb9a --- /dev/null +++ b/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch.rs @@ -0,0 +1,36 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +use std::sync::Arc; + +use common::SourceLocationKey; +use fixture_tests::Fixture; +use graphql_ir::Program; +use graphql_ir::build; +use graphql_syntax::parse_executable; +use graphql_test_helpers::diagnostics_to_sorted_string; +use relay_test_schema::get_test_schema_with_extensions; +use relay_transforms::validate_client_schema_extensions_use_catch; + +pub async fn transform_fixture(fixture: &Fixture<'_>) -> Result { + let parts: Vec<_> = fixture.content.split("%extensions%").collect(); + + if let [base, extensions] = parts.as_slice() { + let source_location = SourceLocationKey::standalone(fixture.file_name); + let ast = parse_executable(base, source_location).unwrap(); + let schema = get_test_schema_with_extensions(extensions); + + let ir = build(&schema, &ast.definitions).unwrap(); + let program: Program = Program::from_definitions(Arc::clone(&schema), ir); + validate_client_schema_extensions_use_catch(&program) + .map_err(|diagnostics| diagnostics_to_sorted_string(fixture.content, &diagnostics))?; + + Ok("OK".to_string()) + } else { + panic!("Expected exactly one %extensions% section marker.") + } +} diff --git a/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch/fixtures/built_in_field_without_catch.expected b/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch/fixtures/built_in_field_without_catch.expected new file mode 100644 index 0000000000000..119e427cb24c4 --- /dev/null +++ b/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch/fixtures/built_in_field_without_catch.expected @@ -0,0 +1,8 @@ +==================================== INPUT ==================================== +query MyQuery @throwOnFieldError { + __id +} + +# %extensions% +==================================== OUTPUT =================================== +OK diff --git a/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch/fixtures/built_in_field_without_catch.graphql b/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch/fixtures/built_in_field_without_catch.graphql new file mode 100644 index 0000000000000..940aad2db681f --- /dev/null +++ b/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch/fixtures/built_in_field_without_catch.graphql @@ -0,0 +1,5 @@ +query MyQuery @throwOnFieldError { + __id +} + +# %extensions% diff --git a/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch/fixtures/client_field_with_catch.expected b/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch/fixtures/client_field_with_catch.expected new file mode 100644 index 0000000000000..1db3ae2d173ba --- /dev/null +++ b/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch/fixtures/client_field_with_catch.expected @@ -0,0 +1,12 @@ +==================================== INPUT ==================================== +query MyQuery @throwOnFieldError { + client_field @catch +} + +# %extensions% + +extend type Query { + client_field: String +} +==================================== OUTPUT =================================== +OK diff --git a/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch/fixtures/client_field_with_catch.graphql b/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch/fixtures/client_field_with_catch.graphql new file mode 100644 index 0000000000000..415a11d6ae4cb --- /dev/null +++ b/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch/fixtures/client_field_with_catch.graphql @@ -0,0 +1,9 @@ +query MyQuery @throwOnFieldError { + client_field @catch +} + +# %extensions% + +extend type Query { + client_field: String +} diff --git a/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch/fixtures/client_field_with_inline_fragment_alias_with_catch.expected b/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch/fixtures/client_field_with_inline_fragment_alias_with_catch.expected new file mode 100644 index 0000000000000..ce109a3eaa2ef --- /dev/null +++ b/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch/fixtures/client_field_with_inline_fragment_alias_with_catch.expected @@ -0,0 +1,14 @@ +==================================== INPUT ==================================== +query MyQuery @throwOnFieldError { + ... @catch @alias(as: "my_alias") { + client_field + } +} + +# %extensions% + +extend type Query { + client_field: String +} +==================================== OUTPUT =================================== +OK diff --git a/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch/fixtures/client_field_with_inline_fragment_alias_with_catch.graphql b/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch/fixtures/client_field_with_inline_fragment_alias_with_catch.graphql new file mode 100644 index 0000000000000..b77d5763045e8 --- /dev/null +++ b/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch/fixtures/client_field_with_inline_fragment_alias_with_catch.graphql @@ -0,0 +1,11 @@ +query MyQuery @throwOnFieldError { + ... @catch @alias(as: "my_alias") { + client_field + } +} + +# %extensions% + +extend type Query { + client_field: String +} diff --git a/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch/fixtures/client_field_within_catch.expected b/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch/fixtures/client_field_within_catch.expected new file mode 100644 index 0000000000000..8b27fbfcf27da --- /dev/null +++ b/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch/fixtures/client_field_within_catch.expected @@ -0,0 +1,14 @@ +==================================== INPUT ==================================== +query MyQuery @throwOnFieldError { + me @catch { + client_field + } +} + +# %extensions% + +extend type User { + client_field: String +} +==================================== OUTPUT =================================== +OK diff --git a/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch/fixtures/client_field_within_catch.graphql b/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch/fixtures/client_field_within_catch.graphql new file mode 100644 index 0000000000000..dce482e9c3086 --- /dev/null +++ b/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch/fixtures/client_field_within_catch.graphql @@ -0,0 +1,11 @@ +query MyQuery @throwOnFieldError { + me @catch { + client_field + } +} + +# %extensions% + +extend type User { + client_field: String +} diff --git a/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch/fixtures/client_field_without_catch.expected b/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch/fixtures/client_field_without_catch.expected new file mode 100644 index 0000000000000..e9b8d9fbbd323 --- /dev/null +++ b/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch/fixtures/client_field_without_catch.expected @@ -0,0 +1,19 @@ +==================================== INPUT ==================================== +# expected-to-throw +query MyQuery @throwOnFieldError { + client_field +} + +# %extensions% + +extend type Query { + client_field: String +} +==================================== ERROR ==================================== +✖︎ Expected client-defined field within `@throwOnFieldError` to be annotated with `@catch`. Accessing an unset field is treated as a field error, but Relay cannot guarantee that client field will be set before they are read. Add `@catch` to explicitly handle the case where the field is unset. + + client_field_without_catch.graphql:3:3 + 2 │ query MyQuery @throwOnFieldError { + 3 │ client_field + │ ^^^^^^^^^^^^ + 4 │ } diff --git a/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch/fixtures/client_field_without_catch.graphql b/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch/fixtures/client_field_without_catch.graphql new file mode 100644 index 0000000000000..a4fa093e409f8 --- /dev/null +++ b/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch/fixtures/client_field_without_catch.graphql @@ -0,0 +1,10 @@ +# expected-to-throw +query MyQuery @throwOnFieldError { + client_field +} + +# %extensions% + +extend type Query { + client_field: String +} diff --git a/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch/fixtures/client_linked_field_without_catch.expected b/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch/fixtures/client_linked_field_without_catch.expected new file mode 100644 index 0000000000000..c668a9fefe9ce --- /dev/null +++ b/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch/fixtures/client_linked_field_without_catch.expected @@ -0,0 +1,21 @@ +==================================== INPUT ==================================== +# expected-to-throw +query MyQuery @throwOnFieldError { + client_field { + name + } +} + +# %extensions% + +extend type Query { + client_field: User +} +==================================== ERROR ==================================== +✖︎ Expected client-defined field within `@throwOnFieldError` to be annotated with `@catch`. Accessing an unset field is treated as a field error, but Relay cannot guarantee that client field will be set before they are read. Add `@catch` to explicitly handle the case where the field is unset. + + client_linked_field_without_catch.graphql:3:3 + 2 │ query MyQuery @throwOnFieldError { + 3 │ client_field { + │ ^^^^^^^^^^^^ + 4 │ name diff --git a/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch/fixtures/client_linked_field_without_catch.graphql b/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch/fixtures/client_linked_field_without_catch.graphql new file mode 100644 index 0000000000000..04c877c19765c --- /dev/null +++ b/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch/fixtures/client_linked_field_without_catch.graphql @@ -0,0 +1,12 @@ +# expected-to-throw +query MyQuery @throwOnFieldError { + client_field { + name + } +} + +# %extensions% + +extend type Query { + client_field: User +} diff --git a/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch/fixtures/non_nullable_client_field_with_catch.expected b/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch/fixtures/non_nullable_client_field_with_catch.expected new file mode 100644 index 0000000000000..951164924ba3f --- /dev/null +++ b/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch/fixtures/non_nullable_client_field_with_catch.expected @@ -0,0 +1,12 @@ +==================================== INPUT ==================================== +query MyQuery @throwOnFieldError { + client_field @catch +} + +# %extensions% + +extend type Query { + client_field: String! +} +==================================== OUTPUT =================================== +OK diff --git a/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch/fixtures/non_nullable_client_field_with_catch.graphql b/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch/fixtures/non_nullable_client_field_with_catch.graphql new file mode 100644 index 0000000000000..7afcf45a178fc --- /dev/null +++ b/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch/fixtures/non_nullable_client_field_with_catch.graphql @@ -0,0 +1,9 @@ +query MyQuery @throwOnFieldError { + client_field @catch +} + +# %extensions% + +extend type Query { + client_field: String! +} diff --git a/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch_test.rs b/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch_test.rs new file mode 100644 index 0000000000000..8b368b5431ad4 --- /dev/null +++ b/compiler/crates/relay-transforms/tests/validate_client_schema_extensions_use_catch_test.rs @@ -0,0 +1,62 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @generated SignedSource<> + */ + +mod validate_client_schema_extensions_use_catch; + +use validate_client_schema_extensions_use_catch::transform_fixture; +use fixture_tests::test_fixture; + +#[tokio::test] +async fn built_in_field_without_catch() { + let input = include_str!("validate_client_schema_extensions_use_catch/fixtures/built_in_field_without_catch.graphql"); + let expected = include_str!("validate_client_schema_extensions_use_catch/fixtures/built_in_field_without_catch.expected"); + test_fixture(transform_fixture, file!(), "built_in_field_without_catch.graphql", "validate_client_schema_extensions_use_catch/fixtures/built_in_field_without_catch.expected", input, expected).await; +} + +#[tokio::test] +async fn client_field_with_catch() { + let input = include_str!("validate_client_schema_extensions_use_catch/fixtures/client_field_with_catch.graphql"); + let expected = include_str!("validate_client_schema_extensions_use_catch/fixtures/client_field_with_catch.expected"); + test_fixture(transform_fixture, file!(), "client_field_with_catch.graphql", "validate_client_schema_extensions_use_catch/fixtures/client_field_with_catch.expected", input, expected).await; +} + +#[tokio::test] +async fn client_field_with_inline_fragment_alias_with_catch() { + let input = include_str!("validate_client_schema_extensions_use_catch/fixtures/client_field_with_inline_fragment_alias_with_catch.graphql"); + let expected = include_str!("validate_client_schema_extensions_use_catch/fixtures/client_field_with_inline_fragment_alias_with_catch.expected"); + test_fixture(transform_fixture, file!(), "client_field_with_inline_fragment_alias_with_catch.graphql", "validate_client_schema_extensions_use_catch/fixtures/client_field_with_inline_fragment_alias_with_catch.expected", input, expected).await; +} + +#[tokio::test] +async fn client_field_within_catch() { + let input = include_str!("validate_client_schema_extensions_use_catch/fixtures/client_field_within_catch.graphql"); + let expected = include_str!("validate_client_schema_extensions_use_catch/fixtures/client_field_within_catch.expected"); + test_fixture(transform_fixture, file!(), "client_field_within_catch.graphql", "validate_client_schema_extensions_use_catch/fixtures/client_field_within_catch.expected", input, expected).await; +} + +#[tokio::test] +async fn client_field_without_catch() { + let input = include_str!("validate_client_schema_extensions_use_catch/fixtures/client_field_without_catch.graphql"); + let expected = include_str!("validate_client_schema_extensions_use_catch/fixtures/client_field_without_catch.expected"); + test_fixture(transform_fixture, file!(), "client_field_without_catch.graphql", "validate_client_schema_extensions_use_catch/fixtures/client_field_without_catch.expected", input, expected).await; +} + +#[tokio::test] +async fn client_linked_field_without_catch() { + let input = include_str!("validate_client_schema_extensions_use_catch/fixtures/client_linked_field_without_catch.graphql"); + let expected = include_str!("validate_client_schema_extensions_use_catch/fixtures/client_linked_field_without_catch.expected"); + test_fixture(transform_fixture, file!(), "client_linked_field_without_catch.graphql", "validate_client_schema_extensions_use_catch/fixtures/client_linked_field_without_catch.expected", input, expected).await; +} + +#[tokio::test] +async fn non_nullable_client_field_with_catch() { + let input = include_str!("validate_client_schema_extensions_use_catch/fixtures/non_nullable_client_field_with_catch.graphql"); + let expected = include_str!("validate_client_schema_extensions_use_catch/fixtures/non_nullable_client_field_with_catch.expected"); + test_fixture(transform_fixture, file!(), "non_nullable_client_field_with_catch.graphql", "validate_client_schema_extensions_use_catch/fixtures/non_nullable_client_field_with_catch.expected", input, expected).await; +} diff --git a/compiler/crates/relay-transforms/tests/validate_connections.rs b/compiler/crates/relay-transforms/tests/validate_connections.rs index beb384d389476..fc6853f8abe74 100644 --- a/compiler/crates/relay-transforms/tests/validate_connections.rs +++ b/compiler/crates/relay-transforms/tests/validate_connections.rs @@ -9,13 +9,13 @@ use std::sync::Arc; use common::SourceLocationKey; use fixture_tests::Fixture; -use graphql_ir::build; use graphql_ir::Program; +use graphql_ir::build; use graphql_syntax::parse_executable; use graphql_test_helpers::diagnostics_to_sorted_string; use relay_test_schema::TEST_SCHEMA; -use relay_transforms::validate_connections; use relay_transforms::ConnectionInterface; +use relay_transforms::validate_connections; pub async fn transform_fixture(fixture: &Fixture<'_>) -> Result { let source_location = SourceLocationKey::standalone(fixture.file_name); diff --git a/compiler/crates/relay-transforms/tests/validate_connections/fixtures/connection-invalid-key-name.invalid.expected b/compiler/crates/relay-transforms/tests/validate_connections/fixtures/connection-invalid-key-name.invalid.expected index 492cb4715ec82..6d11151bf8f7a 100644 --- a/compiler/crates/relay-transforms/tests/validate_connections/fixtures/connection-invalid-key-name.invalid.expected +++ b/compiler/crates/relay-transforms/tests/validate_connections/fixtures/connection-invalid-key-name.invalid.expected @@ -15,7 +15,7 @@ query NodeQuery($id: ID!) { } } ==================================== ERROR ==================================== -✖︎ Expected the key argument to @connection to be of form '__comments', got 'invalid'. For a detailed explanation, check out https://relay.dev/docs/en/pagination-container#connection +✖︎ Expected the key argument to @connection to be of form '__comments', got 'invalid'. For a detailed explanation, check out https://relay.dev/docs/tutorial/connections-pagination/ connection-invalid-key-name.invalid.graphql:6:44 5 │ ... on Story { diff --git a/compiler/crates/relay-transforms/tests/validate_connections_schema.rs b/compiler/crates/relay-transforms/tests/validate_connections_schema.rs index 9b98a721d7cb9..3c9b6f2023905 100644 --- a/compiler/crates/relay-transforms/tests/validate_connections_schema.rs +++ b/compiler/crates/relay-transforms/tests/validate_connections_schema.rs @@ -9,13 +9,13 @@ use std::sync::Arc; use common::SourceLocationKey; use fixture_tests::Fixture; -use graphql_ir::build; use graphql_ir::Program; +use graphql_ir::build; use graphql_syntax::parse_executable; use graphql_test_helpers::diagnostics_to_sorted_string; use relay_test_schema::get_test_schema_with_extensions; -use relay_transforms::validate_connections; use relay_transforms::ConnectionInterface; +use relay_transforms::validate_connections; pub async fn transform_fixture(fixture: &Fixture<'_>) -> Result { let parts: Vec<_> = fixture.content.split("%extensions%").collect(); diff --git a/compiler/crates/relay-transforms/tests/validate_deprecated_fields.rs b/compiler/crates/relay-transforms/tests/validate_deprecated_fields.rs index 9c29a4f82be8a..81afbecd63531 100644 --- a/compiler/crates/relay-transforms/tests/validate_deprecated_fields.rs +++ b/compiler/crates/relay-transforms/tests/validate_deprecated_fields.rs @@ -9,8 +9,8 @@ use std::sync::Arc; use common::SourceLocationKey; use fixture_tests::Fixture; -use graphql_ir::build; use graphql_ir::Program; +use graphql_ir::build; use graphql_syntax::parse_executable; use graphql_test_helpers::diagnostics_to_sorted_string; use relay_test_schema::get_test_schema_with_extensions; diff --git a/compiler/crates/relay-transforms/tests/validate_fragment_alias_conflict.rs b/compiler/crates/relay-transforms/tests/validate_fragment_alias_conflict.rs index ebdb78fcdfb5e..3bc4381c1ece3 100644 --- a/compiler/crates/relay-transforms/tests/validate_fragment_alias_conflict.rs +++ b/compiler/crates/relay-transforms/tests/validate_fragment_alias_conflict.rs @@ -11,12 +11,12 @@ use common::SourceLocationKey; use common::TextSource; use fixture_tests::Fixture; use graphql_cli::DiagnosticPrinter; -use graphql_ir::build; use graphql_ir::Program; +use graphql_ir::build; use graphql_syntax::parse_executable; use graphql_test_helpers::diagnostics_to_sorted_string; -use relay_test_schema::get_test_schema_with_located_extensions; use relay_test_schema::TEST_SCHEMA; +use relay_test_schema::get_test_schema_with_located_extensions; use relay_transforms::validate_fragment_alias_conflict; pub async fn transform_fixture(fixture: &Fixture<'_>) -> Result { diff --git a/compiler/crates/relay-transforms/tests/validate_global_variable_names.rs b/compiler/crates/relay-transforms/tests/validate_global_variable_names.rs index d9a1d3807c070..38673fc16df0f 100644 --- a/compiler/crates/relay-transforms/tests/validate_global_variable_names.rs +++ b/compiler/crates/relay-transforms/tests/validate_global_variable_names.rs @@ -9,8 +9,8 @@ use std::sync::Arc; use common::SourceLocationKey; use fixture_tests::Fixture; -use graphql_ir::build; use graphql_ir::Program; +use graphql_ir::build; use graphql_syntax::parse_executable; use graphql_test_helpers::diagnostics_to_sorted_string; use relay_test_schema::TEST_SCHEMA; diff --git a/compiler/crates/relay-transforms/tests/validate_global_variables.rs b/compiler/crates/relay-transforms/tests/validate_global_variables.rs index 635f14709ba57..e3091e64a45bb 100644 --- a/compiler/crates/relay-transforms/tests/validate_global_variables.rs +++ b/compiler/crates/relay-transforms/tests/validate_global_variables.rs @@ -11,11 +11,11 @@ use common::SourceLocationKey; use common::TextSource; use fixture_tests::Fixture; use graphql_cli::DiagnosticPrinter; -use graphql_ir::build_ir_with_extra_features; use graphql_ir::BuilderOptions; use graphql_ir::FragmentVariablesSemantic; use graphql_ir::Program; use graphql_ir::RelayMode; +use graphql_ir::build_ir_with_extra_features; use graphql_syntax::parse_executable; use graphql_test_helpers::diagnostics_to_sorted_string; use relay_test_schema::TEST_SCHEMA; diff --git a/compiler/crates/relay-transforms/tests/validate_module_names.rs b/compiler/crates/relay-transforms/tests/validate_module_names.rs index 7823d564a37e2..722a986014819 100644 --- a/compiler/crates/relay-transforms/tests/validate_module_names.rs +++ b/compiler/crates/relay-transforms/tests/validate_module_names.rs @@ -9,8 +9,8 @@ use std::sync::Arc; use common::SourceLocationKey; use fixture_tests::Fixture; -use graphql_ir::build; use graphql_ir::Program; +use graphql_ir::build; use graphql_syntax::parse_executable; use graphql_test_helpers::diagnostics_to_sorted_string; use relay_test_schema::TEST_SCHEMA; diff --git a/compiler/crates/relay-transforms/tests/validate_no_double_underscore_alias.rs b/compiler/crates/relay-transforms/tests/validate_no_double_underscore_alias.rs index a73b65df5d867..ea94a8a7a57ee 100644 --- a/compiler/crates/relay-transforms/tests/validate_no_double_underscore_alias.rs +++ b/compiler/crates/relay-transforms/tests/validate_no_double_underscore_alias.rs @@ -9,8 +9,8 @@ use std::sync::Arc; use common::SourceLocationKey; use fixture_tests::Fixture; -use graphql_ir::build; use graphql_ir::Program; +use graphql_ir::build; use graphql_syntax::parse_executable; use graphql_test_helpers::diagnostics_to_sorted_string; use relay_test_schema::TEST_SCHEMA; diff --git a/compiler/crates/relay-transforms/tests/validate_relay_directives.rs b/compiler/crates/relay-transforms/tests/validate_relay_directives.rs index f6408ba43b3ab..7a33061e04aff 100644 --- a/compiler/crates/relay-transforms/tests/validate_relay_directives.rs +++ b/compiler/crates/relay-transforms/tests/validate_relay_directives.rs @@ -7,8 +7,8 @@ use common::SourceLocationKey; use fixture_tests::Fixture; -use graphql_ir::build; use graphql_ir::Program; +use graphql_ir::build; use graphql_syntax::parse_executable; use graphql_test_helpers::diagnostics_to_sorted_string; use relay_test_schema::get_test_schema; diff --git a/compiler/crates/relay-transforms/tests/validate_required_arguments.rs b/compiler/crates/relay-transforms/tests/validate_required_arguments.rs index d88b2770b5196..772a88b3746f1 100644 --- a/compiler/crates/relay-transforms/tests/validate_required_arguments.rs +++ b/compiler/crates/relay-transforms/tests/validate_required_arguments.rs @@ -9,8 +9,8 @@ use std::sync::Arc; use common::SourceLocationKey; use fixture_tests::Fixture; -use graphql_ir::build; use graphql_ir::Program; +use graphql_ir::build; use graphql_syntax::parse_executable; use graphql_test_helpers::diagnostics_to_sorted_string; use relay_test_schema::TEST_SCHEMA; diff --git a/compiler/crates/relay-transforms/tests/validate_server_only_directives.rs b/compiler/crates/relay-transforms/tests/validate_server_only_directives.rs index 589ada2fccee3..b5f3e5b4455ef 100644 --- a/compiler/crates/relay-transforms/tests/validate_server_only_directives.rs +++ b/compiler/crates/relay-transforms/tests/validate_server_only_directives.rs @@ -9,8 +9,8 @@ use std::sync::Arc; use common::SourceLocationKey; use fixture_tests::Fixture; -use graphql_ir::build; use graphql_ir::Program; +use graphql_ir::build; use graphql_syntax::parse_executable; use graphql_test_helpers::diagnostics_to_sorted_string; use relay_test_schema::get_test_schema_with_extensions; diff --git a/compiler/crates/relay-transforms/tests/validate_static_args.rs b/compiler/crates/relay-transforms/tests/validate_static_args.rs index a3978e842419e..d2e8531d01877 100644 --- a/compiler/crates/relay-transforms/tests/validate_static_args.rs +++ b/compiler/crates/relay-transforms/tests/validate_static_args.rs @@ -9,8 +9,8 @@ use std::sync::Arc; use common::SourceLocationKey; use fixture_tests::Fixture; -use graphql_ir::build; use graphql_ir::Program; +use graphql_ir::build; use graphql_syntax::parse_executable; use graphql_test_helpers::diagnostics_to_sorted_string; use relay_test_schema::get_test_schema_with_extensions; diff --git a/compiler/crates/relay-transforms/tests/validate_unused_variables.rs b/compiler/crates/relay-transforms/tests/validate_unused_variables.rs index e7e7716a9a442..bc4bff934a18f 100644 --- a/compiler/crates/relay-transforms/tests/validate_unused_variables.rs +++ b/compiler/crates/relay-transforms/tests/validate_unused_variables.rs @@ -9,12 +9,15 @@ use std::sync::Arc; use common::SourceLocationKey; use fixture_tests::Fixture; -use graphql_ir::build; use graphql_ir::Program; +use graphql_ir::Validator; +use graphql_ir::build; +use graphql_ir::reexport::string_key::Lookup; use graphql_syntax::parse_executable; use graphql_test_helpers::diagnostics_to_sorted_string; use relay_test_schema::TEST_SCHEMA; -use relay_transforms::validate_unused_variables; +use relay_transforms::ValidateUnusedVariables; +use relay_transforms::VariableMapEntry; pub async fn transform_fixture(fixture: &Fixture<'_>) -> Result { let source_location = SourceLocationKey::standalone(fixture.file_name); @@ -24,8 +27,36 @@ pub async fn transform_fixture(fixture: &Fixture<'_>) -> Result .map_err(|diagnostics| diagnostics_to_sorted_string(fixture.content, &diagnostics))?; let program = Program::from_definitions(Arc::clone(&TEST_SCHEMA), ir); - validate_unused_variables(&program) - .map_err(|diagnostics| diagnostics_to_sorted_string(fixture.content, &diagnostics))?; + let mut validator = ValidateUnusedVariables::new(&program); + let result = validator.validate_program(&program); + result.map_err(|diagnostics| diagnostics_to_sorted_string(fixture.content, &diagnostics))?; + + // If we didn't error, print out the state of the fragment cache so we can + // validate which entries get populated. + let mut lines = validator + .visitor + .visited_fragments + .iter() + .map(|(name, variables_entry)| match variables_entry { + VariableMapEntry::Pending => format!("{} -> PENDING", name), + VariableMapEntry::Populated(variables) => { + let mut variables_string = variables + .keys() + .map(|key| key.0.lookup()) + .collect::>(); + + variables_string.sort(); + + format!("{} -> POPULATED ({:?})", name, variables_string.join(", ")) + } + }) + .collect::>(); + + lines.sort(); - Ok("OK".to_owned()) + if lines.is_empty() { + Ok("OK.\n\nNo Cached Fragments.".to_string()) + } else { + Ok(format!("OK.\n\nCached Fragments:\n{}", lines.join("\n"))) + } } diff --git a/compiler/crates/relay-transforms/tests/validate_unused_variables/fixtures/cycles-read-from-different-entrypoints.expected b/compiler/crates/relay-transforms/tests/validate_unused_variables/fixtures/cycles-read-from-different-entrypoints.expected new file mode 100644 index 0000000000000..227562610dc61 --- /dev/null +++ b/compiler/crates/relay-transforms/tests/validate_unused_variables/fixtures/cycles-read-from-different-entrypoints.expected @@ -0,0 +1,33 @@ +==================================== INPUT ==================================== +# Ensures cycles don't result in us caching incomplete used variables for a +# fragment which then gets reused by another query. + +fragment FragmentC on User { + url(relative: true, site: $varX) + nearest_neighbor { + ...FragmentD + } +} + +fragment FragmentD on User { + url(relative: true, site: $varY) + ...FragmentC +} + +query QueryA($varX: String, $varY: String) { + me { + ...FragmentC + } +} + +query QueryB($varX: String, $varY: String) { + me { + ...FragmentD + } +} +==================================== OUTPUT =================================== +OK. + +Cached Fragments: +FragmentC -> POPULATED ("varX, varY") +FragmentD -> POPULATED ("varX, varY") diff --git a/compiler/crates/relay-transforms/tests/validate_unused_variables/fixtures/cycles-read-from-different-entrypoints.graphql b/compiler/crates/relay-transforms/tests/validate_unused_variables/fixtures/cycles-read-from-different-entrypoints.graphql new file mode 100644 index 0000000000000..f69e6ca8b5e65 --- /dev/null +++ b/compiler/crates/relay-transforms/tests/validate_unused_variables/fixtures/cycles-read-from-different-entrypoints.graphql @@ -0,0 +1,26 @@ +# Ensures cycles don't result in us caching incomplete used variables for a +# fragment which then gets reused by another query. + +fragment FragmentC on User { + url(relative: true, site: $varX) + nearest_neighbor { + ...FragmentD + } +} + +fragment FragmentD on User { + url(relative: true, site: $varY) + ...FragmentC +} + +query QueryA($varX: String, $varY: String) { + me { + ...FragmentC + } +} + +query QueryB($varX: String, $varY: String) { + me { + ...FragmentD + } +} diff --git a/compiler/crates/relay-transforms/tests/validate_unused_variables/fixtures/fragment-with-root-arguments.expected b/compiler/crates/relay-transforms/tests/validate_unused_variables/fixtures/fragment-with-root-arguments.expected index 64d4277d4c209..b8af49bb37029 100644 --- a/compiler/crates/relay-transforms/tests/validate_unused_variables/fixtures/fragment-with-root-arguments.expected +++ b/compiler/crates/relay-transforms/tests/validate_unused_variables/fixtures/fragment-with-root-arguments.expected @@ -15,4 +15,6 @@ query QueryWithCondition($shouldIncludeName: Boolean!) { } } ==================================== OUTPUT =================================== -OK +OK. + +No Cached Fragments. diff --git a/compiler/crates/relay-transforms/tests/validate_unused_variables/fixtures/practically-unused-but-actually-used-variables.expected b/compiler/crates/relay-transforms/tests/validate_unused_variables/fixtures/practically-unused-but-actually-used-variables.expected index 46dccb465abd1..80e79fbabbfcd 100644 --- a/compiler/crates/relay-transforms/tests/validate_unused_variables/fixtures/practically-unused-but-actually-used-variables.expected +++ b/compiler/crates/relay-transforms/tests/validate_unused_variables/fixtures/practically-unused-but-actually-used-variables.expected @@ -24,4 +24,7 @@ fragment ConnectionFragment on User } } ==================================== OUTPUT =================================== -OK +OK. + +Cached Fragments: +ConnectionFragment -> POPULATED ("unusedAfter, unusedFirst") diff --git a/compiler/crates/relay-transforms/tests/validate_unused_variables/fixtures/query-with-unused-variable-error-suppressed.expected b/compiler/crates/relay-transforms/tests/validate_unused_variables/fixtures/query-with-unused-variable-error-suppressed.expected index aa38771f7f4fd..ead87f895d0b3 100644 --- a/compiler/crates/relay-transforms/tests/validate_unused_variables/fixtures/query-with-unused-variable-error-suppressed.expected +++ b/compiler/crates/relay-transforms/tests/validate_unused_variables/fixtures/query-with-unused-variable-error-suppressed.expected @@ -6,4 +6,6 @@ query QueryWithUnusedVariable($unused: ID) } } ==================================== OUTPUT =================================== -OK +OK. + +No Cached Fragments. diff --git a/compiler/crates/relay-transforms/tests/validate_unused_variables/fixtures/query-with-variables-shadowed-by-local-variable-and-used-as-root-variable.expected b/compiler/crates/relay-transforms/tests/validate_unused_variables/fixtures/query-with-variables-shadowed-by-local-variable-and-used-as-root-variable.expected index af400a5e2341e..48ef47887adfe 100644 --- a/compiler/crates/relay-transforms/tests/validate_unused_variables/fixtures/query-with-variables-shadowed-by-local-variable-and-used-as-root-variable.expected +++ b/compiler/crates/relay-transforms/tests/validate_unused_variables/fixtures/query-with-variables-shadowed-by-local-variable-and-used-as-root-variable.expected @@ -24,4 +24,9 @@ fragment AnotherUser_data on User { } } ==================================== OUTPUT =================================== -OK +OK. + +Cached Fragments: +AnotherUser_data -> POPULATED ("foo") +User_data -> POPULATED ("foo") +User_data_with_args -> POPULATED ("foo") diff --git a/compiler/crates/relay-transforms/tests/validate_unused_variables/fixtures/variable-in-the-complex-object-list.expected b/compiler/crates/relay-transforms/tests/validate_unused_variables/fixtures/variable-in-the-complex-object-list.expected index 4e40efa352417..9ab84bdc63b2f 100644 --- a/compiler/crates/relay-transforms/tests/validate_unused_variables/fixtures/variable-in-the-complex-object-list.expected +++ b/compiler/crates/relay-transforms/tests/validate_unused_variables/fixtures/variable-in-the-complex-object-list.expected @@ -15,4 +15,6 @@ query Q2($size: Int) { } } ==================================== OUTPUT =================================== -OK +OK. + +No Cached Fragments. diff --git a/compiler/crates/relay-transforms/tests/validate_unused_variables_test.rs b/compiler/crates/relay-transforms/tests/validate_unused_variables_test.rs index 0910f691fa962..bf0e366058cd2 100644 --- a/compiler/crates/relay-transforms/tests/validate_unused_variables_test.rs +++ b/compiler/crates/relay-transforms/tests/validate_unused_variables_test.rs @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<2d9c78832993b2bbe4c580b22d0aeecf>> + * @generated SignedSource<<4d8584b4ed41dabea8eb7e0b80ec52a2>> */ mod validate_unused_variables; @@ -12,6 +12,13 @@ mod validate_unused_variables; use validate_unused_variables::transform_fixture; use fixture_tests::test_fixture; +#[tokio::test] +async fn cycles_read_from_different_entrypoints() { + let input = include_str!("validate_unused_variables/fixtures/cycles-read-from-different-entrypoints.graphql"); + let expected = include_str!("validate_unused_variables/fixtures/cycles-read-from-different-entrypoints.expected"); + test_fixture(transform_fixture, file!(), "cycles-read-from-different-entrypoints.graphql", "validate_unused_variables/fixtures/cycles-read-from-different-entrypoints.expected", input, expected).await; +} + #[tokio::test] async fn fragment_with_root_arguments() { let input = include_str!("validate_unused_variables/fixtures/fragment-with-root-arguments.graphql"); diff --git a/compiler/crates/relay-typegen/Cargo.toml b/compiler/crates/relay-typegen/Cargo.toml index 09ec8d813a6f4..5f932463703a8 100644 --- a/compiler/crates/relay-typegen/Cargo.toml +++ b/compiler/crates/relay-typegen/Cargo.toml @@ -4,7 +4,7 @@ name = "relay-typegen" version = "0.0.0" authors = ["Facebook"] -edition = "2021" +edition = "2024" repository = "https://github.com/facebook/relay" license = "MIT" @@ -18,11 +18,11 @@ docblock-shared = { path = "../docblock-shared" } fnv = "1.0" log = { version = "0.4.14", features = ["kv_unstable", "kv_unstable_std"] } graphql-ir = { path = "../graphql-ir" } +indexmap = { version = "2.9.0", features = ["arbitrary", "rayon", "serde"] } graphql-syntax = { path = "../graphql-syntax" } -indexmap = { version = "2.2.6", features = ["arbitrary", "rayon", "serde"] } intern = { path = "../intern" } -itertools = "0.13.0" -lazy_static = "1.4" +itertools = "0.14.0" +lazy_static = "1.5" relay-config = { path = "../relay-config" } relay-schema = { path = "../relay-schema" } relay-transforms = { path = "../relay-transforms" } @@ -32,7 +32,7 @@ schema = { path = "../schema" } fixture-tests = { path = "../fixture-tests" } graphql-syntax = { path = "../graphql-syntax" } graphql-test-helpers = { path = "../graphql-test-helpers" } -regex = "1.9.2" +regex = "1.11.1" relay-codegen = { path = "../relay-codegen" } relay-test-schema = { path = "../relay-test-schema" } -tokio = { version = "1.41.0", features = ["full", "test-util", "tracing"] } +tokio = { version = "1.46.1", features = ["full", "test-util", "tracing"] } diff --git a/compiler/crates/relay-typegen/src/flow.rs b/compiler/crates/relay-typegen/src/flow.rs index a75c9d9636a2d..695ec4d4c6aa8 100644 --- a/compiler/crates/relay-typegen/src/flow.rs +++ b/compiler/crates/relay-typegen/src/flow.rs @@ -11,11 +11,11 @@ use std::fmt::Write; use ::intern::string_key::StringKey; use itertools::Itertools; +use crate::writer::AST; use crate::writer::FunctionTypeAssertion; use crate::writer::KeyValuePairProp; use crate::writer::Prop; use crate::writer::Writer; -use crate::writer::AST; pub struct FlowPrinter { result: String, diff --git a/compiler/crates/relay-typegen/src/javascript.rs b/compiler/crates/relay-typegen/src/javascript.rs index ccdeb20329789..d33c9c0f04d49 100644 --- a/compiler/crates/relay-typegen/src/javascript.rs +++ b/compiler/crates/relay-typegen/src/javascript.rs @@ -8,8 +8,8 @@ use std::fmt::Result as FmtResult; use std::fmt::Write; -use crate::writer::Writer; use crate::writer::AST; +use crate::writer::Writer; #[derive(Default)] pub struct JavaScriptPrinter { diff --git a/compiler/crates/relay-typegen/src/type_selection.rs b/compiler/crates/relay-typegen/src/type_selection.rs index 5d685fae5ace8..3d0efa37d20d3 100644 --- a/compiler/crates/relay-typegen/src/type_selection.rs +++ b/compiler/crates/relay-typegen/src/type_selection.rs @@ -14,11 +14,11 @@ use relay_transforms::TypeConditionInfo; use schema::Type; use schema::TypeReference; -use crate::writer::AST; use crate::JS_FIELD_NAME; use crate::KEY_CLIENTID; use crate::KEY_TYPENAME; use crate::SPREAD_KEY; +use crate::writer::AST; #[derive(Debug, Clone)] pub(crate) enum TypeSelection { diff --git a/compiler/crates/relay-typegen/src/typegen_state.rs b/compiler/crates/relay-typegen/src/typegen_state.rs index f05ef139d359e..c1a2832de4f2b 100644 --- a/compiler/crates/relay-typegen/src/typegen_state.rs +++ b/compiler/crates/relay-typegen/src/typegen_state.rs @@ -15,20 +15,20 @@ use fnv::FnvHashSet; use graphql_ir::FragmentDefinition; use graphql_ir::FragmentDefinitionName; use indexmap::IndexMap; -use intern::string_key::StringKey; use intern::Lookup; +use intern::string_key::StringKey; use schema::EnumID; use schema::SDLSchema; use schema::Schema; -use crate::writer::ExactObject; -use crate::writer::Writer; -use crate::writer::AST; use crate::KEY_DATA_ID; use crate::LIVE_RESOLVERS_LIVE_STATE; use crate::LOCAL_3D_PAYLOAD; use crate::RELAY_RUNTIME; use crate::RESULT_TYPE_NAME; +use crate::writer::AST; +use crate::writer::ExactObject; +use crate::writer::Writer; /// A struct that is mutated as we iterate through an operation/fragment and /// contains information about whether and how to write import types. diff --git a/compiler/crates/relay-typegen/src/typescript.rs b/compiler/crates/relay-typegen/src/typescript.rs index 0410b68276fa4..05a2a3c6bb97b 100644 --- a/compiler/crates/relay-typegen/src/typescript.rs +++ b/compiler/crates/relay-typegen/src/typescript.rs @@ -13,6 +13,10 @@ use intern::intern; use itertools::Itertools; use relay_config::TypegenConfig; +use crate::KEY_DATA; +use crate::KEY_FRAGMENT_SPREADS; +use crate::KEY_FRAGMENT_TYPE; +use crate::writer::AST; use crate::writer::FunctionTypeAssertion; use crate::writer::KeyValuePairProp; use crate::writer::Prop; @@ -20,10 +24,6 @@ use crate::writer::SortedASTList; use crate::writer::SortedStringKeyList; use crate::writer::StringLiteral; use crate::writer::Writer; -use crate::writer::AST; -use crate::KEY_DATA; -use crate::KEY_FRAGMENT_SPREADS; -use crate::KEY_FRAGMENT_TYPE; pub struct TypeScriptPrinter { result: String, diff --git a/compiler/crates/relay-typegen/src/visit.rs b/compiler/crates/relay-typegen/src/visit.rs index dad9e2c7b1e0c..4298772cba9ca 100644 --- a/compiler/crates/relay-typegen/src/visit.rs +++ b/compiler/crates/relay-typegen/src/visit.rs @@ -10,10 +10,10 @@ use std::hash::Hash; use std::path::PathBuf; use std::sync::Arc; +use ::intern::Lookup; use ::intern::intern; use ::intern::string_key::Intern; use ::intern::string_key::StringKey; -use ::intern::Lookup; use common::ArgumentName; use common::DirectiveName; use common::NamedItem; @@ -32,19 +32,23 @@ use graphql_ir::LinkedField; use graphql_ir::OperationDefinition; use graphql_ir::ScalarField; use graphql_ir::Selection; -use indexmap::map::Entry; use indexmap::IndexMap; use indexmap::IndexSet; +use indexmap::map::Entry; use itertools::Itertools; use lazy_static::lazy_static; use relay_config::CustomType; use relay_config::CustomTypeImport; use relay_config::ResolverContextTypeInput; use relay_config::TypegenLanguage; -use relay_schema::definitions::ResolverType; use relay_schema::CUSTOM_SCALAR_DIRECTIVE_NAME; use relay_schema::EXPORT_NAME_CUSTOM_SCALAR_ARGUMENT_NAME; use relay_schema::PATH_CUSTOM_SCALAR_ARGUMENT_NAME; +use relay_schema::definitions::ResolverType; +use relay_transforms::ASSIGNABLE_DIRECTIVE_FOR_TYPEGEN; +use relay_transforms::CATCH_DIRECTIVE_NAME; +use relay_transforms::CHILDREN_CAN_BUBBLE_METADATA_KEY; +use relay_transforms::CLIENT_EXTENSION_DIRECTIVE_NAME; use relay_transforms::CatchMetadataDirective; use relay_transforms::CatchTo; use relay_transforms::ClientEdgeMetadata; @@ -52,16 +56,13 @@ use relay_transforms::FragmentAliasMetadata; use relay_transforms::FragmentDataInjectionMode; use relay_transforms::ModuleMetadata; use relay_transforms::NoInlineFragmentSpreadMetadata; +use relay_transforms::RELAY_ACTOR_CHANGE_DIRECTIVE_FOR_CODEGEN; use relay_transforms::RelayResolverMetadata; use relay_transforms::RequiredMetadataDirective; use relay_transforms::ResolverOutputTypeInfo; use relay_transforms::TypeConditionInfo; -use relay_transforms::ASSIGNABLE_DIRECTIVE_FOR_TYPEGEN; -use relay_transforms::CATCH_DIRECTIVE_NAME; -use relay_transforms::CHILDREN_CAN_BUBBLE_METADATA_KEY; -use relay_transforms::CLIENT_EXTENSION_DIRECTIVE_NAME; -use relay_transforms::RELAY_ACTOR_CHANGE_DIRECTIVE_FOR_CODEGEN; use relay_transforms::UPDATABLE_DIRECTIVE_FOR_TYPEGEN; +use relay_transforms::relay_resolvers::ResolverSchemaGenType; use schema::EnumID; use schema::Field; use schema::ObjectID; @@ -71,6 +72,24 @@ use schema::Schema; use schema::Type; use schema::TypeReference; +use crate::FRAGMENT_PROP_NAME; +use crate::KEY_DATA_ID; +use crate::KEY_FRAGMENT_SPREADS; +use crate::KEY_FRAGMENT_TYPE; +use crate::KEY_TYPENAME; +use crate::KEY_UPDATABLE_FRAGMENT_SPREADS; +use crate::LIVE_STATE_TYPE; +use crate::MODULE_COMPONENT; +use crate::MaskStatus; +use crate::RESPONSE; +use crate::RESULT_TYPE_NAME; +use crate::TYPE_BOOLEAN; +use crate::TYPE_FLOAT; +use crate::TYPE_ID; +use crate::TYPE_INT; +use crate::TYPE_STRING; +use crate::TypegenContext; +use crate::VARIABLES; use crate::type_selection::ModuleDirective; use crate::type_selection::RawResponseFragmentSpread; use crate::type_selection::ScalarFieldSpecialSchemaField; @@ -95,6 +114,7 @@ use crate::typegen_state::MatchFields; use crate::typegen_state::ResolverContextType; use crate::typegen_state::RuntimeImports; use crate::write::CustomScalarsImports; +use crate::writer::AST; use crate::writer::ExactObject; use crate::writer::FunctionTypeAssertion; use crate::writer::GetterSetterPairProp; @@ -105,25 +125,6 @@ use crate::writer::SortedASTList; use crate::writer::SortedStringKeyList; use crate::writer::SpreadProp; use crate::writer::StringLiteral; -use crate::writer::AST; -use crate::MaskStatus; -use crate::TypegenContext; -use crate::FRAGMENT_PROP_NAME; -use crate::KEY_DATA_ID; -use crate::KEY_FRAGMENT_SPREADS; -use crate::KEY_FRAGMENT_TYPE; -use crate::KEY_TYPENAME; -use crate::KEY_UPDATABLE_FRAGMENT_SPREADS; -use crate::LIVE_STATE_TYPE; -use crate::MODULE_COMPONENT; -use crate::RESPONSE; -use crate::RESULT_TYPE_NAME; -use crate::TYPE_BOOLEAN; -use crate::TYPE_FLOAT; -use crate::TYPE_ID; -use crate::TYPE_INT; -use crate::TYPE_STRING; -use crate::VARIABLES; lazy_static! { static ref THROW_ON_FIELD_ERROR_DIRECTIVE: DirectiveName = @@ -641,12 +642,17 @@ fn import_relay_resolver_function_type( None => None, }; - let resolver_type = if resolver_metadata.type_confirmed + let is_property_lookup = match resolver_metadata.resolver_type { + ResolverSchemaGenType::PropertyLookup { .. } => true, + ResolverSchemaGenType::ResolverModule => false, + }; + let resolver_type = if (resolver_metadata.type_confirmed && typegen_context .project_config .feature_flags .omit_resolver_type_assertions_for_confirmed_types - .is_fully_enabled() + .is_fully_enabled()) + || is_property_lookup { None } else { @@ -665,17 +671,19 @@ fn import_relay_resolver_function_type( )) }; - let imported_resolver = ImportedResolver { - resolver_name, - resolver_type, - import_path, - context_import, - }; + if !is_property_lookup { + let imported_resolver = ImportedResolver { + resolver_name, + resolver_type, + import_path, + context_import, + }; - imported_resolvers - .0 - .entry(local_resolver_name) - .or_insert(imported_resolver); + imported_resolvers + .0 + .entry(local_resolver_name) + .or_insert(imported_resolver); + } } /// Check if the scalar field has the special type `RelayResolverValue`. This is a type that @@ -1087,6 +1095,7 @@ fn visit_actor_change( value: AST::Nullable(Box::new(AST::ActorChangePoint(Box::new( selections_to_babel( typegen_context, + &field.type_.inner(), linked_field_selections.into_iter(), MaskStatus::Masked, None, @@ -1289,6 +1298,38 @@ fn visit_scalar_field( })); } +#[allow(clippy::too_many_arguments)] +fn raw_response_visit_condition( + typegen_context: &'_ TypegenContext<'_>, + type_selections: &mut Vec, + condition: &Condition, + encountered_enums: &mut EncounteredEnums, + match_fields: &mut MatchFields, + encountered_fragments: &mut EncounteredFragments, + imported_raw_response_types: &mut ImportedRawResponseTypes, + runtime_imports: &mut RuntimeImports, + custom_scalars: &mut CustomScalarsImports, + enclosing_linked_field_concrete_type: Option, + is_throw_on_field_error: bool, +) { + let mut selections = raw_response_visit_selections( + typegen_context, + &condition.selections, + encountered_enums, + match_fields, + encountered_fragments, + imported_raw_response_types, + runtime_imports, + custom_scalars, + enclosing_linked_field_concrete_type, + is_throw_on_field_error, + ); + for selection in selections.iter_mut() { + selection.set_conditional(true); + } + type_selections.append(&mut selections); +} + #[allow(clippy::too_many_arguments)] fn visit_condition( typegen_context: &'_ TypegenContext<'_>, @@ -1330,6 +1371,7 @@ fn visit_condition( #[allow(clippy::too_many_arguments)] pub(crate) fn get_data_type( typegen_context: &'_ TypegenContext<'_>, + concrete_type: &Type, selections: impl Iterator, mask_status: MaskStatus, fragment_type_name: Option, @@ -1343,6 +1385,7 @@ pub(crate) fn get_data_type( ) -> AST { let mut data_type = selections_to_babel( typegen_context, + concrete_type, selections, mask_status, fragment_type_name, @@ -1364,6 +1407,7 @@ pub(crate) fn get_data_type( #[allow(clippy::too_many_arguments)] fn selections_to_babel( typegen_context: &'_ TypegenContext<'_>, + concrete_type: &Type, selections: impl Iterator, mask_status: MaskStatus, fragment_type_name: Option, @@ -1403,7 +1447,7 @@ fn selections_to_babel( } } - if should_emit_discriminated_union(&by_concrete_type, &base_fields) { + if should_emit_discriminated_union(concrete_type, &by_concrete_type, &base_fields) { get_discriminated_union_ast( by_concrete_type, &base_fields, @@ -1622,11 +1666,15 @@ fn get_discriminated_union_ast( /// /// If this condition passes, we emit a discriminated union fn should_emit_discriminated_union( + concrete_type: &Type, by_concrete_type: &IndexMap>, base_fields: &IndexMap, ) -> bool { - !by_concrete_type.is_empty() - && base_fields.values().all(TypeSelection::is_typename) + if by_concrete_type.is_empty() || !concrete_type.is_abstract_type() { + return false; + } + + base_fields.values().all(TypeSelection::is_typename) && (base_fields.values().any(TypeSelection::is_typename) || by_concrete_type .values() @@ -1851,6 +1899,7 @@ fn make_prop( let getter_object_props = selections_to_babel( typegen_context, + &linked_field.node_type.inner(), no_fragments.into_iter(), mask_status, None, @@ -1942,6 +1991,7 @@ fn make_prop( } else { let object_props = selections_to_babel( typegen_context, + &linked_field.node_type.inner(), hashmap_into_values(linked_field.node_selections), mask_status, None, @@ -2340,20 +2390,19 @@ pub(crate) fn raw_response_visit_selections( enclosing_linked_field_concrete_type, emit_semantic_types, ), - Selection::Condition(condition) => { - type_selections.extend(raw_response_visit_selections( - typegen_context, - &condition.selections, - encountered_enums, - match_fields, - encountered_fragments, - imported_raw_response_types, - runtime_imports, - custom_scalars, - enclosing_linked_field_concrete_type, - emit_semantic_types, - )); - } + Selection::Condition(condition) => raw_response_visit_condition( + typegen_context, + &mut type_selections, + condition, + encountered_enums, + match_fields, + encountered_fragments, + imported_raw_response_types, + runtime_imports, + custom_scalars, + enclosing_linked_field_concrete_type, + emit_semantic_types, + ), } } type_selections diff --git a/compiler/crates/relay-typegen/src/write.rs b/compiler/crates/relay-typegen/src/write.rs index 66d72c1f8e74d..c04c74de48deb 100644 --- a/compiler/crates/relay-typegen/src/write.rs +++ b/compiler/crates/relay-typegen/src/write.rs @@ -9,10 +9,10 @@ use std::collections::HashSet; use std::fmt::Result as FmtResult; use std::path::PathBuf; +use ::intern::Lookup; use ::intern::intern; use ::intern::string_key::Intern; use ::intern::string_key::StringKey; -use ::intern::Lookup; use common::DirectiveName; use common::InputObjectName; use common::NamedItem; @@ -27,14 +27,28 @@ use lazy_static::lazy_static; use relay_config::CustomTypeImport; use relay_config::JsModuleFormat; use relay_config::TypegenLanguage; -use relay_transforms::RefetchableDerivedFromMetadata; -use relay_transforms::RefetchableMetadata; -use relay_transforms::RelayDirective; use relay_transforms::ASSIGNABLE_DIRECTIVE; use relay_transforms::CATCH_DIRECTIVE_NAME; use relay_transforms::CHILDREN_CAN_BUBBLE_METADATA_KEY; +use relay_transforms::RefetchableDerivedFromMetadata; +use relay_transforms::RefetchableMetadata; +use relay_transforms::RelayDirective; use schema::Schema; +use crate::ACTOR_CHANGE_POINT; +use crate::FUTURE_ENUM_VALUE; +use crate::KEY_CLIENTID; +use crate::KEY_DATA; +use crate::KEY_FRAGMENT_SPREADS; +use crate::KEY_FRAGMENT_TYPE; +use crate::KEY_RAW_RESPONSE; +use crate::KEY_TYPENAME; +use crate::KEY_UPDATABLE_FRAGMENT_SPREADS; +use crate::MaskStatus; +use crate::RAW_RESPONSE_TYPE_DIRECTIVE_NAME; +use crate::REACT_RELAY_MULTI_ACTOR; +use crate::TypegenContext; +use crate::VALIDATOR_EXPORT_NAME; use crate::typegen_state::ActorChangeStatus; use crate::typegen_state::EncounteredEnums; use crate::typegen_state::EncounteredFragment; @@ -56,6 +70,7 @@ use crate::visit::raw_response_selections_to_babel; use crate::visit::raw_response_visit_selections; use crate::visit::transform_input_type; use crate::visit::visit_selections; +use crate::writer::AST; use crate::writer::ExactObject; use crate::writer::InexactObject; use crate::writer::KeyValuePairProp; @@ -64,21 +79,6 @@ use crate::writer::SortedASTList; use crate::writer::SortedStringKeyList; use crate::writer::StringLiteral; use crate::writer::Writer; -use crate::writer::AST; -use crate::MaskStatus; -use crate::TypegenContext; -use crate::ACTOR_CHANGE_POINT; -use crate::FUTURE_ENUM_VALUE; -use crate::KEY_CLIENTID; -use crate::KEY_DATA; -use crate::KEY_FRAGMENT_SPREADS; -use crate::KEY_FRAGMENT_TYPE; -use crate::KEY_RAW_RESPONSE; -use crate::KEY_TYPENAME; -use crate::KEY_UPDATABLE_FRAGMENT_SPREADS; -use crate::RAW_RESPONSE_TYPE_DIRECTIVE_NAME; -use crate::REACT_RELAY_MULTI_ACTOR; -use crate::VALIDATOR_EXPORT_NAME; pub(crate) type CustomScalarsImports = HashSet<(StringKey, PathBuf)>; @@ -139,6 +139,7 @@ pub(crate) fn write_operation_type_exports_section( let mut data_type = get_data_type( typegen_context, + &typegen_operation.type_, type_selections.into_iter(), MaskStatus::Masked, // Queries are never unmasked None, @@ -461,6 +462,7 @@ pub(crate) fn write_fragment_type_exports_section( let mut data_type = get_data_type( typegen_context, + &fragment_definition.type_condition, type_selections.into_iter(), mask_status, if mask_status == MaskStatus::Unmasked { @@ -607,12 +609,9 @@ fn write_fragment_imports( ), }; - let should_write_current_referenced_fragment = fragment_name_to_skip - .map_or(true, |fragment_name_to_skip| { - fragment_name_to_skip != current_referenced_fragment - }); - - if !should_write_current_referenced_fragment { + let should_skip_writing_current_referenced_fragment = + fragment_name_to_skip == Some(current_referenced_fragment); + if should_skip_writing_current_referenced_fragment { continue; } @@ -1111,7 +1110,7 @@ fn write_concrete_validator_function( } fn is_plural(node: &FragmentDefinition) -> bool { - RelayDirective::find(&node.directives).map_or(false, |relay_directive| relay_directive.plural) + RelayDirective::find(&node.directives).is_some_and(|relay_directive| relay_directive.plural) } fn has_fragment_spread(selections: &[Selection]) -> bool { diff --git a/compiler/crates/relay-typegen/src/writer.rs b/compiler/crates/relay-typegen/src/writer.rs index 842957c9163ef..a901939afd499 100644 --- a/compiler/crates/relay-typegen/src/writer.rs +++ b/compiler/crates/relay-typegen/src/writer.rs @@ -11,8 +11,8 @@ use std::fmt::Write; use std::ops::Deref; use fnv::FnvHashSet; -use intern::string_key::StringKey; use intern::Lookup; +use intern::string_key::StringKey; use relay_config::TypegenConfig; use relay_config::TypegenLanguage; use schema::Schema; diff --git a/compiler/crates/relay-typegen/tests/generate_flow.rs b/compiler/crates/relay-typegen/tests/generate_flow.rs index 9507fb0015c67..4716e88beacc5 100644 --- a/compiler/crates/relay-typegen/tests/generate_flow.rs +++ b/compiler/crates/relay-typegen/tests/generate_flow.rs @@ -20,15 +20,15 @@ use common::SourceLocationKey; use fixture_tests::Fixture; use fnv::FnvBuildHasher; use fnv::FnvHashMap; -use graphql_ir::build_ir_in_relay_mode; use graphql_ir::OperationDefinitionName; use graphql_ir::Program; +use graphql_ir::build_ir_in_relay_mode; use graphql_syntax::parse_executable; use graphql_test_helpers::diagnostics_to_sorted_string; use indexmap::IndexMap; use regex::Regex; -use relay_codegen::print_provided_variables; use relay_codegen::JsModuleFormat; +use relay_codegen::print_provided_variables; use relay_config::CustomType; use relay_config::CustomTypeImport; use relay_config::ProjectConfig; diff --git a/compiler/crates/relay-typegen/tests/generate_flow/fixtures/fragment-spread.expected b/compiler/crates/relay-typegen/tests/generate_flow/fixtures/fragment-spread.expected index 92307abaf347d..42c38439b5729 100644 --- a/compiler/crates/relay-typegen/tests/generate_flow/fixtures/fragment-spread.expected +++ b/compiler/crates/relay-typegen/tests/generate_flow/fixtures/fragment-spread.expected @@ -113,11 +113,6 @@ declare export opaque type PageFragment$fragmentType: FragmentType; export type PageFragment$data = {| +__typename: "Page", +$fragmentType: PageFragment$fragmentType, -|} | {| - // This will never be '%other', but we need some - // value in case none of the concrete values match. - +__typename: "%other", - +$fragmentType: PageFragment$fragmentType, |}; export type PageFragment$key = { +$data?: PageFragment$data, @@ -130,11 +125,6 @@ declare export opaque type PictureFragment$fragmentType: FragmentType; export type PictureFragment$data = {| +__typename: "Image", +$fragmentType: PictureFragment$fragmentType, -|} | {| - // This will never be '%other', but we need some - // value in case none of the concrete values match. - +__typename: "%other", - +$fragmentType: PictureFragment$fragmentType, |}; export type PictureFragment$key = { +$data?: PictureFragment$data, @@ -147,11 +137,6 @@ declare export opaque type UserFrag1$fragmentType: FragmentType; export type UserFrag1$data = {| +__typename: "User", +$fragmentType: UserFrag1$fragmentType, -|} | {| - // This will never be '%other', but we need some - // value in case none of the concrete values match. - +__typename: "%other", - +$fragmentType: UserFrag1$fragmentType, |}; export type UserFrag1$key = { +$data?: UserFrag1$data, @@ -164,11 +149,6 @@ declare export opaque type UserFrag2$fragmentType: FragmentType; export type UserFrag2$data = {| +__typename: "User", +$fragmentType: UserFrag2$fragmentType, -|} | {| - // This will never be '%other', but we need some - // value in case none of the concrete values match. - +__typename: "%other", - +$fragmentType: UserFrag2$fragmentType, |}; export type UserFrag2$key = { +$data?: UserFrag2$data, diff --git a/compiler/crates/relay-typegen/tests/generate_flow/fixtures/inline-fragment.expected b/compiler/crates/relay-typegen/tests/generate_flow/fixtures/inline-fragment.expected index c865e0610f83d..7758f99ae7f6c 100644 --- a/compiler/crates/relay-typegen/tests/generate_flow/fixtures/inline-fragment.expected +++ b/compiler/crates/relay-typegen/tests/generate_flow/fixtures/inline-fragment.expected @@ -139,11 +139,6 @@ declare export opaque type SomeFragment$fragmentType: FragmentType; export type SomeFragment$data = {| +__typename: "User", +$fragmentType: SomeFragment$fragmentType, -|} | {| - // This will never be '%other', but we need some - // value in case none of the concrete values match. - +__typename: "%other", - +$fragmentType: SomeFragment$fragmentType, |}; export type SomeFragment$key = { +$data?: SomeFragment$data, diff --git a/compiler/crates/relay-typegen/tests/generate_flow/fixtures/mixed-conditonal-raw-response.expected b/compiler/crates/relay-typegen/tests/generate_flow/fixtures/mixed-conditonal-raw-response.expected new file mode 100644 index 0000000000000..a3f67d8970424 --- /dev/null +++ b/compiler/crates/relay-typegen/tests/generate_flow/fixtures/mixed-conditonal-raw-response.expected @@ -0,0 +1,176 @@ +==================================== INPUT ==================================== +query AppQuery($showEmail: Boolean!) @raw_response_type { + # Repro of the issue raised in https://github.com/facebook/relay/issues/4914 + # the `story` key should only be generated once here but it's been generated twice + ...AppFragment + ...AppConditionalFragment +} + +fragment AppFragment on Query { + story { + name + } +} + +fragment AppConditionalFragment on Query { + story @include(if: $showEmail) { + tracking + } +} + +# Query Root +query AppQuery1($showEmail: Boolean!) @raw_response_type @__debug { + story { + name + } + story @include(if: $showEmail) { + tracking + } +} + +# Inline Fragment +query AppQuery2($showEmail: Boolean!) @raw_response_type @__debug { + ... on Query { + story { + name + } + story @include(if: $showEmail) { + tracking + } + } +} + +# Condition +query AppQuery3($showEmail: Boolean!) @raw_response_type @__debug { + ... on Query @include(if: $showEmail) { + story { + name + } + story @include(if: $showEmail) { + tracking + } + } +} +==================================== OUTPUT =================================== +import type { AppConditionalFragment$fragmentType } from "AppConditionalFragment.graphql"; +import type { AppFragment$fragmentType } from "AppFragment.graphql"; +export type AppQuery$variables = {| + showEmail: CustomBoolean, +|}; +export type AppQuery$data = {| + +$fragmentSpreads: AppConditionalFragment$fragmentType & AppFragment$fragmentType, +|}; +export type AppQuery$rawResponse = {| + +story: ?{| + +id: string, + +name: ?string, + |}, + +story?: ?{| + +tracking: ?string, + |}, +|}; +export type AppQuery = {| + rawResponse: AppQuery$rawResponse, + response: AppQuery$data, + variables: AppQuery$variables, +|}; +------------------------------------------------------------------------------- +export type AppQuery1$variables = {| + showEmail: CustomBoolean, +|}; +export type AppQuery1$data = {| + +story: ?{| + +name?: ?string, + +tracking: ?string, + |}, +|}; +export type AppQuery1$rawResponse = {| + +story: ?{| + +id: string, + +name: ?string, + |}, + +story?: ?{| + +tracking: ?string, + |}, +|}; +export type AppQuery1 = {| + rawResponse: AppQuery1$rawResponse, + response: AppQuery1$data, + variables: AppQuery1$variables, +|}; +------------------------------------------------------------------------------- +export type AppQuery2$variables = {| + showEmail: CustomBoolean, +|}; +export type AppQuery2$data = {| + +story: ?{| + +name?: ?string, + +tracking: ?string, + |}, +|}; +export type AppQuery2$rawResponse = {| + +story: ?{| + +id: string, + +name: ?string, + |}, + +story?: ?{| + +tracking: ?string, + |}, +|}; +export type AppQuery2 = {| + rawResponse: AppQuery2$rawResponse, + response: AppQuery2$data, + variables: AppQuery2$variables, +|}; +------------------------------------------------------------------------------- +export type AppQuery3$variables = {| + showEmail: CustomBoolean, +|}; +export type AppQuery3$data = {| + +story?: ?{| + +name?: ?string, + +tracking: ?string, + |}, +|}; +export type AppQuery3$rawResponse = {| + +story?: ?{| + +id: string, + +name: ?string, + |}, + +story?: ?{| + +tracking: ?string, + |}, +|}; +export type AppQuery3 = {| + rawResponse: AppQuery3$rawResponse, + response: AppQuery3$data, + variables: AppQuery3$variables, +|}; +------------------------------------------------------------------------------- +import type { FragmentType } from "relay-runtime"; +declare export opaque type AppConditionalFragment$fragmentType: FragmentType; +export type AppConditionalFragment$data = {| + +story?: ?{| + +tracking: ?string, + |}, + +$fragmentType: AppConditionalFragment$fragmentType, +|}; +export type AppConditionalFragment$key = { + +$data?: AppConditionalFragment$data, + +$fragmentSpreads: AppConditionalFragment$fragmentType, + ... +}; +------------------------------------------------------------------------------- +import type { FragmentType } from "relay-runtime"; +declare export opaque type AppFragment$fragmentType: FragmentType; +export type AppFragment$data = {| + +story: ?{| + +name: ?string, + |}, + +$fragmentType: AppFragment$fragmentType, +|}; +export type AppFragment$key = { + +$data?: AppFragment$data, + +$fragmentSpreads: AppFragment$fragmentType, + ... +}; diff --git a/compiler/crates/relay-typegen/tests/generate_flow/fixtures/mixed-conditonal-raw-response.graphql b/compiler/crates/relay-typegen/tests/generate_flow/fixtures/mixed-conditonal-raw-response.graphql new file mode 100644 index 0000000000000..264facace2b02 --- /dev/null +++ b/compiler/crates/relay-typegen/tests/generate_flow/fixtures/mixed-conditonal-raw-response.graphql @@ -0,0 +1,53 @@ + +query AppQuery($showEmail: Boolean!) @raw_response_type { + # Repro of the issue raised in https://github.com/facebook/relay/issues/4914 + # the `story` key should only be generated once here but it's been generated twice + ...AppFragment + ...AppConditionalFragment +} + +fragment AppFragment on Query { + story { + name + } +} + +fragment AppConditionalFragment on Query { + story @include(if: $showEmail) { + tracking + } +} + +# Query Root +query AppQuery1($showEmail: Boolean!) @raw_response_type @__debug { + story { + name + } + story @include(if: $showEmail) { + tracking + } +} + +# Inline Fragment +query AppQuery2($showEmail: Boolean!) @raw_response_type @__debug { + ... on Query { + story { + name + } + story @include(if: $showEmail) { + tracking + } + } +} + +# Condition +query AppQuery3($showEmail: Boolean!) @raw_response_type @__debug { + ... on Query @include(if: $showEmail) { + story { + name + } + story @include(if: $showEmail) { + tracking + } + } +} diff --git a/compiler/crates/relay-typegen/tests/generate_flow/fixtures/query-with-raw-response-on-conditional.expected b/compiler/crates/relay-typegen/tests/generate_flow/fixtures/query-with-raw-response-on-conditional.expected index 445990cd1e0a7..5963298259101 100644 --- a/compiler/crates/relay-typegen/tests/generate_flow/fixtures/query-with-raw-response-on-conditional.expected +++ b/compiler/crates/relay-typegen/tests/generate_flow/fixtures/query-with-raw-response-on-conditional.expected @@ -29,13 +29,13 @@ export type ExampleQuery$data = {| export type ExampleQuery$rawResponse = {| +node: ?({| +__typename: "User", - +feedback: ?{| + +feedback?: ?{| +id: string, +name: ?string, |}, +id: string, - +lastName: ?string, - +name: ?string, + +lastName?: ?string, + +name?: ?string, |} | {| +__typename: string, +id: string, diff --git a/compiler/crates/relay-typegen/tests/generate_flow/fixtures/relay-resolver-plural-client-edge-with-required-edge.expected b/compiler/crates/relay-typegen/tests/generate_flow/fixtures/relay-resolver-plural-client-edge-with-required-edge.expected index dbe459b6b87e4..d011c9721e97b 100644 --- a/compiler/crates/relay-typegen/tests/generate_flow/fixtures/relay-resolver-plural-client-edge-with-required-edge.expected +++ b/compiler/crates/relay-typegen/tests/generate_flow/fixtures/relay-resolver-plural-client-edge-with-required-edge.expected @@ -5,7 +5,7 @@ fragment relayResolver_BestFriendResolverFragment_name on User { query relayResolver_Query { me { - best_friends @waterfall { + best_friends { name } } @@ -13,24 +13,18 @@ query relayResolver_Query { # %extensions% +type ClientUser { + name: String +} + extend type User { - best_friends: [User!] @relay_resolver(fragment_name: "relayResolver_BestFriendResolverFragment_name", import_path: "./foo/bar/baz/BestFriendResolver.js") + best_friends: [ClientUser!] + @relay_resolver( + fragment_name: "relayResolver_BestFriendResolverFragment_name" + import_path: "./foo/bar/baz/BestFriendResolver.js" + ) } ==================================== OUTPUT =================================== -import type { RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$fragmentType } from "RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends.graphql"; -export type ClientEdgeQuery_relayResolver_Query_me__best_friends$variables = {| - id: string, -|}; -export type ClientEdgeQuery_relayResolver_Query_me__best_friends$data = {| - +node: ?{| - +$fragmentSpreads: RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$fragmentType, - |}, -|}; -export type ClientEdgeQuery_relayResolver_Query_me__best_friends = {| - response: ClientEdgeQuery_relayResolver_Query_me__best_friends$data, - variables: ClientEdgeQuery_relayResolver_Query_me__best_friends$variables, -|}; -------------------------------------------------------------------------------- import type { DataID } from "relay-runtime"; import type { relayResolver_BestFriendResolverFragment_name$key } from "relayResolver_BestFriendResolverFragment_name.graphql"; import userBestFriendsResolverType from "BestFriendResolver"; @@ -55,20 +49,6 @@ export type relayResolver_Query = {| |}; ------------------------------------------------------------------------------- import type { FragmentType } from "relay-runtime"; -declare export opaque type RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$fragmentType: FragmentType; -import type { ClientEdgeQuery_relayResolver_Query_me__best_friends$variables } from "ClientEdgeQuery_relayResolver_Query_me__best_friends.graphql"; -export type RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$data = {| - +id: string, - +name: ?string, - +$fragmentType: RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$fragmentType, -|}; -export type RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$key = { - +$data?: RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$data, - +$fragmentSpreads: RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$fragmentType, - ... -}; -------------------------------------------------------------------------------- -import type { FragmentType } from "relay-runtime"; declare export opaque type relayResolver_BestFriendResolverFragment_name$fragmentType: FragmentType; export type relayResolver_BestFriendResolverFragment_name$data = {| +name: ?string, diff --git a/compiler/crates/relay-typegen/tests/generate_flow/fixtures/relay-resolver-plural-client-edge-with-required-edge.graphql b/compiler/crates/relay-typegen/tests/generate_flow/fixtures/relay-resolver-plural-client-edge-with-required-edge.graphql index d1c5ee3543017..7c3e2e9acc1b2 100644 --- a/compiler/crates/relay-typegen/tests/generate_flow/fixtures/relay-resolver-plural-client-edge-with-required-edge.graphql +++ b/compiler/crates/relay-typegen/tests/generate_flow/fixtures/relay-resolver-plural-client-edge-with-required-edge.graphql @@ -4,7 +4,7 @@ fragment relayResolver_BestFriendResolverFragment_name on User { query relayResolver_Query { me { - best_friends @waterfall { + best_friends { name } } @@ -12,6 +12,14 @@ query relayResolver_Query { # %extensions% +type ClientUser { + name: String +} + extend type User { - best_friends: [User!] @relay_resolver(fragment_name: "relayResolver_BestFriendResolverFragment_name", import_path: "./foo/bar/baz/BestFriendResolver.js") + best_friends: [ClientUser!] + @relay_resolver( + fragment_name: "relayResolver_BestFriendResolverFragment_name" + import_path: "./foo/bar/baz/BestFriendResolver.js" + ) } diff --git a/compiler/crates/relay-typegen/tests/generate_flow/fixtures/relay-resolver-plural-client-edge.expected b/compiler/crates/relay-typegen/tests/generate_flow/fixtures/relay-resolver-plural-client-edge.expected index 3a8df881bba83..74f55feefe7b1 100644 --- a/compiler/crates/relay-typegen/tests/generate_flow/fixtures/relay-resolver-plural-client-edge.expected +++ b/compiler/crates/relay-typegen/tests/generate_flow/fixtures/relay-resolver-plural-client-edge.expected @@ -5,7 +5,7 @@ fragment relayResolver_BestFriendResolverFragment_name on User { query relayResolver_Query { me { - best_friends @waterfall { + best_friends { name } } @@ -13,24 +13,18 @@ query relayResolver_Query { # %extensions% +type ClientUser { + name: String +} + extend type User { - best_friends: [User] @relay_resolver(fragment_name: "relayResolver_BestFriendResolverFragment_name", import_path: "./foo/bar/baz/BestFriendResolver.js") + best_friends: [ClientUser] + @relay_resolver( + fragment_name: "relayResolver_BestFriendResolverFragment_name" + import_path: "./foo/bar/baz/BestFriendResolver.js" + ) } ==================================== OUTPUT =================================== -import type { RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$fragmentType } from "RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends.graphql"; -export type ClientEdgeQuery_relayResolver_Query_me__best_friends$variables = {| - id: string, -|}; -export type ClientEdgeQuery_relayResolver_Query_me__best_friends$data = {| - +node: ?{| - +$fragmentSpreads: RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$fragmentType, - |}, -|}; -export type ClientEdgeQuery_relayResolver_Query_me__best_friends = {| - response: ClientEdgeQuery_relayResolver_Query_me__best_friends$data, - variables: ClientEdgeQuery_relayResolver_Query_me__best_friends$variables, -|}; -------------------------------------------------------------------------------- import type { DataID } from "relay-runtime"; import type { relayResolver_BestFriendResolverFragment_name$key } from "relayResolver_BestFriendResolverFragment_name.graphql"; import userBestFriendsResolverType from "BestFriendResolver"; @@ -55,20 +49,6 @@ export type relayResolver_Query = {| |}; ------------------------------------------------------------------------------- import type { FragmentType } from "relay-runtime"; -declare export opaque type RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$fragmentType: FragmentType; -import type { ClientEdgeQuery_relayResolver_Query_me__best_friends$variables } from "ClientEdgeQuery_relayResolver_Query_me__best_friends.graphql"; -export type RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$data = {| - +id: string, - +name: ?string, - +$fragmentType: RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$fragmentType, -|}; -export type RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$key = { - +$data?: RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$data, - +$fragmentSpreads: RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$fragmentType, - ... -}; -------------------------------------------------------------------------------- -import type { FragmentType } from "relay-runtime"; declare export opaque type relayResolver_BestFriendResolverFragment_name$fragmentType: FragmentType; export type relayResolver_BestFriendResolverFragment_name$data = {| +name: ?string, diff --git a/compiler/crates/relay-typegen/tests/generate_flow/fixtures/relay-resolver-plural-client-edge.graphql b/compiler/crates/relay-typegen/tests/generate_flow/fixtures/relay-resolver-plural-client-edge.graphql index 63c02afd5ec52..db0ebd4589fe7 100644 --- a/compiler/crates/relay-typegen/tests/generate_flow/fixtures/relay-resolver-plural-client-edge.graphql +++ b/compiler/crates/relay-typegen/tests/generate_flow/fixtures/relay-resolver-plural-client-edge.graphql @@ -4,7 +4,7 @@ fragment relayResolver_BestFriendResolverFragment_name on User { query relayResolver_Query { me { - best_friends @waterfall { + best_friends { name } } @@ -12,6 +12,14 @@ query relayResolver_Query { # %extensions% +type ClientUser { + name: String +} + extend type User { - best_friends: [User] @relay_resolver(fragment_name: "relayResolver_BestFriendResolverFragment_name", import_path: "./foo/bar/baz/BestFriendResolver.js") + best_friends: [ClientUser] + @relay_resolver( + fragment_name: "relayResolver_BestFriendResolverFragment_name" + import_path: "./foo/bar/baz/BestFriendResolver.js" + ) } diff --git a/compiler/crates/relay-typegen/tests/generate_flow/fixtures/relay-resolver-plural-required-client-edge-with-required-edge.expected b/compiler/crates/relay-typegen/tests/generate_flow/fixtures/relay-resolver-plural-required-client-edge-with-required-edge.expected index d0f9898a42683..8b5a2986dea14 100644 --- a/compiler/crates/relay-typegen/tests/generate_flow/fixtures/relay-resolver-plural-required-client-edge-with-required-edge.expected +++ b/compiler/crates/relay-typegen/tests/generate_flow/fixtures/relay-resolver-plural-required-client-edge-with-required-edge.expected @@ -5,7 +5,7 @@ fragment relayResolver_BestFriendResolverFragment_name on User { query relayResolver_Query { me { - best_friends @waterfall { + best_friends { name } } @@ -13,24 +13,18 @@ query relayResolver_Query { # %extensions% +type ClientUser { + name: String +} + extend type User { - best_friends: [User!]! @relay_resolver(fragment_name: "relayResolver_BestFriendResolverFragment_name", import_path: "./foo/bar/baz/BestFriendResolver.js") + best_friends: [ClientUser!]! + @relay_resolver( + fragment_name: "relayResolver_BestFriendResolverFragment_name" + import_path: "./foo/bar/baz/BestFriendResolver.js" + ) } ==================================== OUTPUT =================================== -import type { RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$fragmentType } from "RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends.graphql"; -export type ClientEdgeQuery_relayResolver_Query_me__best_friends$variables = {| - id: string, -|}; -export type ClientEdgeQuery_relayResolver_Query_me__best_friends$data = {| - +node: ?{| - +$fragmentSpreads: RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$fragmentType, - |}, -|}; -export type ClientEdgeQuery_relayResolver_Query_me__best_friends = {| - response: ClientEdgeQuery_relayResolver_Query_me__best_friends$data, - variables: ClientEdgeQuery_relayResolver_Query_me__best_friends$variables, -|}; -------------------------------------------------------------------------------- import type { DataID } from "relay-runtime"; import type { relayResolver_BestFriendResolverFragment_name$key } from "relayResolver_BestFriendResolverFragment_name.graphql"; import userBestFriendsResolverType from "BestFriendResolver"; @@ -55,20 +49,6 @@ export type relayResolver_Query = {| |}; ------------------------------------------------------------------------------- import type { FragmentType } from "relay-runtime"; -declare export opaque type RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$fragmentType: FragmentType; -import type { ClientEdgeQuery_relayResolver_Query_me__best_friends$variables } from "ClientEdgeQuery_relayResolver_Query_me__best_friends.graphql"; -export type RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$data = {| - +id: string, - +name: ?string, - +$fragmentType: RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$fragmentType, -|}; -export type RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$key = { - +$data?: RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$data, - +$fragmentSpreads: RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$fragmentType, - ... -}; -------------------------------------------------------------------------------- -import type { FragmentType } from "relay-runtime"; declare export opaque type relayResolver_BestFriendResolverFragment_name$fragmentType: FragmentType; export type relayResolver_BestFriendResolverFragment_name$data = {| +name: ?string, diff --git a/compiler/crates/relay-typegen/tests/generate_flow/fixtures/relay-resolver-plural-required-client-edge-with-required-edge.graphql b/compiler/crates/relay-typegen/tests/generate_flow/fixtures/relay-resolver-plural-required-client-edge-with-required-edge.graphql index ac9752b4046f4..e3a2bb6f955ba 100644 --- a/compiler/crates/relay-typegen/tests/generate_flow/fixtures/relay-resolver-plural-required-client-edge-with-required-edge.graphql +++ b/compiler/crates/relay-typegen/tests/generate_flow/fixtures/relay-resolver-plural-required-client-edge-with-required-edge.graphql @@ -4,7 +4,7 @@ fragment relayResolver_BestFriendResolverFragment_name on User { query relayResolver_Query { me { - best_friends @waterfall { + best_friends { name } } @@ -12,6 +12,14 @@ query relayResolver_Query { # %extensions% +type ClientUser { + name: String +} + extend type User { - best_friends: [User!]! @relay_resolver(fragment_name: "relayResolver_BestFriendResolverFragment_name", import_path: "./foo/bar/baz/BestFriendResolver.js") + best_friends: [ClientUser!]! + @relay_resolver( + fragment_name: "relayResolver_BestFriendResolverFragment_name" + import_path: "./foo/bar/baz/BestFriendResolver.js" + ) } diff --git a/compiler/crates/relay-typegen/tests/generate_flow/fixtures/relay-resolver-plural-required-client-edge.expected b/compiler/crates/relay-typegen/tests/generate_flow/fixtures/relay-resolver-plural-required-client-edge.expected index 84956e9fc5a43..26161ef29372b 100644 --- a/compiler/crates/relay-typegen/tests/generate_flow/fixtures/relay-resolver-plural-required-client-edge.expected +++ b/compiler/crates/relay-typegen/tests/generate_flow/fixtures/relay-resolver-plural-required-client-edge.expected @@ -5,7 +5,7 @@ fragment relayResolver_BestFriendResolverFragment_name on User { query relayResolver_Query { me { - best_friends @waterfall { + best_friends { name } } @@ -13,24 +13,18 @@ query relayResolver_Query { # %extensions% +type ClientUser { + name: String +} + extend type User { - best_friends: [User]! @relay_resolver(fragment_name: "relayResolver_BestFriendResolverFragment_name", import_path: "./foo/bar/baz/BestFriendResolver.js") + best_friends: [ClientUser]! + @relay_resolver( + fragment_name: "relayResolver_BestFriendResolverFragment_name" + import_path: "./foo/bar/baz/BestFriendResolver.js" + ) } ==================================== OUTPUT =================================== -import type { RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$fragmentType } from "RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends.graphql"; -export type ClientEdgeQuery_relayResolver_Query_me__best_friends$variables = {| - id: string, -|}; -export type ClientEdgeQuery_relayResolver_Query_me__best_friends$data = {| - +node: ?{| - +$fragmentSpreads: RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$fragmentType, - |}, -|}; -export type ClientEdgeQuery_relayResolver_Query_me__best_friends = {| - response: ClientEdgeQuery_relayResolver_Query_me__best_friends$data, - variables: ClientEdgeQuery_relayResolver_Query_me__best_friends$variables, -|}; -------------------------------------------------------------------------------- import type { DataID } from "relay-runtime"; import type { relayResolver_BestFriendResolverFragment_name$key } from "relayResolver_BestFriendResolverFragment_name.graphql"; import userBestFriendsResolverType from "BestFriendResolver"; @@ -55,20 +49,6 @@ export type relayResolver_Query = {| |}; ------------------------------------------------------------------------------- import type { FragmentType } from "relay-runtime"; -declare export opaque type RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$fragmentType: FragmentType; -import type { ClientEdgeQuery_relayResolver_Query_me__best_friends$variables } from "ClientEdgeQuery_relayResolver_Query_me__best_friends.graphql"; -export type RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$data = {| - +id: string, - +name: ?string, - +$fragmentType: RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$fragmentType, -|}; -export type RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$key = { - +$data?: RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$data, - +$fragmentSpreads: RefetchableClientEdgeQuery_relayResolver_Query_me__best_friends$fragmentType, - ... -}; -------------------------------------------------------------------------------- -import type { FragmentType } from "relay-runtime"; declare export opaque type relayResolver_BestFriendResolverFragment_name$fragmentType: FragmentType; export type relayResolver_BestFriendResolverFragment_name$data = {| +name: ?string, diff --git a/compiler/crates/relay-typegen/tests/generate_flow/fixtures/relay-resolver-plural-required-client-edge.graphql b/compiler/crates/relay-typegen/tests/generate_flow/fixtures/relay-resolver-plural-required-client-edge.graphql index 2151c70bc603b..dd5ce21d14303 100644 --- a/compiler/crates/relay-typegen/tests/generate_flow/fixtures/relay-resolver-plural-required-client-edge.graphql +++ b/compiler/crates/relay-typegen/tests/generate_flow/fixtures/relay-resolver-plural-required-client-edge.graphql @@ -4,7 +4,7 @@ fragment relayResolver_BestFriendResolverFragment_name on User { query relayResolver_Query { me { - best_friends @waterfall { + best_friends { name } } @@ -12,6 +12,14 @@ query relayResolver_Query { # %extensions% +type ClientUser { + name: String +} + extend type User { - best_friends: [User]! @relay_resolver(fragment_name: "relayResolver_BestFriendResolverFragment_name", import_path: "./foo/bar/baz/BestFriendResolver.js") + best_friends: [ClientUser]! + @relay_resolver( + fragment_name: "relayResolver_BestFriendResolverFragment_name" + import_path: "./foo/bar/baz/BestFriendResolver.js" + ) } diff --git a/compiler/crates/relay-typegen/tests/generate_flow_test.rs b/compiler/crates/relay-typegen/tests/generate_flow_test.rs index 43804434c77d7..b4413c90c643c 100644 --- a/compiler/crates/relay-typegen/tests/generate_flow_test.rs +++ b/compiler/crates/relay-typegen/tests/generate_flow_test.rs @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<<7c4bbd43433461799f4caf7a6320fa4e>> + * @generated SignedSource<<1362eec89123512c826effa9bf3a99ec>> */ mod generate_flow; @@ -271,6 +271,13 @@ async fn match_field_in_query() { test_fixture(transform_fixture, file!(), "match-field-in-query.graphql", "generate_flow/fixtures/match-field-in-query.expected", input, expected).await; } +#[tokio::test] +async fn mixed_conditonal_raw_response() { + let input = include_str!("generate_flow/fixtures/mixed-conditonal-raw-response.graphql"); + let expected = include_str!("generate_flow/fixtures/mixed-conditonal-raw-response.expected"); + test_fixture(transform_fixture, file!(), "mixed-conditonal-raw-response.graphql", "generate_flow/fixtures/mixed-conditonal-raw-response.expected", input, expected).await; +} + #[tokio::test] async fn mutation() { let input = include_str!("generate_flow/fixtures/mutation.graphql"); diff --git a/compiler/crates/relay-typegen/tests/generate_flow_with_custom_id.rs b/compiler/crates/relay-typegen/tests/generate_flow_with_custom_id.rs index 51627afe1407a..9793ff937605f 100644 --- a/compiler/crates/relay-typegen/tests/generate_flow_with_custom_id.rs +++ b/compiler/crates/relay-typegen/tests/generate_flow_with_custom_id.rs @@ -15,14 +15,14 @@ use common::SourceLocationKey; use fixture_tests::Fixture; use fnv::FnvBuildHasher; use fnv::FnvHashMap; -use graphql_ir::build_ir_in_relay_mode; use graphql_ir::OperationDefinitionName; use graphql_ir::Program; +use graphql_ir::build_ir_in_relay_mode; use graphql_syntax::parse_executable; use indexmap::IndexMap; use intern::string_key::Intern; -use relay_codegen::print_provided_variables; use relay_codegen::JsModuleFormat; +use relay_codegen::print_provided_variables; use relay_config::ProjectConfig; use relay_config::ProjectName; use relay_config::SchemaConfig; diff --git a/compiler/crates/relay-typegen/tests/generate_typescript.rs b/compiler/crates/relay-typegen/tests/generate_typescript.rs index e234eb6762776..5e10731be7b90 100644 --- a/compiler/crates/relay-typegen/tests/generate_typescript.rs +++ b/compiler/crates/relay-typegen/tests/generate_typescript.rs @@ -19,14 +19,14 @@ use common::SourceLocationKey; use fixture_tests::Fixture; use fnv::FnvBuildHasher; use fnv::FnvHashMap; -use graphql_ir::build; use graphql_ir::OperationDefinitionName; use graphql_ir::Program; +use graphql_ir::build; use graphql_syntax::parse_executable; use indexmap::IndexMap; use regex::Regex; -use relay_codegen::print_provided_variables; use relay_codegen::JsModuleFormat; +use relay_codegen::print_provided_variables; use relay_config::CustomType; use relay_config::CustomTypeImport; use relay_config::ProjectConfig; diff --git a/compiler/crates/relay-typegen/tests/generate_typescript/fixtures/fragment-spread.expected b/compiler/crates/relay-typegen/tests/generate_typescript/fixtures/fragment-spread.expected index c97ede67cd471..29e06b56fab8d 100644 --- a/compiler/crates/relay-typegen/tests/generate_typescript/fixtures/fragment-spread.expected +++ b/compiler/crates/relay-typegen/tests/generate_typescript/fixtures/fragment-spread.expected @@ -101,11 +101,6 @@ import { FragmentRefs } from "relay-runtime"; export type PageFragment$data = { readonly __typename: "Page"; readonly " $fragmentType": "PageFragment"; -} | { - // This will never be '%other', but we need some - // value in case none of the concrete values match. - readonly __typename: "%other"; - readonly " $fragmentType": "PageFragment"; }; export type PageFragment$key = { readonly " $data"?: PageFragment$data; @@ -116,11 +111,6 @@ import { FragmentRefs } from "relay-runtime"; export type PictureFragment$data = { readonly __typename: "Image"; readonly " $fragmentType": "PictureFragment"; -} | { - // This will never be '%other', but we need some - // value in case none of the concrete values match. - readonly __typename: "%other"; - readonly " $fragmentType": "PictureFragment"; }; export type PictureFragment$key = { readonly " $data"?: PictureFragment$data; @@ -131,11 +121,6 @@ import { FragmentRefs } from "relay-runtime"; export type UserFrag1$data = { readonly __typename: "User"; readonly " $fragmentType": "UserFrag1"; -} | { - // This will never be '%other', but we need some - // value in case none of the concrete values match. - readonly __typename: "%other"; - readonly " $fragmentType": "UserFrag1"; }; export type UserFrag1$key = { readonly " $data"?: UserFrag1$data; @@ -146,11 +131,6 @@ import { FragmentRefs } from "relay-runtime"; export type UserFrag2$data = { readonly __typename: "User"; readonly " $fragmentType": "UserFrag2"; -} | { - // This will never be '%other', but we need some - // value in case none of the concrete values match. - readonly __typename: "%other"; - readonly " $fragmentType": "UserFrag2"; }; export type UserFrag2$key = { readonly " $data"?: UserFrag2$data; diff --git a/compiler/crates/relay-typegen/tests/generate_typescript/fixtures/inline-fragment.expected b/compiler/crates/relay-typegen/tests/generate_typescript/fixtures/inline-fragment.expected index a68de1296e88c..7b07ca864fe6b 100644 --- a/compiler/crates/relay-typegen/tests/generate_typescript/fixtures/inline-fragment.expected +++ b/compiler/crates/relay-typegen/tests/generate_typescript/fixtures/inline-fragment.expected @@ -129,11 +129,6 @@ import { FragmentRefs } from "relay-runtime"; export type SomeFragment$data = { readonly __typename: "User"; readonly " $fragmentType": "SomeFragment"; -} | { - // This will never be '%other', but we need some - // value in case none of the concrete values match. - readonly __typename: "%other"; - readonly " $fragmentType": "SomeFragment"; }; export type SomeFragment$key = { readonly " $data"?: SomeFragment$data; diff --git a/compiler/crates/relay-typegen/tests/generate_typescript/fixtures/query-with-raw-response-on-conditional.expected b/compiler/crates/relay-typegen/tests/generate_typescript/fixtures/query-with-raw-response-on-conditional.expected index 777565655be7b..4cd30d9a68dc7 100644 --- a/compiler/crates/relay-typegen/tests/generate_typescript/fixtures/query-with-raw-response-on-conditional.expected +++ b/compiler/crates/relay-typegen/tests/generate_typescript/fixtures/query-with-raw-response-on-conditional.expected @@ -29,13 +29,13 @@ export type ExampleQuery$data = { export type ExampleQuery$rawResponse = { readonly node: { readonly __typename: "User"; - readonly feedback: { + readonly feedback?: { readonly id: string; readonly name: string | null | undefined; } | null | undefined; readonly id: string; - readonly lastName: string | null | undefined; - readonly name: string | null | undefined; + readonly lastName?: string | null | undefined; + readonly name?: string | null | undefined; } | { readonly __typename: string; readonly id: string; diff --git a/compiler/crates/resolution-path/Cargo.toml b/compiler/crates/resolution-path/Cargo.toml index aabff0726eeca..c78840d9423f1 100644 --- a/compiler/crates/resolution-path/Cargo.toml +++ b/compiler/crates/resolution-path/Cargo.toml @@ -4,7 +4,7 @@ name = "resolution-path" version = "0.0.0" authors = ["Facebook"] -edition = "2021" +edition = "2024" repository = "https://github.com/facebook/relay" license = "MIT" diff --git a/compiler/crates/resolution-path/src/lib.rs b/compiler/crates/resolution-path/src/lib.rs index 05c3b9743368c..f91683f92574e 100644 --- a/compiler/crates/resolution-path/src/lib.rs +++ b/compiler/crates/resolution-path/src/lib.rs @@ -188,7 +188,7 @@ pub trait ResolvePosition<'a>: Sized { type Parent; fn resolve(&'a self, parent: Self::Parent, position: Span) -> ResolutionPath<'a>; fn contains(&'a self, position: Span) -> bool; - fn path(&'a self, parent: Self::Parent) -> Path<&Self, Self::Parent> { + fn path(&'a self, parent: Self::Parent) -> Path<&'a Self, Self::Parent> { Path { inner: self, parent, diff --git a/compiler/crates/resolution-path/src/selection_parent_type.rs b/compiler/crates/resolution-path/src/selection_parent_type.rs index 7790ecbb504b5..9b08afe526d40 100644 --- a/compiler/crates/resolution-path/src/selection_parent_type.rs +++ b/compiler/crates/resolution-path/src/selection_parent_type.rs @@ -111,9 +111,9 @@ impl<'a> SelectionParent<'a> { mod tests { use common::SourceLocationKey; use common::Span; - use graphql_syntax::parse_executable_with_features; use graphql_syntax::FragmentArgumentSyntaxKind; use graphql_syntax::ParserFeatures; + use graphql_syntax::parse_executable_with_features; use intern::string_key::Intern; use intern::string_key::StringKey; use relay_test_schema::get_test_schema; @@ -133,6 +133,7 @@ mod tests { ParserFeatures { fragment_argument_capability: FragmentArgumentSyntaxKind::SpreadArgumentsAndFragmentVariableDefinitions, + allow_string_literal_alias: false, }, ) .unwrap(); diff --git a/compiler/crates/resolution-path/src/test.rs b/compiler/crates/resolution-path/src/test.rs index 5b45247ae41d1..ec775fe3bd48e 100644 --- a/compiler/crates/resolution-path/src/test.rs +++ b/compiler/crates/resolution-path/src/test.rs @@ -7,10 +7,10 @@ use common::SourceLocationKey; use common::Span; -use graphql_syntax::parse_executable_with_features; -use graphql_syntax::parse_schema_document; use graphql_syntax::FragmentArgumentSyntaxKind; use graphql_syntax::ParserFeatures; +use graphql_syntax::parse_executable_with_features; +use graphql_syntax::parse_schema_document; use super::*; @@ -21,6 +21,7 @@ pub(super) fn test_resolution(source: &str, sub_str: &str, cb: impl Fn(&Resoluti ParserFeatures { fragment_argument_capability: FragmentArgumentSyntaxKind::SpreadArgumentsAndFragmentVariableDefinitions, + allow_string_literal_alias: false, }, ) .unwrap(); diff --git a/compiler/crates/schema-diff/Cargo.toml b/compiler/crates/schema-diff/Cargo.toml index 921d92ceb5eba..673448553e743 100644 --- a/compiler/crates/schema-diff/Cargo.toml +++ b/compiler/crates/schema-diff/Cargo.toml @@ -4,7 +4,7 @@ name = "schema-diff" version = "0.0.0" authors = ["Facebook"] -edition = "2021" +edition = "2024" repository = "https://github.com/facebook/relay" license = "MIT" @@ -17,7 +17,7 @@ common = { path = "../common" } fnv = "1.0" graphql-syntax = { path = "../graphql-syntax" } intern = { path = "../intern" } -lazy_static = "1.4" +lazy_static = "1.5" relay-config = { path = "../relay-config" } -rustc-hash = "1.1.0" +rustc-hash = "2.1.1" schema = { path = "../schema" } diff --git a/compiler/crates/schema-diff/src/lib.rs b/compiler/crates/schema-diff/src/lib.rs index f60e7ecfed8ff..46392fbaf89ac 100644 --- a/compiler/crates/schema-diff/src/lib.rs +++ b/compiler/crates/schema-diff/src/lib.rs @@ -14,7 +14,6 @@ pub mod definitions; use common::SourceLocationKey; use fnv::FnvHashMap; use fnv::FnvHashSet; -use graphql_syntax::parse_schema_document; use graphql_syntax::EnumTypeDefinition; use graphql_syntax::FieldDefinition; use graphql_syntax::Identifier; @@ -26,6 +25,7 @@ use graphql_syntax::ObjectTypeDefinition; use graphql_syntax::ScalarTypeDefinition; use graphql_syntax::TypeSystemDefinition; use graphql_syntax::UnionTypeDefinition; +use graphql_syntax::parse_schema_document; use intern::string_key::StringKey; use crate::definitions::*; diff --git a/compiler/crates/schema-diff/tests/diff_schema_tests.rs b/compiler/crates/schema-diff/tests/diff_schema_tests.rs index d289134622d52..f498ecc97d28d 100644 --- a/compiler/crates/schema-diff/tests/diff_schema_tests.rs +++ b/compiler/crates/schema-diff/tests/diff_schema_tests.rs @@ -1445,36 +1445,24 @@ fn sort_change(change: &mut SchemaChange) { changes.sort(); for c in changes { match c { - DefinitionChange::UnionChanged { - ref mut added, - ref mut removed, - .. - } => { + DefinitionChange::UnionChanged { added, removed, .. } => { added.sort(); removed.sort(); } - DefinitionChange::InputObjectChanged { - ref mut added, - ref mut removed, - .. - } => { + DefinitionChange::InputObjectChanged { added, removed, .. } => { added.sort_by_key(|item| item.name); removed.sort_by_key(|item| item.name); } - DefinitionChange::InterfaceChanged { - ref mut added, - ref mut removed, - .. - } => { + DefinitionChange::InterfaceChanged { added, removed, .. } => { added.sort_by_key(|item| item.name); removed.sort_by_key(|item| item.name); } DefinitionChange::ObjectChanged { - ref mut added, - ref mut removed, - ref mut changed, - ref mut interfaces_added, - ref mut interfaces_removed, + added, + removed, + changed, + interfaces_added, + interfaces_removed, .. } => { added.sort_by_key(|item| item.name); diff --git a/compiler/crates/schema-documentation/Cargo.toml b/compiler/crates/schema-documentation/Cargo.toml index 1422134541cf4..af1cb7f54ce28 100644 --- a/compiler/crates/schema-documentation/Cargo.toml +++ b/compiler/crates/schema-documentation/Cargo.toml @@ -4,7 +4,7 @@ name = "schema-documentation" version = "0.0.0" authors = ["Facebook"] -edition = "2021" +edition = "2024" repository = "https://github.com/facebook/relay" license = "MIT" diff --git a/compiler/crates/schema-documentation/src/sdl_schema_impl.rs b/compiler/crates/schema-documentation/src/sdl_schema_impl.rs index 100b80b3f7a8f..d0c1cbda7a8d7 100644 --- a/compiler/crates/schema-documentation/src/sdl_schema_impl.rs +++ b/compiler/crates/schema-documentation/src/sdl_schema_impl.rs @@ -5,9 +5,9 @@ * LICENSE file in the root directory of this source tree. */ +use intern::Lookup; use intern::string_key::Intern; use intern::string_key::StringKey; -use intern::Lookup; use schema::Field; use schema::SDLSchema; use schema::Schema; diff --git a/compiler/crates/schema-extractor/Cargo.toml b/compiler/crates/schema-extractor/Cargo.toml index ee5a3b4ecc69b..1179007ee44a2 100644 --- a/compiler/crates/schema-extractor/Cargo.toml +++ b/compiler/crates/schema-extractor/Cargo.toml @@ -4,7 +4,7 @@ name = "schema-extractor" version = "0.0.0" authors = ["Facebook"] -edition = "2021" +edition = "2024" repository = "https://github.com/facebook/relay" license = "MIT" @@ -14,6 +14,6 @@ hermes_comments = { git = "https://github.com/facebook/hermes.git" } hermes_estree = { git = "https://github.com/facebook/hermes.git" } hermes_parser = { git = "https://github.com/facebook/hermes.git" } intern = { path = "../intern" } -rustc-hash = "1.1.0" -serde = { version = "1.0.185", features = ["derive", "rc"] } -thiserror = "1.0.64" +rustc-hash = "2.1.1" +serde = { version = "1.0.219", features = ["derive", "rc"] } +thiserror = "2.0.12" diff --git a/compiler/crates/schema-flatbuffer/Cargo.toml b/compiler/crates/schema-flatbuffer/Cargo.toml index 025d79633b647..a4961e98005a7 100644 --- a/compiler/crates/schema-flatbuffer/Cargo.toml +++ b/compiler/crates/schema-flatbuffer/Cargo.toml @@ -4,9 +4,9 @@ name = "schema-flatbuffer" version = "0.0.0" authors = ["Facebook"] -edition = "2021" +edition = "2024" repository = "https://github.com/facebook/relay" license = "MIT" [dependencies] -flatbuffers = "2.0" +flatbuffers = "25.2.10" diff --git a/compiler/crates/schema-flatbuffer/generate_flatbuffer.py b/compiler/crates/schema-flatbuffer/generate_flatbuffer.py index 3cfa703afab50..0cde77e76614b 100755 --- a/compiler/crates/schema-flatbuffer/generate_flatbuffer.py +++ b/compiler/crates/schema-flatbuffer/generate_flatbuffer.py @@ -42,9 +42,7 @@ * * \x40generated */ -""".format( - flatc_version -) +""".format(flatc_version) content = content.replace(old_header, new_header) diff --git a/compiler/crates/schema-flatbuffer/src/graphqlschema_generated.rs b/compiler/crates/schema-flatbuffer/src/graphqlschema_generated.rs index d283599aa5861..85cf9a5b0784d 100644 --- a/compiler/crates/schema-flatbuffer/src/graphqlschema_generated.rs +++ b/compiler/crates/schema-flatbuffer/src/graphqlschema_generated.rs @@ -10,7 +10,7 @@ * ./generate_flatbuffer.py * * Using: - * flatc version 2.0.8 + * flatc version 25.2.10 * * NOTE: The script requires `flatc` in the path which can be installed * using `brew install flatbuffers` or similar. @@ -18,29 +18,37 @@ * @generated */ -// @generated - -use core::mem; use core::cmp::Ordering; +use core::mem; extern crate flatbuffers; -use self::flatbuffers::{EndianScalar, Follow}; +use self::flatbuffers::EndianScalar; +use self::flatbuffers::Follow; -#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[deprecated( + since = "2.0.0", + note = "Use associated constants instead. This will no longer be generated in 2021." +)] pub const ENUM_MIN_CONST_VALUE_KIND: i8 = 0; -#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[deprecated( + since = "2.0.0", + note = "Use associated constants instead. This will no longer be generated in 2021." +)] pub const ENUM_MAX_CONST_VALUE_KIND: i8 = 7; -#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[deprecated( + since = "2.0.0", + note = "Use associated constants instead. This will no longer be generated in 2021." +)] #[allow(non_camel_case_types)] pub const ENUM_VALUES_CONST_VALUE_KIND: [ConstValueKind; 8] = [ - ConstValueKind::Null, - ConstValueKind::String, - ConstValueKind::Bool, - ConstValueKind::Int, - ConstValueKind::Float, - ConstValueKind::Enum, - ConstValueKind::List, - ConstValueKind::Object, + ConstValueKind::Null, + ConstValueKind::String, + ConstValueKind::Bool, + ConstValueKind::Int, + ConstValueKind::Float, + ConstValueKind::Enum, + ConstValueKind::List, + ConstValueKind::Object, ]; #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] @@ -48,108 +56,116 @@ pub const ENUM_VALUES_CONST_VALUE_KIND: [ConstValueKind; 8] = [ pub struct ConstValueKind(pub i8); #[allow(non_upper_case_globals)] impl ConstValueKind { - pub const Null: Self = Self(0); - pub const String: Self = Self(1); - pub const Bool: Self = Self(2); - pub const Int: Self = Self(3); - pub const Float: Self = Self(4); - pub const Enum: Self = Self(5); - pub const List: Self = Self(6); - pub const Object: Self = Self(7); - - pub const ENUM_MIN: i8 = 0; - pub const ENUM_MAX: i8 = 7; - pub const ENUM_VALUES: &'static [Self] = &[ - Self::Null, - Self::String, - Self::Bool, - Self::Int, - Self::Float, - Self::Enum, - Self::List, - Self::Object, - ]; - /// Returns the variant's name or "" if unknown. - pub fn variant_name(self) -> Option<&'static str> { - match self { - Self::Null => Some("Null"), - Self::String => Some("String"), - Self::Bool => Some("Bool"), - Self::Int => Some("Int"), - Self::Float => Some("Float"), - Self::Enum => Some("Enum"), - Self::List => Some("List"), - Self::Object => Some("Object"), - _ => None, - } - } + pub const Null: Self = Self(0); + pub const String: Self = Self(1); + pub const Bool: Self = Self(2); + pub const Int: Self = Self(3); + pub const Float: Self = Self(4); + pub const Enum: Self = Self(5); + pub const List: Self = Self(6); + pub const Object: Self = Self(7); + + pub const ENUM_MIN: i8 = 0; + pub const ENUM_MAX: i8 = 7; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::Null, + Self::String, + Self::Bool, + Self::Int, + Self::Float, + Self::Enum, + Self::List, + Self::Object, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::Null => Some("Null"), + Self::String => Some("String"), + Self::Bool => Some("Bool"), + Self::Int => Some("Int"), + Self::Float => Some("Float"), + Self::Enum => Some("Enum"), + Self::List => Some("List"), + Self::Object => Some("Object"), + _ => None, + } + } } impl core::fmt::Debug for ConstValueKind { - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - if let Some(name) = self.variant_name() { - f.write_str(name) - } else { - f.write_fmt(format_args!("", self.0)) + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } } - } } impl<'a> flatbuffers::Follow<'a> for ConstValueKind { - type Inner = Self; - #[inline] - fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { - let b = unsafe { - flatbuffers::read_scalar_at::(buf, loc) - }; - Self(b) - } + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = flatbuffers::read_scalar_at::(buf, loc); + Self(b) + } } impl flatbuffers::Push for ConstValueKind { type Output = ConstValueKind; #[inline] - fn push(&self, dst: &mut [u8], _rest: &[u8]) { - unsafe { flatbuffers::emplace_scalar::(dst, self.0); } + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + flatbuffers::emplace_scalar::(dst, self.0); } } impl flatbuffers::EndianScalar for ConstValueKind { - #[inline] - fn to_little_endian(self) -> Self { - let b = i8::to_le(self.0); - Self(b) - } - #[inline] - #[allow(clippy::wrong_self_convention)] - fn from_little_endian(self) -> Self { - let b = i8::from_le(self.0); - Self(b) - } + type Scalar = i8; + #[inline] + fn to_little_endian(self) -> i8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: i8) -> Self { + let b = i8::from_le(v); + Self(b) + } } impl<'a> flatbuffers::Verifiable for ConstValueKind { - #[inline] - fn run_verifier( - v: &mut flatbuffers::Verifier, pos: usize - ) -> Result<(), flatbuffers::InvalidFlatbuffer> { - use self::flatbuffers::Verifiable; - i8::run_verifier(v, pos) - } + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + i8::run_verifier(v, pos) + } } impl flatbuffers::SimpleToVerifyInSlice for ConstValueKind {} -#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[deprecated( + since = "2.0.0", + note = "Use associated constants instead. This will no longer be generated in 2021." +)] pub const ENUM_MIN_TYPE_KIND: i8 = 0; -#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[deprecated( + since = "2.0.0", + note = "Use associated constants instead. This will no longer be generated in 2021." +)] pub const ENUM_MAX_TYPE_KIND: i8 = 5; -#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[deprecated( + since = "2.0.0", + note = "Use associated constants instead. This will no longer be generated in 2021." +)] #[allow(non_camel_case_types)] pub const ENUM_VALUES_TYPE_KIND: [TypeKind; 6] = [ - TypeKind::Scalar, - TypeKind::InputObject, - TypeKind::Enum, - TypeKind::Object, - TypeKind::Interface, - TypeKind::Union, + TypeKind::Scalar, + TypeKind::InputObject, + TypeKind::Enum, + TypeKind::Object, + TypeKind::Interface, + TypeKind::Union, ]; #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] @@ -157,99 +173,107 @@ pub const ENUM_VALUES_TYPE_KIND: [TypeKind; 6] = [ pub struct TypeKind(pub i8); #[allow(non_upper_case_globals)] impl TypeKind { - pub const Scalar: Self = Self(0); - pub const InputObject: Self = Self(1); - pub const Enum: Self = Self(2); - pub const Object: Self = Self(3); - pub const Interface: Self = Self(4); - pub const Union: Self = Self(5); - - pub const ENUM_MIN: i8 = 0; - pub const ENUM_MAX: i8 = 5; - pub const ENUM_VALUES: &'static [Self] = &[ - Self::Scalar, - Self::InputObject, - Self::Enum, - Self::Object, - Self::Interface, - Self::Union, - ]; - /// Returns the variant's name or "" if unknown. - pub fn variant_name(self) -> Option<&'static str> { - match self { - Self::Scalar => Some("Scalar"), - Self::InputObject => Some("InputObject"), - Self::Enum => Some("Enum"), - Self::Object => Some("Object"), - Self::Interface => Some("Interface"), - Self::Union => Some("Union"), - _ => None, - } - } + pub const Scalar: Self = Self(0); + pub const InputObject: Self = Self(1); + pub const Enum: Self = Self(2); + pub const Object: Self = Self(3); + pub const Interface: Self = Self(4); + pub const Union: Self = Self(5); + + pub const ENUM_MIN: i8 = 0; + pub const ENUM_MAX: i8 = 5; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::Scalar, + Self::InputObject, + Self::Enum, + Self::Object, + Self::Interface, + Self::Union, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::Scalar => Some("Scalar"), + Self::InputObject => Some("InputObject"), + Self::Enum => Some("Enum"), + Self::Object => Some("Object"), + Self::Interface => Some("Interface"), + Self::Union => Some("Union"), + _ => None, + } + } } impl core::fmt::Debug for TypeKind { - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - if let Some(name) = self.variant_name() { - f.write_str(name) - } else { - f.write_fmt(format_args!("", self.0)) + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } } - } } impl<'a> flatbuffers::Follow<'a> for TypeKind { - type Inner = Self; - #[inline] - fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { - let b = unsafe { - flatbuffers::read_scalar_at::(buf, loc) - }; - Self(b) - } + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = flatbuffers::read_scalar_at::(buf, loc); + Self(b) + } } impl flatbuffers::Push for TypeKind { type Output = TypeKind; #[inline] - fn push(&self, dst: &mut [u8], _rest: &[u8]) { - unsafe { flatbuffers::emplace_scalar::(dst, self.0); } + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + flatbuffers::emplace_scalar::(dst, self.0); } } impl flatbuffers::EndianScalar for TypeKind { - #[inline] - fn to_little_endian(self) -> Self { - let b = i8::to_le(self.0); - Self(b) - } - #[inline] - #[allow(clippy::wrong_self_convention)] - fn from_little_endian(self) -> Self { - let b = i8::from_le(self.0); - Self(b) - } + type Scalar = i8; + #[inline] + fn to_little_endian(self) -> i8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: i8) -> Self { + let b = i8::from_le(v); + Self(b) + } } impl<'a> flatbuffers::Verifiable for TypeKind { - #[inline] - fn run_verifier( - v: &mut flatbuffers::Verifier, pos: usize - ) -> Result<(), flatbuffers::InvalidFlatbuffer> { - use self::flatbuffers::Verifiable; - i8::run_verifier(v, pos) - } + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + i8::run_verifier(v, pos) + } } impl flatbuffers::SimpleToVerifyInSlice for TypeKind {} -#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[deprecated( + since = "2.0.0", + note = "Use associated constants instead. This will no longer be generated in 2021." +)] pub const ENUM_MIN_TYPE_REFERENCE_KIND: i8 = 0; -#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[deprecated( + since = "2.0.0", + note = "Use associated constants instead. This will no longer be generated in 2021." +)] pub const ENUM_MAX_TYPE_REFERENCE_KIND: i8 = 2; -#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[deprecated( + since = "2.0.0", + note = "Use associated constants instead. This will no longer be generated in 2021." +)] #[allow(non_camel_case_types)] pub const ENUM_VALUES_TYPE_REFERENCE_KIND: [TypeReferenceKind; 3] = [ - TypeReferenceKind::Named, - TypeReferenceKind::NonNull, - TypeReferenceKind::List, + TypeReferenceKind::Named, + TypeReferenceKind::NonNull, + TypeReferenceKind::List, ]; #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] @@ -257,107 +281,110 @@ pub const ENUM_VALUES_TYPE_REFERENCE_KIND: [TypeReferenceKind; 3] = [ pub struct TypeReferenceKind(pub i8); #[allow(non_upper_case_globals)] impl TypeReferenceKind { - pub const Named: Self = Self(0); - pub const NonNull: Self = Self(1); - pub const List: Self = Self(2); - pub const Catchable: Self = Self(3); - - pub const ENUM_MIN: i8 = 0; - pub const ENUM_MAX: i8 = 2; - pub const ENUM_VALUES: &'static [Self] = &[ - Self::Named, - Self::NonNull, - Self::List, - ]; - /// Returns the variant's name or "" if unknown. - pub fn variant_name(self) -> Option<&'static str> { - match self { - Self::Named => Some("Named"), - Self::NonNull => Some("NonNull"), - Self::List => Some("List"), - _ => None, - } - } + pub const Named: Self = Self(0); + pub const NonNull: Self = Self(1); + pub const List: Self = Self(2); + + pub const ENUM_MIN: i8 = 0; + pub const ENUM_MAX: i8 = 2; + pub const ENUM_VALUES: &'static [Self] = &[Self::Named, Self::NonNull, Self::List]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::Named => Some("Named"), + Self::NonNull => Some("NonNull"), + Self::List => Some("List"), + _ => None, + } + } } impl core::fmt::Debug for TypeReferenceKind { - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - if let Some(name) = self.variant_name() { - f.write_str(name) - } else { - f.write_fmt(format_args!("", self.0)) + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } } - } } impl<'a> flatbuffers::Follow<'a> for TypeReferenceKind { - type Inner = Self; - #[inline] - fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { - let b = unsafe { - flatbuffers::read_scalar_at::(buf, loc) - }; - Self(b) - } + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = flatbuffers::read_scalar_at::(buf, loc); + Self(b) + } } impl flatbuffers::Push for TypeReferenceKind { type Output = TypeReferenceKind; #[inline] - fn push(&self, dst: &mut [u8], _rest: &[u8]) { - unsafe { flatbuffers::emplace_scalar::(dst, self.0); } + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + flatbuffers::emplace_scalar::(dst, self.0); } } impl flatbuffers::EndianScalar for TypeReferenceKind { - #[inline] - fn to_little_endian(self) -> Self { - let b = i8::to_le(self.0); - Self(b) - } - #[inline] - #[allow(clippy::wrong_self_convention)] - fn from_little_endian(self) -> Self { - let b = i8::from_le(self.0); - Self(b) - } + type Scalar = i8; + #[inline] + fn to_little_endian(self) -> i8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: i8) -> Self { + let b = i8::from_le(v); + Self(b) + } } impl<'a> flatbuffers::Verifiable for TypeReferenceKind { - #[inline] - fn run_verifier( - v: &mut flatbuffers::Verifier, pos: usize - ) -> Result<(), flatbuffers::InvalidFlatbuffer> { - use self::flatbuffers::Verifiable; - i8::run_verifier(v, pos) - } + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + i8::run_verifier(v, pos) + } } impl flatbuffers::SimpleToVerifyInSlice for TypeReferenceKind {} -#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[deprecated( + since = "2.0.0", + note = "Use associated constants instead. This will no longer be generated in 2021." +)] pub const ENUM_MIN_DIRECTIVE_LOCATION: i8 = 0; -#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[deprecated( + since = "2.0.0", + note = "Use associated constants instead. This will no longer be generated in 2021." +)] pub const ENUM_MAX_DIRECTIVE_LOCATION: i8 = 18; -#[deprecated(since = "2.0.0", note = "Use associated constants instead. This will no longer be generated in 2021.")] +#[deprecated( + since = "2.0.0", + note = "Use associated constants instead. This will no longer be generated in 2021." +)] #[allow(non_camel_case_types)] pub const ENUM_VALUES_DIRECTIVE_LOCATION: [DirectiveLocation; 19] = [ - DirectiveLocation::Query, - DirectiveLocation::Mutation, - DirectiveLocation::Subscription, - DirectiveLocation::Field, - DirectiveLocation::FragmentDefinition, - DirectiveLocation::FragmentSpread, - DirectiveLocation::InlineFragment, - DirectiveLocation::Schema, - DirectiveLocation::Scalar, - DirectiveLocation::Object, - DirectiveLocation::FieldDefinition, - DirectiveLocation::ArgumentDefinition, - DirectiveLocation::Interface, - DirectiveLocation::Union, - DirectiveLocation::Enum, - DirectiveLocation::EnumValue, - DirectiveLocation::InputObject, - DirectiveLocation::InputFieldDefinition, - DirectiveLocation::VariableDefinition, + DirectiveLocation::Query, + DirectiveLocation::Mutation, + DirectiveLocation::Subscription, + DirectiveLocation::Field, + DirectiveLocation::FragmentDefinition, + DirectiveLocation::FragmentSpread, + DirectiveLocation::InlineFragment, + DirectiveLocation::Schema, + DirectiveLocation::Scalar, + DirectiveLocation::Object, + DirectiveLocation::FieldDefinition, + DirectiveLocation::ArgumentDefinition, + DirectiveLocation::Interface, + DirectiveLocation::Union, + DirectiveLocation::Enum, + DirectiveLocation::EnumValue, + DirectiveLocation::InputObject, + DirectiveLocation::InputFieldDefinition, + DirectiveLocation::VariableDefinition, ]; #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] @@ -365,125 +392,124 @@ pub const ENUM_VALUES_DIRECTIVE_LOCATION: [DirectiveLocation; 19] = [ pub struct DirectiveLocation(pub i8); #[allow(non_upper_case_globals)] impl DirectiveLocation { - pub const Query: Self = Self(0); - pub const Mutation: Self = Self(1); - pub const Subscription: Self = Self(2); - pub const Field: Self = Self(3); - pub const FragmentDefinition: Self = Self(4); - pub const FragmentSpread: Self = Self(5); - pub const InlineFragment: Self = Self(6); - pub const Schema: Self = Self(7); - pub const Scalar: Self = Self(8); - pub const Object: Self = Self(9); - pub const FieldDefinition: Self = Self(10); - pub const ArgumentDefinition: Self = Self(11); - pub const Interface: Self = Self(12); - pub const Union: Self = Self(13); - pub const Enum: Self = Self(14); - pub const EnumValue: Self = Self(15); - pub const InputObject: Self = Self(16); - pub const InputFieldDefinition: Self = Self(17); - pub const VariableDefinition: Self = Self(18); - - pub const ENUM_MIN: i8 = 0; - pub const ENUM_MAX: i8 = 18; - pub const ENUM_VALUES: &'static [Self] = &[ - Self::Query, - Self::Mutation, - Self::Subscription, - Self::Field, - Self::FragmentDefinition, - Self::FragmentSpread, - Self::InlineFragment, - Self::Schema, - Self::Scalar, - Self::Object, - Self::FieldDefinition, - Self::ArgumentDefinition, - Self::Interface, - Self::Union, - Self::Enum, - Self::EnumValue, - Self::InputObject, - Self::InputFieldDefinition, - Self::VariableDefinition, - ]; - /// Returns the variant's name or "" if unknown. - pub fn variant_name(self) -> Option<&'static str> { - match self { - Self::Query => Some("Query"), - Self::Mutation => Some("Mutation"), - Self::Subscription => Some("Subscription"), - Self::Field => Some("Field"), - Self::FragmentDefinition => Some("FragmentDefinition"), - Self::FragmentSpread => Some("FragmentSpread"), - Self::InlineFragment => Some("InlineFragment"), - Self::Schema => Some("Schema"), - Self::Scalar => Some("Scalar"), - Self::Object => Some("Object"), - Self::FieldDefinition => Some("FieldDefinition"), - Self::ArgumentDefinition => Some("ArgumentDefinition"), - Self::Interface => Some("Interface"), - Self::Union => Some("Union"), - Self::Enum => Some("Enum"), - Self::EnumValue => Some("EnumValue"), - Self::InputObject => Some("InputObject"), - Self::InputFieldDefinition => Some("InputFieldDefinition"), - Self::VariableDefinition => Some("VariableDefinition"), - _ => None, - } - } + pub const Query: Self = Self(0); + pub const Mutation: Self = Self(1); + pub const Subscription: Self = Self(2); + pub const Field: Self = Self(3); + pub const FragmentDefinition: Self = Self(4); + pub const FragmentSpread: Self = Self(5); + pub const InlineFragment: Self = Self(6); + pub const Schema: Self = Self(7); + pub const Scalar: Self = Self(8); + pub const Object: Self = Self(9); + pub const FieldDefinition: Self = Self(10); + pub const ArgumentDefinition: Self = Self(11); + pub const Interface: Self = Self(12); + pub const Union: Self = Self(13); + pub const Enum: Self = Self(14); + pub const EnumValue: Self = Self(15); + pub const InputObject: Self = Self(16); + pub const InputFieldDefinition: Self = Self(17); + pub const VariableDefinition: Self = Self(18); + + pub const ENUM_MIN: i8 = 0; + pub const ENUM_MAX: i8 = 18; + pub const ENUM_VALUES: &'static [Self] = &[ + Self::Query, + Self::Mutation, + Self::Subscription, + Self::Field, + Self::FragmentDefinition, + Self::FragmentSpread, + Self::InlineFragment, + Self::Schema, + Self::Scalar, + Self::Object, + Self::FieldDefinition, + Self::ArgumentDefinition, + Self::Interface, + Self::Union, + Self::Enum, + Self::EnumValue, + Self::InputObject, + Self::InputFieldDefinition, + Self::VariableDefinition, + ]; + /// Returns the variant's name or "" if unknown. + pub fn variant_name(self) -> Option<&'static str> { + match self { + Self::Query => Some("Query"), + Self::Mutation => Some("Mutation"), + Self::Subscription => Some("Subscription"), + Self::Field => Some("Field"), + Self::FragmentDefinition => Some("FragmentDefinition"), + Self::FragmentSpread => Some("FragmentSpread"), + Self::InlineFragment => Some("InlineFragment"), + Self::Schema => Some("Schema"), + Self::Scalar => Some("Scalar"), + Self::Object => Some("Object"), + Self::FieldDefinition => Some("FieldDefinition"), + Self::ArgumentDefinition => Some("ArgumentDefinition"), + Self::Interface => Some("Interface"), + Self::Union => Some("Union"), + Self::Enum => Some("Enum"), + Self::EnumValue => Some("EnumValue"), + Self::InputObject => Some("InputObject"), + Self::InputFieldDefinition => Some("InputFieldDefinition"), + Self::VariableDefinition => Some("VariableDefinition"), + _ => None, + } + } } impl core::fmt::Debug for DirectiveLocation { - fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { - if let Some(name) = self.variant_name() { - f.write_str(name) - } else { - f.write_fmt(format_args!("", self.0)) + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + if let Some(name) = self.variant_name() { + f.write_str(name) + } else { + f.write_fmt(format_args!("", self.0)) + } } - } } impl<'a> flatbuffers::Follow<'a> for DirectiveLocation { - type Inner = Self; - #[inline] - fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { - let b = unsafe { - flatbuffers::read_scalar_at::(buf, loc) - }; - Self(b) - } + type Inner = Self; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + let b = flatbuffers::read_scalar_at::(buf, loc); + Self(b) + } } impl flatbuffers::Push for DirectiveLocation { type Output = DirectiveLocation; #[inline] - fn push(&self, dst: &mut [u8], _rest: &[u8]) { - unsafe { flatbuffers::emplace_scalar::(dst, self.0); } + unsafe fn push(&self, dst: &mut [u8], _written_len: usize) { + flatbuffers::emplace_scalar::(dst, self.0); } } impl flatbuffers::EndianScalar for DirectiveLocation { - #[inline] - fn to_little_endian(self) -> Self { - let b = i8::to_le(self.0); - Self(b) - } - #[inline] - #[allow(clippy::wrong_self_convention)] - fn from_little_endian(self) -> Self { - let b = i8::from_le(self.0); - Self(b) - } + type Scalar = i8; + #[inline] + fn to_little_endian(self) -> i8 { + self.0.to_le() + } + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_little_endian(v: i8) -> Self { + let b = i8::from_le(v); + Self(b) + } } impl<'a> flatbuffers::Verifiable for DirectiveLocation { - #[inline] - fn run_verifier( - v: &mut flatbuffers::Verifier, pos: usize - ) -> Result<(), flatbuffers::InvalidFlatbuffer> { - use self::flatbuffers::Verifiable; - i8::run_verifier(v, pos) - } + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + i8::run_verifier(v, pos) + } } impl flatbuffers::SimpleToVerifyInSlice for DirectiveLocation {} @@ -491,100 +517,189 @@ pub enum ConstValueOffset {} #[derive(Copy, Clone, PartialEq)] pub struct ConstValue<'a> { - pub _tab: flatbuffers::Table<'a>, + pub _tab: flatbuffers::Table<'a>, } impl<'a> flatbuffers::Follow<'a> for ConstValue<'a> { - type Inner = ConstValue<'a>; - #[inline] - fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { - Self { _tab: flatbuffers::Table { buf, loc } } - } + type Inner = ConstValue<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: flatbuffers::Table::new(buf, loc), + } + } } impl<'a> ConstValue<'a> { - pub const VT_KIND: flatbuffers::VOffsetT = 4; - pub const VT_STRING_VALUE: flatbuffers::VOffsetT = 6; - pub const VT_BOOL_VALUE: flatbuffers::VOffsetT = 8; - pub const VT_INT_VALUE: flatbuffers::VOffsetT = 10; - pub const VT_FLOAT_VALUE: flatbuffers::VOffsetT = 12; - pub const VT_ENUM_VALUE: flatbuffers::VOffsetT = 14; - pub const VT_LIST_VALUE: flatbuffers::VOffsetT = 16; - pub const VT_OBJECT_VALUE: flatbuffers::VOffsetT = 18; - - #[inline] - pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self { - ConstValue { _tab: table } - } - #[allow(unused_mut)] - pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>( - _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>, - args: &'args ConstValueArgs<'args> - ) -> flatbuffers::WIPOffset> { - let mut builder = ConstValueBuilder::new(_fbb); - if let Some(x) = args.object_value { builder.add_object_value(x); } - if let Some(x) = args.list_value { builder.add_list_value(x); } - if let Some(x) = args.enum_value { builder.add_enum_value(x); } - if let Some(x) = args.float_value { builder.add_float_value(x); } - if let Some(x) = args.int_value { builder.add_int_value(x); } - if let Some(x) = args.string_value { builder.add_string_value(x); } - builder.add_bool_value(args.bool_value); - builder.add_kind(args.kind); - builder.finish() - } - - #[inline] - pub fn kind(&self) -> ConstValueKind { - self._tab.get::(ConstValue::VT_KIND, Some(ConstValueKind::Null)).unwrap() - } - #[inline] - pub fn string_value(&self) -> Option<&'a str> { - self._tab.get::>(ConstValue::VT_STRING_VALUE, None) - } - #[inline] - pub fn bool_value(&self) -> bool { - self._tab.get::(ConstValue::VT_BOOL_VALUE, Some(false)).unwrap() - } - #[inline] - pub fn int_value(&self) -> Option<&'a str> { - self._tab.get::>(ConstValue::VT_INT_VALUE, None) - } - #[inline] - pub fn float_value(&self) -> Option<&'a str> { - self._tab.get::>(ConstValue::VT_FLOAT_VALUE, None) - } - #[inline] - pub fn enum_value(&self) -> Option<&'a str> { - self._tab.get::>(ConstValue::VT_ENUM_VALUE, None) - } - #[inline] - pub fn list_value(&self) -> Option> { - self._tab.get::>(ConstValue::VT_LIST_VALUE, None) - } - #[inline] - pub fn object_value(&self) -> Option> { - self._tab.get::>(ConstValue::VT_OBJECT_VALUE, None) - } + pub const VT_KIND: flatbuffers::VOffsetT = 4; + pub const VT_STRING_VALUE: flatbuffers::VOffsetT = 6; + pub const VT_BOOL_VALUE: flatbuffers::VOffsetT = 8; + pub const VT_INT_VALUE: flatbuffers::VOffsetT = 10; + pub const VT_FLOAT_VALUE: flatbuffers::VOffsetT = 12; + pub const VT_ENUM_VALUE: flatbuffers::VOffsetT = 14; + pub const VT_LIST_VALUE: flatbuffers::VOffsetT = 16; + pub const VT_OBJECT_VALUE: flatbuffers::VOffsetT = 18; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + ConstValue { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args ConstValueArgs<'args>, + ) -> flatbuffers::WIPOffset> { + let mut builder = ConstValueBuilder::new(_fbb); + if let Some(x) = args.object_value { + builder.add_object_value(x); + } + if let Some(x) = args.list_value { + builder.add_list_value(x); + } + if let Some(x) = args.enum_value { + builder.add_enum_value(x); + } + if let Some(x) = args.float_value { + builder.add_float_value(x); + } + if let Some(x) = args.int_value { + builder.add_int_value(x); + } + if let Some(x) = args.string_value { + builder.add_string_value(x); + } + builder.add_bool_value(args.bool_value); + builder.add_kind(args.kind); + builder.finish() + } + + #[inline] + pub fn kind(&self) -> ConstValueKind { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::(ConstValue::VT_KIND, Some(ConstValueKind::Null)) + .unwrap() + } + } + #[inline] + pub fn string_value(&self) -> Option<&'a str> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(ConstValue::VT_STRING_VALUE, None) + } + } + #[inline] + pub fn bool_value(&self) -> bool { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::(ConstValue::VT_BOOL_VALUE, Some(false)) + .unwrap() + } + } + #[inline] + pub fn int_value(&self) -> Option<&'a str> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(ConstValue::VT_INT_VALUE, None) + } + } + #[inline] + pub fn float_value(&self) -> Option<&'a str> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(ConstValue::VT_FLOAT_VALUE, None) + } + } + #[inline] + pub fn enum_value(&self) -> Option<&'a str> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(ConstValue::VT_ENUM_VALUE, None) + } + } + #[inline] + pub fn list_value(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(ConstValue::VT_LIST_VALUE, None) + } + } + #[inline] + pub fn object_value(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(ConstValue::VT_OBJECT_VALUE, None) + } + } } impl flatbuffers::Verifiable for ConstValue<'_> { - #[inline] - fn run_verifier( - v: &mut flatbuffers::Verifier, pos: usize - ) -> Result<(), flatbuffers::InvalidFlatbuffer> { - use self::flatbuffers::Verifiable; - v.visit_table(pos)? - .visit_field::("kind", Self::VT_KIND, false)? - .visit_field::>("string_value", Self::VT_STRING_VALUE, false)? - .visit_field::("bool_value", Self::VT_BOOL_VALUE, false)? - .visit_field::>("int_value", Self::VT_INT_VALUE, false)? - .visit_field::>("float_value", Self::VT_FLOAT_VALUE, false)? - .visit_field::>("enum_value", Self::VT_ENUM_VALUE, false)? - .visit_field::>("list_value", Self::VT_LIST_VALUE, false)? - .visit_field::>("object_value", Self::VT_OBJECT_VALUE, false)? - .finish(); - Ok(()) - } + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::("kind", Self::VT_KIND, false)? + .visit_field::>( + "string_value", + Self::VT_STRING_VALUE, + false, + )? + .visit_field::("bool_value", Self::VT_BOOL_VALUE, false)? + .visit_field::>( + "int_value", + Self::VT_INT_VALUE, + false, + )? + .visit_field::>( + "float_value", + Self::VT_FLOAT_VALUE, + false, + )? + .visit_field::>( + "enum_value", + Self::VT_ENUM_VALUE, + false, + )? + .visit_field::>( + "list_value", + Self::VT_LIST_VALUE, + false, + )? + .visit_field::>( + "object_value", + Self::VT_OBJECT_VALUE, + false, + )? + .finish(); + Ok(()) + } } pub struct ConstValueArgs<'a> { pub kind: ConstValueKind, @@ -597,472 +712,604 @@ pub struct ConstValueArgs<'a> { pub object_value: Option>>, } impl<'a> Default for ConstValueArgs<'a> { - #[inline] - fn default() -> Self { - ConstValueArgs { - kind: ConstValueKind::Null, - string_value: None, - bool_value: false, - int_value: None, - float_value: None, - enum_value: None, - list_value: None, - object_value: None, - } - } -} - -pub struct ConstValueBuilder<'a: 'b, 'b> { - fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>, - start_: flatbuffers::WIPOffset, -} -impl<'a: 'b, 'b> ConstValueBuilder<'a, 'b> { - #[inline] - pub fn add_kind(&mut self, kind: ConstValueKind) { - self.fbb_.push_slot::(ConstValue::VT_KIND, kind, ConstValueKind::Null); - } - #[inline] - pub fn add_string_value(&mut self, string_value: flatbuffers::WIPOffset<&'b str>) { - self.fbb_.push_slot_always::>(ConstValue::VT_STRING_VALUE, string_value); - } - #[inline] - pub fn add_bool_value(&mut self, bool_value: bool) { - self.fbb_.push_slot::(ConstValue::VT_BOOL_VALUE, bool_value, false); - } - #[inline] - pub fn add_int_value(&mut self, int_value: flatbuffers::WIPOffset<&'b str>) { - self.fbb_.push_slot_always::>(ConstValue::VT_INT_VALUE, int_value); - } - #[inline] - pub fn add_float_value(&mut self, float_value: flatbuffers::WIPOffset<&'b str>) { - self.fbb_.push_slot_always::>(ConstValue::VT_FLOAT_VALUE, float_value); - } - #[inline] - pub fn add_enum_value(&mut self, enum_value: flatbuffers::WIPOffset<&'b str>) { - self.fbb_.push_slot_always::>(ConstValue::VT_ENUM_VALUE, enum_value); - } - #[inline] - pub fn add_list_value(&mut self, list_value: flatbuffers::WIPOffset>) { - self.fbb_.push_slot_always::>(ConstValue::VT_LIST_VALUE, list_value); - } - #[inline] - pub fn add_object_value(&mut self, object_value: flatbuffers::WIPOffset>) { - self.fbb_.push_slot_always::>(ConstValue::VT_OBJECT_VALUE, object_value); - } - #[inline] - pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> ConstValueBuilder<'a, 'b> { - let start = _fbb.start_table(); - ConstValueBuilder { - fbb_: _fbb, - start_: start, - } - } - #[inline] - pub fn finish(self) -> flatbuffers::WIPOffset> { - let o = self.fbb_.end_table(self.start_); - flatbuffers::WIPOffset::new(o.value()) - } + #[inline] + fn default() -> Self { + ConstValueArgs { + kind: ConstValueKind::Null, + string_value: None, + bool_value: false, + int_value: None, + float_value: None, + enum_value: None, + list_value: None, + object_value: None, + } + } +} + +pub struct ConstValueBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + start_: flatbuffers::WIPOffset, +} +impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> ConstValueBuilder<'a, 'b, A> { + #[inline] + pub fn add_kind(&mut self, kind: ConstValueKind) { + self.fbb_ + .push_slot::(ConstValue::VT_KIND, kind, ConstValueKind::Null); + } + #[inline] + pub fn add_string_value(&mut self, string_value: flatbuffers::WIPOffset<&'b str>) { + self.fbb_.push_slot_always::>( + ConstValue::VT_STRING_VALUE, + string_value, + ); + } + #[inline] + pub fn add_bool_value(&mut self, bool_value: bool) { + self.fbb_ + .push_slot::(ConstValue::VT_BOOL_VALUE, bool_value, false); + } + #[inline] + pub fn add_int_value(&mut self, int_value: flatbuffers::WIPOffset<&'b str>) { + self.fbb_ + .push_slot_always::>(ConstValue::VT_INT_VALUE, int_value); + } + #[inline] + pub fn add_float_value(&mut self, float_value: flatbuffers::WIPOffset<&'b str>) { + self.fbb_ + .push_slot_always::>(ConstValue::VT_FLOAT_VALUE, float_value); + } + #[inline] + pub fn add_enum_value(&mut self, enum_value: flatbuffers::WIPOffset<&'b str>) { + self.fbb_ + .push_slot_always::>(ConstValue::VT_ENUM_VALUE, enum_value); + } + #[inline] + pub fn add_list_value(&mut self, list_value: flatbuffers::WIPOffset>) { + self.fbb_ + .push_slot_always::>( + ConstValue::VT_LIST_VALUE, + list_value, + ); + } + #[inline] + pub fn add_object_value(&mut self, object_value: flatbuffers::WIPOffset>) { + self.fbb_ + .push_slot_always::>( + ConstValue::VT_OBJECT_VALUE, + object_value, + ); + } + #[inline] + pub fn new( + _fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + ) -> ConstValueBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + ConstValueBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + flatbuffers::WIPOffset::new(o.value()) + } } impl core::fmt::Debug for ConstValue<'_> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let mut ds = f.debug_struct("ConstValue"); - ds.field("kind", &self.kind()); - ds.field("string_value", &self.string_value()); - ds.field("bool_value", &self.bool_value()); - ds.field("int_value", &self.int_value()); - ds.field("float_value", &self.float_value()); - ds.field("enum_value", &self.enum_value()); - ds.field("list_value", &self.list_value()); - ds.field("object_value", &self.object_value()); - ds.finish() - } + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("ConstValue"); + ds.field("kind", &self.kind()); + ds.field("string_value", &self.string_value()); + ds.field("bool_value", &self.bool_value()); + ds.field("int_value", &self.int_value()); + ds.field("float_value", &self.float_value()); + ds.field("enum_value", &self.enum_value()); + ds.field("list_value", &self.list_value()); + ds.field("object_value", &self.object_value()); + ds.finish() + } } pub enum ListValueOffset {} #[derive(Copy, Clone, PartialEq)] pub struct ListValue<'a> { - pub _tab: flatbuffers::Table<'a>, + pub _tab: flatbuffers::Table<'a>, } impl<'a> flatbuffers::Follow<'a> for ListValue<'a> { - type Inner = ListValue<'a>; - #[inline] - fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { - Self { _tab: flatbuffers::Table { buf, loc } } - } + type Inner = ListValue<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: flatbuffers::Table::new(buf, loc), + } + } } impl<'a> ListValue<'a> { - pub const VT_VALUES: flatbuffers::VOffsetT = 4; - - #[inline] - pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self { - ListValue { _tab: table } - } - #[allow(unused_mut)] - pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>( - _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>, - args: &'args ListValueArgs<'args> - ) -> flatbuffers::WIPOffset> { - let mut builder = ListValueBuilder::new(_fbb); - if let Some(x) = args.values { builder.add_values(x); } - builder.finish() - } - - #[inline] - pub fn values(&self) -> Option>>> { - self._tab.get::>>>(ListValue::VT_VALUES, None) - } + pub const VT_VALUES: flatbuffers::VOffsetT = 4; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + ListValue { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args ListValueArgs<'args>, + ) -> flatbuffers::WIPOffset> { + let mut builder = ListValueBuilder::new(_fbb); + if let Some(x) = args.values { + builder.add_values(x); + } + builder.finish() + } + + #[inline] + pub fn values( + &self, + ) -> Option>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab.get::>, + >>(ListValue::VT_VALUES, None) + } + } } impl flatbuffers::Verifiable for ListValue<'_> { - #[inline] - fn run_verifier( - v: &mut flatbuffers::Verifier, pos: usize - ) -> Result<(), flatbuffers::InvalidFlatbuffer> { - use self::flatbuffers::Verifiable; - v.visit_table(pos)? - .visit_field::>>>("values", Self::VT_VALUES, false)? - .finish(); - Ok(()) - } + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::>, + >>("values", Self::VT_VALUES, false)? + .finish(); + Ok(()) + } } pub struct ListValueArgs<'a> { - pub values: Option>>>>, + pub values: Option< + flatbuffers::WIPOffset< + flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>>, + >, + >, } impl<'a> Default for ListValueArgs<'a> { - #[inline] - fn default() -> Self { - ListValueArgs { - values: None, - } - } -} - -pub struct ListValueBuilder<'a: 'b, 'b> { - fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>, - start_: flatbuffers::WIPOffset, -} -impl<'a: 'b, 'b> ListValueBuilder<'a, 'b> { - #[inline] - pub fn add_values(&mut self, values: flatbuffers::WIPOffset>>>) { - self.fbb_.push_slot_always::>(ListValue::VT_VALUES, values); - } - #[inline] - pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> ListValueBuilder<'a, 'b> { - let start = _fbb.start_table(); - ListValueBuilder { - fbb_: _fbb, - start_: start, - } - } - #[inline] - pub fn finish(self) -> flatbuffers::WIPOffset> { - let o = self.fbb_.end_table(self.start_); - flatbuffers::WIPOffset::new(o.value()) - } + #[inline] + fn default() -> Self { + ListValueArgs { values: None } + } +} + +pub struct ListValueBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + start_: flatbuffers::WIPOffset, +} +impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> ListValueBuilder<'a, 'b, A> { + #[inline] + pub fn add_values( + &mut self, + values: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_ + .push_slot_always::>(ListValue::VT_VALUES, values); + } + #[inline] + pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>) -> ListValueBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + ListValueBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + flatbuffers::WIPOffset::new(o.value()) + } } impl core::fmt::Debug for ListValue<'_> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let mut ds = f.debug_struct("ListValue"); - ds.field("values", &self.values()); - ds.finish() - } + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("ListValue"); + ds.field("values", &self.values()); + ds.finish() + } } pub enum ObjectFieldOffset {} #[derive(Copy, Clone, PartialEq)] pub struct ObjectField<'a> { - pub _tab: flatbuffers::Table<'a>, + pub _tab: flatbuffers::Table<'a>, } impl<'a> flatbuffers::Follow<'a> for ObjectField<'a> { - type Inner = ObjectField<'a>; - #[inline] - fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { - Self { _tab: flatbuffers::Table { buf, loc } } - } + type Inner = ObjectField<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: flatbuffers::Table::new(buf, loc), + } + } } impl<'a> ObjectField<'a> { - pub const VT_NAME: flatbuffers::VOffsetT = 4; - pub const VT_VALUE: flatbuffers::VOffsetT = 6; - - #[inline] - pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self { - ObjectField { _tab: table } - } - #[allow(unused_mut)] - pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>( - _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>, - args: &'args ObjectFieldArgs<'args> - ) -> flatbuffers::WIPOffset> { - let mut builder = ObjectFieldBuilder::new(_fbb); - if let Some(x) = args.value { builder.add_value(x); } - if let Some(x) = args.name { builder.add_name(x); } - builder.finish() - } - - #[inline] - pub fn name(&self) -> Option<&'a str> { - self._tab.get::>(ObjectField::VT_NAME, None) - } - #[inline] - pub fn value(&self) -> Option> { - self._tab.get::>(ObjectField::VT_VALUE, None) - } + pub const VT_NAME: flatbuffers::VOffsetT = 4; + pub const VT_VALUE: flatbuffers::VOffsetT = 6; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + ObjectField { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args ObjectFieldArgs<'args>, + ) -> flatbuffers::WIPOffset> { + let mut builder = ObjectFieldBuilder::new(_fbb); + if let Some(x) = args.value { + builder.add_value(x); + } + if let Some(x) = args.name { + builder.add_name(x); + } + builder.finish() + } + + #[inline] + pub fn name(&self) -> Option<&'a str> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(ObjectField::VT_NAME, None) + } + } + #[inline] + pub fn value(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(ObjectField::VT_VALUE, None) + } + } } impl flatbuffers::Verifiable for ObjectField<'_> { - #[inline] - fn run_verifier( - v: &mut flatbuffers::Verifier, pos: usize - ) -> Result<(), flatbuffers::InvalidFlatbuffer> { - use self::flatbuffers::Verifiable; - v.visit_table(pos)? - .visit_field::>("name", Self::VT_NAME, false)? - .visit_field::>("value", Self::VT_VALUE, false)? - .finish(); - Ok(()) - } + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::>("name", Self::VT_NAME, false)? + .visit_field::>( + "value", + Self::VT_VALUE, + false, + )? + .finish(); + Ok(()) + } } pub struct ObjectFieldArgs<'a> { pub name: Option>, pub value: Option>>, } impl<'a> Default for ObjectFieldArgs<'a> { - #[inline] - fn default() -> Self { - ObjectFieldArgs { - name: None, - value: None, - } - } -} - -pub struct ObjectFieldBuilder<'a: 'b, 'b> { - fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>, - start_: flatbuffers::WIPOffset, -} -impl<'a: 'b, 'b> ObjectFieldBuilder<'a, 'b> { - #[inline] - pub fn add_name(&mut self, name: flatbuffers::WIPOffset<&'b str>) { - self.fbb_.push_slot_always::>(ObjectField::VT_NAME, name); - } - #[inline] - pub fn add_value(&mut self, value: flatbuffers::WIPOffset>) { - self.fbb_.push_slot_always::>(ObjectField::VT_VALUE, value); - } - #[inline] - pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> ObjectFieldBuilder<'a, 'b> { - let start = _fbb.start_table(); - ObjectFieldBuilder { - fbb_: _fbb, - start_: start, - } - } - #[inline] - pub fn finish(self) -> flatbuffers::WIPOffset> { - let o = self.fbb_.end_table(self.start_); - flatbuffers::WIPOffset::new(o.value()) - } + #[inline] + fn default() -> Self { + ObjectFieldArgs { + name: None, + value: None, + } + } +} + +pub struct ObjectFieldBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + start_: flatbuffers::WIPOffset, +} +impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> ObjectFieldBuilder<'a, 'b, A> { + #[inline] + pub fn add_name(&mut self, name: flatbuffers::WIPOffset<&'b str>) { + self.fbb_ + .push_slot_always::>(ObjectField::VT_NAME, name); + } + #[inline] + pub fn add_value(&mut self, value: flatbuffers::WIPOffset>) { + self.fbb_ + .push_slot_always::>(ObjectField::VT_VALUE, value); + } + #[inline] + pub fn new( + _fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + ) -> ObjectFieldBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + ObjectFieldBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + flatbuffers::WIPOffset::new(o.value()) + } } impl core::fmt::Debug for ObjectField<'_> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let mut ds = f.debug_struct("ObjectField"); - ds.field("name", &self.name()); - ds.field("value", &self.value()); - ds.finish() - } + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("ObjectField"); + ds.field("name", &self.name()); + ds.field("value", &self.value()); + ds.finish() + } } pub enum ObjectValueOffset {} #[derive(Copy, Clone, PartialEq)] pub struct ObjectValue<'a> { - pub _tab: flatbuffers::Table<'a>, + pub _tab: flatbuffers::Table<'a>, } impl<'a> flatbuffers::Follow<'a> for ObjectValue<'a> { - type Inner = ObjectValue<'a>; - #[inline] - fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { - Self { _tab: flatbuffers::Table { buf, loc } } - } + type Inner = ObjectValue<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: flatbuffers::Table::new(buf, loc), + } + } } impl<'a> ObjectValue<'a> { - pub const VT_FIELDS: flatbuffers::VOffsetT = 4; - - #[inline] - pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self { - ObjectValue { _tab: table } - } - #[allow(unused_mut)] - pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>( - _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>, - args: &'args ObjectValueArgs<'args> - ) -> flatbuffers::WIPOffset> { - let mut builder = ObjectValueBuilder::new(_fbb); - if let Some(x) = args.fields { builder.add_fields(x); } - builder.finish() - } - - #[inline] - pub fn fields(&self) -> Option>>> { - self._tab.get::>>>(ObjectValue::VT_FIELDS, None) - } + pub const VT_FIELDS: flatbuffers::VOffsetT = 4; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + ObjectValue { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args ObjectValueArgs<'args>, + ) -> flatbuffers::WIPOffset> { + let mut builder = ObjectValueBuilder::new(_fbb); + if let Some(x) = args.fields { + builder.add_fields(x); + } + builder.finish() + } + + #[inline] + pub fn fields( + &self, + ) -> Option>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab.get::>, + >>(ObjectValue::VT_FIELDS, None) + } + } } impl flatbuffers::Verifiable for ObjectValue<'_> { - #[inline] - fn run_verifier( - v: &mut flatbuffers::Verifier, pos: usize - ) -> Result<(), flatbuffers::InvalidFlatbuffer> { - use self::flatbuffers::Verifiable; - v.visit_table(pos)? - .visit_field::>>>("fields", Self::VT_FIELDS, false)? - .finish(); - Ok(()) - } + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::>, + >>("fields", Self::VT_FIELDS, false)? + .finish(); + Ok(()) + } } pub struct ObjectValueArgs<'a> { - pub fields: Option>>>>, + pub fields: Option< + flatbuffers::WIPOffset< + flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>>, + >, + >, } impl<'a> Default for ObjectValueArgs<'a> { - #[inline] - fn default() -> Self { - ObjectValueArgs { - fields: None, - } - } -} - -pub struct ObjectValueBuilder<'a: 'b, 'b> { - fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>, - start_: flatbuffers::WIPOffset, -} -impl<'a: 'b, 'b> ObjectValueBuilder<'a, 'b> { - #[inline] - pub fn add_fields(&mut self, fields: flatbuffers::WIPOffset>>>) { - self.fbb_.push_slot_always::>(ObjectValue::VT_FIELDS, fields); - } - #[inline] - pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> ObjectValueBuilder<'a, 'b> { - let start = _fbb.start_table(); - ObjectValueBuilder { - fbb_: _fbb, - start_: start, - } - } - #[inline] - pub fn finish(self) -> flatbuffers::WIPOffset> { - let o = self.fbb_.end_table(self.start_); - flatbuffers::WIPOffset::new(o.value()) - } + #[inline] + fn default() -> Self { + ObjectValueArgs { fields: None } + } +} + +pub struct ObjectValueBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + start_: flatbuffers::WIPOffset, +} +impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> ObjectValueBuilder<'a, 'b, A> { + #[inline] + pub fn add_fields( + &mut self, + fields: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_ + .push_slot_always::>(ObjectValue::VT_FIELDS, fields); + } + #[inline] + pub fn new( + _fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + ) -> ObjectValueBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + ObjectValueBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + flatbuffers::WIPOffset::new(o.value()) + } } impl core::fmt::Debug for ObjectValue<'_> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let mut ds = f.debug_struct("ObjectValue"); - ds.field("fields", &self.fields()); - ds.finish() - } + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("ObjectValue"); + ds.field("fields", &self.fields()); + ds.finish() + } } pub enum TypeOffset {} #[derive(Copy, Clone, PartialEq)] pub struct Type<'a> { - pub _tab: flatbuffers::Table<'a>, + pub _tab: flatbuffers::Table<'a>, } impl<'a> flatbuffers::Follow<'a> for Type<'a> { - type Inner = Type<'a>; - #[inline] - fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { - Self { _tab: flatbuffers::Table { buf, loc } } - } + type Inner = Type<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: flatbuffers::Table::new(buf, loc), + } + } } impl<'a> Type<'a> { - pub const VT_KIND: flatbuffers::VOffsetT = 4; - pub const VT_SCALAR_ID: flatbuffers::VOffsetT = 6; - pub const VT_INPUT_OBJECT_ID: flatbuffers::VOffsetT = 8; - pub const VT_ENUM_ID: flatbuffers::VOffsetT = 10; - pub const VT_OBJECT_ID: flatbuffers::VOffsetT = 12; - pub const VT_INTERFACE_ID: flatbuffers::VOffsetT = 14; - pub const VT_UNION_ID: flatbuffers::VOffsetT = 16; - - #[inline] - pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self { - Type { _tab: table } - } - #[allow(unused_mut)] - pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>( - _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>, - args: &'args TypeArgs - ) -> flatbuffers::WIPOffset> { - let mut builder = TypeBuilder::new(_fbb); - builder.add_union_id(args.union_id); - builder.add_interface_id(args.interface_id); - builder.add_object_id(args.object_id); - builder.add_enum_id(args.enum_id); - builder.add_input_object_id(args.input_object_id); - builder.add_scalar_id(args.scalar_id); - builder.add_kind(args.kind); - builder.finish() - } - - #[inline] - pub fn kind(&self) -> TypeKind { - self._tab.get::(Type::VT_KIND, Some(TypeKind::Scalar)).unwrap() - } - #[inline] - pub fn scalar_id(&self) -> u32 { - self._tab.get::(Type::VT_SCALAR_ID, Some(0)).unwrap() - } - #[inline] - pub fn input_object_id(&self) -> u32 { - self._tab.get::(Type::VT_INPUT_OBJECT_ID, Some(0)).unwrap() - } - #[inline] - pub fn enum_id(&self) -> u32 { - self._tab.get::(Type::VT_ENUM_ID, Some(0)).unwrap() - } - #[inline] - pub fn object_id(&self) -> u32 { - self._tab.get::(Type::VT_OBJECT_ID, Some(0)).unwrap() - } - #[inline] - pub fn interface_id(&self) -> u32 { - self._tab.get::(Type::VT_INTERFACE_ID, Some(0)).unwrap() - } - #[inline] - pub fn union_id(&self) -> u32 { - self._tab.get::(Type::VT_UNION_ID, Some(0)).unwrap() - } + pub const VT_KIND: flatbuffers::VOffsetT = 4; + pub const VT_SCALAR_ID: flatbuffers::VOffsetT = 6; + pub const VT_INPUT_OBJECT_ID: flatbuffers::VOffsetT = 8; + pub const VT_ENUM_ID: flatbuffers::VOffsetT = 10; + pub const VT_OBJECT_ID: flatbuffers::VOffsetT = 12; + pub const VT_INTERFACE_ID: flatbuffers::VOffsetT = 14; + pub const VT_UNION_ID: flatbuffers::VOffsetT = 16; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + Type { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args TypeArgs, + ) -> flatbuffers::WIPOffset> { + let mut builder = TypeBuilder::new(_fbb); + builder.add_union_id(args.union_id); + builder.add_interface_id(args.interface_id); + builder.add_object_id(args.object_id); + builder.add_enum_id(args.enum_id); + builder.add_input_object_id(args.input_object_id); + builder.add_scalar_id(args.scalar_id); + builder.add_kind(args.kind); + builder.finish() + } + + #[inline] + pub fn kind(&self) -> TypeKind { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::(Type::VT_KIND, Some(TypeKind::Scalar)) + .unwrap() + } + } + #[inline] + pub fn scalar_id(&self) -> u32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(Type::VT_SCALAR_ID, Some(0)).unwrap() } + } + #[inline] + pub fn input_object_id(&self) -> u32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::(Type::VT_INPUT_OBJECT_ID, Some(0)) + .unwrap() + } + } + #[inline] + pub fn enum_id(&self) -> u32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(Type::VT_ENUM_ID, Some(0)).unwrap() } + } + #[inline] + pub fn object_id(&self) -> u32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(Type::VT_OBJECT_ID, Some(0)).unwrap() } + } + #[inline] + pub fn interface_id(&self) -> u32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::(Type::VT_INTERFACE_ID, Some(0)) + .unwrap() + } + } + #[inline] + pub fn union_id(&self) -> u32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { self._tab.get::(Type::VT_UNION_ID, Some(0)).unwrap() } + } } impl flatbuffers::Verifiable for Type<'_> { - #[inline] - fn run_verifier( - v: &mut flatbuffers::Verifier, pos: usize - ) -> Result<(), flatbuffers::InvalidFlatbuffer> { - use self::flatbuffers::Verifiable; - v.visit_table(pos)? - .visit_field::("kind", Self::VT_KIND, false)? - .visit_field::("scalar_id", Self::VT_SCALAR_ID, false)? - .visit_field::("input_object_id", Self::VT_INPUT_OBJECT_ID, false)? - .visit_field::("enum_id", Self::VT_ENUM_ID, false)? - .visit_field::("object_id", Self::VT_OBJECT_ID, false)? - .visit_field::("interface_id", Self::VT_INTERFACE_ID, false)? - .visit_field::("union_id", Self::VT_UNION_ID, false)? - .finish(); - Ok(()) - } + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::("kind", Self::VT_KIND, false)? + .visit_field::("scalar_id", Self::VT_SCALAR_ID, false)? + .visit_field::("input_object_id", Self::VT_INPUT_OBJECT_ID, false)? + .visit_field::("enum_id", Self::VT_ENUM_ID, false)? + .visit_field::("object_id", Self::VT_OBJECT_ID, false)? + .visit_field::("interface_id", Self::VT_INTERFACE_ID, false)? + .visit_field::("union_id", Self::VT_UNION_ID, false)? + .finish(); + Ok(()) + } } + #[derive(Copy, Clone)] pub struct TypeArgs { pub kind: TypeKind, @@ -1074,151 +1321,196 @@ pub struct TypeArgs { pub union_id: u32, } impl<'a> Default for TypeArgs { - #[inline] - fn default() -> Self { - TypeArgs { - kind: TypeKind::Scalar, - scalar_id: 0, - input_object_id: 0, - enum_id: 0, - object_id: 0, - interface_id: 0, - union_id: 0, - } - } -} - -pub struct TypeBuilder<'a: 'b, 'b> { - fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>, - start_: flatbuffers::WIPOffset, -} -impl<'a: 'b, 'b> TypeBuilder<'a, 'b> { - #[inline] - pub fn add_kind(&mut self, kind: TypeKind) { - self.fbb_.push_slot::(Type::VT_KIND, kind, TypeKind::Scalar); - } - #[inline] - pub fn add_scalar_id(&mut self, scalar_id: u32) { - self.fbb_.push_slot::(Type::VT_SCALAR_ID, scalar_id, 0); - } - #[inline] - pub fn add_input_object_id(&mut self, input_object_id: u32) { - self.fbb_.push_slot::(Type::VT_INPUT_OBJECT_ID, input_object_id, 0); - } - #[inline] - pub fn add_enum_id(&mut self, enum_id: u32) { - self.fbb_.push_slot::(Type::VT_ENUM_ID, enum_id, 0); - } - #[inline] - pub fn add_object_id(&mut self, object_id: u32) { - self.fbb_.push_slot::(Type::VT_OBJECT_ID, object_id, 0); - } - #[inline] - pub fn add_interface_id(&mut self, interface_id: u32) { - self.fbb_.push_slot::(Type::VT_INTERFACE_ID, interface_id, 0); - } - #[inline] - pub fn add_union_id(&mut self, union_id: u32) { - self.fbb_.push_slot::(Type::VT_UNION_ID, union_id, 0); - } - #[inline] - pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> TypeBuilder<'a, 'b> { - let start = _fbb.start_table(); - TypeBuilder { - fbb_: _fbb, - start_: start, - } - } - #[inline] - pub fn finish(self) -> flatbuffers::WIPOffset> { - let o = self.fbb_.end_table(self.start_); - flatbuffers::WIPOffset::new(o.value()) - } + #[inline] + fn default() -> Self { + TypeArgs { + kind: TypeKind::Scalar, + scalar_id: 0, + input_object_id: 0, + enum_id: 0, + object_id: 0, + interface_id: 0, + union_id: 0, + } + } +} + +pub struct TypeBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + start_: flatbuffers::WIPOffset, +} +impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> TypeBuilder<'a, 'b, A> { + #[inline] + pub fn add_kind(&mut self, kind: TypeKind) { + self.fbb_ + .push_slot::(Type::VT_KIND, kind, TypeKind::Scalar); + } + #[inline] + pub fn add_scalar_id(&mut self, scalar_id: u32) { + self.fbb_.push_slot::(Type::VT_SCALAR_ID, scalar_id, 0); + } + #[inline] + pub fn add_input_object_id(&mut self, input_object_id: u32) { + self.fbb_ + .push_slot::(Type::VT_INPUT_OBJECT_ID, input_object_id, 0); + } + #[inline] + pub fn add_enum_id(&mut self, enum_id: u32) { + self.fbb_.push_slot::(Type::VT_ENUM_ID, enum_id, 0); + } + #[inline] + pub fn add_object_id(&mut self, object_id: u32) { + self.fbb_.push_slot::(Type::VT_OBJECT_ID, object_id, 0); + } + #[inline] + pub fn add_interface_id(&mut self, interface_id: u32) { + self.fbb_ + .push_slot::(Type::VT_INTERFACE_ID, interface_id, 0); + } + #[inline] + pub fn add_union_id(&mut self, union_id: u32) { + self.fbb_.push_slot::(Type::VT_UNION_ID, union_id, 0); + } + #[inline] + pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>) -> TypeBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + TypeBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + flatbuffers::WIPOffset::new(o.value()) + } } impl core::fmt::Debug for Type<'_> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let mut ds = f.debug_struct("Type"); - ds.field("kind", &self.kind()); - ds.field("scalar_id", &self.scalar_id()); - ds.field("input_object_id", &self.input_object_id()); - ds.field("enum_id", &self.enum_id()); - ds.field("object_id", &self.object_id()); - ds.field("interface_id", &self.interface_id()); - ds.field("union_id", &self.union_id()); - ds.finish() - } + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("Type"); + ds.field("kind", &self.kind()); + ds.field("scalar_id", &self.scalar_id()); + ds.field("input_object_id", &self.input_object_id()); + ds.field("enum_id", &self.enum_id()); + ds.field("object_id", &self.object_id()); + ds.field("interface_id", &self.interface_id()); + ds.field("union_id", &self.union_id()); + ds.finish() + } } pub enum TypeReferenceOffset {} #[derive(Copy, Clone, PartialEq)] pub struct TypeReference<'a> { - pub _tab: flatbuffers::Table<'a>, + pub _tab: flatbuffers::Table<'a>, } impl<'a> flatbuffers::Follow<'a> for TypeReference<'a> { - type Inner = TypeReference<'a>; - #[inline] - fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { - Self { _tab: flatbuffers::Table { buf, loc } } - } + type Inner = TypeReference<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: flatbuffers::Table::new(buf, loc), + } + } } impl<'a> TypeReference<'a> { - pub const VT_KIND: flatbuffers::VOffsetT = 4; - pub const VT_NAMED: flatbuffers::VOffsetT = 6; - pub const VT_NULL: flatbuffers::VOffsetT = 8; - pub const VT_LIST: flatbuffers::VOffsetT = 10; - - #[inline] - pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self { - TypeReference { _tab: table } - } - #[allow(unused_mut)] - pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>( - _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>, - args: &'args TypeReferenceArgs<'args> - ) -> flatbuffers::WIPOffset> { - let mut builder = TypeReferenceBuilder::new(_fbb); - if let Some(x) = args.list { builder.add_list(x); } - if let Some(x) = args.null { builder.add_null(x); } - if let Some(x) = args.named { builder.add_named(x); } - builder.add_kind(args.kind); - builder.finish() - } - - #[inline] - pub fn kind(&self) -> TypeReferenceKind { - self._tab.get::(TypeReference::VT_KIND, Some(TypeReferenceKind::Named)).unwrap() - } - #[inline] - pub fn named(&self) -> Option> { - self._tab.get::>(TypeReference::VT_NAMED, None) - } - #[inline] - pub fn null(&self) -> Option> { - self._tab.get::>(TypeReference::VT_NULL, None) - } - #[inline] - pub fn list(&self) -> Option> { - self._tab.get::>(TypeReference::VT_LIST, None) - } + pub const VT_KIND: flatbuffers::VOffsetT = 4; + pub const VT_NAMED: flatbuffers::VOffsetT = 6; + pub const VT_NULL: flatbuffers::VOffsetT = 8; + pub const VT_LIST: flatbuffers::VOffsetT = 10; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + TypeReference { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args TypeReferenceArgs<'args>, + ) -> flatbuffers::WIPOffset> { + let mut builder = TypeReferenceBuilder::new(_fbb); + if let Some(x) = args.list { + builder.add_list(x); + } + if let Some(x) = args.null { + builder.add_null(x); + } + if let Some(x) = args.named { + builder.add_named(x); + } + builder.add_kind(args.kind); + builder.finish() + } + + #[inline] + pub fn kind(&self) -> TypeReferenceKind { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::(TypeReference::VT_KIND, Some(TypeReferenceKind::Named)) + .unwrap() + } + } + #[inline] + pub fn named(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(TypeReference::VT_NAMED, None) + } + } + #[inline] + pub fn null(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(TypeReference::VT_NULL, None) + } + } + #[inline] + pub fn list(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(TypeReference::VT_LIST, None) + } + } } impl flatbuffers::Verifiable for TypeReference<'_> { - #[inline] - fn run_verifier( - v: &mut flatbuffers::Verifier, pos: usize - ) -> Result<(), flatbuffers::InvalidFlatbuffer> { - use self::flatbuffers::Verifiable; - v.visit_table(pos)? - .visit_field::("kind", Self::VT_KIND, false)? - .visit_field::>("named", Self::VT_NAMED, false)? - .visit_field::>("null", Self::VT_NULL, false)? - .visit_field::>("list", Self::VT_LIST, false)? - .finish(); - Ok(()) - } + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::("kind", Self::VT_KIND, false)? + .visit_field::>("named", Self::VT_NAMED, false)? + .visit_field::>( + "null", + Self::VT_NULL, + false, + )? + .visit_field::>( + "list", + Self::VT_LIST, + false, + )? + .finish(); + Ok(()) + } } pub struct TypeReferenceArgs<'a> { pub kind: TypeReferenceKind, @@ -1227,340 +1519,454 @@ pub struct TypeReferenceArgs<'a> { pub list: Option>>, } impl<'a> Default for TypeReferenceArgs<'a> { - #[inline] - fn default() -> Self { - TypeReferenceArgs { - kind: TypeReferenceKind::Named, - named: None, - null: None, - list: None, - } - } -} - -pub struct TypeReferenceBuilder<'a: 'b, 'b> { - fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>, - start_: flatbuffers::WIPOffset, -} -impl<'a: 'b, 'b> TypeReferenceBuilder<'a, 'b> { - #[inline] - pub fn add_kind(&mut self, kind: TypeReferenceKind) { - self.fbb_.push_slot::(TypeReference::VT_KIND, kind, TypeReferenceKind::Named); - } - #[inline] - pub fn add_named(&mut self, named: flatbuffers::WIPOffset>) { - self.fbb_.push_slot_always::>(TypeReference::VT_NAMED, named); - } - #[inline] - pub fn add_null(&mut self, null: flatbuffers::WIPOffset>) { - self.fbb_.push_slot_always::>(TypeReference::VT_NULL, null); - } - #[inline] - pub fn add_list(&mut self, list: flatbuffers::WIPOffset>) { - self.fbb_.push_slot_always::>(TypeReference::VT_LIST, list); - } - #[inline] - pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> TypeReferenceBuilder<'a, 'b> { - let start = _fbb.start_table(); - TypeReferenceBuilder { - fbb_: _fbb, - start_: start, - } - } - #[inline] - pub fn finish(self) -> flatbuffers::WIPOffset> { - let o = self.fbb_.end_table(self.start_); - flatbuffers::WIPOffset::new(o.value()) - } + #[inline] + fn default() -> Self { + TypeReferenceArgs { + kind: TypeReferenceKind::Named, + named: None, + null: None, + list: None, + } + } +} + +pub struct TypeReferenceBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + start_: flatbuffers::WIPOffset, +} +impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> TypeReferenceBuilder<'a, 'b, A> { + #[inline] + pub fn add_kind(&mut self, kind: TypeReferenceKind) { + self.fbb_.push_slot::( + TypeReference::VT_KIND, + kind, + TypeReferenceKind::Named, + ); + } + #[inline] + pub fn add_named(&mut self, named: flatbuffers::WIPOffset>) { + self.fbb_ + .push_slot_always::>(TypeReference::VT_NAMED, named); + } + #[inline] + pub fn add_null(&mut self, null: flatbuffers::WIPOffset>) { + self.fbb_ + .push_slot_always::>( + TypeReference::VT_NULL, + null, + ); + } + #[inline] + pub fn add_list(&mut self, list: flatbuffers::WIPOffset>) { + self.fbb_ + .push_slot_always::>( + TypeReference::VT_LIST, + list, + ); + } + #[inline] + pub fn new( + _fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + ) -> TypeReferenceBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + TypeReferenceBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + flatbuffers::WIPOffset::new(o.value()) + } } impl core::fmt::Debug for TypeReference<'_> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let mut ds = f.debug_struct("TypeReference"); - ds.field("kind", &self.kind()); - ds.field("named", &self.named()); - ds.field("null", &self.null()); - ds.field("list", &self.list()); - ds.finish() - } + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("TypeReference"); + ds.field("kind", &self.kind()); + ds.field("named", &self.named()); + ds.field("null", &self.null()); + ds.field("list", &self.list()); + ds.finish() + } } pub enum ArgumentValueOffset {} #[derive(Copy, Clone, PartialEq)] pub struct ArgumentValue<'a> { - pub _tab: flatbuffers::Table<'a>, + pub _tab: flatbuffers::Table<'a>, } impl<'a> flatbuffers::Follow<'a> for ArgumentValue<'a> { - type Inner = ArgumentValue<'a>; - #[inline] - fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { - Self { _tab: flatbuffers::Table { buf, loc } } - } + type Inner = ArgumentValue<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: flatbuffers::Table::new(buf, loc), + } + } } impl<'a> ArgumentValue<'a> { - pub const VT_NAME: flatbuffers::VOffsetT = 4; - pub const VT_VALUE: flatbuffers::VOffsetT = 6; - - #[inline] - pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self { - ArgumentValue { _tab: table } - } - #[allow(unused_mut)] - pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>( - _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>, - args: &'args ArgumentValueArgs<'args> - ) -> flatbuffers::WIPOffset> { - let mut builder = ArgumentValueBuilder::new(_fbb); - if let Some(x) = args.value { builder.add_value(x); } - if let Some(x) = args.name { builder.add_name(x); } - builder.finish() - } - - #[inline] - pub fn name(&self) -> Option<&'a str> { - self._tab.get::>(ArgumentValue::VT_NAME, None) - } - #[inline] - pub fn value(&self) -> Option> { - self._tab.get::>(ArgumentValue::VT_VALUE, None) - } -} + pub const VT_NAME: flatbuffers::VOffsetT = 4; + pub const VT_VALUE: flatbuffers::VOffsetT = 6; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + ArgumentValue { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args ArgumentValueArgs<'args>, + ) -> flatbuffers::WIPOffset> { + let mut builder = ArgumentValueBuilder::new(_fbb); + if let Some(x) = args.value { + builder.add_value(x); + } + if let Some(x) = args.name { + builder.add_name(x); + } + builder.finish() + } + + #[inline] + pub fn name(&self) -> Option<&'a str> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(ArgumentValue::VT_NAME, None) + } + } + #[inline] + pub fn value(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(ArgumentValue::VT_VALUE, None) + } + } +} impl flatbuffers::Verifiable for ArgumentValue<'_> { - #[inline] - fn run_verifier( - v: &mut flatbuffers::Verifier, pos: usize - ) -> Result<(), flatbuffers::InvalidFlatbuffer> { - use self::flatbuffers::Verifiable; - v.visit_table(pos)? - .visit_field::>("name", Self::VT_NAME, false)? - .visit_field::>("value", Self::VT_VALUE, false)? - .finish(); - Ok(()) - } + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::>("name", Self::VT_NAME, false)? + .visit_field::>( + "value", + Self::VT_VALUE, + false, + )? + .finish(); + Ok(()) + } } pub struct ArgumentValueArgs<'a> { pub name: Option>, pub value: Option>>, } impl<'a> Default for ArgumentValueArgs<'a> { - #[inline] - fn default() -> Self { - ArgumentValueArgs { - name: None, - value: None, - } - } -} - -pub struct ArgumentValueBuilder<'a: 'b, 'b> { - fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>, - start_: flatbuffers::WIPOffset, -} -impl<'a: 'b, 'b> ArgumentValueBuilder<'a, 'b> { - #[inline] - pub fn add_name(&mut self, name: flatbuffers::WIPOffset<&'b str>) { - self.fbb_.push_slot_always::>(ArgumentValue::VT_NAME, name); - } - #[inline] - pub fn add_value(&mut self, value: flatbuffers::WIPOffset>) { - self.fbb_.push_slot_always::>(ArgumentValue::VT_VALUE, value); - } - #[inline] - pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> ArgumentValueBuilder<'a, 'b> { - let start = _fbb.start_table(); - ArgumentValueBuilder { - fbb_: _fbb, - start_: start, - } - } - #[inline] - pub fn finish(self) -> flatbuffers::WIPOffset> { - let o = self.fbb_.end_table(self.start_); - flatbuffers::WIPOffset::new(o.value()) - } + #[inline] + fn default() -> Self { + ArgumentValueArgs { + name: None, + value: None, + } + } +} + +pub struct ArgumentValueBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + start_: flatbuffers::WIPOffset, +} +impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> ArgumentValueBuilder<'a, 'b, A> { + #[inline] + pub fn add_name(&mut self, name: flatbuffers::WIPOffset<&'b str>) { + self.fbb_ + .push_slot_always::>(ArgumentValue::VT_NAME, name); + } + #[inline] + pub fn add_value(&mut self, value: flatbuffers::WIPOffset>) { + self.fbb_ + .push_slot_always::>(ArgumentValue::VT_VALUE, value); + } + #[inline] + pub fn new( + _fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + ) -> ArgumentValueBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + ArgumentValueBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + flatbuffers::WIPOffset::new(o.value()) + } } impl core::fmt::Debug for ArgumentValue<'_> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let mut ds = f.debug_struct("ArgumentValue"); - ds.field("name", &self.name()); - ds.field("value", &self.value()); - ds.finish() - } + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("ArgumentValue"); + ds.field("name", &self.name()); + ds.field("value", &self.value()); + ds.finish() + } } pub enum DirectiveValueOffset {} #[derive(Copy, Clone, PartialEq)] pub struct DirectiveValue<'a> { - pub _tab: flatbuffers::Table<'a>, + pub _tab: flatbuffers::Table<'a>, } impl<'a> flatbuffers::Follow<'a> for DirectiveValue<'a> { - type Inner = DirectiveValue<'a>; - #[inline] - fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { - Self { _tab: flatbuffers::Table { buf, loc } } - } + type Inner = DirectiveValue<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: flatbuffers::Table::new(buf, loc), + } + } } impl<'a> DirectiveValue<'a> { - pub const VT_NAME: flatbuffers::VOffsetT = 4; - pub const VT_ARGUMENTS: flatbuffers::VOffsetT = 6; - - #[inline] - pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self { - DirectiveValue { _tab: table } - } - #[allow(unused_mut)] - pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>( - _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>, - args: &'args DirectiveValueArgs<'args> - ) -> flatbuffers::WIPOffset> { - let mut builder = DirectiveValueBuilder::new(_fbb); - if let Some(x) = args.arguments { builder.add_arguments(x); } - if let Some(x) = args.name { builder.add_name(x); } - builder.finish() - } - - #[inline] - pub fn name(&self) -> Option<&'a str> { - self._tab.get::>(DirectiveValue::VT_NAME, None) - } - #[inline] - pub fn arguments(&self) -> Option>>> { - self._tab.get::>>>(DirectiveValue::VT_ARGUMENTS, None) - } + pub const VT_NAME: flatbuffers::VOffsetT = 4; + pub const VT_ARGUMENTS: flatbuffers::VOffsetT = 6; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + DirectiveValue { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args DirectiveValueArgs<'args>, + ) -> flatbuffers::WIPOffset> { + let mut builder = DirectiveValueBuilder::new(_fbb); + if let Some(x) = args.arguments { + builder.add_arguments(x); + } + if let Some(x) = args.name { + builder.add_name(x); + } + builder.finish() + } + + #[inline] + pub fn name(&self) -> Option<&'a str> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(DirectiveValue::VT_NAME, None) + } + } + #[inline] + pub fn arguments( + &self, + ) -> Option>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab.get::>, + >>(DirectiveValue::VT_ARGUMENTS, None) + } + } } impl flatbuffers::Verifiable for DirectiveValue<'_> { - #[inline] - fn run_verifier( - v: &mut flatbuffers::Verifier, pos: usize - ) -> Result<(), flatbuffers::InvalidFlatbuffer> { - use self::flatbuffers::Verifiable; - v.visit_table(pos)? - .visit_field::>("name", Self::VT_NAME, false)? - .visit_field::>>>("arguments", Self::VT_ARGUMENTS, false)? - .finish(); - Ok(()) - } + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::>("name", Self::VT_NAME, false)? + .visit_field::>, + >>("arguments", Self::VT_ARGUMENTS, false)? + .finish(); + Ok(()) + } } pub struct DirectiveValueArgs<'a> { pub name: Option>, - pub arguments: Option>>>>, + pub arguments: Option< + flatbuffers::WIPOffset< + flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>>, + >, + >, } impl<'a> Default for DirectiveValueArgs<'a> { - #[inline] - fn default() -> Self { - DirectiveValueArgs { - name: None, - arguments: None, - } - } -} - -pub struct DirectiveValueBuilder<'a: 'b, 'b> { - fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>, - start_: flatbuffers::WIPOffset, -} -impl<'a: 'b, 'b> DirectiveValueBuilder<'a, 'b> { - #[inline] - pub fn add_name(&mut self, name: flatbuffers::WIPOffset<&'b str>) { - self.fbb_.push_slot_always::>(DirectiveValue::VT_NAME, name); - } - #[inline] - pub fn add_arguments(&mut self, arguments: flatbuffers::WIPOffset>>>) { - self.fbb_.push_slot_always::>(DirectiveValue::VT_ARGUMENTS, arguments); - } - #[inline] - pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> DirectiveValueBuilder<'a, 'b> { - let start = _fbb.start_table(); - DirectiveValueBuilder { - fbb_: _fbb, - start_: start, - } - } - #[inline] - pub fn finish(self) -> flatbuffers::WIPOffset> { - let o = self.fbb_.end_table(self.start_); - flatbuffers::WIPOffset::new(o.value()) - } + #[inline] + fn default() -> Self { + DirectiveValueArgs { + name: None, + arguments: None, + } + } +} + +pub struct DirectiveValueBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + start_: flatbuffers::WIPOffset, +} +impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> DirectiveValueBuilder<'a, 'b, A> { + #[inline] + pub fn add_name(&mut self, name: flatbuffers::WIPOffset<&'b str>) { + self.fbb_ + .push_slot_always::>(DirectiveValue::VT_NAME, name); + } + #[inline] + pub fn add_arguments( + &mut self, + arguments: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_ + .push_slot_always::>(DirectiveValue::VT_ARGUMENTS, arguments); + } + #[inline] + pub fn new( + _fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + ) -> DirectiveValueBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + DirectiveValueBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + flatbuffers::WIPOffset::new(o.value()) + } } impl core::fmt::Debug for DirectiveValue<'_> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let mut ds = f.debug_struct("DirectiveValue"); - ds.field("name", &self.name()); - ds.field("arguments", &self.arguments()); - ds.finish() - } + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("DirectiveValue"); + ds.field("name", &self.name()); + ds.field("arguments", &self.arguments()); + ds.finish() + } } pub enum ArgumentOffset {} #[derive(Copy, Clone, PartialEq)] pub struct Argument<'a> { - pub _tab: flatbuffers::Table<'a>, + pub _tab: flatbuffers::Table<'a>, } impl<'a> flatbuffers::Follow<'a> for Argument<'a> { - type Inner = Argument<'a>; - #[inline] - fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { - Self { _tab: flatbuffers::Table { buf, loc } } - } + type Inner = Argument<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: flatbuffers::Table::new(buf, loc), + } + } } impl<'a> Argument<'a> { - pub const VT_NAME: flatbuffers::VOffsetT = 4; - pub const VT_TYPE_: flatbuffers::VOffsetT = 6; - pub const VT_VALUE: flatbuffers::VOffsetT = 8; - - #[inline] - pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self { - Argument { _tab: table } - } - #[allow(unused_mut)] - pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>( - _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>, - args: &'args ArgumentArgs<'args> - ) -> flatbuffers::WIPOffset> { - let mut builder = ArgumentBuilder::new(_fbb); - if let Some(x) = args.value { builder.add_value(x); } - if let Some(x) = args.type_ { builder.add_type_(x); } - if let Some(x) = args.name { builder.add_name(x); } - builder.finish() - } - - #[inline] - pub fn name(&self) -> Option<&'a str> { - self._tab.get::>(Argument::VT_NAME, None) - } - #[inline] - pub fn type_(&self) -> Option> { - self._tab.get::>(Argument::VT_TYPE_, None) - } - #[inline] - pub fn value(&self) -> Option> { - self._tab.get::>(Argument::VT_VALUE, None) - } + pub const VT_NAME: flatbuffers::VOffsetT = 4; + pub const VT_TYPE_: flatbuffers::VOffsetT = 6; + pub const VT_VALUE: flatbuffers::VOffsetT = 8; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + Argument { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args ArgumentArgs<'args>, + ) -> flatbuffers::WIPOffset> { + let mut builder = ArgumentBuilder::new(_fbb); + if let Some(x) = args.value { + builder.add_value(x); + } + if let Some(x) = args.type_ { + builder.add_type_(x); + } + if let Some(x) = args.name { + builder.add_name(x); + } + builder.finish() + } + + #[inline] + pub fn name(&self) -> Option<&'a str> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(Argument::VT_NAME, None) + } + } + #[inline] + pub fn type_(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(Argument::VT_TYPE_, None) + } + } + #[inline] + pub fn value(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(Argument::VT_VALUE, None) + } + } } impl flatbuffers::Verifiable for Argument<'_> { - #[inline] - fn run_verifier( - v: &mut flatbuffers::Verifier, pos: usize - ) -> Result<(), flatbuffers::InvalidFlatbuffer> { - use self::flatbuffers::Verifiable; - v.visit_table(pos)? - .visit_field::>("name", Self::VT_NAME, false)? - .visit_field::>("type_", Self::VT_TYPE_, false)? - .visit_field::>("value", Self::VT_VALUE, false)? - .finish(); - Ok(()) - } + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::>("name", Self::VT_NAME, false)? + .visit_field::>( + "type_", + Self::VT_TYPE_, + false, + )? + .visit_field::>( + "value", + Self::VT_VALUE, + false, + )? + .finish(); + Ok(()) + } } pub struct ArgumentArgs<'a> { pub name: Option>, @@ -1568,930 +1974,1366 @@ pub struct ArgumentArgs<'a> { pub value: Option>>, } impl<'a> Default for ArgumentArgs<'a> { - #[inline] - fn default() -> Self { - ArgumentArgs { - name: None, - type_: None, - value: None, - } - } -} - -pub struct ArgumentBuilder<'a: 'b, 'b> { - fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>, - start_: flatbuffers::WIPOffset, -} -impl<'a: 'b, 'b> ArgumentBuilder<'a, 'b> { - #[inline] - pub fn add_name(&mut self, name: flatbuffers::WIPOffset<&'b str>) { - self.fbb_.push_slot_always::>(Argument::VT_NAME, name); - } - #[inline] - pub fn add_type_(&mut self, type_: flatbuffers::WIPOffset>) { - self.fbb_.push_slot_always::>(Argument::VT_TYPE_, type_); - } - #[inline] - pub fn add_value(&mut self, value: flatbuffers::WIPOffset>) { - self.fbb_.push_slot_always::>(Argument::VT_VALUE, value); - } - #[inline] - pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> ArgumentBuilder<'a, 'b> { - let start = _fbb.start_table(); - ArgumentBuilder { - fbb_: _fbb, - start_: start, - } - } - #[inline] - pub fn finish(self) -> flatbuffers::WIPOffset> { - let o = self.fbb_.end_table(self.start_); - flatbuffers::WIPOffset::new(o.value()) - } + #[inline] + fn default() -> Self { + ArgumentArgs { + name: None, + type_: None, + value: None, + } + } +} + +pub struct ArgumentBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + start_: flatbuffers::WIPOffset, +} +impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> ArgumentBuilder<'a, 'b, A> { + #[inline] + pub fn add_name(&mut self, name: flatbuffers::WIPOffset<&'b str>) { + self.fbb_ + .push_slot_always::>(Argument::VT_NAME, name); + } + #[inline] + pub fn add_type_(&mut self, type_: flatbuffers::WIPOffset>) { + self.fbb_ + .push_slot_always::>(Argument::VT_TYPE_, type_); + } + #[inline] + pub fn add_value(&mut self, value: flatbuffers::WIPOffset>) { + self.fbb_ + .push_slot_always::>(Argument::VT_VALUE, value); + } + #[inline] + pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>) -> ArgumentBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + ArgumentBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + flatbuffers::WIPOffset::new(o.value()) + } } impl core::fmt::Debug for Argument<'_> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let mut ds = f.debug_struct("Argument"); - ds.field("name", &self.name()); - ds.field("type_", &self.type_()); - ds.field("value", &self.value()); - ds.finish() - } + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("Argument"); + ds.field("name", &self.name()); + ds.field("type_", &self.type_()); + ds.field("value", &self.value()); + ds.finish() + } } pub enum DirectiveOffset {} #[derive(Copy, Clone, PartialEq)] pub struct Directive<'a> { - pub _tab: flatbuffers::Table<'a>, + pub _tab: flatbuffers::Table<'a>, } impl<'a> flatbuffers::Follow<'a> for Directive<'a> { - type Inner = Directive<'a>; - #[inline] - fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { - Self { _tab: flatbuffers::Table { buf, loc } } - } + type Inner = Directive<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: flatbuffers::Table::new(buf, loc), + } + } } impl<'a> Directive<'a> { - pub const VT_NAME: flatbuffers::VOffsetT = 4; - pub const VT_IS_EXTENSION: flatbuffers::VOffsetT = 6; - pub const VT_ARGUMENTS: flatbuffers::VOffsetT = 8; - pub const VT_LOCATIONS: flatbuffers::VOffsetT = 10; - pub const VT_REPEATABLE: flatbuffers::VOffsetT = 12; - - #[inline] - pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self { - Directive { _tab: table } - } - #[allow(unused_mut)] - pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>( - _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>, - args: &'args DirectiveArgs<'args> - ) -> flatbuffers::WIPOffset> { - let mut builder = DirectiveBuilder::new(_fbb); - if let Some(x) = args.locations { builder.add_locations(x); } - if let Some(x) = args.arguments { builder.add_arguments(x); } - if let Some(x) = args.name { builder.add_name(x); } - builder.add_repeatable(args.repeatable); - builder.add_is_extension(args.is_extension); - builder.finish() - } - - #[inline] - pub fn name(&self) -> Option<&'a str> { - self._tab.get::>(Directive::VT_NAME, None) - } - #[inline] - pub fn is_extension(&self) -> bool { - self._tab.get::(Directive::VT_IS_EXTENSION, Some(false)).unwrap() - } - #[inline] - pub fn arguments(&self) -> Option>>> { - self._tab.get::>>>(Directive::VT_ARGUMENTS, None) - } - #[inline] - pub fn locations(&self) -> Option> { - self._tab.get::>>(Directive::VT_LOCATIONS, None) - } - #[inline] - pub fn repeatable(&self) -> bool { - self._tab.get::(Directive::VT_REPEATABLE, Some(false)).unwrap() - } + pub const VT_NAME: flatbuffers::VOffsetT = 4; + pub const VT_IS_EXTENSION: flatbuffers::VOffsetT = 6; + pub const VT_ARGUMENTS: flatbuffers::VOffsetT = 8; + pub const VT_LOCATIONS: flatbuffers::VOffsetT = 10; + pub const VT_REPEATABLE: flatbuffers::VOffsetT = 12; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + Directive { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args DirectiveArgs<'args>, + ) -> flatbuffers::WIPOffset> { + let mut builder = DirectiveBuilder::new(_fbb); + if let Some(x) = args.locations { + builder.add_locations(x); + } + if let Some(x) = args.arguments { + builder.add_arguments(x); + } + if let Some(x) = args.name { + builder.add_name(x); + } + builder.add_repeatable(args.repeatable); + builder.add_is_extension(args.is_extension); + builder.finish() + } + + #[inline] + pub fn name(&self) -> Option<&'a str> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(Directive::VT_NAME, None) + } + } + #[inline] + pub fn is_extension(&self) -> bool { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::(Directive::VT_IS_EXTENSION, Some(false)) + .unwrap() + } + } + #[inline] + pub fn arguments( + &self, + ) -> Option>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab.get::>, + >>(Directive::VT_ARGUMENTS, None) + } + } + #[inline] + pub fn locations(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>>( + Directive::VT_LOCATIONS, + None, + ) + } + } + #[inline] + pub fn repeatable(&self) -> bool { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::(Directive::VT_REPEATABLE, Some(false)) + .unwrap() + } + } } impl flatbuffers::Verifiable for Directive<'_> { - #[inline] - fn run_verifier( - v: &mut flatbuffers::Verifier, pos: usize - ) -> Result<(), flatbuffers::InvalidFlatbuffer> { - use self::flatbuffers::Verifiable; - v.visit_table(pos)? + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? .visit_field::>("name", Self::VT_NAME, false)? .visit_field::("is_extension", Self::VT_IS_EXTENSION, false)? .visit_field::>>>("arguments", Self::VT_ARGUMENTS, false)? .visit_field::>>("locations", Self::VT_LOCATIONS, false)? .visit_field::("repeatable", Self::VT_REPEATABLE, false)? .finish(); - Ok(()) - } + Ok(()) + } } pub struct DirectiveArgs<'a> { pub name: Option>, pub is_extension: bool, - pub arguments: Option>>>>, + pub arguments: Option< + flatbuffers::WIPOffset>>>, + >, pub locations: Option>>, pub repeatable: bool, } impl<'a> Default for DirectiveArgs<'a> { - #[inline] - fn default() -> Self { - DirectiveArgs { - name: None, - is_extension: false, - arguments: None, - locations: None, - repeatable: false, - } - } -} - -pub struct DirectiveBuilder<'a: 'b, 'b> { - fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>, - start_: flatbuffers::WIPOffset, -} -impl<'a: 'b, 'b> DirectiveBuilder<'a, 'b> { - #[inline] - pub fn add_name(&mut self, name: flatbuffers::WIPOffset<&'b str>) { - self.fbb_.push_slot_always::>(Directive::VT_NAME, name); - } - #[inline] - pub fn add_is_extension(&mut self, is_extension: bool) { - self.fbb_.push_slot::(Directive::VT_IS_EXTENSION, is_extension, false); - } - #[inline] - pub fn add_arguments(&mut self, arguments: flatbuffers::WIPOffset>>>) { - self.fbb_.push_slot_always::>(Directive::VT_ARGUMENTS, arguments); - } - #[inline] - pub fn add_locations(&mut self, locations: flatbuffers::WIPOffset>) { - self.fbb_.push_slot_always::>(Directive::VT_LOCATIONS, locations); - } - #[inline] - pub fn add_repeatable(&mut self, repeatable: bool) { - self.fbb_.push_slot::(Directive::VT_REPEATABLE, repeatable, false); - } - #[inline] - pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> DirectiveBuilder<'a, 'b> { - let start = _fbb.start_table(); - DirectiveBuilder { - fbb_: _fbb, - start_: start, - } - } - #[inline] - pub fn finish(self) -> flatbuffers::WIPOffset> { - let o = self.fbb_.end_table(self.start_); - flatbuffers::WIPOffset::new(o.value()) - } + #[inline] + fn default() -> Self { + DirectiveArgs { + name: None, + is_extension: false, + arguments: None, + locations: None, + repeatable: false, + } + } +} + +pub struct DirectiveBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + start_: flatbuffers::WIPOffset, +} +impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> DirectiveBuilder<'a, 'b, A> { + #[inline] + pub fn add_name(&mut self, name: flatbuffers::WIPOffset<&'b str>) { + self.fbb_ + .push_slot_always::>(Directive::VT_NAME, name); + } + #[inline] + pub fn add_is_extension(&mut self, is_extension: bool) { + self.fbb_ + .push_slot::(Directive::VT_IS_EXTENSION, is_extension, false); + } + #[inline] + pub fn add_arguments( + &mut self, + arguments: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_ + .push_slot_always::>(Directive::VT_ARGUMENTS, arguments); + } + #[inline] + pub fn add_locations( + &mut self, + locations: flatbuffers::WIPOffset>, + ) { + self.fbb_ + .push_slot_always::>(Directive::VT_LOCATIONS, locations); + } + #[inline] + pub fn add_repeatable(&mut self, repeatable: bool) { + self.fbb_ + .push_slot::(Directive::VT_REPEATABLE, repeatable, false); + } + #[inline] + pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>) -> DirectiveBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + DirectiveBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + flatbuffers::WIPOffset::new(o.value()) + } } impl core::fmt::Debug for Directive<'_> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let mut ds = f.debug_struct("Directive"); - ds.field("name", &self.name()); - ds.field("is_extension", &self.is_extension()); - ds.field("arguments", &self.arguments()); - ds.field("locations", &self.locations()); - ds.field("repeatable", &self.repeatable()); - ds.finish() - } + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("Directive"); + ds.field("name", &self.name()); + ds.field("is_extension", &self.is_extension()); + ds.field("arguments", &self.arguments()); + ds.field("locations", &self.locations()); + ds.field("repeatable", &self.repeatable()); + ds.finish() + } } pub enum EnumValueOffset {} #[derive(Copy, Clone, PartialEq)] pub struct EnumValue<'a> { - pub _tab: flatbuffers::Table<'a>, + pub _tab: flatbuffers::Table<'a>, } impl<'a> flatbuffers::Follow<'a> for EnumValue<'a> { - type Inner = EnumValue<'a>; - #[inline] - fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { - Self { _tab: flatbuffers::Table { buf, loc } } - } + type Inner = EnumValue<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: flatbuffers::Table::new(buf, loc), + } + } } impl<'a> EnumValue<'a> { - pub const VT_VALUE: flatbuffers::VOffsetT = 4; - pub const VT_DIRECTIVES: flatbuffers::VOffsetT = 6; - - #[inline] - pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self { - EnumValue { _tab: table } - } - #[allow(unused_mut)] - pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>( - _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>, - args: &'args EnumValueArgs<'args> - ) -> flatbuffers::WIPOffset> { - let mut builder = EnumValueBuilder::new(_fbb); - if let Some(x) = args.directives { builder.add_directives(x); } - if let Some(x) = args.value { builder.add_value(x); } - builder.finish() - } - - #[inline] - pub fn value(&self) -> Option<&'a str> { - self._tab.get::>(EnumValue::VT_VALUE, None) - } - #[inline] - pub fn directives(&self) -> Option>>> { - self._tab.get::>>>(EnumValue::VT_DIRECTIVES, None) - } + pub const VT_VALUE: flatbuffers::VOffsetT = 4; + pub const VT_DIRECTIVES: flatbuffers::VOffsetT = 6; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + EnumValue { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args EnumValueArgs<'args>, + ) -> flatbuffers::WIPOffset> { + let mut builder = EnumValueBuilder::new(_fbb); + if let Some(x) = args.directives { + builder.add_directives(x); + } + if let Some(x) = args.value { + builder.add_value(x); + } + builder.finish() + } + + #[inline] + pub fn value(&self) -> Option<&'a str> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(EnumValue::VT_VALUE, None) + } + } + #[inline] + pub fn directives( + &self, + ) -> Option>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab.get::>, + >>(EnumValue::VT_DIRECTIVES, None) + } + } } impl flatbuffers::Verifiable for EnumValue<'_> { - #[inline] - fn run_verifier( - v: &mut flatbuffers::Verifier, pos: usize - ) -> Result<(), flatbuffers::InvalidFlatbuffer> { - use self::flatbuffers::Verifiable; - v.visit_table(pos)? - .visit_field::>("value", Self::VT_VALUE, false)? - .visit_field::>>>("directives", Self::VT_DIRECTIVES, false)? - .finish(); - Ok(()) - } + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::>("value", Self::VT_VALUE, false)? + .visit_field::>, + >>("directives", Self::VT_DIRECTIVES, false)? + .finish(); + Ok(()) + } } pub struct EnumValueArgs<'a> { pub value: Option>, - pub directives: Option>>>>, + pub directives: Option< + flatbuffers::WIPOffset< + flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>>, + >, + >, } impl<'a> Default for EnumValueArgs<'a> { - #[inline] - fn default() -> Self { - EnumValueArgs { - value: None, - directives: None, - } - } -} - -pub struct EnumValueBuilder<'a: 'b, 'b> { - fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>, - start_: flatbuffers::WIPOffset, -} -impl<'a: 'b, 'b> EnumValueBuilder<'a, 'b> { - #[inline] - pub fn add_value(&mut self, value: flatbuffers::WIPOffset<&'b str>) { - self.fbb_.push_slot_always::>(EnumValue::VT_VALUE, value); - } - #[inline] - pub fn add_directives(&mut self, directives: flatbuffers::WIPOffset>>>) { - self.fbb_.push_slot_always::>(EnumValue::VT_DIRECTIVES, directives); - } - #[inline] - pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> EnumValueBuilder<'a, 'b> { - let start = _fbb.start_table(); - EnumValueBuilder { - fbb_: _fbb, - start_: start, - } - } - #[inline] - pub fn finish(self) -> flatbuffers::WIPOffset> { - let o = self.fbb_.end_table(self.start_); - flatbuffers::WIPOffset::new(o.value()) - } + #[inline] + fn default() -> Self { + EnumValueArgs { + value: None, + directives: None, + } + } +} + +pub struct EnumValueBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + start_: flatbuffers::WIPOffset, +} +impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> EnumValueBuilder<'a, 'b, A> { + #[inline] + pub fn add_value(&mut self, value: flatbuffers::WIPOffset<&'b str>) { + self.fbb_ + .push_slot_always::>(EnumValue::VT_VALUE, value); + } + #[inline] + pub fn add_directives( + &mut self, + directives: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_ + .push_slot_always::>(EnumValue::VT_DIRECTIVES, directives); + } + #[inline] + pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>) -> EnumValueBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + EnumValueBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + flatbuffers::WIPOffset::new(o.value()) + } } impl core::fmt::Debug for EnumValue<'_> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let mut ds = f.debug_struct("EnumValue"); - ds.field("value", &self.value()); - ds.field("directives", &self.directives()); - ds.finish() - } + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("EnumValue"); + ds.field("value", &self.value()); + ds.field("directives", &self.directives()); + ds.finish() + } } pub enum ScalarOffset {} #[derive(Copy, Clone, PartialEq)] pub struct Scalar<'a> { - pub _tab: flatbuffers::Table<'a>, + pub _tab: flatbuffers::Table<'a>, } impl<'a> flatbuffers::Follow<'a> for Scalar<'a> { - type Inner = Scalar<'a>; - #[inline] - fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { - Self { _tab: flatbuffers::Table { buf, loc } } - } + type Inner = Scalar<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: flatbuffers::Table::new(buf, loc), + } + } } impl<'a> Scalar<'a> { - pub const VT_NAME: flatbuffers::VOffsetT = 4; - pub const VT_IS_EXTENSION: flatbuffers::VOffsetT = 6; - pub const VT_DIRECTIVES: flatbuffers::VOffsetT = 8; - - #[inline] - pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self { - Scalar { _tab: table } - } - #[allow(unused_mut)] - pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>( - _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>, - args: &'args ScalarArgs<'args> - ) -> flatbuffers::WIPOffset> { - let mut builder = ScalarBuilder::new(_fbb); - if let Some(x) = args.directives { builder.add_directives(x); } - if let Some(x) = args.name { builder.add_name(x); } - builder.add_is_extension(args.is_extension); - builder.finish() - } - - #[inline] - pub fn name(&self) -> Option<&'a str> { - self._tab.get::>(Scalar::VT_NAME, None) - } - #[inline] - pub fn is_extension(&self) -> bool { - self._tab.get::(Scalar::VT_IS_EXTENSION, Some(false)).unwrap() - } - #[inline] - pub fn directives(&self) -> Option>>> { - self._tab.get::>>>(Scalar::VT_DIRECTIVES, None) - } + pub const VT_NAME: flatbuffers::VOffsetT = 4; + pub const VT_IS_EXTENSION: flatbuffers::VOffsetT = 6; + pub const VT_DIRECTIVES: flatbuffers::VOffsetT = 8; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + Scalar { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args ScalarArgs<'args>, + ) -> flatbuffers::WIPOffset> { + let mut builder = ScalarBuilder::new(_fbb); + if let Some(x) = args.directives { + builder.add_directives(x); + } + if let Some(x) = args.name { + builder.add_name(x); + } + builder.add_is_extension(args.is_extension); + builder.finish() + } + + #[inline] + pub fn name(&self) -> Option<&'a str> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(Scalar::VT_NAME, None) + } + } + #[inline] + pub fn is_extension(&self) -> bool { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::(Scalar::VT_IS_EXTENSION, Some(false)) + .unwrap() + } + } + #[inline] + pub fn directives( + &self, + ) -> Option>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab.get::>, + >>(Scalar::VT_DIRECTIVES, None) + } + } } impl flatbuffers::Verifiable for Scalar<'_> { - #[inline] - fn run_verifier( - v: &mut flatbuffers::Verifier, pos: usize - ) -> Result<(), flatbuffers::InvalidFlatbuffer> { - use self::flatbuffers::Verifiable; - v.visit_table(pos)? - .visit_field::>("name", Self::VT_NAME, false)? - .visit_field::("is_extension", Self::VT_IS_EXTENSION, false)? - .visit_field::>>>("directives", Self::VT_DIRECTIVES, false)? - .finish(); - Ok(()) - } + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::>("name", Self::VT_NAME, false)? + .visit_field::("is_extension", Self::VT_IS_EXTENSION, false)? + .visit_field::>, + >>("directives", Self::VT_DIRECTIVES, false)? + .finish(); + Ok(()) + } } pub struct ScalarArgs<'a> { pub name: Option>, pub is_extension: bool, - pub directives: Option>>>>, + pub directives: Option< + flatbuffers::WIPOffset< + flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>>, + >, + >, } impl<'a> Default for ScalarArgs<'a> { - #[inline] - fn default() -> Self { - ScalarArgs { - name: None, - is_extension: false, - directives: None, - } - } -} - -pub struct ScalarBuilder<'a: 'b, 'b> { - fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>, - start_: flatbuffers::WIPOffset, -} -impl<'a: 'b, 'b> ScalarBuilder<'a, 'b> { - #[inline] - pub fn add_name(&mut self, name: flatbuffers::WIPOffset<&'b str>) { - self.fbb_.push_slot_always::>(Scalar::VT_NAME, name); - } - #[inline] - pub fn add_is_extension(&mut self, is_extension: bool) { - self.fbb_.push_slot::(Scalar::VT_IS_EXTENSION, is_extension, false); - } - #[inline] - pub fn add_directives(&mut self, directives: flatbuffers::WIPOffset>>>) { - self.fbb_.push_slot_always::>(Scalar::VT_DIRECTIVES, directives); - } - #[inline] - pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> ScalarBuilder<'a, 'b> { - let start = _fbb.start_table(); - ScalarBuilder { - fbb_: _fbb, - start_: start, - } - } - #[inline] - pub fn finish(self) -> flatbuffers::WIPOffset> { - let o = self.fbb_.end_table(self.start_); - flatbuffers::WIPOffset::new(o.value()) - } + #[inline] + fn default() -> Self { + ScalarArgs { + name: None, + is_extension: false, + directives: None, + } + } +} + +pub struct ScalarBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + start_: flatbuffers::WIPOffset, +} +impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> ScalarBuilder<'a, 'b, A> { + #[inline] + pub fn add_name(&mut self, name: flatbuffers::WIPOffset<&'b str>) { + self.fbb_ + .push_slot_always::>(Scalar::VT_NAME, name); + } + #[inline] + pub fn add_is_extension(&mut self, is_extension: bool) { + self.fbb_ + .push_slot::(Scalar::VT_IS_EXTENSION, is_extension, false); + } + #[inline] + pub fn add_directives( + &mut self, + directives: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_ + .push_slot_always::>(Scalar::VT_DIRECTIVES, directives); + } + #[inline] + pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>) -> ScalarBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + ScalarBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + flatbuffers::WIPOffset::new(o.value()) + } } impl core::fmt::Debug for Scalar<'_> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let mut ds = f.debug_struct("Scalar"); - ds.field("name", &self.name()); - ds.field("is_extension", &self.is_extension()); - ds.field("directives", &self.directives()); - ds.finish() - } + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("Scalar"); + ds.field("name", &self.name()); + ds.field("is_extension", &self.is_extension()); + ds.field("directives", &self.directives()); + ds.finish() + } } pub enum InputObjectOffset {} #[derive(Copy, Clone, PartialEq)] pub struct InputObject<'a> { - pub _tab: flatbuffers::Table<'a>, + pub _tab: flatbuffers::Table<'a>, } impl<'a> flatbuffers::Follow<'a> for InputObject<'a> { - type Inner = InputObject<'a>; - #[inline] - fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { - Self { _tab: flatbuffers::Table { buf, loc } } - } + type Inner = InputObject<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: flatbuffers::Table::new(buf, loc), + } + } } impl<'a> InputObject<'a> { - pub const VT_NAME: flatbuffers::VOffsetT = 4; - pub const VT_FIELDS: flatbuffers::VOffsetT = 6; - pub const VT_DIRECTIVES: flatbuffers::VOffsetT = 8; - - #[inline] - pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self { - InputObject { _tab: table } - } - #[allow(unused_mut)] - pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>( - _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>, - args: &'args InputObjectArgs<'args> - ) -> flatbuffers::WIPOffset> { - let mut builder = InputObjectBuilder::new(_fbb); - if let Some(x) = args.directives { builder.add_directives(x); } - if let Some(x) = args.fields { builder.add_fields(x); } - if let Some(x) = args.name { builder.add_name(x); } - builder.finish() - } - - #[inline] - pub fn name(&self) -> Option<&'a str> { - self._tab.get::>(InputObject::VT_NAME, None) - } - #[inline] - pub fn fields(&self) -> Option>>> { - self._tab.get::>>>(InputObject::VT_FIELDS, None) - } - #[inline] - pub fn directives(&self) -> Option>>> { - self._tab.get::>>>(InputObject::VT_DIRECTIVES, None) - } + pub const VT_NAME: flatbuffers::VOffsetT = 4; + pub const VT_FIELDS: flatbuffers::VOffsetT = 6; + pub const VT_DIRECTIVES: flatbuffers::VOffsetT = 8; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + InputObject { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args InputObjectArgs<'args>, + ) -> flatbuffers::WIPOffset> { + let mut builder = InputObjectBuilder::new(_fbb); + if let Some(x) = args.directives { + builder.add_directives(x); + } + if let Some(x) = args.fields { + builder.add_fields(x); + } + if let Some(x) = args.name { + builder.add_name(x); + } + builder.finish() + } + + #[inline] + pub fn name(&self) -> Option<&'a str> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(InputObject::VT_NAME, None) + } + } + #[inline] + pub fn fields( + &self, + ) -> Option>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab.get::>, + >>(InputObject::VT_FIELDS, None) + } + } + #[inline] + pub fn directives( + &self, + ) -> Option>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab.get::>, + >>(InputObject::VT_DIRECTIVES, None) + } + } } impl flatbuffers::Verifiable for InputObject<'_> { - #[inline] - fn run_verifier( - v: &mut flatbuffers::Verifier, pos: usize - ) -> Result<(), flatbuffers::InvalidFlatbuffer> { - use self::flatbuffers::Verifiable; - v.visit_table(pos)? - .visit_field::>("name", Self::VT_NAME, false)? - .visit_field::>>>("fields", Self::VT_FIELDS, false)? - .visit_field::>>>("directives", Self::VT_DIRECTIVES, false)? - .finish(); - Ok(()) - } + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::>("name", Self::VT_NAME, false)? + .visit_field::>, + >>("fields", Self::VT_FIELDS, false)? + .visit_field::>, + >>("directives", Self::VT_DIRECTIVES, false)? + .finish(); + Ok(()) + } } pub struct InputObjectArgs<'a> { pub name: Option>, - pub fields: Option>>>>, - pub directives: Option>>>>, + pub fields: Option< + flatbuffers::WIPOffset>>>, + >, + pub directives: Option< + flatbuffers::WIPOffset< + flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>>, + >, + >, } impl<'a> Default for InputObjectArgs<'a> { - #[inline] - fn default() -> Self { - InputObjectArgs { - name: None, - fields: None, - directives: None, - } - } -} - -pub struct InputObjectBuilder<'a: 'b, 'b> { - fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>, - start_: flatbuffers::WIPOffset, -} -impl<'a: 'b, 'b> InputObjectBuilder<'a, 'b> { - #[inline] - pub fn add_name(&mut self, name: flatbuffers::WIPOffset<&'b str>) { - self.fbb_.push_slot_always::>(InputObject::VT_NAME, name); - } - #[inline] - pub fn add_fields(&mut self, fields: flatbuffers::WIPOffset>>>) { - self.fbb_.push_slot_always::>(InputObject::VT_FIELDS, fields); - } - #[inline] - pub fn add_directives(&mut self, directives: flatbuffers::WIPOffset>>>) { - self.fbb_.push_slot_always::>(InputObject::VT_DIRECTIVES, directives); - } - #[inline] - pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> InputObjectBuilder<'a, 'b> { - let start = _fbb.start_table(); - InputObjectBuilder { - fbb_: _fbb, - start_: start, - } - } - #[inline] - pub fn finish(self) -> flatbuffers::WIPOffset> { - let o = self.fbb_.end_table(self.start_); - flatbuffers::WIPOffset::new(o.value()) - } + #[inline] + fn default() -> Self { + InputObjectArgs { + name: None, + fields: None, + directives: None, + } + } +} + +pub struct InputObjectBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + start_: flatbuffers::WIPOffset, +} +impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> InputObjectBuilder<'a, 'b, A> { + #[inline] + pub fn add_name(&mut self, name: flatbuffers::WIPOffset<&'b str>) { + self.fbb_ + .push_slot_always::>(InputObject::VT_NAME, name); + } + #[inline] + pub fn add_fields( + &mut self, + fields: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_ + .push_slot_always::>(InputObject::VT_FIELDS, fields); + } + #[inline] + pub fn add_directives( + &mut self, + directives: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_ + .push_slot_always::>(InputObject::VT_DIRECTIVES, directives); + } + #[inline] + pub fn new( + _fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + ) -> InputObjectBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + InputObjectBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + flatbuffers::WIPOffset::new(o.value()) + } } impl core::fmt::Debug for InputObject<'_> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let mut ds = f.debug_struct("InputObject"); - ds.field("name", &self.name()); - ds.field("fields", &self.fields()); - ds.field("directives", &self.directives()); - ds.finish() - } + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("InputObject"); + ds.field("name", &self.name()); + ds.field("fields", &self.fields()); + ds.field("directives", &self.directives()); + ds.finish() + } } pub enum EnumOffset {} #[derive(Copy, Clone, PartialEq)] pub struct Enum<'a> { - pub _tab: flatbuffers::Table<'a>, + pub _tab: flatbuffers::Table<'a>, } impl<'a> flatbuffers::Follow<'a> for Enum<'a> { - type Inner = Enum<'a>; - #[inline] - fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { - Self { _tab: flatbuffers::Table { buf, loc } } - } + type Inner = Enum<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: flatbuffers::Table::new(buf, loc), + } + } } impl<'a> Enum<'a> { - pub const VT_NAME: flatbuffers::VOffsetT = 4; - pub const VT_IS_EXTENSION: flatbuffers::VOffsetT = 6; - pub const VT_VALUES: flatbuffers::VOffsetT = 8; - pub const VT_DIRECTIVES: flatbuffers::VOffsetT = 10; - - #[inline] - pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self { - Enum { _tab: table } - } - #[allow(unused_mut)] - pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>( - _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>, - args: &'args EnumArgs<'args> - ) -> flatbuffers::WIPOffset> { - let mut builder = EnumBuilder::new(_fbb); - if let Some(x) = args.directives { builder.add_directives(x); } - if let Some(x) = args.values { builder.add_values(x); } - if let Some(x) = args.name { builder.add_name(x); } - builder.add_is_extension(args.is_extension); - builder.finish() - } - - #[inline] - pub fn name(&self) -> Option<&'a str> { - self._tab.get::>(Enum::VT_NAME, None) - } - #[inline] - pub fn is_extension(&self) -> bool { - self._tab.get::(Enum::VT_IS_EXTENSION, Some(false)).unwrap() - } - #[inline] - pub fn values(&self) -> Option>>> { - self._tab.get::>>>(Enum::VT_VALUES, None) - } - #[inline] - pub fn directives(&self) -> Option>>> { - self._tab.get::>>>(Enum::VT_DIRECTIVES, None) - } + pub const VT_NAME: flatbuffers::VOffsetT = 4; + pub const VT_IS_EXTENSION: flatbuffers::VOffsetT = 6; + pub const VT_VALUES: flatbuffers::VOffsetT = 8; + pub const VT_DIRECTIVES: flatbuffers::VOffsetT = 10; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + Enum { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args EnumArgs<'args>, + ) -> flatbuffers::WIPOffset> { + let mut builder = EnumBuilder::new(_fbb); + if let Some(x) = args.directives { + builder.add_directives(x); + } + if let Some(x) = args.values { + builder.add_values(x); + } + if let Some(x) = args.name { + builder.add_name(x); + } + builder.add_is_extension(args.is_extension); + builder.finish() + } + + #[inline] + pub fn name(&self) -> Option<&'a str> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(Enum::VT_NAME, None) + } + } + #[inline] + pub fn is_extension(&self) -> bool { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::(Enum::VT_IS_EXTENSION, Some(false)) + .unwrap() + } + } + #[inline] + pub fn values( + &self, + ) -> Option>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab.get::>, + >>(Enum::VT_VALUES, None) + } + } + #[inline] + pub fn directives( + &self, + ) -> Option>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab.get::>, + >>(Enum::VT_DIRECTIVES, None) + } + } } impl flatbuffers::Verifiable for Enum<'_> { - #[inline] - fn run_verifier( - v: &mut flatbuffers::Verifier, pos: usize - ) -> Result<(), flatbuffers::InvalidFlatbuffer> { - use self::flatbuffers::Verifiable; - v.visit_table(pos)? - .visit_field::>("name", Self::VT_NAME, false)? - .visit_field::("is_extension", Self::VT_IS_EXTENSION, false)? - .visit_field::>>>("values", Self::VT_VALUES, false)? - .visit_field::>>>("directives", Self::VT_DIRECTIVES, false)? - .finish(); - Ok(()) - } + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::>("name", Self::VT_NAME, false)? + .visit_field::("is_extension", Self::VT_IS_EXTENSION, false)? + .visit_field::>, + >>("values", Self::VT_VALUES, false)? + .visit_field::>, + >>("directives", Self::VT_DIRECTIVES, false)? + .finish(); + Ok(()) + } } pub struct EnumArgs<'a> { pub name: Option>, pub is_extension: bool, - pub values: Option>>>>, - pub directives: Option>>>>, + pub values: Option< + flatbuffers::WIPOffset< + flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>>, + >, + >, + pub directives: Option< + flatbuffers::WIPOffset< + flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>>, + >, + >, } impl<'a> Default for EnumArgs<'a> { - #[inline] - fn default() -> Self { - EnumArgs { - name: None, - is_extension: false, - values: None, - directives: None, - } - } -} - -pub struct EnumBuilder<'a: 'b, 'b> { - fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>, - start_: flatbuffers::WIPOffset, -} -impl<'a: 'b, 'b> EnumBuilder<'a, 'b> { - #[inline] - pub fn add_name(&mut self, name: flatbuffers::WIPOffset<&'b str>) { - self.fbb_.push_slot_always::>(Enum::VT_NAME, name); - } - #[inline] - pub fn add_is_extension(&mut self, is_extension: bool) { - self.fbb_.push_slot::(Enum::VT_IS_EXTENSION, is_extension, false); - } - #[inline] - pub fn add_values(&mut self, values: flatbuffers::WIPOffset>>>) { - self.fbb_.push_slot_always::>(Enum::VT_VALUES, values); - } - #[inline] - pub fn add_directives(&mut self, directives: flatbuffers::WIPOffset>>>) { - self.fbb_.push_slot_always::>(Enum::VT_DIRECTIVES, directives); - } - #[inline] - pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> EnumBuilder<'a, 'b> { - let start = _fbb.start_table(); - EnumBuilder { - fbb_: _fbb, - start_: start, - } - } - #[inline] - pub fn finish(self) -> flatbuffers::WIPOffset> { - let o = self.fbb_.end_table(self.start_); - flatbuffers::WIPOffset::new(o.value()) - } -} - -impl core::fmt::Debug for Enum<'_> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let mut ds = f.debug_struct("Enum"); - ds.field("name", &self.name()); - ds.field("is_extension", &self.is_extension()); - ds.field("values", &self.values()); - ds.field("directives", &self.directives()); - ds.finish() - } -} -pub enum ObjectOffset {} -#[derive(Copy, Clone, PartialEq)] - -pub struct Object<'a> { - pub _tab: flatbuffers::Table<'a>, + #[inline] + fn default() -> Self { + EnumArgs { + name: None, + is_extension: false, + values: None, + directives: None, + } + } } -impl<'a> flatbuffers::Follow<'a> for Object<'a> { - type Inner = Object<'a>; - #[inline] - fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { - Self { _tab: flatbuffers::Table { buf, loc } } - } +pub struct EnumBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + start_: flatbuffers::WIPOffset, } - +impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> EnumBuilder<'a, 'b, A> { + #[inline] + pub fn add_name(&mut self, name: flatbuffers::WIPOffset<&'b str>) { + self.fbb_ + .push_slot_always::>(Enum::VT_NAME, name); + } + #[inline] + pub fn add_is_extension(&mut self, is_extension: bool) { + self.fbb_ + .push_slot::(Enum::VT_IS_EXTENSION, is_extension, false); + } + #[inline] + pub fn add_values( + &mut self, + values: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_ + .push_slot_always::>(Enum::VT_VALUES, values); + } + #[inline] + pub fn add_directives( + &mut self, + directives: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_ + .push_slot_always::>(Enum::VT_DIRECTIVES, directives); + } + #[inline] + pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>) -> EnumBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + EnumBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + flatbuffers::WIPOffset::new(o.value()) + } +} + +impl core::fmt::Debug for Enum<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("Enum"); + ds.field("name", &self.name()); + ds.field("is_extension", &self.is_extension()); + ds.field("values", &self.values()); + ds.field("directives", &self.directives()); + ds.finish() + } +} +pub enum ObjectOffset {} +#[derive(Copy, Clone, PartialEq)] + +pub struct Object<'a> { + pub _tab: flatbuffers::Table<'a>, +} + +impl<'a> flatbuffers::Follow<'a> for Object<'a> { + type Inner = Object<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: flatbuffers::Table::new(buf, loc), + } + } +} + impl<'a> Object<'a> { - pub const VT_NAME: flatbuffers::VOffsetT = 4; - pub const VT_IS_EXTENSION: flatbuffers::VOffsetT = 6; - pub const VT_FIELDS: flatbuffers::VOffsetT = 8; - pub const VT_INTERFACES: flatbuffers::VOffsetT = 10; - pub const VT_DIRECTIVES: flatbuffers::VOffsetT = 12; - - #[inline] - pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self { - Object { _tab: table } - } - #[allow(unused_mut)] - pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>( - _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>, - args: &'args ObjectArgs<'args> - ) -> flatbuffers::WIPOffset> { - let mut builder = ObjectBuilder::new(_fbb); - if let Some(x) = args.directives { builder.add_directives(x); } - if let Some(x) = args.interfaces { builder.add_interfaces(x); } - if let Some(x) = args.fields { builder.add_fields(x); } - if let Some(x) = args.name { builder.add_name(x); } - builder.add_is_extension(args.is_extension); - builder.finish() - } - - #[inline] - pub fn name(&self) -> Option<&'a str> { - self._tab.get::>(Object::VT_NAME, None) - } - #[inline] - pub fn is_extension(&self) -> bool { - self._tab.get::(Object::VT_IS_EXTENSION, Some(false)).unwrap() - } - #[inline] - pub fn fields(&self) -> Option> { - self._tab.get::>>(Object::VT_FIELDS, None) - } - #[inline] - pub fn interfaces(&self) -> Option> { - self._tab.get::>>(Object::VT_INTERFACES, None) - } - #[inline] - pub fn directives(&self) -> Option>>> { - self._tab.get::>>>(Object::VT_DIRECTIVES, None) - } + pub const VT_NAME: flatbuffers::VOffsetT = 4; + pub const VT_IS_EXTENSION: flatbuffers::VOffsetT = 6; + pub const VT_FIELDS: flatbuffers::VOffsetT = 8; + pub const VT_INTERFACES: flatbuffers::VOffsetT = 10; + pub const VT_DIRECTIVES: flatbuffers::VOffsetT = 12; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + Object { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args ObjectArgs<'args>, + ) -> flatbuffers::WIPOffset> { + let mut builder = ObjectBuilder::new(_fbb); + if let Some(x) = args.directives { + builder.add_directives(x); + } + if let Some(x) = args.interfaces { + builder.add_interfaces(x); + } + if let Some(x) = args.fields { + builder.add_fields(x); + } + if let Some(x) = args.name { + builder.add_name(x); + } + builder.add_is_extension(args.is_extension); + builder.finish() + } + + #[inline] + pub fn name(&self) -> Option<&'a str> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(Object::VT_NAME, None) + } + } + #[inline] + pub fn is_extension(&self) -> bool { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::(Object::VT_IS_EXTENSION, Some(false)) + .unwrap() + } + } + #[inline] + pub fn fields(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>>( + Object::VT_FIELDS, + None, + ) + } + } + #[inline] + pub fn interfaces(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>>( + Object::VT_INTERFACES, + None, + ) + } + } + #[inline] + pub fn directives( + &self, + ) -> Option>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab.get::>, + >>(Object::VT_DIRECTIVES, None) + } + } } impl flatbuffers::Verifiable for Object<'_> { - #[inline] - fn run_verifier( - v: &mut flatbuffers::Verifier, pos: usize - ) -> Result<(), flatbuffers::InvalidFlatbuffer> { - use self::flatbuffers::Verifiable; - v.visit_table(pos)? - .visit_field::>("name", Self::VT_NAME, false)? - .visit_field::("is_extension", Self::VT_IS_EXTENSION, false)? - .visit_field::>>("fields", Self::VT_FIELDS, false)? - .visit_field::>>("interfaces", Self::VT_INTERFACES, false)? - .visit_field::>>>("directives", Self::VT_DIRECTIVES, false)? - .finish(); - Ok(()) - } + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::>("name", Self::VT_NAME, false)? + .visit_field::("is_extension", Self::VT_IS_EXTENSION, false)? + .visit_field::>>( + "fields", + Self::VT_FIELDS, + false, + )? + .visit_field::>>( + "interfaces", + Self::VT_INTERFACES, + false, + )? + .visit_field::>, + >>("directives", Self::VT_DIRECTIVES, false)? + .finish(); + Ok(()) + } } pub struct ObjectArgs<'a> { pub name: Option>, pub is_extension: bool, pub fields: Option>>, pub interfaces: Option>>, - pub directives: Option>>>>, + pub directives: Option< + flatbuffers::WIPOffset< + flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>>, + >, + >, } impl<'a> Default for ObjectArgs<'a> { - #[inline] - fn default() -> Self { - ObjectArgs { - name: None, - is_extension: false, - fields: None, - interfaces: None, - directives: None, - } - } -} - -pub struct ObjectBuilder<'a: 'b, 'b> { - fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>, - start_: flatbuffers::WIPOffset, -} -impl<'a: 'b, 'b> ObjectBuilder<'a, 'b> { - #[inline] - pub fn add_name(&mut self, name: flatbuffers::WIPOffset<&'b str>) { - self.fbb_.push_slot_always::>(Object::VT_NAME, name); - } - #[inline] - pub fn add_is_extension(&mut self, is_extension: bool) { - self.fbb_.push_slot::(Object::VT_IS_EXTENSION, is_extension, false); - } - #[inline] - pub fn add_fields(&mut self, fields: flatbuffers::WIPOffset>) { - self.fbb_.push_slot_always::>(Object::VT_FIELDS, fields); - } - #[inline] - pub fn add_interfaces(&mut self, interfaces: flatbuffers::WIPOffset>) { - self.fbb_.push_slot_always::>(Object::VT_INTERFACES, interfaces); - } - #[inline] - pub fn add_directives(&mut self, directives: flatbuffers::WIPOffset>>>) { - self.fbb_.push_slot_always::>(Object::VT_DIRECTIVES, directives); - } - #[inline] - pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> ObjectBuilder<'a, 'b> { - let start = _fbb.start_table(); - ObjectBuilder { - fbb_: _fbb, - start_: start, - } - } - #[inline] - pub fn finish(self) -> flatbuffers::WIPOffset> { - let o = self.fbb_.end_table(self.start_); - flatbuffers::WIPOffset::new(o.value()) - } + #[inline] + fn default() -> Self { + ObjectArgs { + name: None, + is_extension: false, + fields: None, + interfaces: None, + directives: None, + } + } +} + +pub struct ObjectBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + start_: flatbuffers::WIPOffset, +} +impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> ObjectBuilder<'a, 'b, A> { + #[inline] + pub fn add_name(&mut self, name: flatbuffers::WIPOffset<&'b str>) { + self.fbb_ + .push_slot_always::>(Object::VT_NAME, name); + } + #[inline] + pub fn add_is_extension(&mut self, is_extension: bool) { + self.fbb_ + .push_slot::(Object::VT_IS_EXTENSION, is_extension, false); + } + #[inline] + pub fn add_fields(&mut self, fields: flatbuffers::WIPOffset>) { + self.fbb_ + .push_slot_always::>(Object::VT_FIELDS, fields); + } + #[inline] + pub fn add_interfaces( + &mut self, + interfaces: flatbuffers::WIPOffset>, + ) { + self.fbb_ + .push_slot_always::>(Object::VT_INTERFACES, interfaces); + } + #[inline] + pub fn add_directives( + &mut self, + directives: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_ + .push_slot_always::>(Object::VT_DIRECTIVES, directives); + } + #[inline] + pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>) -> ObjectBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + ObjectBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + flatbuffers::WIPOffset::new(o.value()) + } } impl core::fmt::Debug for Object<'_> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let mut ds = f.debug_struct("Object"); - ds.field("name", &self.name()); - ds.field("is_extension", &self.is_extension()); - ds.field("fields", &self.fields()); - ds.field("interfaces", &self.interfaces()); - ds.field("directives", &self.directives()); - ds.finish() - } + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("Object"); + ds.field("name", &self.name()); + ds.field("is_extension", &self.is_extension()); + ds.field("fields", &self.fields()); + ds.field("interfaces", &self.interfaces()); + ds.field("directives", &self.directives()); + ds.finish() + } } pub enum InterfaceOffset {} #[derive(Copy, Clone, PartialEq)] pub struct Interface<'a> { - pub _tab: flatbuffers::Table<'a>, + pub _tab: flatbuffers::Table<'a>, } impl<'a> flatbuffers::Follow<'a> for Interface<'a> { - type Inner = Interface<'a>; - #[inline] - fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { - Self { _tab: flatbuffers::Table { buf, loc } } - } + type Inner = Interface<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: flatbuffers::Table::new(buf, loc), + } + } } impl<'a> Interface<'a> { - pub const VT_NAME: flatbuffers::VOffsetT = 4; - pub const VT_IS_EXTENSION: flatbuffers::VOffsetT = 6; - pub const VT_FIELDS: flatbuffers::VOffsetT = 8; - pub const VT_INTERFACES: flatbuffers::VOffsetT = 10; - pub const VT_IMPLEMENTING_INTERFACES: flatbuffers::VOffsetT = 12; - pub const VT_IMPLEMENTING_OBJECTS: flatbuffers::VOffsetT = 14; - pub const VT_DIRECTIVES: flatbuffers::VOffsetT = 16; - - #[inline] - pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self { - Interface { _tab: table } - } - #[allow(unused_mut)] - pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>( - _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>, - args: &'args InterfaceArgs<'args> - ) -> flatbuffers::WIPOffset> { - let mut builder = InterfaceBuilder::new(_fbb); - if let Some(x) = args.directives { builder.add_directives(x); } - if let Some(x) = args.implementing_objects { builder.add_implementing_objects(x); } - if let Some(x) = args.implementing_interfaces { builder.add_implementing_interfaces(x); } - if let Some(x) = args.interfaces { builder.add_interfaces(x); } - if let Some(x) = args.fields { builder.add_fields(x); } - if let Some(x) = args.name { builder.add_name(x); } - builder.add_is_extension(args.is_extension); - builder.finish() - } - - #[inline] - pub fn name(&self) -> Option<&'a str> { - self._tab.get::>(Interface::VT_NAME, None) - } - #[inline] - pub fn is_extension(&self) -> bool { - self._tab.get::(Interface::VT_IS_EXTENSION, Some(false)).unwrap() - } - #[inline] - pub fn fields(&self) -> Option> { - self._tab.get::>>(Interface::VT_FIELDS, None) - } - #[inline] - pub fn interfaces(&self) -> Option> { - self._tab.get::>>(Interface::VT_INTERFACES, None) - } - #[inline] - pub fn implementing_interfaces(&self) -> Option> { - self._tab.get::>>(Interface::VT_IMPLEMENTING_INTERFACES, None) - } - #[inline] - pub fn implementing_objects(&self) -> Option> { - self._tab.get::>>(Interface::VT_IMPLEMENTING_OBJECTS, None) - } - #[inline] - pub fn directives(&self) -> Option>>> { - self._tab.get::>>>(Interface::VT_DIRECTIVES, None) - } + pub const VT_NAME: flatbuffers::VOffsetT = 4; + pub const VT_IS_EXTENSION: flatbuffers::VOffsetT = 6; + pub const VT_FIELDS: flatbuffers::VOffsetT = 8; + pub const VT_INTERFACES: flatbuffers::VOffsetT = 10; + pub const VT_IMPLEMENTING_INTERFACES: flatbuffers::VOffsetT = 12; + pub const VT_IMPLEMENTING_OBJECTS: flatbuffers::VOffsetT = 14; + pub const VT_DIRECTIVES: flatbuffers::VOffsetT = 16; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + Interface { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args InterfaceArgs<'args>, + ) -> flatbuffers::WIPOffset> { + let mut builder = InterfaceBuilder::new(_fbb); + if let Some(x) = args.directives { + builder.add_directives(x); + } + if let Some(x) = args.implementing_objects { + builder.add_implementing_objects(x); + } + if let Some(x) = args.implementing_interfaces { + builder.add_implementing_interfaces(x); + } + if let Some(x) = args.interfaces { + builder.add_interfaces(x); + } + if let Some(x) = args.fields { + builder.add_fields(x); + } + if let Some(x) = args.name { + builder.add_name(x); + } + builder.add_is_extension(args.is_extension); + builder.finish() + } + + #[inline] + pub fn name(&self) -> Option<&'a str> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(Interface::VT_NAME, None) + } + } + #[inline] + pub fn is_extension(&self) -> bool { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::(Interface::VT_IS_EXTENSION, Some(false)) + .unwrap() + } + } + #[inline] + pub fn fields(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>>( + Interface::VT_FIELDS, + None, + ) + } + } + #[inline] + pub fn interfaces(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>>( + Interface::VT_INTERFACES, + None, + ) + } + } + #[inline] + pub fn implementing_interfaces(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>>( + Interface::VT_IMPLEMENTING_INTERFACES, + None, + ) + } + } + #[inline] + pub fn implementing_objects(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>>( + Interface::VT_IMPLEMENTING_OBJECTS, + None, + ) + } + } + #[inline] + pub fn directives( + &self, + ) -> Option>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab.get::>, + >>(Interface::VT_DIRECTIVES, None) + } + } } impl flatbuffers::Verifiable for Interface<'_> { - #[inline] - fn run_verifier( - v: &mut flatbuffers::Verifier, pos: usize - ) -> Result<(), flatbuffers::InvalidFlatbuffer> { - use self::flatbuffers::Verifiable; - v.visit_table(pos)? - .visit_field::>("name", Self::VT_NAME, false)? - .visit_field::("is_extension", Self::VT_IS_EXTENSION, false)? - .visit_field::>>("fields", Self::VT_FIELDS, false)? - .visit_field::>>("interfaces", Self::VT_INTERFACES, false)? - .visit_field::>>("implementing_interfaces", Self::VT_IMPLEMENTING_INTERFACES, false)? - .visit_field::>>("implementing_objects", Self::VT_IMPLEMENTING_OBJECTS, false)? - .visit_field::>>>("directives", Self::VT_DIRECTIVES, false)? - .finish(); - Ok(()) - } + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::>("name", Self::VT_NAME, false)? + .visit_field::("is_extension", Self::VT_IS_EXTENSION, false)? + .visit_field::>>( + "fields", + Self::VT_FIELDS, + false, + )? + .visit_field::>>( + "interfaces", + Self::VT_INTERFACES, + false, + )? + .visit_field::>>( + "implementing_interfaces", + Self::VT_IMPLEMENTING_INTERFACES, + false, + )? + .visit_field::>>( + "implementing_objects", + Self::VT_IMPLEMENTING_OBJECTS, + false, + )? + .visit_field::>, + >>("directives", Self::VT_DIRECTIVES, false)? + .finish(); + Ok(()) + } } pub struct InterfaceArgs<'a> { pub name: Option>, @@ -2500,758 +3342,1152 @@ pub struct InterfaceArgs<'a> { pub interfaces: Option>>, pub implementing_interfaces: Option>>, pub implementing_objects: Option>>, - pub directives: Option>>>>, + pub directives: Option< + flatbuffers::WIPOffset< + flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>>, + >, + >, } impl<'a> Default for InterfaceArgs<'a> { - #[inline] - fn default() -> Self { - InterfaceArgs { - name: None, - is_extension: false, - fields: None, - interfaces: None, - implementing_interfaces: None, - implementing_objects: None, - directives: None, - } - } -} - -pub struct InterfaceBuilder<'a: 'b, 'b> { - fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>, - start_: flatbuffers::WIPOffset, -} -impl<'a: 'b, 'b> InterfaceBuilder<'a, 'b> { - #[inline] - pub fn add_name(&mut self, name: flatbuffers::WIPOffset<&'b str>) { - self.fbb_.push_slot_always::>(Interface::VT_NAME, name); - } - #[inline] - pub fn add_is_extension(&mut self, is_extension: bool) { - self.fbb_.push_slot::(Interface::VT_IS_EXTENSION, is_extension, false); - } - #[inline] - pub fn add_fields(&mut self, fields: flatbuffers::WIPOffset>) { - self.fbb_.push_slot_always::>(Interface::VT_FIELDS, fields); - } - #[inline] - pub fn add_interfaces(&mut self, interfaces: flatbuffers::WIPOffset>) { - self.fbb_.push_slot_always::>(Interface::VT_INTERFACES, interfaces); - } - #[inline] - pub fn add_implementing_interfaces(&mut self, implementing_interfaces: flatbuffers::WIPOffset>) { - self.fbb_.push_slot_always::>(Interface::VT_IMPLEMENTING_INTERFACES, implementing_interfaces); - } - #[inline] - pub fn add_implementing_objects(&mut self, implementing_objects: flatbuffers::WIPOffset>) { - self.fbb_.push_slot_always::>(Interface::VT_IMPLEMENTING_OBJECTS, implementing_objects); - } - #[inline] - pub fn add_directives(&mut self, directives: flatbuffers::WIPOffset>>>) { - self.fbb_.push_slot_always::>(Interface::VT_DIRECTIVES, directives); - } - #[inline] - pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> InterfaceBuilder<'a, 'b> { - let start = _fbb.start_table(); - InterfaceBuilder { - fbb_: _fbb, - start_: start, - } - } - #[inline] - pub fn finish(self) -> flatbuffers::WIPOffset> { - let o = self.fbb_.end_table(self.start_); - flatbuffers::WIPOffset::new(o.value()) - } + #[inline] + fn default() -> Self { + InterfaceArgs { + name: None, + is_extension: false, + fields: None, + interfaces: None, + implementing_interfaces: None, + implementing_objects: None, + directives: None, + } + } +} + +pub struct InterfaceBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + start_: flatbuffers::WIPOffset, +} +impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> InterfaceBuilder<'a, 'b, A> { + #[inline] + pub fn add_name(&mut self, name: flatbuffers::WIPOffset<&'b str>) { + self.fbb_ + .push_slot_always::>(Interface::VT_NAME, name); + } + #[inline] + pub fn add_is_extension(&mut self, is_extension: bool) { + self.fbb_ + .push_slot::(Interface::VT_IS_EXTENSION, is_extension, false); + } + #[inline] + pub fn add_fields(&mut self, fields: flatbuffers::WIPOffset>) { + self.fbb_ + .push_slot_always::>(Interface::VT_FIELDS, fields); + } + #[inline] + pub fn add_interfaces( + &mut self, + interfaces: flatbuffers::WIPOffset>, + ) { + self.fbb_ + .push_slot_always::>(Interface::VT_INTERFACES, interfaces); + } + #[inline] + pub fn add_implementing_interfaces( + &mut self, + implementing_interfaces: flatbuffers::WIPOffset>, + ) { + self.fbb_.push_slot_always::>( + Interface::VT_IMPLEMENTING_INTERFACES, + implementing_interfaces, + ); + } + #[inline] + pub fn add_implementing_objects( + &mut self, + implementing_objects: flatbuffers::WIPOffset>, + ) { + self.fbb_.push_slot_always::>( + Interface::VT_IMPLEMENTING_OBJECTS, + implementing_objects, + ); + } + #[inline] + pub fn add_directives( + &mut self, + directives: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_ + .push_slot_always::>(Interface::VT_DIRECTIVES, directives); + } + #[inline] + pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>) -> InterfaceBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + InterfaceBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + flatbuffers::WIPOffset::new(o.value()) + } } impl core::fmt::Debug for Interface<'_> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let mut ds = f.debug_struct("Interface"); - ds.field("name", &self.name()); - ds.field("is_extension", &self.is_extension()); - ds.field("fields", &self.fields()); - ds.field("interfaces", &self.interfaces()); - ds.field("implementing_interfaces", &self.implementing_interfaces()); - ds.field("implementing_objects", &self.implementing_objects()); - ds.field("directives", &self.directives()); - ds.finish() - } + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("Interface"); + ds.field("name", &self.name()); + ds.field("is_extension", &self.is_extension()); + ds.field("fields", &self.fields()); + ds.field("interfaces", &self.interfaces()); + ds.field("implementing_interfaces", &self.implementing_interfaces()); + ds.field("implementing_objects", &self.implementing_objects()); + ds.field("directives", &self.directives()); + ds.finish() + } } pub enum UnionOffset {} #[derive(Copy, Clone, PartialEq)] pub struct Union<'a> { - pub _tab: flatbuffers::Table<'a>, + pub _tab: flatbuffers::Table<'a>, } impl<'a> flatbuffers::Follow<'a> for Union<'a> { - type Inner = Union<'a>; - #[inline] - fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { - Self { _tab: flatbuffers::Table { buf, loc } } - } + type Inner = Union<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: flatbuffers::Table::new(buf, loc), + } + } } impl<'a> Union<'a> { - pub const VT_NAME: flatbuffers::VOffsetT = 4; - pub const VT_IS_EXTENSION: flatbuffers::VOffsetT = 6; - pub const VT_MEMBERS: flatbuffers::VOffsetT = 8; - pub const VT_DIRECTIVES: flatbuffers::VOffsetT = 10; - - #[inline] - pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self { - Union { _tab: table } - } - #[allow(unused_mut)] - pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>( - _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>, - args: &'args UnionArgs<'args> - ) -> flatbuffers::WIPOffset> { - let mut builder = UnionBuilder::new(_fbb); - if let Some(x) = args.directives { builder.add_directives(x); } - if let Some(x) = args.members { builder.add_members(x); } - if let Some(x) = args.name { builder.add_name(x); } - builder.add_is_extension(args.is_extension); - builder.finish() - } - - #[inline] - pub fn name(&self) -> Option<&'a str> { - self._tab.get::>(Union::VT_NAME, None) - } - #[inline] - pub fn is_extension(&self) -> bool { - self._tab.get::(Union::VT_IS_EXTENSION, Some(false)).unwrap() - } - #[inline] - pub fn members(&self) -> Option> { - self._tab.get::>>(Union::VT_MEMBERS, None) - } - #[inline] - pub fn directives(&self) -> Option>>> { - self._tab.get::>>>(Union::VT_DIRECTIVES, None) - } + pub const VT_NAME: flatbuffers::VOffsetT = 4; + pub const VT_IS_EXTENSION: flatbuffers::VOffsetT = 6; + pub const VT_MEMBERS: flatbuffers::VOffsetT = 8; + pub const VT_DIRECTIVES: flatbuffers::VOffsetT = 10; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + Union { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args UnionArgs<'args>, + ) -> flatbuffers::WIPOffset> { + let mut builder = UnionBuilder::new(_fbb); + if let Some(x) = args.directives { + builder.add_directives(x); + } + if let Some(x) = args.members { + builder.add_members(x); + } + if let Some(x) = args.name { + builder.add_name(x); + } + builder.add_is_extension(args.is_extension); + builder.finish() + } + + #[inline] + pub fn name(&self) -> Option<&'a str> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(Union::VT_NAME, None) + } + } + #[inline] + pub fn is_extension(&self) -> bool { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::(Union::VT_IS_EXTENSION, Some(false)) + .unwrap() + } + } + #[inline] + pub fn members(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>>( + Union::VT_MEMBERS, + None, + ) + } + } + #[inline] + pub fn directives( + &self, + ) -> Option>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab.get::>, + >>(Union::VT_DIRECTIVES, None) + } + } } impl flatbuffers::Verifiable for Union<'_> { - #[inline] - fn run_verifier( - v: &mut flatbuffers::Verifier, pos: usize - ) -> Result<(), flatbuffers::InvalidFlatbuffer> { - use self::flatbuffers::Verifiable; - v.visit_table(pos)? - .visit_field::>("name", Self::VT_NAME, false)? - .visit_field::("is_extension", Self::VT_IS_EXTENSION, false)? - .visit_field::>>("members", Self::VT_MEMBERS, false)? - .visit_field::>>>("directives", Self::VT_DIRECTIVES, false)? - .finish(); - Ok(()) - } + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::>("name", Self::VT_NAME, false)? + .visit_field::("is_extension", Self::VT_IS_EXTENSION, false)? + .visit_field::>>( + "members", + Self::VT_MEMBERS, + false, + )? + .visit_field::>, + >>("directives", Self::VT_DIRECTIVES, false)? + .finish(); + Ok(()) + } } pub struct UnionArgs<'a> { pub name: Option>, pub is_extension: bool, pub members: Option>>, - pub directives: Option>>>>, + pub directives: Option< + flatbuffers::WIPOffset< + flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>>, + >, + >, } impl<'a> Default for UnionArgs<'a> { - #[inline] - fn default() -> Self { - UnionArgs { - name: None, - is_extension: false, - members: None, - directives: None, - } - } -} - -pub struct UnionBuilder<'a: 'b, 'b> { - fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>, - start_: flatbuffers::WIPOffset, -} -impl<'a: 'b, 'b> UnionBuilder<'a, 'b> { - #[inline] - pub fn add_name(&mut self, name: flatbuffers::WIPOffset<&'b str>) { - self.fbb_.push_slot_always::>(Union::VT_NAME, name); - } - #[inline] - pub fn add_is_extension(&mut self, is_extension: bool) { - self.fbb_.push_slot::(Union::VT_IS_EXTENSION, is_extension, false); - } - #[inline] - pub fn add_members(&mut self, members: flatbuffers::WIPOffset>) { - self.fbb_.push_slot_always::>(Union::VT_MEMBERS, members); - } - #[inline] - pub fn add_directives(&mut self, directives: flatbuffers::WIPOffset>>>) { - self.fbb_.push_slot_always::>(Union::VT_DIRECTIVES, directives); - } - #[inline] - pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> UnionBuilder<'a, 'b> { - let start = _fbb.start_table(); - UnionBuilder { - fbb_: _fbb, - start_: start, - } - } - #[inline] - pub fn finish(self) -> flatbuffers::WIPOffset> { - let o = self.fbb_.end_table(self.start_); - flatbuffers::WIPOffset::new(o.value()) - } + #[inline] + fn default() -> Self { + UnionArgs { + name: None, + is_extension: false, + members: None, + directives: None, + } + } +} + +pub struct UnionBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + start_: flatbuffers::WIPOffset, +} +impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> UnionBuilder<'a, 'b, A> { + #[inline] + pub fn add_name(&mut self, name: flatbuffers::WIPOffset<&'b str>) { + self.fbb_ + .push_slot_always::>(Union::VT_NAME, name); + } + #[inline] + pub fn add_is_extension(&mut self, is_extension: bool) { + self.fbb_ + .push_slot::(Union::VT_IS_EXTENSION, is_extension, false); + } + #[inline] + pub fn add_members(&mut self, members: flatbuffers::WIPOffset>) { + self.fbb_ + .push_slot_always::>(Union::VT_MEMBERS, members); + } + #[inline] + pub fn add_directives( + &mut self, + directives: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_ + .push_slot_always::>(Union::VT_DIRECTIVES, directives); + } + #[inline] + pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>) -> UnionBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + UnionBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + flatbuffers::WIPOffset::new(o.value()) + } } impl core::fmt::Debug for Union<'_> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let mut ds = f.debug_struct("Union"); - ds.field("name", &self.name()); - ds.field("is_extension", &self.is_extension()); - ds.field("members", &self.members()); - ds.field("directives", &self.directives()); - ds.finish() - } + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("Union"); + ds.field("name", &self.name()); + ds.field("is_extension", &self.is_extension()); + ds.field("members", &self.members()); + ds.field("directives", &self.directives()); + ds.finish() + } } pub enum FieldOffset {} #[derive(Copy, Clone, PartialEq)] pub struct Field<'a> { - pub _tab: flatbuffers::Table<'a>, + pub _tab: flatbuffers::Table<'a>, } impl<'a> flatbuffers::Follow<'a> for Field<'a> { - type Inner = Field<'a>; - #[inline] - fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { - Self { _tab: flatbuffers::Table { buf, loc } } - } + type Inner = Field<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: flatbuffers::Table::new(buf, loc), + } + } } impl<'a> Field<'a> { - pub const VT_NAME: flatbuffers::VOffsetT = 4; - pub const VT_IS_EXTENSION: flatbuffers::VOffsetT = 6; - pub const VT_ARGUMENTS: flatbuffers::VOffsetT = 8; - pub const VT_TYPE_: flatbuffers::VOffsetT = 10; - pub const VT_DIRECTIVES: flatbuffers::VOffsetT = 12; - pub const VT_PARENT_TYPE: flatbuffers::VOffsetT = 14; - - #[inline] - pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self { - Field { _tab: table } - } - #[allow(unused_mut)] - pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>( - _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>, - args: &'args FieldArgs<'args> - ) -> flatbuffers::WIPOffset> { - let mut builder = FieldBuilder::new(_fbb); - if let Some(x) = args.parent_type { builder.add_parent_type(x); } - if let Some(x) = args.directives { builder.add_directives(x); } - if let Some(x) = args.type_ { builder.add_type_(x); } - if let Some(x) = args.arguments { builder.add_arguments(x); } - if let Some(x) = args.name { builder.add_name(x); } - builder.add_is_extension(args.is_extension); - builder.finish() - } - - #[inline] - pub fn name(&self) -> Option<&'a str> { - self._tab.get::>(Field::VT_NAME, None) - } - #[inline] - pub fn is_extension(&self) -> bool { - self._tab.get::(Field::VT_IS_EXTENSION, Some(false)).unwrap() - } - #[inline] - pub fn arguments(&self) -> Option>>> { - self._tab.get::>>>(Field::VT_ARGUMENTS, None) - } - #[inline] - pub fn type_(&self) -> Option> { - self._tab.get::>(Field::VT_TYPE_, None) - } - #[inline] - pub fn directives(&self) -> Option>>> { - self._tab.get::>>>(Field::VT_DIRECTIVES, None) - } - #[inline] - pub fn parent_type(&self) -> Option> { - self._tab.get::>(Field::VT_PARENT_TYPE, None) - } + pub const VT_NAME: flatbuffers::VOffsetT = 4; + pub const VT_IS_EXTENSION: flatbuffers::VOffsetT = 6; + pub const VT_ARGUMENTS: flatbuffers::VOffsetT = 8; + pub const VT_TYPE_: flatbuffers::VOffsetT = 10; + pub const VT_DIRECTIVES: flatbuffers::VOffsetT = 12; + pub const VT_PARENT_TYPE: flatbuffers::VOffsetT = 14; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + Field { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args FieldArgs<'args>, + ) -> flatbuffers::WIPOffset> { + let mut builder = FieldBuilder::new(_fbb); + if let Some(x) = args.parent_type { + builder.add_parent_type(x); + } + if let Some(x) = args.directives { + builder.add_directives(x); + } + if let Some(x) = args.type_ { + builder.add_type_(x); + } + if let Some(x) = args.arguments { + builder.add_arguments(x); + } + if let Some(x) = args.name { + builder.add_name(x); + } + builder.add_is_extension(args.is_extension); + builder.finish() + } + + #[inline] + pub fn name(&self) -> Option<&'a str> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(Field::VT_NAME, None) + } + } + #[inline] + pub fn is_extension(&self) -> bool { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::(Field::VT_IS_EXTENSION, Some(false)) + .unwrap() + } + } + #[inline] + pub fn arguments( + &self, + ) -> Option>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab.get::>, + >>(Field::VT_ARGUMENTS, None) + } + } + #[inline] + pub fn type_(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(Field::VT_TYPE_, None) + } + } + #[inline] + pub fn directives( + &self, + ) -> Option>>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab.get::>, + >>(Field::VT_DIRECTIVES, None) + } + } + #[inline] + pub fn parent_type(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(Field::VT_PARENT_TYPE, None) + } + } } impl flatbuffers::Verifiable for Field<'_> { - #[inline] - fn run_verifier( - v: &mut flatbuffers::Verifier, pos: usize - ) -> Result<(), flatbuffers::InvalidFlatbuffer> { - use self::flatbuffers::Verifiable; - v.visit_table(pos)? - .visit_field::>("name", Self::VT_NAME, false)? - .visit_field::("is_extension", Self::VT_IS_EXTENSION, false)? - .visit_field::>>>("arguments", Self::VT_ARGUMENTS, false)? - .visit_field::>("type_", Self::VT_TYPE_, false)? - .visit_field::>>>("directives", Self::VT_DIRECTIVES, false)? - .visit_field::>("parent_type", Self::VT_PARENT_TYPE, false)? - .finish(); - Ok(()) - } + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::>("name", Self::VT_NAME, false)? + .visit_field::("is_extension", Self::VT_IS_EXTENSION, false)? + .visit_field::>, + >>("arguments", Self::VT_ARGUMENTS, false)? + .visit_field::>( + "type_", + Self::VT_TYPE_, + false, + )? + .visit_field::>, + >>("directives", Self::VT_DIRECTIVES, false)? + .visit_field::>( + "parent_type", + Self::VT_PARENT_TYPE, + false, + )? + .finish(); + Ok(()) + } } pub struct FieldArgs<'a> { pub name: Option>, pub is_extension: bool, - pub arguments: Option>>>>, + pub arguments: Option< + flatbuffers::WIPOffset>>>, + >, pub type_: Option>>, - pub directives: Option>>>>, + pub directives: Option< + flatbuffers::WIPOffset< + flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>>, + >, + >, pub parent_type: Option>>, } impl<'a> Default for FieldArgs<'a> { - #[inline] - fn default() -> Self { - FieldArgs { - name: None, - is_extension: false, - arguments: None, - type_: None, - directives: None, - parent_type: None, - } - } -} - -pub struct FieldBuilder<'a: 'b, 'b> { - fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>, - start_: flatbuffers::WIPOffset, -} -impl<'a: 'b, 'b> FieldBuilder<'a, 'b> { - #[inline] - pub fn add_name(&mut self, name: flatbuffers::WIPOffset<&'b str>) { - self.fbb_.push_slot_always::>(Field::VT_NAME, name); - } - #[inline] - pub fn add_is_extension(&mut self, is_extension: bool) { - self.fbb_.push_slot::(Field::VT_IS_EXTENSION, is_extension, false); - } - #[inline] - pub fn add_arguments(&mut self, arguments: flatbuffers::WIPOffset>>>) { - self.fbb_.push_slot_always::>(Field::VT_ARGUMENTS, arguments); - } - #[inline] - pub fn add_type_(&mut self, type_: flatbuffers::WIPOffset>) { - self.fbb_.push_slot_always::>(Field::VT_TYPE_, type_); - } - #[inline] - pub fn add_directives(&mut self, directives: flatbuffers::WIPOffset>>>) { - self.fbb_.push_slot_always::>(Field::VT_DIRECTIVES, directives); - } - #[inline] - pub fn add_parent_type(&mut self, parent_type: flatbuffers::WIPOffset>) { - self.fbb_.push_slot_always::>(Field::VT_PARENT_TYPE, parent_type); - } - #[inline] - pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> FieldBuilder<'a, 'b> { - let start = _fbb.start_table(); - FieldBuilder { - fbb_: _fbb, - start_: start, - } - } - #[inline] - pub fn finish(self) -> flatbuffers::WIPOffset> { - let o = self.fbb_.end_table(self.start_); - flatbuffers::WIPOffset::new(o.value()) - } + #[inline] + fn default() -> Self { + FieldArgs { + name: None, + is_extension: false, + arguments: None, + type_: None, + directives: None, + parent_type: None, + } + } +} + +pub struct FieldBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + start_: flatbuffers::WIPOffset, +} +impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> FieldBuilder<'a, 'b, A> { + #[inline] + pub fn add_name(&mut self, name: flatbuffers::WIPOffset<&'b str>) { + self.fbb_ + .push_slot_always::>(Field::VT_NAME, name); + } + #[inline] + pub fn add_is_extension(&mut self, is_extension: bool) { + self.fbb_ + .push_slot::(Field::VT_IS_EXTENSION, is_extension, false); + } + #[inline] + pub fn add_arguments( + &mut self, + arguments: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_ + .push_slot_always::>(Field::VT_ARGUMENTS, arguments); + } + #[inline] + pub fn add_type_(&mut self, type_: flatbuffers::WIPOffset>) { + self.fbb_ + .push_slot_always::>(Field::VT_TYPE_, type_); + } + #[inline] + pub fn add_directives( + &mut self, + directives: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_ + .push_slot_always::>(Field::VT_DIRECTIVES, directives); + } + #[inline] + pub fn add_parent_type(&mut self, parent_type: flatbuffers::WIPOffset>) { + self.fbb_ + .push_slot_always::>(Field::VT_PARENT_TYPE, parent_type); + } + #[inline] + pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>) -> FieldBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + FieldBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + flatbuffers::WIPOffset::new(o.value()) + } } impl core::fmt::Debug for Field<'_> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let mut ds = f.debug_struct("Field"); - ds.field("name", &self.name()); - ds.field("is_extension", &self.is_extension()); - ds.field("arguments", &self.arguments()); - ds.field("type_", &self.type_()); - ds.field("directives", &self.directives()); - ds.field("parent_type", &self.parent_type()); - ds.finish() - } + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("Field"); + ds.field("name", &self.name()); + ds.field("is_extension", &self.is_extension()); + ds.field("arguments", &self.arguments()); + ds.field("type_", &self.type_()); + ds.field("directives", &self.directives()); + ds.field("parent_type", &self.parent_type()); + ds.finish() + } } pub enum TypeMapEntryOffset {} #[derive(Copy, Clone, PartialEq)] pub struct TypeMapEntry<'a> { - pub _tab: flatbuffers::Table<'a>, + pub _tab: flatbuffers::Table<'a>, } impl<'a> flatbuffers::Follow<'a> for TypeMapEntry<'a> { - type Inner = TypeMapEntry<'a>; - #[inline] - fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { - Self { _tab: flatbuffers::Table { buf, loc } } - } + type Inner = TypeMapEntry<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: flatbuffers::Table::new(buf, loc), + } + } } impl<'a> TypeMapEntry<'a> { - pub const VT_NAME: flatbuffers::VOffsetT = 4; - pub const VT_VALUE: flatbuffers::VOffsetT = 6; - - #[inline] - pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self { - TypeMapEntry { _tab: table } - } - #[allow(unused_mut)] - pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>( - _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>, - args: &'args TypeMapEntryArgs<'args> - ) -> flatbuffers::WIPOffset> { - let mut builder = TypeMapEntryBuilder::new(_fbb); - if let Some(x) = args.value { builder.add_value(x); } - if let Some(x) = args.name { builder.add_name(x); } - builder.finish() - } - - #[inline] - pub fn name(&self) -> &'a str { - self._tab.get::>(TypeMapEntry::VT_NAME, None).unwrap() - } - #[inline] - pub fn key_compare_less_than(&self, o: &TypeMapEntry) -> bool { - self.name() < o.name() - } - - #[inline] - pub fn key_compare_with_value(&self, val: & str) -> ::core::cmp::Ordering { - let key = self.name(); - key.cmp(val) - } - #[inline] - pub fn value(&self) -> Option> { - self._tab.get::>(TypeMapEntry::VT_VALUE, None) - } + pub const VT_NAME: flatbuffers::VOffsetT = 4; + pub const VT_VALUE: flatbuffers::VOffsetT = 6; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + TypeMapEntry { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args TypeMapEntryArgs<'args>, + ) -> flatbuffers::WIPOffset> { + let mut builder = TypeMapEntryBuilder::new(_fbb); + if let Some(x) = args.value { + builder.add_value(x); + } + if let Some(x) = args.name { + builder.add_name(x); + } + builder.finish() + } + + #[inline] + pub fn name(&self) -> &'a str { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(TypeMapEntry::VT_NAME, None) + .unwrap() + } + } + #[inline] + pub fn key_compare_less_than(&self, o: &TypeMapEntry) -> bool { + self.name() < o.name() + } + + #[inline] + pub fn key_compare_with_value(&self, val: &str) -> ::core::cmp::Ordering { + let key = self.name(); + key.cmp(val) + } + #[inline] + pub fn value(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(TypeMapEntry::VT_VALUE, None) + } + } } impl flatbuffers::Verifiable for TypeMapEntry<'_> { - #[inline] - fn run_verifier( - v: &mut flatbuffers::Verifier, pos: usize - ) -> Result<(), flatbuffers::InvalidFlatbuffer> { - use self::flatbuffers::Verifiable; - v.visit_table(pos)? - .visit_field::>("name", Self::VT_NAME, true)? - .visit_field::>("value", Self::VT_VALUE, false)? - .finish(); - Ok(()) - } + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::>("name", Self::VT_NAME, true)? + .visit_field::>("value", Self::VT_VALUE, false)? + .finish(); + Ok(()) + } } pub struct TypeMapEntryArgs<'a> { pub name: Option>, pub value: Option>>, } impl<'a> Default for TypeMapEntryArgs<'a> { - #[inline] - fn default() -> Self { - TypeMapEntryArgs { - name: None, // required field - value: None, - } - } -} - -pub struct TypeMapEntryBuilder<'a: 'b, 'b> { - fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>, - start_: flatbuffers::WIPOffset, -} -impl<'a: 'b, 'b> TypeMapEntryBuilder<'a, 'b> { - #[inline] - pub fn add_name(&mut self, name: flatbuffers::WIPOffset<&'b str>) { - self.fbb_.push_slot_always::>(TypeMapEntry::VT_NAME, name); - } - #[inline] - pub fn add_value(&mut self, value: flatbuffers::WIPOffset>) { - self.fbb_.push_slot_always::>(TypeMapEntry::VT_VALUE, value); - } - #[inline] - pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> TypeMapEntryBuilder<'a, 'b> { - let start = _fbb.start_table(); - TypeMapEntryBuilder { - fbb_: _fbb, - start_: start, - } - } - #[inline] - pub fn finish(self) -> flatbuffers::WIPOffset> { - let o = self.fbb_.end_table(self.start_); - self.fbb_.required(o, TypeMapEntry::VT_NAME,"name"); - flatbuffers::WIPOffset::new(o.value()) - } + #[inline] + fn default() -> Self { + TypeMapEntryArgs { + name: None, // required field + value: None, + } + } +} + +pub struct TypeMapEntryBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + start_: flatbuffers::WIPOffset, +} +impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> TypeMapEntryBuilder<'a, 'b, A> { + #[inline] + pub fn add_name(&mut self, name: flatbuffers::WIPOffset<&'b str>) { + self.fbb_ + .push_slot_always::>(TypeMapEntry::VT_NAME, name); + } + #[inline] + pub fn add_value(&mut self, value: flatbuffers::WIPOffset>) { + self.fbb_ + .push_slot_always::>(TypeMapEntry::VT_VALUE, value); + } + #[inline] + pub fn new( + _fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + ) -> TypeMapEntryBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + TypeMapEntryBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + self.fbb_.required(o, TypeMapEntry::VT_NAME, "name"); + flatbuffers::WIPOffset::new(o.value()) + } } impl core::fmt::Debug for TypeMapEntry<'_> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let mut ds = f.debug_struct("TypeMapEntry"); - ds.field("name", &self.name()); - ds.field("value", &self.value()); - ds.finish() - } + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("TypeMapEntry"); + ds.field("name", &self.name()); + ds.field("value", &self.value()); + ds.finish() + } } pub enum DirectiveMapEntryOffset {} #[derive(Copy, Clone, PartialEq)] pub struct DirectiveMapEntry<'a> { - pub _tab: flatbuffers::Table<'a>, + pub _tab: flatbuffers::Table<'a>, } impl<'a> flatbuffers::Follow<'a> for DirectiveMapEntry<'a> { - type Inner = DirectiveMapEntry<'a>; - #[inline] - fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { - Self { _tab: flatbuffers::Table { buf, loc } } - } + type Inner = DirectiveMapEntry<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: flatbuffers::Table::new(buf, loc), + } + } } impl<'a> DirectiveMapEntry<'a> { - pub const VT_NAME: flatbuffers::VOffsetT = 4; - pub const VT_VALUE: flatbuffers::VOffsetT = 6; - - #[inline] - pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self { - DirectiveMapEntry { _tab: table } - } - #[allow(unused_mut)] - pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>( - _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>, - args: &'args DirectiveMapEntryArgs<'args> - ) -> flatbuffers::WIPOffset> { - let mut builder = DirectiveMapEntryBuilder::new(_fbb); - if let Some(x) = args.value { builder.add_value(x); } - if let Some(x) = args.name { builder.add_name(x); } - builder.finish() - } - - #[inline] - pub fn name(&self) -> &'a str { - self._tab.get::>(DirectiveMapEntry::VT_NAME, None).unwrap() - } - #[inline] - pub fn key_compare_less_than(&self, o: &DirectiveMapEntry) -> bool { - self.name() < o.name() - } - - #[inline] - pub fn key_compare_with_value(&self, val: & str) -> ::core::cmp::Ordering { - let key = self.name(); - key.cmp(val) - } - #[inline] - pub fn value(&self) -> Option> { - self._tab.get::>(DirectiveMapEntry::VT_VALUE, None) - } + pub const VT_NAME: flatbuffers::VOffsetT = 4; + pub const VT_VALUE: flatbuffers::VOffsetT = 6; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + DirectiveMapEntry { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args DirectiveMapEntryArgs<'args>, + ) -> flatbuffers::WIPOffset> { + let mut builder = DirectiveMapEntryBuilder::new(_fbb); + if let Some(x) = args.value { + builder.add_value(x); + } + if let Some(x) = args.name { + builder.add_name(x); + } + builder.finish() + } + + #[inline] + pub fn name(&self) -> &'a str { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(DirectiveMapEntry::VT_NAME, None) + .unwrap() + } + } + #[inline] + pub fn key_compare_less_than(&self, o: &DirectiveMapEntry) -> bool { + self.name() < o.name() + } + + #[inline] + pub fn key_compare_with_value(&self, val: &str) -> ::core::cmp::Ordering { + let key = self.name(); + key.cmp(val) + } + #[inline] + pub fn value(&self) -> Option> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>(DirectiveMapEntry::VT_VALUE, None) + } + } } impl flatbuffers::Verifiable for DirectiveMapEntry<'_> { - #[inline] - fn run_verifier( - v: &mut flatbuffers::Verifier, pos: usize - ) -> Result<(), flatbuffers::InvalidFlatbuffer> { - use self::flatbuffers::Verifiable; - v.visit_table(pos)? - .visit_field::>("name", Self::VT_NAME, true)? - .visit_field::>("value", Self::VT_VALUE, false)? - .finish(); - Ok(()) - } + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::>("name", Self::VT_NAME, true)? + .visit_field::>("value", Self::VT_VALUE, false)? + .finish(); + Ok(()) + } } pub struct DirectiveMapEntryArgs<'a> { pub name: Option>, pub value: Option>>, } impl<'a> Default for DirectiveMapEntryArgs<'a> { - #[inline] - fn default() -> Self { - DirectiveMapEntryArgs { - name: None, // required field - value: None, - } - } -} - -pub struct DirectiveMapEntryBuilder<'a: 'b, 'b> { - fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>, - start_: flatbuffers::WIPOffset, -} -impl<'a: 'b, 'b> DirectiveMapEntryBuilder<'a, 'b> { - #[inline] - pub fn add_name(&mut self, name: flatbuffers::WIPOffset<&'b str>) { - self.fbb_.push_slot_always::>(DirectiveMapEntry::VT_NAME, name); - } - #[inline] - pub fn add_value(&mut self, value: flatbuffers::WIPOffset>) { - self.fbb_.push_slot_always::>(DirectiveMapEntry::VT_VALUE, value); - } - #[inline] - pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> DirectiveMapEntryBuilder<'a, 'b> { - let start = _fbb.start_table(); - DirectiveMapEntryBuilder { - fbb_: _fbb, - start_: start, - } - } - #[inline] - pub fn finish(self) -> flatbuffers::WIPOffset> { - let o = self.fbb_.end_table(self.start_); - self.fbb_.required(o, DirectiveMapEntry::VT_NAME,"name"); - flatbuffers::WIPOffset::new(o.value()) - } + #[inline] + fn default() -> Self { + DirectiveMapEntryArgs { + name: None, // required field + value: None, + } + } +} + +pub struct DirectiveMapEntryBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + start_: flatbuffers::WIPOffset, +} +impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> DirectiveMapEntryBuilder<'a, 'b, A> { + #[inline] + pub fn add_name(&mut self, name: flatbuffers::WIPOffset<&'b str>) { + self.fbb_ + .push_slot_always::>(DirectiveMapEntry::VT_NAME, name); + } + #[inline] + pub fn add_value(&mut self, value: flatbuffers::WIPOffset>) { + self.fbb_ + .push_slot_always::>( + DirectiveMapEntry::VT_VALUE, + value, + ); + } + #[inline] + pub fn new( + _fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + ) -> DirectiveMapEntryBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + DirectiveMapEntryBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + self.fbb_.required(o, DirectiveMapEntry::VT_NAME, "name"); + flatbuffers::WIPOffset::new(o.value()) + } } impl core::fmt::Debug for DirectiveMapEntry<'_> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let mut ds = f.debug_struct("DirectiveMapEntry"); - ds.field("name", &self.name()); - ds.field("value", &self.value()); - ds.finish() - } + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("DirectiveMapEntry"); + ds.field("name", &self.name()); + ds.field("value", &self.value()); + ds.finish() + } } pub enum SchemaOffset {} #[derive(Copy, Clone, PartialEq)] pub struct Schema<'a> { - pub _tab: flatbuffers::Table<'a>, + pub _tab: flatbuffers::Table<'a>, } impl<'a> flatbuffers::Follow<'a> for Schema<'a> { - type Inner = Schema<'a>; - #[inline] - fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { - Self { _tab: flatbuffers::Table { buf, loc } } - } + type Inner = Schema<'a>; + #[inline] + unsafe fn follow(buf: &'a [u8], loc: usize) -> Self::Inner { + Self { + _tab: flatbuffers::Table::new(buf, loc), + } + } } impl<'a> Schema<'a> { - pub const VT_QUERY_TYPE: flatbuffers::VOffsetT = 4; - pub const VT_HAS_MUTATION_TYPE: flatbuffers::VOffsetT = 6; - pub const VT_MUTATION_TYPE: flatbuffers::VOffsetT = 8; - pub const VT_HAS_SUBSCRIPTION_TYPE: flatbuffers::VOffsetT = 10; - pub const VT_SUBSCRIPTION_TYPE: flatbuffers::VOffsetT = 12; - pub const VT_TYPES: flatbuffers::VOffsetT = 14; - pub const VT_DIRECTIVES: flatbuffers::VOffsetT = 16; - pub const VT_SCALARS: flatbuffers::VOffsetT = 18; - pub const VT_INPUT_OBJECTS: flatbuffers::VOffsetT = 20; - pub const VT_ENUMS: flatbuffers::VOffsetT = 22; - pub const VT_OBJECTS: flatbuffers::VOffsetT = 24; - pub const VT_INTERFACES: flatbuffers::VOffsetT = 26; - pub const VT_UNIONS: flatbuffers::VOffsetT = 28; - pub const VT_FIELDS: flatbuffers::VOffsetT = 30; - - #[inline] - pub fn init_from_table(table: flatbuffers::Table<'a>) -> Self { - Schema { _tab: table } - } - #[allow(unused_mut)] - pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr>( - _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr>, - args: &'args SchemaArgs<'args> - ) -> flatbuffers::WIPOffset> { - let mut builder = SchemaBuilder::new(_fbb); - if let Some(x) = args.fields { builder.add_fields(x); } - if let Some(x) = args.unions { builder.add_unions(x); } - if let Some(x) = args.interfaces { builder.add_interfaces(x); } - if let Some(x) = args.objects { builder.add_objects(x); } - if let Some(x) = args.enums { builder.add_enums(x); } - if let Some(x) = args.input_objects { builder.add_input_objects(x); } - if let Some(x) = args.scalars { builder.add_scalars(x); } - if let Some(x) = args.directives { builder.add_directives(x); } - if let Some(x) = args.types { builder.add_types(x); } - builder.add_subscription_type(args.subscription_type); - builder.add_mutation_type(args.mutation_type); - builder.add_query_type(args.query_type); - builder.add_has_subscription_type(args.has_subscription_type); - builder.add_has_mutation_type(args.has_mutation_type); - builder.finish() - } - - #[inline] - pub fn query_type(&self) -> u32 { - self._tab.get::(Schema::VT_QUERY_TYPE, Some(0)).unwrap() - } - #[inline] - pub fn has_mutation_type(&self) -> bool { - self._tab.get::(Schema::VT_HAS_MUTATION_TYPE, Some(false)).unwrap() - } - #[inline] - pub fn mutation_type(&self) -> u32 { - self._tab.get::(Schema::VT_MUTATION_TYPE, Some(0)).unwrap() - } - #[inline] - pub fn has_subscription_type(&self) -> bool { - self._tab.get::(Schema::VT_HAS_SUBSCRIPTION_TYPE, Some(false)).unwrap() - } - #[inline] - pub fn subscription_type(&self) -> u32 { - self._tab.get::(Schema::VT_SUBSCRIPTION_TYPE, Some(0)).unwrap() - } - #[inline] - pub fn types(&self) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>> { - self._tab.get::>>>(Schema::VT_TYPES, None).unwrap() - } - #[inline] - pub fn directives(&self) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>> { - self._tab.get::>>>(Schema::VT_DIRECTIVES, None).unwrap() - } - #[inline] - pub fn scalars(&self) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>> { - self._tab.get::>>>(Schema::VT_SCALARS, None).unwrap() - } - #[inline] - pub fn input_objects(&self) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>> { - self._tab.get::>>>(Schema::VT_INPUT_OBJECTS, None).unwrap() - } - #[inline] - pub fn enums(&self) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>> { - self._tab.get::>>>(Schema::VT_ENUMS, None).unwrap() - } - #[inline] - pub fn objects(&self) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>> { - self._tab.get::>>>(Schema::VT_OBJECTS, None).unwrap() - } - #[inline] - pub fn interfaces(&self) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>> { - self._tab.get::>>>(Schema::VT_INTERFACES, None).unwrap() - } - #[inline] - pub fn unions(&self) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>> { - self._tab.get::>>>(Schema::VT_UNIONS, None).unwrap() - } - #[inline] - pub fn fields(&self) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>> { - self._tab.get::>>>(Schema::VT_FIELDS, None).unwrap() - } + pub const VT_QUERY_TYPE: flatbuffers::VOffsetT = 4; + pub const VT_HAS_MUTATION_TYPE: flatbuffers::VOffsetT = 6; + pub const VT_MUTATION_TYPE: flatbuffers::VOffsetT = 8; + pub const VT_HAS_SUBSCRIPTION_TYPE: flatbuffers::VOffsetT = 10; + pub const VT_SUBSCRIPTION_TYPE: flatbuffers::VOffsetT = 12; + pub const VT_TYPES: flatbuffers::VOffsetT = 14; + pub const VT_DIRECTIVES: flatbuffers::VOffsetT = 16; + pub const VT_SCALARS: flatbuffers::VOffsetT = 18; + pub const VT_INPUT_OBJECTS: flatbuffers::VOffsetT = 20; + pub const VT_ENUMS: flatbuffers::VOffsetT = 22; + pub const VT_OBJECTS: flatbuffers::VOffsetT = 24; + pub const VT_INTERFACES: flatbuffers::VOffsetT = 26; + pub const VT_UNIONS: flatbuffers::VOffsetT = 28; + pub const VT_FIELDS: flatbuffers::VOffsetT = 30; + + #[inline] + pub unsafe fn init_from_table(table: flatbuffers::Table<'a>) -> Self { + Schema { _tab: table } + } + #[allow(unused_mut)] + pub fn create<'bldr: 'args, 'args: 'mut_bldr, 'mut_bldr, A: flatbuffers::Allocator + 'bldr>( + _fbb: &'mut_bldr mut flatbuffers::FlatBufferBuilder<'bldr, A>, + args: &'args SchemaArgs<'args>, + ) -> flatbuffers::WIPOffset> { + let mut builder = SchemaBuilder::new(_fbb); + if let Some(x) = args.fields { + builder.add_fields(x); + } + if let Some(x) = args.unions { + builder.add_unions(x); + } + if let Some(x) = args.interfaces { + builder.add_interfaces(x); + } + if let Some(x) = args.objects { + builder.add_objects(x); + } + if let Some(x) = args.enums { + builder.add_enums(x); + } + if let Some(x) = args.input_objects { + builder.add_input_objects(x); + } + if let Some(x) = args.scalars { + builder.add_scalars(x); + } + if let Some(x) = args.directives { + builder.add_directives(x); + } + if let Some(x) = args.types { + builder.add_types(x); + } + builder.add_subscription_type(args.subscription_type); + builder.add_mutation_type(args.mutation_type); + builder.add_query_type(args.query_type); + builder.add_has_subscription_type(args.has_subscription_type); + builder.add_has_mutation_type(args.has_mutation_type); + builder.finish() + } + + #[inline] + pub fn query_type(&self) -> u32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::(Schema::VT_QUERY_TYPE, Some(0)) + .unwrap() + } + } + #[inline] + pub fn has_mutation_type(&self) -> bool { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::(Schema::VT_HAS_MUTATION_TYPE, Some(false)) + .unwrap() + } + } + #[inline] + pub fn mutation_type(&self) -> u32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::(Schema::VT_MUTATION_TYPE, Some(0)) + .unwrap() + } + } + #[inline] + pub fn has_subscription_type(&self) -> bool { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::(Schema::VT_HAS_SUBSCRIPTION_TYPE, Some(false)) + .unwrap() + } + } + #[inline] + pub fn subscription_type(&self) -> u32 { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::(Schema::VT_SUBSCRIPTION_TYPE, Some(0)) + .unwrap() + } + } + #[inline] + pub fn types(&self) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>, + >>(Schema::VT_TYPES, None) + .unwrap() + } + } + #[inline] + pub fn directives( + &self, + ) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>, + >>(Schema::VT_DIRECTIVES, None) + .unwrap() + } + } + #[inline] + pub fn scalars(&self) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>, + >>(Schema::VT_SCALARS, None) + .unwrap() + } + } + #[inline] + pub fn input_objects( + &self, + ) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>, + >>(Schema::VT_INPUT_OBJECTS, None) + .unwrap() + } + } + #[inline] + pub fn enums(&self) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>, + >>(Schema::VT_ENUMS, None) + .unwrap() + } + } + #[inline] + pub fn objects(&self) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>, + >>(Schema::VT_OBJECTS, None) + .unwrap() + } + } + #[inline] + pub fn interfaces( + &self, + ) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>, + >>(Schema::VT_INTERFACES, None) + .unwrap() + } + } + #[inline] + pub fn unions(&self) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>, + >>(Schema::VT_UNIONS, None) + .unwrap() + } + } + #[inline] + pub fn fields(&self) -> flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>> { + // Safety: + // Created from valid Table for this object + // which contains a valid value in this slot + unsafe { + self._tab + .get::>, + >>(Schema::VT_FIELDS, None) + .unwrap() + } + } } impl flatbuffers::Verifiable for Schema<'_> { - #[inline] - fn run_verifier( - v: &mut flatbuffers::Verifier, pos: usize - ) -> Result<(), flatbuffers::InvalidFlatbuffer> { - use self::flatbuffers::Verifiable; - v.visit_table(pos)? - .visit_field::("query_type", Self::VT_QUERY_TYPE, false)? - .visit_field::("has_mutation_type", Self::VT_HAS_MUTATION_TYPE, false)? - .visit_field::("mutation_type", Self::VT_MUTATION_TYPE, false)? - .visit_field::("has_subscription_type", Self::VT_HAS_SUBSCRIPTION_TYPE, false)? - .visit_field::("subscription_type", Self::VT_SUBSCRIPTION_TYPE, false)? - .visit_field::>>>("types", Self::VT_TYPES, true)? - .visit_field::>>>("directives", Self::VT_DIRECTIVES, true)? - .visit_field::>>>("scalars", Self::VT_SCALARS, true)? - .visit_field::>>>("input_objects", Self::VT_INPUT_OBJECTS, true)? - .visit_field::>>>("enums", Self::VT_ENUMS, true)? - .visit_field::>>>("objects", Self::VT_OBJECTS, true)? - .visit_field::>>>("interfaces", Self::VT_INTERFACES, true)? - .visit_field::>>>("unions", Self::VT_UNIONS, true)? - .visit_field::>>>("fields", Self::VT_FIELDS, true)? - .finish(); - Ok(()) - } + #[inline] + fn run_verifier( + v: &mut flatbuffers::Verifier, + pos: usize, + ) -> Result<(), flatbuffers::InvalidFlatbuffer> { + use self::flatbuffers::Verifiable; + v.visit_table(pos)? + .visit_field::("query_type", Self::VT_QUERY_TYPE, false)? + .visit_field::("has_mutation_type", Self::VT_HAS_MUTATION_TYPE, false)? + .visit_field::("mutation_type", Self::VT_MUTATION_TYPE, false)? + .visit_field::( + "has_subscription_type", + Self::VT_HAS_SUBSCRIPTION_TYPE, + false, + )? + .visit_field::("subscription_type", Self::VT_SUBSCRIPTION_TYPE, false)? + .visit_field::>, + >>("types", Self::VT_TYPES, true)? + .visit_field::>, + >>("directives", Self::VT_DIRECTIVES, true)? + .visit_field::>, + >>("scalars", Self::VT_SCALARS, true)? + .visit_field::>, + >>("input_objects", Self::VT_INPUT_OBJECTS, true)? + .visit_field::>, + >>("enums", Self::VT_ENUMS, true)? + .visit_field::>, + >>("objects", Self::VT_OBJECTS, true)? + .visit_field::>, + >>("interfaces", Self::VT_INTERFACES, true)? + .visit_field::>, + >>("unions", Self::VT_UNIONS, true)? + .visit_field::>, + >>("fields", Self::VT_FIELDS, true)? + .finish(); + Ok(()) + } } pub struct SchemaArgs<'a> { pub query_type: u32, @@ -3259,155 +4495,232 @@ pub struct SchemaArgs<'a> { pub mutation_type: u32, pub has_subscription_type: bool, pub subscription_type: u32, - pub types: Option>>>>, - pub directives: Option>>>>, - pub scalars: Option>>>>, - pub input_objects: Option>>>>, - pub enums: Option>>>>, - pub objects: Option>>>>, - pub interfaces: Option>>>>, - pub unions: Option>>>>, - pub fields: Option>>>>, + pub types: Option< + flatbuffers::WIPOffset< + flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>>, + >, + >, + pub directives: Option< + flatbuffers::WIPOffset< + flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>>, + >, + >, + pub scalars: Option< + flatbuffers::WIPOffset>>>, + >, + pub input_objects: Option< + flatbuffers::WIPOffset< + flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>>, + >, + >, + pub enums: Option< + flatbuffers::WIPOffset>>>, + >, + pub objects: Option< + flatbuffers::WIPOffset>>>, + >, + pub interfaces: Option< + flatbuffers::WIPOffset< + flatbuffers::Vector<'a, flatbuffers::ForwardsUOffset>>, + >, + >, + pub unions: Option< + flatbuffers::WIPOffset>>>, + >, + pub fields: Option< + flatbuffers::WIPOffset>>>, + >, } impl<'a> Default for SchemaArgs<'a> { - #[inline] - fn default() -> Self { - SchemaArgs { - query_type: 0, - has_mutation_type: false, - mutation_type: 0, - has_subscription_type: false, - subscription_type: 0, - types: None, // required field - directives: None, // required field - scalars: None, // required field - input_objects: None, // required field - enums: None, // required field - objects: None, // required field - interfaces: None, // required field - unions: None, // required field - fields: None, // required field - } - } -} - -pub struct SchemaBuilder<'a: 'b, 'b> { - fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a>, - start_: flatbuffers::WIPOffset, -} -impl<'a: 'b, 'b> SchemaBuilder<'a, 'b> { - #[inline] - pub fn add_query_type(&mut self, query_type: u32) { - self.fbb_.push_slot::(Schema::VT_QUERY_TYPE, query_type, 0); - } - #[inline] - pub fn add_has_mutation_type(&mut self, has_mutation_type: bool) { - self.fbb_.push_slot::(Schema::VT_HAS_MUTATION_TYPE, has_mutation_type, false); - } - #[inline] - pub fn add_mutation_type(&mut self, mutation_type: u32) { - self.fbb_.push_slot::(Schema::VT_MUTATION_TYPE, mutation_type, 0); - } - #[inline] - pub fn add_has_subscription_type(&mut self, has_subscription_type: bool) { - self.fbb_.push_slot::(Schema::VT_HAS_SUBSCRIPTION_TYPE, has_subscription_type, false); - } - #[inline] - pub fn add_subscription_type(&mut self, subscription_type: u32) { - self.fbb_.push_slot::(Schema::VT_SUBSCRIPTION_TYPE, subscription_type, 0); - } - #[inline] - pub fn add_types(&mut self, types: flatbuffers::WIPOffset>>>) { - self.fbb_.push_slot_always::>(Schema::VT_TYPES, types); - } - #[inline] - pub fn add_directives(&mut self, directives: flatbuffers::WIPOffset>>>) { - self.fbb_.push_slot_always::>(Schema::VT_DIRECTIVES, directives); - } - #[inline] - pub fn add_scalars(&mut self, scalars: flatbuffers::WIPOffset>>>) { - self.fbb_.push_slot_always::>(Schema::VT_SCALARS, scalars); - } - #[inline] - pub fn add_input_objects(&mut self, input_objects: flatbuffers::WIPOffset>>>) { - self.fbb_.push_slot_always::>(Schema::VT_INPUT_OBJECTS, input_objects); - } - #[inline] - pub fn add_enums(&mut self, enums: flatbuffers::WIPOffset>>>) { - self.fbb_.push_slot_always::>(Schema::VT_ENUMS, enums); - } - #[inline] - pub fn add_objects(&mut self, objects: flatbuffers::WIPOffset>>>) { - self.fbb_.push_slot_always::>(Schema::VT_OBJECTS, objects); - } - #[inline] - pub fn add_interfaces(&mut self, interfaces: flatbuffers::WIPOffset>>>) { - self.fbb_.push_slot_always::>(Schema::VT_INTERFACES, interfaces); - } - #[inline] - pub fn add_unions(&mut self, unions: flatbuffers::WIPOffset>>>) { - self.fbb_.push_slot_always::>(Schema::VT_UNIONS, unions); - } - #[inline] - pub fn add_fields(&mut self, fields: flatbuffers::WIPOffset>>>) { - self.fbb_.push_slot_always::>(Schema::VT_FIELDS, fields); - } - #[inline] - pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>) -> SchemaBuilder<'a, 'b> { - let start = _fbb.start_table(); - SchemaBuilder { - fbb_: _fbb, - start_: start, - } - } - #[inline] - pub fn finish(self) -> flatbuffers::WIPOffset> { - let o = self.fbb_.end_table(self.start_); - self.fbb_.required(o, Schema::VT_TYPES,"types"); - self.fbb_.required(o, Schema::VT_DIRECTIVES,"directives"); - self.fbb_.required(o, Schema::VT_SCALARS,"scalars"); - self.fbb_.required(o, Schema::VT_INPUT_OBJECTS,"input_objects"); - self.fbb_.required(o, Schema::VT_ENUMS,"enums"); - self.fbb_.required(o, Schema::VT_OBJECTS,"objects"); - self.fbb_.required(o, Schema::VT_INTERFACES,"interfaces"); - self.fbb_.required(o, Schema::VT_UNIONS,"unions"); - self.fbb_.required(o, Schema::VT_FIELDS,"fields"); - flatbuffers::WIPOffset::new(o.value()) - } + #[inline] + fn default() -> Self { + SchemaArgs { + query_type: 0, + has_mutation_type: false, + mutation_type: 0, + has_subscription_type: false, + subscription_type: 0, + types: None, // required field + directives: None, // required field + scalars: None, // required field + input_objects: None, // required field + enums: None, // required field + objects: None, // required field + interfaces: None, // required field + unions: None, // required field + fields: None, // required field + } + } } -impl core::fmt::Debug for Schema<'_> { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - let mut ds = f.debug_struct("Schema"); - ds.field("query_type", &self.query_type()); - ds.field("has_mutation_type", &self.has_mutation_type()); - ds.field("mutation_type", &self.mutation_type()); - ds.field("has_subscription_type", &self.has_subscription_type()); - ds.field("subscription_type", &self.subscription_type()); - ds.field("types", &self.types()); - ds.field("directives", &self.directives()); - ds.field("scalars", &self.scalars()); - ds.field("input_objects", &self.input_objects()); - ds.field("enums", &self.enums()); - ds.field("objects", &self.objects()); - ds.field("interfaces", &self.interfaces()); - ds.field("unions", &self.unions()); - ds.field("fields", &self.fields()); - ds.finish() - } +pub struct SchemaBuilder<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> { + fbb_: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + start_: flatbuffers::WIPOffset, } -#[inline] -#[deprecated(since="2.0.0", note="Deprecated in favor of `root_as...` methods.")] -pub fn get_root_as_schema<'a>(buf: &'a [u8]) -> Schema<'a> { - unsafe { flatbuffers::root_unchecked::>(buf) } +impl<'a: 'b, 'b, A: flatbuffers::Allocator + 'a> SchemaBuilder<'a, 'b, A> { + #[inline] + pub fn add_query_type(&mut self, query_type: u32) { + self.fbb_ + .push_slot::(Schema::VT_QUERY_TYPE, query_type, 0); + } + #[inline] + pub fn add_has_mutation_type(&mut self, has_mutation_type: bool) { + self.fbb_ + .push_slot::(Schema::VT_HAS_MUTATION_TYPE, has_mutation_type, false); + } + #[inline] + pub fn add_mutation_type(&mut self, mutation_type: u32) { + self.fbb_ + .push_slot::(Schema::VT_MUTATION_TYPE, mutation_type, 0); + } + #[inline] + pub fn add_has_subscription_type(&mut self, has_subscription_type: bool) { + self.fbb_.push_slot::( + Schema::VT_HAS_SUBSCRIPTION_TYPE, + has_subscription_type, + false, + ); + } + #[inline] + pub fn add_subscription_type(&mut self, subscription_type: u32) { + self.fbb_ + .push_slot::(Schema::VT_SUBSCRIPTION_TYPE, subscription_type, 0); + } + #[inline] + pub fn add_types( + &mut self, + types: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_ + .push_slot_always::>(Schema::VT_TYPES, types); + } + #[inline] + pub fn add_directives( + &mut self, + directives: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_ + .push_slot_always::>(Schema::VT_DIRECTIVES, directives); + } + #[inline] + pub fn add_scalars( + &mut self, + scalars: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_ + .push_slot_always::>(Schema::VT_SCALARS, scalars); + } + #[inline] + pub fn add_input_objects( + &mut self, + input_objects: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_ + .push_slot_always::>(Schema::VT_INPUT_OBJECTS, input_objects); + } + #[inline] + pub fn add_enums( + &mut self, + enums: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_ + .push_slot_always::>(Schema::VT_ENUMS, enums); + } + #[inline] + pub fn add_objects( + &mut self, + objects: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_ + .push_slot_always::>(Schema::VT_OBJECTS, objects); + } + #[inline] + pub fn add_interfaces( + &mut self, + interfaces: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_ + .push_slot_always::>(Schema::VT_INTERFACES, interfaces); + } + #[inline] + pub fn add_unions( + &mut self, + unions: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_ + .push_slot_always::>(Schema::VT_UNIONS, unions); + } + #[inline] + pub fn add_fields( + &mut self, + fields: flatbuffers::WIPOffset< + flatbuffers::Vector<'b, flatbuffers::ForwardsUOffset>>, + >, + ) { + self.fbb_ + .push_slot_always::>(Schema::VT_FIELDS, fields); + } + #[inline] + pub fn new(_fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>) -> SchemaBuilder<'a, 'b, A> { + let start = _fbb.start_table(); + SchemaBuilder { + fbb_: _fbb, + start_: start, + } + } + #[inline] + pub fn finish(self) -> flatbuffers::WIPOffset> { + let o = self.fbb_.end_table(self.start_); + self.fbb_.required(o, Schema::VT_TYPES, "types"); + self.fbb_.required(o, Schema::VT_DIRECTIVES, "directives"); + self.fbb_.required(o, Schema::VT_SCALARS, "scalars"); + self.fbb_ + .required(o, Schema::VT_INPUT_OBJECTS, "input_objects"); + self.fbb_.required(o, Schema::VT_ENUMS, "enums"); + self.fbb_.required(o, Schema::VT_OBJECTS, "objects"); + self.fbb_.required(o, Schema::VT_INTERFACES, "interfaces"); + self.fbb_.required(o, Schema::VT_UNIONS, "unions"); + self.fbb_.required(o, Schema::VT_FIELDS, "fields"); + flatbuffers::WIPOffset::new(o.value()) + } } -#[inline] -#[deprecated(since="2.0.0", note="Deprecated in favor of `root_as...` methods.")] -pub fn get_size_prefixed_root_as_schema<'a>(buf: &'a [u8]) -> Schema<'a> { - unsafe { flatbuffers::size_prefixed_root_unchecked::>(buf) } +impl core::fmt::Debug for Schema<'_> { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let mut ds = f.debug_struct("Schema"); + ds.field("query_type", &self.query_type()); + ds.field("has_mutation_type", &self.has_mutation_type()); + ds.field("mutation_type", &self.mutation_type()); + ds.field("has_subscription_type", &self.has_subscription_type()); + ds.field("subscription_type", &self.subscription_type()); + ds.field("types", &self.types()); + ds.field("directives", &self.directives()); + ds.field("scalars", &self.scalars()); + ds.field("input_objects", &self.input_objects()); + ds.field("enums", &self.enums()); + ds.field("objects", &self.objects()); + ds.field("interfaces", &self.interfaces()); + ds.field("unions", &self.unions()); + ds.field("fields", &self.fields()); + ds.finish() + } } - #[inline] /// Verifies that a buffer of bytes contains a `Schema` /// and returns it. @@ -3416,7 +4729,7 @@ pub fn get_size_prefixed_root_as_schema<'a>(buf: &'a [u8]) -> Schema<'a> { /// previous, unchecked, behavior use /// `root_as_schema_unchecked`. pub fn root_as_schema(buf: &[u8]) -> Result { - flatbuffers::root::(buf) + flatbuffers::root::(buf) } #[inline] /// Verifies that a buffer of bytes contains a size prefixed @@ -3426,7 +4739,7 @@ pub fn root_as_schema(buf: &[u8]) -> Result Result { - flatbuffers::size_prefixed_root::(buf) + flatbuffers::size_prefixed_root::(buf) } #[inline] /// Verifies, with the given options, that a buffer of bytes @@ -3436,10 +4749,10 @@ pub fn size_prefixed_root_as_schema(buf: &[u8]) -> Result( - opts: &'o flatbuffers::VerifierOptions, - buf: &'b [u8], + opts: &'o flatbuffers::VerifierOptions, + buf: &'b [u8], ) -> Result, flatbuffers::InvalidFlatbuffer> { - flatbuffers::root_with_opts::>(opts, buf) + flatbuffers::root_with_opts::>(opts, buf) } #[inline] /// Verifies, with the given verifier options, that a buffer of @@ -3449,33 +4762,37 @@ pub fn root_as_schema_with_opts<'b, 'o>( /// previous, unchecked, behavior use /// `root_as_schema_unchecked`. pub fn size_prefixed_root_as_schema_with_opts<'b, 'o>( - opts: &'o flatbuffers::VerifierOptions, - buf: &'b [u8], + opts: &'o flatbuffers::VerifierOptions, + buf: &'b [u8], ) -> Result, flatbuffers::InvalidFlatbuffer> { - flatbuffers::size_prefixed_root_with_opts::>(opts, buf) + flatbuffers::size_prefixed_root_with_opts::>(opts, buf) } #[inline] /// Assumes, without verification, that a buffer of bytes contains a Schema and returns it. /// # Safety /// Callers must trust the given bytes do indeed contain a valid `Schema`. pub unsafe fn root_as_schema_unchecked(buf: &[u8]) -> Schema { - flatbuffers::root_unchecked::(buf) + flatbuffers::root_unchecked::(buf) } #[inline] /// Assumes, without verification, that a buffer of bytes contains a size prefixed Schema and returns it. /// # Safety /// Callers must trust the given bytes do indeed contain a valid size prefixed `Schema`. pub unsafe fn size_prefixed_root_as_schema_unchecked(buf: &[u8]) -> Schema { - flatbuffers::size_prefixed_root_unchecked::(buf) + flatbuffers::size_prefixed_root_unchecked::(buf) } #[inline] -pub fn finish_schema_buffer<'a, 'b>( - fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>, - root: flatbuffers::WIPOffset>) { - fbb.finish(root, None); +pub fn finish_schema_buffer<'a, 'b, A: flatbuffers::Allocator + 'a>( + fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + root: flatbuffers::WIPOffset>, +) { + fbb.finish(root, None); } #[inline] -pub fn finish_size_prefixed_schema_buffer<'a, 'b>(fbb: &'b mut flatbuffers::FlatBufferBuilder<'a>, root: flatbuffers::WIPOffset>) { - fbb.finish_size_prefixed(root, None); +pub fn finish_size_prefixed_schema_buffer<'a, 'b, A: flatbuffers::Allocator + 'a>( + fbb: &'b mut flatbuffers::FlatBufferBuilder<'a, A>, + root: flatbuffers::WIPOffset>, +) { + fbb.finish_size_prefixed(root, None); } diff --git a/compiler/crates/schema-print/Cargo.toml b/compiler/crates/schema-print/Cargo.toml index 7939af1914afe..459641cc3f643 100644 --- a/compiler/crates/schema-print/Cargo.toml +++ b/compiler/crates/schema-print/Cargo.toml @@ -4,7 +4,7 @@ name = "schema-print" version = "0.0.0" authors = ["Facebook"] -edition = "2021" +edition = "2024" repository = "https://github.com/facebook/relay" license = "MIT" @@ -23,11 +23,11 @@ path = "tests/print_schema_test.rs" [dependencies] fnv = "1.0" intern = { path = "../intern" } -itertools = "0.13.0" +itertools = "0.14.0" rayon = "1.9.0" schema = { path = "../schema" } [dev-dependencies] diff = "0.1" fixture-tests = { path = "../fixture-tests" } -tokio = { version = "1.41.0", features = ["full", "test-util", "tracing"] } +tokio = { version = "1.46.1", features = ["full", "test-util", "tracing"] } diff --git a/compiler/crates/schema-print/src/print_schema.rs b/compiler/crates/schema-print/src/print_schema.rs index 5ab82d4c42e4f..b3a17534e372c 100644 --- a/compiler/crates/schema-print/src/print_schema.rs +++ b/compiler/crates/schema-print/src/print_schema.rs @@ -9,21 +9,21 @@ use std::fmt::Result as FmtResult; use std::fmt::Write; use fnv::FnvHashMap; -use intern::string_key::StringKey; use intern::Lookup; +use intern::string_key::StringKey; use itertools::Itertools; use rayon::iter::IntoParallelIterator; use rayon::iter::ParallelIterator; use schema::*; +use crate::DEAULT_SHARD_COUNT; +use crate::Printer; +use crate::ShardPrinter; +use crate::TypedShardPrinter; use crate::generate_shard_map; use crate::generate_typed_shard_map; use crate::has_schema_definition_types; use crate::is_schema_of_common_name; -use crate::Printer; -use crate::ShardPrinter; -use crate::TypedShardPrinter; -use crate::DEAULT_SHARD_COUNT; pub fn print(schema: &SDLSchema) -> String { let mut builder: String = String::new(); diff --git a/compiler/crates/schema-print/src/printer.rs b/compiler/crates/schema-print/src/printer.rs index 62054b6a76d88..b4afae0857fee 100644 --- a/compiler/crates/schema-print/src/printer.rs +++ b/compiler/crates/schema-print/src/printer.rs @@ -10,9 +10,9 @@ use std::fmt::Result as FmtResult; use std::fmt::Write; use fnv::FnvHashMap; +use intern::Lookup; use intern::string_key::Intern; use intern::string_key::StringKey; -use intern::Lookup; use itertools::Itertools; use schema::*; diff --git a/compiler/crates/schema-print/src/shard_printer.rs b/compiler/crates/schema-print/src/shard_printer.rs index a0ef3ca943390..5c041586377c3 100644 --- a/compiler/crates/schema-print/src/shard_printer.rs +++ b/compiler/crates/schema-print/src/shard_printer.rs @@ -7,8 +7,8 @@ use fnv::FnvHashMap; use fnv::FnvHashSet; -use intern::string_key::StringKey; use intern::Lookup; +use intern::string_key::StringKey; use rayon::iter::IntoParallelIterator; use rayon::iter::ParallelIterator; use schema::FieldID; @@ -16,8 +16,8 @@ use schema::SDLSchema; use schema::Schema; use schema::Type; -use crate::calculate_hash; use crate::Printer; +use crate::calculate_hash; pub struct ShardPrinter<'schema> { schema: &'schema SDLSchema, diff --git a/compiler/crates/schema-validate/Cargo.toml b/compiler/crates/schema-validate/Cargo.toml index cdbd1a62b35bb..53857279f7323 100644 --- a/compiler/crates/schema-validate/Cargo.toml +++ b/compiler/crates/schema-validate/Cargo.toml @@ -4,7 +4,7 @@ name = "schema-validate-lib" version = "0.0.0" authors = ["Facebook"] -edition = "2021" +edition = "2024" repository = "https://github.com/facebook/relay" license = "MIT" @@ -17,18 +17,18 @@ name = "schema_validate_test" path = "tests/validate_schema_test.rs" [dependencies] -clap = { version = "4.5.20", features = ["derive", "env", "string", "unicode", "wrap_help"] } +clap = { version = "4.5.42", features = ["derive", "env", "string", "unicode", "wrap_help"] } common = { path = "../common" } fnv = "1.0" graphql-cli = { path = "../graphql-cli" } intern = { path = "../intern" } -lazy_static = "1.4" +lazy_static = "1.5" rayon = "1.9.0" -regex = "1.9.2" +regex = "1.11.1" schema = { path = "../schema" } -serde = { version = "1.0.185", features = ["derive", "rc"] } -thiserror = "1.0.64" +serde = { version = "1.0.219", features = ["derive", "rc"] } +thiserror = "2.0.12" [dev-dependencies] fixture-tests = { path = "../fixture-tests" } -tokio = { version = "1.41.0", features = ["full", "test-util", "tracing"] } +tokio = { version = "1.46.1", features = ["full", "test-util", "tracing"] } diff --git a/compiler/crates/schema-validate/src/lib.rs b/compiler/crates/schema-validate/src/lib.rs index b1da358228cf6..858e438224dc6 100644 --- a/compiler/crates/schema-validate/src/lib.rs +++ b/compiler/crates/schema-validate/src/lib.rs @@ -19,9 +19,9 @@ use common::WithLocation; use errors::*; use fnv::FnvHashMap; use fnv::FnvHashSet; +use intern::Lookup; use intern::string_key::Intern; use intern::string_key::StringKey; -use intern::Lookup; use lazy_static::lazy_static; use rayon::prelude::*; use regex::Regex; diff --git a/compiler/crates/schema-validate/src/main.rs b/compiler/crates/schema-validate/src/main.rs index b6b09d9f79472..2035af7805bcd 100644 --- a/compiler/crates/schema-validate/src/main.rs +++ b/compiler/crates/schema-validate/src/main.rs @@ -14,10 +14,10 @@ use common::SourceLocationKey; use common::TextSource; use graphql_cli::DiagnosticPrinter; use intern::intern::Lookup; -use schema::build_schema_with_extensions; use schema::SDLSchema; -use schema_validate_lib::validate; +use schema::build_schema_with_extensions; use schema_validate_lib::SchemaValidationOptions; +use schema_validate_lib::validate; #[derive(Parser)] #[clap(name = "schema-validate", about = "Binary to Validate GraphQL Schema.")] @@ -61,7 +61,7 @@ fn build_schema_from_path(schema_file: &str) -> DiagnosticsResult { let path = Path::new(schema_file); let extensions: &[(&str, SourceLocationKey)] = &[]; - return if path.is_file() { + if path.is_file() { build_schema_with_extensions(&[path_to_schema_source(path)], extensions) } else { let paths = path @@ -81,7 +81,7 @@ fn build_schema_from_path(schema_file: &str) -> DiagnosticsResult { .collect(); build_schema_with_extensions(&sdls, extensions) - }; + } } fn path_to_schema_source(path: &Path) -> (String, SourceLocationKey) { diff --git a/compiler/crates/schema-validate/tests/validate_schema.rs b/compiler/crates/schema-validate/tests/validate_schema.rs index f287b7737930e..1dea17453fca6 100644 --- a/compiler/crates/schema-validate/tests/validate_schema.rs +++ b/compiler/crates/schema-validate/tests/validate_schema.rs @@ -10,8 +10,8 @@ use common::TextSource; use fixture_tests::Fixture; use graphql_cli::DiagnosticPrinter; use schema::build_schema_with_extensions; -use schema_validate_lib::validate; use schema_validate_lib::SchemaValidationOptions; +use schema_validate_lib::validate; pub async fn transform_fixture(fixture: &Fixture<'_>) -> Result { let result = build_schema_with_extensions::<&str, &str>( diff --git a/compiler/crates/schema/Cargo.toml b/compiler/crates/schema/Cargo.toml index 04382e25864ff..484f44218a1c4 100644 --- a/compiler/crates/schema/Cargo.toml +++ b/compiler/crates/schema/Cargo.toml @@ -4,7 +4,7 @@ name = "schema" version = "0.0.0" authors = ["Facebook"] -edition = "2021" +edition = "2024" repository = "https://github.com/facebook/relay" license = "MIT" @@ -15,19 +15,19 @@ path = "tests/build_schema_test.rs" [dependencies] common = { path = "../common" } dashmap = { version = "5.5.3", features = ["rayon", "serde"] } -flatbuffers = "2.0" +flatbuffers = "25.2.10" fnv = "1.0" graphql-syntax = { path = "../graphql-syntax" } intern = { path = "../intern" } -lazy_static = "1.4" -ouroboros = "0.18.4" +lazy_static = "1.5" +ouroboros = "0.18.5" rayon = "1.9.0" schema-flatbuffer = { path = "../schema-flatbuffer" } -serde = { version = "1.0.185", features = ["derive", "rc"] } +serde = { version = "1.0.219", features = ["derive", "rc"] } strsim = "0.10.0" -thiserror = "1.0.64" +thiserror = "2.0.12" [dev-dependencies] fixture-tests = { path = "../fixture-tests" } graphql-cli = { path = "../graphql-cli" } -tokio = { version = "1.41.0", features = ["full", "test-util", "tracing"] } +tokio = { version = "1.46.1", features = ["full", "test-util", "tracing"] } diff --git a/compiler/crates/schema/src/definitions.rs b/compiler/crates/schema/src/definitions.rs index 297d25a7ee6bc..09e1c08a87912 100644 --- a/compiler/crates/schema/src/definitions.rs +++ b/compiler/crates/schema/src/definitions.rs @@ -668,6 +668,28 @@ impl TypeWithFields for Object { } } +pub trait TypeWithDirectives { + fn directives(&self) -> &Vec; +} + +macro_rules! impl_type_with_directives { + ($type_name:ident) => { + impl TypeWithDirectives for $type_name { + fn directives(&self) -> &Vec { + &self.directives + } + } + }; +} + +impl_type_with_directives!(Object); +impl_type_with_directives!(Field); +impl_type_with_directives!(InputObject); +impl_type_with_directives!(Interface); +impl_type_with_directives!(Union); +impl_type_with_directives!(Scalar); +impl_type_with_directives!(Enum); + #[allow(unused_macros)] macro_rules! impl_named { ($type_name:ident) => { diff --git a/compiler/crates/schema/src/flatbuffer.rs b/compiler/crates/schema/src/flatbuffer.rs index a6190375d1c29..6505e369f1350 100644 --- a/compiler/crates/schema/src/flatbuffer.rs +++ b/compiler/crates/schema/src/flatbuffer.rs @@ -22,6 +22,7 @@ use common::UnionName; use common::WithLocation; use flatbuffers::ForwardsUOffset; use flatbuffers::Vector; +use flatbuffers::VerifierOptions; use graphql_syntax::BooleanNode; use graphql_syntax::ConstantArgument; use graphql_syntax::ConstantValue; @@ -35,9 +36,9 @@ use graphql_syntax::List; use graphql_syntax::StringNode; use graphql_syntax::Token; use graphql_syntax::TokenKind; +use intern::Lookup; use intern::string_key::Intern; use intern::string_key::StringKey; -use intern::Lookup; pub use serialize::serialize_as_flatbuffer; pub use wrapper::SchemaWrapper; @@ -64,8 +65,15 @@ pub struct FlatBufferSchema<'fb> { impl<'fb> FlatBufferSchema<'fb> { pub fn build(bytes: &'fb [u8]) -> Self { #![allow(deprecated)] + // Use custom verifier options with increased max_tables limit (default 1M) to handle large schemas + let opts: VerifierOptions = flatbuffers::VerifierOptions { + max_tables: usize::MAX, + ..Default::default() + }; + let fb_schema: schema_flatbuffer::Schema<'fb> = - schema_flatbuffer::get_root_as_schema(bytes); + schema_flatbuffer::root_as_schema_with_opts(&opts, bytes) + .expect("Failed to get root as schema"); let query_type = Type::Object(ObjectID(fb_schema.query_type())); let mutation_type = fb_schema @@ -571,8 +579,8 @@ fn wrap_ids(ids: Option>, f: impl Fn(u32) -> T) -> Vec { } fn get_mapped_location(location: schema_flatbuffer::DirectiveLocation) -> DirectiveLocation { - use schema_flatbuffer::DirectiveLocation as FDL; use DirectiveLocation as DL; + use schema_flatbuffer::DirectiveLocation as FDL; match location { FDL::Query => DL::Query, FDL::Mutation => DL::Mutation, diff --git a/compiler/crates/schema/src/flatbuffer/serialize.rs b/compiler/crates/schema/src/flatbuffer/serialize.rs index 4ca7e00b9ec75..1acebe2daa615 100644 --- a/compiler/crates/schema/src/flatbuffer/serialize.rs +++ b/compiler/crates/schema/src/flatbuffer/serialize.rs @@ -17,10 +17,9 @@ use graphql_syntax::ConstantArgument; use graphql_syntax::ConstantValue; use graphql_syntax::DirectiveLocation; use graphql_syntax::List; -use intern::string_key::StringKey; use intern::Lookup; +use intern::string_key::StringKey; -use crate::in_memory::InMemorySchema; use crate::Argument; use crate::ArgumentDefinitions; use crate::ArgumentValue; @@ -37,6 +36,7 @@ use crate::Schema; use crate::Type; use crate::TypeReference; use crate::UnionID; +use crate::in_memory::InMemorySchema; pub fn serialize_as_flatbuffer(schema: &InMemorySchema) -> Vec { let mut serializer = Serializer::new(schema); @@ -627,8 +627,8 @@ impl<'fb, 'schema> Serializer<'fb, 'schema> { } fn get_mapped_location(location: DirectiveLocation) -> schema_flatbuffer::DirectiveLocation { - use schema_flatbuffer::DirectiveLocation as FDL; use DirectiveLocation as DL; + use schema_flatbuffer::DirectiveLocation as FDL; match location { DL::Query => FDL::Query, DL::Mutation => FDL::Mutation, diff --git a/compiler/crates/schema/src/flatbuffer/wrapper.rs b/compiler/crates/schema/src/flatbuffer/wrapper.rs index 1fb6fcd7e54cc..f90d57af4fc33 100644 --- a/compiler/crates/schema/src/flatbuffer/wrapper.rs +++ b/compiler/crates/schema/src/flatbuffer/wrapper.rs @@ -13,14 +13,12 @@ use common::DirectiveName; use common::WithLocation; use dashmap::DashMap; use fnv::FnvBuildHasher; +use intern::Lookup; use intern::string_key::Intern; use intern::string_key::StringKey; -use intern::Lookup; use ouroboros::self_referencing; use super::FlatBufferSchema; -use crate::field_descriptions::CLIENT_ID_DESCRIPTION; -use crate::field_descriptions::TYPENAME_DESCRIPTION; use crate::Argument; use crate::ArgumentDefinitions; use crate::Directive; @@ -41,6 +39,8 @@ use crate::Type; use crate::TypeReference; use crate::Union; use crate::UnionID; +use crate::field_descriptions::CLIENT_ID_DESCRIPTION; +use crate::field_descriptions::TYPENAME_DESCRIPTION; #[self_referencing] struct OwnedFlatBufferSchema { diff --git a/compiler/crates/schema/src/graphql_schema.rs b/compiler/crates/schema/src/graphql_schema.rs index 0a4afb1f53165..f503ddc412e9d 100644 --- a/compiler/crates/schema/src/graphql_schema.rs +++ b/compiler/crates/schema/src/graphql_schema.rs @@ -9,8 +9,8 @@ use std::fmt::Result as FmtResult; use std::fmt::Write; use common::DirectiveName; -use intern::string_key::StringKey; use intern::Lookup; +use intern::string_key::StringKey; use crate::definitions::Directive; use crate::definitions::*; diff --git a/compiler/crates/schema/src/in_memory.rs b/compiler/crates/schema/src/in_memory.rs index 94d8db2bf047f..30b049a5c98d0 100644 --- a/compiler/crates/schema/src/in_memory.rs +++ b/compiler/crates/schema/src/in_memory.rs @@ -5,9 +5,9 @@ * LICENSE file in the root directory of this source tree. */ -use std::collections::hash_map::Entry; use std::collections::BTreeMap; use std::collections::HashMap; +use std::collections::hash_map::Entry; use common::ArgumentName; use common::Diagnostic; @@ -23,9 +23,9 @@ use common::SourceLocationKey; use common::UnionName; use common::WithLocation; use graphql_syntax::*; +use intern::Lookup; use intern::string_key::Intern; use intern::string_key::StringKey; -use intern::Lookup; use rayon::iter::IntoParallelRefIterator; use rayon::iter::ParallelIterator; @@ -365,6 +365,10 @@ impl InMemorySchema { self.enums.iter() } + pub fn get_enums_par_iter(&self) -> impl ParallelIterator { + self.enums.par_iter() + } + pub fn get_objects(&self) -> impl Iterator { self.objects.iter() } diff --git a/compiler/crates/schema/src/lib.rs b/compiler/crates/schema/src/lib.rs index 099d193f5afab..86ab50f1d7b7f 100644 --- a/compiler/crates/schema/src/lib.rs +++ b/compiler/crates/schema/src/lib.rs @@ -21,10 +21,10 @@ mod schema; pub mod suggestion_list; use std::borrow::Cow; -use common::sync::IntoParallelIterator; -use common::sync::ParallelIterator; use common::DiagnosticsResult; use common::SourceLocationKey; +use common::sync::IntoParallelIterator; +use common::sync::ParallelIterator; pub use definitions::Argument; pub use definitions::ArgumentDefinitions; pub use definitions::ArgumentValue; diff --git a/compiler/crates/schema/src/schema.rs b/compiler/crates/schema/src/schema.rs index 76186af6c7b64..89106b8274b82 100644 --- a/compiler/crates/schema/src/schema.rs +++ b/compiler/crates/schema/src/schema.rs @@ -389,6 +389,13 @@ impl SDLSchema { } } + pub fn get_enums_par_iter(&self) -> impl ParallelIterator { + match self { + SDLSchema::FlatBuffer(_schema) => todo!(), + SDLSchema::InMemory(schema) => schema.get_enums_par_iter(), + } + } + pub fn get_objects(&self) -> impl Iterator { match self { SDLSchema::FlatBuffer(_schema) => todo!(), diff --git a/compiler/crates/schema/src/suggestion_list.rs b/compiler/crates/schema/src/suggestion_list.rs index 685c26a519925..d2a12c870b8d3 100644 --- a/compiler/crates/schema/src/suggestion_list.rs +++ b/compiler/crates/schema/src/suggestion_list.rs @@ -5,9 +5,9 @@ * LICENSE file in the root directory of this source tree. */ +use intern::Lookup; use intern::string_key::Intern; use intern::string_key::StringKey; -use intern::Lookup; use strsim::damerau_levenshtein; use crate::SDLSchema; diff --git a/compiler/crates/schema/tests/build_schema.rs b/compiler/crates/schema/tests/build_schema.rs index 09a5c17cd5596..6cc28b033734d 100644 --- a/compiler/crates/schema/tests/build_schema.rs +++ b/compiler/crates/schema/tests/build_schema.rs @@ -12,12 +12,12 @@ use common::SourceLocationKey; use common::TextSource; use fixture_tests::Fixture; use graphql_cli::DiagnosticPrinter; -use schema::build_schema_from_flat_buffer; -use schema::build_schema_with_extensions; -use schema::serialize_as_flatbuffer; use schema::SDLSchema; use schema::Schema; use schema::Type; +use schema::build_schema_from_flat_buffer; +use schema::build_schema_with_extensions; +use schema::serialize_as_flatbuffer; const SCHEMA_SEPARATOR: &str = "%extensions%"; diff --git a/compiler/crates/signedsource/Cargo.toml b/compiler/crates/signedsource/Cargo.toml index 4d68654fea935..64fe11a269d03 100644 --- a/compiler/crates/signedsource/Cargo.toml +++ b/compiler/crates/signedsource/Cargo.toml @@ -4,12 +4,12 @@ name = "signedsource" version = "0.0.0" authors = ["Facebook"] -edition = "2021" +edition = "2024" repository = "https://github.com/facebook/relay" license = "MIT" [dependencies] -hex = "0.4.3" -lazy_static = "1.4" +hex = { version = "0.4.3", features = ["alloc"] } +lazy_static = "1.5" md-5 = "0.10" -regex = "1.9.2" +regex = "1.11.1" diff --git a/compiler/fixture_dirs.txt b/compiler/fixture_dirs.txt index 2de33294de59c..ec8265bd97d4d 100644 --- a/compiler/fixture_dirs.txt +++ b/compiler/fixture_dirs.txt @@ -9,9 +9,10 @@ crates/graphql-ir/tests/parse_with_provider crates/graphql-ir/tests/parse crates/graphql-syntax/tests/advance_schema_document crates/graphql-syntax/tests/parse_document +crates/graphql-syntax/tests/parse_document_with_enquoted_alias crates/graphql-syntax/tests/parse_document_with_features -crates/graphql-syntax/tests/parse_executable_document_with_error_recovery crates/graphql-syntax/tests/parse_executable_document +crates/graphql-syntax/tests/parse_executable_document_with_error_recovery crates/graphql-syntax/tests/parse_schema_document crates/graphql-syntax/tests/print crates/graphql-text-printer/tests/compact @@ -94,6 +95,7 @@ crates/relay-transforms/tests/validate_no_unselectable_selections crates/relay-transforms/tests/validate_module_names crates/relay-transforms/tests/validate_relay_directives crates/relay-transforms/tests/validate_required_arguments +crates/relay-transforms/tests/validate_client_schema_extensions_use_catch crates/relay-transforms/tests/validate_server_only_directives crates/relay-transforms/tests/validate_static_args crates/relay-transforms/tests/validate_unused_variables diff --git a/compiler/rust-toolchain.toml b/compiler/rust-toolchain.toml index 4a435ef71caba..8959b801380a6 100644 --- a/compiler/rust-toolchain.toml +++ b/compiler/rust-toolchain.toml @@ -1,3 +1,3 @@ # rust-toolchain.toml [toolchain] -channel = "1.76.0" +channel = "nightly-2025-05-09" diff --git a/compiler/rustfmt.toml b/compiler/rustfmt.toml index 336df502642ed..0762474bcedcd 100644 --- a/compiler/rustfmt.toml +++ b/compiler/rustfmt.toml @@ -5,5 +5,5 @@ format_code_in_doc_comments = true group_imports = "StdExternalCrate" imports_granularity = "Item" merge_derives = false +style_edition = "2024" use_field_init_shorthand = true -version = "Two" diff --git a/compiler/test-project-res/src/Test_modelResolverAliased.res b/compiler/test-project-res/src/Test_modelResolverAliased.res new file mode 100644 index 0000000000000..00246ee01304a --- /dev/null +++ b/compiler/test-project-res/src/Test_modelResolverAliased.res @@ -0,0 +1,11 @@ +module Query = %relay(` + query TestModelResolverAliasedQuery { + myLocal: localUser { + name + meta @required(action: NONE) { + online + } + } + } +`) + diff --git a/compiler/test-project-res/src/Test_modelResolverInline.res b/compiler/test-project-res/src/Test_modelResolverInline.res new file mode 100644 index 0000000000000..1098208f4019b --- /dev/null +++ b/compiler/test-project-res/src/Test_modelResolverInline.res @@ -0,0 +1,13 @@ +module Query = %relay(` + query TestModelResolverInlineQuery { + localUser { + ... on LocalUser { + name + meta @required(action: NONE) { + online + } + } + } + } +`) + diff --git a/compiler/test-project-res/src/__generated__/TestModelResolverAliasedQuery_graphql.res b/compiler/test-project-res/src/__generated__/TestModelResolverAliasedQuery_graphql.res new file mode 100644 index 0000000000000..8b2c7a9daa4c9 --- /dev/null +++ b/compiler/test-project-res/src/__generated__/TestModelResolverAliasedQuery_graphql.res @@ -0,0 +1,376 @@ +/* @sourceLoc Test_modelResolverAliased.res */ +/* @generated */ +%%raw("/* @generated */") +module Types = { + @@warning("-30") + + type rec response_myLocal_meta = { + online: option, + } + and response_myLocal = { + meta: response_myLocal_meta, + name: option, + } + type response = { + myLocal: option, + } + @live + type rawResponse = response + @live + type variables = unit + @live + type refetchVariables = unit + @live let makeRefetchVariables = () => () +} + + +type queryRef + +module Internal = { + @live + let variablesConverter: Js.Dict.t>> = %raw( + json`{}` + ) + @live + let variablesConverterMap = () + @live + let convertVariables = v => v->RescriptRelay.convertObj( + variablesConverter, + variablesConverterMap, + Js.undefined + ) + @live + type wrapResponseRaw + @live + let wrapResponseConverter: Js.Dict.t>> = %raw( + json`{}` + ) + @live + let wrapResponseConverterMap = () + @live + let convertWrapResponse = v => v->RescriptRelay.convertObj( + wrapResponseConverter, + wrapResponseConverterMap, + Js.null + ) + @live + type responseRaw + @live + let responseConverter: Js.Dict.t>> = %raw( + json`{}` + ) + @live + let responseConverterMap = () + @live + let convertResponse = v => v->RescriptRelay.convertObj( + responseConverter, + responseConverterMap, + Js.undefined + ) + type wrapRawResponseRaw = wrapResponseRaw + @live + let convertWrapRawResponse = convertWrapResponse + type rawResponseRaw = responseRaw + @live + let convertRawResponse = convertResponse + type rawPreloadToken<'response> = {source: Js.Nullable.t>} + external tokenToRaw: queryRef => rawPreloadToken = "%identity" +} +module Utils = { + @@warning("-33") + open Types +} + +type relayOperationNode +type operationType = RescriptRelay.queryNode + + +%%private(let makeNode = (rescript_graphql_node_LocalUser__id, rescript_graphql_node_LocalUser____relay_model_instance, rescript_graphql_node_UserMeta____relay_model_instance, resolverDataInjector, rescript_module_TestRelayResolverMulti_LocalUser, rescript_module_TestRelayResolverMulti_localUser, rescript_module_TestRelayResolverMulti_name, rescript_module_TestRelayResolverMulti_meta, rescript_module_TestRelayResolverMulti_online): operationType => { + ignore(rescript_graphql_node_LocalUser__id) + ignore(rescript_graphql_node_LocalUser____relay_model_instance) + ignore(rescript_graphql_node_UserMeta____relay_model_instance) + ignore(resolverDataInjector) + ignore(rescript_module_TestRelayResolverMulti_LocalUser) + ignore(rescript_module_TestRelayResolverMulti_localUser) + ignore(rescript_module_TestRelayResolverMulti_name) + ignore(rescript_module_TestRelayResolverMulti_meta) + ignore(rescript_module_TestRelayResolverMulti_online) + %raw(json`(function(){ +var v0 = { + "args": null, + "kind": "FragmentSpread", + "name": "LocalUser____relay_model_instance" +}, +v1 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null +}, +v2 = { + "kind": "InlineFragment", + "selections": [ + { + "name": "__relay_model_instance", + "args": null, + "fragment": { + "kind": "InlineFragment", + "selections": [ + (v1/*: any*/) + ], + "type": "LocalUser", + "abstractKey": null + }, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": false + } + ], + "type": "LocalUser", + "abstractKey": null +}; +return { + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": { + "hasClientEdges": true + }, + "name": "TestModelResolverAliasedQuery", + "selections": [ + { + "kind": "ClientEdgeToClientObject", + "concreteType": "LocalUser", + "modelResolvers": { + "LocalUser": { + "alias": null, + "args": null, + "fragment": { + "args": null, + "kind": "FragmentSpread", + "name": "LocalUser__id" + }, + "kind": "RelayResolver", + "name": "myLocal", + "resolverModule": resolverDataInjector(rescript_graphql_node_LocalUser__id, rescript_module_TestRelayResolverMulti_LocalUser, 'id', true), + "path": "myLocal.__relay_model_instance" + } + }, + "backingField": { + "alias": "myLocal", + "args": null, + "fragment": null, + "kind": "RelayResolver", + "name": "localUser", + "resolverModule": rescript_module_TestRelayResolverMulti_localUser, + "path": "myLocal" + }, + "linkedField": { + "alias": "myLocal", + "args": null, + "concreteType": "LocalUser", + "kind": "LinkedField", + "name": "localUser", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "fragment": (v0/*: any*/), + "kind": "RelayResolver", + "name": "name", + "resolverModule": resolverDataInjector(rescript_graphql_node_LocalUser____relay_model_instance, rescript_module_TestRelayResolverMulti_name, '__relay_model_instance', true), + "path": "myLocal.name" + }, + { + "kind": "RequiredField", + "field": { + "kind": "ClientEdgeToClientObject", + "concreteType": "UserMeta", + "modelResolvers": null, + "backingField": { + "alias": null, + "args": null, + "fragment": (v0/*: any*/), + "kind": "RelayResolver", + "name": "meta", + "resolverModule": resolverDataInjector(rescript_graphql_node_LocalUser____relay_model_instance, rescript_module_TestRelayResolverMulti_meta, '__relay_model_instance', true), + "path": "myLocal.meta", + "normalizationInfo": { + "kind": "WeakModel", + "concreteType": "UserMeta", + "plural": false + } + }, + "linkedField": { + "alias": null, + "args": null, + "concreteType": "UserMeta", + "kind": "LinkedField", + "name": "meta", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "fragment": { + "args": null, + "kind": "FragmentSpread", + "name": "UserMeta____relay_model_instance" + }, + "kind": "RelayResolver", + "name": "online", + "resolverModule": resolverDataInjector(rescript_graphql_node_UserMeta____relay_model_instance, rescript_module_TestRelayResolverMulti_online, '__relay_model_instance', true), + "path": "myLocal.meta.online" + } + ], + "storageKey": null + } + }, + "action": "NONE" + } + ], + "storageKey": null + } + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [], + "kind": "Operation", + "name": "TestModelResolverAliasedQuery", + "selections": [ + { + "kind": "ClientEdgeToClientObject", + "backingField": { + "name": "localUser", + "args": null, + "fragment": null, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": false + }, + "linkedField": { + "alias": "myLocal", + "args": null, + "concreteType": "LocalUser", + "kind": "LinkedField", + "name": "localUser", + "plural": false, + "selections": [ + { + "name": "name", + "args": null, + "fragment": (v2/*: any*/), + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": true + }, + { + "kind": "ClientEdgeToClientObject", + "backingField": { + "name": "meta", + "args": null, + "fragment": (v2/*: any*/), + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": true + }, + "linkedField": { + "alias": null, + "args": null, + "concreteType": "UserMeta", + "kind": "LinkedField", + "name": "meta", + "plural": false, + "selections": [ + { + "name": "online", + "args": null, + "fragment": { + "kind": "InlineFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__relay_model_instance", + "storageKey": null + } + ], + "type": "UserMeta", + "abstractKey": null + }, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": true + } + ], + "storageKey": null + } + }, + (v1/*: any*/) + ], + "storageKey": null + } + } + ] + }, + "params": { + "cacheID": "30dd44ce0559e69a41cb47b9c70e13d8", + "id": null, + "metadata": {}, + "name": "TestModelResolverAliasedQuery", + "operationKind": "query", + "text": null + } +}; +})()`) +}) +let node: operationType = makeNode(LocalUser__id_graphql.node, LocalUser____relay_model_instance_graphql.node, UserMeta____relay_model_instance_graphql.node, RescriptRelay.resolverDataInjector, TestRelayResolverMulti.localUser, TestRelayResolverMulti.localUser, TestRelayResolverMulti.name, TestRelayResolverMulti.meta, TestRelayResolverMulti.online) + +@live let load: ( + ~environment: RescriptRelay.Environment.t, + ~variables: Types.variables, + ~fetchPolicy: RescriptRelay.fetchPolicy=?, + ~fetchKey: string=?, + ~networkCacheConfig: RescriptRelay.cacheConfig=?, +) => queryRef = ( + ~environment, + ~variables, + ~fetchPolicy=?, + ~fetchKey=?, + ~networkCacheConfig=?, +) => + RescriptRelay.loadQuery( + environment, + node, + variables->Internal.convertVariables, + { + fetchKey, + fetchPolicy, + networkCacheConfig, + }, + ) + +@live +let queryRefToObservable = token => { + let raw = token->Internal.tokenToRaw + raw.source->Js.Nullable.toOption +} + +@live +let queryRefToPromise = token => { + Js.Promise.make((~resolve, ~reject as _) => { + switch token->queryRefToObservable { + | None => resolve(Error()) + | Some(o) => + open RescriptRelay.Observable + let _: subscription = o->subscribe(makeObserver(~complete=() => resolve(Ok()))) + } + }) +} diff --git a/compiler/test-project-res/src/__generated__/TestModelResolverInlineQuery_graphql.res b/compiler/test-project-res/src/__generated__/TestModelResolverInlineQuery_graphql.res new file mode 100644 index 0000000000000..b704c66145254 --- /dev/null +++ b/compiler/test-project-res/src/__generated__/TestModelResolverInlineQuery_graphql.res @@ -0,0 +1,376 @@ +/* @sourceLoc Test_modelResolverInline.res */ +/* @generated */ +%%raw("/* @generated */") +module Types = { + @@warning("-30") + + type rec response_localUser_meta = { + online: option, + } + and response_localUser = { + meta: response_localUser_meta, + name: option, + } + type response = { + localUser: option, + } + @live + type rawResponse = response + @live + type variables = unit + @live + type refetchVariables = unit + @live let makeRefetchVariables = () => () +} + + +type queryRef + +module Internal = { + @live + let variablesConverter: Js.Dict.t>> = %raw( + json`{}` + ) + @live + let variablesConverterMap = () + @live + let convertVariables = v => v->RescriptRelay.convertObj( + variablesConverter, + variablesConverterMap, + Js.undefined + ) + @live + type wrapResponseRaw + @live + let wrapResponseConverter: Js.Dict.t>> = %raw( + json`{}` + ) + @live + let wrapResponseConverterMap = () + @live + let convertWrapResponse = v => v->RescriptRelay.convertObj( + wrapResponseConverter, + wrapResponseConverterMap, + Js.null + ) + @live + type responseRaw + @live + let responseConverter: Js.Dict.t>> = %raw( + json`{}` + ) + @live + let responseConverterMap = () + @live + let convertResponse = v => v->RescriptRelay.convertObj( + responseConverter, + responseConverterMap, + Js.undefined + ) + type wrapRawResponseRaw = wrapResponseRaw + @live + let convertWrapRawResponse = convertWrapResponse + type rawResponseRaw = responseRaw + @live + let convertRawResponse = convertResponse + type rawPreloadToken<'response> = {source: Js.Nullable.t>} + external tokenToRaw: queryRef => rawPreloadToken = "%identity" +} +module Utils = { + @@warning("-33") + open Types +} + +type relayOperationNode +type operationType = RescriptRelay.queryNode + + +%%private(let makeNode = (rescript_graphql_node_LocalUser__id, rescript_graphql_node_LocalUser____relay_model_instance, rescript_graphql_node_UserMeta____relay_model_instance, resolverDataInjector, rescript_module_TestRelayResolverMulti_LocalUser, rescript_module_TestRelayResolverMulti_localUser, rescript_module_TestRelayResolverMulti_name, rescript_module_TestRelayResolverMulti_meta, rescript_module_TestRelayResolverMulti_online): operationType => { + ignore(rescript_graphql_node_LocalUser__id) + ignore(rescript_graphql_node_LocalUser____relay_model_instance) + ignore(rescript_graphql_node_UserMeta____relay_model_instance) + ignore(resolverDataInjector) + ignore(rescript_module_TestRelayResolverMulti_LocalUser) + ignore(rescript_module_TestRelayResolverMulti_localUser) + ignore(rescript_module_TestRelayResolverMulti_name) + ignore(rescript_module_TestRelayResolverMulti_meta) + ignore(rescript_module_TestRelayResolverMulti_online) + %raw(json`(function(){ +var v0 = { + "args": null, + "kind": "FragmentSpread", + "name": "LocalUser____relay_model_instance" +}, +v1 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null +}, +v2 = { + "kind": "InlineFragment", + "selections": [ + { + "name": "__relay_model_instance", + "args": null, + "fragment": { + "kind": "InlineFragment", + "selections": [ + (v1/*: any*/) + ], + "type": "LocalUser", + "abstractKey": null + }, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": false + } + ], + "type": "LocalUser", + "abstractKey": null +}; +return { + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": { + "hasClientEdges": true + }, + "name": "TestModelResolverInlineQuery", + "selections": [ + { + "kind": "ClientEdgeToClientObject", + "concreteType": "LocalUser", + "modelResolvers": { + "LocalUser": { + "alias": null, + "args": null, + "fragment": { + "args": null, + "kind": "FragmentSpread", + "name": "LocalUser__id" + }, + "kind": "RelayResolver", + "name": "localUser", + "resolverModule": resolverDataInjector(rescript_graphql_node_LocalUser__id, rescript_module_TestRelayResolverMulti_LocalUser, 'id', true), + "path": "localUser.__relay_model_instance" + } + }, + "backingField": { + "alias": null, + "args": null, + "fragment": null, + "kind": "RelayResolver", + "name": "localUser", + "resolverModule": rescript_module_TestRelayResolverMulti_localUser, + "path": "localUser" + }, + "linkedField": { + "alias": null, + "args": null, + "concreteType": "LocalUser", + "kind": "LinkedField", + "name": "localUser", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "fragment": (v0/*: any*/), + "kind": "RelayResolver", + "name": "name", + "resolverModule": resolverDataInjector(rescript_graphql_node_LocalUser____relay_model_instance, rescript_module_TestRelayResolverMulti_name, '__relay_model_instance', true), + "path": "localUser.name" + }, + { + "kind": "RequiredField", + "field": { + "kind": "ClientEdgeToClientObject", + "concreteType": "UserMeta", + "modelResolvers": null, + "backingField": { + "alias": null, + "args": null, + "fragment": (v0/*: any*/), + "kind": "RelayResolver", + "name": "meta", + "resolverModule": resolverDataInjector(rescript_graphql_node_LocalUser____relay_model_instance, rescript_module_TestRelayResolverMulti_meta, '__relay_model_instance', true), + "path": "localUser.meta", + "normalizationInfo": { + "kind": "WeakModel", + "concreteType": "UserMeta", + "plural": false + } + }, + "linkedField": { + "alias": null, + "args": null, + "concreteType": "UserMeta", + "kind": "LinkedField", + "name": "meta", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "fragment": { + "args": null, + "kind": "FragmentSpread", + "name": "UserMeta____relay_model_instance" + }, + "kind": "RelayResolver", + "name": "online", + "resolverModule": resolverDataInjector(rescript_graphql_node_UserMeta____relay_model_instance, rescript_module_TestRelayResolverMulti_online, '__relay_model_instance', true), + "path": "localUser.meta.online" + } + ], + "storageKey": null + } + }, + "action": "NONE" + } + ], + "storageKey": null + } + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [], + "kind": "Operation", + "name": "TestModelResolverInlineQuery", + "selections": [ + { + "kind": "ClientEdgeToClientObject", + "backingField": { + "name": "localUser", + "args": null, + "fragment": null, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": false + }, + "linkedField": { + "alias": null, + "args": null, + "concreteType": "LocalUser", + "kind": "LinkedField", + "name": "localUser", + "plural": false, + "selections": [ + { + "name": "name", + "args": null, + "fragment": (v2/*: any*/), + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": true + }, + { + "kind": "ClientEdgeToClientObject", + "backingField": { + "name": "meta", + "args": null, + "fragment": (v2/*: any*/), + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": true + }, + "linkedField": { + "alias": null, + "args": null, + "concreteType": "UserMeta", + "kind": "LinkedField", + "name": "meta", + "plural": false, + "selections": [ + { + "name": "online", + "args": null, + "fragment": { + "kind": "InlineFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__relay_model_instance", + "storageKey": null + } + ], + "type": "UserMeta", + "abstractKey": null + }, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": true + } + ], + "storageKey": null + } + }, + (v1/*: any*/) + ], + "storageKey": null + } + } + ] + }, + "params": { + "cacheID": "cb4c636b0604c62b9b75743e2aecbac2", + "id": null, + "metadata": {}, + "name": "TestModelResolverInlineQuery", + "operationKind": "query", + "text": null + } +}; +})()`) +}) +let node: operationType = makeNode(LocalUser__id_graphql.node, LocalUser____relay_model_instance_graphql.node, UserMeta____relay_model_instance_graphql.node, RescriptRelay.resolverDataInjector, TestRelayResolverMulti.localUser, TestRelayResolverMulti.localUser, TestRelayResolverMulti.name, TestRelayResolverMulti.meta, TestRelayResolverMulti.online) + +@live let load: ( + ~environment: RescriptRelay.Environment.t, + ~variables: Types.variables, + ~fetchPolicy: RescriptRelay.fetchPolicy=?, + ~fetchKey: string=?, + ~networkCacheConfig: RescriptRelay.cacheConfig=?, +) => queryRef = ( + ~environment, + ~variables, + ~fetchPolicy=?, + ~fetchKey=?, + ~networkCacheConfig=?, +) => + RescriptRelay.loadQuery( + environment, + node, + variables->Internal.convertVariables, + { + fetchKey, + fetchPolicy, + networkCacheConfig, + }, + ) + +@live +let queryRefToObservable = token => { + let raw = token->Internal.tokenToRaw + raw.source->Js.Nullable.toOption +} + +@live +let queryRefToPromise = token => { + Js.Promise.make((~resolve, ~reject as _) => { + switch token->queryRefToObservable { + | None => resolve(Error()) + | Some(o) => + open RescriptRelay.Observable + let _: subscription = o->subscribe(makeObserver(~complete=() => resolve(Ok()))) + } + }) +} diff --git a/compiler/test-project/src/__generated__/AppQuery.graphql.js b/compiler/test-project/src/__generated__/AppQuery.graphql.js index 5bbe3daa33018..c9e61fb1d20a4 100644 --- a/compiler/test-project/src/__generated__/AppQuery.graphql.js +++ b/compiler/test-project/src/__generated__/AppQuery.graphql.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -98,4 +98,4 @@ return { node.hash = "942e72826c882d3a02cb0cfbf267dd83"; -module.exports = node; +export default node; diff --git a/compiler/test-project/src/__generated__/Component_node.graphql.js b/compiler/test-project/src/__generated__/Component_node.graphql.js index 961f909f02891..e2f67331f26ab 100644 --- a/compiler/test-project/src/__generated__/Component_node.graphql.js +++ b/compiler/test-project/src/__generated__/Component_node.graphql.js @@ -4,7 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * - * @generated SignedSource<> + * @generated SignedSource<> * @lightSyntaxTransform * @nogrep */ @@ -33,4 +33,4 @@ var node = { node.hash = "c1076fdf6414be9f597194edf35d01a0"; -module.exports = node; +export default node; diff --git a/flow-typed/npm/chalk_v4.x.x.js b/flow-typed/npm/chalk_v4.x.x.js index d30d76a38905c..b73753ba68a16 100644 --- a/flow-typed/npm/chalk_v4.x.x.js +++ b/flow-typed/npm/chalk_v4.x.x.js @@ -6,9 +6,11 @@ * * @flow * @format - * @oncall relay + * @oncall react_native */ +// From: https://github.com/chalk/chalk/blob/main/source/index.d.ts + declare module 'chalk' { declare type TemplateStringsArray = $ReadOnlyArray; @@ -21,7 +23,6 @@ declare module 'chalk' { }>; declare type ChalkOptions = {| - enabled?: boolean, level?: Level, |}; @@ -32,10 +33,80 @@ declare module 'chalk' { has16m: boolean, |}; + declare class Instance implements Chalk { + constructor(options?: ChalkOptions): this; + + (...text: string[]): string; + (text: TemplateStringsArray, ...placeholders: string[]): string; + Instance: typeof Instance; + level: Level; + rgb(r: number, g: number, b: number): Chalk; + hsl(h: number, s: number, l: number): Chalk; + hsv(h: number, s: number, v: number): Chalk; + hwb(h: number, w: number, b: number): Chalk; + bgHex(color: string): Chalk; + bgKeyword(color: string): Chalk; + bgRgb(r: number, g: number, b: number): Chalk; + bgHsl(h: number, s: number, l: number): Chalk; + bgHsv(h: number, s: number, v: number): Chalk; + bgHwb(h: number, w: number, b: number): Chalk; + hex(color: string): Chalk; + keyword(color: string): Chalk; + + +reset: Chalk; + +bold: Chalk; + +dim: Chalk; + +italic: Chalk; + +underline: Chalk; + +inverse: Chalk; + +hidden: Chalk; + +strikethrough: Chalk; + + +visible: Chalk; + + +black: Chalk; + +red: Chalk; + +green: Chalk; + +yellow: Chalk; + +blue: Chalk; + +magenta: Chalk; + +cyan: Chalk; + +white: Chalk; + +gray: Chalk; + +grey: Chalk; + +blackBright: Chalk; + +redBright: Chalk; + +greenBright: Chalk; + +yellowBright: Chalk; + +blueBright: Chalk; + +magentaBright: Chalk; + +cyanBright: Chalk; + +whiteBright: Chalk; + + +bgBlack: Chalk; + +bgRed: Chalk; + +bgGreen: Chalk; + +bgYellow: Chalk; + +bgBlue: Chalk; + +bgMagenta: Chalk; + +bgCyan: Chalk; + +bgWhite: Chalk; + +bgBlackBright: Chalk; + +bgRedBright: Chalk; + +bgGreenBright: Chalk; + +bgYellowBright: Chalk; + +bgBlueBright: Chalk; + +bgMagentaBright: Chalk; + +bgCyanBright: Chalk; + +bgWhiteBright: Chalk; + + supportsColor: ColorSupport; + } + declare interface Chalk { (...text: string[]): string; (text: TemplateStringsArray, ...placeholders: string[]): string; - Instance(options?: ChalkOptions): Chalk; + Instance: typeof Instance; level: Level; rgb(r: number, g: number, b: number): Chalk; hsl(h: number, s: number, l: number): Chalk; @@ -95,7 +166,7 @@ declare module 'chalk' { +bgBlueBright: Chalk; +bgMagentaBright: Chalk; +bgCyanBright: Chalk; - +bgWhiteBrigh: Chalk; + +bgWhiteBright: Chalk; supportsColor: ColorSupport; } diff --git a/flow-typed/react.js b/flow-typed/react.js deleted file mode 100644 index 90b97cacc359e..0000000000000 --- a/flow-typed/react.js +++ /dev/null @@ -1,235 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - * @format - * @oncall relay - */ - -/* eslint-disable no-unused-vars */ - -'use strict'; - -// TODO: Remove after upgrading the flow version. This was added because we changed -// the types of useTransition, startTranstion, and useDeferredValue used only in test. -declare module react { - declare export var DOM: any; - declare export var PropTypes: any; - declare export var version: string; - - declare export function checkPropTypes( - propTypes: any, - values: V, - location: string, - componentName: string, - getStack: ?() => ?string, - ): void; - - declare export var createClass: $FlowFixMe; - declare export function createContext( - defaultValue: T, - calculateChangedBits: ?(a: T, b: T) => number, - ): React$Context; - declare export var createElement: React$CreateElement; - declare export var cloneElement: React$CloneElement; - declare export function createRef(): {|current: null | T|}; - - declare export function isValidElement(element: any): boolean; - - declare export var Component: typeof React$Component; - declare export var PureComponent: typeof React$PureComponent; - declare export type ComponentType<-P> = React$ComponentType

; - declare export type MixedElement = React$MixedElement; - declare export type ElementType = React$ElementType; - declare export type Element<+C> = React$Element; - declare export var Fragment: React$FragmentType; - declare export type Key = React$Key; - declare export type RefSetter<-T> = React$RefSetter; - declare export type Node = React$Node; - declare export type Context = React$Context; - declare export type Portal = React$Portal; - declare export var ConcurrentMode: ({ - children?: React$Node, - ... - }) => React$Node; // 16.7+ - declare export var StrictMode: ({children?: React$Node, ...}) => React$Node; - - declare export var Suspense: React$ComponentType<{ - children?: React$Node, - fallback?: React$Node, - ... - }>; // 16.6+ - - declare export type ElementProps = React$ElementProps; - declare export type ElementConfig = React$ElementConfig; - declare export type ElementRef = React$ElementRef; - declare export type Config = React$Config< - Props, - DefaultProps, - >; - - declare export type ChildrenArray<+T> = $ReadOnlyArray> | T; - declare export var Children: { - map( - children: ChildrenArray, - fn: (child: $NonMaybeType, index: number) => U, - thisArg?: mixed, - ): Array<$NonMaybeType>, - forEach( - children: ChildrenArray, - fn: (child: T, index: number) => mixed, - thisArg?: mixed, - ): void, - count(children: ChildrenArray): number, - only(children: ChildrenArray): $NonMaybeType, - toArray(children: ChildrenArray): Array<$NonMaybeType>, - ... - }; - - declare export function forwardRef( - render: (props: Config, ref: React$RefSetter) => React$Node, - ): component(ref: React.RefSetter, ...Config); - - declare export function memo( - component: component(ref: React.RefSetter, ...Config), - equal?: (Config, Config) => boolean, - ): component(ref: React.RefSetter, ...Config); - - declare export function lazy( - component_: () => Promise< - $ReadOnly<{ - default: component(ref: React.RefSetter, ...Config), - ... - }>, - >, - ): component(ref: React.RefSetter, ...Config); - - declare type MaybeCleanUpFn = void | (() => void); - - declare export function useContext(context: React$Context): T; - - declare export function useState( - initialState: (() => S) | S, - ): [S, ((S => S) | S) => void]; - - declare type Dispatch = (A) => void; - - declare export function useReducer( - reducer: (S, A) => S, - initialState: S, - ): [S, Dispatch]; - - declare export function useReducer( - reducer: (S, A) => S, - initialState: S, - init: void, - ): [S, Dispatch]; - - declare export function useReducer( - reducer: (S, A) => S, - initialArg: I, - init: (I) => S, - ): [S, Dispatch]; - - declare export function useRef(initialValue: T): {|current: T|}; - - declare export function useDebugValue(value: any): void; - - declare export function useEffect( - create: () => MaybeCleanUpFn, - inputs?: ?$ReadOnlyArray, - ): void; - - declare export function useLayoutEffect( - create: () => MaybeCleanUpFn, - inputs?: ?$ReadOnlyArray, - ): void; - - declare export function useCallback< - T: (...args: $ReadOnlyArray) => mixed, - >( - callback: T, - inputs: ?$ReadOnlyArray, - ): T; - - declare export function useMemo( - create: () => T, - inputs: ?$ReadOnlyArray, - ): T; - - declare export function useImperativeHandle( - ref: {current: T | null, ...} | ((inst: T | null) => mixed) | null | void, - create: () => T, - inputs: ?$ReadOnlyArray, - ): void; - - declare export function useDeferredValue(value: T): T; - - declare export function useTransition(): [boolean, (() => void) => void]; - - declare export function startTransition(() => void): void; - - declare export type Interaction = { - name: string, - timestamp: number, - ... - }; - - declare type ProfilerOnRenderFnType = ( - id: string, - phase: 'mount' | 'update', - actualDuration: number, - baseDuration: number, - startTime: number, - commitTime: number, - interactions: Set, - ) => void; - - declare export var Profiler: React$ComponentType<{| - children?: React$Node, - id: string, - onRender: ProfilerOnRenderFnType, - |}>; - - declare type TimeoutConfig = {| - timeoutMs: number, - |}; - - declare export default {| - +DOM: typeof DOM, - +PropTypes: typeof PropTypes, - +version: typeof version, - +checkPropTypes: typeof checkPropTypes, - +memo: typeof memo, - +lazy: typeof lazy, - +createClass: typeof createClass, - +createContext: typeof createContext, - +createElement: typeof createElement, - +cloneElement: typeof cloneElement, - +createRef: typeof createRef, - +forwardRef: typeof forwardRef, - +isValidElement: typeof isValidElement, - +Component: typeof Component, - +PureComponent: typeof PureComponent, - +Fragment: React$FragmentType, - +Children: typeof Children, - +ConcurrentMode: typeof ConcurrentMode, - +StrictMode: typeof StrictMode, - +Profiler: typeof Profiler, - +Suspense: typeof Suspense, - +useContext: typeof useContext, - +useState: typeof useState, - +useReducer: typeof useReducer, - +useRef: typeof useRef, - +useEffect: typeof useEffect, - +useLayoutEffect: typeof useLayoutEffect, - +useCallback: typeof useCallback, - +useMemo: typeof useMemo, - +useImperativeHandle: typeof useImperativeHandle, - +useTransition: typeof useTransition, - +useDeferredValue: typeof useDeferredValue, - +startTransition: typeof startTransition, - |}; -} diff --git a/flow-typed/shim.js.flow b/flow-typed/shim.js.flow new file mode 100644 index 0000000000000..c8b653aaf35db --- /dev/null +++ b/flow-typed/shim.js.flow @@ -0,0 +1,13 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @oncall relay + */ + +declare class Blob {} +declare class File {} +declare class HTMLElement {} diff --git a/gulpfile.js b/gulpfile.js index bee132230ce16..9ba515d816c7a 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -33,12 +33,8 @@ const del = require('del'); const fs = require('fs'); const gulp = require('gulp'); const babel = require('gulp-babel'); -const header = require('gulp-header'); const rename = require('gulp-rename'); -const gulpUtil = require('gulp-util'); const path = require('path'); -const webpack = require('webpack'); -const webpackStream = require('webpack-stream'); const RELEASE_COMMIT_SHA = process.env.RELEASE_COMMIT_SHA; if (RELEASE_COMMIT_SHA && RELEASE_COMMIT_SHA.length !== 40) { @@ -52,10 +48,6 @@ const VERSION = RELEASE_COMMIT_SHA ? `0.0.0-main-${RELEASE_COMMIT_SHA.substr(0, 8)}` : process.env.npm_package_version; -const DEVELOPMENT_HEADER = `/** - * Relay v${VERSION} - */ -`; const PRODUCTION_HEADER = `/** * Relay v${VERSION} * @@ -66,39 +58,6 @@ const PRODUCTION_HEADER = `/** */ `; -const buildDist = function (filename, opts, isProduction) { - const webpackOpts = { - externals: [/^[-/a-zA-Z0-9]+$/, /^@babel\/.+$/], - target: opts.target, - output: { - filename: filename, - libraryTarget: opts.libraryTarget, - library: opts.libraryName, - }, - plugins: [ - new webpack.DefinePlugin({ - 'process.env.NODE_ENV': JSON.stringify( - isProduction ? 'production' : 'development', - ), - }), - ], - }; - if (isProduction && !opts.noMinify) { - // See more chunks configuration here: https://gist.github.com/sokra/1522d586b8e5c0f5072d7565c2bee693 - webpackOpts.optimization = { - minimize: true, - }; - } - return webpackStream(webpackOpts, webpack, function (err, stats) { - if (err) { - throw new gulpUtil.PluginError('webpack', err); - } - if (stats.compilation.errors.length) { - throw new gulpUtil.PluginError('webpack', stats.toString()); - } - }); -}; - // Paths from package-root const PACKAGES = 'packages'; const DIST = 'dist'; @@ -119,15 +78,6 @@ const builds = [ index: 'BabelPluginRelay.js', macro: 'BabelPluginRelay.macro.js', }, - bundles: [ - { - entry: 'BabelPluginRelay.js', - output: 'babel-plugin-relay', - libraryName: 'BabelPluginRelay', - libraryTarget: 'commonjs2', - target: 'node', - }, - ], }, { package: 'react-relay', @@ -137,26 +87,6 @@ const builds = [ legacy: 'legacy.js', ReactRelayContext: 'ReactRelayContext.js', }, - bundles: [ - { - entry: 'index.js', - output: 'react-relay', - libraryName: 'ReactRelay', - libraryTarget: 'umd', - }, - { - entry: 'hooks.js', - output: 'react-relay-hooks', - libraryName: 'ReactRelayHooks', - libraryTarget: 'umd', - }, - { - entry: 'legacy.js', - output: 'react-relay-legacy', - libraryName: 'ReactRelayLegacy', - libraryTarget: 'umd', - }, - ], }, { package: 'relay-runtime', @@ -164,52 +94,18 @@ const builds = [ index: 'index.js', experimental: 'experimental.js', }, - bundles: [ - { - entry: 'index.js', - output: 'relay-runtime', - libraryName: 'RelayRuntime', - libraryTarget: 'umd', - }, - { - entry: 'experimental.js', - output: 'relay-runtime-experimental', - libraryName: 'ReactRelayExperimental', - libraryTarget: 'umd', - }, - ], }, { package: 'relay-test-utils', exports: { index: 'index.js', }, - bundles: [ - { - entry: 'index.js', - output: 'relay-test-utils', - libraryName: 'RelayTestUtils', - libraryTarget: 'commonjs2', - target: 'node', - noMinify: true, // Note: uglify can't yet handle modern JS - }, - ], }, { package: 'relay-test-utils-internal', exports: { index: 'index.js', }, - bundles: [ - { - entry: 'index.js', - output: 'relay-test-utils-internal', - libraryName: 'RelayTestUtilsInternal', - libraryTarget: 'commonjs2', - target: 'node', - noMinify: true, // Note: uglify can't yet handle modern JS - }, - ], }, ]; @@ -295,40 +191,8 @@ const exportsFiles = gulp.series( ), ); -const bundlesTasks = []; -builds.forEach(build => { - build.bundles.forEach(bundle => { - bundlesTasks.push(function bundleTask() { - return gulp - .src(path.join(DIST, build.package, 'lib', bundle.entry)) - .pipe( - buildDist(bundle.output + '.js', bundle, /* isProduction */ false), - ) - .pipe(header(DEVELOPMENT_HEADER)) - .pipe(gulp.dest(path.join(DIST, build.package))); - }); - }); -}); -const bundles = gulp.series(bundlesTasks); - -const bundlesMinTasks = []; -builds.forEach(build => { - build.bundles.forEach(bundle => { - bundlesMinTasks.push(function bundlesMinTask() { - return gulp - .src(path.join(DIST, build.package, 'lib', bundle.entry)) - .pipe( - buildDist(bundle.output + '.min.js', bundle, /* isProduction */ true), - ) - .pipe(header(PRODUCTION_HEADER)) - .pipe(gulp.dest(path.join(DIST, build.package))); - }); - }); -}); -const bundlesMin = gulp.series(bundlesMinTasks); - const clean = () => del(DIST); -const dist = gulp.series(exportsFiles, bundles, bundlesMin); +const dist = gulp.series(exportsFiles); const watch = gulp.series(dist, () => gulp.watch(INCLUDE_GLOBS, {cwd: PACKAGES}, dist), ); diff --git a/package.json b/package.json index 6fb2d8e94264a..2ce74e47e9430 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "relay-github-root", "description": "A framework for building data-driven React applications.", - "version": "18.2.0", + "version": "20.1.1", "license": "MIT", "homepage": "https://relay.dev", "bugs": "https://github.com/facebook/relay/issues", @@ -41,7 +41,7 @@ "@testing-library/react": "^16.0.1", "babel-eslint": "^10.1.0", "babel-plugin-macros": "^2.0.0", - "babel-plugin-syntax-hermes-parser": "0.24.0", + "babel-plugin-syntax-hermes-parser": "0.30.0", "babel-plugin-tester": "^6.0.1", "babel-preset-fbjs": "^3.4.0", "cosmiconfig": "^5.0.5", @@ -56,33 +56,29 @@ "eslint-plugin-jsx-a11y": "6.6.0", "eslint-plugin-node": "^11.1.0", "eslint-plugin-react": "7.30.1", - "eslint-plugin-react-hooks": "4.6.0", + "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-relay": "1.8.3", "eslint-plugin-relay-internal": "link:./packages/eslint-plugin-relay-internal", "fbjs": "^3.0.2", - "flow-bin": "^0.253.0", + "flow-bin": "^0.278.0", "glob": "^7.1.1", "graphql": "15.3.0", "gulp": "4.0.2", "gulp-babel": "8.0.0", "gulp-chmod": "3.0.0", - "gulp-header": "2.0.9", "gulp-rename": "^2.0.0", - "gulp-util": "3.0.8", - "hermes-eslint": "0.24.0", + "hermes-eslint": "0.30.0", "invariant": "^2.2.4", "jest": "^29.7.0", "jest-environment-jsdom": "^29.7.0", "nullthrows": "^1.1.1", - "prettier": "2.8.8", - "prettier-plugin-hermes-parser": "0.24.0", + "prettier": "3.6.2", + "prettier-plugin-hermes-parser": "0.31.1", "promise-polyfill": "6.1.0", - "react": "0.0.0-experimental-4beb1fd8-20241118", - "react-dom": "0.0.0-experimental-4beb1fd8-20241118", - "react-refresh": "0.0.0-experimental-4beb1fd8-20241118", - "react-test-renderer": "0.0.0-experimental-4beb1fd8-20241118", - "webpack": "^5.89.0", - "webpack-stream": "^7.0.0" + "react": "0.0.0-experimental-7670501b-20241124", + "react-dom": "0.0.0-experimental-7670501b-20241124", + "react-refresh": "0.0.0-experimental-7670501b-20241124", + "react-test-renderer": "0.0.0-experimental-7670501b-20241124" }, "resolutions": { "glob-watcher": "^6.0.0" @@ -92,15 +88,6 @@ "node": ">=18.x", "npm": ">=8.x" }, - "prettier": { - "arrowParens": "avoid", - "bracketSameLine": true, - "bracketSpacing": false, - "requirePragma": true, - "singleQuote": true, - "trailingComma": "all", - "parser": "hermes" - }, "jest": { "testMatch": [ "/packages/**/__tests__/**/*-test.js" diff --git a/packages/babel-plugin-relay/BabelPluginRelay.js b/packages/babel-plugin-relay/BabelPluginRelay.js index 84d97cec424cc..f015580ace591 100644 --- a/packages/babel-plugin-relay/BabelPluginRelay.js +++ b/packages/babel-plugin-relay/BabelPluginRelay.js @@ -41,7 +41,8 @@ export type RelayPluginOptions = { // Name of the global variable for dev mode isDevVariableName?: string, - // enable generating eager es modules for modern runtime + // Enable generating eager es modules for modern runtime. + // Defaults to `true` as of v19.0.1 eagerEsModules?: boolean, // Directory as specified by artifactDirectory when running relay-compiler diff --git a/packages/babel-plugin-relay/BabelPluginRelay.macro.js b/packages/babel-plugin-relay/BabelPluginRelay.macro.js index 4ab50084f7a90..dae3c05e60d01 100644 --- a/packages/babel-plugin-relay/BabelPluginRelay.macro.js +++ b/packages/babel-plugin-relay/BabelPluginRelay.macro.js @@ -27,6 +27,7 @@ function BabelPluginRelayMacro({references, state, babel, config}: any) { compileGraphQLTag( t, path, + // $FlowFixMe[unsafe-object-assign] Object.assign(state, config ? {opts: config} : {}), ast, ); diff --git a/packages/babel-plugin-relay/__tests__/BabelPluginRelay-esm-test.js b/packages/babel-plugin-relay/__tests__/BabelPluginRelay-esm-test.js index c5ca92d037361..07ae23a8588cf 100644 --- a/packages/babel-plugin-relay/__tests__/BabelPluginRelay-esm-test.js +++ b/packages/babel-plugin-relay/__tests__/BabelPluginRelay-esm-test.js @@ -13,27 +13,27 @@ const transformerWithOptions = require('./transformerWithOptions'); describe('`development` option', () => { - it('tests the hash when `development` is set', () => { + it('tests the hash when `development` is set', async () => { expect( - transformerWithOptions( + await transformerWithOptions( {eagerEsModules: true}, 'development', )('graphql`fragment TestFrag on Node { id }`'), ).toMatchSnapshot(); }); - it('tests the hash when `isDevVariable` is set', () => { + it('tests the hash when `isDevVariable` is set', async () => { expect( - transformerWithOptions({ + await transformerWithOptions({ eagerEsModules: true, isDevVariableName: 'IS_DEV', })('graphql`fragment TestFrag on Node { id }`'), ).toMatchSnapshot(); }); - it('uses a custom build command in message', () => { + it('uses a custom build command in message', async () => { expect( - transformerWithOptions( + await transformerWithOptions( { buildCommand: 'relay-compiler', eagerEsModules: true, @@ -43,9 +43,9 @@ describe('`development` option', () => { ).toMatchSnapshot(); }); - it('does not test the hash when `development` is not set', () => { + it('does not test the hash when `development` is not set', async () => { expect( - transformerWithOptions( + await transformerWithOptions( {eagerEsModules: true}, 'production', )('graphql`fragment TestFrag on Node { id }`'), diff --git a/packages/babel-plugin-relay/__tests__/BabelPluginRelay-path-test.js b/packages/babel-plugin-relay/__tests__/BabelPluginRelay-path-test.js index 96836554754c3..e47c31c063d3c 100644 --- a/packages/babel-plugin-relay/__tests__/BabelPluginRelay-path-test.js +++ b/packages/babel-plugin-relay/__tests__/BabelPluginRelay-path-test.js @@ -11,7 +11,7 @@ 'use strict'; describe('`development` option', () => { - function transformOnPlatform(platform: string) { + async function transformOnPlatform(platform: string) { jest.resetModules(); Object.defineProperty(process, 'platform', { @@ -28,17 +28,19 @@ describe('`development` option', () => { const transformerWithOptions = require('./transformerWithOptions'); - return transformerWithOptions( - { - artifactDirectory: '/test/artifacts', - }, - 'development', + return ( + await transformerWithOptions( + { + artifactDirectory: '/test/artifacts', + }, + 'development', + ) )('graphql`fragment TestFrag on Node { id }`'); } - it('tests the handling of file path', () => { - const codeOnPosix = transformOnPlatform('linux'); - const codeOnNonPosix = transformOnPlatform('win32'); + it('tests the handling of file path', async () => { + const codeOnPosix = await transformOnPlatform('linux'); + const codeOnNonPosix = await transformOnPlatform('win32'); expect(codeOnNonPosix).toEqual(codeOnPosix); expect(codeOnPosix).toMatchSnapshot(); diff --git a/packages/babel-plugin-relay/__tests__/BabelPluginRelay-test.js b/packages/babel-plugin-relay/__tests__/BabelPluginRelay-test.js index 0d531d19ede56..04c8bcdf9b69c 100644 --- a/packages/babel-plugin-relay/__tests__/BabelPluginRelay-test.js +++ b/packages/babel-plugin-relay/__tests__/BabelPluginRelay-test.js @@ -13,26 +13,26 @@ const transformerWithOptions = require('./transformerWithOptions'); describe('`development` option', () => { - it('tests the hash when `development` is set', () => { + it('tests the hash when `development` is set', async () => { expect( - transformerWithOptions( + await transformerWithOptions( {}, 'development', )('graphql`fragment TestFrag on Node { id }`'), ).toMatchSnapshot(); }); - it('tests the hash when `isDevVariableName` is set', () => { + it('tests the hash when `isDevVariableName` is set', async () => { expect( - transformerWithOptions({isDevVariableName: 'IS_DEV'})( + await transformerWithOptions({isDevVariableName: 'IS_DEV'})( 'graphql`fragment TestFrag on Node { id }`', ), ).toMatchSnapshot(); }); - it('uses a custom build command in message', () => { + it('uses a custom build command in message', async () => { expect( - transformerWithOptions( + await transformerWithOptions( { codegenCommand: 'relay-build', }, @@ -41,9 +41,9 @@ describe('`development` option', () => { ).toMatchSnapshot(); }); - it('does not test the hash when `development` is not set', () => { + it('does not test the hash when `development` is not set', async () => { expect( - transformerWithOptions( + await transformerWithOptions( {}, 'production', )('graphql`fragment TestFrag on Node { id }`'), diff --git a/packages/babel-plugin-relay/__tests__/__snapshots__/BabelPluginRelay-esm-test.js.snap b/packages/babel-plugin-relay/__tests__/__snapshots__/BabelPluginRelay-esm-test.js.snap index 53c75f3768f8c..215c46a6763e1 100644 --- a/packages/babel-plugin-relay/__tests__/__snapshots__/BabelPluginRelay-esm-test.js.snap +++ b/packages/babel-plugin-relay/__tests__/__snapshots__/BabelPluginRelay-esm-test.js.snap @@ -8,12 +8,12 @@ _TestFrag; exports[`\`development\` option tests the hash when \`development\` is set 1`] = ` "import _TestFrag from './__generated__/TestFrag.graphql'; -_TestFrag.hash && +(_TestFrag.hash && _TestFrag.hash !== '0bb6b7b29bc3e910921551c4ff5b6757' && console.error( \\"The definition of 'TestFrag' appears to have changed. Run \`relay-compiler\` to update the generated files to receive the expected data.\\", ), - _TestFrag; + _TestFrag); " `; @@ -32,11 +32,11 @@ IS_DEV exports[`\`development\` option uses a custom build command in message 1`] = ` "import _TestFrag from './__generated__/TestFrag.graphql'; -_TestFrag.hash && +(_TestFrag.hash && _TestFrag.hash !== '0bb6b7b29bc3e910921551c4ff5b6757' && console.error( \\"The definition of 'TestFrag' appears to have changed. Run \`relay-compiler\` to update the generated files to receive the expected data.\\", ), - _TestFrag; + _TestFrag); " `; diff --git a/packages/babel-plugin-relay/__tests__/__snapshots__/BabelPluginRelay-modern-artifact-directory-test.js.snap b/packages/babel-plugin-relay/__tests__/__snapshots__/BabelPluginRelay-modern-artifact-directory-test.js.snap index f7f522b87b00c..f288f9904ae7d 100644 --- a/packages/babel-plugin-relay/__tests__/__snapshots__/BabelPluginRelay-modern-artifact-directory-test.js.snap +++ b/packages/babel-plugin-relay/__tests__/__snapshots__/BabelPluginRelay-modern-artifact-directory-test.js.snap @@ -29,12 +29,9 @@ const testFragment = graphql\` 'use strict'; -var _TestFragment; +import _TestFragment from '../test/artifacts/TestFragment.graphql'; const {graphql} = require('relay-runtime'); -const testFragment = - _TestFragment !== void 0 - ? _TestFragment - : (_TestFragment = require('../test/artifacts/TestFragment.graphql')); +const testFragment = _TestFragment; `; @@ -79,18 +76,14 @@ function SomeTopLevelView() { 'use strict'; -var _ExampleQuery; +import _ExampleQuery from '../test/artifacts/ExampleQuery.graphql'; function SomeTopLevelView() { const _graphql = 'unrelated'; return ( string { +): string => Promise { return (text, providedFileName) => { const previousEnv = process.env.BABEL_ENV; try { diff --git a/packages/babel-plugin-relay/compileGraphQLTag.js b/packages/babel-plugin-relay/compileGraphQLTag.js index 785f2e29e6c93..794d711d3e9fe 100644 --- a/packages/babel-plugin-relay/compileGraphQLTag.js +++ b/packages/babel-plugin-relay/compileGraphQLTag.js @@ -18,6 +18,7 @@ import type { OperationDefinitionNode, } from 'graphql'; +// $FlowFixMe[cannot-resolve-module] const crypto = require('crypto'); const {print} = require('graphql'); const { @@ -25,6 +26,7 @@ const { join: joinPath, relative: relativePath, resolve: resolvePath, + // $FlowFixMe[cannot-resolve-module] } = require('path'); const GENERATED = './__generated__/'; @@ -34,6 +36,7 @@ const GENERATED = './__generated__/'; * cross-platform compatibility. */ function posixifyPath(path: string): string { + // $FlowFixMe[cannot-resolve-name] return process.platform === 'win32' ? path.replace(/\\/g, '/') : path; } @@ -65,13 +68,14 @@ function compileGraphQLTag( ); } - const eagerEsModules = state.opts?.eagerEsModules ?? false; + const eagerEsModules = state.opts?.eagerEsModules ?? true; const isHasteMode = state.opts?.jsModuleFormat === 'haste'; const isDevVariable = state.opts?.isDevVariableName; const artifactDirectory = state.opts?.artifactDirectory; const buildCommand = state.opts?.codegenCommand ?? 'relay-compiler'; // Fallback is 'true' const isDevelopment = + // $FlowFixMe[cannot-resolve-name] (process.env.BABEL_ENV || process.env.NODE_ENV) !== 'production'; return createNode(t, state, path, definition, { diff --git a/packages/babel-plugin-relay/package.json b/packages/babel-plugin-relay/package.json index 481c05c2d8172..d69b60669d506 100644 --- a/packages/babel-plugin-relay/package.json +++ b/packages/babel-plugin-relay/package.json @@ -1,7 +1,7 @@ { "name": "babel-plugin-relay", "description": "A Babel Plugin for use with Relay applications.", - "version": "18.2.0", + "version": "20.1.1", "keywords": [ "graphql", "relay", @@ -23,7 +23,7 @@ }, "devDependencies": { "@babel/core": "^7.25.2", - "prettier": "2.8.8", - "prettier-plugin-hermes-parser": "0.24.0" + "prettier": "3.6.2", + "prettier-plugin-hermes-parser": "0.31.1" } } diff --git a/packages/eslint-plugin-relay-internal/__tests__/lib/rules/no-for-of-loops-test.js b/packages/eslint-plugin-relay-internal/__tests__/lib/rules/no-for-of-loops-test.js new file mode 100644 index 0000000000000..54ce3b9753ca5 --- /dev/null +++ b/packages/eslint-plugin-relay-internal/__tests__/lib/rules/no-for-of-loops-test.js @@ -0,0 +1,46 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow + * @format + * @oncall relay + */ + +'use strict'; + +const rule = require('../../../lib/rules/no-for-of-loops'); +const RuleTester = require('eslint').RuleTester; + +const ruleTester = new RuleTester({ + parser: require.resolve('hermes-eslint'), + parserOptions: { + sourceType: 'module', + ecmaVersion: 6, + }, +}); + +ruleTester.run('no-for-of-loops', rule, { + valid: [ + // Valid code samples that should never error + 'for (let i = 0; i < 10; i++) {}', + 'for (const i in obj) {}', + 'arr.forEach(() => {});', + ], + invalid: [ + // Invalid code samples that trigger the rule to report + { + code: 'for (const i of arr) {}', + errors: [{messageId: 'forOfLoop'}], + }, + { + code: ` + for (const missing of cachedSnapshot.missingClientEdges) { + this._missingClientEdges.push(missing); + }`, + errors: [{messageId: 'forOfLoop'}], + }, + ], +}); diff --git a/packages/eslint-plugin-relay-internal/lib/index.js b/packages/eslint-plugin-relay-internal/lib/index.js index ebb8120aedeb4..b9434601fcd86 100644 --- a/packages/eslint-plugin-relay-internal/lib/index.js +++ b/packages/eslint-plugin-relay-internal/lib/index.js @@ -16,4 +16,5 @@ module.exports.rules = { 'no-mixed-import-and-require': require('./rules/no-mixed-import-and-require'), 'sort-imports': require('./rules/sort-imports'), // Synced from WWW + 'no-for-of-loops': require('./rules/no-for-of-loops'), }; diff --git a/packages/eslint-plugin-relay-internal/lib/rules/no-for-of-loops.js b/packages/eslint-plugin-relay-internal/lib/rules/no-for-of-loops.js new file mode 100644 index 0000000000000..77696e5ed0907 --- /dev/null +++ b/packages/eslint-plugin-relay-internal/lib/rules/no-for-of-loops.js @@ -0,0 +1,29 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @oncall relay + */ + +'use strict'; + +const rule = { + meta: { + messages: { + forOfLoop: + 'for..of loops are not allowed. They get transformed into expensive iterator loops at build time.', + }, + }, + create(context) { + return { + ForOfStatement: node => { + context.report({node, messageId: 'forOfLoop'}); + }, + }; + }, +}; + +module.exports = rule; diff --git a/packages/eslint-plugin-relay-internal/lib/rules/sort-imports.js b/packages/eslint-plugin-relay-internal/lib/rules/sort-imports.js index 6f52515b75afc..618bf01efd1e7 100644 --- a/packages/eslint-plugin-relay-internal/lib/rules/sort-imports.js +++ b/packages/eslint-plugin-relay-internal/lib/rules/sort-imports.js @@ -6,7 +6,10 @@ * * To regenerate this file, please run this command on Meta's monorepo: * @codegen-command : xplat/js/tools/sort-imports/scripts/build.sh - * @generated SignedSource<<165e00cb24cdaa9e47306c8384d4355f>> + * @generated SignedSource<<7b842f046d041c0d5d44b62d0f8244e1>> * @nolint */ -"use strict";function e(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}function t(e){var t=e.default;if("function"==typeof t){var n=function(){return t.apply(this,arguments)};n.prototype=t.prototype}else n={};return Object.defineProperty(n,"__esModule",{value:!0}),Object.keys(e).forEach((function(t){var r=Object.getOwnPropertyDescriptor(e,t);Object.defineProperty(n,t,r.get?r:{enumerable:!0,get:function(){return e[t]}})})),n}const n=e(require("path")).default,r=__dirname.split(n.sep).includes("xplat");function i(e){if(("Literal"===e.type||"JSXText"===e.type)&&"string"==typeof e.value)return e.value;if("BinaryExpression"===e.type&&"+"===e.operator){const t=i(e.left),n=i(e.right);if(null!=t&&null!=n)return t+n}return null}function o(e){if("Identifier"===e.type)return e.name;if("ThisExpression"===e.type)return"this";if("MemberExpression"===e.type){const t=o(e.object),n=e.computed?i(e.property):o(e.property);if(null!=t&&null!=n)return t+"."+n}else if("TypeCastExpression"===e.type||"AsExpression"===e.type)return o(e.expression);return null}function a(e){return e.callee?o(e.callee):null}function l(e,t,n){const r=e.getSourceCode().getText(),i=function(e,t){if(t.line<1)throw new RangeError("Line number "+t.line+" is before the start of file");const n=/\r\n|\r|\n|\u2028|\u2029/g;let r={index:0};for(let i=1;i=e.length)throw new RangeError("computed offset "+t+" is past the end of file");const n=/\r\n|\r|\n|\u2028|\u2029/g;let r,i={index:0},o=0;do{r=i,i=n.exec(e),++o}while(i&&i.index"string"==typeof e&&e.charCodeAt(0)>=97,c=e=>"string"==typeof e&&e.charCodeAt(0)<=90;function p(e){return null!=e&&"VariableDeclaration"===e.type&&"Program"===e.parent.type&&1===e.declarations.length&&null!=e.declarations[0].init&&m(e.declarations[0].init)}function f(e){return null!=e&&"VariableDeclarator"===e.type&&"VariableDeclaration"===e.parent.type&&"Program"===e.parent.parent.type&&1===e.parent.declarations.length&&m(e.init)}function m(e){return null!=e&&(g(e)||y(e)||h(e))}function d(e){return null!=e&&"CallExpression"===e.type&&e.callee&&"Identifier"===e.callee.type&&"requireNUX"===e.callee.name&&2===e.arguments.length&&"Literal"===e.arguments[0].type}function g(e){return"CallExpression"===e.type&&e.callee&&"Identifier"===e.callee.type&&"require"===e.callee.name&&1===e.arguments.length&&"Literal"===e.arguments[0].type}function y(e){return"CallExpression"===e.type&&"Identifier"===e.callee.type&&"requireDeferred"===e.callee.name&&1===e.arguments.length&&"Literal"===e.arguments[0].type&&"string"==typeof e.arguments[0].value}function h(e){return"CallExpression"===e.type&&"Identifier"===e.callee.type&&"requireDeferredForDisplay"===e.callee.name&&1===e.arguments.length&&"Literal"===e.arguments[0].type&&"string"==typeof e.arguments[0].value}function x(e){return"ImportDeclaration"===e.type&&(null==e.importKind||"value"===e.importKind)}function b(e){return"ImportDeclaration"===e.type&&("type"===e.importKind||"typeof"===e.importKind)}function C(e){return x(e)||b(e)}function v(e){return null!=e&&"CallExpression"===e.type&&e.callee&&"Identifier"===e.callee.type&&"JSResource"===e.callee.name&&1===e.arguments.length&&"Literal"===e.arguments[0].type}function S(e){return null!=e&&"CallExpression"===e.type&&e.callee&&"Identifier"===e.callee.type&&"JSResourceForInteraction"===e.callee.name&&2===e.arguments.length&&"Literal"===e.arguments[0].type}function E(e){return null!=e&&"CallExpression"===e.type&&"Identifier"===e.callee.type&&"ClientJSResource"===e.callee.name&&1===e.arguments.length&&"Literal"===e.arguments[0].type&&"string"==typeof e.arguments[0].value}function I(e){if(null==e||"CallExpression"!==e.type||null==e.callee)return!1;let t;return"Identifier"===e.callee.type&&(t=e.callee),"MemberExpression"===e.callee.type&&"Identifier"===e.callee.object.type&&(t=e.callee.object),null!=t&&"requireCond"===t.name&&e.arguments.length>0}function T(e){return 0===e.indexOf("m#")?e.substring(2):e}function D(e,t,n=1){return"CallExpression"!==e.type||e.arguments.length!==n||null!=t&&"string"==typeof t&&a(e)!==t||"Literal"!==e.arguments[0].type||"string"!=typeof e.arguments[0].value?null:T(e.arguments[0].value)}function R(e,t,n){const r=e.arguments;return I(e)?3!==r.length?null:(null==t||r[0]&&"Literal"===r[0].type&&r[0].value===t)&&(null==n||r[1]&&"Literal"===r[1].type&&r[1].value===n)?r[2]&&"ObjectExpression"===r[2].type?r[2].properties.reduce(((e,t)=>{if("Property"===t.type&&"Literal"===t.value.type){let n;if("Identifier"===t.key.type)n=t.key.name;else{if("Literal"!==t.key.type)return e;n=String(t.key.value)}e[n]="string"==typeof t.value.value?T(t.value.value):null}return e}),{}):r[2]&&"Literal"===r[2].type&&"string"==typeof r[2].value?T(r[2].value):null:null:null}function w(e){return D(e,"JSResource")}function N(e){return D(e,"JSResourceForInteraction",2)}function k(e){return null==e?null:m(e)?"string"!=typeof e.arguments[0].value?null:T(e.arguments[0].value):null}function q(e){return d(e)?2!==e.arguments.length||"string"!=typeof e.arguments[1].value?null:T(e.arguments[1].value):null}function L(e,t){const n=e.getSourceCode().ast.body.find((e=>"VariableDeclaration"===e.type&&1===e.declarations.length&&"VariableDeclarator"===e.declarations[0].type&&null!=e.declarations[0].init&&k(e.declarations[0].init)===t));return n||null}function O(e){let t=e;for(;"TypeCastExpression"===t.type||"AsExpression"===t.type;)t=t.expression;return t}function P(e){if("value"!==e.importKind)return!0;return e.specifiers.every((e=>"ImportSpecifier"===e.type&&("type"===e.importKind||"typeof"===e.importKind)))}function j(e,t){const n=e.getSourceCode().ast.body.find((e=>"ImportDeclaration"===e.type&&e.source.value===t&&!P(e)));return n||null}var B={asAnyKindOfRequireCall:function(e){return m(e)?e:null},asAnyKindOfRequireVariableDeclaration:function(e){return p(e)?e:null},asAnyKindOfRequireVariableDeclarator:function(e){return f(e)?e:null},getAnyRequiredModuleName:function(e,t=Object.freeze({})){const n=k(e)||w(e)||N(e)||q(e);return null!=n?n:R(e,t.condType,t.condition)},getBaseNode:function(e){let t=e;for(;"MemberExpression"===t.type;)t=t.object;return t},getBinding:s,getBootloadedModuleNames:function(e){return"ExpressionStatement"===e.type&&e.expression&&"CallExpression"===e.expression.type&&e.expression.callee&&"MemberExpression"===e.expression.callee.type&&e.expression.callee.object&&"Bootloader"===e.expression.callee.object.name&&e.expression.callee.property&&"loadModules"===e.expression.callee.property.name&&e.expression.arguments.length>0&&"ArrayExpression"===e.expression.arguments[0].type&&e.expression.arguments[0].elements.length>0?e.expression.arguments[0].elements.map((e=>"Literal"===e.type&&"string"==typeof e.value?T(e.value):null)).filter(Boolean):null},getCalleeName:a,getConstantStringExpression:i,getCurrentClassName:function(e){const t=e.getAncestors().find((e=>"ClassDeclaration"===e.type));return t&&"ClassDeclaration"===t.type&&null!=t.id?t.id.name:null},getEnglishForNth:function(e){return["first","second","third","fourth","fifth","sixth"][e]},getFullyQualifiedIdentifier:o,getJSResourceModuleName:w,getJSXMemberOrNamespaceRoot:function(e){let t=e;for(;"JSXIdentifier"!==t.type;)if("JSXMemberExpression"===t.type)t=t.object;else{if("JSXNamespacedName"!==t.type)throw new Error("unexpected "+t.type);t=t.namespace}return t},getLocOffset:function(e,t,n){return l(e,t.loc.start,n)},getLocOffsetOfLoc:l,getName:function(e){const t=O(e);return"Identifier"===t.type?t.name:"Literal"===t.type?String(t.value):null},getObjectPropertyName:function(e){if("Property"!==e.type&&"PropertyDefinition"!==e.type&&"MethodDefinition"!==e.type)return null;const t=e.key;return"Identifier"!==t.type||e.computed?function(e){switch(e.type){case"Literal":switch(e.literalType){case"bigint":return e.bigint;case"null":return"null";case"regexp":return`/${e.regex.pattern}/${e.regex.flags}`;default:return String(e.value)}case"TemplateLiteral":if(0===e.expressions.length&&1===e.quasis.length)return e.quasis[0].value.cooked}return null}(t):t.name},getParamComments:function(e,t){return t.params.map((function(t){const n=e.getSourceCode().getCommentsBefore(t);return n[n.length-1]}))},getPropertyName:function(e){return"MemberExpression"!==e.type?null:e.computed?i(e.property):"Identifier"!==e.property.type?null:e.property.name},getPropTokens:function(e,t){const n=e.getSourceCode().getTokens(t),r=[];return n.forEach(((e,t,n)=>{"JSXIdentifier"===e.type&&"Punctuator"===n[t+1].type&&"="===n[t+1].value&&r.push(e)})),r},getRequireCondModules:R,getRequireModuleName:k,getRequireModuleNode:L,getReturnComment:function(e,t){return e.getSourceCode().getCommentsBefore(t.body)[0]},getValueImportNode:j,getValueImportVariables:function(e,t){const n=[];for(const r of e.getSourceCode().ast.body)"ImportDeclaration"!==r.type||r.source.value!==t||P(r)||r.specifiers.filter((e=>{null!=e.importKind&&"value"!==e.importKind||n.push(e.local.name)}));return n},hasValueImport:function(e){return e.getSourceCode().ast.body.some((e=>"ImportDeclaration"===e.type&&!P(e)))},getVariable:function(e,t){let n=t;for(;n;){const t=n.set.get(e);if(t)return t;n=n.upper}return null},insertRequireStatement:function(e,t,n,r=""){if(""===n)throw new Error("Name must be a string with length larger than 0");const i=`const ${n} = require('${n}${r}');`,o=[];e.getSourceCode().ast.body.forEach((e=>{if("VariableDeclaration"===e.type&&1===e.declarations.length&&"VariableDeclarator"===e.declarations[0].type&&e.declarations[0].init){const t=k(e.declarations[0].init);null!=t&&o.push({name:t,node:e})}}));const a=o.find((e=>e.name>=n));if(a){if(a.name.replace(r,"")===n)return[];const e=o[0];if(u(a.name)&&c(n)){if(e!==a){const e=a.node.range[0];return[t.removeRange([e-1,e-1]),t.insertTextBefore(a.node,i+"\n\n")]}return[t.insertTextBefore(a.node,i+"\n\n")]}return[t.insertTextBefore(a.node,i+"\n")]}if(o.length>0){const e=o[o.length-1],r=c(e.name)&&u(n)?"\n":"";return[t.insertTextAfter(e.node,"\n"+i+r)]}{const n=e.getSourceCode().ast.body,r=n[0];return"ExpressionStatement"===r.type&&"Literal"===r.expression.type&&"use strict"===r.expression.value?[t.insertTextBefore(n[1],i+"\n")]:[t.insertTextBefore(n[0],i+"\n")]}},insertValueImportStatement:function(e,t,n,r="",i){if(""===n)throw new Error("Name must be a string with length larger than 0");const o=`import ${i??n} from '${n}${r}';`,a=[];e.getSourceCode().ast.body.forEach((e=>{if(x(e)){const t=function(e){if(m(e))return"string"!=typeof e.arguments[0].value?null:T(e.arguments[0].value);if(C(e))return e.source.value;return null}(e);null!=t&&a.push({name:t,node:e})}}));const l=a.find((e=>e.name>=n));if(l){if(l.name.replace(r,"")===n)return[];const e=a[0];if(u(l.name)&&c(n)){if(e!==l){const e=l.node.range[0];return[t.removeRange([e-1,e-1]),t.insertTextBefore(l.node,o+"\n\n")]}return[t.insertTextBefore(l.node,o+"\n\n")]}return[t.insertTextBefore(l.node,o+"\n")]}if(a.length>0){const e=a[a.length-1],r=c(e.name)&&u(n)?"\n":"";return[t.insertTextAfter(e.node,"\n"+o+r)]}{const n=e.getSourceCode().ast.body,r=n[0];return"ExpressionStatement"===r.type&&"Literal"===r.expression.type&&"use strict"===r.expression.value?[t.insertTextBefore(n[1],o+"\n")]:[t.insertTextBefore(n[0],o+"\n")]}},isAnyKindOfImport:C,isAnyKindOfModuleCall:function(e){return m(e)||I(e)||v(e)||S(e)||E(e)||d(e)},isAnyKindOfRequireCall:m,isAnyKindOfRequireVariableDeclaration:p,isAnyKindOfRequireVariableDeclarator:f,isClientJSResource:E,isFbSourceRepo:r,isGraphQLTemplate:function(e){return"Identifier"===e.tag.type&&"graphql"===e.tag.name&&1===e.quasi.quasis.length},isInsideMethod:function(e,t){return e.getAncestors().some((e=>"MethodDefinition"===e.type&&"Identifier"===e.key.type&&e.key.name===t))},isJSResource:v,isJSResourceForInteraction:S,isModuleRef:function(e){return"Literal"===e.type&&"string"==typeof e.value&&e.value.startsWith("m#")},isOnlyTypeImport:P,isOnlyTypeExport:function(e){return"type"===e.exportKind},isReferenced:function(e){const t=e.parent;switch(t.type){case"MemberExpression":case"JSXMemberExpression":return t.property===e&&!0===t.computed||t.object===e;case"MetaProperty":case"ImportDefaultSpecifier":case"ImportNamespaceSpecifier":case"ImportSpecifier":case"LabeledStatement":case"RestElement":case"ObjectPattern":case"ArrayPattern":return!1;case"Property":case"MethodDefinition":return t.key===e&&t.computed;case"VariableDeclarator":case"ClassDeclaration":case"ClassExpression":return t.id!==e;case"ArrowFunctionExpression":case"FunctionDeclaration":case"HookDeclaration":case"FunctionExpression":for(let n=0;ne.writeExpr));return null!=i?i.writeExpr:null},resolveModuleSource:function(e,t){const n=s(e.getScope(),t);if(null==n)return null;const r=n.defs[0];switch(r.node.type){case"VariableDeclarator":{const e=r.node.init;if(null!=e)return k(e)||w(e)||N(e)||q(e);break}case"ImportNamespaceSpecifier":case"ImportSpecifier":case"ImportDefaultSpecifier":return r.node.parent.source.value}return null},stripModuleRef:T,uncast:O};var F=t(Object.freeze({__proto__:null,isCommaOrSemiToken:function(e){return"Punctuator"===e.type&&(","===e.value||";"===e.value)}}));const{isCommaOrSemiToken:M}=F;var A={getInlineComments:function(e,t,n=M){const r=e.ast.comments.filter((e=>e.loc.start.line===t.loc.end.line&&e.range[0]>t.range[1])).sort(((e,t)=>e.range[0]-t.range[0])),i=[];let o=t;for(const t of r){const r=e.getTokensBetween(o,t);if(r.length>0){if(!n)break;if(!r.every(n))break}i.push(t),o=t}return i},getLeadingComments:function(e,t,n){const r=e.getCommentsBefore(t),i=[];let o=t;for(let t=r.length-1;t>=0;t-=1){const a=r[t];if(a===n)break;if(a.loc.end.line===o.loc.start.line){i.unshift(a),o=a;continue}if(a.loc.end.line!==o.loc.start.line-1)break;const l=e.getTokenBefore(a);if(l&&l.loc.end.line===a.loc.start.line)break;i.unshift(a),o=a}return i}};var V=function(e){return e.getSourceCode().ast.docblock?.comment};const{getInlineComments:$,getLeadingComments:K}=A,{isCommaOrSemiToken:_}=F;function J(e){return"@"===e[0]||":"===e[0]}const X=/\d{1,3}(\.\d+)?%/;function U(e){switch(typeof e){case"number":return{isSafeNumericString:!0,isPercentage:!1};case"boolean":return{isSafeNumericString:!1,isPercentage:!1}}return isNaN(e)||isNaN(parseFloat(e))?X.test(e)?{isSafeNumericString:!0,isPercentage:!0}:{isSafeNumericString:!1,isPercentage:!1}:{isSafeNumericString:!0,isPercentage:!1}}function z(e){return e.reduce((([e,t],[n,r])=>[Math.min(e,n),Math.max(t,r)]),[Number.MAX_SAFE_INTEGER,0])}var G={compareNames:function(e,t,n=!1){if("number"==typeof e&&"number"==typeof t)return e-t;const r=String(e),i=String(t);if(""===r&&""!==i)return-1;if(""!==r&&""===i)return 1;if(""===r&&""===i)return 0;const{isSafeNumericString:o,isPercentage:a}=U(r),{isSafeNumericString:l,isPercentage:s}=U(i);if(o&&l){const e=Number.parseFloat(r),t=Number.parseFloat(i);if(e===t){if(!a&&s)return-1;if(a&&!s)return 1}return e-t}if(n){const e=J(r),t=J(i);if(!e&&t)return-1;if(e&&!t)return 1}const u=o||r[0].toLowerCase()===r[0].toUpperCase(),c=l||i[0].toLowerCase()===i[0].toUpperCase();if(!u&&c)return 1;if(u&&!c)return-1;if(!u&&!c){const e=r[0].toLowerCase()===r[0],t=i[0].toLowerCase()===i[0];if(!e&&t)return-1;if(e&&!t)return 1}return r.localeCompare(i,"en",{caseFirst:"upper",sensitivity:"base"})},getEncompassingRange:z,getNodeTextWithComments:function(e,t,n,{shouldIncludeNextTokenInRange:r=_,ensureTextFollowsNode:i,inlineCommentIgnoreToken:o}={}){const a=$(e,t,o),l=[...K(e,t,n).map((({range:e})=>e)),t.range,...a.map((({range:e})=>e))],s=e.getTokenAfter(t);s&&!0===r?.(s)&&l.push(s.range);const u=z(l);let c=e.text.slice(u[0],u[1]);const p=t.range[1]-u[0];if(null!=i){e.getTokenAfter(t)?.value!==i&&(c=c.slice(0,p)+i+c.slice(p))}return{range:u,text:c}},isComma:function(e){return"Punctuator"===e.type&&","===e.value}};const Q=B,{getInlineComments:W,getLeadingComments:H}=A,Y=V,{compareNames:Z}=G,ee=0,te={default:1,namespace:2,named:3},ne={default:4,namespace:5,named:6},re={default:7,namespace:8,named:9},ie=0,oe=1,ae=2,le=3,se=99;var ue={meta:{fixable:"code",messages:{incorrectOrder:"Requires should be sorted alphabetically"}},create(e){const t=e.getSourceCode(),n=function(e){const t=Y(e);if(null!=t)return t;const n=e.getSourceCode().ast,r=n.body.length>0?n.body[0]:n,i=e.getSourceCode().getCommentsBefore(r)[0];return i&&"Block"===i.type?i:null}(e);if(n&&(n.value.includes("* @generated")||n.value.includes("* @partially-generated"))&&!n.value.includes("* @xpr_allow_generated_lint"))return{};const r=Object.freeze({typeImport:{priority:10,uppercase:[],lowercase:[],tiebreakFunction:s},valueImport:{priority:20,uppercase:[],lowercase:[],tiebreakFunction:s},requiresUsedByOtherRequires:{priority:30,uppercase:[],lowercase:[],tiebreakFunction:u},require:{priority:40,uppercase:[],lowercase:[],tiebreakFunction:u}}),i=[];let o=null,a=null;const l=new Set;return{Program(n){for(const e of n.body)switch(e.type){case"ImportDeclaration":if("type"===e.importKind||"typeof"===e.importKind)d(r.typeImport,e,e.source.value,!0);else{const n=t.getLastToken(e.source,(e=>"from"===e.value));0===e.specifiers.length&&null==n?g(e):d(r.valueImport,e,e.source.value)}break;case"VariableDeclaration":{const t=e.declarations[0]?.init;if(1!==e.declarations.length||null==t){g(e);break}f(t,e);break}default:g(e)}const s=[];for(const e of Object.keys(r)){const t=r[e];s.push({priority:t.priority,nodes:t.uppercase.sort(((e,n)=>c(e,n,t.tiebreakFunction)))}),s.push({priority:t.priority+5,nodes:t.lowercase.sort(((e,n)=>c(e,n,t.tiebreakFunction)))})}function u(e){return[e.leadingComments.length?e.leadingComments.map((e=>t.getText(e))).join("\n")+"\n":"",e.inlineComments.length?" "+e.inlineComments.map((e=>t.getText(e))).join(" "):""]}const p=s.filter((e=>0!==e.nodes.length)).sort(((e,t)=>e.priority-t.priority)).map((e=>e.nodes.map((e=>{const[n,r]=u(e),i=function(e,t){const n=t.node,r=e.getText(n),i=(()=>{if("ImportDeclaration"===n.type&&null!=n.specifiers.find((e=>"ImportSpecifier"===e.type))){const t=e.getFirstToken(n,(e=>"Punctuator"===e.type&&"{"===e.value)),r=e.getFirstToken(n,(e=>"Punctuator"===e.type&&"}"===e.value));return null==t||null==r?null:e.getText().substring(t.range[0],r.range[1])}if("VariableDeclaration"===n.type&&"ObjectPattern"===n.declarations[0].id.type){const t=n.declarations[0].id.typeAnnotation,r=e.getText(n.declarations[0].id);return t?r.substr(0,t.range[0]-n.declarations[0].id.range[0]):r}return null})();if(null==i)return r;let o=[],a=null;"ImportDeclaration"===n.type?o=n.specifiers.map((t=>"ImportDefaultSpecifier"===t.type||"ImportNamespaceSpecifier"===t.type?null:{leadingComments:e.getCommentsBefore(t),name:t.imported.name,node:t})).filter(Boolean):"VariableDeclaration"===n.type&&"ObjectPattern"===n.declarations[0].id.type&&(o=n.declarations[0].id.properties.map((t=>{if("ExperimentalRestProperty"===t.type||"RestElement"===t.type)return a=t,null;const n=t.key,r="Literal"===n.type?String(n.value):"Identifier"===n.type?t.computed?null:n.name:null;return null==r?null:{leadingComments:e.getCommentsBefore(t),name:r,node:t}})).filter(Boolean));if(o.length<=1)return r;const l=i.indexOf("\n")>=0,s=o.sort(((e,t)=>Z(e.name,t.name))).map((e=>e.node));null!=a&&s.push(a);const u=s.map((t=>{const n=e.getCommentsBefore(t).map((t=>e.getText(t))),r=n.length?n.join(""):"";return l?(r?" "+r+"\n":"")+" "+e.getText(t):(r?r+" ":"")+e.getText(t)})),c=(()=>{const t=[];if("ImportDeclaration"===n.type){if(t.push(...e.getCommentsBefore(n.source)),l&&null!=n.specifiers.find((e=>"ImportSpecifier"===e.type))){const r=e.getTokenBefore(n.source,(e=>"Punctuator"===e.type&&"}"===e.value));null!=r&&t.push(...e.getCommentsBefore(r))}}else if("VariableDeclaration"===n.type&&"ObjectPattern"===n.declarations[0].id.type){const r=e.getLastToken(n.declarations[0].id);null!=r&&t.push(...e.getCommentsBefore(r))}return t})(),p=l&&c.length?c.map((t=>" "+e.getText(t))).join("\n")+"\n":"",f=l?"\n"+u.map((e=>e.includes("...")?`${e}\n`:`${e},\n`)).join("")+p:u.join(", ");return r.replace(i,(()=>`{${f}}`))}(t,e);return n+i+r})).join("\n"))).join("\n\n"),m=o;if(null==m||null==a)return;const y=m.leadingComments.length?m.leadingComments[0].range[0]:m.node.range[0],h=a.inlineComments.length>0?a.inlineComments[a.inlineComments.length-1].range[1]:a.node.range[1];t.getText(m.node,m.node.range[0]-y,h-m.node.range[1])!==p&&e.report({node:m.node,messageId:"incorrectOrder",fix(e){const n=i.filter((({node:e})=>e.range[0]>=y&&e.range[1]<=h)).map((e=>{const[n,r]=u(e),i=t.getText(e.node);return{range:e.node.range,text:n+i+r}})).flat().concat(t.getAllComments().filter((e=>e.range[0]>=y&&e.range[1]<=h&&!l.has(e))).map((e=>({range:e.range,text:t.getText(e)})))).sort(((e,t)=>e.range[0]-t.range[0]||e.range[1]-t.range[1])).map((({text:e})=>e)).join("\n");return e.replaceTextRange([y,h],[p,n].filter(Boolean).join("\n\n"))}})}};function s(e){if(0===e.specifiers.length)return ee;const t=(()=>{switch(e.importKind){default:case"value":return te;case"type":return ne;case"typeof":return re}})();return e.specifiers.find((e=>"ImportDefaultSpecifier"===e.type))?t.default:e.specifiers.find((e=>"ImportNamespaceSpecifier"===e.type))?t.namespace:t.named}function u(e){if("ExpressionStatement"===e.type)return ie;switch(e.declarations[0].id.type){case"Identifier":return oe;case"ObjectPattern":return ae;case"ArrayPattern":return le}return se}function c(e,t,n){const r=Z(e.moduleName,t.moduleName);if(0!==r)return r;const i=n(e.node)-n(t.node);return 0!==i?i:e.node.loc.start.line-t.node.loc.start.line}function p(e,t){if(null==e)throw new Error("Missing required module name");return null!=t?`${e}_${t}`:e}function f(e,n,i){const o=Q.getRequireModuleName(e);if(Q.isRequire(e)){const e=p(o,i);d("requireCond"===e||"requireDeferred"===e||"requireDeferredForDisplay"===e?r.requiresUsedByOtherRequires:r.require,n,p(o,i))}else if(Q.isRequireDeferred(e)||Q.isRequireDeferredForDisplay(e))d(r.require,n,p(o,i));else{if(Q.isRequireCond(e)){if("VariableDeclaration"===n.type){const e=t.getText(n.declarations[0].id);return void d(r.require,n,p(e,i))}}else{if("MemberExpression"===e.type)return void f(e.object,n,p(t.getText(e.property),i));if("CallExpression"===e.type)return void f(e.callee,n,i)}g(n)}}function m(e){e.leadingComments.forEach((e=>l.add(e))),e.inlineComments.forEach((e=>l.add(e)));t.getCommentsInside(e.node).forEach((e=>l.add(e)))}function d(e,r,i,l=!1){const s={inlineComments:W(t,r),leadingComments:H(t,r,n),moduleName:i,node:r};if(l)e.uppercase.push(s);else{const t=i[0]||"";t.toLowerCase()===t?e.lowercase.push(s):e.uppercase.push(s)}null==o&&(o=s),a=s,m(s)}function g(e){const r={inlineComments:W(t,e),leadingComments:H(t,e,n),node:e};i.push(r),m(r)}}};var ce=ue;module.exports=ce; + +/* eslint-disable */ + +"use strict";var e,t,n=require("path");function r(e){var t=Object.create(null);return e&&Object.keys(e).forEach((function(n){if("default"!==n){var r=Object.getOwnPropertyDescriptor(e,n);Object.defineProperty(t,n,r.get?r:{enumerable:!0,get:function(){return e[n]}})}})),t.default=e,Object.freeze(t)}function o(e,t){return t.forEach((function(t){t&&"string"!=typeof t&&!Array.isArray(t)&&Object.keys(t).forEach((function(n){if("default"!==n&&!(n in e)){var r=Object.getOwnPropertyDescriptor(t,n);Object.defineProperty(e,n,r.get?r:{enumerable:!0,get:function(){return t[n]}})}}))})),Object.freeze(e)}function i(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}function a(e){if(Object.prototype.hasOwnProperty.call(e,"__esModule"))return e;var t=e.default;if("function"==typeof t){var n=function e(){return this instanceof e?Reflect.construct(t,arguments,this.constructor):t.apply(this,arguments)};n.prototype=t.prototype}else n={};return Object.defineProperty(n,"__esModule",{value:!0}),Object.keys(e).forEach((function(t){var r=Object.getOwnPropertyDescriptor(e,t);Object.defineProperty(n,t,r.get?r:{enumerable:!0,get:function(){return e[t]}})})),n}function l(){if(t)return e;t=1;const r=n,o=__dirname.split(r.sep).includes("xplat");function i(e){if(("Literal"===e.type||"JSXText"===e.type)&&"string"==typeof e.value)return e.value;if("BinaryExpression"===e.type&&"+"===e.operator){const t=i(e.left),n=i(e.right);if(null!=t&&null!=n)return t+n}return null}function a(e){if("Identifier"===e.type)return e.name;if("ThisExpression"===e.type)return"this";if("MemberExpression"===e.type){const t=a(e.object),n=e.computed?i(e.property):a(e.property);if(null!=t&&null!=n)return t+"."+n}else if("TypeCastExpression"===e.type||"AsExpression"===e.type)return a(e.expression);return null}function l(e){return e.callee?a(e.callee):null}function s(e,t,n){const r=e.getSourceCode().getText(),o=function(e,t){if(t.line<1)throw new RangeError("Line number "+t.line+" is before the start of file");const n=/\r\n|\r|\n|\u2028|\u2029/g;let r={index:0};for(let o=1;o=e.length)throw new RangeError("computed offset "+t+" is past the end of file");const n=/\r\n|\r|\n|\u2028|\u2029/g;let r,o={index:0},i=0;do{r=o,o=n.exec(e),++i}while(o&&o.index"string"==typeof e&&e.charCodeAt(0)>=97,p=e=>"string"==typeof e&&e.charCodeAt(0)<=90;function f(e){return null!=e&&"VariableDeclaration"===e.type&&"Program"===e.parent.type&&1===e.declarations.length&&null!=e.declarations[0].init&&d(e.declarations[0].init)}function m(e){return null!=e&&"VariableDeclarator"===e.type&&"VariableDeclaration"===e.parent.type&&"Program"===e.parent.parent.type&&1===e.parent.declarations.length&&d(e.init)}function d(e){return null!=e&&(y(e)||h(e)||x(e))}function g(e){return null!=e&&"CallExpression"===e.type&&e.callee&&"Identifier"===e.callee.type&&"requireNUX"===e.callee.name&&2===e.arguments.length&&"Literal"===e.arguments[0].type}function y(e){return"CallExpression"===e.type&&e.callee&&"Identifier"===e.callee.type&&"require"===e.callee.name&&1===e.arguments.length&&"Literal"===e.arguments[0].type}function h(e){return"CallExpression"===e.type&&"Identifier"===e.callee.type&&"requireDeferred"===e.callee.name&&1===e.arguments.length&&"Literal"===e.arguments[0].type&&"string"==typeof e.arguments[0].value}function x(e){return"CallExpression"===e.type&&"Identifier"===e.callee.type&&"requireDeferredForDisplay"===e.callee.name&&1===e.arguments.length&&"Literal"===e.arguments[0].type&&"string"==typeof e.arguments[0].value}function b(e){return"ImportDeclaration"===e.type&&(null==e.importKind||"value"===e.importKind)}function C(e){return"ImportDeclaration"===e.type&&("type"===e.importKind||"typeof"===e.importKind)}function S(e){return b(e)||C(e)}function v(e){return null!=e&&"CallExpression"===e.type&&e.callee&&"Identifier"===e.callee.type&&"JSResource"===e.callee.name&&1===e.arguments.length&&"Literal"===e.arguments[0].type}function E(e){return null!=e&&"CallExpression"===e.type&&e.callee&&"Identifier"===e.callee.type&&"JSResourceForInteraction"===e.callee.name&&2===e.arguments.length&&"Literal"===e.arguments[0].type}function I(e){return null!=e&&"CallExpression"===e.type&&"Identifier"===e.callee.type&&"ClientJSResource"===e.callee.name&&1===e.arguments.length&&"Literal"===e.arguments[0].type&&"string"==typeof e.arguments[0].value}function D(e){if(null==e||"CallExpression"!==e.type||null==e.callee)return!1;let t;return"Identifier"===e.callee.type&&(t=e.callee),"MemberExpression"===e.callee.type&&"Identifier"===e.callee.object.type&&(t=e.callee.object),null!=t&&"requireCond"===t.name&&e.arguments.length>0}function T(e){return 0===e.indexOf("m#")?e.substring(2):e}function R(e,t,n=1){return"CallExpression"!==e.type||e.arguments.length!==n||null!=t&&"string"==typeof t&&l(e)!==t||"Literal"!==e.arguments[0].type||"string"!=typeof e.arguments[0].value?null:T(e.arguments[0].value)}function w(e,t,n){const r=e.arguments;return D(e)?3!==r.length?null:(null==t||r[0]&&"Literal"===r[0].type&&r[0].value===t)&&(null==n||r[1]&&"Literal"===r[1].type&&r[1].value===n)?r[2]&&"ObjectExpression"===r[2].type?r[2].properties.reduce(((e,t)=>{if("Property"===t.type&&"Literal"===t.value.type){let n;if("Identifier"===t.key.type)n=t.key.name;else{if("Literal"!==t.key.type)return e;n=String(t.key.value)}e[n]="string"==typeof t.value.value?T(t.value.value):null}return e}),{}):r[2]&&"Literal"===r[2].type&&"string"==typeof r[2].value?T(r[2].value):null:null:null}function O(e){return R(e,"JSResource")}function N(e){return R(e,"JSResourceForInteraction",2)}function k(e){return null==e?null:d(e)?"string"!=typeof e.arguments[0].value?null:T(e.arguments[0].value):null}function q(e){return g(e)?2!==e.arguments.length||"string"!=typeof e.arguments[1].value?null:T(e.arguments[1].value):null}function j(e,t){const n=e.getSourceCode().ast.body.find((e=>"VariableDeclaration"===e.type&&1===e.declarations.length&&"VariableDeclarator"===e.declarations[0].type&&null!=e.declarations[0].init&&k(e.declarations[0].init)===t));return n||null}function P(e){let t=e;for(;"TypeCastExpression"===t.type||"AsExpression"===t.type;)t=t.expression;return t}function L(e){if(0===e.specifiers.length)return!1;if("value"!==e.importKind)return!0;return e.specifiers.every((e=>"ImportSpecifier"===e.type&&("type"===e.importKind||"typeof"===e.importKind)))}function F(e,t){const n=e.getSourceCode().ast.body.find((e=>"ImportDeclaration"===e.type&&e.source.value===t&&!L(e)));return n||null}return e={asAnyKindOfRequireCall:function(e){return d(e)?e:null},asAnyKindOfRequireVariableDeclaration:function(e){return f(e)?e:null},asAnyKindOfRequireVariableDeclarator:function(e){return m(e)?e:null},getAnyRequiredModuleName:function(e,t=Object.freeze({})){const n=k(e)||O(e)||N(e)||q(e);return null!=n?n:w(e,t.condType,t.condition)},getBaseNode:function(e){let t=e;for(;"MemberExpression"===t.type;)t=t.object;return t},getBinding:u,getBootloadedModuleNames:function(e){return"ExpressionStatement"===e.type&&e.expression&&"CallExpression"===e.expression.type&&e.expression.callee&&"MemberExpression"===e.expression.callee.type&&e.expression.callee.object&&"Bootloader"===e.expression.callee.object.name&&e.expression.callee.property&&"loadModules"===e.expression.callee.property.name&&e.expression.arguments.length>0&&"ArrayExpression"===e.expression.arguments[0].type&&e.expression.arguments[0].elements.length>0?e.expression.arguments[0].elements.map((e=>"Literal"===e.type&&"string"==typeof e.value?T(e.value):null)).filter(Boolean):null},getCalleeName:l,getConstantStringExpression:i,getCurrentClassName:function(e){const t=e.getAncestors().find((e=>"ClassDeclaration"===e.type));return t&&"ClassDeclaration"===t.type&&null!=t.id?t.id.name:null},getEnglishForNth:function(e){return["first","second","third","fourth","fifth","sixth"][e]},getFullyQualifiedIdentifier:a,getJSResourceModuleName:O,getJSXMemberOrNamespaceRoot:function(e){let t=e;for(;"JSXIdentifier"!==t.type;)if("JSXMemberExpression"===t.type)t=t.object;else{if("JSXNamespacedName"!==t.type)throw new Error("unexpected "+t.type);t=t.namespace}return t},getLocOffset:function(e,t,n){return s(e,t.loc.start,n)},getLocOffsetOfLoc:s,getName:function(e){const t=P(e);return"Identifier"===t.type?t.name:"Literal"===t.type?String(t.value):null},getObjectPropertyName:function(e){if("Property"!==e.type&&"PropertyDefinition"!==e.type&&"MethodDefinition"!==e.type)return null;const t=e.key;return"Identifier"!==t.type||e.computed?function(e){switch(e.type){case"Literal":switch(e.literalType){case"bigint":return e.bigint;case"null":return"null";case"regexp":return`/${e.regex.pattern}/${e.regex.flags}`;default:return String(e.value)}case"TemplateLiteral":if(0===e.expressions.length&&1===e.quasis.length)return e.quasis[0].value.cooked}return null}(t):t.name},getParamComments:function(e,t){return t.params.map((function(t){const n=e.getSourceCode().getCommentsBefore(t);return n[n.length-1]}))},getPropertyName:function(e){return"MemberExpression"!==e.type?null:e.computed?i(e.property):"Identifier"!==e.property.type?null:e.property.name},getPropTokens:function(e,t){const n=e.getSourceCode().getTokens(t),r=[];return n.forEach(((e,t,n)=>{"JSXIdentifier"===e.type&&"Punctuator"===n[t+1].type&&"="===n[t+1].value&&r.push(e)})),r},getRequireCondModules:w,getRequireModuleName:k,getRequireModuleNode:j,getReturnComment:function(e,t){return e.getSourceCode().getCommentsBefore(t.body)[0]},getValueImportNode:F,getValueImportVariables:function(e,t){const n=[];for(const r of e.getSourceCode().ast.body)"ImportDeclaration"!==r.type||r.source.value!==t||L(r)||r.specifiers.filter((e=>{null!=e.importKind&&"value"!==e.importKind||n.push(e.local.name)}));return n},hasValueImport:function(e){return e.getSourceCode().ast.body.some((e=>"ImportDeclaration"===e.type&&!L(e)))},getVariable:function(e,t){let n=t;for(;n;){const t=n.set.get(e);if(t)return t;n=n.upper}return null},insertRequireStatement:function(e,t,n,r=""){if(""===n)throw new Error("Name must be a string with length larger than 0");const o=`const ${n} = require('${n}${r}');`,i=[];e.getSourceCode().ast.body.forEach((e=>{if("VariableDeclaration"===e.type&&1===e.declarations.length&&"VariableDeclarator"===e.declarations[0].type&&e.declarations[0].init){const t=k(e.declarations[0].init);null!=t&&i.push({name:t,node:e})}}));const a=i.find((e=>e.name>=n));if(a){if(a.name.replace(r,"")===n)return[];const e=i[0];if(c(a.name)&&p(n)){if(e!==a){const e=a.node.range[0];return[t.removeRange([e-1,e-1]),t.insertTextBefore(a.node,o+"\n\n")]}return[t.insertTextBefore(a.node,o+"\n\n")]}return[t.insertTextBefore(a.node,o+"\n")]}if(i.length>0){const e=i[i.length-1],r=p(e.name)&&c(n)?"\n":"";return[t.insertTextAfter(e.node,"\n"+o+r)]}{const n=e.getSourceCode().ast.body,r=n[0];return"ExpressionStatement"===r.type&&"Literal"===r.expression.type&&"use strict"===r.expression.value?[t.insertTextBefore(n[1],o+"\n")]:[t.insertTextBefore(n[0],o+"\n")]}},insertValueImportStatement:function(e,t,n,r="",o){if(""===n)throw new Error("Name must be a string with length larger than 0");const i=`import ${o??n} from '${n}${r}';`,a=[];e.getSourceCode().ast.body.forEach((e=>{if(b(e)){const t=function(e){if(d(e))return"string"!=typeof e.arguments[0].value?null:T(e.arguments[0].value);if(S(e))return e.source.value;return null}(e);null!=t&&a.push({name:t,node:e})}}));const l=a.find((e=>e.name>=n));if(l){if(l.name.replace(r,"")===n)return[];const e=a[0];if(c(l.name)&&p(n)){if(e!==l){const e=l.node.range[0];return[t.removeRange([e-1,e-1]),t.insertTextBefore(l.node,i+"\n\n")]}return[t.insertTextBefore(l.node,i+"\n\n")]}return[t.insertTextBefore(l.node,i+"\n")]}if(a.length>0){const e=a[a.length-1],r=p(e.name)&&c(n)?"\n":"";return[t.insertTextAfter(e.node,"\n"+i+r)]}{const n=e.getSourceCode().ast.body,r=n[0];return"ExpressionStatement"===r.type&&"Literal"===r.expression.type&&"use strict"===r.expression.value?[t.insertTextBefore(n[1],i+"\n")]:[t.insertTextBefore(n[0],i+"\n")]}},isAnyKindOfImport:S,isAnyKindOfModuleCall:function(e){return d(e)||D(e)||v(e)||E(e)||I(e)||g(e)},isAnyKindOfRequireCall:d,isAnyKindOfRequireVariableDeclaration:f,isAnyKindOfRequireVariableDeclarator:m,isClientJSResource:I,isFbSourceRepo:o,isGraphQLTemplate:function(e){return"Identifier"===e.tag.type&&"graphql"===e.tag.name&&1===e.quasi.quasis.length},isInsideMethod:function(e,t){return e.getAncestors().some((e=>"MethodDefinition"===e.type&&"Identifier"===e.key.type&&e.key.name===t))},isJSResource:v,isJSResourceForInteraction:E,isModuleRef:function(e){return"Literal"===e.type&&"string"==typeof e.value&&e.value.startsWith("m#")},isNodeInsideFunctionBody:function e(t){return null!=t.parent&&("ArrowFunctionExpression"===t.parent.type||"FunctionDeclaration"===t.parent.type||"FunctionExpression"===t.parent.type||"HookDeclaration"===t.parent.type||"ComponentDeclaration"===t.parent.type||e(t.parent))},isNodeInsideNode:function(e,t){return e.range[0]>=t.range[0]&&e.range[1]<=t.range[1]},isOnlyTypeImport:L,isOnlyTypeExport:function(e){return"type"===e.exportKind},isReferenced:function(e){const t=e.parent;switch(t.type){case"MemberExpression":case"JSXMemberExpression":return t.property===e&&!0===t.computed||t.object===e;case"MetaProperty":case"ImportDefaultSpecifier":case"ImportNamespaceSpecifier":case"ImportSpecifier":case"LabeledStatement":case"RestElement":case"ObjectPattern":case"ArrayPattern":return!1;case"Property":case"MethodDefinition":return t.key===e&&t.computed;case"VariableDeclarator":case"ClassDeclaration":case"ClassExpression":return t.id!==e;case"ArrowFunctionExpression":case"FunctionDeclaration":case"HookDeclaration":case"FunctionExpression":for(let n=0;ne.writeExpr));return null!=o?o.writeExpr:null},resolveModuleSource:function(e,t){const n=u(e.getScope(),t);if(null==n)return null;const r=n.defs[0];switch(r.node.type){case"VariableDeclarator":{const e=r.node.init;if(null!=e)return k(e)||O(e)||N(e)||q(e);break}case"ImportNamespaceSpecifier":case"ImportSpecifier":case"ImportDefaultSpecifier":return r.node.parent.source.value}return null},stripModuleRef:T,uncast:P},e}var s,u,c,p,f,m,d,g,y,h,x=a(o({__proto__:null,isCommaOrSemiToken:function(e){return"Punctuator"===e.type&&(","===e.value||";"===e.value)}},[r(require("hermes-estree"))]));function b(){if(u)return s;u=1;const{isCommaOrSemiToken:e}=x;return s={getInlineComments:function(t,n,r=e){const o=t.ast.comments.filter((e=>e.loc.start.line===n.loc.end.line&&e.range[0]>n.range[1])).sort(((e,t)=>e.range[0]-t.range[0])),i=[];let a=n;for(const e of o){const n=t.getTokensBetween(a,e);if(n.length>0){if(!r)break;if(!n.every(r))break}i.push(e),a=e}return i},getLeadingComments:function(e,t,n){const r=e.getCommentsBefore(t),o=[];let i=t;for(let t=r.length-1;t>=0;t-=1){const a=r[t];if(a===n)break;if(a.loc.end.line===i.loc.start.line){o.unshift(a),i=a;continue}if(a.loc.end.line!==i.loc.start.line-1)break;const l=e.getTokenBefore(a);if(l&&l.loc.end.line===a.loc.start.line)break;o.unshift(a),i=a}return o}}}function C(){if(g)return d;g=1;const e=l(),{getInlineComments:t,getLeadingComments:n}=b(),r=p?c:(p=1,c=function(e){const t=e.getSourceCode().ast;return t.docblock?.comment}),{compareNames:o}=function(){if(m)return f;m=1;const{getInlineComments:e,getLeadingComments:t}=b(),{isCommaOrSemiToken:n}=x;function r(e){return"@"===e[0]||":"===e[0]}const o=/\d{1,3}(\.\d+)?%/;function i(e){switch(typeof e){case"number":return{isSafeNumericString:!0,isPercentage:!1};case"boolean":return{isSafeNumericString:!1,isPercentage:!1}}return isNaN(e)||isNaN(parseFloat(e))?o.test(e)?{isSafeNumericString:!0,isPercentage:!0}:{isSafeNumericString:!1,isPercentage:!1}:{isSafeNumericString:!0,isPercentage:!1}}function a(e){return e.reduce((([e,t],[n,r])=>[Math.min(e,n),Math.max(t,r)]),[Number.MAX_SAFE_INTEGER,0])}return f={compareNames:function(e,t,n=!1){if("number"==typeof e&&"number"==typeof t)return e-t;const o=String(e),a=String(t);if(""===o&&""!==a)return-1;if(""!==o&&""===a)return 1;if(""===o&&""===a)return 0;const{isSafeNumericString:l,isPercentage:s}=i(o),{isSafeNumericString:u,isPercentage:c}=i(a);if(l&&u){const e=Number.parseFloat(o),t=Number.parseFloat(a);if(e===t){if(!s&&c)return-1;if(s&&!c)return 1}return e-t}if(n){const e=r(o),t=r(a);if(!e&&t)return-1;if(e&&!t)return 1}const p=l||o[0].toLowerCase()===o[0].toUpperCase(),f=u||a[0].toLowerCase()===a[0].toUpperCase();if(!p&&f)return 1;if(p&&!f)return-1;if(!p&&!f){const e=o[0].toLowerCase()===o[0],t=a[0].toLowerCase()===a[0];if(!e&&t)return-1;if(e&&!t)return 1}return o.localeCompare(a,"en",{caseFirst:"upper",sensitivity:"base"})},getEncompassingRange:a,getNodeTextWithComments:function(r,o,i,{shouldIncludeNextTokenInRange:l=n,ensureTextFollowsNode:s,inlineCommentIgnoreToken:u}={}){const c=e(r,o,u),p=[...t(r,o,i).map((({range:e})=>e)),o.range,...c.map((({range:e})=>e))],f=r.getTokenAfter(o);f&&!0===l?.(f)&&p.push(f.range);const m=a(p);let d=r.text.slice(m[0],m[1]);const g=o.range[1]-m[0];if(null!=s){const e=r.getTokenAfter(o);e?.value!==s&&(d=d.slice(0,g)+s+d.slice(g))}return{range:m,text:d}},isComma:function(e){return"Punctuator"===e.type&&","===e.value}}}(),i=0,a={default:1,namespace:2,named:3},s={default:4,namespace:5,named:6},u={default:7,namespace:8,named:9},y=0,h=1,C=2,S=3,v=99;return d={meta:{fixable:"code",messages:{incorrectOrder:"Requires should be sorted alphabetically, with at least one line between imports/requires and code"}},create(l){const c=l.getSourceCode(),p=function(e){const t=r(e);if(null!=t)return t;const n=e.getSourceCode().ast,o=n.body.length>0?n.body[0]:n,i=e.getSourceCode().getCommentsBefore(o)[0];return i&&"Block"===i.type?i:null}(l);if(p&&(p.value.includes("* @generated")||p.value.includes("* @partially-generated")))return{};const f=Object.freeze({typeImport:{priority:10,uppercase:[],lowercase:[],tiebreakFunction:b},valueImport:{priority:20,uppercase:[],lowercase:[],tiebreakFunction:b},requiresUsedByOtherRequires:{priority:30,uppercase:[],lowercase:[],tiebreakFunction:E},require:{priority:40,uppercase:[],lowercase:[],tiebreakFunction:E}}),m=[];let d=null,g=null;const x=new Set;return{Program(e){for(const t of e.body)switch(t.type){case"ImportDeclaration":if("type"===t.importKind||"typeof"===t.importKind)w(f.typeImport,t,t.source.value,!0);else{const e=c.getLastToken(t.source,(e=>"from"===e.value));0===t.specifiers.length&&null==e?O(t):w(f.valueImport,t,t.source.value)}break;case"VariableDeclaration":{const e=t.declarations[0]?.init;if(1!==t.declarations.length||null==e){O(t);break}T(e,t);break}default:O(t)}const t=[];for(const e of Object.keys(f)){const n=f[e];t.push({priority:n.priority,nodes:n.uppercase.sort(((e,t)=>I(e,t,n.tiebreakFunction)))}),t.push({priority:n.priority+5,nodes:n.lowercase.sort(((e,t)=>I(e,t,n.tiebreakFunction)))})}function n(e){return[e.leadingComments.length?e.leadingComments.map((e=>c.getText(e))).join("\n")+"\n":"",e.inlineComments.length?" "+e.inlineComments.map((e=>c.getText(e))).join(" "):""]}let r=t.filter((e=>0!==e.nodes.length)).sort(((e,t)=>e.priority-t.priority)).map((e=>e.nodes.map((e=>{const[t,r]=n(e),i=function(e,t){const n=t.node,r=e.getText(n),i=(()=>{if("ImportDeclaration"===n.type&&null!=n.specifiers.find((e=>"ImportSpecifier"===e.type))){const t=e.getFirstToken(n,(e=>"Punctuator"===e.type&&"{"===e.value)),r=e.getFirstToken(n,(e=>"Punctuator"===e.type&&"}"===e.value));return null==t||null==r?null:e.getText().substring(t.range[0],r.range[1])}if("VariableDeclaration"===n.type&&"ObjectPattern"===n.declarations[0].id.type){const t=n.declarations[0].id.typeAnnotation,r=e.getText(n.declarations[0].id);return t?r.substr(0,t.range[0]-n.declarations[0].id.range[0]):r}return null})();if(null==i)return r;let a=[],l=null;"ImportDeclaration"===n.type?a=n.specifiers.map((t=>"ImportDefaultSpecifier"===t.type||"ImportNamespaceSpecifier"===t.type?null:{leadingComments:e.getCommentsBefore(t),name:t.imported.name,node:t})).filter(Boolean):"VariableDeclaration"===n.type&&"ObjectPattern"===n.declarations[0].id.type&&(a=n.declarations[0].id.properties.map((t=>{if("ExperimentalRestProperty"===t.type||"RestElement"===t.type)return l=t,null;const n=t.key,r="Literal"===n.type?String(n.value):"Identifier"===n.type?t.computed?null:n.name:null;return null==r?null:{leadingComments:e.getCommentsBefore(t),name:r,node:t}})).filter(Boolean));if(a.length<=1)return r;const s=i.indexOf("\n")>=0,u=a.sort(((e,t)=>o(e.name,t.name))).map((e=>e.node));null!=l&&u.push(l);const c=u.map((t=>{const n=e.getCommentsBefore(t).map((t=>e.getText(t))),r=n.length?n.join(""):"";return s?(r?" "+r+"\n":"")+" "+e.getText(t):(r?r+" ":"")+e.getText(t)})),p=(()=>{const t=[];if("ImportDeclaration"===n.type){if(t.push(...e.getCommentsBefore(n.source)),s&&null!=n.specifiers.find((e=>"ImportSpecifier"===e.type))){const r=e.getTokenBefore(n.source,(e=>"Punctuator"===e.type&&"}"===e.value));null!=r&&t.push(...e.getCommentsBefore(r))}}else if("VariableDeclaration"===n.type&&"ObjectPattern"===n.declarations[0].id.type){const r=e.getLastToken(n.declarations[0].id);null!=r&&t.push(...e.getCommentsBefore(r))}return t})(),f=s&&p.length?p.map((t=>" "+e.getText(t))).join("\n")+"\n":"",m=s?"\n"+c.map((e=>e.includes("...")?`${e}\n`:`${e},\n`)).join("")+f:c.join(", ");return r.replace(i,(()=>`{${m}}`))}(c,e);return t+i+r})).join("\n"))).join("\n\n");const i=d;if(null==i||null==g)return;const a=i.leadingComments.length?i.leadingComments[0].range[0]:i.node.range[0],s=g.inlineComments.length>0?g.inlineComments[g.inlineComments.length-1].range[1]:g.node.range[1],u=c.getText(i.node,i.node.range[0]-a,s-i.node.range[1]),p=g?.node;if(null!=p){const t=e.body.indexOf(p),n=e.body[t+1];null!=n&&n.loc.start.line-p.loc.end.line===1&&(r+="\n")}u!==r&&l.report({node:i.node,messageId:"incorrectOrder",fix(e){const t=m.filter((({node:e})=>e.range[0]>=a&&e.range[1]<=s)).map((e=>{const[t,r]=n(e),o=c.getText(e.node);return{range:e.node.range,text:t+o+r}})).flat().concat(c.getAllComments().filter((e=>e.range[0]>=a&&e.range[1]<=s&&!x.has(e))).map((e=>({range:e.range,text:c.getText(e)})))).sort(((e,t)=>e.range[0]-t.range[0]||e.range[1]-t.range[1])).map((({text:e})=>e)).join("\n");return e.replaceTextRange([a,s],[r,t].filter(Boolean).join("\n\n"))}})}};function b(e){if(0===e.specifiers.length)return i;const t=(()=>{switch(e.importKind){default:case"value":return a;case"type":return s;case"typeof":return u}})();return e.specifiers.find((e=>"ImportDefaultSpecifier"===e.type))?t.default:e.specifiers.find((e=>"ImportNamespaceSpecifier"===e.type))?t.namespace:t.named}function E(e){if("ExpressionStatement"===e.type)return y;switch(e.declarations[0].id.type){case"Identifier":return h;case"ObjectPattern":return C;case"ArrayPattern":return S}return v}function I(e,t,n){const r=o(e.moduleName,t.moduleName);if(0!==r)return r;const i=n(e.node)-n(t.node);return 0!==i?i:e.node.loc.start.line-t.node.loc.start.line}function D(e,t){if(null==e)throw new Error("Missing required module name");return null!=t?`${e}_${t}`:e}function T(t,n,r){const o=e.getRequireModuleName(t);if(e.isRequire(t)){const e=D(o,r);w("requireCond"===e||"requireDeferred"===e||"requireDeferredForDisplay"===e?f.requiresUsedByOtherRequires:f.require,n,D(o,r))}else if(e.isRequireDeferred(t)||e.isRequireDeferredForDisplay(t))w(f.require,n,D(o,r));else{if(e.isRequireCond(t)){if("VariableDeclaration"===n.type){const e=c.getText(n.declarations[0].id);return void w(f.require,n,D(e,r))}}else{if("MemberExpression"===t.type)return void T(t.object,n,D(c.getText(t.property),r));if("CallExpression"===t.type)return void T(t.callee,n,r)}O(n)}}function R(e){e.leadingComments.forEach((e=>x.add(e))),e.inlineComments.forEach((e=>x.add(e)));c.getCommentsInside(e.node).forEach((e=>x.add(e)))}function w(e,r,o,i=!1){const a={inlineComments:t(c,r),leadingComments:n(c,r,p),moduleName:o,node:r};if(i)e.uppercase.push(a);else{const t=o[0]||"";t.toLowerCase()===t?e.lowercase.push(a):e.uppercase.push(a)}null==d&&(d=a),g=a,R(a)}function O(e){const r={inlineComments:t(c,e),leadingComments:n(c,e,p),node:e};m.push(r),R(r)}}},d}var S=i(function(){if(h)return y;h=1;const e=C();return y=e}());module.exports=S; diff --git a/packages/react-relay/ReactRelayContainerUtils.js b/packages/react-relay/ReactRelayContainerUtils.js index 91513a72b9b8c..9d171a4e3c76e 100644 --- a/packages/react-relay/ReactRelayContainerUtils.js +++ b/packages/react-relay/ReactRelayContainerUtils.js @@ -11,11 +11,11 @@ 'use strict'; -function getComponentName(component: React.ComponentType): string { +function getComponentName(component: component(...empty)): string { return component.displayName || component.name || 'Component'; } -function getContainerName(Component: React.ComponentType): string { +function getContainerName(Component: component(...empty)): string { return 'Relay(' + getComponentName(Component) + ')'; } diff --git a/packages/react-relay/ReactRelayFragmentContainer.js b/packages/react-relay/ReactRelayFragmentContainer.js index 79db5b936658f..104df419cb2e8 100644 --- a/packages/react-relay/ReactRelayFragmentContainer.js +++ b/packages/react-relay/ReactRelayFragmentContainer.js @@ -29,7 +29,7 @@ const { isScalarAndEqual, } = require('relay-runtime'); -type ContainerProps = $FlowFixMeProps; +type ContainerProps = $FlowFixMe; type ContainerState = { data: {[key: string]: mixed, ...}, prevProps: ContainerProps, @@ -46,13 +46,11 @@ type ContainerState = { */ function createContainerWithFragments< Props: {...}, - TComponent: React.ComponentType, + TComponent: component(...Props), >( Component: TComponent, fragments: FragmentMap, -): React.ComponentType< - $RelayProps, RelayProp>, -> { +): component(...$RelayProps, RelayProp>) { const containerName = getContainerName(Component); return class extends React.Component { @@ -269,12 +267,12 @@ function getRelayProp(environment: IEnvironment) { */ function createContainer< Props: {...}, - Instance, - TComponent: component(ref: React.RefSetter, ...Props), + Ref, + TComponent: component(ref: Ref, ...Props), >( Component: TComponent, fragmentSpec: GeneratedNodeMap, -): component(ref: React.RefSetter, ...$RelayProps) { +): component(ref: Ref, ...$RelayProps) { // $FlowFixMe[incompatible-return] return buildReactRelayContainer( Component, diff --git a/packages/react-relay/ReactRelayLoggingContext.js b/packages/react-relay/ReactRelayLoggingContext.js new file mode 100644 index 0000000000000..5a2a57b4043f3 --- /dev/null +++ b/packages/react-relay/ReactRelayLoggingContext.js @@ -0,0 +1,21 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + * @oncall relay + */ + +'use strict'; + +const React = require('react'); +const { + __internal: {createRelayLoggingContext}, +} = require('relay-runtime'); + +module.exports = (createRelayLoggingContext(React): React.Context< + mixed | null, +>); diff --git a/packages/react-relay/ReactRelayPaginationContainer.js b/packages/react-relay/ReactRelayPaginationContainer.js index 8ca7e3215deef..a82698c61f7ae 100644 --- a/packages/react-relay/ReactRelayPaginationContainer.js +++ b/packages/react-relay/ReactRelayPaginationContainer.js @@ -319,14 +319,14 @@ function toObserver(observerOrCallback: ?ObserverOrCallback): Observer { function createContainerWithFragments< Props: {...}, - TComponent: React.ComponentType, + TComponent: component(...Props), >( Component: TComponent, fragments: FragmentMap, connectionConfig: ConnectionConfig, -): React.ComponentType< - $RelayProps, RelayPaginationProp>, -> { +): component( + ...$RelayProps, RelayPaginationProp> +) { const componentName = getComponentName(Component); const containerName = getContainerName(Component); @@ -347,7 +347,7 @@ function createContainerWithFragments< connectionConfig.getFragmentVariables || createGetFragmentVariables(metadata); - return class extends React.Component<$FlowFixMeProps, ContainerState> { + return class extends React.Component<$FlowFixMe, ContainerState> { // $FlowFixMe[missing-local-annot] static displayName = containerName; @@ -805,7 +805,7 @@ function createContainerWithFragments< ...fragmentVariables, }: Variables); - const cacheConfig: ?CacheConfig = options + const cacheConfig: ?{...CacheConfig} = options ? {force: !!options.force} : undefined; if (cacheConfig != null && options?.metadata != null) { @@ -937,13 +937,13 @@ function createContainerWithFragments< * `fragmentSpec` is memoized once per environment, rather than once per * instance of the container constructed/rendered. */ -function createContainer>( +function createContainer( Component: TComponent, fragmentSpec: GeneratedNodeMap, connectionConfig: ConnectionConfig, -): React.ComponentType< - $RelayProps, RelayPaginationProp>, -> { +): component( + ...$RelayProps, RelayPaginationProp> +) { // $FlowFixMe[incompatible-return] return buildReactRelayContainer( Component, diff --git a/packages/react-relay/ReactRelayRefetchContainer.js b/packages/react-relay/ReactRelayRefetchContainer.js index 4783020290fa7..9edc8a7714b51 100644 --- a/packages/react-relay/ReactRelayRefetchContainer.js +++ b/packages/react-relay/ReactRelayRefetchContainer.js @@ -49,7 +49,7 @@ const { } = require('relay-runtime'); const warning = require('warning'); -type ContainerProps = $FlowFixMeProps; +type ContainerProps = $FlowFixMe; type ContainerState = { data: {[key: string]: mixed, ...}, @@ -69,14 +69,14 @@ type ContainerState = { */ function createContainerWithFragments< Props: {...}, - TComponent: React.ComponentType, + TComponent: component(...Props), >( Component: TComponent, fragments: FragmentMap, taggedNode: GraphQLTaggedNode, -): React.ComponentType< - $RelayProps, RelayRefetchProp>, -> { +): component( + ...$RelayProps, RelayRefetchProp> +) { const containerName = getContainerName(Component); return class extends React.Component { @@ -339,7 +339,7 @@ function createContainerWithFragments< ? {...fetchVariables, ...renderVariables} : fetchVariables; - const cacheConfig: ?CacheConfig = options + const cacheConfig: ?{...CacheConfig} = options ? {force: !!options.force} : undefined; if (cacheConfig != null && options?.metadata != null) { @@ -486,13 +486,13 @@ function getRelayProp( * `fragmentSpec` is memoized once per environment, rather than once per * instance of the container constructed/rendered. */ -function createContainer>( +function createContainer( Component: TComponent, fragmentSpec: GeneratedNodeMap, taggedNode: GraphQLTaggedNode, -): React.ComponentType< - $RelayProps, RelayRefetchProp>, -> { +): component( + ...$RelayProps, RelayRefetchProp> +) { // $FlowFixMe[incompatible-return] return buildReactRelayContainer( Component, diff --git a/packages/react-relay/ReactRelayTypes.js b/packages/react-relay/ReactRelayTypes.js index 615a5a68262ff..81186a899356f 100644 --- a/packages/react-relay/ReactRelayTypes.js +++ b/packages/react-relay/ReactRelayTypes.js @@ -97,14 +97,20 @@ export type $FragmentRef = { ... }; +/* $FlowExpectedError[unclear-type]: Intentional so that it won't fail, + * even if the type we want to exclude doesn't exist in Props */ +type LooseOmitRelayProps> = Pick< + Props, + Exclude<$Keys, K>, +>; /** * A utility type that takes the Props of a component and the type of * `props.relay` and returns the props of the container. */ // prettier-ignore // $FlowFixMe[extra-type-arg] xplat redux flow type error -export type $RelayProps = MapRelayProps< - $Diff, +export type $RelayProps = MapRelayProps< + LooseOmitRelayProps, >; type MapRelayProps = {[K in keyof Props]: MapRelayProp}; @@ -144,14 +150,15 @@ type MapRelayProp = [+t: T] extends [+t: {+$fragmentType: empty, ...}] ? ?$ReadOnlyArray>> : T; -export type RelayFragmentContainer = React.ComponentType< - $RelayProps, RelayProp>, ->; +export type RelayFragmentContainer = component( + ...$RelayProps, RelayProp> +); -export type RelayPaginationContainer = React.ComponentType< - $RelayProps, RelayPaginationProp>, ->; +export type RelayPaginationContainer = + component( + ...$RelayProps, RelayPaginationProp> + ); -export type RelayRefetchContainer = React.ComponentType< - $RelayProps, RelayRefetchProp>, ->; +export type RelayRefetchContainer = component( + ...$RelayProps, RelayRefetchProp> +); diff --git a/packages/react-relay/__flowtests__/ReactRelayFragmentContainer-flowtest.js b/packages/react-relay/__flowtests__/ReactRelayFragmentContainer-flowtest.js index 167107dc1eac3..049098154aa56 100644 --- a/packages/react-relay/__flowtests__/ReactRelayFragmentContainer-flowtest.js +++ b/packages/react-relay/__flowtests__/ReactRelayFragmentContainer-flowtest.js @@ -138,14 +138,7 @@ module.exports = { return bad ? 'not good' : ok; } render(): React.MixedElement { - return ( - { - this._barRef = (ref: empty); - }} - requiredProp="bar" - /> - ); + return ; } } return ; diff --git a/packages/react-relay/__flowtests__/ReactRelayPaginationContainer-flowtest.js b/packages/react-relay/__flowtests__/ReactRelayPaginationContainer-flowtest.js index 299c4e9e428ef..93f31f7958aeb 100644 --- a/packages/react-relay/__flowtests__/ReactRelayPaginationContainer-flowtest.js +++ b/packages/react-relay/__flowtests__/ReactRelayPaginationContainer-flowtest.js @@ -22,10 +22,7 @@ const {graphql} = require('relay-runtime'); * type-checked correctly on Relay components. */ -/* $FlowFixMe(>=0.53.0) This comment suppresses an error - * when upgrading Flow's support for React. Common errors found when upgrading - * Flow's React support are documented at https://fburl.com/eq7bs81w */ -class FooComponent extends React.Component { +class FooComponent extends React.Component<$FlowFixMe> { props: { optionalProp?: {foo: number, ...}, defaultProp: string, @@ -56,7 +53,7 @@ class FooComponent extends React.Component { } // Note that we must reassign to a new identifier to make sure flow doesn't propogate types without // the relay type definition doing the work. -const Foo = createPaginationContainer( +const Foo = createPaginationContainer<$FlowFixMe, _>( FooComponent, { viewer: graphql` diff --git a/packages/react-relay/__flowtests__/ReactRelayRefetchContainer-flowtest.js b/packages/react-relay/__flowtests__/ReactRelayRefetchContainer-flowtest.js index b39c0e40c1964..1a03fbcf862b3 100644 --- a/packages/react-relay/__flowtests__/ReactRelayRefetchContainer-flowtest.js +++ b/packages/react-relay/__flowtests__/ReactRelayRefetchContainer-flowtest.js @@ -21,10 +21,7 @@ const {graphql} = require('relay-runtime'); * Verifies that normal prop type checking works correctly on Relay components. */ -/* $FlowFixMe(>=0.53.0) This comment suppresses an error - * when upgrading Flow's support for React. Common errors found when upgrading - * Flow's React support are documented at https://fburl.com/eq7bs81w */ -class FooComponent extends React.Component { +class FooComponent extends React.Component<$FlowFixMe> { props: { optionalProp?: {foo: number, ...}, defaultProp: string, @@ -55,7 +52,7 @@ class FooComponent extends React.Component { } // Note that we must reassign to a new identifier to make sure flow doesn't propogate types without // the relay type definition doing the work. -const Foo = createRefetchContainer( +const Foo = createRefetchContainer<$FlowFixMe, _>( FooComponent, { viewer: graphql` diff --git a/packages/react-relay/__tests__/ClientEdges-test.js b/packages/react-relay/__tests__/ClientEdges-test.js index 25a389e3a8c07..5dbedc990d748 100644 --- a/packages/react-relay/__tests__/ClientEdges-test.js +++ b/packages/react-relay/__tests__/ClientEdges-test.js @@ -11,10 +11,23 @@ 'use strict'; +import type {ClientEdgesTestUpperName$key} from './__generated__/ClientEdgesTestUpperName.graphql'; + const React = require('react'); -const {RelayEnvironmentProvider, useLazyLoadQuery} = require('react-relay'); +const { + RelayEnvironmentProvider, + useFragment, + useLazyLoadQuery, +} = require('react-relay'); const TestRenderer = require('react-test-renderer'); -const {Environment, Network, RecordSource, graphql} = require('relay-runtime'); +const { + Environment, + Network, + RecordSource, + RelayFeatureFlags, + graphql, + readFragment, +} = require('relay-runtime'); const RelayObservable = require('relay-runtime/network/RelayObservable'); const RelayModernStore = require('relay-runtime/store/RelayModernStore'); const { @@ -27,269 +40,463 @@ injectPromisePolyfill__DEPRECATED(); disallowWarnings(); disallowConsoleErrors(); -let networkSink; -let environment; -let fetchFn; -beforeEach(() => { - fetchFn = jest.fn(() => - // $FlowFixMe[missing-local-annot] Error found while enabling LTI on this file - RelayObservable.create(sink => { - networkSink = sink; - }), +/** + * @RelayResolver User.same_user_client_edge: User + */ +export function same_user_client_edge(): {id: string} { + return {id: '1'}; +} + +/** + * @RelayResolver User.upper_name: String + * @rootFragment ClientEdgesTestUpperName + */ + +export function upper_name(key: ClientEdgesTestUpperName$key): ?string { + const user = readFragment( + graphql` + fragment ClientEdgesTestUpperName on User { + name + } + `, + key, ); + return user?.name?.toUpperCase(); +} - environment = new Environment({ - store: new RelayModernStore( - new RecordSource({ - 'client:root': { - __id: 'client:root', - __typename: '__Root', - me: {__ref: '1'}, - }, - '1': { - __id: '1', - id: '1', - __typename: 'User', - }, - }), - ), - // $FlowFixMe[invalid-tuple-arity] Error found while enabling LTI on this file - network: Network.create(fetchFn), - }); -}); - -it('should fetch and render client-edge query', () => { - function TestComponent() { - return ( - - - - - - ); - } - - const variables = {id: '4'}; - function InnerComponent() { - const data = useLazyLoadQuery( - graphql` - query ClientEdgesTest1Query($id: ID!) { - me { - client_node(id: $id) @waterfall { - ... on User { - name +describe.each([[true], [false]])( + 'RelayFeatureFlags.ENABLE_ACTIVITY_COMPATIBILITY = %s', + (activityEnabled: boolean) => { + RelayFeatureFlags.ENABLE_ACTIVITY_COMPATIBILITY = activityEnabled; + + let networkSink; + let environment; + let fetchFn; + beforeEach(() => { + RelayFeatureFlags.CHECK_ALL_FRAGMENTS_FOR_MISSING_CLIENT_EDGES = false; + fetchFn = jest.fn(() => + // $FlowFixMe[missing-local-annot] Error found while enabling LTI on this file + RelayObservable.create(sink => { + networkSink = sink; + }), + ); + + environment = new Environment({ + store: new RelayModernStore( + new RecordSource({ + 'client:root': { + __id: 'client:root', + __typename: '__Root', + me: {__ref: '1'}, + }, + '1': { + __id: '1', + id: '1', + __typename: 'User', + }, + }), + ), + // $FlowFixMe[invalid-tuple-arity] Error found while enabling LTI on this file + network: Network.create(fetchFn), + }); + }); + + it('should fetch and render client-edge query', () => { + function TestComponent() { + return ( + + + + + + ); + } + + const variables = {id: '4'}; + function InnerComponent() { + const data = useLazyLoadQuery( + graphql` + query ClientEdgesTest1Query($id: ID!) { + me { + client_node(id: $id) @waterfall { + ... on User { + name + } + } } } - } - } - `, - variables, - ); - return data.me?.client_node?.name; - } - - let renderer; - TestRenderer.act(() => { - renderer = TestRenderer.create(); - }); - expect(fetchFn.mock.calls.length).toEqual(1); - // We should send the client-edge query - // $FlowFixMe[invalid-tuple-index] Error found while enabling LTI on this file - expect(fetchFn.mock.calls[0][0].name).toBe( - 'ClientEdgeQuery_ClientEdgesTest1Query_me__client_node', - ); - // Check variables - // $FlowFixMe[invalid-tuple-index] Error found while enabling LTI on this file - expect(fetchFn.mock.calls[0][1]).toEqual(variables); - expect(renderer?.toJSON()).toBe('Loading'); - - TestRenderer.act(() => { - // This should resolve client-edge query - networkSink.next({ - data: { - node: { - id: '4', - __typename: 'User', - name: 'Alice', - }, - }, + `, + variables, + ); + return data.me?.client_node?.name; + } + + let renderer; + TestRenderer.act(() => { + renderer = TestRenderer.create(); + }); + expect(fetchFn.mock.calls.length).toEqual(1); + // We should send the client-edge query + // $FlowFixMe[invalid-tuple-index] Error found while enabling LTI on this file + expect(fetchFn.mock.calls[0][0].name).toBe( + 'ClientEdgeQuery_ClientEdgesTest1Query_me__client_node', + ); + // Check variables + // $FlowFixMe[invalid-tuple-index] Error found while enabling LTI on this file + expect(fetchFn.mock.calls[0][1]).toEqual(variables); + expect(renderer?.toJSON()).toBe('Loading'); + + TestRenderer.act(() => { + // This should resolve client-edge query + networkSink.next({ + data: { + node: { + id: '4', + __typename: 'User', + name: 'Alice', + }, + }, + }); + jest.runAllImmediates(); + }); + expect(renderer?.toJSON()).toBe('Alice'); }); - jest.runAllImmediates(); - }); - expect(renderer?.toJSON()).toBe('Alice'); -}); - -// The Relay store does not have a concept of _records_ being null. This means that when a Node -// query returns null, we can't actally write to the store "The record with this ID is null". -// Instead, we just write that `node(id: 4): null` into the root record in the store. -// -// This is a general limitiaton of node fetches in Relay today. -it('should fetch and render `undefined` for client-edge to server query that returns `null`.', () => { - function TestComponent() { - return ( - - - - - - ); - } - - const variables = {id: '4'}; - function InnerComponent() { - const data = useLazyLoadQuery( - graphql` - query ClientEdgesTest2Query($id: ID!) { - me { - client_node(id: $id) @waterfall { - ... on User { - name + + // The Relay store does not have a concept of _records_ being null. This means that when a Node + // query returns null, we can't actally write to the store "The record with this ID is null". + // Instead, we just write that `node(id: 4): null` into the root record in the store. + // + // This is a general limitiaton of node fetches in Relay today. + it('should fetch and render `undefined` for client-edge to server query that returns `null`.', () => { + function TestComponent() { + return ( + + + + + + ); + } + + const variables = {id: '4'}; + function InnerComponent() { + const data = useLazyLoadQuery( + graphql` + query ClientEdgesTest2Query($id: ID!) { + me { + client_node(id: $id) @waterfall { + ... on User { + name + } + } } } - } + `, + variables, + ); + if (data.me?.client_node === undefined) { + return 'client_node is undefined'; } - `, - variables, - ); - if (data.me?.client_node === undefined) { - return 'client_node is undefined'; - } - return data.me?.client_node?.name ?? 'MISSING'; - } - - let renderer; - TestRenderer.act(() => { - renderer = TestRenderer.create(); - }); - expect(fetchFn.mock.calls.length).toEqual(1); - // We should send the client-edge query - // $FlowFixMe[invalid-tuple-index] Error found while enabling LTI on this file - expect(fetchFn.mock.calls[0][0].name).toBe( - 'ClientEdgeQuery_ClientEdgesTest2Query_me__client_node', - ); - // Check variables - // $FlowFixMe[invalid-tuple-index] Error found while enabling LTI on this file - expect(fetchFn.mock.calls[0][1]).toEqual(variables); - expect(renderer?.toJSON()).toBe('Loading'); - - TestRenderer.act(() => { - // This should resolve client-edge query - networkSink.next({ - data: { - node: null, - }, + return data.me?.client_node?.name ?? 'MISSING'; + } + + let renderer; + TestRenderer.act(() => { + renderer = TestRenderer.create(); + }); + expect(fetchFn.mock.calls.length).toEqual(1); + // We should send the client-edge query + // $FlowFixMe[invalid-tuple-index] Error found while enabling LTI on this file + expect(fetchFn.mock.calls[0][0].name).toBe( + 'ClientEdgeQuery_ClientEdgesTest2Query_me__client_node', + ); + // Check variables + // $FlowFixMe[invalid-tuple-index] Error found while enabling LTI on this file + expect(fetchFn.mock.calls[0][1]).toEqual(variables); + expect(renderer?.toJSON()).toBe('Loading'); + + TestRenderer.act(() => { + // This should resolve client-edge query + networkSink.next({ + data: { + node: null, + }, + }); + // It is important to complete network request here, + // otherwise, client-edge query will think that the query is still in progress + // and will show a suspense placeholder + networkSink.complete(); + jest.runAllImmediates(); + }); + expect(renderer?.toJSON()).toBe('client_node is undefined'); + expect(fetchFn.mock.calls.length).toBe(1); }); - // It is important to complete network request here, - // otherwise, client-edge query will think that the query is still in progress - // and will show a suspense placeholder - networkSink.complete(); - jest.runAllImmediates(); - }); - expect(renderer?.toJSON()).toBe('client_node is undefined'); - expect(fetchFn.mock.calls.length).toBe(1); -}); - -it('should throw for missing client-edge field data marked with @required', () => { - function TestComponent() { - return ( - - - - - - ); - } - - const variables = {id: '4'}; - function InnerComponent() { - const data = useLazyLoadQuery( - graphql` - query ClientEdgesTest3Query($id: ID!) { - me { - client_node(id: $id) @waterfall @required(action: THROW) { - ... on User { - name + + it('should throw for missing client-edge field data marked with @required', () => { + function TestComponent() { + return ( + + + + + + ); + } + + const variables = {id: '4'}; + function InnerComponent() { + const data = useLazyLoadQuery( + graphql` + query ClientEdgesTest3Query($id: ID!) { + me { + client_node(id: $id) @waterfall @required(action: THROW) { + ... on User { + name + } + } } } - } - } - `, - variables, - ); - return data.me?.client_node?.name; - } - - let renderer; - TestRenderer.act(() => { - renderer = TestRenderer.create(); - }); - expect(fetchFn.mock.calls.length).toEqual(1); - // We should send the client-edge query - // $FlowFixMe[invalid-tuple-index] Error found while enabling LTI on this file - expect(fetchFn.mock.calls[0][0].name).toBe( - 'ClientEdgeQuery_ClientEdgesTest3Query_me__client_node', - ); - // Check variables - // $FlowFixMe[invalid-tuple-index] Error found while enabling LTI on this file - expect(fetchFn.mock.calls[0][1]).toEqual(variables); - expect(renderer?.toJSON()).toBe('Loading'); - - TestRenderer.act(() => { - networkSink.next({ - data: { - node: null, - }, - }); - jest.runAllImmediates(); - }); - // Still waiting, maybe the data will be there - expect(renderer?.toJSON()).toBe('Loading'); - - expect(() => { - TestRenderer.act(() => { - // This should resolve client-edge query - networkSink.complete(); - jest.runAllImmediates(); + `, + variables, + ); + return data.me?.client_node?.name; + } + + let renderer; + TestRenderer.act(() => { + renderer = TestRenderer.create(); + }); + expect(fetchFn.mock.calls.length).toEqual(1); + // We should send the client-edge query + // $FlowFixMe[invalid-tuple-index] Error found while enabling LTI on this file + expect(fetchFn.mock.calls[0][0].name).toBe( + 'ClientEdgeQuery_ClientEdgesTest3Query_me__client_node', + ); + // Check variables + // $FlowFixMe[invalid-tuple-index] Error found while enabling LTI on this file + expect(fetchFn.mock.calls[0][1]).toEqual(variables); + expect(renderer?.toJSON()).toBe('Loading'); + + TestRenderer.act(() => { + networkSink.next({ + data: { + node: null, + }, + }); + jest.runAllImmediates(); + }); + // Still waiting, maybe the data will be there + expect(renderer?.toJSON()).toBe('Loading'); + + expect(() => { + TestRenderer.act(() => { + // This should resolve client-edge query + networkSink.complete(); + jest.runAllImmediates(); + }); + }).toThrow( + "Relay: Missing @required value at path 'me.client_node' in 'ClientEdgesTest3Query'.", + ); + expect(renderer?.toJSON()).toBe(null); }); - }).toThrow( - "Relay: Missing @required value at path 'me.client_node' in 'ClientEdgesTest3Query'.", - ); - expect(renderer?.toJSON()).toBe(null); -}); - -it('should throw for missing client-edge (client object) field data marked with @required', () => { - function TestComponent() { - return ( - - - - - - ); - } - const variables = {return_null: true}; - function InnerComponent() { - const data = useLazyLoadQuery( - graphql` - query ClientEdgesTest4Query($return_null: Boolean!) { - me { - client_object(return_null: $return_null) @required(action: THROW) { - description + + it('should throw for missing client-edge (client object) field data marked with @required', () => { + function TestComponent() { + return ( + + + + + + ); + } + const variables = {return_null: true}; + function InnerComponent() { + const data = useLazyLoadQuery( + graphql` + query ClientEdgesTest4Query($return_null: Boolean!) { + me { + client_object(return_null: $return_null) + @required(action: THROW) { + description + } + } } - } + `, + variables, + ); + + return data.me?.client_object?.description; + } + expect(() => { + TestRenderer.act(() => { + TestRenderer.create(); + }); + }).toThrow( + "Relay: Missing @required value at path 'me.client_object' in 'ClientEdgesTest4Query'.", + ); + expect(fetchFn.mock.calls.length).toEqual(0); + }); + + // https://github.com/facebook/relay/issues/4882 + it.each([[true], [false]])( + 'should fetch data missing in fragment spread within `@waterfall` field. CHECK_ALL_FRAGMENTS_FOR_MISSING_CLIENT_EDGES = %s', + checkAllFragments => { + RelayFeatureFlags.CHECK_ALL_FRAGMENTS_FOR_MISSING_CLIENT_EDGES = + checkAllFragments; + + function TestComponent() { + return ( + + + + + + ); } - `, - variables, + + const variables = {}; + function InnerComponent() { + const data = useLazyLoadQuery( + graphql` + query ClientEdgesTest5Query { + me { + same_user_client_edge @waterfall { + # No fields here means that we render without detecting any + # missing data here and don't attempt to fetch the @waterfall + # query. + # + # The same bug can be tirggered by adding a field that is already + # in the store for an unrelated reason. + ...ClientEdgesTest5Query_user + # Adding "name" here will cause the query to be fetched. + } + } + } + `, + variables, + ); + + const user = useFragment( + graphql` + fragment ClientEdgesTest5Query_user on User { + name + } + `, + data.me?.same_user_client_edge, + ); + + return user?.name; + } + + // This will be updated when we add the new assertions as part of a fix for + // this bug. + // eslint-disable-next-line no-unused-vars + let renderer; + TestRenderer.act(() => { + renderer = TestRenderer.create(); + }); + + if (!RelayFeatureFlags.CHECK_ALL_FRAGMENTS_FOR_MISSING_CLIENT_EDGES) { + // Oops, we didn't fetch the query! + expect(fetchFn.mock.calls.length).toEqual(0); + } else { + expect(fetchFn.mock.calls.length).toEqual(1); + // We should send the client-edge query + // $FlowFixMe[invalid-tuple-index] Error found while enabling LTI on this file + expect(fetchFn.mock.calls[0][0].name).toBe( + 'ClientEdgeQuery_ClientEdgesTest5Query_me__same_user_client_edge', + ); + // Check variables + // $FlowFixMe[invalid-tuple-index] Error found while enabling LTI on this file + expect(fetchFn.mock.calls[0][1]).toEqual({id: '1'}); + expect(renderer?.toJSON()).toBe('Loading'); + TestRenderer.act(() => { + // This should resolve client-edge query + networkSink.next({ + data: { + node: { + id: '1', + __typename: 'User', + name: 'Alice', + }, + }, + }); + jest.runAllImmediates(); + }); + expect(renderer?.toJSON()).toBe('Alice'); + } + }, ); - return data.me?.client_object?.description; - } - expect(() => { - TestRenderer.act(() => { - TestRenderer.create(); + it('should fetch data missing client edge to server data in resolver @rootFragment', () => { + function TestComponent() { + return ( + + + + + + ); + } + + const variables = {}; + function InnerComponent() { + const data = useLazyLoadQuery( + graphql` + query ClientEdgesTest6Query { + me { + same_user_client_edge @waterfall { + # No fields here means that we render without detecting any + # missing data here and don't attempt to fetch the @waterfall + # query. + # + # The same bug can be triggered by adding a field that is already + # in the store for an unrelated reason. + upper_name + # Adding "name" here will cause the query to be fetched. + } + } + } + `, + variables, + ); + + return data.me?.same_user_client_edge?.upper_name; + } + + // This will be updated when we add the new assertions as part of a fix for + // this bug. + let renderer; + TestRenderer.act(() => { + renderer = TestRenderer.create(); + }); + + expect(fetchFn.mock.calls.length).toEqual(1); + // We should send the client-edge query + // $FlowFixMe[invalid-tuple-index] Error found while enabling LTI on this file + expect(fetchFn.mock.calls[0][0].name).toBe( + 'ClientEdgeQuery_ClientEdgesTest6Query_me__same_user_client_edge', + ); + // Check variables + // $FlowFixMe[invalid-tuple-index] Error found while enabling LTI on this file + expect(fetchFn.mock.calls[0][1]).toEqual({id: '1'}); + expect(renderer?.toJSON()).toBe('Loading'); + + TestRenderer.act(() => { + // This should resolve client-edge query + networkSink.next({ + data: { + node: { + id: '1', + __typename: 'User', + name: 'Alice', + }, + }, + }); + jest.runAllImmediates(); + }); + expect(renderer?.toJSON()).toBe('ALICE'); }); - }).toThrow( - "Relay: Missing @required value at path 'me.client_object' in 'ClientEdgesTest4Query'.", - ); - expect(fetchFn.mock.calls.length).toEqual(0); -}); + }, +); diff --git a/packages/react-relay/__tests__/LiveResolvers-test.js b/packages/react-relay/__tests__/LiveResolvers-test.js index 48836d8b7be79..d6bce21685cac 100644 --- a/packages/react-relay/__tests__/LiveResolvers-test.js +++ b/packages/react-relay/__tests__/LiveResolvers-test.js @@ -39,6 +39,9 @@ const { } = require('relay-runtime/store/RelayModernOperationDescriptor'); const RelayModernStore = require('relay-runtime/store/RelayModernStore'); const RelayRecordSource = require('relay-runtime/store/RelayRecordSource'); +const { + RELAY_READ_TIME_RESOLVER_KEY_PREFIX, +} = require('relay-runtime/store/RelayStoreUtils'); const { disallowConsoleErrors, disallowWarnings, @@ -403,6 +406,7 @@ test('Outer resolvers do not overwrite subscriptions made by inner resolvers (re }); TestRenderer.act(() => jest.runAllImmediates()); + // $FlowFixMe[incompatible-use] expect(renderer.toJSON()).toEqual(null); // Calling increment here should be ignored by Relay. However, if there are @@ -411,12 +415,14 @@ test('Outer resolvers do not overwrite subscriptions made by inner resolvers (re GLOBAL_STORE.dispatch({type: 'INCREMENT'}); }); TestRenderer.act(() => jest.runAllImmediates()); + // $FlowFixMe[incompatible-use] expect(renderer.toJSON()).toEqual(null); // Revering optimistic update puts inner back into a state where its // fragment is valid. HOWEVER, if a dangling subscription has marked inner // as dirty, we will try to read from a LiveValue that does not exist. TestRenderer.act(() => update.dispose()); + // $FlowFixMe[incompatible-use] expect(renderer.toJSON()).toEqual('1'); // Not part of the repro, but just to confirm: We should now be resubscribed... @@ -424,6 +430,7 @@ test('Outer resolvers do not overwrite subscriptions made by inner resolvers (re GLOBAL_STORE.dispatch({type: 'INCREMENT'}); }); TestRenderer.act(() => jest.runAllImmediates()); + // $FlowFixMe[incompatible-use] expect(renderer.toJSON()).toEqual('2'); }); @@ -496,6 +503,7 @@ test("Resolvers without fragments aren't reevaluated when their parent record up TestRenderer.act(() => jest.runAllImmediates()); expect(counterNoFragmentResolver.callCount).toBe(initialCallCount + 1); + // $FlowFixMe[incompatible-use] expect(renderer.toJSON()).toEqual('0'); }); @@ -561,10 +569,12 @@ test('Can suspend', () => { }); // If do not trigger `act` here, the renderer is still `0`. Probably, a React thing... TestRenderer.act(() => jest.runAllImmediates()); + // $FlowFixMe[incompatible-use] expect(renderer.toJSON()).toEqual('Loading...'); TestRenderer.act(() => { GLOBAL_STORE.dispatch({type: 'INCREMENT'}); }); + // $FlowFixMe[incompatible-use] expect(renderer.toJSON()).toEqual('2'); }); @@ -633,10 +643,12 @@ test('Can suspend with resolver that uses live resolver', () => { }); // If do not trigger `act` here, the renderer is still `0`. Probably, a React thing... TestRenderer.act(() => jest.runAllImmediates()); + // $FlowFixMe[incompatible-use] expect(renderer.toJSON()).toEqual('Loading...'); TestRenderer.act(() => { GLOBAL_STORE.dispatch({type: 'INCREMENT'}); }); + // $FlowFixMe[incompatible-use] expect(renderer.toJSON()).toEqual('Alice 2'); TestRenderer.act(() => { const operationDescriptor = createOperationDescriptor( @@ -647,6 +659,7 @@ test('Can suspend with resolver that uses live resolver', () => { me: {id: '1', name: 'Bob', __typename: 'User'}, }); }); + // $FlowFixMe[incompatible-use] expect(renderer.toJSON()).toEqual('Bob 2'); }); @@ -877,6 +890,7 @@ describe('Live Resolver with Suspense and Missing Data', () => { }); skipIf( + // $FlowFixMe[cannot-resolve-name] process.env.OSS, 'Live Resolver with Missing Data and @required', async () => { @@ -1387,7 +1401,7 @@ describe('client-only fragments', () => { const LiveResolversTestLiveResolverSuspenseQuery = graphql` query LiveResolversTestLiveResolverSuspenseQuery($id: ID!) { node(id: $id) { - ...LiveResolversTestCounterUserFragment + ...LiveResolversTestCounterUserFragment @dangerously_unaliased_fixme } } `; @@ -1444,10 +1458,12 @@ describe('client-only fragments', () => { GLOBAL_STORE.dispatch({type: 'INCREMENT'}); }); TestRenderer.act(() => jest.runAllImmediates()); + // $FlowFixMe[incompatible-use] expect(renderer.toJSON()).toEqual('Loading...'); TestRenderer.act(() => { GLOBAL_STORE.dispatch({type: 'INCREMENT'}); }); + // $FlowFixMe[incompatible-use] expect(renderer.toJSON()).toEqual('2'); }); @@ -1477,10 +1493,13 @@ describe('client-only fragments', () => { GLOBAL_STORE.dispatch({type: 'INCREMENT'}); }); TestRenderer.act(() => jest.runAllImmediates()); + // $FlowFixMe[incompatible-use] expect(renderer.toJSON()).toEqual('Loading...'); environment.applyUpdate({ storeUpdater: store => { - const record = store.get('client:1:counter_suspends_when_odd'); + const record = store.get( + `client:1:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}counter_suspends_when_odd`, + ); // this will force the invalid `liveState` value` in the resolver record record?.setValue(undefined, '__resolverLiveStateValue'); }, @@ -1488,8 +1507,9 @@ describe('client-only fragments', () => { expect(() => { GLOBAL_STORE.dispatch({type: 'INCREMENT'}); }).toThrowError( - 'Unexpected LiveState value returned from Relay Resolver internal field `RELAY_RESOLVER_LIVE_STATE_VALUE`. It is likely a bug in Relay, or a corrupt state of the relay store state Field Path `counter_suspends_when_odd`. Record `{"__id":"client:1:counter_suspends_when_odd","__typename":"__RELAY_RESOLVER__","__resolverError":null,"__resolverValue":{"__LIVE_RESOLVER_SUSPENSE_SENTINEL":true},"__resolverLiveStateDirty":true}`.', + 'Unexpected LiveState value returned from Relay Resolver internal field `RELAY_RESOLVER_LIVE_STATE_VALUE`. It is likely a bug in Relay, or a corrupt state of the relay store state Field Path `counter_suspends_when_odd`. Record `{"__id":"client:1:$r:counter_suspends_when_odd","__typename":"__RELAY_RESOLVER__","__resolverError":null,"__resolverValue":{"__LIVE_RESOLVER_SUSPENSE_SENTINEL":true},"__resolverLiveStateDirty":true}`.', ); + // $FlowFixMe[incompatible-use] expect(renderer.toJSON()).toEqual('Loading...'); }); }); @@ -1682,7 +1702,7 @@ test('Errors when reading a @live resolver that does not return a LiveState obje }); const data = environment.lookup(operation.fragment); - expect(data.errorResponseFields).toEqual([ + expect(data.fieldErrors).toEqual([ { kind: 'relay_resolver.error', owner: 'LiveResolversTest18Query', @@ -1738,7 +1758,7 @@ test('provided variables and resolvers', () => { }); const snapshot = environment.lookup(operation.fragment); - expect(snapshot.errorResponseFields).toEqual(null); + expect(snapshot.fieldErrors).toEqual(null); expect(snapshot.data).toEqual({ hello_world_with_provided_variable: 'Hello, Hello, World!!', }); @@ -1763,7 +1783,7 @@ test('allows dependencies to be provided through the store', () => { }); const snapshot = environment.lookup(operation.fragment); - expect(snapshot.errorResponseFields).toEqual(null); + expect(snapshot.fieldErrors).toEqual(null); expect(snapshot.data).toEqual({ hello_world_with_context: 'Hello Hello Allemaal!!', }); @@ -1790,7 +1810,7 @@ test('allows objects to be provided to be provided through the store', () => { }); const snapshot = environment.lookup(operation.fragment); - expect(snapshot.errorResponseFields).toEqual(null); + expect(snapshot.fieldErrors).toEqual(null); expect(snapshot.data).toEqual({ hello_world_with_context_object: 'Hello Hello Allemaal!!', }); @@ -1876,3 +1896,156 @@ test('ResolverContext can contain observable values', async () => { counter_context: 1, }); }); + +test('ResolverContext as passed through nested resolver counters', async () => { + const source = RelayRecordSource.create({ + 'client:root': { + __id: 'client:root', + __typename: '__Root', + me: {__ref: '1'}, + }, + '1': { + __id: '1', + __typename: 'User', + id: '1', + }, + }); + const FooQuery = graphql` + query LiveResolversTestCounterContextBaseQuery { + base_counter_context { + count_plus_one + } + } + `; + + let next: (v: number) => void = () => { + throw new Error('next() not initialized'); + }; + + const operation = createOperationDescriptor(FooQuery, {}); + const store = new RelayModernStore(source, { + gcReleaseBufferSize: 0, + resolverContext: { + counter: Observable.create(observer => { + next = (value: number) => observer.next(value); + }), + }, + }); + + const environment = new RelayModernEnvironment({ + network: RelayNetwork.create(jest.fn()), + store, + }); + + let observedCounter = null; + + const snapshot = environment.lookup(operation.fragment); + // $FlowFixMe[unclear-type] - lookup() doesn't have the nice types of reading a fragment through the actual APIs: + observedCounter = (snapshot.data: any).base_counter_context.count_plus_one; + + const environmentUpdateHandler = jest.fn(() => { + const s = environment.lookup(operation.fragment); + // $FlowFixMe[unclear-type] - lookup() doesn't have the nice types of reading a fragment through the actual APIs: + observedCounter = (s.data: any).base_counter_context.count_plus_one; + }); + const disposable = environment.subscribe( + snapshot, + // $FlowFixMe[invalid-tuple-arity] Error found while enabling LTI on this file + environmentUpdateHandler, + ); + + // SETUP COMPLETE + + // Read the initial value + expect(observedCounter).toBe(0); + expect(environmentUpdateHandler).not.toHaveBeenCalled(); + + // Increment and assert we get notified of the new value + next(43); + expect(environmentUpdateHandler).toHaveBeenCalledTimes(1); + expect(observedCounter).toBe(44); + + // Unsubscribe then increment and assert don't get notified. + disposable.dispose(); + next(1); + expect(environmentUpdateHandler).toHaveBeenCalledTimes(1); + expect(observedCounter).toBe(44); + + // Explicitly read and assert we see the incremented value + // missed before due to unsubscribing. + const nextSnapshot = environment.lookup(operation.fragment); + + expect(nextSnapshot.data).toEqual({ + base_counter_context: { + count_plus_one: 2, + }, + }); +}); + +test('Defer does not prevent suspense from a deferred fragment', () => { + const source = RelayRecordSource.create({ + 'client:root': { + __id: 'client:root', + __typename: '__Root', + }, + }); + + const Fragment = graphql` + fragment LiveResolversTestDeferFragment on Query { + counter_suspends_when_odd + } + `; + const FooQuery = graphql` + query LiveResolversTestDeferQuery { + ...LiveResolversTestDeferFragment @defer + } + `; + + const store = new RelayModernStore(source, { + gcReleaseBufferSize: 0, + }); + + // Make the value odd for `counter_suspends_when_odd` + GLOBAL_STORE.dispatch({type: 'INCREMENT'}); + + const environment = new RelayModernEnvironment({ + network: RelayNetwork.create(jest.fn()), + store, + }); + environment.commitPayload( + createOperationDescriptor(getRequest(FooQuery), {}), + { + me: {id: '1'}, + }, + ); + + function Environment({children}: {children: React.Node}) { + return ( + + {children} + + ); + } + + function TestComponent() { + const queryData = useLazyLoadQuery(FooQuery, {}); + const fragmentData = useFragment(Fragment, queryData); + return fragmentData.counter_suspends_when_odd; + } + + let renderer; + TestRenderer.act(() => { + renderer = TestRenderer.create( + + + , + ); + }); + // @defer does not prevent suspense from a resolve field + expect(renderer?.toJSON()).toEqual('Loading...'); + TestRenderer.act(() => { + GLOBAL_STORE.dispatch({type: 'INCREMENT'}); + }); + TestRenderer.act(() => jest.runAllImmediates()); + expect(renderer?.toJSON()).toEqual('2'); +}); diff --git a/packages/react-relay/__tests__/ReactRelayFragmentContainer-WithFragmentOwnership-test.js b/packages/react-relay/__tests__/ReactRelayFragmentContainer-WithFragmentOwnership-test.js index cb30b57d7e101..19fcf5028d7ad 100644 --- a/packages/react-relay/__tests__/ReactRelayFragmentContainer-WithFragmentOwnership-test.js +++ b/packages/react-relay/__tests__/ReactRelayFragmentContainer-WithFragmentOwnership-test.js @@ -84,6 +84,7 @@ describe('ReactRelayFragmentContainer with fragment ownership', () => { ) { node(id: $id) { ...ReactRelayFragmentContainerWithFragmentOwnershipTestUserFragment + @dangerously_unaliased_fixme } } `; @@ -94,6 +95,7 @@ describe('ReactRelayFragmentContainer with fragment ownership', () => { ) { node(id: $id) { ...ReactRelayFragmentContainerWithFragmentOwnershipTestUserFragment + @dangerously_unaliased_fixme @arguments(cond: $condGlobal) } } @@ -206,7 +208,7 @@ describe('ReactRelayFragmentContainer with fragment ownership', () => { }, __fragmentOwner: ownerUser1.request, }, - errorResponseFields: null, + fieldErrors: null, missingLiveResolverFields: [], missingClientEdges: null, isMissingData: false, @@ -333,7 +335,7 @@ describe('ReactRelayFragmentContainer with fragment ownership', () => { }, __fragmentOwner: ownerUser2.request, }, - errorResponseFields: null, + fieldErrors: null, missingLiveResolverFields: [], missingClientEdges: null, isMissingData: false, @@ -403,7 +405,7 @@ describe('ReactRelayFragmentContainer with fragment ownership', () => { }, __fragmentOwner: ownerUser1WithCondVar.request, }, - errorResponseFields: null, + fieldErrors: null, missingLiveResolverFields: [], missingClientEdges: null, isMissingData: false, diff --git a/packages/react-relay/__tests__/ReactRelayFragmentContainer-react-double-effects-test.js b/packages/react-relay/__tests__/ReactRelayFragmentContainer-react-double-effects-test.js index c3aa0e9bdeb11..ef7ecc9862276 100644 --- a/packages/react-relay/__tests__/ReactRelayFragmentContainer-react-double-effects-test.js +++ b/packages/react-relay/__tests__/ReactRelayFragmentContainer-react-double-effects-test.js @@ -31,7 +31,6 @@ let gqlFragment; let variables; let renderSpy; -// TODO(T83890478): enable once double invoked effects lands in xplat describe.skip('ReactRelayFragmentContainer-react-double-effects-test', () => { beforeEach(() => { // Set up feature flags @@ -50,6 +49,7 @@ describe.skip('ReactRelayFragmentContainer-react-double-effects-test', () => { ) { node(id: $id) { ...ReactRelayFragmentContainerReactDoubleEffectsTestUserFragment + @dangerously_unaliased_fixme } } `; @@ -127,6 +127,7 @@ describe.skip('ReactRelayFragmentContainer-react-double-effects-test', () => { // Assert render state of component after double invoked effects expect(renderLogs).toEqual([ + 'render: Alice', 'render: Alice', 'commit: Alice', 'cleanup: Alice', @@ -148,6 +149,7 @@ describe.skip('ReactRelayFragmentContainer-react-double-effects-test', () => { // Assert render state of component after double invoked effects expect(renderLogs).toEqual([ + 'render: Alice Updated', 'render: Alice Updated', 'cleanup: Alice', 'commit: Alice Updated', diff --git a/packages/react-relay/__tests__/ReactRelayFragmentContainer-test.js b/packages/react-relay/__tests__/ReactRelayFragmentContainer-test.js index 40d9f3d73c6ca..d90987ba022cb 100644 --- a/packages/react-relay/__tests__/ReactRelayFragmentContainer-test.js +++ b/packages/react-relay/__tests__/ReactRelayFragmentContainer-test.js @@ -88,6 +88,7 @@ describe('ReactRelayFragmentContainer', () => { query ReactRelayFragmentContainerTestUserQuery($id: ID!) { node(id: $id) { ...ReactRelayFragmentContainerTestUserFragment + @dangerously_unaliased_fixme } } `; @@ -99,6 +100,7 @@ describe('ReactRelayFragmentContainer', () => { ) { node(id: $id) { ...ReactRelayFragmentContainerTestUserFragment + @dangerously_unaliased_fixme @arguments(cond: $condGlobal) } } @@ -270,7 +272,7 @@ describe('ReactRelayFragmentContainer', () => { id: '4', name: 'Zuck', }, - errorResponseFields: null, + fieldErrors: null, missingLiveResolverFields: [], missingClientEdges: null, isMissingData: false, @@ -371,7 +373,7 @@ describe('ReactRelayFragmentContainer', () => { name: 'Joe', }, isMissingData: false, - errorResponseFields: null, + fieldErrors: null, missingLiveResolverFields: [], missingClientEdges: null, seenRecords: expect.any(Object), @@ -429,7 +431,7 @@ describe('ReactRelayFragmentContainer', () => { // Name is excluded since value of cond is now false }, isMissingData: false, - errorResponseFields: null, + fieldErrors: null, missingLiveResolverFields: [], missingClientEdges: null, seenRecords: expect.any(Object), diff --git a/packages/react-relay/__tests__/ReactRelayLocalQueryRenderer-test.js b/packages/react-relay/__tests__/ReactRelayLocalQueryRenderer-test.js index 50ae350bd490c..633efd050ab8b 100644 --- a/packages/react-relay/__tests__/ReactRelayLocalQueryRenderer-test.js +++ b/packages/react-relay/__tests__/ReactRelayLocalQueryRenderer-test.js @@ -44,6 +44,7 @@ describe('ReactRelayLocalQueryRenderer', () => { lastName } ...ReactRelayLocalQueryRendererTestUserFragment + @dangerously_unaliased_fixme } } `; diff --git a/packages/react-relay/__tests__/ReactRelayPaginationContainer-WithFragmentOwnership-test.js b/packages/react-relay/__tests__/ReactRelayPaginationContainer-WithFragmentOwnership-test.js index 44a0dc47446e6..833acfc2bd1b2 100644 --- a/packages/react-relay/__tests__/ReactRelayPaginationContainer-WithFragmentOwnership-test.js +++ b/packages/react-relay/__tests__/ReactRelayPaginationContainer-WithFragmentOwnership-test.js @@ -115,6 +115,7 @@ describe('ReactRelayPaginationContainer with fragment ownership', () => { id __typename ...ReactRelayPaginationContainerWithFragmentOwnershipTestUserFragment + @dangerously_unaliased_fixme @arguments(isViewerFriendLocal: $isViewerFriend, orderby: $orderby) } } diff --git a/packages/react-relay/__tests__/ReactRelayPaginationContainer-react-double-effects-test.js b/packages/react-relay/__tests__/ReactRelayPaginationContainer-react-double-effects-test.js index 557952997d923..a64545cc0994b 100644 --- a/packages/react-relay/__tests__/ReactRelayPaginationContainer-react-double-effects-test.js +++ b/packages/react-relay/__tests__/ReactRelayPaginationContainer-react-double-effects-test.js @@ -31,7 +31,6 @@ let gqlFragment; let variables; let renderSpy; -// TODO(T83890478): enable once double invoked effects lands in xplat describe.skip('ReactRelayFragmentContainer-react-double-effects-test', () => { beforeEach(() => { // Set up feature flags @@ -51,6 +50,7 @@ describe.skip('ReactRelayFragmentContainer-react-double-effects-test', () => { ) { node(id: $id) { ...ReactRelayPaginationContainerReactDoubleEffectsTestUserFragment + @dangerously_unaliased_fixme } } `; @@ -142,6 +142,7 @@ describe.skip('ReactRelayFragmentContainer-react-double-effects-test', () => { // Assert render state of component after double invoked effects expect(renderLogs).toEqual([ + 'render: Alice', 'render: Alice', 'commit: Alice', 'cleanup: Alice', @@ -163,6 +164,7 @@ describe.skip('ReactRelayFragmentContainer-react-double-effects-test', () => { // Assert render state of component after double invoked effects expect(renderLogs).toEqual([ + 'render: Alice Updated', 'render: Alice Updated', 'cleanup: Alice', 'commit: Alice Updated', diff --git a/packages/react-relay/__tests__/ReactRelayPaginationContainer-test.js b/packages/react-relay/__tests__/ReactRelayPaginationContainer-test.js index 19f60ef692c7d..2ac56b98f1667 100644 --- a/packages/react-relay/__tests__/ReactRelayPaginationContainer-test.js +++ b/packages/react-relay/__tests__/ReactRelayPaginationContainer-test.js @@ -102,6 +102,7 @@ describe('ReactRelayPaginationContainer', () => { id __typename ...ReactRelayPaginationContainerTestUserFragment + @dangerously_unaliased_fixme @arguments(isViewerFriendLocal: $isViewerFriend, orderby: $orderby) } } @@ -343,7 +344,7 @@ describe('ReactRelayPaginationContainer', () => { expect(environment.subscribe.mock.calls[0][0]).toEqual({ data: expect.any(Object), isMissingData: false, - errorResponseFields: null, + fieldErrors: null, missingLiveResolverFields: [], missingClientEdges: null, seenRecords: expect.any(Object), @@ -456,7 +457,7 @@ describe('ReactRelayPaginationContainer', () => { expect(environment.subscribe.mock.calls[0][0]).toEqual({ data: expect.any(Object), isMissingData: false, - errorResponseFields: null, + fieldErrors: null, missingLiveResolverFields: [], missingClientEdges: null, seenRecords: expect.any(Object), @@ -525,7 +526,7 @@ describe('ReactRelayPaginationContainer', () => { expect(environment.subscribe.mock.calls[0][0]).toEqual({ data: expect.any(Object), isMissingData: false, - errorResponseFields: null, + fieldErrors: null, missingLiveResolverFields: [], missingClientEdges: null, seenRecords: expect.any(Object), @@ -629,7 +630,7 @@ describe('ReactRelayPaginationContainer', () => { expect(environment.subscribe.mock.calls[0][0]).toEqual({ data: expect.any(Object), isMissingData: false, - errorResponseFields: null, + fieldErrors: null, missingLiveResolverFields: [], missingClientEdges: null, seenRecords: expect.any(Object), @@ -815,6 +816,7 @@ describe('ReactRelayPaginationContainer', () => { node(id: $id) { id ...ReactRelayPaginationContainerTestNoConnectionUserFragment + @dangerously_unaliased_fixme } } `; @@ -870,6 +872,7 @@ describe('ReactRelayPaginationContainer', () => { node(id: $id) { id ...ReactRelayPaginationContainerTestNoConnectionOnFragmentUserFragment + @dangerously_unaliased_fixme } } `; diff --git a/packages/react-relay/__tests__/ReactRelayQueryRenderer-test.js b/packages/react-relay/__tests__/ReactRelayQueryRenderer-test.js index b3a8eb11033d1..32a24a0421fb2 100644 --- a/packages/react-relay/__tests__/ReactRelayQueryRenderer-test.js +++ b/packages/react-relay/__tests__/ReactRelayQueryRenderer-test.js @@ -93,7 +93,7 @@ describe('ReactRelayQueryRenderer', () => { query ReactRelayQueryRendererTestQuery($id: ID = "") { node(id: $id) { id - ...ReactRelayQueryRendererTestFragment + ...ReactRelayQueryRendererTestFragment @dangerously_unaliased_fixme } } `; diff --git a/packages/react-relay/__tests__/ReactRelayRefetchContainer-WithFragmentOwnership-test.js b/packages/react-relay/__tests__/ReactRelayRefetchContainer-WithFragmentOwnership-test.js index e5edd26c21b6f..d9a35af97002a 100644 --- a/packages/react-relay/__tests__/ReactRelayRefetchContainer-WithFragmentOwnership-test.js +++ b/packages/react-relay/__tests__/ReactRelayRefetchContainer-WithFragmentOwnership-test.js @@ -110,6 +110,7 @@ describe('ReactRelayRefetchContainer with fragment ownership', () => { ) { node(id: $id) { ...ReactRelayRefetchContainerWithFragmentOwnershipTestUserFragment + @dangerously_unaliased_fixme } } `; diff --git a/packages/react-relay/__tests__/ReactRelayRefetchContainer-react-double-effects-test.js b/packages/react-relay/__tests__/ReactRelayRefetchContainer-react-double-effects-test.js index e75b95c8fb8be..dd10e38211f7f 100644 --- a/packages/react-relay/__tests__/ReactRelayRefetchContainer-react-double-effects-test.js +++ b/packages/react-relay/__tests__/ReactRelayRefetchContainer-react-double-effects-test.js @@ -31,7 +31,6 @@ let gqlFragment; let variables; let renderSpy; -// TODO(T83890478): enable once double invoked effects lands in xplat describe.skip('ReactRelayFragmentContainer-react-double-effects-test', () => { beforeEach(() => { // Set up feature flags @@ -50,6 +49,7 @@ describe.skip('ReactRelayFragmentContainer-react-double-effects-test', () => { ) { node(id: $id) { ...ReactRelayRefetchContainerReactDoubleEffectsTestUserFragment + @dangerously_unaliased_fixme } } `; @@ -127,6 +127,7 @@ describe.skip('ReactRelayFragmentContainer-react-double-effects-test', () => { // Assert render state of component after double invoked effects expect(renderLogs).toEqual([ + 'render: Alice', 'render: Alice', 'commit: Alice', 'cleanup: Alice', @@ -148,6 +149,7 @@ describe.skip('ReactRelayFragmentContainer-react-double-effects-test', () => { // Assert render state of component after double invoked effects expect(renderLogs).toEqual([ + 'render: Alice Updated', 'render: Alice Updated', 'cleanup: Alice', 'commit: Alice Updated', diff --git a/packages/react-relay/__tests__/ReactRelayRefetchContainer-test.js b/packages/react-relay/__tests__/ReactRelayRefetchContainer-test.js index 56b2ceb8a3a13..4980e9bc45097 100644 --- a/packages/react-relay/__tests__/ReactRelayRefetchContainer-test.js +++ b/packages/react-relay/__tests__/ReactRelayRefetchContainer-test.js @@ -104,6 +104,7 @@ describe('ReactRelayRefetchContainer', () => { ) { node(id: $id) { ...ReactRelayRefetchContainerTestUserFragment + @dangerously_unaliased_fixme @arguments(cond: $condGlobal) } } @@ -112,6 +113,7 @@ describe('ReactRelayRefetchContainer', () => { query ReactRelayRefetchContainerTestUserQuery($id: ID!) { node(id: $id) { ...ReactRelayRefetchContainerTestUserFragment + @dangerously_unaliased_fixme } } `; @@ -279,7 +281,7 @@ describe('ReactRelayRefetchContainer', () => { name: 'Zuck', }, isMissingData: false, - errorResponseFields: null, + fieldErrors: null, missingLiveResolverFields: [], missingClientEdges: null, seenRecords: expect.any(Object), @@ -379,7 +381,7 @@ describe('ReactRelayRefetchContainer', () => { name: 'Joe', }, isMissingData: false, - errorResponseFields: null, + fieldErrors: null, missingLiveResolverFields: [], missingClientEdges: null, seenRecords: expect.any(Object), @@ -438,7 +440,7 @@ describe('ReactRelayRefetchContainer', () => { // Name is excluded since value of cond is now false }, isMissingData: false, - errorResponseFields: null, + fieldErrors: null, missingLiveResolverFields: [], missingClientEdges: null, seenRecords: expect.any(Object), @@ -524,7 +526,7 @@ describe('ReactRelayRefetchContainer', () => { id: '4', // Name is excluded since value of cond is now false }, - errorResponseFields: null, + fieldErrors: null, missingLiveResolverFields: [], missingClientEdges: null, isMissingData: false, diff --git a/packages/react-relay/__tests__/ReactRelayTestMocker-test.js b/packages/react-relay/__tests__/ReactRelayTestMocker-test.js index fd2b76b618814..d21c97b73feb9 100644 --- a/packages/react-relay/__tests__/ReactRelayTestMocker-test.js +++ b/packages/react-relay/__tests__/ReactRelayTestMocker-test.js @@ -11,6 +11,8 @@ 'use strict'; import type {RelayMockEnvironment} from '../../relay-test-utils/RelayModernMockEnvironment'; +import type {NetworkWriteConfig} from '../ReactRelayTestMocker'; +import type {GeneratedNodeMap} from '../ReactRelayTypes'; const RelayTestRenderer = require('../__mocks__/RelayTestRenderer'); const { @@ -46,7 +48,7 @@ const ReactRelayTestMockerTestNestedQuery = graphql` } } `; -const ReactRelayTestMockerTest_meFragmentDefinition = { +const ReactRelayTestMockerTest_meFragmentDefinition: GeneratedNodeMap = { me: graphql` fragment ReactRelayTestMockerTest_me on User { name @@ -106,7 +108,7 @@ describe('ReactRelayTestMocker', () => { const nestedQuery = ReactRelayTestMockerTestNestedQuery; - const nestedQueryDefault = { + const nestedQueryDefault: NetworkWriteConfig = { query: ReactRelayTestMockerTestNestedQuery, payload: { data: { diff --git a/packages/react-relay/__tests__/RelayClient3DModule-test.js b/packages/react-relay/__tests__/RelayClient3DModule-test.js index 737bf70b6ccf4..9ba4ae35e4767 100644 --- a/packages/react-relay/__tests__/RelayClient3DModule-test.js +++ b/packages/react-relay/__tests__/RelayClient3DModule-test.js @@ -12,6 +12,10 @@ 'use strict'; import type {LogEvent} from '../../relay-runtime/store/RelayStoreTypes'; +import type { + NormalizationRootNode, + NormalizationSplitOperation, +} from 'relay-runtime/util/NormalizationNode'; import MatchContainer from '../relay-hooks/MatchContainer'; import React from 'react'; @@ -116,13 +120,20 @@ beforeEach(() => { describe('ClientUser', () => { let store; let environment; + let operationLoader; beforeEach(() => { + operationLoader = { + load: jest.fn<[mixed], Promise>(), + get: jest.fn<[mixed], ?NormalizationRootNode>(), + }; store = new RelayModernStore(RelayRecordSource.create(), { log: logFn, + operationLoader, }); environment = new RelayModernEnvironment({ network: RelayNetwork.create(jest.fn()), + operationLoader, store, log: logFn, }); @@ -154,7 +165,6 @@ describe('ClientUser', () => { ); //$FlowFixMe const fragmentSnapshot = environment.lookup(fragmentSelector); - const dataSelector = getSelector( CLIENT_USER_FRAGMENT, fragmentSnapshot.data?.basicUser, @@ -185,13 +195,20 @@ describe('ClientUser', () => { describe('SpecialUser', () => { let environment; let store; + let operationLoader; beforeEach(() => { + operationLoader = { + load: jest.fn<[mixed], Promise>(), + get: jest.fn<[mixed], ?NormalizationRootNode>(), + }; store = new RelayModernStore(RelayRecordSource.create(), { log: logFn, + operationLoader, }); environment = new RelayModernEnvironment({ network: RelayNetwork.create(jest.fn()), store, + operationLoader, log: logFn, }); const operation = createOperationDescriptor(CLIENT_3D_TEST_QUERY, {}); @@ -200,6 +217,7 @@ describe('SpecialUser', () => { basicUser: { __typename: 'SpecialUser', id: '2', + data: 'specialUserData', }, }, }); @@ -221,7 +239,6 @@ describe('SpecialUser', () => { ); //$FlowFixMe const fragmentSnapshot = environment.lookup(fragmentSelector); - const dataSelector = getSelector( SPECIAL_USER_FRAGMENT, fragmentSnapshot.data?.basicUser, diff --git a/packages/react-relay/__tests__/RelayResolverModel-test.js b/packages/react-relay/__tests__/RelayResolverModel-test.js index 32ded40d0c9b6..aefa1cdceacdd 100644 --- a/packages/react-relay/__tests__/RelayResolverModel-test.js +++ b/packages/react-relay/__tests__/RelayResolverModel-test.js @@ -282,6 +282,7 @@ describe.each([['New', useFragment]])( completeTodo('todo-1'); jest.runAllImmediates(); }); + // $FlowFixMe[incompatible-use] expect(renderer.toJSON()).toEqual('Test todo - green'); }); @@ -328,6 +329,7 @@ describe.each([['New', useFragment]])( }); expect(LiveColorSubscriptions.activeSubscriptions.length).toBe(1); + // $FlowFixMe[incompatible-use] expect(renderer.toJSON()).toEqual('Test todo - green'); TestRenderer.act(() => { @@ -335,6 +337,7 @@ describe.each([['New', useFragment]])( jest.runAllImmediates(); }); + // $FlowFixMe[incompatible-use] expect(renderer.toJSON()).toEqual(null); // Run GC to will remove "orphan" records and unsubscribe if they have live resolver subscriptions store.scheduleGC(); @@ -378,6 +381,7 @@ describe.each([['New', useFragment]])( changeDescription('todo-1', 'Changed todo description text'); jest.runAllImmediates(); }); + // $FlowFixMe[incompatible-use] expect(renderer.toJSON()).toEqual('[x] Changed todo description text'); }); @@ -478,7 +482,7 @@ describe.each([['New', useFragment]])( return fancyDescriptions .map(item => - item == null ? 'ITEM IS NULL' : item.text ?? 'TEXT IS NULL', + item == null ? 'ITEM IS NULL' : (item.text ?? 'TEXT IS NULL'), ) .join(', '); } @@ -671,6 +675,7 @@ describe.each([['New', useFragment]])( setIsHuman(false); jest.runAllImmediates(); }); + // $FlowFixMe[incompatible-use] expect(renderer.toJSON()).toEqual('robot:0'); TestRenderer.act(() => { @@ -678,6 +683,7 @@ describe.each([['New', useFragment]])( setIsHuman(true); jest.runAllImmediates(); }); + // $FlowFixMe[incompatible-use] expect(renderer.toJSON()).toEqual('human:0'); TestRenderer.act(() => { diff --git a/packages/react-relay/__tests__/RelayResolverModelWithContext-test.js b/packages/react-relay/__tests__/RelayResolverModelWithContext-test.js new file mode 100644 index 0000000000000..a76c2768bf067 --- /dev/null +++ b/packages/react-relay/__tests__/RelayResolverModelWithContext-test.js @@ -0,0 +1,134 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + * @oncall relay + */ + +'use strict'; + +import type {RelayResolverModelWithContextTestFragment$key} from './__generated__/RelayResolverModelWithContextTestFragment.graphql'; +import type {TestResolverContextType} from 'relay-runtime/mutations/__tests__/TestResolverContextType'; + +const React = require('react'); +const { + RelayEnvironmentProvider, + useClientQuery, + useFragment, +} = require('react-relay'); +const TestRenderer = require('react-test-renderer'); +const {Observable} = require('relay-runtime'); +const RelayNetwork = require('relay-runtime/network/RelayNetwork'); +const {graphql} = require('relay-runtime/query/GraphQLTag'); +const { + addTodo, + resetStore, +} = require('relay-runtime/store/__tests__/resolvers/ExampleTodoStore'); +const RelayModernEnvironment = require('relay-runtime/store/RelayModernEnvironment'); +const RelayModernStore = require('relay-runtime/store/RelayModernStore.js'); +const RelayRecordSource = require('relay-runtime/store/RelayRecordSource'); +const { + disallowConsoleErrors, + disallowWarnings, + injectPromisePolyfill__DEPRECATED, +} = require('relay-test-utils-internal'); + +injectPromisePolyfill__DEPRECATED(); +disallowWarnings(); +disallowConsoleErrors(); + +beforeEach(() => { + resetStore(() => {}); +}); + +function EnvironmentWrapper({ + children, + environment, +}: { + children: React.Node, + environment: RelayModernEnvironment, +}) { + return ( + + {children} + + ); +} + +const RelayResolverModelWithContextTestFragment = graphql` + fragment RelayResolverModelWithContextTestFragment on TodoModel { + id + description + another_value_from_context + } +`; + +const RelayResolverModelWithContextTestQuery = graphql` + query RelayResolverModelWithContextTestQuery($id: ID!) { + todo_model(todoID: $id) { + ...RelayResolverModelWithContextTestFragment + } + } +`; + +describe('RelayResolverModelWithContext', () => { + function TodoComponent(props: { + fragmentKey: ?RelayResolverModelWithContextTestFragment$key, + }) { + const data = useFragment( + RelayResolverModelWithContextTestFragment, + props.fragmentKey, + ); + if (data == null) { + return null; + } + + return data.another_value_from_context; + } + + function TodoRootComponent(props: {todoID: string}) { + const data = useClientQuery(RelayResolverModelWithContextTestQuery, { + id: props.todoID, + }); + if (data?.todo_model == null) { + return null; + } + + return ; + } + + test('returns a value from context when resolverDataInjector is used', () => { + const resolverContext: TestResolverContextType = { + greeting: { + myHello: 'This is a value from context', + }, + counter: Observable.create(observer => { + observer.next(10); + }), + }; + + const store = new RelayModernStore(RelayRecordSource.create(), { + resolverContext, + }); + const environment = new RelayModernEnvironment({ + network: RelayNetwork.create(jest.fn()), + store, + }); + + addTodo('Test todo'); + + let renderer; + TestRenderer.act(() => { + renderer = TestRenderer.create( + + + , + ); + }); + expect(renderer?.toJSON()).toEqual('This is a value from context'); + }); +}); diff --git a/packages/react-relay/__tests__/RelayResolverNullableModelClientEdge-test.js b/packages/react-relay/__tests__/RelayResolverNullableModelClientEdge-test.js index 294e3e96c534a..5fc665c05750c 100644 --- a/packages/react-relay/__tests__/RelayResolverNullableModelClientEdge-test.js +++ b/packages/react-relay/__tests__/RelayResolverNullableModelClientEdge-test.js @@ -493,7 +493,7 @@ test('Errors thrown when reading the model a client edge points to are caught as {}, ); const snapshot = environment.lookup(operation.fragment); - expect(snapshot.errorResponseFields).toEqual([ + expect(snapshot.fieldErrors).toEqual([ { error: Error(ERROR_MESSAGE), owner: 'RelayResolverNullableModelClientEdgeTest_ErrorModel_Query', @@ -519,7 +519,7 @@ test('Errors thrown when reading plural client edge are caught as resolver error {}, ); const snapshot = environment.lookup(operation.fragment); - expect(snapshot.errorResponseFields).toEqual([ + expect(snapshot.fieldErrors).toEqual([ { error: Error(ERROR_MESSAGE), owner: 'RelayResolverNullableModelClientEdgeTest_PluralErrorModel_Query', @@ -553,7 +553,7 @@ test('Errors thrown when reading plural client edge are caught as resolver error {}, ); const snapshot = environment.lookup(operation.fragment); - expect(snapshot.errorResponseFields).toEqual([ + expect(snapshot.fieldErrors).toEqual([ { error: Error(ERROR_MESSAGE), owner: diff --git a/packages/react-relay/__tests__/RelayResolvers-withOutputType-test.js b/packages/react-relay/__tests__/RelayResolvers-withOutputType-test.js index 55ac740a64278..4ce49ad5577af 100644 --- a/packages/react-relay/__tests__/RelayResolvers-withOutputType-test.js +++ b/packages/react-relay/__tests__/RelayResolvers-withOutputType-test.js @@ -33,6 +33,9 @@ const { const RelayModernEnvironment = require('relay-runtime/store/RelayModernEnvironment'); const RelayModernStore = require('relay-runtime/store/RelayModernStore.js'); const RelayRecordSource = require('relay-runtime/store/RelayRecordSource'); +const { + RELAY_READ_TIME_RESOLVER_KEY_PREFIX, +} = require('relay-runtime/store/RelayStoreUtils'); const { disallowConsoleErrors, disallowWarnings, @@ -562,12 +565,14 @@ test('renders after GC', () => { 'client:root': { __id: 'client:root', __typename: '__Root', - 'todos(first:10)': { - __ref: 'client:root:todos(first:10)', + // $FlowFixMe[invalid-computed-prop] + [`${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}todos(first:10)`]: { + __ref: `client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}todos(first:10)`, }, }, - 'client:root:todos(first:10)': { - __id: 'client:root:todos(first:10)', + // $FlowFixMe[invalid-computed-prop] + [`client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}todos(first:10)`]: { + __id: `client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}todos(first:10)`, __resolverError: null, __resolverLiveStateDirty: false, __resolverLiveStateSubscription: expect.anything(), @@ -576,59 +581,69 @@ test('renders after GC', () => { subscribe: expect.anything(), }, __resolverOutputTypeRecordIDs: new Set([ - 'client:TodoConnection:client:root:todos(first:10)', - 'client:TodoConnection:client:root:todos(first:10):edges:0', - 'client:TodoConnection:client:root:todos(first:10):edges:0:node', - 'client:TodoConnection:client:root:todos(first:10):pageInfo', + `client:TodoConnection:client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}todos(first:10)`, + `client:TodoConnection:client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}todos(first:10):edges:0`, + `client:TodoConnection:client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}todos(first:10):edges:0:node`, + `client:TodoConnection:client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}todos(first:10):pageInfo`, ]), __resolverSnapshot: undefined, - __resolverValue: 'client:TodoConnection:client:root:todos(first:10)', + __resolverValue: `client:TodoConnection:client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}todos(first:10)`, __typename: '__RELAY_RESOLVER__', }, - 'client:TodoConnection:client:root:todos(first:10)': { - __id: 'client:TodoConnection:client:root:todos(first:10)', - __typename: 'TodoConnection', - count: 1, - edges: { - __refs: ['client:TodoConnection:client:root:todos(first:10):edges:0'], - }, - pageInfo: { - __ref: 'client:TodoConnection:client:root:todos(first:10):pageInfo', + // $FlowFixMe[invalid-computed-prop] + [`client:TodoConnection:client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}todos(first:10)`]: + { + __id: `client:TodoConnection:client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}todos(first:10)`, + __typename: 'TodoConnection', + count: 1, + edges: { + __refs: [ + `client:TodoConnection:client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}todos(first:10):edges:0`, + ], + }, + pageInfo: { + __ref: `client:TodoConnection:client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}todos(first:10):pageInfo`, + }, }, - }, - 'client:TodoConnection:client:root:todos(first:10):edges:0': { - __id: 'client:TodoConnection:client:root:todos(first:10):edges:0', - __typename: 'TodoEdge', - cursor: null, - node: { - __ref: 'client:TodoConnection:client:root:todos(first:10):edges:0:node', + // $FlowFixMe[invalid-computed-prop] + [`client:TodoConnection:client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}todos(first:10):edges:0`]: + { + __id: `client:TodoConnection:client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}todos(first:10):edges:0`, + __typename: 'TodoEdge', + cursor: null, + node: { + __ref: `client:TodoConnection:client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}todos(first:10):edges:0:node`, + }, }, - }, - 'client:TodoConnection:client:root:todos(first:10):edges:0:node': { - __id: 'client:TodoConnection:client:root:todos(first:10):edges:0:node', - __typename: 'Todo', - complete: { - __ref: - 'client:TodoConnection:client:root:todos(first:10):edges:0:node:complete', + // $FlowFixMe[invalid-computed-prop] + [`client:TodoConnection:client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}todos(first:10):edges:0:node`]: + { + __id: `client:TodoConnection:client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}todos(first:10):edges:0:node`, + __typename: 'Todo', + // $FlowFixMe[invalid-computed-prop] + [`${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}complete`]: { + __ref: `client:TodoConnection:client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}todos(first:10):edges:0:node:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}complete`, + }, + // $FlowFixMe[invalid-computed-prop] + [`${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}self`]: { + __ref: `client:TodoConnection:client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}todos(first:10):edges:0:node:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}self`, + }, + // $FlowFixMe[invalid-computed-prop] + [`${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}text`]: { + __ref: `client:TodoConnection:client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}todos(first:10):edges:0:node:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}text`, + }, + todo_id: 'todo-1', }, - self: { - __ref: - 'client:TodoConnection:client:root:todos(first:10):edges:0:node:self', + // $FlowFixMe[invalid-computed-prop] + [`client:TodoConnection:client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}todos(first:10):pageInfo`]: + { + __id: `client:TodoConnection:client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}todos(first:10):pageInfo`, + __typename: 'TodoConnectionPageInfo', + endCursor: null, + hasNextPage: false, + hasPreviousPage: false, + startCursor: null, }, - text: { - __ref: - 'client:TodoConnection:client:root:todos(first:10):edges:0:node:text', - }, - todo_id: 'todo-1', - }, - 'client:TodoConnection:client:root:todos(first:10):pageInfo': { - __id: 'client:TodoConnection:client:root:todos(first:10):pageInfo', - __typename: 'TodoConnectionPageInfo', - endCursor: null, - hasNextPage: false, - hasPreviousPage: false, - startCursor: null, - }, }); expect(() => { diff --git a/packages/react-relay/__tests__/__generated__/CatchTestResolverErrorThrowQuery.graphql.js b/packages/react-relay/__tests__/__generated__/CatchTestResolverErrorThrowQuery.graphql.js index c35d7b58ccc72..df77e81dd7d29 100644 --- a/packages/react-relay/__tests__/__generated__/CatchTestResolverErrorThrowQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/CatchTestResolverErrorThrowQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<30b8c2c3b86f090bdb6df642ac31a67a>> + * @generated SignedSource<<64b14cf1c22887a8a9fbbaa435865ec4>> * @flow * @lightSyntaxTransform * @nogrep @@ -70,7 +70,7 @@ var node/*: ConcreteRequest*/ = { }, "kind": "RelayResolver", "name": "always_throws", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/UserAlwaysThrowsResolver').always_throws, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/UserAlwaysThrowsResolver').always_throws, "path": "me.always_throws" } ], diff --git a/packages/react-relay/__tests__/__generated__/ClientEdgeQuery_ClientEdgesTest5Query_me__same_user_client_edge.graphql.js b/packages/react-relay/__tests__/__generated__/ClientEdgeQuery_ClientEdgesTest5Query_me__same_user_client_edge.graphql.js new file mode 100644 index 0000000000000..9e82d639248e1 --- /dev/null +++ b/packages/react-relay/__tests__/__generated__/ClientEdgeQuery_ClientEdgesTest5Query_me__same_user_client_edge.graphql.js @@ -0,0 +1,143 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<0be0b57292115731bc34535ae6fd1bf0>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ConcreteRequest, Query } from 'relay-runtime'; +import type { RefetchableClientEdgeQuery_ClientEdgesTest5Query_me__same_user_client_edge$fragmentType } from "./RefetchableClientEdgeQuery_ClientEdgesTest5Query_me__same_user_client_edge.graphql"; +export type ClientEdgeQuery_ClientEdgesTest5Query_me__same_user_client_edge$variables = {| + id: string, +|}; +export type ClientEdgeQuery_ClientEdgesTest5Query_me__same_user_client_edge$data = {| + +node: ?{| + +$fragmentSpreads: RefetchableClientEdgeQuery_ClientEdgesTest5Query_me__same_user_client_edge$fragmentType, + |}, +|}; +export type ClientEdgeQuery_ClientEdgesTest5Query_me__same_user_client_edge = {| + response: ClientEdgeQuery_ClientEdgesTest5Query_me__same_user_client_edge$data, + variables: ClientEdgeQuery_ClientEdgesTest5Query_me__same_user_client_edge$variables, +|}; +*/ + +var node/*: ConcreteRequest*/ = (function(){ +var v0 = [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "id" + } +], +v1 = [ + { + "kind": "Variable", + "name": "id", + "variableName": "id" + } +]; +return { + "fragment": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Fragment", + "metadata": null, + "name": "ClientEdgeQuery_ClientEdgesTest5Query_me__same_user_client_edge", + "selections": [ + { + "alias": null, + "args": (v1/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + { + "args": null, + "kind": "FragmentSpread", + "name": "RefetchableClientEdgeQuery_ClientEdgesTest5Query_me__same_user_client_edge" + } + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Operation", + "name": "ClientEdgeQuery_ClientEdgesTest5Query_me__same_user_client_edge", + "selections": [ + { + "alias": null, + "args": (v1/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + }, + { + "kind": "InlineFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + } + ], + "type": "User", + "abstractKey": null + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "134f352cb6943a77539efbdc9c4228ea", + "id": null, + "metadata": {}, + "name": "ClientEdgeQuery_ClientEdgesTest5Query_me__same_user_client_edge", + "operationKind": "query", + "text": "query ClientEdgeQuery_ClientEdgesTest5Query_me__same_user_client_edge(\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...RefetchableClientEdgeQuery_ClientEdgesTest5Query_me__same_user_client_edge\n id\n }\n}\n\nfragment ClientEdgesTest5Query_user on User {\n name\n}\n\nfragment RefetchableClientEdgeQuery_ClientEdgesTest5Query_me__same_user_client_edge on User {\n ...ClientEdgesTest5Query_user\n id\n}\n" + } +}; +})(); + +if (__DEV__) { + (node/*: any*/).hash = "8274337dee7283631e4c2d3992c0add6"; +} + +module.exports = ((node/*: any*/)/*: Query< + ClientEdgeQuery_ClientEdgesTest5Query_me__same_user_client_edge$variables, + ClientEdgeQuery_ClientEdgesTest5Query_me__same_user_client_edge$data, +>*/); diff --git a/packages/react-relay/__tests__/__generated__/ClientEdgeQuery_ClientEdgesTest6Query_me__same_user_client_edge.graphql.js b/packages/react-relay/__tests__/__generated__/ClientEdgeQuery_ClientEdgesTest6Query_me__same_user_client_edge.graphql.js new file mode 100644 index 0000000000000..db94fe9c4c80c --- /dev/null +++ b/packages/react-relay/__tests__/__generated__/ClientEdgeQuery_ClientEdgesTest6Query_me__same_user_client_edge.graphql.js @@ -0,0 +1,157 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<366f4676acbcbb62dabdb15fc6a1e6c9>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ConcreteRequest, Query } from 'relay-runtime'; +import type { RefetchableClientEdgeQuery_ClientEdgesTest6Query_me__same_user_client_edge$fragmentType } from "./RefetchableClientEdgeQuery_ClientEdgesTest6Query_me__same_user_client_edge.graphql"; +export type ClientEdgeQuery_ClientEdgesTest6Query_me__same_user_client_edge$variables = {| + id: string, +|}; +export type ClientEdgeQuery_ClientEdgesTest6Query_me__same_user_client_edge$data = {| + +node: ?{| + +$fragmentSpreads: RefetchableClientEdgeQuery_ClientEdgesTest6Query_me__same_user_client_edge$fragmentType, + |}, +|}; +export type ClientEdgeQuery_ClientEdgesTest6Query_me__same_user_client_edge = {| + response: ClientEdgeQuery_ClientEdgesTest6Query_me__same_user_client_edge$data, + variables: ClientEdgeQuery_ClientEdgesTest6Query_me__same_user_client_edge$variables, +|}; +*/ + +var node/*: ConcreteRequest*/ = (function(){ +var v0 = [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "id" + } +], +v1 = [ + { + "kind": "Variable", + "name": "id", + "variableName": "id" + } +]; +return { + "fragment": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Fragment", + "metadata": null, + "name": "ClientEdgeQuery_ClientEdgesTest6Query_me__same_user_client_edge", + "selections": [ + { + "alias": null, + "args": (v1/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + { + "args": null, + "kind": "FragmentSpread", + "name": "RefetchableClientEdgeQuery_ClientEdgesTest6Query_me__same_user_client_edge" + } + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Operation", + "name": "ClientEdgeQuery_ClientEdgesTest6Query_me__same_user_client_edge", + "selections": [ + { + "alias": null, + "args": (v1/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + }, + { + "kind": "InlineFragment", + "selections": [ + { + "name": "upper_name", + "args": null, + "fragment": { + "kind": "InlineFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + } + ], + "type": "User", + "abstractKey": null + }, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": true + } + ], + "type": "User", + "abstractKey": null + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "4b0b7798dd0bfc7d7ff2e24c459cdccc", + "id": null, + "metadata": {}, + "name": "ClientEdgeQuery_ClientEdgesTest6Query_me__same_user_client_edge", + "operationKind": "query", + "text": "query ClientEdgeQuery_ClientEdgesTest6Query_me__same_user_client_edge(\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...RefetchableClientEdgeQuery_ClientEdgesTest6Query_me__same_user_client_edge\n id\n }\n}\n\nfragment ClientEdgesTestUpperName on User {\n name\n}\n\nfragment RefetchableClientEdgeQuery_ClientEdgesTest6Query_me__same_user_client_edge on User {\n ...ClientEdgesTestUpperName\n id\n}\n" + } +}; +})(); + +if (__DEV__) { + (node/*: any*/).hash = "330a0878ce30575d8c36e2fdd626c833"; +} + +module.exports = ((node/*: any*/)/*: Query< + ClientEdgeQuery_ClientEdgesTest6Query_me__same_user_client_edge$variables, + ClientEdgeQuery_ClientEdgesTest6Query_me__same_user_client_edge$data, +>*/); diff --git a/packages/react-relay/__tests__/__generated__/ClientEdgesTest1Query.graphql.js b/packages/react-relay/__tests__/__generated__/ClientEdgesTest1Query.graphql.js index 1f38546e0dab1..3f68e440da899 100644 --- a/packages/react-relay/__tests__/__generated__/ClientEdgesTest1Query.graphql.js +++ b/packages/react-relay/__tests__/__generated__/ClientEdgesTest1Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<9c3e3f18ca539c837de80b065e0dafee>> * @flow * @lightSyntaxTransform * @nogrep @@ -88,7 +88,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "client_node", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/UserClientEdgeNodeResolver').client_node, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/UserClientEdgeNodeResolver').client_node, "path": "me.client_node" }, "linkedField": { diff --git a/packages/react-relay/__tests__/__generated__/ClientEdgesTest2Query.graphql.js b/packages/react-relay/__tests__/__generated__/ClientEdgesTest2Query.graphql.js index 8229bf2dc6ad9..94867564cbe0a 100644 --- a/packages/react-relay/__tests__/__generated__/ClientEdgesTest2Query.graphql.js +++ b/packages/react-relay/__tests__/__generated__/ClientEdgesTest2Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<62fd1751199c25cf4699ec44dc23beb3>> + * @generated SignedSource<<0a4203e6a549343a8efede75168d8328>> * @flow * @lightSyntaxTransform * @nogrep @@ -88,7 +88,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "client_node", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/UserClientEdgeNodeResolver').client_node, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/UserClientEdgeNodeResolver').client_node, "path": "me.client_node" }, "linkedField": { diff --git a/packages/react-relay/__tests__/__generated__/ClientEdgesTest3Query.graphql.js b/packages/react-relay/__tests__/__generated__/ClientEdgesTest3Query.graphql.js index 0e3ffa9289277..59d944bdbbaf6 100644 --- a/packages/react-relay/__tests__/__generated__/ClientEdgesTest3Query.graphql.js +++ b/packages/react-relay/__tests__/__generated__/ClientEdgesTest3Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<3d1c5acd70a753e740b92cee04e932b7>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -90,7 +90,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "client_node", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/UserClientEdgeNodeResolver').client_node, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/UserClientEdgeNodeResolver').client_node, "path": "me.client_node" }, "linkedField": { diff --git a/packages/react-relay/__tests__/__generated__/ClientEdgesTest4Query.graphql.js b/packages/react-relay/__tests__/__generated__/ClientEdgesTest4Query.graphql.js index e1c67d981ac67..78f718fae0526 100644 --- a/packages/react-relay/__tests__/__generated__/ClientEdgesTest4Query.graphql.js +++ b/packages/react-relay/__tests__/__generated__/ClientEdgesTest4Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<758fd1388eef83475d88178f34e78a75>> * @flow * @lightSyntaxTransform * @nogrep @@ -107,7 +107,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "client_object", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/UserClientEdgeClientObjectResolver').client_object, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/UserClientEdgeClientObjectResolver').client_object, "path": "me.client_object", "normalizationInfo": { "kind": "OutputType", diff --git a/packages/react-relay/__tests__/__generated__/ClientEdgesTest5Query.graphql.js b/packages/react-relay/__tests__/__generated__/ClientEdgesTest5Query.graphql.js new file mode 100644 index 0000000000000..93d2d9f2ba9df --- /dev/null +++ b/packages/react-relay/__tests__/__generated__/ClientEdgesTest5Query.graphql.js @@ -0,0 +1,151 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<1125422887ca67ed83a8d5172cf39e99>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ConcreteRequest, Query } from 'relay-runtime'; +import type { DataID } from "relay-runtime"; +import type { ClientEdgesTest5Query_user$fragmentType } from "./ClientEdgesTest5Query_user.graphql"; +import {same_user_client_edge as userSameUserClientEdgeResolverType} from "../ClientEdges-test.js"; +import type { TestResolverContextType } from "../../../relay-runtime/mutations/__tests__/TestResolverContextType"; +// Type assertion validating that `userSameUserClientEdgeResolverType` resolver is correctly implemented. +// A type error here indicates that the type signature of the resolver module is incorrect. +(userSameUserClientEdgeResolverType: ( + args: void, + context: TestResolverContextType, +) => ?{| + +id: DataID, +|}); +export type ClientEdgesTest5Query$variables = {||}; +export type ClientEdgesTest5Query$data = {| + +me: ?{| + +same_user_client_edge: ?{| + +$fragmentSpreads: ClientEdgesTest5Query_user$fragmentType, + |}, + |}, +|}; +export type ClientEdgesTest5Query = {| + response: ClientEdgesTest5Query$data, + variables: ClientEdgesTest5Query$variables, +|}; +*/ + +var node/*: ConcreteRequest*/ = { + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": { + "hasClientEdges": true + }, + "name": "ClientEdgesTest5Query", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "me", + "plural": false, + "selections": [ + { + "kind": "ClientEdgeToServerObject", + "operation": require('./ClientEdgeQuery_ClientEdgesTest5Query_me__same_user_client_edge.graphql'), + "backingField": { + "alias": null, + "args": null, + "fragment": null, + "kind": "RelayResolver", + "name": "same_user_client_edge", + "resolverModule": require('../ClientEdges-test').same_user_client_edge, + "path": "me.same_user_client_edge" + }, + "linkedField": { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "same_user_client_edge", + "plural": false, + "selections": [ + { + "args": null, + "kind": "FragmentSpread", + "name": "ClientEdgesTest5Query_user" + } + ], + "storageKey": null + } + } + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [], + "kind": "Operation", + "name": "ClientEdgesTest5Query", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "me", + "plural": false, + "selections": [ + { + "name": "same_user_client_edge", + "args": null, + "fragment": null, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": false + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "92f648bfaec7c9135a689dd9dd5b3fe4", + "id": null, + "metadata": {}, + "name": "ClientEdgesTest5Query", + "operationKind": "query", + "text": "query ClientEdgesTest5Query {\n me {\n id\n }\n}\n" + } +}; + +if (__DEV__) { + (node/*: any*/).hash = "8274337dee7283631e4c2d3992c0add6"; +} + +module.exports = ((node/*: any*/)/*: Query< + ClientEdgesTest5Query$variables, + ClientEdgesTest5Query$data, +>*/); diff --git a/packages/react-relay/__tests__/__generated__/ClientEdgesTest5Query_user.graphql.js b/packages/react-relay/__tests__/__generated__/ClientEdgesTest5Query_user.graphql.js new file mode 100644 index 0000000000000..0e794ab7be937 --- /dev/null +++ b/packages/react-relay/__tests__/__generated__/ClientEdgesTest5Query_user.graphql.js @@ -0,0 +1,59 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { Fragment, ReaderFragment } from 'relay-runtime'; +import type { FragmentType } from "relay-runtime"; +declare export opaque type ClientEdgesTest5Query_user$fragmentType: FragmentType; +export type ClientEdgesTest5Query_user$data = {| + +name: ?string, + +$fragmentType: ClientEdgesTest5Query_user$fragmentType, +|}; +export type ClientEdgesTest5Query_user$key = { + +$data?: ClientEdgesTest5Query_user$data, + +$fragmentSpreads: ClientEdgesTest5Query_user$fragmentType, + ... +}; +*/ + +var node/*: ReaderFragment*/ = { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "ClientEdgesTest5Query_user", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + } + ], + "type": "User", + "abstractKey": null +}; + +if (__DEV__) { + (node/*: any*/).hash = "4241c685ba9bb5a0aa7810321b0d1b0d"; +} + +module.exports = ((node/*: any*/)/*: Fragment< + ClientEdgesTest5Query_user$fragmentType, + ClientEdgesTest5Query_user$data, +>*/); diff --git a/packages/react-relay/__tests__/__generated__/ClientEdgesTest6Query.graphql.js b/packages/react-relay/__tests__/__generated__/ClientEdgesTest6Query.graphql.js new file mode 100644 index 0000000000000..08126407b5d5b --- /dev/null +++ b/packages/react-relay/__tests__/__generated__/ClientEdgesTest6Query.graphql.js @@ -0,0 +1,167 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<7b8a2fff0ac8a3e5b71333c58dbc823f>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ConcreteRequest, Query } from 'relay-runtime'; +import type { DataID } from "relay-runtime"; +import type { ClientEdgesTestUpperName$key } from "./ClientEdgesTestUpperName.graphql"; +import {same_user_client_edge as userSameUserClientEdgeResolverType} from "../ClientEdges-test.js"; +import type { TestResolverContextType } from "../../../relay-runtime/mutations/__tests__/TestResolverContextType"; +// Type assertion validating that `userSameUserClientEdgeResolverType` resolver is correctly implemented. +// A type error here indicates that the type signature of the resolver module is incorrect. +(userSameUserClientEdgeResolverType: ( + args: void, + context: TestResolverContextType, +) => ?{| + +id: DataID, +|}); +import {upper_name as userUpperNameResolverType} from "../ClientEdges-test.js"; +// Type assertion validating that `userUpperNameResolverType` resolver is correctly implemented. +// A type error here indicates that the type signature of the resolver module is incorrect. +(userUpperNameResolverType: ( + rootKey: ClientEdgesTestUpperName$key, + args: void, + context: TestResolverContextType, +) => ?string); +export type ClientEdgesTest6Query$variables = {||}; +export type ClientEdgesTest6Query$data = {| + +me: ?{| + +same_user_client_edge: ?{| + +upper_name: ?string, + |}, + |}, +|}; +export type ClientEdgesTest6Query = {| + response: ClientEdgesTest6Query$data, + variables: ClientEdgesTest6Query$variables, +|}; +*/ + +var node/*: ConcreteRequest*/ = { + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": { + "hasClientEdges": true + }, + "name": "ClientEdgesTest6Query", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "me", + "plural": false, + "selections": [ + { + "kind": "ClientEdgeToServerObject", + "operation": require('./ClientEdgeQuery_ClientEdgesTest6Query_me__same_user_client_edge.graphql'), + "backingField": { + "alias": null, + "args": null, + "fragment": null, + "kind": "RelayResolver", + "name": "same_user_client_edge", + "resolverModule": require('../ClientEdges-test').same_user_client_edge, + "path": "me.same_user_client_edge" + }, + "linkedField": { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "same_user_client_edge", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "fragment": { + "args": null, + "kind": "FragmentSpread", + "name": "ClientEdgesTestUpperName" + }, + "kind": "RelayResolver", + "name": "upper_name", + "resolverModule": require('../ClientEdges-test').upper_name, + "path": "me.same_user_client_edge.upper_name" + } + ], + "storageKey": null + } + } + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [], + "kind": "Operation", + "name": "ClientEdgesTest6Query", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "me", + "plural": false, + "selections": [ + { + "name": "same_user_client_edge", + "args": null, + "fragment": null, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": false + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "e62271734cfff9eb7b0535fd011e32b3", + "id": null, + "metadata": {}, + "name": "ClientEdgesTest6Query", + "operationKind": "query", + "text": "query ClientEdgesTest6Query {\n me {\n id\n }\n}\n" + } +}; + +if (__DEV__) { + (node/*: any*/).hash = "330a0878ce30575d8c36e2fdd626c833"; +} + +module.exports = ((node/*: any*/)/*: Query< + ClientEdgesTest6Query$variables, + ClientEdgesTest6Query$data, +>*/); diff --git a/packages/react-relay/__tests__/__generated__/ClientEdgesTestUpperName.graphql.js b/packages/react-relay/__tests__/__generated__/ClientEdgesTestUpperName.graphql.js new file mode 100644 index 0000000000000..c7574c6e720fd --- /dev/null +++ b/packages/react-relay/__tests__/__generated__/ClientEdgesTestUpperName.graphql.js @@ -0,0 +1,59 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { Fragment, ReaderFragment } from 'relay-runtime'; +import type { FragmentType } from "relay-runtime"; +declare export opaque type ClientEdgesTestUpperName$fragmentType: FragmentType; +export type ClientEdgesTestUpperName$data = {| + +name: ?string, + +$fragmentType: ClientEdgesTestUpperName$fragmentType, +|}; +export type ClientEdgesTestUpperName$key = { + +$data?: ClientEdgesTestUpperName$data, + +$fragmentSpreads: ClientEdgesTestUpperName$fragmentType, + ... +}; +*/ + +var node/*: ReaderFragment*/ = { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "ClientEdgesTestUpperName", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + } + ], + "type": "User", + "abstractKey": null +}; + +if (__DEV__) { + (node/*: any*/).hash = "be2c514c21045e5df5e947adccc4f146"; +} + +module.exports = ((node/*: any*/)/*: Fragment< + ClientEdgesTestUpperName$fragmentType, + ClientEdgesTestUpperName$data, +>*/); diff --git a/packages/react-relay/__tests__/__generated__/ClientOnlyQueriesTest2Query.graphql.js b/packages/react-relay/__tests__/__generated__/ClientOnlyQueriesTest2Query.graphql.js index 0e00a80e5c98c..e6d7169369cfe 100644 --- a/packages/react-relay/__tests__/__generated__/ClientOnlyQueriesTest2Query.graphql.js +++ b/packages/react-relay/__tests__/__generated__/ClientOnlyQueriesTest2Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<1554ee265adb0687d0ec685b52cf5d93>> + * @generated SignedSource<<30c863335fc36186c9a5448b91b55ce5>> * @flow * @lightSyntaxTransform * @nogrep @@ -62,7 +62,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "hello", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/HelloWorldResolver').hello, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/HelloWorldResolver').hello, "path": "hello" } ] diff --git a/packages/react-relay/__tests__/__generated__/ClientOnlyQueriesTest3Query.graphql.js b/packages/react-relay/__tests__/__generated__/ClientOnlyQueriesTest3Query.graphql.js index 36c904d21a096..c369b4c3bc4d9 100644 --- a/packages/react-relay/__tests__/__generated__/ClientOnlyQueriesTest3Query.graphql.js +++ b/packages/react-relay/__tests__/__generated__/ClientOnlyQueriesTest3Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<6ac5358a7e04f2a07f708afd0062ed45>> * @flow * @lightSyntaxTransform * @nogrep @@ -69,7 +69,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "hello_user", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/HelloUserResolver').hello_user, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/HelloUserResolver').hello_user, "path": "hello_user" }, "linkedField": { diff --git a/packages/react-relay/__tests__/__generated__/ErrorModel____relay_model_instance.graphql.js b/packages/react-relay/__tests__/__generated__/ErrorModel____relay_model_instance.graphql.js index e4895796c5333..5a9166bf53c05 100644 --- a/packages/react-relay/__tests__/__generated__/ErrorModel____relay_model_instance.graphql.js +++ b/packages/react-relay/__tests__/__generated__/ErrorModel____relay_model_instance.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<15fb9013adc7ea5219f3caae94838def>> * @flow * @lightSyntaxTransform * @nogrep @@ -57,7 +57,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "__relay_model_instance", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./ErrorModel__id.graphql'), require('./../RelayResolverNullableModelClientEdge-test').ErrorModel, 'id', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./ErrorModel__id.graphql'), require('../RelayResolverNullableModelClientEdge-test').ErrorModel, 'id', true), "path": "__relay_model_instance" } ], diff --git a/packages/react-relay/__tests__/__generated__/ExampleWithOutputTypeTestQuery.graphql.js b/packages/react-relay/__tests__/__generated__/ExampleWithOutputTypeTestQuery.graphql.js index 7f3f4c8f8968e..efc669431dbc8 100644 --- a/packages/react-relay/__tests__/__generated__/ExampleWithOutputTypeTestQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/ExampleWithOutputTypeTestQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<14d6d8cfc809694e6ed8e47cc2da2588>> * @flow * @lightSyntaxTransform * @nogrep @@ -77,7 +77,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "example_client_object", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/ExampleClientObjectResolver').example_client_object, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/ExampleClientObjectResolver').example_client_object, "path": "example_client_object", "normalizationInfo": { "kind": "OutputType", diff --git a/packages/react-relay/__tests__/__generated__/LiveResolversTest10Query.graphql.js b/packages/react-relay/__tests__/__generated__/LiveResolversTest10Query.graphql.js index eb61ddf79dc4f..957b0dadf757f 100644 --- a/packages/react-relay/__tests__/__generated__/LiveResolversTest10Query.graphql.js +++ b/packages/react-relay/__tests__/__generated__/LiveResolversTest10Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -86,7 +86,7 @@ return { }, "kind": "RelayLiveResolver", "name": "counter", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/LiveCounterResolver').counter, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/LiveCounterResolver').counter, "path": "counter" } ], diff --git a/packages/react-relay/__tests__/__generated__/LiveResolversTest11Query.graphql.js b/packages/react-relay/__tests__/__generated__/LiveResolversTest11Query.graphql.js index 449bc0e42fee8..4c3d7cd591ed3 100644 --- a/packages/react-relay/__tests__/__generated__/LiveResolversTest11Query.graphql.js +++ b/packages/react-relay/__tests__/__generated__/LiveResolversTest11Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<7b2bc5561974409e8a72f16e097b35ed>> + * @generated SignedSource<<0dafcea554bb78f4f9c9a312d11baf49>> * @flow * @lightSyntaxTransform * @nogrep @@ -53,7 +53,7 @@ var node/*: ClientRequest*/ = { "fragment": null, "kind": "RelayLiveResolver", "name": "counter_no_fragment", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/LiveCounterNoFragment').counter_no_fragment, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/LiveCounterNoFragment').counter_no_fragment, "path": "counter_no_fragment" } ] diff --git a/packages/react-relay/__tests__/__generated__/LiveResolversTest12Query.graphql.js b/packages/react-relay/__tests__/__generated__/LiveResolversTest12Query.graphql.js index 30ff9b2cd6e10..336d3fba26639 100644 --- a/packages/react-relay/__tests__/__generated__/LiveResolversTest12Query.graphql.js +++ b/packages/react-relay/__tests__/__generated__/LiveResolversTest12Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<05fef20d7b540ba5972bbc565d97ce97>> * @flow * @lightSyntaxTransform * @nogrep @@ -72,7 +72,7 @@ return { "fragment": null, "kind": "RelayLiveResolver", "name": "counter_no_fragment_with_arg", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/LiveCounterNoFragmentWithArg').counter_no_fragment_with_arg, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/LiveCounterNoFragmentWithArg').counter_no_fragment_with_arg, "path": "counter_no_fragment_with_arg" } ] diff --git a/packages/react-relay/__tests__/__generated__/LiveResolversTest13Query.graphql.js b/packages/react-relay/__tests__/__generated__/LiveResolversTest13Query.graphql.js index 4ee365aef79fa..a94352acb9714 100644 --- a/packages/react-relay/__tests__/__generated__/LiveResolversTest13Query.graphql.js +++ b/packages/react-relay/__tests__/__generated__/LiveResolversTest13Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -59,7 +59,7 @@ var node/*: ClientRequest*/ = { "fragment": null, "kind": "RelayLiveResolver", "name": "live_constant_client_edge", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/LiveConstantClientEdgeResolver').live_constant_client_edge, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/LiveConstantClientEdgeResolver').live_constant_client_edge, "path": "live_constant_client_edge" }, "linkedField": { diff --git a/packages/react-relay/__tests__/__generated__/LiveResolversTest14Query.graphql.js b/packages/react-relay/__tests__/__generated__/LiveResolversTest14Query.graphql.js index 83ecccee8e965..5a4e27a9f4579 100644 --- a/packages/react-relay/__tests__/__generated__/LiveResolversTest14Query.graphql.js +++ b/packages/react-relay/__tests__/__generated__/LiveResolversTest14Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<0c14c3be31c090e2cc855365b75e71a5>> * @flow * @lightSyntaxTransform * @nogrep @@ -76,7 +76,7 @@ return { "fragment": null, "kind": "RelayLiveResolver", "name": "counter_no_fragment", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/LiveCounterNoFragment').counter_no_fragment, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/LiveCounterNoFragment').counter_no_fragment, "path": "counter_no_fragment" } ] diff --git a/packages/react-relay/__tests__/__generated__/LiveResolversTest15Query.graphql.js b/packages/react-relay/__tests__/__generated__/LiveResolversTest15Query.graphql.js index c35e6263ec892..5abafbe90ef3e 100644 --- a/packages/react-relay/__tests__/__generated__/LiveResolversTest15Query.graphql.js +++ b/packages/react-relay/__tests__/__generated__/LiveResolversTest15Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<1f38951a379a1dcc3ddb9a80eef10d5f>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -59,7 +59,7 @@ var node/*: ClientRequest*/ = { "fragment": null, "kind": "RelayLiveResolver", "name": "live_user_resolver_always_suspend", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/LiveUserAlwaysSuspendResolver').live_user_resolver_always_suspend, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/LiveUserAlwaysSuspendResolver').live_user_resolver_always_suspend, "path": "live_user_resolver_always_suspend" }, "linkedField": { diff --git a/packages/react-relay/__tests__/__generated__/LiveResolversTest16Query.graphql.js b/packages/react-relay/__tests__/__generated__/LiveResolversTest16Query.graphql.js index f226269a586c9..ff4122c7411e8 100644 --- a/packages/react-relay/__tests__/__generated__/LiveResolversTest16Query.graphql.js +++ b/packages/react-relay/__tests__/__generated__/LiveResolversTest16Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<68b4dd73b64df5a10b25c2fb419779ce>> + * @generated SignedSource<<1d8b8736bf6b7d27bbf28dbe24ad95a5>> * @flow * @lightSyntaxTransform * @nogrep @@ -53,7 +53,7 @@ var node/*: ClientRequest*/ = { "fragment": null, "kind": "RelayLiveResolver", "name": "live_resolver_with_bad_return_value", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/QueryLiveResolverWithBadReturnValue').live_resolver_with_bad_return_value, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/QueryLiveResolverWithBadReturnValue').live_resolver_with_bad_return_value, "path": "live_resolver_with_bad_return_value" } ] diff --git a/packages/react-relay/__tests__/__generated__/LiveResolversTest17Query.graphql.js b/packages/react-relay/__tests__/__generated__/LiveResolversTest17Query.graphql.js index e88280f58d5dc..804035ca02f9a 100644 --- a/packages/react-relay/__tests__/__generated__/LiveResolversTest17Query.graphql.js +++ b/packages/react-relay/__tests__/__generated__/LiveResolversTest17Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<4b71126dcb1c61308fe27ef0c4a4fe7e>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -52,7 +52,7 @@ var node/*: ClientRequest*/ = { "fragment": null, "kind": "RelayResolver", "name": "non_live_resolver_with_live_return_value", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/QueryNonLiveResolverWithLiveReturnValue').non_live_resolver_with_live_return_value, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/QueryNonLiveResolverWithLiveReturnValue').non_live_resolver_with_live_return_value, "path": "non_live_resolver_with_live_return_value" } ] diff --git a/packages/react-relay/__tests__/__generated__/LiveResolversTest18Query.graphql.js b/packages/react-relay/__tests__/__generated__/LiveResolversTest18Query.graphql.js index 743f38786d7c2..23f7d2794bdb3 100644 --- a/packages/react-relay/__tests__/__generated__/LiveResolversTest18Query.graphql.js +++ b/packages/react-relay/__tests__/__generated__/LiveResolversTest18Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<284691301e02e53dc0d659b29c4dc630>> + * @generated SignedSource<<4bcb30a7af2240162fed8d60f1519a96>> * @flow * @lightSyntaxTransform * @nogrep @@ -53,7 +53,7 @@ var node/*: ClientRequest*/ = { "fragment": null, "kind": "RelayLiveResolver", "name": "live_resolver_throws", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/QueryLiveResolverThrows').live_resolver_throws, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/QueryLiveResolverThrows').live_resolver_throws, "path": "live_resolver_throws" } ] diff --git a/packages/react-relay/__tests__/__generated__/LiveResolversTest19Query.graphql.js b/packages/react-relay/__tests__/__generated__/LiveResolversTest19Query.graphql.js index ca5550b7fd2ef..cb2ddb6fafd36 100644 --- a/packages/react-relay/__tests__/__generated__/LiveResolversTest19Query.graphql.js +++ b/packages/react-relay/__tests__/__generated__/LiveResolversTest19Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -53,7 +53,7 @@ var node/*: ClientRequest*/ = { "fragment": null, "kind": "RelayLiveResolver", "name": "live_resolver_return_undefined", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/QueryLiveResolverReturnsUndefined').live_resolver_return_undefined, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/QueryLiveResolverReturnsUndefined').live_resolver_return_undefined, "path": "live_resolver_return_undefined" } ] diff --git a/packages/react-relay/__tests__/__generated__/LiveResolversTest1Query.graphql.js b/packages/react-relay/__tests__/__generated__/LiveResolversTest1Query.graphql.js index fb482cb77dec5..00a2a63861c3b 100644 --- a/packages/react-relay/__tests__/__generated__/LiveResolversTest1Query.graphql.js +++ b/packages/react-relay/__tests__/__generated__/LiveResolversTest1Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<2ea3301d4cb63bad15352a00c68b89ad>> + * @generated SignedSource<<4702a7dc75e760d382d368c54bd2f53f>> * @flow * @lightSyntaxTransform * @nogrep @@ -56,7 +56,7 @@ var node/*: ConcreteRequest*/ = { }, "kind": "RelayLiveResolver", "name": "counter", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/LiveCounterResolver').counter, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/LiveCounterResolver').counter, "path": "counter" } ], diff --git a/packages/react-relay/__tests__/__generated__/LiveResolversTest2Query.graphql.js b/packages/react-relay/__tests__/__generated__/LiveResolversTest2Query.graphql.js index 9bb6a37ad19d2..aa47b73ea8281 100644 --- a/packages/react-relay/__tests__/__generated__/LiveResolversTest2Query.graphql.js +++ b/packages/react-relay/__tests__/__generated__/LiveResolversTest2Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<14e6972b8b11662d005117fea8033c74>> * @flow * @lightSyntaxTransform * @nogrep @@ -56,7 +56,7 @@ var node/*: ConcreteRequest*/ = { }, "kind": "RelayLiveResolver", "name": "counter", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/LiveCounterResolver').counter, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/LiveCounterResolver').counter, "path": "counter" } ], diff --git a/packages/react-relay/__tests__/__generated__/LiveResolversTest3Query.graphql.js b/packages/react-relay/__tests__/__generated__/LiveResolversTest3Query.graphql.js index c105334b65872..d334a3e6341e7 100644 --- a/packages/react-relay/__tests__/__generated__/LiveResolversTest3Query.graphql.js +++ b/packages/react-relay/__tests__/__generated__/LiveResolversTest3Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<4ca4681983a4310b4d6e2cbc081947b5>> * @flow * @lightSyntaxTransform * @nogrep @@ -55,7 +55,7 @@ var node/*: ConcreteRequest*/ = { }, "kind": "RelayResolver", "name": "counter_plus_one", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/CounterPlusOneResolver').counter_plus_one, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/CounterPlusOneResolver').counter_plus_one, "path": "counter_plus_one" } ], diff --git a/packages/react-relay/__tests__/__generated__/LiveResolversTest4Query.graphql.js b/packages/react-relay/__tests__/__generated__/LiveResolversTest4Query.graphql.js index 140c3773451c1..0abf0afb3a864 100644 --- a/packages/react-relay/__tests__/__generated__/LiveResolversTest4Query.graphql.js +++ b/packages/react-relay/__tests__/__generated__/LiveResolversTest4Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<92a23a8a19aaccfe9a40069c40ae5dd1>> + * @generated SignedSource<<4d77fd2704c7be091d038e5f2d74a80c>> * @flow * @lightSyntaxTransform * @nogrep @@ -56,7 +56,7 @@ var node/*: ConcreteRequest*/ = { }, "kind": "RelayLiveResolver", "name": "ping", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/LivePingPongResolver').ping, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/LivePingPongResolver').ping, "path": "ping" } ], diff --git a/packages/react-relay/__tests__/__generated__/LiveResolversTest5Fragment.graphql.js b/packages/react-relay/__tests__/__generated__/LiveResolversTest5Fragment.graphql.js index 86fd7d0988a1a..b7c0b81bfdc8d 100644 --- a/packages/react-relay/__tests__/__generated__/LiveResolversTest5Fragment.graphql.js +++ b/packages/react-relay/__tests__/__generated__/LiveResolversTest5Fragment.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<8e30dfb8d84acedb4c8dba7270e72168>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -57,7 +57,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayLiveResolver", "name": "counter_suspends_when_odd", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/CounterSuspendsWhenOdd').counter_suspends_when_odd, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/CounterSuspendsWhenOdd').counter_suspends_when_odd, "path": "counter_suspends_when_odd" } ], diff --git a/packages/react-relay/__tests__/__generated__/LiveResolversTest6Fragment.graphql.js b/packages/react-relay/__tests__/__generated__/LiveResolversTest6Fragment.graphql.js index 8debde1ae991b..56689c8c1ffaf 100644 --- a/packages/react-relay/__tests__/__generated__/LiveResolversTest6Fragment.graphql.js +++ b/packages/react-relay/__tests__/__generated__/LiveResolversTest6Fragment.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<6fec78dd40586f408ecb6f7219476367>> * @flow * @lightSyntaxTransform * @nogrep @@ -57,7 +57,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "user_name_and_counter_suspends_when_odd", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/UserNameAndCounterSuspendsWhenOdd').user_name_and_counter_suspends_when_odd, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/UserNameAndCounterSuspendsWhenOdd').user_name_and_counter_suspends_when_odd, "path": "user_name_and_counter_suspends_when_odd" } ], diff --git a/packages/react-relay/__tests__/__generated__/LiveResolversTest7Query.graphql.js b/packages/react-relay/__tests__/__generated__/LiveResolversTest7Query.graphql.js index 676f8e5a058d5..4cece77e7fd65 100644 --- a/packages/react-relay/__tests__/__generated__/LiveResolversTest7Query.graphql.js +++ b/packages/react-relay/__tests__/__generated__/LiveResolversTest7Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<1fcac115525fba26301ead3a213b084e>> + * @generated SignedSource<<9ad0c77d211333deb1b74114aa00f4d4>> * @flow * @lightSyntaxTransform * @nogrep @@ -110,7 +110,7 @@ return { }, "kind": "RelayLiveResolver", "name": "user_profile_picture_uri_suspends_when_the_counter_is_odd", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/UserProfilePictureUriSuspendsWhenTheCounterIsOdd').user_profile_picture_uri_suspends_when_the_counter_is_odd, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/UserProfilePictureUriSuspendsWhenTheCounterIsOdd').user_profile_picture_uri_suspends_when_the_counter_is_odd, "path": "node.user_profile_picture_uri_suspends_when_the_counter_is_odd" } ], diff --git a/packages/react-relay/__tests__/__generated__/LiveResolversTest8Query.graphql.js b/packages/react-relay/__tests__/__generated__/LiveResolversTest8Query.graphql.js index 23bbe23d49902..a85dca14f0db0 100644 --- a/packages/react-relay/__tests__/__generated__/LiveResolversTest8Query.graphql.js +++ b/packages/react-relay/__tests__/__generated__/LiveResolversTest8Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<7d6c14f61090bb9548bed957397de1ca>> * @flow * @lightSyntaxTransform * @nogrep @@ -95,7 +95,7 @@ return { }, "kind": "RelayLiveResolver", "name": "resolver_that_throws", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/ResolverThatThrows').resolver_that_throws, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/ResolverThatThrows').resolver_that_throws, "path": "node.resolver_that_throws" } ], diff --git a/packages/react-relay/__tests__/__generated__/LiveResolversTest9Query.graphql.js b/packages/react-relay/__tests__/__generated__/LiveResolversTest9Query.graphql.js index a7998b2335310..06800b6fb2f5f 100644 --- a/packages/react-relay/__tests__/__generated__/LiveResolversTest9Query.graphql.js +++ b/packages/react-relay/__tests__/__generated__/LiveResolversTest9Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<2a9e3a2a16377a56464e40f1f5055284>> * @flow * @lightSyntaxTransform * @nogrep @@ -101,7 +101,7 @@ return { }, "kind": "RelayLiveResolver", "name": "user_profile_picture_uri_suspends_when_the_counter_is_odd", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/UserProfilePictureUriSuspendsWhenTheCounterIsOdd').user_profile_picture_uri_suspends_when_the_counter_is_odd, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/UserProfilePictureUriSuspendsWhenTheCounterIsOdd').user_profile_picture_uri_suspends_when_the_counter_is_odd, "path": "node.profile_picture_uri" } ], diff --git a/packages/react-relay/__tests__/__generated__/LiveResolversTestCounterContextBaseQuery.graphql.js b/packages/react-relay/__tests__/__generated__/LiveResolversTestCounterContextBaseQuery.graphql.js new file mode 100644 index 0000000000000..2b774de65c2c9 --- /dev/null +++ b/packages/react-relay/__tests__/__generated__/LiveResolversTestCounterContextBaseQuery.graphql.js @@ -0,0 +1,176 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<037817371343c160146009e57a039cfd>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ClientRequest, ClientQuery } from 'relay-runtime'; +import type { LiveState } from "relay-runtime"; +import type { BaseCounter____relay_model_instance$data } from "./../../../relay-runtime/store/__tests__/resolvers/__generated__/BaseCounter____relay_model_instance.graphql"; +import {count_plus_one as baseCounterCountPlusOneResolverType} from "../../../relay-runtime/store/__tests__/resolvers/LiveCounterContextResolver.js"; +import type { TestResolverContextType } from "../../../relay-runtime/mutations/__tests__/TestResolverContextType"; +// Type assertion validating that `baseCounterCountPlusOneResolverType` resolver is correctly implemented. +// A type error here indicates that the type signature of the resolver module is incorrect. +(baseCounterCountPlusOneResolverType: ( + __relay_model_instance: BaseCounter____relay_model_instance$data['__relay_model_instance'], + args: void, + context: TestResolverContextType, +) => LiveState); +import {base_counter_context as queryBaseCounterContextResolverType} from "../../../relay-runtime/store/__tests__/resolvers/LiveCounterContextResolver.js"; +// Type assertion validating that `queryBaseCounterContextResolverType` resolver is correctly implemented. +// A type error here indicates that the type signature of the resolver module is incorrect. +(queryBaseCounterContextResolverType: ( + args: void, + context: TestResolverContextType, +) => LiveState); +import type { BaseCounter } from "../../../relay-runtime/store/__tests__/resolvers/LiveCounterContextResolver.js"; +export type LiveResolversTestCounterContextBaseQuery$variables = {||}; +export type LiveResolversTestCounterContextBaseQuery$data = {| + +base_counter_context: ?{| + +count_plus_one: ?number, + |}, +|}; +export type LiveResolversTestCounterContextBaseQuery = {| + response: LiveResolversTestCounterContextBaseQuery$data, + variables: LiveResolversTestCounterContextBaseQuery$variables, +|}; +*/ + +var node/*: ClientRequest*/ = { + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": { + "hasClientEdges": true + }, + "name": "LiveResolversTestCounterContextBaseQuery", + "selections": [ + { + "kind": "ClientEdgeToClientObject", + "concreteType": "BaseCounter", + "modelResolvers": null, + "backingField": { + "alias": null, + "args": null, + "fragment": null, + "kind": "RelayLiveResolver", + "name": "base_counter_context", + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/LiveCounterContextResolver').base_counter_context, + "path": "base_counter_context", + "normalizationInfo": { + "kind": "WeakModel", + "concreteType": "BaseCounter", + "plural": false + } + }, + "linkedField": { + "alias": null, + "args": null, + "concreteType": "BaseCounter", + "kind": "LinkedField", + "name": "base_counter_context", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "fragment": { + "args": null, + "kind": "FragmentSpread", + "name": "BaseCounter____relay_model_instance" + }, + "kind": "RelayLiveResolver", + "name": "count_plus_one", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/BaseCounter____relay_model_instance.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/LiveCounterContextResolver').count_plus_one, '__relay_model_instance', true), + "path": "base_counter_context.count_plus_one" + } + ], + "storageKey": null + } + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [], + "kind": "Operation", + "name": "LiveResolversTestCounterContextBaseQuery", + "selections": [ + { + "kind": "ClientEdgeToClientObject", + "backingField": { + "name": "base_counter_context", + "args": null, + "fragment": null, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": true + }, + "linkedField": { + "alias": null, + "args": null, + "concreteType": "BaseCounter", + "kind": "LinkedField", + "name": "base_counter_context", + "plural": false, + "selections": [ + { + "name": "count_plus_one", + "args": null, + "fragment": { + "kind": "InlineFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__relay_model_instance", + "storageKey": null + } + ], + "type": "BaseCounter", + "abstractKey": null + }, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": true + } + ], + "storageKey": null + } + } + ] + }, + "params": { + "cacheID": "97f0edeb9700f58debe76b3ab0ca4f82", + "id": null, + "metadata": {}, + "name": "LiveResolversTestCounterContextBaseQuery", + "operationKind": "query", + "text": null + } +}; + +if (__DEV__) { + (node/*: any*/).hash = "a7ba71ed7b3caaefa42c8686bc81819c"; +} + +module.exports = ((node/*: any*/)/*: ClientQuery< + LiveResolversTestCounterContextBaseQuery$variables, + LiveResolversTestCounterContextBaseQuery$data, +>*/); diff --git a/packages/react-relay/__tests__/__generated__/LiveResolversTestCounterContextQuery.graphql.js b/packages/react-relay/__tests__/__generated__/LiveResolversTestCounterContextQuery.graphql.js index 2cf23c5590f6e..b107d88dd3a5e 100644 --- a/packages/react-relay/__tests__/__generated__/LiveResolversTestCounterContextQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/LiveResolversTestCounterContextQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<195a6e5b1e17157d0d5f46baff77c69e>> + * @generated SignedSource<<44ecd07b6b22d9bf7257979f562cd28a>> * @flow * @lightSyntaxTransform * @nogrep @@ -53,7 +53,7 @@ var node/*: ClientRequest*/ = { "fragment": null, "kind": "RelayLiveResolver", "name": "counter_context", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/LiveCounterContextResolver').counter_context, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/LiveCounterContextResolver').counter_context, "path": "counter_context" } ] diff --git a/packages/react-relay/__tests__/__generated__/LiveResolversTestCounterUserFragment.graphql.js b/packages/react-relay/__tests__/__generated__/LiveResolversTestCounterUserFragment.graphql.js index be455d1887f8c..ac3019d3e5120 100644 --- a/packages/react-relay/__tests__/__generated__/LiveResolversTestCounterUserFragment.graphql.js +++ b/packages/react-relay/__tests__/__generated__/LiveResolversTestCounterUserFragment.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<4b212074abe6b9279f28a21273aea562>> + * @generated SignedSource<<6c2a76037cf9638cbf0b23ed551c0076>> * @flow * @lightSyntaxTransform * @nogrep @@ -54,7 +54,7 @@ var node/*: ReaderFragment*/ = { "fragment": null, "kind": "RelayLiveResolver", "name": "counter_suspends_when_odd", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/CounterSuspendsWhenOddOnUser').counter_suspends_when_odd, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/CounterSuspendsWhenOddOnUser').counter_suspends_when_odd, "path": "counter_suspends_when_odd" } ] diff --git a/packages/react-relay/__tests__/__generated__/LiveResolversTestDeferFragment.graphql.js b/packages/react-relay/__tests__/__generated__/LiveResolversTestDeferFragment.graphql.js new file mode 100644 index 0000000000000..5bfb104ad760d --- /dev/null +++ b/packages/react-relay/__tests__/__generated__/LiveResolversTestDeferFragment.graphql.js @@ -0,0 +1,75 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<2cff16ba28615a46e0243f5f23a8da64>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { Fragment, ReaderFragment } from 'relay-runtime'; +import type { CounterSuspendsWhenOdd$key } from "./../../../relay-runtime/store/__tests__/resolvers/__generated__/CounterSuspendsWhenOdd.graphql"; +import type { LiveState, FragmentType } from "relay-runtime"; +import {counter_suspends_when_odd as queryCounterSuspendsWhenOddResolverType} from "../../../relay-runtime/store/__tests__/resolvers/CounterSuspendsWhenOdd.js"; +import type { TestResolverContextType } from "../../../relay-runtime/mutations/__tests__/TestResolverContextType"; +// Type assertion validating that `queryCounterSuspendsWhenOddResolverType` resolver is correctly implemented. +// A type error here indicates that the type signature of the resolver module is incorrect. +(queryCounterSuspendsWhenOddResolverType: ( + rootKey: CounterSuspendsWhenOdd$key, + args: void, + context: TestResolverContextType, +) => LiveState); +declare export opaque type LiveResolversTestDeferFragment$fragmentType: FragmentType; +export type LiveResolversTestDeferFragment$data = {| + +counter_suspends_when_odd: ?number, + +$fragmentType: LiveResolversTestDeferFragment$fragmentType, +|}; +export type LiveResolversTestDeferFragment$key = { + +$data?: LiveResolversTestDeferFragment$data, + +$fragmentSpreads: LiveResolversTestDeferFragment$fragmentType, + ... +}; +*/ + +var node/*: ReaderFragment*/ = { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "LiveResolversTestDeferFragment", + "selections": [ + { + "alias": null, + "args": null, + "fragment": { + "args": null, + "kind": "FragmentSpread", + "name": "CounterSuspendsWhenOdd" + }, + "kind": "RelayLiveResolver", + "name": "counter_suspends_when_odd", + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/CounterSuspendsWhenOdd').counter_suspends_when_odd, + "path": "counter_suspends_when_odd" + } + ], + "type": "Query", + "abstractKey": null +}; + +if (__DEV__) { + (node/*: any*/).hash = "b0021654a94dd7cd7005caf576f7ba36"; +} + +module.exports = ((node/*: any*/)/*: Fragment< + LiveResolversTestDeferFragment$fragmentType, + LiveResolversTestDeferFragment$data, +>*/); diff --git a/packages/react-relay/__tests__/__generated__/LiveResolversTestDeferQuery.graphql.js b/packages/react-relay/__tests__/__generated__/LiveResolversTestDeferQuery.graphql.js new file mode 100644 index 0000000000000..c54c94bc834fd --- /dev/null +++ b/packages/react-relay/__tests__/__generated__/LiveResolversTestDeferQuery.graphql.js @@ -0,0 +1,129 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<87df34e52623f9638b06152e37b9591c>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ConcreteRequest, Query } from 'relay-runtime'; +import type { LiveResolversTestDeferFragment$fragmentType } from "./LiveResolversTestDeferFragment.graphql"; +export type LiveResolversTestDeferQuery$variables = {||}; +export type LiveResolversTestDeferQuery$data = {| + +$fragmentSpreads: LiveResolversTestDeferFragment$fragmentType, +|}; +export type LiveResolversTestDeferQuery = {| + response: LiveResolversTestDeferQuery$data, + variables: LiveResolversTestDeferQuery$variables, +|}; +*/ + +var node/*: ConcreteRequest*/ = { + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "LiveResolversTestDeferQuery", + "selections": [ + { + "kind": "Defer", + "selections": [ + { + "args": null, + "kind": "FragmentSpread", + "name": "LiveResolversTestDeferFragment" + } + ] + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [], + "kind": "Operation", + "name": "LiveResolversTestDeferQuery", + "selections": [ + { + "if": null, + "kind": "Defer", + "label": "LiveResolversTestDeferQuery$defer$LiveResolversTestDeferFragment", + "selections": [ + { + "name": "counter_suspends_when_odd", + "args": null, + "fragment": { + "kind": "InlineFragment", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "me", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + }, + { + "kind": "ClientExtension", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__id", + "storageKey": null + } + ] + } + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": true + } + ] + } + ] + }, + "params": { + "cacheID": "3d21f7198375d03268b15f6dd226cffd", + "id": null, + "metadata": {}, + "name": "LiveResolversTestDeferQuery", + "operationKind": "query", + "text": "query LiveResolversTestDeferQuery {\n ...LiveResolversTestDeferFragment @defer(label: \"LiveResolversTestDeferQuery$defer$LiveResolversTestDeferFragment\")\n}\n\nfragment CounterSuspendsWhenOdd on Query {\n me {\n id\n }\n}\n\nfragment LiveResolversTestDeferFragment on Query {\n ...CounterSuspendsWhenOdd\n}\n" + } +}; + +if (__DEV__) { + (node/*: any*/).hash = "ae82e55c303b6b124964901fdf647b8a"; +} + +module.exports = ((node/*: any*/)/*: Query< + LiveResolversTestDeferQuery$variables, + LiveResolversTestDeferQuery$data, +>*/); diff --git a/packages/react-relay/__tests__/__generated__/LiveResolversTestLiveResolverSuspenseQuery.graphql.js b/packages/react-relay/__tests__/__generated__/LiveResolversTestLiveResolverSuspenseQuery.graphql.js index effbd0722a5a9..9fc8c9646fe6e 100644 --- a/packages/react-relay/__tests__/__generated__/LiveResolversTestLiveResolverSuspenseQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/LiveResolversTestLiveResolverSuspenseQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<9c526503810e8356431d34d4bb60779a>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -140,7 +140,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "068b94edeee505324b2540cd3f157307"; + (node/*: any*/).hash = "d8986066eeaf1dab3efb1495e3820b43"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/__tests__/__generated__/LiveResolversTestNestedQuery.graphql.js b/packages/react-relay/__tests__/__generated__/LiveResolversTestNestedQuery.graphql.js index 92b812b446044..46c11c140b147 100644 --- a/packages/react-relay/__tests__/__generated__/LiveResolversTestNestedQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/LiveResolversTestNestedQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<83196c7b30b5c44e4136ad1a83091ee2>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -106,7 +106,7 @@ return { }, "kind": "RelayResolver", "name": "outer", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/OuterResolver').outer, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/OuterResolver').outer, "path": "outer" }, { @@ -119,7 +119,7 @@ return { }, "kind": "RelayLiveResolver", "name": "inner", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/InnerResolver').inner, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/InnerResolver').inner, "path": "inner" } ], diff --git a/packages/react-relay/__tests__/__generated__/LiveResolversTestOptimisticUpdateQuery.graphql.js b/packages/react-relay/__tests__/__generated__/LiveResolversTestOptimisticUpdateQuery.graphql.js index 587948f072598..cd01f7d5a1a6b 100644 --- a/packages/react-relay/__tests__/__generated__/LiveResolversTestOptimisticUpdateQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/LiveResolversTestOptimisticUpdateQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<4ce8df86da9640f07c72fd372c91e70f>> * @flow * @lightSyntaxTransform * @nogrep @@ -56,7 +56,7 @@ var node/*: ConcreteRequest*/ = { }, "kind": "RelayLiveResolver", "name": "counter", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/LiveCounterResolver').counter, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/LiveCounterResolver').counter, "path": "counter" } ], diff --git a/packages/react-relay/__tests__/__generated__/LiveResolversTestWithContextObjectQuery.graphql.js b/packages/react-relay/__tests__/__generated__/LiveResolversTestWithContextObjectQuery.graphql.js index 97f89e9872a0c..0b897c3a4f456 100644 --- a/packages/react-relay/__tests__/__generated__/LiveResolversTestWithContextObjectQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/LiveResolversTestWithContextObjectQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<1249882b07a43e9d88272854b8fc97f3>> * @flow * @lightSyntaxTransform * @nogrep @@ -53,7 +53,7 @@ var node/*: ClientRequest*/ = { "fragment": null, "kind": "RelayLiveResolver", "name": "hello_world_with_context_object", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/HelloWorldResolverWithContextObject').hello_world_with_context_object, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/HelloWorldResolverWithContextObject').hello_world_with_context_object, "path": "hello_world_with_context_object" } ] diff --git a/packages/react-relay/__tests__/__generated__/LiveResolversTestWithContextQuery.graphql.js b/packages/react-relay/__tests__/__generated__/LiveResolversTestWithContextQuery.graphql.js index e5c9a0f767bee..3e047b0ac66c8 100644 --- a/packages/react-relay/__tests__/__generated__/LiveResolversTestWithContextQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/LiveResolversTestWithContextQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<7318e97bc9c3513421f3a6de2505783a>> + * @generated SignedSource<<1e9947dd69785c5b415a9173cb9c2e08>> * @flow * @lightSyntaxTransform * @nogrep @@ -53,7 +53,7 @@ var node/*: ClientRequest*/ = { "fragment": null, "kind": "RelayLiveResolver", "name": "hello_world_with_context", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/HelloWorldResolverWithContext').hello_world_with_context, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/HelloWorldResolverWithContext').hello_world_with_context, "path": "hello_world_with_context" } ] diff --git a/packages/react-relay/__tests__/__generated__/LiveResolversTestWithGCCounterQuery.graphql.js b/packages/react-relay/__tests__/__generated__/LiveResolversTestWithGCCounterQuery.graphql.js index 3d8082b525687..4616f55e1c9ea 100644 --- a/packages/react-relay/__tests__/__generated__/LiveResolversTestWithGCCounterQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/LiveResolversTestWithGCCounterQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<283279d2524b0ad9ba7176c08100dfd6>> + * @generated SignedSource<<1cb00a974cdb5931f2d6448bc6468c96>> * @flow * @lightSyntaxTransform * @nogrep @@ -53,7 +53,7 @@ var node/*: ClientRequest*/ = { "fragment": null, "kind": "RelayLiveResolver", "name": "counter_no_fragment", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/LiveCounterNoFragment').counter_no_fragment, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/LiveCounterNoFragment').counter_no_fragment, "path": "counter_no_fragment" } ] diff --git a/packages/react-relay/__tests__/__generated__/LiveResolversTestWithGCQuery.graphql.js b/packages/react-relay/__tests__/__generated__/LiveResolversTestWithGCQuery.graphql.js index ad1585999dcff..eb4780a5c9e40 100644 --- a/packages/react-relay/__tests__/__generated__/LiveResolversTestWithGCQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/LiveResolversTestWithGCQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<97bacf905cc0b47154e384233004dc8a>> + * @generated SignedSource<<02cb2d2d39e4dc014fbd31a92ad39b59>> * @flow * @lightSyntaxTransform * @nogrep @@ -56,7 +56,7 @@ var node/*: ConcreteRequest*/ = { }, "kind": "RelayLiveResolver", "name": "live_counter_with_possible_missing_fragment_data", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/LiveCounterWithPossibleMissingFragmentDataResolver').live_counter_with_possible_missing_fragment_data, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/LiveCounterWithPossibleMissingFragmentDataResolver').live_counter_with_possible_missing_fragment_data, "path": "live_counter_with_possible_missing_fragment_data" } ], diff --git a/packages/react-relay/__tests__/__generated__/LiveResolversTestWithProvidedVariablesQuery.graphql.js b/packages/react-relay/__tests__/__generated__/LiveResolversTestWithProvidedVariablesQuery.graphql.js index b746851e5a09b..404d3abbc1c56 100644 --- a/packages/react-relay/__tests__/__generated__/LiveResolversTestWithProvidedVariablesQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/LiveResolversTestWithProvidedVariablesQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<5ebfebc869324c761e909fb1b7e1e7f3>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -37,7 +37,7 @@ export type LiveResolversTestWithProvidedVariablesQuery = {| variables: LiveResolversTestWithProvidedVariablesQuery$variables, |}; ({ - "__relay_internal__pv__HelloWorldProviderrelayprovider": require('./../../../relay-runtime/store/__tests__/resolvers/HelloWorldProvider.relayprovider') + "__relay_internal__pv__HelloWorldProviderrelayprovider": require('../../../relay-runtime/store/__tests__/resolvers/HelloWorldProvider.relayprovider') }: {| +__relay_internal__pv__HelloWorldProviderrelayprovider: {| +get: () => string, @@ -62,7 +62,7 @@ var node/*: ClientRequest*/ = { }, "kind": "RelayResolver", "name": "hello_world_with_provided_variable", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/HelloWorldResolverWithProvidedVariable').hello_world_with_provided_variable, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/HelloWorldResolverWithProvidedVariable').hello_world_with_provided_variable, "path": "hello_world_with_provided_variable" } ], @@ -124,7 +124,7 @@ var node/*: ClientRequest*/ = { "operationKind": "query", "text": null, "providedVariables": { - "__relay_internal__pv__HelloWorldProviderrelayprovider": require('./../../../relay-runtime/store/__tests__/resolvers/HelloWorldProvider.relayprovider') + "__relay_internal__pv__HelloWorldProviderrelayprovider": require('../../../relay-runtime/store/__tests__/resolvers/HelloWorldProvider.relayprovider') } } }; diff --git a/packages/react-relay/__tests__/__generated__/QueryResourceClientEdgesTest2Query.graphql.js b/packages/react-relay/__tests__/__generated__/QueryResourceClientEdgesTest2Query.graphql.js index 6e5d8994c89d3..048ef7297c1f0 100644 --- a/packages/react-relay/__tests__/__generated__/QueryResourceClientEdgesTest2Query.graphql.js +++ b/packages/react-relay/__tests__/__generated__/QueryResourceClientEdgesTest2Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -77,7 +77,7 @@ var node/*: ConcreteRequest*/ = { }, "kind": "RelayResolver", "name": "client_edge", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/UserClientEdgeResolver').client_edge, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/UserClientEdgeResolver').client_edge, "path": "me.client_edge" }, "linkedField": { diff --git a/packages/react-relay/__tests__/__generated__/ReactRelayFragmentContainerReactDoubleEffectsTestUserQuery.graphql.js b/packages/react-relay/__tests__/__generated__/ReactRelayFragmentContainerReactDoubleEffectsTestUserQuery.graphql.js index 8ea18f92ca076..48a94a2e13f62 100644 --- a/packages/react-relay/__tests__/__generated__/ReactRelayFragmentContainerReactDoubleEffectsTestUserQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/ReactRelayFragmentContainerReactDoubleEffectsTestUserQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<69d8411257601e1142fecb6b2c50b44a>> * @flow * @lightSyntaxTransform * @nogrep @@ -134,7 +134,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "b3bff2e0a46eaf7e4382f6fbf75d02ac"; + (node/*: any*/).hash = "2267a7b5958025ee93de3427d77a90ac"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/__tests__/__generated__/ReactRelayFragmentContainerTestUserQuery.graphql.js b/packages/react-relay/__tests__/__generated__/ReactRelayFragmentContainerTestUserQuery.graphql.js index a5fc17334a515..f5fa41a2bb191 100644 --- a/packages/react-relay/__tests__/__generated__/ReactRelayFragmentContainerTestUserQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/ReactRelayFragmentContainerTestUserQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<5d3dadb543af02911aa402ccea0c3da4>> + * @generated SignedSource<<9088238905a709c5e8e410a00f06fb63>> * @flow * @lightSyntaxTransform * @nogrep @@ -134,7 +134,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "0e22a390b8b36d761b73909bcdbaf606"; + (node/*: any*/).hash = "77aa337a1abdd22ab4e7d85da3ffc8c6"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/__tests__/__generated__/ReactRelayFragmentContainerTestUserWithCondQuery.graphql.js b/packages/react-relay/__tests__/__generated__/ReactRelayFragmentContainerTestUserWithCondQuery.graphql.js index b5b1f12241e0b..b6a538bde191f 100644 --- a/packages/react-relay/__tests__/__generated__/ReactRelayFragmentContainerTestUserWithCondQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/ReactRelayFragmentContainerTestUserWithCondQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<6b2101bac612118a648c66c84db7de1e>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -157,7 +157,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "810e07ba9d2d6d4dd6194dd3f49b1211"; + (node/*: any*/).hash = "236716637f5e84cb62c420776a4d9838"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/__tests__/__generated__/ReactRelayFragmentContainerWithFragmentOwnershipTestUserQuery.graphql.js b/packages/react-relay/__tests__/__generated__/ReactRelayFragmentContainerWithFragmentOwnershipTestUserQuery.graphql.js index bcfd152098240..4f7880545f0dd 100644 --- a/packages/react-relay/__tests__/__generated__/ReactRelayFragmentContainerWithFragmentOwnershipTestUserQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/ReactRelayFragmentContainerWithFragmentOwnershipTestUserQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<32a25b5f1bc39fd59c49d7e54abd07e1>> + * @generated SignedSource<<9b5834e69eb9329b872f158917a0fd48>> * @flow * @lightSyntaxTransform * @nogrep @@ -141,7 +141,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "20539d35a38e31c9ccbdd6b4d54aec21"; + (node/*: any*/).hash = "93b6d7b88ae03eb88e1eb60be7cb244d"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/__tests__/__generated__/ReactRelayFragmentContainerWithFragmentOwnershipTestWithCondUserQuery.graphql.js b/packages/react-relay/__tests__/__generated__/ReactRelayFragmentContainerWithFragmentOwnershipTestWithCondUserQuery.graphql.js index 4db44be34036f..f8e62a3de2553 100644 --- a/packages/react-relay/__tests__/__generated__/ReactRelayFragmentContainerWithFragmentOwnershipTestWithCondUserQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/ReactRelayFragmentContainerWithFragmentOwnershipTestWithCondUserQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<98c104eef25d992b6fce2fad458e9b79>> + * @generated SignedSource<<885d6675bd00f1ad0bbefc43c0f7595d>> * @flow * @lightSyntaxTransform * @nogrep @@ -164,7 +164,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "577a07568a7abf3e31c0e6f44fb64a8d"; + (node/*: any*/).hash = "02786543a0348beed6020ab497c904e6"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/__tests__/__generated__/ReactRelayLocalQueryRendererTestUserQuery.graphql.js b/packages/react-relay/__tests__/__generated__/ReactRelayLocalQueryRendererTestUserQuery.graphql.js index 768134c186a02..a01ab54e91e02 100644 --- a/packages/react-relay/__tests__/__generated__/ReactRelayLocalQueryRendererTestUserQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/ReactRelayLocalQueryRendererTestUserQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<96724d396fb1effe971de29910a1d9f4>> + * @generated SignedSource<<13dfe6bf725d6392e60940fad4056ebe>> * @flow * @lightSyntaxTransform * @nogrep @@ -154,7 +154,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "f46ad8fa64eb4b5933ed039b4cc94e16"; + (node/*: any*/).hash = "24c704b361b5a05121618dff23ef81f9"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/__tests__/__generated__/ReactRelayPaginationContainerReactDoubleEffectsTestUserQuery.graphql.js b/packages/react-relay/__tests__/__generated__/ReactRelayPaginationContainerReactDoubleEffectsTestUserQuery.graphql.js index 59dd9badc7e74..a2ec729c43b92 100644 --- a/packages/react-relay/__tests__/__generated__/ReactRelayPaginationContainerReactDoubleEffectsTestUserQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/ReactRelayPaginationContainerReactDoubleEffectsTestUserQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -231,7 +231,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "9abbd5101c0d2ac96fcf72a41c26a948"; + (node/*: any*/).hash = "1115325f54d6d9cdc55d128aa5647048"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/__tests__/__generated__/ReactRelayPaginationContainerTestNoConnectionOnFragmentUserQuery.graphql.js b/packages/react-relay/__tests__/__generated__/ReactRelayPaginationContainerTestNoConnectionOnFragmentUserQuery.graphql.js index 13c9bc9349118..f78e9ca534d55 100644 --- a/packages/react-relay/__tests__/__generated__/ReactRelayPaginationContainerTestNoConnectionOnFragmentUserQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/ReactRelayPaginationContainerTestNoConnectionOnFragmentUserQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<291f86500cd237cbb5e4f56c15d52e78>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -288,7 +288,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "16cf027e832c7432065f4965429431c0"; + (node/*: any*/).hash = "66bbe4963a808c62bf186f33395775b7"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/__tests__/__generated__/ReactRelayPaginationContainerTestNoConnectionUserQuery.graphql.js b/packages/react-relay/__tests__/__generated__/ReactRelayPaginationContainerTestNoConnectionUserQuery.graphql.js index 7901f02d3e95e..d8fafecf02d46 100644 --- a/packages/react-relay/__tests__/__generated__/ReactRelayPaginationContainerTestNoConnectionUserQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/ReactRelayPaginationContainerTestNoConnectionUserQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<05b2793371ff21dc1a2523949eaedeeb>> * @flow * @lightSyntaxTransform * @nogrep @@ -223,7 +223,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "87f6a6ca60ee4bf34ab055283ec5b341"; + (node/*: any*/).hash = "0c78e21bfeadc7de224add3a607c917f"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/__tests__/__generated__/ReactRelayPaginationContainerTestUserQuery.graphql.js b/packages/react-relay/__tests__/__generated__/ReactRelayPaginationContainerTestUserQuery.graphql.js index 8766c7bc38535..5ddfd631ad6b1 100644 --- a/packages/react-relay/__tests__/__generated__/ReactRelayPaginationContainerTestUserQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/ReactRelayPaginationContainerTestUserQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<40317e714e5cb9a352d44ac9057da374>> + * @generated SignedSource<<1d4e247dea3b7195e03e16e4673cf51c>> * @flow * @lightSyntaxTransform * @nogrep @@ -276,7 +276,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "50a3bd82c5fad5ead1ce004df8427725"; + (node/*: any*/).hash = "f780c50ab163e4a29c81eb497cfd5d1b"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/__tests__/__generated__/ReactRelayPaginationContainerWithFragmentOwnershipTestUserQuery.graphql.js b/packages/react-relay/__tests__/__generated__/ReactRelayPaginationContainerWithFragmentOwnershipTestUserQuery.graphql.js index be3f0f3e3b5fb..7fc7398bf4959 100644 --- a/packages/react-relay/__tests__/__generated__/ReactRelayPaginationContainerWithFragmentOwnershipTestUserQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/ReactRelayPaginationContainerWithFragmentOwnershipTestUserQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<949c292c388e628caec8bca19f66fbef>> + * @generated SignedSource<<830ef30aafdb7a89e6f86de3cb2142fa>> * @flow * @lightSyntaxTransform * @nogrep @@ -290,7 +290,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "46153f91be065033bb3e83f093819f1b"; + (node/*: any*/).hash = "e9d0c6d0cb6439a0d13db8521b4cefe3"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/__tests__/__generated__/ReactRelayQueryRendererTestQuery.graphql.js b/packages/react-relay/__tests__/__generated__/ReactRelayQueryRendererTestQuery.graphql.js index 3d1ee97ed94d4..3b1d27ade0546 100644 --- a/packages/react-relay/__tests__/__generated__/ReactRelayQueryRendererTestQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/ReactRelayQueryRendererTestQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<8cc908af3948ba35bc9a4a3d9b018b0b>> + * @generated SignedSource<<640d3b690af15282d859679fdde281e6>> * @flow * @lightSyntaxTransform * @nogrep @@ -137,7 +137,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "868a762b987be24c755ff000a86baacc"; + (node/*: any*/).hash = "9fa9b4608e79feda0a43038cfc0fa816"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/__tests__/__generated__/ReactRelayRefetchContainerReactDoubleEffectsTestUserQuery.graphql.js b/packages/react-relay/__tests__/__generated__/ReactRelayRefetchContainerReactDoubleEffectsTestUserQuery.graphql.js index c8ce092bbc1ab..b851faab1aba2 100644 --- a/packages/react-relay/__tests__/__generated__/ReactRelayRefetchContainerReactDoubleEffectsTestUserQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/ReactRelayRefetchContainerReactDoubleEffectsTestUserQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<59211d841db5434330d8141df7099a3f>> + * @generated SignedSource<<487ca659e859dc2f0d25c7c10580f904>> * @flow * @lightSyntaxTransform * @nogrep @@ -134,7 +134,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "672ea26a52c353b070b2114ac3dedb53"; + (node/*: any*/).hash = "88ddbf011c327ab1ad2e4ff9ab9012e7"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/__tests__/__generated__/ReactRelayRefetchContainerTestUserQuery.graphql.js b/packages/react-relay/__tests__/__generated__/ReactRelayRefetchContainerTestUserQuery.graphql.js index 3ee49392e7386..7bd3938b9fe05 100644 --- a/packages/react-relay/__tests__/__generated__/ReactRelayRefetchContainerTestUserQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/ReactRelayRefetchContainerTestUserQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<738b3c6ab445a73362b4634006c5e06c>> * @flow * @lightSyntaxTransform * @nogrep @@ -134,7 +134,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "699fa1e4a00f325e18b50aa63eb635f6"; + (node/*: any*/).hash = "d6a4a7c84fa97970ee4f08e7b3a643b2"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/__tests__/__generated__/ReactRelayRefetchContainerTestUserWithCondQuery.graphql.js b/packages/react-relay/__tests__/__generated__/ReactRelayRefetchContainerTestUserWithCondQuery.graphql.js index 032b407d23105..4ea6c68a91137 100644 --- a/packages/react-relay/__tests__/__generated__/ReactRelayRefetchContainerTestUserWithCondQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/ReactRelayRefetchContainerTestUserWithCondQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<9f0b885da902476df0579ea146e3d384>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -157,7 +157,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "ec592599ed4721431b9724f76eb81196"; + (node/*: any*/).hash = "5ead741f93728a15b610b870e2b420f2"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/__tests__/__generated__/ReactRelayRefetchContainerWithFragmentOwnershipTestUserQuery.graphql.js b/packages/react-relay/__tests__/__generated__/ReactRelayRefetchContainerWithFragmentOwnershipTestUserQuery.graphql.js index 3b735b515d671..d87841ab6ebee 100644 --- a/packages/react-relay/__tests__/__generated__/ReactRelayRefetchContainerWithFragmentOwnershipTestUserQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/ReactRelayRefetchContainerWithFragmentOwnershipTestUserQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<2a9062913803b91bb4a0fcb64285cc36>> * @flow * @lightSyntaxTransform * @nogrep @@ -171,7 +171,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "226c6570b64b74227f1cd9b069e9815e"; + (node/*: any*/).hash = "ae463ee1fb5c82b6a7b4574ac78a78ef"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/__tests__/__generated__/RefetchableClientEdgeQuery_ClientEdgesTest5Query_me__same_user_client_edge.graphql.js b/packages/react-relay/__tests__/__generated__/RefetchableClientEdgeQuery_ClientEdgesTest5Query_me__same_user_client_edge.graphql.js new file mode 100644 index 0000000000000..6055277452dbd --- /dev/null +++ b/packages/react-relay/__tests__/__generated__/RefetchableClientEdgeQuery_ClientEdgesTest5Query_me__same_user_client_edge.graphql.js @@ -0,0 +1,80 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<9a375d2a5621a15ba4c2a1f71de1afc2>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ReaderFragment, RefetchableFragment } from 'relay-runtime'; +import type { ClientEdgesTest5Query_user$fragmentType } from "./ClientEdgesTest5Query_user.graphql"; +import type { FragmentType } from "relay-runtime"; +declare export opaque type RefetchableClientEdgeQuery_ClientEdgesTest5Query_me__same_user_client_edge$fragmentType: FragmentType; +type ClientEdgeQuery_ClientEdgesTest5Query_me__same_user_client_edge$variables = any; +export type RefetchableClientEdgeQuery_ClientEdgesTest5Query_me__same_user_client_edge$data = {| + +id: string, + +$fragmentSpreads: ClientEdgesTest5Query_user$fragmentType, + +$fragmentType: RefetchableClientEdgeQuery_ClientEdgesTest5Query_me__same_user_client_edge$fragmentType, +|}; +export type RefetchableClientEdgeQuery_ClientEdgesTest5Query_me__same_user_client_edge$key = { + +$data?: RefetchableClientEdgeQuery_ClientEdgesTest5Query_me__same_user_client_edge$data, + +$fragmentSpreads: RefetchableClientEdgeQuery_ClientEdgesTest5Query_me__same_user_client_edge$fragmentType, + ... +}; +*/ + +var node/*: ReaderFragment*/ = { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": { + "refetch": { + "connection": null, + "fragmentPathInResult": [ + "node" + ], + "operation": require('./ClientEdgeQuery_ClientEdgesTest5Query_me__same_user_client_edge.graphql'), + "identifierInfo": { + "identifierField": "id", + "identifierQueryVariableName": "id" + } + } + }, + "name": "RefetchableClientEdgeQuery_ClientEdgesTest5Query_me__same_user_client_edge", + "selections": [ + { + "args": null, + "kind": "FragmentSpread", + "name": "ClientEdgesTest5Query_user" + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + } + ], + "type": "User", + "abstractKey": null +}; + +if (__DEV__) { + (node/*: any*/).hash = "8274337dee7283631e4c2d3992c0add6"; +} + +module.exports = ((node/*: any*/)/*: RefetchableFragment< + RefetchableClientEdgeQuery_ClientEdgesTest5Query_me__same_user_client_edge$fragmentType, + RefetchableClientEdgeQuery_ClientEdgesTest5Query_me__same_user_client_edge$data, + ClientEdgeQuery_ClientEdgesTest5Query_me__same_user_client_edge$variables, +>*/); diff --git a/packages/react-relay/__tests__/__generated__/RefetchableClientEdgeQuery_ClientEdgesTest6Query_me__same_user_client_edge.graphql.js b/packages/react-relay/__tests__/__generated__/RefetchableClientEdgeQuery_ClientEdgesTest6Query_me__same_user_client_edge.graphql.js new file mode 100644 index 0000000000000..02cbf47af8b9a --- /dev/null +++ b/packages/react-relay/__tests__/__generated__/RefetchableClientEdgeQuery_ClientEdgesTest6Query_me__same_user_client_edge.graphql.js @@ -0,0 +1,97 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<8ea39c0a6070c25a68e02cc8a3820ec5>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ReaderFragment, RefetchableFragment } from 'relay-runtime'; +import type { ClientEdgesTestUpperName$key } from "./ClientEdgesTestUpperName.graphql"; +import type { FragmentType } from "relay-runtime"; +import {upper_name as userUpperNameResolverType} from "../ClientEdges-test.js"; +import type { TestResolverContextType } from "../../../relay-runtime/mutations/__tests__/TestResolverContextType"; +// Type assertion validating that `userUpperNameResolverType` resolver is correctly implemented. +// A type error here indicates that the type signature of the resolver module is incorrect. +(userUpperNameResolverType: ( + rootKey: ClientEdgesTestUpperName$key, + args: void, + context: TestResolverContextType, +) => ?string); +declare export opaque type RefetchableClientEdgeQuery_ClientEdgesTest6Query_me__same_user_client_edge$fragmentType: FragmentType; +type ClientEdgeQuery_ClientEdgesTest6Query_me__same_user_client_edge$variables = any; +export type RefetchableClientEdgeQuery_ClientEdgesTest6Query_me__same_user_client_edge$data = {| + +id: string, + +upper_name: ?string, + +$fragmentType: RefetchableClientEdgeQuery_ClientEdgesTest6Query_me__same_user_client_edge$fragmentType, +|}; +export type RefetchableClientEdgeQuery_ClientEdgesTest6Query_me__same_user_client_edge$key = { + +$data?: RefetchableClientEdgeQuery_ClientEdgesTest6Query_me__same_user_client_edge$data, + +$fragmentSpreads: RefetchableClientEdgeQuery_ClientEdgesTest6Query_me__same_user_client_edge$fragmentType, + ... +}; +*/ + +var node/*: ReaderFragment*/ = { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": { + "refetch": { + "connection": null, + "fragmentPathInResult": [ + "node" + ], + "operation": require('./ClientEdgeQuery_ClientEdgesTest6Query_me__same_user_client_edge.graphql'), + "identifierInfo": { + "identifierField": "id", + "identifierQueryVariableName": "id" + } + } + }, + "name": "RefetchableClientEdgeQuery_ClientEdgesTest6Query_me__same_user_client_edge", + "selections": [ + { + "alias": null, + "args": null, + "fragment": { + "args": null, + "kind": "FragmentSpread", + "name": "ClientEdgesTestUpperName" + }, + "kind": "RelayResolver", + "name": "upper_name", + "resolverModule": require('../ClientEdges-test').upper_name, + "path": "upper_name" + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + } + ], + "type": "User", + "abstractKey": null +}; + +if (__DEV__) { + (node/*: any*/).hash = "330a0878ce30575d8c36e2fdd626c833"; +} + +module.exports = ((node/*: any*/)/*: RefetchableFragment< + RefetchableClientEdgeQuery_ClientEdgesTest6Query_me__same_user_client_edge$fragmentType, + RefetchableClientEdgeQuery_ClientEdgesTest6Query_me__same_user_client_edge$data, + ClientEdgeQuery_ClientEdgesTest6Query_me__same_user_client_edge$variables, +>*/); diff --git a/packages/react-relay/__tests__/__generated__/RelayClient3DModuleTestFragment2BasicUser.graphql.js b/packages/react-relay/__tests__/__generated__/RelayClient3DModuleTestFragment2BasicUser.graphql.js index 7227a08b40f97..73abc0a9ecca6 100644 --- a/packages/react-relay/__tests__/__generated__/RelayClient3DModuleTestFragment2BasicUser.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayClient3DModuleTestFragment2BasicUser.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<3a9589b474903828549baf48b7644806>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -16,6 +16,8 @@ 'use strict'; +// @dataDrivenDependency RelayClient3DModuleTestFragment2BasicUser.basicUser {"branches":{"ClientUser":{"component":"ClientUser.react","fragment":"RelayClient3DModuleTestFragmentClientUser_data$normalization.graphql"},"SpecialUser":{"component":"SpecialUser.react","fragment":"RelayClient3DModuleTestFragmentSpecialUser_data$normalization.graphql"}},"plural":false} + /*:: import type { Fragment, ReaderFragment } from 'relay-runtime'; import type { RelayClient3DModuleTestFragmentClientUser_data$fragmentType } from "./RelayClient3DModuleTestFragmentClientUser_data.graphql"; diff --git a/packages/react-relay/__tests__/__generated__/RelayClient3DModuleTestFragmentClientUser_data.graphql.js b/packages/react-relay/__tests__/__generated__/RelayClient3DModuleTestFragmentClientUser_data.graphql.js index 4bd1471f8c782..35f1277d30d29 100644 --- a/packages/react-relay/__tests__/__generated__/RelayClient3DModuleTestFragmentClientUser_data.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayClient3DModuleTestFragmentClientUser_data.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<9680c5a1930472705911182b40292f91>> + * @generated SignedSource<<1b84f5463d8cb4f862ba0f2c066dafa8>> * @flow * @lightSyntaxTransform * @nogrep @@ -57,7 +57,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "data", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/ClientUser____relay_model_instance.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/Client3DClientUserResolvers').data, '__relay_model_instance', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/ClientUser____relay_model_instance.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/Client3DClientUserResolvers').data, '__relay_model_instance', true), "path": "data" } ], diff --git a/packages/react-relay/__tests__/__generated__/RelayClient3DModuleTestFragmentSpecialUser_data.graphql.js b/packages/react-relay/__tests__/__generated__/RelayClient3DModuleTestFragmentSpecialUser_data.graphql.js index 32977d6c8d8a7..2479573239afe 100644 --- a/packages/react-relay/__tests__/__generated__/RelayClient3DModuleTestFragmentSpecialUser_data.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayClient3DModuleTestFragmentSpecialUser_data.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -57,7 +57,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "data", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/SpecialUser____relay_model_instance.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/Client3DSpecialUserResolvers').data, '__relay_model_instance', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/SpecialUser____relay_model_instance.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/Client3DSpecialUserResolvers').data, '__relay_model_instance', true), "path": "data" } ], diff --git a/packages/react-relay/__tests__/__generated__/RelayClient3DModuleTestQuery.graphql.js b/packages/react-relay/__tests__/__generated__/RelayClient3DModuleTestQuery.graphql.js index d645fdfbf47d5..df53444beb0bc 100644 --- a/packages/react-relay/__tests__/__generated__/RelayClient3DModuleTestQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayClient3DModuleTestQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<1a9a6616628a9eabcb1d1df9c1fac6a3>> + * @generated SignedSource<<623769945819d176c24e3232659b0b2c>> * @flow * @lightSyntaxTransform * @nogrep @@ -16,6 +16,8 @@ 'use strict'; +// @indirectDataDrivenDependency RelayClient3DModuleTestFragment2BasicUser.basicUser {"branches":{"ClientUser":{"component":"ClientUser.react","fragment":"RelayClient3DModuleTestFragmentClientUser_data$normalization.graphql"},"SpecialUser":{"component":"SpecialUser.react","fragment":"RelayClient3DModuleTestFragmentSpecialUser_data$normalization.graphql"}},"plural":false} + /*:: import type { ClientRequest, ClientQuery } from 'relay-runtime'; import type { RelayClient3DModuleTestFragment2BasicUser$fragmentType } from "./RelayClient3DModuleTestFragment2BasicUser.graphql"; @@ -105,13 +107,33 @@ return { }, { "kind": "InlineFragment", - "selections": [], + "selections": [ + { + "args": null, + "documentName": "RelayClient3DModuleTestFragment2BasicUser", + "fragmentName": "RelayClient3DModuleTestFragmentClientUser_data", + "fragmentPropName": "data", + "kind": "ModuleImport", + "componentModuleProvider": () => require('./../ClientUser.react'), + "operationModuleProvider": () => require('./RelayClient3DModuleTestFragmentClientUser_data$normalization.graphql') + } + ], "type": "ClientUser", "abstractKey": null }, { "kind": "InlineFragment", - "selections": [], + "selections": [ + { + "args": null, + "documentName": "RelayClient3DModuleTestFragment2BasicUser", + "fragmentName": "RelayClient3DModuleTestFragmentSpecialUser_data", + "fragmentPropName": "data", + "kind": "ModuleImport", + "componentModuleProvider": () => require('./../SpecialUser.react'), + "operationModuleProvider": () => require('./RelayClient3DModuleTestFragmentSpecialUser_data$normalization.graphql') + } + ], "type": "SpecialUser", "abstractKey": null }, diff --git a/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestAnimalGreetingQuery.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestAnimalGreetingQuery.graphql.js index a6ae1bec84c79..793972155a8d4 100644 --- a/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestAnimalGreetingQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestAnimalGreetingQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<97d12bb3a656d891fe4d828b1b1d24ce>> + * @generated SignedSource<<87ea795183a44f4aa1db1ae95d9d297b>> * @flow * @lightSyntaxTransform * @nogrep @@ -114,28 +114,28 @@ return { "modelResolvers": { "Cat": { "alias": null, - "args": [], + "args": null, "fragment": { "args": null, "kind": "FragmentSpread", "name": "Cat__id" }, "kind": "RelayResolver", - "name": "animal", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/Cat__id.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/CatResolvers').Cat, 'id', true), + "name": "__relay_model_instance", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/Cat__id.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/CatResolvers').Cat, 'id', true), "path": "animal.__relay_model_instance" }, "Fish": { "alias": null, - "args": [], + "args": null, "fragment": { "args": null, "kind": "FragmentSpread", "name": "Fish__id" }, "kind": "RelayResolver", - "name": "animal", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/Fish__id.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/FishResolvers').Fish, 'id', true), + "name": "__relay_model_instance", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/Fish__id.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/FishResolvers').Fish, 'id', true), "path": "animal.__relay_model_instance" } }, @@ -145,7 +145,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "animal", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/AnimalQueryResolvers').animal, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/AnimalQueryResolvers').animal, "path": "animal" }, "linkedField": { @@ -170,7 +170,7 @@ return { }, "kind": "RelayResolver", "name": "greeting", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/Cat____relay_model_instance.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/AnimalQueryResolvers').greeting, '__relay_model_instance', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/Cat____relay_model_instance.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/AnimalQueryResolvers').greeting, '__relay_model_instance', true), "path": "animal.greeting" } ], @@ -190,7 +190,7 @@ return { }, "kind": "RelayResolver", "name": "greeting", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/Fish____relay_model_instance.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/AnimalQueryResolvers').greeting, '__relay_model_instance', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/Fish____relay_model_instance.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/AnimalQueryResolvers').greeting, '__relay_model_instance', true), "path": "animal.greeting" } ], diff --git a/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestAnimalLegsFragment.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestAnimalLegsFragment.graphql.js index a319d7ec5c660..be49940feb901 100644 --- a/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestAnimalLegsFragment.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestAnimalLegsFragment.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<438b9cfa39ef26639040bed14b5b7274>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -67,7 +67,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "legs", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/Cat____relay_model_instance.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/CatResolvers').legs, '__relay_model_instance', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/Cat____relay_model_instance.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/CatResolvers').legs, '__relay_model_instance', true), "path": "legs" } ], @@ -87,7 +87,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "legs", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/Fish____relay_model_instance.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/FishResolvers').legs, '__relay_model_instance', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/Fish____relay_model_instance.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/FishResolvers').legs, '__relay_model_instance', true), "path": "legs" } ], diff --git a/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestAnimalLegsQuery.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestAnimalLegsQuery.graphql.js index 478127e1c84ae..c9aba76263f0d 100644 --- a/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestAnimalLegsQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestAnimalLegsQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<62c5f010d33fc347393ac7b3fc0723db>> * @flow * @lightSyntaxTransform * @nogrep @@ -91,28 +91,28 @@ return { "modelResolvers": { "Cat": { "alias": null, - "args": [], + "args": null, "fragment": { "args": null, "kind": "FragmentSpread", "name": "Cat__id" }, "kind": "RelayResolver", - "name": "animal", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/Cat__id.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/CatResolvers').Cat, 'id', true), + "name": "__relay_model_instance", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/Cat__id.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/CatResolvers').Cat, 'id', true), "path": "animal.__relay_model_instance" }, "Fish": { "alias": null, - "args": [], + "args": null, "fragment": { "args": null, "kind": "FragmentSpread", "name": "Fish__id" }, "kind": "RelayResolver", - "name": "animal", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/Fish__id.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/FishResolvers').Fish, 'id', true), + "name": "__relay_model_instance", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/Fish__id.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/FishResolvers').Fish, 'id', true), "path": "animal.__relay_model_instance" } }, @@ -122,7 +122,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "animal", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/AnimalQueryResolvers').animal, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/AnimalQueryResolvers').animal, "path": "animal" }, "linkedField": { diff --git a/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestAnimalsLegsQuery.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestAnimalsLegsQuery.graphql.js index f71239172b975..85556e34185e6 100644 --- a/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestAnimalsLegsQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestAnimalsLegsQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<9d339ec2dd6244541ac1a4c374aac165>> + * @generated SignedSource<<612fe59fa71f3f27797f79399a765e7f>> * @flow * @lightSyntaxTransform * @nogrep @@ -92,28 +92,28 @@ return { "modelResolvers": { "Cat": { "alias": null, - "args": [], + "args": null, "fragment": { "args": null, "kind": "FragmentSpread", "name": "Cat__id" }, "kind": "RelayResolver", - "name": "animals", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/Cat__id.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/CatResolvers').Cat, 'id', true), + "name": "__relay_model_instance", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/Cat__id.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/CatResolvers').Cat, 'id', true), "path": "animals.__relay_model_instance" }, "Fish": { "alias": null, - "args": [], + "args": null, "fragment": { "args": null, "kind": "FragmentSpread", "name": "Fish__id" }, "kind": "RelayResolver", - "name": "animals", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/Fish__id.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/FishResolvers').Fish, 'id', true), + "name": "__relay_model_instance", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/Fish__id.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/FishResolvers').Fish, 'id', true), "path": "animals.__relay_model_instance" } }, @@ -123,7 +123,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "animals", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/AnimalQueryResolvers').animals, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/AnimalQueryResolvers').animals, "path": "animals" }, "linkedField": { diff --git a/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestCatLegsQuery.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestCatLegsQuery.graphql.js index 0c9a93531c87c..52689e28c6455 100644 --- a/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestCatLegsQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestCatLegsQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<593198c057eeeda8d160623a10c56a3a>> + * @generated SignedSource<<0293a0c9a7d6f43f6ee18d498db221ee>> * @flow * @lightSyntaxTransform * @nogrep @@ -75,8 +75,8 @@ return { "name": "Cat__id" }, "kind": "RelayResolver", - "name": "cat", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/Cat__id.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/CatResolvers').Cat, 'id', true), + "name": "__relay_model_instance", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/Cat__id.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/CatResolvers').Cat, 'id', true), "path": "cat.__relay_model_instance" } }, @@ -86,7 +86,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "cat", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/CatResolvers').cat, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/CatResolvers').cat, "path": "cat" }, "linkedField": { diff --git a/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestFishLegsQuery.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestFishLegsQuery.graphql.js index 0b821467a7b47..734015d78b684 100644 --- a/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestFishLegsQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestFishLegsQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<56b719c19d0f223cb1466e307e985c34>> + * @generated SignedSource<<0b4e5d9f1b6038fac312f7b010fff792>> * @flow * @lightSyntaxTransform * @nogrep @@ -75,8 +75,8 @@ return { "name": "Fish__id" }, "kind": "RelayResolver", - "name": "fish", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/Fish__id.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/FishResolvers').Fish, 'id', true), + "name": "__relay_model_instance", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/Fish__id.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/FishResolvers').Fish, 'id', true), "path": "fish.__relay_model_instance" } }, @@ -86,7 +86,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "fish", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/FishResolvers').fish, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/FishResolvers').fish, "path": "fish" }, "linkedField": { diff --git a/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestRedOctopusColorQuery.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestRedOctopusColorQuery.graphql.js index 2e11adc469d96..5786eb2ab102e 100644 --- a/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestRedOctopusColorQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestRedOctopusColorQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<6fd94860e46899b3b8315f34de60a209>> + * @generated SignedSource<<2f492cc11d674dd4831f2b377f5feae0>> * @flow * @lightSyntaxTransform * @nogrep @@ -69,7 +69,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "red_octopus", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/RedOctopusResolvers').red_octopus, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/RedOctopusResolvers').red_octopus, "path": "red_octopus", "normalizationInfo": { "kind": "WeakModel", diff --git a/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestWeakAnimalColorFragment.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestWeakAnimalColorFragment.graphql.js index d8e1408342e0a..222bfdc5ede29 100644 --- a/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestWeakAnimalColorFragment.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestWeakAnimalColorFragment.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<07373ffe396325694b75d4819686d994>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -53,7 +53,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "color", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/PurpleOctopus____relay_model_instance.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/PurpleOctopusResolvers').color, '__relay_model_instance', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/PurpleOctopus____relay_model_instance.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/PurpleOctopusResolvers').color, '__relay_model_instance', true), "path": "color" } ], @@ -73,7 +73,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "color", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/RedOctopus____relay_model_instance.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/RedOctopusResolvers').color, '__relay_model_instance', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/RedOctopus____relay_model_instance.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/RedOctopusResolvers').color, '__relay_model_instance', true), "path": "color" } ], diff --git a/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestWeakAnimalColorQuery.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestWeakAnimalColorQuery.graphql.js index 8dbe6efcbe77a..bdd771978934d 100644 --- a/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestWeakAnimalColorQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestWeakAnimalColorQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<9dcafc2a843a171cbbbf890be2203754>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -90,7 +90,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "weak_animal", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/WeakAnimalQueryResolvers').weak_animal, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/WeakAnimalQueryResolvers').weak_animal, "path": "weak_animal", "normalizationInfo": { "kind": "OutputType", diff --git a/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestWeakAnimalGreetingQuery.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestWeakAnimalGreetingQuery.graphql.js index dbc09f5913ef4..208c7c92603e0 100644 --- a/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestWeakAnimalGreetingQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayResolverInterfaceTestWeakAnimalGreetingQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<1385c82c4f7d308f7fb1db2b10d94c70>> * @flow * @lightSyntaxTransform * @nogrep @@ -99,7 +99,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "weak_animal", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/WeakAnimalQueryResolvers').weak_animal, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/WeakAnimalQueryResolvers').weak_animal, "path": "weak_animal", "normalizationInfo": { "kind": "OutputType", @@ -129,7 +129,7 @@ return { }, "kind": "RelayResolver", "name": "greeting", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/PurpleOctopus____relay_model_instance.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/WeakAnimalQueryResolvers').greeting, '__relay_model_instance', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/PurpleOctopus____relay_model_instance.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/WeakAnimalQueryResolvers').greeting, '__relay_model_instance', true), "path": "weak_animal.greeting" } ], @@ -149,7 +149,7 @@ return { }, "kind": "RelayResolver", "name": "greeting", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/RedOctopus____relay_model_instance.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/WeakAnimalQueryResolvers').greeting, '__relay_model_instance', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/RedOctopus____relay_model_instance.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/WeakAnimalQueryResolvers').greeting, '__relay_model_instance', true), "path": "weak_animal.greeting" } ], diff --git a/packages/react-relay/__tests__/__generated__/RelayResolverModelTestFieldWithArgumentsQuery.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolverModelTestFieldWithArgumentsQuery.graphql.js index 54f157cca91a5..b6d67be8a483a 100644 --- a/packages/react-relay/__tests__/__generated__/RelayResolverModelTestFieldWithArgumentsQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayResolverModelTestFieldWithArgumentsQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<4ea07a5b1e7c025e3c4c0e8ffadad282>> + * @generated SignedSource<<9ef1f52104624c2e023f5c39b88f31f1>> * @flow * @lightSyntaxTransform * @nogrep @@ -112,15 +112,15 @@ return { "modelResolvers": { "TodoModel": { "alias": null, - "args": [], + "args": null, "fragment": { "args": null, "kind": "FragmentSpread", "name": "TodoModel__id" }, "kind": "RelayLiveResolver", - "name": "todo_model", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel__id.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/TodoModel').TodoModel, 'id', true), + "name": "__relay_model_instance", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel__id.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/TodoModel').TodoModel, 'id', true), "path": "todo_model.__relay_model_instance" } }, @@ -130,7 +130,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "todo_model", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/QueryTodoModel').todo_model, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/QueryTodoModel').todo_model, "path": "todo_model" }, "linkedField": { @@ -155,7 +155,7 @@ return { }, "kind": "RelayResolver", "name": "fancy_description", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel____relay_model_instance.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/TodoModel').fancy_description, '__relay_model_instance', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel____relay_model_instance.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/TodoModel').fancy_description, '__relay_model_instance', true), "path": "todo_model.fancy_description", "normalizationInfo": { "kind": "WeakModel", @@ -181,7 +181,7 @@ return { }, "kind": "RelayResolver", "name": "text_with_prefix", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoDescription____relay_model_instance.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/TodoDescription').text_with_prefix, '__relay_model_instance', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoDescription____relay_model_instance.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/TodoDescription').text_with_prefix, '__relay_model_instance', true), "path": "todo_model.fancy_description.text_with_prefix" } ], diff --git a/packages/react-relay/__tests__/__generated__/RelayResolverModelTestFieldWithRootFragmentLegacyQuery.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolverModelTestFieldWithRootFragmentLegacyQuery.graphql.js index f6a63fb954df9..6b836053cdb3d 100644 --- a/packages/react-relay/__tests__/__generated__/RelayResolverModelTestFieldWithRootFragmentLegacyQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayResolverModelTestFieldWithRootFragmentLegacyQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<25344ea51d96e1715d614a4699b5bb91>> + * @generated SignedSource<<4d00f108270488d43e06c8d0592e657d>> * @flow * @lightSyntaxTransform * @nogrep @@ -91,15 +91,15 @@ return { "modelResolvers": { "TodoModel": { "alias": null, - "args": [], + "args": null, "fragment": { "args": null, "kind": "FragmentSpread", "name": "TodoModel__id" }, "kind": "RelayLiveResolver", - "name": "todo_model", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel__id.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/TodoModel').TodoModel, 'id', true), + "name": "__relay_model_instance", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel__id.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/TodoModel').TodoModel, 'id', true), "path": "todo_model.__relay_model_instance" } }, @@ -109,7 +109,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "todo_model", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/QueryTodoModel').todo_model, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/QueryTodoModel').todo_model, "path": "todo_model" }, "linkedField": { @@ -130,7 +130,7 @@ return { }, "kind": "RelayResolver", "name": "capitalized_id_legacy", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/TodoModel').capitalized_id_legacy, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/TodoModel').capitalized_id_legacy, "path": "todo_model.capitalized_id_legacy" } ], diff --git a/packages/react-relay/__tests__/__generated__/RelayResolverModelTestFieldWithRootFragmentQuery.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolverModelTestFieldWithRootFragmentQuery.graphql.js index d461485f45e5b..51710d5841162 100644 --- a/packages/react-relay/__tests__/__generated__/RelayResolverModelTestFieldWithRootFragmentQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayResolverModelTestFieldWithRootFragmentQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<39fa7e9948e81139e1731663852f83bb>> + * @generated SignedSource<<258b0f877f9d5363b434e257efc9bd4b>> * @flow * @lightSyntaxTransform * @nogrep @@ -91,15 +91,15 @@ return { "modelResolvers": { "TodoModel": { "alias": null, - "args": [], + "args": null, "fragment": { "args": null, "kind": "FragmentSpread", "name": "TodoModel__id" }, "kind": "RelayLiveResolver", - "name": "todo_model", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel__id.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/TodoModel').TodoModel, 'id', true), + "name": "__relay_model_instance", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel__id.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/TodoModel').TodoModel, 'id', true), "path": "todo_model.__relay_model_instance" } }, @@ -109,7 +109,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "todo_model", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/QueryTodoModel').todo_model, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/QueryTodoModel').todo_model, "path": "todo_model" }, "linkedField": { @@ -130,7 +130,7 @@ return { }, "kind": "RelayResolver", "name": "capitalized_id", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/TodoModel').capitalized_id, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/TodoModel').capitalized_id, "path": "todo_model.capitalized_id" } ], diff --git a/packages/react-relay/__tests__/__generated__/RelayResolverModelTestFragment.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolverModelTestFragment.graphql.js index 1aa46acbe0e2a..ed87887d9cb93 100644 --- a/packages/react-relay/__tests__/__generated__/RelayResolverModelTestFragment.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayResolverModelTestFragment.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<2d0e399db7f5efce3c2e67f7747b555b>> * @flow * @lightSyntaxTransform * @nogrep @@ -91,7 +91,7 @@ return { }, "kind": "RelayResolver", "name": "fancy_description", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel____relay_model_instance.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/TodoModel').fancy_description, '__relay_model_instance', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel____relay_model_instance.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/TodoModel').fancy_description, '__relay_model_instance', true), "path": "fancy_description", "normalizationInfo": { "kind": "WeakModel", @@ -113,7 +113,7 @@ return { "fragment": (v0/*: any*/), "kind": "RelayResolver", "name": "text", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoDescription____relay_model_instance.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/TodoDescription').text, '__relay_model_instance', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoDescription____relay_model_instance.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/TodoDescription').text, '__relay_model_instance', true), "path": "fancy_description.text" }, { @@ -122,7 +122,7 @@ return { "fragment": (v0/*: any*/), "kind": "RelayResolver", "name": "color", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoDescription____relay_model_instance.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/TodoDescription').color, '__relay_model_instance', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoDescription____relay_model_instance.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/TodoDescription').color, '__relay_model_instance', true), "path": "fancy_description.color" } ], diff --git a/packages/react-relay/__tests__/__generated__/RelayResolverModelTestGetMutableEntityQuery.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolverModelTestGetMutableEntityQuery.graphql.js index 8e7d095329425..d8663570bec9f 100644 --- a/packages/react-relay/__tests__/__generated__/RelayResolverModelTestGetMutableEntityQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayResolverModelTestGetMutableEntityQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<3f370f33db9526932b4ca632fef5cf2b>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -53,7 +53,7 @@ var node/*: ClientRequest*/ = { "fragment": null, "kind": "RelayLiveResolver", "name": "mutable_entity", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/MutableModel').mutable_entity, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/MutableModel').mutable_entity, "path": "mutable_entity" } ] diff --git a/packages/react-relay/__tests__/__generated__/RelayResolverModelTestInterfaceFragment.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolverModelTestInterfaceFragment.graphql.js index 57a0faf2a97fd..dc949b949e2fd 100644 --- a/packages/react-relay/__tests__/__generated__/RelayResolverModelTestInterfaceFragment.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayResolverModelTestInterfaceFragment.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<7e0d627481ef17420968b76926640cfb>> * @flow * @lightSyntaxTransform * @nogrep @@ -116,7 +116,7 @@ return { }, "kind": "RelayResolver", "name": "fancy_description", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel____relay_model_instance.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/TodoModel').fancy_description, '__relay_model_instance', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel____relay_model_instance.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/TodoModel').fancy_description, '__relay_model_instance', true), "path": "fancy_description", "normalizationInfo": { "kind": "WeakModel", @@ -142,7 +142,7 @@ return { "fragment": (v0/*: any*/), "kind": "RelayResolver", "name": "some_interface", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoDescription____relay_model_instance.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/TodoDescription').some_interface, '__relay_model_instance', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoDescription____relay_model_instance.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/TodoDescription').some_interface, '__relay_model_instance', true), "path": "fancy_description.some_interface", "normalizationInfo": { "kind": "OutputType", @@ -172,7 +172,7 @@ return { "fragment": (v0/*: any*/), "kind": "RelayResolver", "name": "some_client_type_with_interface", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoDescription____relay_model_instance.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/TodoDescription').some_client_type_with_interface, '__relay_model_instance', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoDescription____relay_model_instance.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/TodoDescription').some_client_type_with_interface, '__relay_model_instance', true), "path": "fancy_description.some_client_type_with_interface", "normalizationInfo": { "kind": "OutputType", diff --git a/packages/react-relay/__tests__/__generated__/RelayResolverModelTestNullWeakClientEdgeQuery.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolverModelTestNullWeakClientEdgeQuery.graphql.js index e23188e399281..9c0022ffc7b5d 100644 --- a/packages/react-relay/__tests__/__generated__/RelayResolverModelTestNullWeakClientEdgeQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayResolverModelTestNullWeakClientEdgeQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<45e2c7ecca355141617d8f3e54671870>> + * @generated SignedSource<<0b17246624744ff48879a2aaae2b1cd2>> * @flow * @lightSyntaxTransform * @nogrep @@ -112,15 +112,15 @@ return { "modelResolvers": { "TodoModel": { "alias": null, - "args": [], + "args": null, "fragment": { "args": null, "kind": "FragmentSpread", "name": "TodoModel__id" }, "kind": "RelayLiveResolver", - "name": "todo_model", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel__id.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/TodoModel').TodoModel, 'id', true), + "name": "__relay_model_instance", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel__id.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/TodoModel').TodoModel, 'id', true), "path": "todo_model.__relay_model_instance" } }, @@ -130,7 +130,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "todo_model", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/QueryTodoModel').todo_model, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/QueryTodoModel').todo_model, "path": "todo_model" }, "linkedField": { @@ -155,7 +155,7 @@ return { }, "kind": "RelayResolver", "name": "fancy_description_null", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel____relay_model_instance.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/TodoModel').fancy_description_null, '__relay_model_instance', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel____relay_model_instance.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/TodoModel').fancy_description_null, '__relay_model_instance', true), "path": "todo_model.fancy_description_null", "normalizationInfo": { "kind": "WeakModel", @@ -181,7 +181,7 @@ return { }, "kind": "RelayResolver", "name": "text_with_prefix", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoDescription____relay_model_instance.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/TodoDescription').text_with_prefix, '__relay_model_instance', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoDescription____relay_model_instance.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/TodoDescription').text_with_prefix, '__relay_model_instance', true), "path": "todo_model.fancy_description_null.text_with_prefix" } ], diff --git a/packages/react-relay/__tests__/__generated__/RelayResolverModelTestSuspendedWeakClientEdgeQuery.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolverModelTestSuspendedWeakClientEdgeQuery.graphql.js index 552d6465862e1..c637d195d51b0 100644 --- a/packages/react-relay/__tests__/__generated__/RelayResolverModelTestSuspendedWeakClientEdgeQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayResolverModelTestSuspendedWeakClientEdgeQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<0a6e01a8a8623f37bd48109669ffd97e>> * @flow * @lightSyntaxTransform * @nogrep @@ -112,15 +112,15 @@ return { "modelResolvers": { "TodoModel": { "alias": null, - "args": [], + "args": null, "fragment": { "args": null, "kind": "FragmentSpread", "name": "TodoModel__id" }, "kind": "RelayLiveResolver", - "name": "todo_model", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel__id.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/TodoModel').TodoModel, 'id', true), + "name": "__relay_model_instance", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel__id.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/TodoModel').TodoModel, 'id', true), "path": "todo_model.__relay_model_instance" } }, @@ -130,7 +130,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "todo_model", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/QueryTodoModel').todo_model, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/QueryTodoModel').todo_model, "path": "todo_model" }, "linkedField": { @@ -155,7 +155,7 @@ return { }, "kind": "RelayLiveResolver", "name": "fancy_description_suspends", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel____relay_model_instance.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/TodoModel').fancy_description_suspends, '__relay_model_instance', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel____relay_model_instance.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/TodoModel').fancy_description_suspends, '__relay_model_instance', true), "path": "todo_model.fancy_description_suspends", "normalizationInfo": { "kind": "WeakModel", @@ -181,7 +181,7 @@ return { }, "kind": "RelayResolver", "name": "text_with_prefix", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoDescription____relay_model_instance.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/TodoDescription').text_with_prefix, '__relay_model_instance', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoDescription____relay_model_instance.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/TodoDescription').text_with_prefix, '__relay_model_instance', true), "path": "todo_model.fancy_description_suspends.text_with_prefix" } ], diff --git a/packages/react-relay/__tests__/__generated__/RelayResolverModelTestTodoNullQuery.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolverModelTestTodoNullQuery.graphql.js index e52104f522325..8973d1ad25400 100644 --- a/packages/react-relay/__tests__/__generated__/RelayResolverModelTestTodoNullQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayResolverModelTestTodoNullQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<9edf424e72a960b724f97061f861ceda>> * @flow * @lightSyntaxTransform * @nogrep @@ -82,8 +82,8 @@ return { "name": "TodoModel__id" }, "kind": "RelayLiveResolver", - "name": "todo_model_null", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel__id.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/TodoModel').TodoModel, 'id', true), + "name": "__relay_model_instance", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel__id.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/TodoModel').TodoModel, 'id', true), "path": "todo_model_null.__relay_model_instance" } }, @@ -93,7 +93,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "todo_model_null", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/TodoModel').todo_model_null, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/TodoModel').todo_model_null, "path": "todo_model_null" }, "linkedField": (v0/*: any*/) diff --git a/packages/react-relay/__tests__/__generated__/RelayResolverModelTestTodoQuery.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolverModelTestTodoQuery.graphql.js index 948db9f9b6979..e4013f955ee6a 100644 --- a/packages/react-relay/__tests__/__generated__/RelayResolverModelTestTodoQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayResolverModelTestTodoQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<04aaac201c9331444d7ab96f0e4e2979>> + * @generated SignedSource<<4d2bbd3d8b9b27255cbaba755ba3350d>> * @flow * @lightSyntaxTransform * @nogrep @@ -97,15 +97,15 @@ return { "modelResolvers": { "TodoModel": { "alias": null, - "args": [], + "args": null, "fragment": { "args": null, "kind": "FragmentSpread", "name": "TodoModel__id" }, "kind": "RelayLiveResolver", - "name": "todo_model", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel__id.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/TodoModel').TodoModel, 'id', true), + "name": "__relay_model_instance", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel__id.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/TodoModel').TodoModel, 'id', true), "path": "todo_model.__relay_model_instance" } }, @@ -115,7 +115,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "todo_model", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/QueryTodoModel').todo_model, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/QueryTodoModel').todo_model, "path": "todo_model" }, "linkedField": { diff --git a/packages/react-relay/__tests__/__generated__/RelayResolverModelTestTodoWithInterfaceQuery.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolverModelTestTodoWithInterfaceQuery.graphql.js index de4bbf63863df..b45d810154daa 100644 --- a/packages/react-relay/__tests__/__generated__/RelayResolverModelTestTodoWithInterfaceQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayResolverModelTestTodoWithInterfaceQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<8da157015067e235a0b15f4ba6064b15>> + * @generated SignedSource<<6523c180ad17f819d7fc6ca1b41d7b20>> * @flow * @lightSyntaxTransform * @nogrep @@ -113,15 +113,15 @@ return { "modelResolvers": { "TodoModel": { "alias": null, - "args": [], + "args": null, "fragment": { "args": null, "kind": "FragmentSpread", "name": "TodoModel__id" }, "kind": "RelayLiveResolver", - "name": "todo_model", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel__id.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/TodoModel').TodoModel, 'id', true), + "name": "__relay_model_instance", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel__id.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/TodoModel').TodoModel, 'id', true), "path": "todo_model.__relay_model_instance" } }, @@ -131,7 +131,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "todo_model", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/QueryTodoModel').todo_model, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/QueryTodoModel').todo_model, "path": "todo_model" }, "linkedField": { diff --git a/packages/react-relay/__tests__/__generated__/RelayResolverModelTestTodoWithNullablePluralFieldQuery.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolverModelTestTodoWithNullablePluralFieldQuery.graphql.js index f94c7ebb63114..899e8912c6110 100644 --- a/packages/react-relay/__tests__/__generated__/RelayResolverModelTestTodoWithNullablePluralFieldQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayResolverModelTestTodoWithNullablePluralFieldQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<8e891b03214b392a12d3e75304e2e4b1>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -103,15 +103,15 @@ return { "modelResolvers": { "TodoModel": { "alias": null, - "args": [], + "args": null, "fragment": { "args": null, "kind": "FragmentSpread", "name": "TodoModel__id" }, "kind": "RelayLiveResolver", - "name": "todo_model", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel__id.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/TodoModel').TodoModel, 'id', true), + "name": "__relay_model_instance", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel__id.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/TodoModel').TodoModel, 'id', true), "path": "todo_model.__relay_model_instance" } }, @@ -121,7 +121,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "todo_model", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/QueryTodoModel').todo_model, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/QueryTodoModel').todo_model, "path": "todo_model" }, "linkedField": { @@ -146,7 +146,7 @@ return { }, "kind": "RelayResolver", "name": "many_fancy_descriptions_but_some_are_null", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel____relay_model_instance.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/TodoModel').many_fancy_descriptions_but_some_are_null, '__relay_model_instance', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel____relay_model_instance.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/TodoModel').many_fancy_descriptions_but_some_are_null, '__relay_model_instance', true), "path": "todo_model.many_fancy_descriptions_but_some_are_null", "normalizationInfo": { "kind": "WeakModel", @@ -172,7 +172,7 @@ return { }, "kind": "RelayResolver", "name": "text", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoDescription____relay_model_instance.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/TodoDescription').text, '__relay_model_instance', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoDescription____relay_model_instance.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/TodoDescription').text, '__relay_model_instance', true), "path": "todo_model.many_fancy_descriptions_but_some_are_null.text" } ], diff --git a/packages/react-relay/__tests__/__generated__/RelayResolverModelTestTodoWithPluralFieldQuery.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolverModelTestTodoWithPluralFieldQuery.graphql.js index fb014d414b088..cd51c71c72951 100644 --- a/packages/react-relay/__tests__/__generated__/RelayResolverModelTestTodoWithPluralFieldQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayResolverModelTestTodoWithPluralFieldQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<36c5b62e32058dfdc0d89a1b36f528b1>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -97,15 +97,15 @@ return { "modelResolvers": { "TodoModel": { "alias": null, - "args": [], + "args": null, "fragment": { "args": null, "kind": "FragmentSpread", "name": "TodoModel__id" }, "kind": "RelayLiveResolver", - "name": "todo_model", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel__id.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/TodoModel').TodoModel, 'id', true), + "name": "__relay_model_instance", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel__id.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/TodoModel').TodoModel, 'id', true), "path": "todo_model.__relay_model_instance" } }, @@ -115,7 +115,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "todo_model", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/QueryTodoModel').todo_model, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/QueryTodoModel').todo_model, "path": "todo_model" }, "linkedField": { diff --git a/packages/react-relay/__tests__/__generated__/RelayResolverModelTestWeakLiveColorFieldQuery.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolverModelTestWeakLiveColorFieldQuery.graphql.js index d9c8b5636e99b..d9cc4f53456b6 100644 --- a/packages/react-relay/__tests__/__generated__/RelayResolverModelTestWeakLiveColorFieldQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayResolverModelTestWeakLiveColorFieldQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<21e86151eb0ff5f1b6f39eec20afd006>> + * @generated SignedSource<<40cb639926faacb63845ac1ecb060118>> * @flow * @lightSyntaxTransform * @nogrep @@ -115,7 +115,7 @@ return { "fragment": null, "kind": "RelayLiveResolver", "name": "live_todo_description", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/TodoModel').live_todo_description, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/TodoModel').live_todo_description, "path": "live_todo_description", "normalizationInfo": { "kind": "WeakModel", @@ -137,7 +137,7 @@ return { "fragment": (v2/*: any*/), "kind": "RelayResolver", "name": "text", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoDescription____relay_model_instance.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/TodoDescription').text, '__relay_model_instance', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoDescription____relay_model_instance.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/TodoDescription').text, '__relay_model_instance', true), "path": "live_todo_description.text" }, { @@ -146,7 +146,7 @@ return { "fragment": (v2/*: any*/), "kind": "RelayLiveResolver", "name": "live_color", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoDescription____relay_model_instance.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/TodoDescription').live_color, '__relay_model_instance', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoDescription____relay_model_instance.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/TodoDescription').live_color, '__relay_model_instance', true), "path": "live_todo_description.live_color" } ], diff --git a/packages/react-relay/__tests__/__generated__/RelayResolverModelTestWeakLiveFieldQuery.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolverModelTestWeakLiveFieldQuery.graphql.js index 6b075561c543b..eb429b070fcd4 100644 --- a/packages/react-relay/__tests__/__generated__/RelayResolverModelTestWeakLiveFieldQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayResolverModelTestWeakLiveFieldQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<580e60a7f5ecd5f9499f638994e9aa8b>> + * @generated SignedSource<<221925ff11a1c71b497849092c64dc39>> * @flow * @lightSyntaxTransform * @nogrep @@ -115,7 +115,7 @@ return { "fragment": null, "kind": "RelayLiveResolver", "name": "live_todo_description", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/TodoModel').live_todo_description, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/TodoModel').live_todo_description, "path": "live_todo_description", "normalizationInfo": { "kind": "WeakModel", @@ -137,7 +137,7 @@ return { "fragment": (v2/*: any*/), "kind": "RelayResolver", "name": "text", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoDescription____relay_model_instance.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/TodoDescription').text, '__relay_model_instance', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoDescription____relay_model_instance.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/TodoDescription').text, '__relay_model_instance', true), "path": "live_todo_description.text" }, { @@ -146,7 +146,7 @@ return { "fragment": (v2/*: any*/), "kind": "RelayResolver", "name": "color", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoDescription____relay_model_instance.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/TodoDescription').color, '__relay_model_instance', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoDescription____relay_model_instance.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/TodoDescription').color, '__relay_model_instance', true), "path": "live_todo_description.color" } ], diff --git a/packages/react-relay/__tests__/__generated__/RelayResolverModelTestWithPluralFragment.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolverModelTestWithPluralFragment.graphql.js index b41e02f95811f..262c9fe29505a 100644 --- a/packages/react-relay/__tests__/__generated__/RelayResolverModelTestWithPluralFragment.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayResolverModelTestWithPluralFragment.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<2c60ec73649f0e4bc8815075399bb5bc>> * @flow * @lightSyntaxTransform * @nogrep @@ -90,7 +90,7 @@ return { }, "kind": "RelayResolver", "name": "many_fancy_descriptions", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel____relay_model_instance.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/TodoModel').many_fancy_descriptions, '__relay_model_instance', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel____relay_model_instance.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/TodoModel').many_fancy_descriptions, '__relay_model_instance', true), "path": "many_fancy_descriptions", "normalizationInfo": { "kind": "WeakModel", @@ -112,7 +112,7 @@ return { "fragment": (v0/*: any*/), "kind": "RelayResolver", "name": "text", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoDescription____relay_model_instance.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/TodoDescription').text, '__relay_model_instance', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoDescription____relay_model_instance.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/TodoDescription').text, '__relay_model_instance', true), "path": "many_fancy_descriptions.text" }, { @@ -121,7 +121,7 @@ return { "fragment": (v0/*: any*/), "kind": "RelayResolver", "name": "color", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoDescription____relay_model_instance.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/TodoDescription').color, '__relay_model_instance', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoDescription____relay_model_instance.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/TodoDescription').color, '__relay_model_instance', true), "path": "many_fancy_descriptions.color" } ], diff --git a/packages/react-relay/__tests__/__generated__/RelayResolverModelWithContextTestFragment.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolverModelWithContextTestFragment.graphql.js new file mode 100644 index 0000000000000..a95571b1db9de --- /dev/null +++ b/packages/react-relay/__tests__/__generated__/RelayResolverModelWithContextTestFragment.graphql.js @@ -0,0 +1,109 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<460ee893682cc66f3f00f29fb4d5780d>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { Fragment, ReaderFragment } from 'relay-runtime'; +import type { TodoModel____relay_model_instance$data } from "./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel____relay_model_instance.graphql"; +import type { FragmentType } from "relay-runtime"; +import {another_value_from_context as todoModelAnotherValueFromContextResolverType} from "../../../relay-runtime/store/__tests__/resolvers/TodoModel.js"; +import type { TestResolverContextType } from "../../../relay-runtime/mutations/__tests__/TestResolverContextType"; +// Type assertion validating that `todoModelAnotherValueFromContextResolverType` resolver is correctly implemented. +// A type error here indicates that the type signature of the resolver module is incorrect. +(todoModelAnotherValueFromContextResolverType: ( + __relay_model_instance: TodoModel____relay_model_instance$data['__relay_model_instance'], + args: void, + context: TestResolverContextType, +) => ?string); +import {description as todoModelDescriptionResolverType} from "../../../relay-runtime/store/__tests__/resolvers/TodoModel.js"; +// Type assertion validating that `todoModelDescriptionResolverType` resolver is correctly implemented. +// A type error here indicates that the type signature of the resolver module is incorrect. +(todoModelDescriptionResolverType: ( + __relay_model_instance: TodoModel____relay_model_instance$data['__relay_model_instance'], + args: void, + context: TestResolverContextType, +) => ?string); +declare export opaque type RelayResolverModelWithContextTestFragment$fragmentType: FragmentType; +export type RelayResolverModelWithContextTestFragment$data = {| + +another_value_from_context: ?string, + +description: ?string, + +id: string, + +$fragmentType: RelayResolverModelWithContextTestFragment$fragmentType, +|}; +export type RelayResolverModelWithContextTestFragment$key = { + +$data?: RelayResolverModelWithContextTestFragment$data, + +$fragmentSpreads: RelayResolverModelWithContextTestFragment$fragmentType, + ... +}; +*/ + +var node/*: ReaderFragment*/ = (function(){ +var v0 = { + "args": null, + "kind": "FragmentSpread", + "name": "TodoModel____relay_model_instance" +}; +return { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "RelayResolverModelWithContextTestFragment", + "selections": [ + { + "alias": null, + "args": null, + "fragment": (v0/*: any*/), + "kind": "RelayResolver", + "name": "description", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel____relay_model_instance.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/TodoModel').description, '__relay_model_instance', true), + "path": "description" + }, + { + "alias": null, + "args": null, + "fragment": (v0/*: any*/), + "kind": "RelayResolver", + "name": "another_value_from_context", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel____relay_model_instance.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/TodoModel').another_value_from_context, '__relay_model_instance', true), + "path": "another_value_from_context" + }, + { + "kind": "ClientExtension", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + } + ] + } + ], + "type": "TodoModel", + "abstractKey": null +}; +})(); + +if (__DEV__) { + (node/*: any*/).hash = "5373c916bad7d7b7c83e32558cfde3d4"; +} + +module.exports = ((node/*: any*/)/*: Fragment< + RelayResolverModelWithContextTestFragment$fragmentType, + RelayResolverModelWithContextTestFragment$data, +>*/); diff --git a/packages/react-relay/__tests__/__generated__/RelayResolverModelWithContextTestQuery.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolverModelWithContextTestQuery.graphql.js new file mode 100644 index 0000000000000..f7450fd46632d --- /dev/null +++ b/packages/react-relay/__tests__/__generated__/RelayResolverModelWithContextTestQuery.graphql.js @@ -0,0 +1,215 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<01eac9918f72b64c24d8ae8dcc17af10>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ClientRequest, ClientQuery } from 'relay-runtime'; +import type { DataID } from "relay-runtime"; +import type { RelayResolverModelWithContextTestFragment$fragmentType } from "./RelayResolverModelWithContextTestFragment.graphql"; +import {todo_model as queryTodoModelResolverType} from "../../../relay-runtime/store/__tests__/resolvers/QueryTodoModel.js"; +import type { TestResolverContextType } from "../../../relay-runtime/mutations/__tests__/TestResolverContextType"; +// Type assertion validating that `queryTodoModelResolverType` resolver is correctly implemented. +// A type error here indicates that the type signature of the resolver module is incorrect. +(queryTodoModelResolverType: ( + args: {| + todoID: string, + |}, + context: TestResolverContextType, +) => ?{| + +id: DataID, +|}); +export type RelayResolverModelWithContextTestQuery$variables = {| + id: string, +|}; +export type RelayResolverModelWithContextTestQuery$data = {| + +todo_model: ?{| + +$fragmentSpreads: RelayResolverModelWithContextTestFragment$fragmentType, + |}, +|}; +export type RelayResolverModelWithContextTestQuery = {| + response: RelayResolverModelWithContextTestQuery$data, + variables: RelayResolverModelWithContextTestQuery$variables, +|}; +*/ + +var node/*: ClientRequest*/ = (function(){ +var v0 = [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "id" + } +], +v1 = [ + { + "kind": "Variable", + "name": "todoID", + "variableName": "id" + } +], +v2 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null +}, +v3 = { + "kind": "InlineFragment", + "selections": [ + { + "name": "__relay_model_instance", + "args": null, + "fragment": { + "kind": "InlineFragment", + "selections": [ + (v2/*: any*/) + ], + "type": "TodoModel", + "abstractKey": null + }, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": false + } + ], + "type": "TodoModel", + "abstractKey": null +}; +return { + "fragment": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Fragment", + "metadata": { + "hasClientEdges": true + }, + "name": "RelayResolverModelWithContextTestQuery", + "selections": [ + { + "kind": "ClientEdgeToClientObject", + "concreteType": "TodoModel", + "modelResolvers": { + "TodoModel": { + "alias": null, + "args": null, + "fragment": { + "args": null, + "kind": "FragmentSpread", + "name": "TodoModel__id" + }, + "kind": "RelayLiveResolver", + "name": "__relay_model_instance", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel__id.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/TodoModel').TodoModel, 'id', true), + "path": "todo_model.__relay_model_instance" + } + }, + "backingField": { + "alias": null, + "args": (v1/*: any*/), + "fragment": null, + "kind": "RelayResolver", + "name": "todo_model", + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/QueryTodoModel').todo_model, + "path": "todo_model" + }, + "linkedField": { + "alias": null, + "args": (v1/*: any*/), + "concreteType": "TodoModel", + "kind": "LinkedField", + "name": "todo_model", + "plural": false, + "selections": [ + { + "args": null, + "kind": "FragmentSpread", + "name": "RelayResolverModelWithContextTestFragment" + } + ], + "storageKey": null + } + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Operation", + "name": "RelayResolverModelWithContextTestQuery", + "selections": [ + { + "kind": "ClientEdgeToClientObject", + "backingField": { + "name": "todo_model", + "args": (v1/*: any*/), + "fragment": null, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": false + }, + "linkedField": { + "alias": null, + "args": (v1/*: any*/), + "concreteType": "TodoModel", + "kind": "LinkedField", + "name": "todo_model", + "plural": false, + "selections": [ + (v2/*: any*/), + { + "name": "description", + "args": null, + "fragment": (v3/*: any*/), + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": true + }, + { + "name": "another_value_from_context", + "args": null, + "fragment": (v3/*: any*/), + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": true + } + ], + "storageKey": null + } + } + ] + }, + "params": { + "cacheID": "0196832cb242ac4177d6b6eede119e96", + "id": null, + "metadata": {}, + "name": "RelayResolverModelWithContextTestQuery", + "operationKind": "query", + "text": null + } +}; +})(); + +if (__DEV__) { + (node/*: any*/).hash = "5322e368bc0ab0fd2107e5680043ba4c"; +} + +module.exports = ((node/*: any*/)/*: ClientQuery< + RelayResolverModelWithContextTestQuery$variables, + RelayResolverModelWithContextTestQuery$data, +>*/); diff --git a/packages/react-relay/__tests__/__generated__/RelayResolverNullableModelClientEdgeTest_ErrorModel_Query.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolverNullableModelClientEdgeTest_ErrorModel_Query.graphql.js index 97a0365650a06..b74f0b8b44889 100644 --- a/packages/react-relay/__tests__/__generated__/RelayResolverNullableModelClientEdgeTest_ErrorModel_Query.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayResolverNullableModelClientEdgeTest_ErrorModel_Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<881b7ccb55caa92347237f48c4e3990e>> + * @generated SignedSource<<264224bcbf3cac18840f289171287825>> * @flow * @lightSyntaxTransform * @nogrep @@ -71,8 +71,8 @@ return { "name": "ErrorModel__id" }, "kind": "RelayResolver", - "name": "edge_to_model_that_throws", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./ErrorModel__id.graphql'), require('./../RelayResolverNullableModelClientEdge-test').ErrorModel, 'id', true), + "name": "__relay_model_instance", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./ErrorModel__id.graphql'), require('../RelayResolverNullableModelClientEdge-test').ErrorModel, 'id', true), "path": "edge_to_model_that_throws.__relay_model_instance" } }, @@ -82,7 +82,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "edge_to_model_that_throws", - "resolverModule": require('./../RelayResolverNullableModelClientEdge-test').edge_to_model_that_throws, + "resolverModule": require('../RelayResolverNullableModelClientEdge-test').edge_to_model_that_throws, "path": "edge_to_model_that_throws" }, "linkedField": { diff --git a/packages/react-relay/__tests__/__generated__/RelayResolverNullableModelClientEdgeTest_LiveModel_Query.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolverNullableModelClientEdgeTest_LiveModel_Query.graphql.js index 589e4d719df80..dc193f5a062d5 100644 --- a/packages/react-relay/__tests__/__generated__/RelayResolverNullableModelClientEdgeTest_LiveModel_Query.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayResolverNullableModelClientEdgeTest_LiveModel_Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<70b480b97721b86a5618c2f8f8012117>> + * @generated SignedSource<<4d0c48c1712a5e0c8a0662becaea11ac>> * @flow * @lightSyntaxTransform * @nogrep @@ -93,8 +93,8 @@ return { "name": "TodoModel__id" }, "kind": "RelayLiveResolver", - "name": "edge_to_live_object_does_not_exist", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel__id.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/TodoModel').TodoModel, 'id', true), + "name": "__relay_model_instance", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel__id.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/TodoModel').TodoModel, 'id', true), "path": "edge_to_live_object_does_not_exist.__relay_model_instance" } }, @@ -104,7 +104,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "edge_to_live_object_does_not_exist", - "resolverModule": require('./../RelayResolverNullableModelClientEdge-test').edge_to_live_object_does_not_exist, + "resolverModule": require('../RelayResolverNullableModelClientEdge-test').edge_to_live_object_does_not_exist, "path": "edge_to_live_object_does_not_exist" }, "linkedField": { @@ -130,7 +130,7 @@ return { }, "kind": "RelayResolver", "name": "fancy_description", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel____relay_model_instance.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/TodoModel').fancy_description, '__relay_model_instance', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel____relay_model_instance.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/TodoModel').fancy_description, '__relay_model_instance', true), "path": "edge_to_live_object_does_not_exist.fancy_description", "normalizationInfo": { "kind": "WeakModel", @@ -156,7 +156,7 @@ return { }, "kind": "RelayResolver", "name": "text", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoDescription____relay_model_instance.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/TodoDescription').text, '__relay_model_instance', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoDescription____relay_model_instance.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/TodoDescription').text, '__relay_model_instance', true), "path": "edge_to_live_object_does_not_exist.fancy_description.text" } ], diff --git a/packages/react-relay/__tests__/__generated__/RelayResolverNullableModelClientEdgeTest_PluralErrorModel_Query.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolverNullableModelClientEdgeTest_PluralErrorModel_Query.graphql.js index 0ef7b753d6ffe..48e2e3b83f16d 100644 --- a/packages/react-relay/__tests__/__generated__/RelayResolverNullableModelClientEdgeTest_PluralErrorModel_Query.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayResolverNullableModelClientEdgeTest_PluralErrorModel_Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<843eff00f65f0343dd36ff40ea5e55c6>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -71,8 +71,8 @@ return { "name": "ErrorModel__id" }, "kind": "RelayResolver", - "name": "edge_to_plural_models_that_throw", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./ErrorModel__id.graphql'), require('./../RelayResolverNullableModelClientEdge-test').ErrorModel, 'id', true), + "name": "__relay_model_instance", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./ErrorModel__id.graphql'), require('../RelayResolverNullableModelClientEdge-test').ErrorModel, 'id', true), "path": "edge_to_plural_models_that_throw.__relay_model_instance" } }, @@ -82,7 +82,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "edge_to_plural_models_that_throw", - "resolverModule": require('./../RelayResolverNullableModelClientEdge-test').edge_to_plural_models_that_throw, + "resolverModule": require('../RelayResolverNullableModelClientEdge-test').edge_to_plural_models_that_throw, "path": "edge_to_plural_models_that_throw" }, "linkedField": { diff --git a/packages/react-relay/__tests__/__generated__/RelayResolverNullableModelClientEdgeTest_PluralLiveModelNoneExist_Query.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolverNullableModelClientEdgeTest_PluralLiveModelNoneExist_Query.graphql.js index 58d45b9c9e395..1829a7205a936 100644 --- a/packages/react-relay/__tests__/__generated__/RelayResolverNullableModelClientEdgeTest_PluralLiveModelNoneExist_Query.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayResolverNullableModelClientEdgeTest_PluralLiveModelNoneExist_Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<171f9703e5eb0afae9de81d268fccbdd>> * @flow * @lightSyntaxTransform * @nogrep @@ -81,8 +81,8 @@ return { "name": "TodoModel__id" }, "kind": "RelayLiveResolver", - "name": "edge_to_plural_live_objects_none_exist", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel__id.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/TodoModel').TodoModel, 'id', true), + "name": "__relay_model_instance", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel__id.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/TodoModel').TodoModel, 'id', true), "path": "edge_to_plural_live_objects_none_exist.__relay_model_instance" } }, @@ -92,7 +92,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "edge_to_plural_live_objects_none_exist", - "resolverModule": require('./../RelayResolverNullableModelClientEdge-test').edge_to_plural_live_objects_none_exist, + "resolverModule": require('../RelayResolverNullableModelClientEdge-test').edge_to_plural_live_objects_none_exist, "path": "edge_to_plural_live_objects_none_exist" }, "linkedField": { @@ -114,7 +114,7 @@ return { }, "kind": "RelayResolver", "name": "description", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel____relay_model_instance.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/TodoModel').description, '__relay_model_instance', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel____relay_model_instance.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/TodoModel').description, '__relay_model_instance', true), "path": "edge_to_plural_live_objects_none_exist.description" } ], diff --git a/packages/react-relay/__tests__/__generated__/RelayResolverNullableModelClientEdgeTest_PluralLiveModel_Query.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolverNullableModelClientEdgeTest_PluralLiveModel_Query.graphql.js index 9ec9988bedde2..ccd91d68e78d5 100644 --- a/packages/react-relay/__tests__/__generated__/RelayResolverNullableModelClientEdgeTest_PluralLiveModel_Query.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayResolverNullableModelClientEdgeTest_PluralLiveModel_Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -81,8 +81,8 @@ return { "name": "TodoModel__id" }, "kind": "RelayLiveResolver", - "name": "edge_to_plural_live_objects_some_exist", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel__id.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/TodoModel').TodoModel, 'id', true), + "name": "__relay_model_instance", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel__id.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/TodoModel').TodoModel, 'id', true), "path": "edge_to_plural_live_objects_some_exist.__relay_model_instance" } }, @@ -92,7 +92,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "edge_to_plural_live_objects_some_exist", - "resolverModule": require('./../RelayResolverNullableModelClientEdge-test').edge_to_plural_live_objects_some_exist, + "resolverModule": require('../RelayResolverNullableModelClientEdge-test').edge_to_plural_live_objects_some_exist, "path": "edge_to_plural_live_objects_some_exist" }, "linkedField": { @@ -114,7 +114,7 @@ return { }, "kind": "RelayResolver", "name": "description", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel____relay_model_instance.graphql'), require('./../../../relay-runtime/store/__tests__/resolvers/TodoModel').description, '__relay_model_instance', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./../../../relay-runtime/store/__tests__/resolvers/__generated__/TodoModel____relay_model_instance.graphql'), require('../../../relay-runtime/store/__tests__/resolvers/TodoModel').description, '__relay_model_instance', true), "path": "edge_to_plural_live_objects_some_exist.description" } ], diff --git a/packages/react-relay/__tests__/__generated__/RelayResolverNullableModelClientEdgeTest_PluralSomeErrorModel_Query.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolverNullableModelClientEdgeTest_PluralSomeErrorModel_Query.graphql.js index 01f58bb9788d7..6035a744120e2 100644 --- a/packages/react-relay/__tests__/__generated__/RelayResolverNullableModelClientEdgeTest_PluralSomeErrorModel_Query.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayResolverNullableModelClientEdgeTest_PluralSomeErrorModel_Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<8fb43e03539a3a71a4f73a2629d70f43>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -82,8 +82,8 @@ return { "name": "ErrorModel__id" }, "kind": "RelayResolver", - "name": "edge_to_plural_models_some_throw", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./ErrorModel__id.graphql'), require('./../RelayResolverNullableModelClientEdge-test').ErrorModel, 'id', true), + "name": "__relay_model_instance", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./ErrorModel__id.graphql'), require('../RelayResolverNullableModelClientEdge-test').ErrorModel, 'id', true), "path": "edge_to_plural_models_some_throw.__relay_model_instance" } }, @@ -93,7 +93,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "edge_to_plural_models_some_throw", - "resolverModule": require('./../RelayResolverNullableModelClientEdge-test').edge_to_plural_models_some_throw, + "resolverModule": require('../RelayResolverNullableModelClientEdge-test').edge_to_plural_models_some_throw, "path": "edge_to_plural_models_some_throw" }, "linkedField": (v0/*: any*/) diff --git a/packages/react-relay/__tests__/__generated__/RelayResolverNullableModelClientEdgeTest_ServerObjectReadOnlyId_Query.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolverNullableModelClientEdgeTest_ServerObjectReadOnlyId_Query.graphql.js index 2bf24e82ed5d2..459b3af8b1a9c 100644 --- a/packages/react-relay/__tests__/__generated__/RelayResolverNullableModelClientEdgeTest_ServerObjectReadOnlyId_Query.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayResolverNullableModelClientEdgeTest_ServerObjectReadOnlyId_Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<4c9d6d4bdb7c88023861cae72a2e5c97>> + * @generated SignedSource<<6983558fc7ee772d8e56d5b22396f263>> * @flow * @lightSyntaxTransform * @nogrep @@ -59,7 +59,7 @@ var node/*: ClientRequest*/ = { "fragment": null, "kind": "RelayResolver", "name": "edge_to_server_object_does_not_exist", - "resolverModule": require('./../RelayResolverNullableModelClientEdge-test').edge_to_server_object_does_not_exist, + "resolverModule": require('../RelayResolverNullableModelClientEdge-test').edge_to_server_object_does_not_exist, "path": "edge_to_server_object_does_not_exist" }, "linkedField": { diff --git a/packages/react-relay/__tests__/__generated__/RelayResolverNullableModelClientEdgeTest_ServerObject_Query.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolverNullableModelClientEdgeTest_ServerObject_Query.graphql.js index 47e1a66c54c6f..51af0d4af23fc 100644 --- a/packages/react-relay/__tests__/__generated__/RelayResolverNullableModelClientEdgeTest_ServerObject_Query.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayResolverNullableModelClientEdgeTest_ServerObject_Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -59,7 +59,7 @@ var node/*: ClientRequest*/ = { "fragment": null, "kind": "RelayResolver", "name": "edge_to_server_object_does_not_exist", - "resolverModule": require('./../RelayResolverNullableModelClientEdge-test').edge_to_server_object_does_not_exist, + "resolverModule": require('../RelayResolverNullableModelClientEdge-test').edge_to_server_object_does_not_exist, "path": "edge_to_server_object_does_not_exist" }, "linkedField": { diff --git a/packages/react-relay/__tests__/__generated__/RelayResolverNullableModelClientEdgeTest_StrongModel_Query.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolverNullableModelClientEdgeTest_StrongModel_Query.graphql.js index a83f294b524ba..c2ca53b9b7f9e 100644 --- a/packages/react-relay/__tests__/__generated__/RelayResolverNullableModelClientEdgeTest_StrongModel_Query.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayResolverNullableModelClientEdgeTest_StrongModel_Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<6a1a53ca2d307c6484b70e3353466c49>> + * @generated SignedSource<<3387d6019d487ace12a8690a20e2e40e>> * @flow * @lightSyntaxTransform * @nogrep @@ -80,8 +80,8 @@ return { "name": "StrongModel__id" }, "kind": "RelayResolver", - "name": "edge_to_strong_model_does_not_exist", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./StrongModel__id.graphql'), require('./../RelayResolverNullableModelClientEdge-test').StrongModel, 'id', true), + "name": "__relay_model_instance", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./StrongModel__id.graphql'), require('../RelayResolverNullableModelClientEdge-test').StrongModel, 'id', true), "path": "edge_to_strong_model_does_not_exist.__relay_model_instance" } }, @@ -91,7 +91,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "edge_to_strong_model_does_not_exist", - "resolverModule": require('./../RelayResolverNullableModelClientEdge-test').edge_to_strong_model_does_not_exist, + "resolverModule": require('../RelayResolverNullableModelClientEdge-test').edge_to_strong_model_does_not_exist, "path": "edge_to_strong_model_does_not_exist" }, "linkedField": { @@ -112,7 +112,7 @@ return { }, "kind": "RelayResolver", "name": "name", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./StrongModel____relay_model_instance.graphql'), require('./../RelayResolverNullableModelClientEdge-test').name, '__relay_model_instance', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./StrongModel____relay_model_instance.graphql'), require('../RelayResolverNullableModelClientEdge-test').name, '__relay_model_instance', true), "path": "edge_to_strong_model_does_not_exist.name" } ], diff --git a/packages/react-relay/__tests__/__generated__/RelayResolverNullableModelClientEdgeTest_WeakModel_Query.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolverNullableModelClientEdgeTest_WeakModel_Query.graphql.js index c5f3b75c63f1e..04df2b9e5355f 100644 --- a/packages/react-relay/__tests__/__generated__/RelayResolverNullableModelClientEdgeTest_WeakModel_Query.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayResolverNullableModelClientEdgeTest_WeakModel_Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<43501afad7f54936db863c04d0dcdecc>> + * @generated SignedSource<<86e78e986e692e1e021ded761a9a0389>> * @flow * @lightSyntaxTransform * @nogrep @@ -67,7 +67,7 @@ var node/*: ClientRequest*/ = { "fragment": null, "kind": "RelayResolver", "name": "edge_to_null_weak_model", - "resolverModule": require('./../RelayResolverNullableModelClientEdge-test').edge_to_null_weak_model, + "resolverModule": require('../RelayResolverNullableModelClientEdge-test').edge_to_null_weak_model, "path": "edge_to_null_weak_model", "normalizationInfo": { "kind": "WeakModel", @@ -93,7 +93,7 @@ var node/*: ClientRequest*/ = { }, "kind": "RelayResolver", "name": "first_name", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./WeakModel____relay_model_instance.graphql'), require('./../RelayResolverNullableModelClientEdge-test').first_name, '__relay_model_instance', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./WeakModel____relay_model_instance.graphql'), require('../RelayResolverNullableModelClientEdge-test').first_name, '__relay_model_instance', true), "path": "edge_to_null_weak_model.first_name" } ], diff --git a/packages/react-relay/__tests__/__generated__/RelayResolversWithOutputTypeTestExceptionalProjectQuery.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolversWithOutputTypeTestExceptionalProjectQuery.graphql.js index ca89322d7d77d..37b2e0a62cf36 100644 --- a/packages/react-relay/__tests__/__generated__/RelayResolversWithOutputTypeTestExceptionalProjectQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayResolversWithOutputTypeTestExceptionalProjectQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<0e2d032ecd3ec51cf764e966e1ee6164>> + * @generated SignedSource<<94fa59b9b1969957f89b6e6161d76e6d>> * @flow * @lightSyntaxTransform * @nogrep @@ -103,7 +103,7 @@ return { "fragment": null, "kind": "RelayLiveResolver", "name": "todos", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/QueryTodos').todos, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/QueryTodos').todos, "path": "todos", "normalizationInfo": { "kind": "OutputType", diff --git a/packages/react-relay/__tests__/__generated__/RelayResolversWithOutputTypeTestFragment.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolversWithOutputTypeTestFragment.graphql.js index c3ae25989d851..debc92a16d2f6 100644 --- a/packages/react-relay/__tests__/__generated__/RelayResolversWithOutputTypeTestFragment.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayResolversWithOutputTypeTestFragment.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<7461a0a38bb01a1f9e9401cec319c0e8>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -72,7 +72,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "text", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/TodoTextResolver').text, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/TodoTextResolver').text, "path": "text", "normalizationInfo": { "kind": "OutputType", diff --git a/packages/react-relay/__tests__/__generated__/RelayResolversWithOutputTypeTestManyLiveTodosQuery.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolversWithOutputTypeTestManyLiveTodosQuery.graphql.js index b2fa11e0e27a1..9dadf8891e966 100644 --- a/packages/react-relay/__tests__/__generated__/RelayResolversWithOutputTypeTestManyLiveTodosQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayResolversWithOutputTypeTestManyLiveTodosQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<04d4254435d6d6e20bf89146b058e903>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -89,7 +89,7 @@ return { "fragment": null, "kind": "RelayLiveResolver", "name": "many_live_todos", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/QueryManyLiveTodos').many_live_todos, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/QueryManyLiveTodos').many_live_todos, "path": "many_live_todos", "normalizationInfo": { "kind": "OutputType", diff --git a/packages/react-relay/__tests__/__generated__/RelayResolversWithOutputTypeTestManyTodosQuery.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolversWithOutputTypeTestManyTodosQuery.graphql.js index d6679c32c5965..8729b62d9de17 100644 --- a/packages/react-relay/__tests__/__generated__/RelayResolversWithOutputTypeTestManyTodosQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayResolversWithOutputTypeTestManyTodosQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<625f3d3da7a67e9fc7e7aeacf719b5c1>> + * @generated SignedSource<<8234a98afc2ebec72a3b7912ec081f8c>> * @flow * @lightSyntaxTransform * @nogrep @@ -106,7 +106,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "many_todos", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/QueryManyTodos').many_todos, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/QueryManyTodos').many_todos, "path": "many_todos", "normalizationInfo": { "kind": "OutputType", diff --git a/packages/react-relay/__tests__/__generated__/RelayResolversWithOutputTypeTestTextColorComponentFragment.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolversWithOutputTypeTestTextColorComponentFragment.graphql.js index 8bccdffd5b42c..9b3604f3a3e14 100644 --- a/packages/react-relay/__tests__/__generated__/RelayResolversWithOutputTypeTestTextColorComponentFragment.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayResolversWithOutputTypeTestTextColorComponentFragment.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<8ccda0e6400fbe28467e1480ff53c72f>> * @flow * @lightSyntaxTransform * @nogrep @@ -57,7 +57,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "human_readable_color", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/TodoTextColorResolver').human_readable_color, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/TodoTextColorResolver').human_readable_color, "path": "human_readable_color" } ], diff --git a/packages/react-relay/__tests__/__generated__/RelayResolversWithOutputTypeTestTodoCompleteFragment.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolversWithOutputTypeTestTodoCompleteFragment.graphql.js index c33d19af09c45..c9a91c0308bb4 100644 --- a/packages/react-relay/__tests__/__generated__/RelayResolversWithOutputTypeTestTodoCompleteFragment.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayResolversWithOutputTypeTestTodoCompleteFragment.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<43fa0b8db56a1cecbdfac01614f5af19>> + * @generated SignedSource<<05930bea2e8d0c7e9dec0c6ac85deb11>> * @flow * @lightSyntaxTransform * @nogrep @@ -57,7 +57,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "complete", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/TodoCompleteResolver').complete, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/TodoCompleteResolver').complete, "path": "complete" } ], diff --git a/packages/react-relay/__tests__/__generated__/RelayResolversWithOutputTypeTestTodoQuery.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolversWithOutputTypeTestTodoQuery.graphql.js index a207c46a9980f..2ecb0199ec041 100644 --- a/packages/react-relay/__tests__/__generated__/RelayResolversWithOutputTypeTestTodoQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayResolversWithOutputTypeTestTodoQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<0032a167bd192696ee98ab45717f504f>> + * @generated SignedSource<<1032383843b1e37f04b1c035fac490c6>> * @flow * @lightSyntaxTransform * @nogrep @@ -107,7 +107,7 @@ return { "fragment": null, "kind": "RelayLiveResolver", "name": "todo", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/QueryTodo').todo, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/QueryTodo').todo, "path": "todo", "normalizationInfo": { "kind": "OutputType", diff --git a/packages/react-relay/__tests__/__generated__/RelayResolversWithOutputTypeTestTodoWithBlockedQuery.graphql.js b/packages/react-relay/__tests__/__generated__/RelayResolversWithOutputTypeTestTodoWithBlockedQuery.graphql.js index 95f9221e8f7c0..7c0f67ded0b92 100644 --- a/packages/react-relay/__tests__/__generated__/RelayResolversWithOutputTypeTestTodoWithBlockedQuery.graphql.js +++ b/packages/react-relay/__tests__/__generated__/RelayResolversWithOutputTypeTestTodoWithBlockedQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -119,7 +119,7 @@ return { "fragment": null, "kind": "RelayLiveResolver", "name": "todo", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/QueryTodo').todo, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/QueryTodo').todo, "path": "todo", "normalizationInfo": { "kind": "OutputType", @@ -150,7 +150,7 @@ return { }, "kind": "RelayResolver", "name": "blocked_by", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/TodoBlockedByResolver').blocked_by, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/TodoBlockedByResolver').blocked_by, "path": "todo.blocked_by", "normalizationInfo": { "kind": "OutputType", diff --git a/packages/react-relay/__tests__/__generated__/StrongModel____relay_model_instance.graphql.js b/packages/react-relay/__tests__/__generated__/StrongModel____relay_model_instance.graphql.js index 7e049f4bbfc04..861479d2d817b 100644 --- a/packages/react-relay/__tests__/__generated__/StrongModel____relay_model_instance.graphql.js +++ b/packages/react-relay/__tests__/__generated__/StrongModel____relay_model_instance.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<6c5d33e4d39a223e0f74de3fb1be82ba>> + * @generated SignedSource<<121d882061018b32d818400b6e40b218>> * @flow * @lightSyntaxTransform * @nogrep @@ -57,7 +57,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "__relay_model_instance", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./StrongModel__id.graphql'), require('./../RelayResolverNullableModelClientEdge-test').StrongModel, 'id', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./StrongModel__id.graphql'), require('../RelayResolverNullableModelClientEdge-test').StrongModel, 'id', true), "path": "__relay_model_instance" } ], diff --git a/packages/react-relay/buildReactRelayContainer.js b/packages/react-relay/buildReactRelayContainer.js index f425c44635028..2ae1d4a85001d 100644 --- a/packages/react-relay/buildReactRelayContainer.js +++ b/packages/react-relay/buildReactRelayContainer.js @@ -28,15 +28,15 @@ const {getFragment} = require('relay-runtime'); const {useContext} = React; type ContainerCreator = ( - Component: React.ComponentType, + Component: component(...any), fragments: FragmentMap, -) => React.ComponentType; +) => component(...any); /** * Helper to create the Relay HOCs with ref forwarding, setting the displayName * and reading the React context. */ -function buildReactRelayContainer>( +function buildReactRelayContainer( ComponentClass: TBase, fragmentSpec: GeneratedNodeMap, createContainerWithFragments: ContainerCreator, @@ -82,8 +82,7 @@ function buildReactRelayContainer>( ); } ForwardRef.displayName = containerName; - // $FlowFixMe[incompatible-call] - const ForwardContainer = React.forwardRef(ForwardRef); + const ForwardContainer = (React as $FlowFixMe).forwardRef(ForwardRef); if (__DEV__) { // Used by RelayModernTestUtils diff --git a/packages/react-relay/getRootVariablesForFragments.js b/packages/react-relay/getRootVariablesForFragments.js index 50e84507bcf10..c0752bd269f21 100644 --- a/packages/react-relay/getRootVariablesForFragments.js +++ b/packages/react-relay/getRootVariablesForFragments.js @@ -30,8 +30,10 @@ function getRootVariablesForFragments( const selector = getSelector(fragmentNode, fragmentRef); const fragmentOwnerVariables = selector != null && selector.kind === 'PluralReaderSelector' - ? selector.selectors[0]?.owner.variables ?? {} - : selector?.owner.variables ?? {}; + ? (selector.selectors[0]?.owner.variables ?? {}) + : (selector?.owner.variables ?? {}); + /* $FlowFixMe[incompatible-indexer] Natural Inference rollout. See + * https://fburl.com/gdoc/y8dn025u */ rootVariables = { ...rootVariables, ...fragmentOwnerVariables, diff --git a/packages/react-relay/index.js b/packages/react-relay/index.js index a9e1520fd96e7..238e8d1a39d7d 100644 --- a/packages/react-relay/index.js +++ b/packages/react-relay/index.js @@ -28,7 +28,7 @@ const useFragment = require('./relay-hooks/useFragment'); const useLazyLoadQuery = require('./relay-hooks/useLazyLoadQuery'); const useMutation = require('./relay-hooks/useMutation'); const usePaginationFragment = require('./relay-hooks/usePaginationFragment'); -const usePrefetchableForwardPaginationFragment_EXPERIMENTAL = require('./relay-hooks/usePrefetchableForwardPaginationFragment_EXPERIMENTAL'); +const usePrefetchableForwardPaginationFragment = require('./relay-hooks/usePrefetchableForwardPaginationFragment'); const usePreloadedQuery = require('./relay-hooks/usePreloadedQuery'); const useQueryLoader = require('./relay-hooks/useQueryLoader'); const useRefetchableFragment = require('./relay-hooks/useRefetchableFragment'); @@ -126,8 +126,8 @@ module.exports = { usePaginationFragment: usePaginationFragment, usePreloadedQuery: usePreloadedQuery, useRefetchableFragment: useRefetchableFragment, - usePrefetchableForwardPaginationFragment_EXPERIMENTAL: - usePrefetchableForwardPaginationFragment_EXPERIMENTAL, + usePrefetchableForwardPaginationFragment: + usePrefetchableForwardPaginationFragment, useRelayEnvironment: useRelayEnvironment, useSubscribeToInvalidationState: useSubscribeToInvalidationState, useSubscription: useSubscription, diff --git a/packages/react-relay/multi-actor/__tests__/ActorChange-test.js b/packages/react-relay/multi-actor/__tests__/ActorChange-test.js index 23a25aae17b92..02011bb8aea61 100644 --- a/packages/react-relay/multi-actor/__tests__/ActorChange-test.js +++ b/packages/react-relay/multi-actor/__tests__/ActorChange-test.js @@ -185,6 +185,7 @@ describe('ActorChange', () => { ); }); + // $FlowFixMe[cannot-resolve-name] skipIf(process.env.OSS, 'should render a fragment for actor', () => { fetchFnForActor = jest.fn(actorId => Observable.from( @@ -286,6 +287,7 @@ describe('ActorChange', () => { }); skipIf( + // $FlowFixMe[cannot-resolve-name] process.env.OSS, 'should send a query and mutations with correct actor id, from the correct environment', () => { diff --git a/packages/react-relay/package.json b/packages/react-relay/package.json index b0641ce9aa8db..ba420c2a3b106 100644 --- a/packages/react-relay/package.json +++ b/packages/react-relay/package.json @@ -1,7 +1,7 @@ { "name": "react-relay", "description": "A framework for building GraphQL-driven React applications.", - "version": "18.2.0", + "version": "20.1.1", "keywords": [ "graphql", "relay", @@ -20,10 +20,10 @@ "fbjs": "^3.0.2", "invariant": "^2.2.4", "nullthrows": "^1.1.1", - "relay-runtime": "18.2.0" + "relay-runtime": "20.1.1" }, "peerDependencies": { - "react": "^16.9.0 || ^17 || ^18" + "react": "^16.9.0 || ^17 || ^18 || ^19" }, "directories": { "": "./" diff --git a/packages/react-relay/relay-hooks/EntryPointContainer.react.js b/packages/react-relay/relay-hooks/EntryPointContainer.react.js index 669b776f0085c..35a86164b9e5a 100644 --- a/packages/react-relay/relay-hooks/EntryPointContainer.react.js +++ b/packages/react-relay/relay-hooks/EntryPointContainer.react.js @@ -22,29 +22,25 @@ const React = require('react'); const {useContext, useEffect} = require('react'); const warning = require('warning'); -function EntryPointContainer< +component EntryPointContainer< // $FlowFixMe[unsupported-variance-annotation] - +TPreloadedQueries: {...}, + TRuntimeProps: {...}, + TRenders: React.Node, // $FlowFixMe[unsupported-variance-annotation] - +TPreloadedNestedEntryPoints: {...}, - // $FlowFixMe[unsupported-variance-annotation] - +TRuntimeProps: {...}, - // $FlowFixMe[unsupported-variance-annotation] - +TExtraProps, - // $FlowFixMe[unsupported-variance-annotation] - +TEntryPointComponent: EntryPointComponent< - TPreloadedQueries, - TPreloadedNestedEntryPoints, + TEntryPointComponent: EntryPointComponent< + // $FlowExpectedErrors[unclear-type] Use any to accept all kinds of EntryPointComponent + any, + // $FlowExpectedErrors[unclear-type] Use any to accept all kinds of EntryPointComponent + any, TRuntimeProps, - TExtraProps, + // $FlowExpectedErrors[unclear-type] Use any to accept all kinds of EntryPointComponent + any, + TRenders, >, ->({ - entryPointReference, - props, -}: $ReadOnly<{ +>( entryPointReference: PreloadedEntryPoint, props: TRuntimeProps, -}>): React.MixedElement { +) renders TRenders { warning( entryPointReference.isDisposed === false, ': Expected entryPointReference to not be disposed ' + diff --git a/packages/react-relay/relay-hooks/EntryPointTypes.flow.js b/packages/react-relay/relay-hooks/EntryPointTypes.flow.js index 85001ea7c9ac6..7bfab6e659431 100644 --- a/packages/react-relay/relay-hooks/EntryPointTypes.flow.js +++ b/packages/react-relay/relay-hooks/EntryPointTypes.flow.js @@ -36,6 +36,8 @@ export type PreloadFetchPolicy = export type PreloadOptions = { +fetchKey?: string | number, +fetchPolicy?: ?PreloadFetchPolicy, + +includeIf?: ?boolean, + +prefetchExpiryInHours?: ?number, +networkCacheConfig?: ?CacheConfig, }; @@ -178,14 +180,15 @@ export type EntryPointComponent< TPreloadedEntryPoints = {}, TRuntimeProps = {}, TExtraProps = null, -> = ComponentType< - EntryPointProps< + TRenders: React.Node = React.Node, +> = component( + ...EntryPointProps< TPreloadedQueries, TPreloadedEntryPoints, TRuntimeProps, TExtraProps, - >, ->; + > +) renders TRenders; // Return type of the `getPreloadProps(...)` of the entry point export type PreloadProps< @@ -290,6 +293,6 @@ export type EntryPoint = export type PreloadParamsOf = Parameters[0]; -export type IEnvironmentProvider = { +export type IEnvironmentProvider = $ReadOnly<{ getEnvironment: (options: ?TOptions) => IEnvironment, -}; +}>; diff --git a/packages/react-relay/relay-hooks/MatchContainer.js b/packages/react-relay/relay-hooks/MatchContainer.js index dc59daefbcde6..fba82f009063c 100644 --- a/packages/react-relay/relay-hooks/MatchContainer.js +++ b/packages/react-relay/relay-hooks/MatchContainer.js @@ -95,7 +95,7 @@ export type MatchPointer = { export type MatchContainerProps = { +fallback?: ?TFallback, - +loader: (module: mixed) => React.ComponentType, + +loader: (module: mixed) => component(...TProps), +match: ?MatchPointer | ?TypenameOnlyPointer, +props?: TProps, }; diff --git a/packages/react-relay/relay-hooks/RelayEnvironmentProvider.js b/packages/react-relay/relay-hooks/RelayEnvironmentProvider.js index 84241fba29d68..2ab7694ebbabc 100644 --- a/packages/react-relay/relay-hooks/RelayEnvironmentProvider.js +++ b/packages/react-relay/relay-hooks/RelayEnvironmentProvider.js @@ -22,15 +22,17 @@ const React = require('react'); const {useMemo} = React; -type Props = $ReadOnly<{ - children: React.Node, +type Props = $ReadOnly<{ + children: TChildren, environment: IEnvironment, getEnvironmentForActor?: ?( actorIdentifier: ActorIdentifier, ) => IActorEnvironment, }>; -function RelayEnvironmentProvider(props: Props): React.Node { +component RelayEnvironmentProvider( + ...props: Props +) renders TChildren { const {children, environment, getEnvironmentForActor} = props; const context = useMemo( () => ({environment, getEnvironmentForActor}), diff --git a/packages/react-relay/relay-hooks/__tests__/FragmentResource-ClientEdges-test.js b/packages/react-relay/relay-hooks/__tests__/FragmentResource-ClientEdges-test.js index 29cbc7a52afa6..d0935d63a4765 100644 --- a/packages/react-relay/relay-hooks/__tests__/FragmentResource-ClientEdges-test.js +++ b/packages/react-relay/relay-hooks/__tests__/FragmentResource-ClientEdges-test.js @@ -32,7 +32,7 @@ const BASIC_QUERY = graphql` query FragmentResourceClientEdgesTest1Query($id: ID!) { node(id: $id) { __typename - ...FragmentResourceClientEdgesTestFragment1 + ...FragmentResourceClientEdgesTestFragment1 @dangerously_unaliased_fixme } } `; diff --git a/packages/react-relay/relay-hooks/__tests__/FragmentResource-Resolver-test.js b/packages/react-relay/relay-hooks/__tests__/FragmentResource-Resolver-test.js index 9bcff17ec7e50..2aaffb99ae0d9 100644 --- a/packages/react-relay/relay-hooks/__tests__/FragmentResource-Resolver-test.js +++ b/packages/react-relay/relay-hooks/__tests__/FragmentResource-Resolver-test.js @@ -34,7 +34,7 @@ const BASIC_QUERY = graphql` query FragmentResourceResolverTest1Query($id: ID!) { node(id: $id) { __typename - ...FragmentResourceClientEdgesTestFragment1 + ...FragmentResourceClientEdgesTestFragment1 @dangerously_unaliased_fixme } } `; diff --git a/packages/react-relay/relay-hooks/__tests__/FragmentResource-SemanticNonNull-test.js b/packages/react-relay/relay-hooks/__tests__/FragmentResource-SemanticNonNull-test.js index c857854450fdd..a81359c1998e7 100644 --- a/packages/react-relay/relay-hooks/__tests__/FragmentResource-SemanticNonNull-test.js +++ b/packages/react-relay/relay-hooks/__tests__/FragmentResource-SemanticNonNull-test.js @@ -62,7 +62,9 @@ beforeEach(() => { node(id: $id) { __typename ...FragmentResourceSemanticNonNullTestFragment1 + @dangerously_unaliased_fixme ...FragmentResourceSemanticNonNullTestFragment2 + @dangerously_unaliased_fixme } } `, diff --git a/packages/react-relay/relay-hooks/__tests__/FragmentResource-WithOperationTracker-test.js b/packages/react-relay/relay-hooks/__tests__/FragmentResource-WithOperationTracker-test.js index d28ed063e2c95..a2ec8feb71fdc 100644 --- a/packages/react-relay/relay-hooks/__tests__/FragmentResource-WithOperationTracker-test.js +++ b/packages/react-relay/relay-hooks/__tests__/FragmentResource-WithOperationTracker-test.js @@ -70,6 +70,7 @@ describe.each([true, false])( query FragmentResourceWithOperationTrackerTestNodeQuery($id: ID!) { node(id: $id) { ...FragmentResourceWithOperationTrackerTestUserFragment + @dangerously_unaliased_fixme } } `; diff --git a/packages/react-relay/relay-hooks/__tests__/FragmentResource-WithOperationTrackerOptimisticUpdates-test.js b/packages/react-relay/relay-hooks/__tests__/FragmentResource-WithOperationTrackerOptimisticUpdates-test.js index 6cca0ac01238e..bf99d984ee0cc 100644 --- a/packages/react-relay/relay-hooks/__tests__/FragmentResource-WithOperationTrackerOptimisticUpdates-test.js +++ b/packages/react-relay/relay-hooks/__tests__/FragmentResource-WithOperationTrackerOptimisticUpdates-test.js @@ -61,6 +61,7 @@ describe('FragmentResource with Operation Tracker for optimistic updates behavio node(id: $id) { __typename ...FragmentResourceWithOperationTrackerOptimisticUpdatesTestFragment + @dangerously_unaliased_fixme } } `; diff --git a/packages/react-relay/relay-hooks/__tests__/FragmentResource-WithOperationTrackerSuspense-test.js b/packages/react-relay/relay-hooks/__tests__/FragmentResource-WithOperationTrackerSuspense-test.js index 77211d64c1473..a0b7648286b05 100644 --- a/packages/react-relay/relay-hooks/__tests__/FragmentResource-WithOperationTrackerSuspense-test.js +++ b/packages/react-relay/relay-hooks/__tests__/FragmentResource-WithOperationTrackerSuspense-test.js @@ -64,6 +64,7 @@ describe('FragmentResource with Operation Tracker and Suspense behavior', () => node(id: $id) { __typename ...FragmentResourceWithOperationTrackerSuspenseTestFragment + @dangerously_unaliased_fixme } } `; diff --git a/packages/react-relay/relay-hooks/__tests__/FragmentResource-test.js b/packages/react-relay/relay-hooks/__tests__/FragmentResource-test.js index 1318e1adec816..96dea1f46892f 100644 --- a/packages/react-relay/relay-hooks/__tests__/FragmentResource-test.js +++ b/packages/react-relay/relay-hooks/__tests__/FragmentResource-test.js @@ -160,7 +160,7 @@ describe('FragmentResource', () => { query FragmentResourceTest1Query($id: ID!) { node(id: $id) { __typename - ...FragmentResourceTest1Fragment + ...FragmentResourceTest1Fragment @dangerously_unaliased_fixme } } `; @@ -176,7 +176,7 @@ describe('FragmentResource', () => { query FragmentResourceTest2Query($id: ID!) { node(id: $id) { __typename - ...FragmentResourceTest2Fragment + ...FragmentResourceTest2Fragment @dangerously_unaliased_fixme } } `; @@ -449,7 +449,7 @@ describe('FragmentResource', () => { node(id: $id) { __typename name @include(if: $foo) - ...FragmentResourceTest6Fragment + ...FragmentResourceTest6Fragment @dangerously_unaliased_fixme } } `; @@ -712,7 +712,7 @@ describe('FragmentResource', () => { componentDisplayName, ), ).toThrow( - "Relay: Expected to receive an object where `...FragmentResourceTest1Fragment` was spread, but the fragment reference was not found`. This is most likely the result of:\n- Forgetting to spread `FragmentResourceTest1Fragment` in `TestComponent`'s parent's fragment.\n- Conditionally fetching `FragmentResourceTest1Fragment` but unconditionally passing a fragment reference prop to `TestComponent`. If the parent fragment only fetches the fragment conditionally - with e.g. `@include`, `@skip`, or inside a `... on SomeType { }` spread - then the fragment reference will not exist. In this case, pass `null` if the conditions for evaluating the fragment are not met (e.g. if the `@include(if)` value is false.)", + `Relay: Expected to receive an object where \`...FragmentResourceTest1Fragment\` was spread, but the fragment reference was not found\`.`, ); }); }); diff --git a/packages/react-relay/relay-hooks/__tests__/FragmentResourceRequiredField-test.js b/packages/react-relay/relay-hooks/__tests__/FragmentResourceRequiredField-test.js index 93f21e192d822..8f8cffbb03c35 100644 --- a/packages/react-relay/relay-hooks/__tests__/FragmentResourceRequiredField-test.js +++ b/packages/react-relay/relay-hooks/__tests__/FragmentResourceRequiredField-test.js @@ -56,6 +56,7 @@ beforeEach(() => { node(id: $id) { __typename ...FragmentResourceRequiredFieldTestUserFragment + @dangerously_unaliased_fixme } } `, diff --git a/packages/react-relay/relay-hooks/__tests__/LazyLoadEntryPointContainer_DEEPRECATED-test.js b/packages/react-relay/relay-hooks/__tests__/LazyLoadEntryPointContainer_DEEPRECATED-test.js index fb352876c4767..9e5e2e90fbb8f 100644 --- a/packages/react-relay/relay-hooks/__tests__/LazyLoadEntryPointContainer_DEEPRECATED-test.js +++ b/packages/react-relay/relay-hooks/__tests__/LazyLoadEntryPointContainer_DEEPRECATED-test.js @@ -133,7 +133,7 @@ beforeEach(() => { }); params = { - kind: 'PreloadableConcreteRequest', + kind: 'PreloadableConcreteRequest' as const, params: query.params, }; entryPoint = { @@ -269,6 +269,7 @@ it.skip('suspends then updates when the query and component load', () => { expect(entryPoint.root.load).toBeCalledTimes(1); expect(receivedProps).not.toBe(null); expect(receivedProps?.props).toBe(otherProps); + // $FlowFixMe[incompatible-use] expect(renderer.toJSON()).toEqual('Zuck'); }); @@ -355,6 +356,7 @@ it('re-renders without reloading when non-prefetch props change', () => { , ); }); + // $FlowFixMe[incompatible-use] expect(renderer.toJSON()).toEqual('Zuck'); expect(Component).toBeCalledTimes(2); expect(entryPoint.getPreloadProps).toBeCalledTimes(1); @@ -404,6 +406,7 @@ it.skip('re-renders and reloads when prefetch params change', () => { , ); }); + // $FlowFixMe[incompatible-use] expect(renderer.toJSON()).toEqual('Fallback'); expect(Component).toBeCalledTimes(2); expect(entryPoint.getPreloadProps).toBeCalledTimes(2); @@ -422,6 +425,7 @@ it.skip('re-renders and reloads when prefetch params change', () => { }); dataSource.complete(); TestRenderer.act(() => jest.runAllImmediates()); + // $FlowFixMe[incompatible-use] expect(renderer.toJSON()).toEqual('Mark'); }); diff --git a/packages/react-relay/relay-hooks/__tests__/MatchContainer-test.js b/packages/react-relay/relay-hooks/__tests__/MatchContainer-test.js index 26c434d30714b..e49a58b6e958b 100644 --- a/packages/react-relay/relay-hooks/__tests__/MatchContainer-test.js +++ b/packages/react-relay/relay-hooks/__tests__/MatchContainer-test.js @@ -53,7 +53,7 @@ describe('MatchContainer', () => { beforeEach(() => { jest.resetModules(); - loader = jest.fn<[mixed], React.ComponentType>(); + loader = jest.fn<[mixed], component(...any)>(); // $FlowFixMe[missing-local-annot] error found when enabling Flow LTI mode UserComponent = jest.fn(props => (

diff --git a/packages/react-relay/relay-hooks/__tests__/QueryResource-test.js b/packages/react-relay/relay-hooks/__tests__/QueryResource-test.js index 650f373e77859..89e4e60acae58 100644 --- a/packages/react-relay/relay-hooks/__tests__/QueryResource-test.js +++ b/packages/react-relay/relay-hooks/__tests__/QueryResource-test.js @@ -11,7 +11,7 @@ 'use strict'; -import type {FetchPolicy, Subscription} from 'relay-runtime'; +import type {FetchPolicy, RenderPolicy, Subscription} from 'relay-runtime'; const {getQueryResourceForEnvironment} = require('../QueryResource'); const { @@ -35,7 +35,7 @@ disallowConsoleErrors(); describe('QueryResource', () => { let environment; let QueryResource; - let fetchPolicy; + let fetchPolicy: FetchPolicy; let fetchObservable; let fetchObservableMissingData; let fetchObserverableLiveMissingData; @@ -46,7 +46,7 @@ describe('QueryResource', () => { let liveQueryMissingData; let gqlLiveQueryMissingData; let release; - let renderPolicy; + let renderPolicy: RenderPolicy; let store; const variables = { id: '4', @@ -414,7 +414,7 @@ describe('QueryResource', () => { query QueryResourceTest3Query($id: ID!) { node(id: $id) { __typename - ...QueryResourceTest1Fragment + ...QueryResourceTest1Fragment @dangerously_unaliased_fixme } } `; @@ -472,7 +472,7 @@ describe('QueryResource', () => { query QueryResourceTest4Query($id: ID!) { node(id: $id) { __typename - ...QueryResourceTest2Fragment + ...QueryResourceTest2Fragment @dangerously_unaliased_fixme } } `; @@ -528,7 +528,7 @@ describe('QueryResource', () => { query QueryResourceTest5Query($id: ID!) { node(id: $id) { __typename - ...QueryResourceTest3Fragment + ...QueryResourceTest3Fragment @dangerously_unaliased_fixme } } `; @@ -884,7 +884,7 @@ describe('QueryResource', () => { query QueryResourceTest6Query($id: ID!) { node(id: $id) { __typename - ...QueryResourceTest4Fragment + ...QueryResourceTest4Fragment @dangerously_unaliased_fixme } } `; @@ -942,7 +942,7 @@ describe('QueryResource', () => { query QueryResourceTest7Query($id: ID!) { node(id: $id) { __typename - ...QueryResourceTest5Fragment + ...QueryResourceTest5Fragment @dangerously_unaliased_fixme } } `; @@ -998,7 +998,9 @@ describe('QueryResource', () => { node(id: $id) { __typename id - ...QueryResourceTest6Fragment @defer + ...QueryResourceTest6Fragment + @dangerously_unaliased_fixme + @defer } } `; @@ -2892,7 +2894,7 @@ describe('QueryResource, with an environment meant for SSR', () => { let gqlQuery; let query; let release; - let renderPolicy; + let renderPolicy: RenderPolicy; const variables = { id: '4', }; diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceClientEdgesTest1Query.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceClientEdgesTest1Query.graphql.js index 75ed6ddf7ad1b..b531285de8993 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceClientEdgesTest1Query.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceClientEdgesTest1Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<0a547f35f5a76a3b0a391aa204a5858d>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -151,7 +151,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "a45d5fe34c226d737ff1adb0205a4141"; + (node/*: any*/).hash = "12b2f7755a7f2800f27cf07a3b735b7f"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceClientEdgesTestFragment1.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceClientEdgesTestFragment1.graphql.js index 527e702d2f5bc..163d56125424c 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceClientEdgesTestFragment1.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceClientEdgesTestFragment1.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<11cda9107ce9a5b3931cd2dcc9e3e176>> + * @generated SignedSource<<7028fe2aede7e97bfd47e8cc72cc68db>> * @flow * @lightSyntaxTransform * @nogrep @@ -66,7 +66,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "client_edge", - "resolverModule": require('./../../../../relay-runtime/store/__tests__/resolvers/UserClientEdgeResolver').client_edge, + "resolverModule": require('../../../../relay-runtime/store/__tests__/resolvers/UserClientEdgeResolver').client_edge, "path": "client_edge" }, "linkedField": { diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceRequiredFieldTestUserQuery.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceRequiredFieldTestUserQuery.graphql.js index f879414d3ea2f..38adbaaff3cc7 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceRequiredFieldTestUserQuery.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceRequiredFieldTestUserQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<46550cd0d6791b7406a22bdbf66a6def>> + * @generated SignedSource<<210e2d96d0e20787747f361efbcfb67f>> * @flow * @lightSyntaxTransform * @nogrep @@ -144,7 +144,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "d8c62e66c365d6179520bf2a12a9f8ac"; + (node/*: any*/).hash = "4d73d93e8b781ced6ccafcf8d0b963e7"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceResolverTest1Query.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceResolverTest1Query.graphql.js index d7b7d345b1c03..9ff6351b49ee5 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceResolverTest1Query.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceResolverTest1Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<92f8ffd1c3c7db7f5dfbe80710eee93f>> * @flow * @lightSyntaxTransform * @nogrep @@ -151,7 +151,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "e5738684a7645492621dd008c800ee24"; + (node/*: any*/).hash = "8255413a07b7c7f7caeb7dd17db86835"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceResolverTestFragment1.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceResolverTestFragment1.graphql.js index 3cdef5d099308..be456974ec321 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceResolverTestFragment1.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceResolverTestFragment1.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<668fccd4d0035a3fcbae78463b67958b>> * @flow * @lightSyntaxTransform * @nogrep @@ -57,7 +57,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "always_throws", - "resolverModule": require('./../../../../relay-runtime/store/__tests__/resolvers/UserAlwaysThrowsResolver').always_throws, + "resolverModule": require('../../../../relay-runtime/store/__tests__/resolvers/UserAlwaysThrowsResolver').always_throws, "path": "always_throws" } ], diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceSemanticNonNullTestQuery.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceSemanticNonNullTestQuery.graphql.js index 9df00e206ca39..eae336860c7ae 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceSemanticNonNullTestQuery.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceSemanticNonNullTestQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<9edf937671528fb8914422019bada16e>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -143,7 +143,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "79609fa487e20403b5757ec5fe030446"; + (node/*: any*/).hash = "530d4a1ded82952de97544eaa016b219"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceTest1Query.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceTest1Query.graphql.js index ac513a45ce13d..13b436b2dc353 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceTest1Query.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceTest1Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<55a15f486d0a77f5d41718c1277d9c19>> * @flow * @lightSyntaxTransform * @nogrep @@ -137,7 +137,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "bf5cd6051d37288b658f3312de8c527a"; + (node/*: any*/).hash = "cc7b893d1f91ada940b65acd486ebdf8"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceTest2Query.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceTest2Query.graphql.js index 5753ef2113af7..0c5dc8aaa6267 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceTest2Query.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceTest2Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<492f5a13adcdccae4ff4c0c25d2bd98a>> + * @generated SignedSource<<08a13fc9537b8da73c09d9b783f91a65>> * @flow * @lightSyntaxTransform * @nogrep @@ -144,7 +144,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "9485c8a47f2c7a7fa324c0997ffc6869"; + (node/*: any*/).hash = "3dcce69a04edf144de4803ff886fd056"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceTest6Query.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceTest6Query.graphql.js index 4cdcdc4c94585..2557207e8620b 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceTest6Query.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceTest6Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<727afb0fdaccca2f20b0f1be83b1eaec>> + * @generated SignedSource<<50516fea5aed3c47306f84a03c246326>> * @flow * @lightSyntaxTransform * @nogrep @@ -157,7 +157,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "6729d563666fcfaad04fef9ac67fe82f"; + (node/*: any*/).hash = "b3516b8d8ace6d328f7c9b16fbbd16e7"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceWithOperationTrackerOptimisticUpdatesTestQuery.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceWithOperationTrackerOptimisticUpdatesTestQuery.graphql.js index 91382633a1dc1..03cf46501b39b 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceWithOperationTrackerOptimisticUpdatesTestQuery.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceWithOperationTrackerOptimisticUpdatesTestQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<74a5dbdc8516bd27deb03e9589c5ec06>> + * @generated SignedSource<<43667d825dc762821b93c44a4fd2ece1>> * @flow * @lightSyntaxTransform * @nogrep @@ -137,7 +137,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "14c673af170df5bad4f4905bb7368415"; + (node/*: any*/).hash = "92ff47cf9f81c373192ab8d76a50f574"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceWithOperationTrackerSuspenseTestQuery.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceWithOperationTrackerSuspenseTestQuery.graphql.js index f8c4e905bb48e..dc9fe06d34f99 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceWithOperationTrackerSuspenseTestQuery.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceWithOperationTrackerSuspenseTestQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<3913d108171e606ef0523ee539b2b4f1>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -137,7 +137,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "cbef95bbb7223d9a6dca6b4123ef5730"; + (node/*: any*/).hash = "b3105aa8587f88e054af71980209e680"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceWithOperationTrackerTestNodeQuery.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceWithOperationTrackerTestNodeQuery.graphql.js index ee949e0273c3d..93b9a393e051e 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceWithOperationTrackerTestNodeQuery.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/FragmentResourceWithOperationTrackerTestNodeQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<52a8c1b75facb93f5014c33c45de06a7>> + * @generated SignedSource<<19fc9778d41a62d22c998b391918e9b3>> * @flow * @lightSyntaxTransform * @nogrep @@ -216,7 +216,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "e4c3c9f6213e6aca299a02aff58349e3"; + (node/*: any*/).hash = "43da25d82b333c14f0570f553ebc8631"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/QueryResourceTest3Query.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/QueryResourceTest3Query.graphql.js index d6b8573624162..2e4585a127ea8 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/QueryResourceTest3Query.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/QueryResourceTest3Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<21b0ced9a0271e12434a3f814023dba7>> + * @generated SignedSource<<4a36a85370be58e4d9e3e17e3e20c554>> * @flow * @lightSyntaxTransform * @nogrep @@ -123,7 +123,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "0cdada5db8e942ed6d118885802edcc4"; + (node/*: any*/).hash = "08191eeb9de0fb6b85b5e9abeb33bd0e"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/QueryResourceTest4Query.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/QueryResourceTest4Query.graphql.js index 142a5db12658d..c36a8b2ef906c 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/QueryResourceTest4Query.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/QueryResourceTest4Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<0be85a4f15a03050729c4a354df810aa>> * @flow * @lightSyntaxTransform * @nogrep @@ -137,7 +137,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "f60bd73c9198b5edbb1b218d58b8828b"; + (node/*: any*/).hash = "988e78fe20f9d313094ef46433092ce7"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/QueryResourceTest5Query.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/QueryResourceTest5Query.graphql.js index f1ab23fdb88e2..6ce5d9775626c 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/QueryResourceTest5Query.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/QueryResourceTest5Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<8a9e361f30759be638a0e58e54d2744b>> + * @generated SignedSource<<4baa57782add3272b9ce912cc50f3270>> * @flow * @lightSyntaxTransform * @nogrep @@ -123,7 +123,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "602fa3055d2ebb555eef09c120f95c19"; + (node/*: any*/).hash = "1db024536cad50b8dffa9214614b980e"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/QueryResourceTest6Query.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/QueryResourceTest6Query.graphql.js index 8bfb231204a21..691d51611a3cd 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/QueryResourceTest6Query.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/QueryResourceTest6Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -123,7 +123,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "2aadb7307400e7819f52bcdad17c5301"; + (node/*: any*/).hash = "19bc0d618b5dced35c705f3ba789fdd6"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/QueryResourceTest7Query.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/QueryResourceTest7Query.graphql.js index 15591f92b2583..9126f24f3190f 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/QueryResourceTest7Query.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/QueryResourceTest7Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<48ac33e8d5ff8d8979d680e1e90b67c8>> * @flow * @lightSyntaxTransform * @nogrep @@ -137,7 +137,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "bf937ab1046354c4b80e3a07597b4bfa"; + (node/*: any*/).hash = "256baee2dce2b172d5cf955589c1ab8f"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/QueryResourceTest8Query.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/QueryResourceTest8Query.graphql.js index 87725a6d5fc8d..193a4d3f6d86c 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/QueryResourceTest8Query.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/QueryResourceTest8Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<34539f71f610e0639e8dd0eeee091643>> * @flow * @lightSyntaxTransform * @nogrep @@ -153,7 +153,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "6e87df11edb8826c7aad49628ee3bb71"; + (node/*: any*/).hash = "5e0f54bce33d434141c59d68d5c52013"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/preloadQueryDEPRECATEDTest_ProvidedVarQuery.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/preloadQueryDEPRECATEDTest_ProvidedVarQuery.graphql.js index 7fa7134a61359..c943e27a0d35b 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/preloadQueryDEPRECATEDTest_ProvidedVarQuery.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/preloadQueryDEPRECATEDTest_ProvidedVarQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<593b3030b73bbadda5607047611b5d02>> * @flow * @lightSyntaxTransform * @nogrep @@ -32,8 +32,8 @@ export type preloadQueryDEPRECATEDTest_ProvidedVarQuery = {| variables: preloadQueryDEPRECATEDTest_ProvidedVarQuery$variables, |}; ({ - "__relay_internal__pv__RelayProvider_returnsTruerelayprovider": require('./../RelayProvider_returnsTrue.relayprovider'), - "__relay_internal__pv__RelayProvider_returnsFalserelayprovider": require('./../RelayProvider_returnsFalse.relayprovider') + "__relay_internal__pv__RelayProvider_returnsTruerelayprovider": require('../RelayProvider_returnsTrue.relayprovider'), + "__relay_internal__pv__RelayProvider_returnsFalserelayprovider": require('../RelayProvider_returnsFalse.relayprovider') }: {| +__relay_internal__pv__RelayProvider_returnsFalserelayprovider: {| +get: () => boolean, @@ -202,15 +202,15 @@ return { "operationKind": "query", "text": "query preloadQueryDEPRECATEDTest_ProvidedVarQuery(\n $id: ID!\n $__relay_internal__pv__RelayProvider_returnsTruerelayprovider: Boolean!\n $__relay_internal__pv__RelayProvider_returnsFalserelayprovider: Boolean!\n) {\n node(id: $id) {\n __typename\n ...preloadQueryDEPRECATEDTest_ProvidedVarFragment\n id\n }\n}\n\nfragment preloadQueryDEPRECATEDTest_ProvidedVarFragment on User {\n name @include(if: $__relay_internal__pv__RelayProvider_returnsTruerelayprovider)\n firstName @include(if: $__relay_internal__pv__RelayProvider_returnsFalserelayprovider)\n lastName @skip(if: $__relay_internal__pv__RelayProvider_returnsFalserelayprovider)\n username @skip(if: $__relay_internal__pv__RelayProvider_returnsTruerelayprovider)\n}\n", "providedVariables": { - "__relay_internal__pv__RelayProvider_returnsTruerelayprovider": require('./../RelayProvider_returnsTrue.relayprovider'), - "__relay_internal__pv__RelayProvider_returnsFalserelayprovider": require('./../RelayProvider_returnsFalse.relayprovider') + "__relay_internal__pv__RelayProvider_returnsTruerelayprovider": require('../RelayProvider_returnsTrue.relayprovider'), + "__relay_internal__pv__RelayProvider_returnsFalserelayprovider": require('../RelayProvider_returnsFalse.relayprovider') } } }; })(); if (__DEV__) { - (node/*: any*/).hash = "949cb1280a8579bbc809976fd4ed18c6"; + (node/*: any*/).hash = "4979a880c3f4961191919f23c6de8c42"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/useBlockingPaginationFragmentTestUserQuery.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/useBlockingPaginationFragmentTestUserQuery.graphql.js index 48c79b864c2b4..c2773696af3df 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/useBlockingPaginationFragmentTestUserQuery.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/useBlockingPaginationFragmentTestUserQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<2b24879dbe7d70ef4f82329d7920aa18>> + * @generated SignedSource<<81e3953d5a549a21fb4c20a9bb9f79e6>> * @flow * @lightSyntaxTransform * @nogrep @@ -328,7 +328,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "24a79476464a446cf23640ec65b6850f"; + (node/*: any*/).hash = "56876e3affcf427dac0fddd361ed0707"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/useBlockingPaginationFragmentTestUserQueryNestedFragmentQuery.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/useBlockingPaginationFragmentTestUserQueryNestedFragmentQuery.graphql.js index cd6d8f769b7f4..49cec7d79a056 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/useBlockingPaginationFragmentTestUserQueryNestedFragmentQuery.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/useBlockingPaginationFragmentTestUserQueryNestedFragmentQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<60642da297baaa6e036f8ad58d2e74b4>> + * @generated SignedSource<<8e59b06a123a216465358c6af67591d1>> * @flow * @lightSyntaxTransform * @nogrep @@ -354,7 +354,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "19018d89e0c127bfa85e874e07a4b743"; + (node/*: any*/).hash = "82ec6417d1b5231f70f297af02cfa3e9"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/useBlockingPaginationFragmentTestUserQueryWithLiteralArgsQuery.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/useBlockingPaginationFragmentTestUserQueryWithLiteralArgsQuery.graphql.js index d5a7f723adbb7..80bb0a4621c9c 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/useBlockingPaginationFragmentTestUserQueryWithLiteralArgsQuery.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/useBlockingPaginationFragmentTestUserQueryWithLiteralArgsQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<4e6f2e06315d1a96223ff40ed272a7a7>> + * @generated SignedSource<<5f766aedba037b882a891998cbc21dee>> * @flow * @lightSyntaxTransform * @nogrep @@ -314,7 +314,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "48433b64c70ecbb519ed6a7d45ec3952"; + (node/*: any*/).hash = "43b6d41944fcdea59eebc1d5f59fe65c"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/useBlockingPaginationFragmentTestUserQueryWithoutIDQuery.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/useBlockingPaginationFragmentTestUserQueryWithoutIDQuery.graphql.js index 9d305ddf3f1f7..963e977110c53 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/useBlockingPaginationFragmentTestUserQueryWithoutIDQuery.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/useBlockingPaginationFragmentTestUserQueryWithoutIDQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<3f1bf507d7b273a2047f88b3384d35a1>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -337,7 +337,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "f5f7783184358dbce0a1bd2544fdddd4"; + (node/*: any*/).hash = "259619db11a4a82a8c105f3462338e2b"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/useBlockingPaginationFragmentWithSuspenseTransitionTestUserQuery.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/useBlockingPaginationFragmentWithSuspenseTransitionTestUserQuery.graphql.js index 416106de396e7..b5fc635c778a2 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/useBlockingPaginationFragmentWithSuspenseTransitionTestUserQuery.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/useBlockingPaginationFragmentWithSuspenseTransitionTestUserQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<57ed9b4c4ffc0e3b54a7e5397cb3e191>> * @flow * @lightSyntaxTransform * @nogrep @@ -354,7 +354,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "d89c8846126eef2be90954dd65755d31"; + (node/*: any*/).hash = "5ec19807252bd1d9073922445aefa2a8"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/useBlockingPaginationFragmentWithSuspenseTransitionTestUserQueryWithoutIDQuery.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/useBlockingPaginationFragmentWithSuspenseTransitionTestUserQueryWithoutIDQuery.graphql.js index 5bad8df4519c1..a065a109eef40 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/useBlockingPaginationFragmentWithSuspenseTransitionTestUserQueryWithoutIDQuery.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/useBlockingPaginationFragmentWithSuspenseTransitionTestUserQueryWithoutIDQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<9cfdf56c655590459bfac06d65fcc5bc>> + * @generated SignedSource<<534e25badb1599dfe036b7f761a950c9>> * @flow * @lightSyntaxTransform * @nogrep @@ -337,7 +337,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "8d3ec6e6a62b839f261ad74ddca4588a"; + (node/*: any*/).hash = "82c3921e9fe483d213ac9b7581afd1de"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/useEntryPointLoaderReactDoubleEffectsTestUserQuery.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/useEntryPointLoaderReactDoubleEffectsTestUserQuery.graphql.js index 3e46026e1aea4..8d4dd9cd82fe7 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/useEntryPointLoaderReactDoubleEffectsTestUserQuery.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/useEntryPointLoaderReactDoubleEffectsTestUserQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<3ca775c4cf21029abec02867b8b46c0f>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -133,7 +133,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "d296fc0f253f3d0b6940f48b0e01845e"; + (node/*: any*/).hash = "6b7f3606d43827b1210a7de7c6a81556"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/useFragmentNodeReactDoubleEffectsTestUserQuery.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/useFragmentNodeReactDoubleEffectsTestUserQuery.graphql.js index d46e17712dec2..c1df40f17accf 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/useFragmentNodeReactDoubleEffectsTestUserQuery.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/useFragmentNodeReactDoubleEffectsTestUserQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<6124b7833cd3997376d25200238af31e>> + * @generated SignedSource<<6e54926b33fa38bd596e1b7b0b314562>> * @flow * @lightSyntaxTransform * @nogrep @@ -134,7 +134,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "362966f1b2b2a5e926134b5ad581e901"; + (node/*: any*/).hash = "8558f0f05b05382bdd2044c0b2e5f4e6"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/useFragmentNodeRequiredTestUserQuery.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/useFragmentNodeRequiredTestUserQuery.graphql.js index bde7893d275f2..a1c5bb3bfe2bd 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/useFragmentNodeRequiredTestUserQuery.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/useFragmentNodeRequiredTestUserQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<717bc84de90168f0b2ea717596fe8145>> + * @generated SignedSource<<7730fe0ebb5b361b1703da8bdaf0ac6d>> * @flow * @lightSyntaxTransform * @nogrep @@ -134,7 +134,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "fc7ae7325babad6ab4c2fbda74adc09d"; + (node/*: any*/).hash = "681f0f81107d574d43702ccd528a3a71"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/useFragmentNodeTestUserQuery.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/useFragmentNodeTestUserQuery.graphql.js index 60e7561472507..f654acdf6d942 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/useFragmentNodeTestUserQuery.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/useFragmentNodeTestUserQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<2b2e9dd521238896ef66b278c2a11075>> + * @generated SignedSource<<1b9cd4b4e3f5731a7f403eb88a70d3c4>> * @flow * @lightSyntaxTransform * @nogrep @@ -171,7 +171,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "84829266b419cba0d2a2e681f3d1db63"; + (node/*: any*/).hash = "1445e0037c0bb573c2ca3f35d1f84cf7"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/useFragmentNullabilityTest1Query.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/useFragmentNullabilityTest1Query.graphql.js index 9f8ebc2431b55..aeb2ac97156e0 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/useFragmentNullabilityTest1Query.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/useFragmentNullabilityTest1Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<124d562a9ef8b2a66a2f45a6cea04174>> * @flow * @lightSyntaxTransform * @nogrep @@ -54,7 +54,7 @@ var node/*: ClientRequest*/ = { "fragment": null, "kind": "RelayResolver", "name": "field_that_throws", - "resolverModule": require('./../useFragment_nullability-test').field_that_throws, + "resolverModule": require('../useFragment_nullability-test').field_that_throws, "path": "field_that_throws" } ] diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/useFragmentNullabilityTest2Query.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/useFragmentNullabilityTest2Query.graphql.js index 1db40a2b07e10..b9f2c38ed0e07 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/useFragmentNullabilityTest2Query.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/useFragmentNullabilityTest2Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<944174802f80cfe9e20a1cc95e6b62c6>> + * @generated SignedSource<<3e68f3ca839652cc36ec9e58aea48ac4>> * @flow * @lightSyntaxTransform * @nogrep @@ -57,7 +57,7 @@ var node/*: ClientRequest*/ = { }, "kind": "RelayResolver", "name": "field_with_fragment_that_throws", - "resolverModule": require('./../useFragment_nullability-test').field_with_fragment_that_throws, + "resolverModule": require('../useFragment_nullability-test').field_with_fragment_that_throws, "path": "field_with_fragment_that_throws" } ], diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/useFragmentNullabilityTest3Query.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/useFragmentNullabilityTest3Query.graphql.js index 3a4105826975d..11a136588f7cd 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/useFragmentNullabilityTest3Query.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/useFragmentNullabilityTest3Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<47421c9c8675dce5d268e4221778f812>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -55,7 +55,7 @@ var node/*: ClientRequest*/ = { }, "kind": "RelayResolver", "name": "field_with_fragment_that_throws", - "resolverModule": require('./../useFragment_nullability-test').field_with_fragment_that_throws, + "resolverModule": require('../useFragment_nullability-test').field_with_fragment_that_throws, "path": "field_with_fragment_that_throws" } ], diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/useFragmentNullabilityTestFragmentWithFieldThatThrows.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/useFragmentNullabilityTestFragmentWithFieldThatThrows.graphql.js index 147243cba3cde..45d112167b7dd 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/useFragmentNullabilityTestFragmentWithFieldThatThrows.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/useFragmentNullabilityTestFragmentWithFieldThatThrows.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -56,7 +56,7 @@ var node/*: ReaderFragment*/ = { "fragment": null, "kind": "RelayResolver", "name": "field_that_throws", - "resolverModule": require('./../useFragment_nullability-test').field_that_throws, + "resolverModule": require('../useFragment_nullability-test').field_that_throws, "path": "field_that_throws" } ] diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/useFragmentTestUserQuery.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/useFragmentTestUserQuery.graphql.js index f50d2fe6ef87a..19064abe601bb 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/useFragmentTestUserQuery.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/useFragmentTestUserQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<33b44471531b2bc8bc4ca6f476203cc6>> * @flow * @lightSyntaxTransform * @nogrep @@ -141,7 +141,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "b7cb456d0fac348a436c061eb926e10b"; + (node/*: any*/).hash = "3b73e10cf16353d2ea6f653510d3874b"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/useFragmentWithOperationTrackerSuspenseTestQuery.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/useFragmentWithOperationTrackerSuspenseTestQuery.graphql.js index 5d366ce3fdbed..c5abc2c47a70c 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/useFragmentWithOperationTrackerSuspenseTestQuery.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/useFragmentWithOperationTrackerSuspenseTestQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<3c807f2294b482947010f72e07e490a2>> * @flow * @lightSyntaxTransform * @nogrep @@ -137,7 +137,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "d9a040d89152cefea35ea45be32edaa5"; + (node/*: any*/).hash = "d21e697bfd19cd686c5039c4a5d8d27f"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/useFragmentWithRequiredTestQuery.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/useFragmentWithRequiredTestQuery.graphql.js index b712fb7ca1959..d43d571dad5d7 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/useFragmentWithRequiredTestQuery.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/useFragmentWithRequiredTestQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<15c3c6b4bed449af7f25913d945cc922>> * @flow * @lightSyntaxTransform * @nogrep @@ -141,7 +141,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "66a4cfb191113d8dc82023073e6a8884"; + (node/*: any*/).hash = "13a5758b6ca5410f0169950b83543b47"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/useIsParentQueryActiveTestUserDeferQuery.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/useIsParentQueryActiveTestUserDeferQuery.graphql.js index 2a64ff619c67b..eb7b1ce5ba662 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/useIsParentQueryActiveTestUserDeferQuery.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/useIsParentQueryActiveTestUserDeferQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<8b64ffb21bc96f31e1e05c0d81d28cf2>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -148,7 +148,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "4bd5e517a288bf4fcf9d2330b6d2e13e"; + (node/*: any*/).hash = "567eaed2bb0068576063e236f9b4560a"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/useIsParentQueryActiveTestUserQuery.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/useIsParentQueryActiveTestUserQuery.graphql.js index a6b4afe967b4b..0c438c9bfc675 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/useIsParentQueryActiveTestUserQuery.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/useIsParentQueryActiveTestUserQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -134,7 +134,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "f41a54a0c430c98e7f9e1975a2cfac15"; + (node/*: any*/).hash = "6eb3252eb53f89ed77bbdecca56eae94"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/useLazyLoadQueryNodeActivityTestUserQuery.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/useLazyLoadQueryNodeActivityTestUserQuery.graphql.js index c8581a4d68cc7..afec39c547259 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/useLazyLoadQueryNodeActivityTestUserQuery.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/useLazyLoadQueryNodeActivityTestUserQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<3a12553b8e168fce15387920b230a522>> + * @generated SignedSource<<793498f577ba19175f5e0da7d6fd9a12>> * @flow * @lightSyntaxTransform * @nogrep @@ -133,7 +133,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "63bb781a6f6d4114078c8e85d80b9bc9"; + (node/*: any*/).hash = "666cde58381d36fc1cad522b6da4cbb7"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/useLazyLoadQueryNodeFastRefreshTestUserQuery.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/useLazyLoadQueryNodeFastRefreshTestUserQuery.graphql.js index 9cdb20c4c3517..a5112f95a0f8e 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/useLazyLoadQueryNodeFastRefreshTestUserQuery.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/useLazyLoadQueryNodeFastRefreshTestUserQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<913362b6843cb785f7c89ccea96009e1>> + * @generated SignedSource<<25d516731ff8d6871063f2816fe6257e>> * @flow * @lightSyntaxTransform * @nogrep @@ -133,7 +133,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "ee05f828677e819fb1cfd6cf12c4e4a0"; + (node/*: any*/).hash = "bccaba4f51a130fa4e7c4a7c52677c9e"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/useLazyLoadQueryNodeReactDoubleEffectsTestUserQuery.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/useLazyLoadQueryNodeReactDoubleEffectsTestUserQuery.graphql.js index 4d969ab32e51c..c16f58718ace1 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/useLazyLoadQueryNodeReactDoubleEffectsTestUserQuery.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/useLazyLoadQueryNodeReactDoubleEffectsTestUserQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<476545c9feafe66809f18fbe494f99a4>> + * @generated SignedSource<<025dbd59ac7c6543a42c631b492d5554>> * @flow * @lightSyntaxTransform * @nogrep @@ -147,7 +147,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "4f5221aaf962b9f3544c53ae0f65d8cd"; + (node/*: any*/).hash = "231f94302ea2eb1609d41079dda967f2"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/useLazyLoadQueryNodeReactDoubleEffectsTestUserQueryWithDeferQuery.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/useLazyLoadQueryNodeReactDoubleEffectsTestUserQueryWithDeferQuery.graphql.js index 42852a7734e01..929f4b652afdc 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/useLazyLoadQueryNodeReactDoubleEffectsTestUserQueryWithDeferQuery.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/useLazyLoadQueryNodeReactDoubleEffectsTestUserQueryWithDeferQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<608044f18e7cb808fde148f334053945>> * @flow * @lightSyntaxTransform * @nogrep @@ -159,7 +159,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "12d18891ad074f33e2e0064f4d26ebd2"; + (node/*: any*/).hash = "6e60686779f65e9c9b3369261f936944"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/useLazyLoadQueryNodeTest1Query.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/useLazyLoadQueryNodeTest1Query.graphql.js index 5c99d2c1327f1..b12fb0506c666 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/useLazyLoadQueryNodeTest1Query.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/useLazyLoadQueryNodeTest1Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<2a197f4fec9a47a0a82d656e9f19d028>> + * @generated SignedSource<<96c27eb2caa81b47f2f9108024d7017b>> * @flow * @lightSyntaxTransform * @nogrep @@ -148,7 +148,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "021b10a8f84891aa446aeda4b318a341"; + (node/*: any*/).hash = "7dace47991244c4c8a38b786cf8e85c4"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/useLazyLoadQueryNodeTestUserQuery.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/useLazyLoadQueryNodeTestUserQuery.graphql.js index 7de63a79abbea..940aa87224201 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/useLazyLoadQueryNodeTestUserQuery.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/useLazyLoadQueryNodeTestUserQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<38b9916cd4425d8a094c80b4a29ba659>> + * @generated SignedSource<<282ca852f80210afbdc477b340e37aae>> * @flow * @lightSyntaxTransform * @nogrep @@ -133,7 +133,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "bc3d98c6a2a45e4247e409cca1d3f06f"; + (node/*: any*/).hash = "19ad769206887ea7a7b87d2855e4cc1f"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/usePaginationFragmentTestStoryFragment.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/usePaginationFragmentTestStoryFragment.graphql.js index b5449c614b58f..1e5f7b7dd2921 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/usePaginationFragmentTestStoryFragment.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/usePaginationFragmentTestStoryFragment.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<78d2548c61fb6ef49166b87871b6e52c>> + * @generated SignedSource<<2e4ae25f68052d1b3f1d5a0327a06554>> * @flow * @lightSyntaxTransform * @nogrep @@ -22,7 +22,6 @@ import type { FragmentType } from "relay-runtime"; declare export opaque type usePaginationFragmentTestStoryFragment$fragmentType: FragmentType; type usePaginationFragmentTestStoryFragmentRefetchQuery$variables = any; export type usePaginationFragmentTestStoryFragment$data = {| - +__token: string, +comments: ?{| +edges: ?$ReadOnlyArray> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -221,13 +221,6 @@ return { "name": "fetch_id", "storageKey": null }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "__token", - "storageKey": null - }, (v3/*: any*/) ], "storageKey": null @@ -235,12 +228,12 @@ return { ] }, "params": { - "cacheID": "141601b4d00333b2f388709fff638ee6", + "cacheID": "16690aeca128c15136cb3c656e061799", "id": null, "metadata": {}, "name": "usePaginationFragmentTestStoryFragmentRefetchQuery", "operationKind": "query", - "text": "query usePaginationFragmentTestStoryFragmentRefetchQuery(\n $count: Int = 10\n $cursor: ID\n $id: ID!\n) {\n fetch__NonNodeStory(input_fetch_id: $id) {\n ...usePaginationFragmentTestStoryFragment_1G22uz\n id\n }\n}\n\nfragment usePaginationFragmentTestStoryFragment_1G22uz on NonNodeStory {\n comments(first: $count, after: $cursor) {\n edges {\n node {\n id\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n fetch_id\n __token\n}\n" + "text": "query usePaginationFragmentTestStoryFragmentRefetchQuery(\n $count: Int = 10\n $cursor: ID\n $id: ID!\n) {\n fetch__NonNodeStory(input_fetch_id: $id) {\n ...usePaginationFragmentTestStoryFragment_1G22uz\n id\n }\n}\n\nfragment usePaginationFragmentTestStoryFragment_1G22uz on NonNodeStory {\n comments(first: $count, after: $cursor) {\n edges {\n node {\n id\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n fetch_id\n}\n" } }; })(); diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/usePaginationFragmentTestStoryQuery.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/usePaginationFragmentTestStoryQuery.graphql.js index 0d0a48db67a14..12156d2f48991 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/usePaginationFragmentTestStoryQuery.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/usePaginationFragmentTestStoryQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<6f9740295cde0d77668c74f29279d7cc>> * @flow * @lightSyntaxTransform * @nogrep @@ -192,13 +192,6 @@ return { "name": "fetch_id", "storageKey": null }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "__token", - "storageKey": null - }, (v3/*: any*/) ], "storageKey": null @@ -206,12 +199,12 @@ return { ] }, "params": { - "cacheID": "4f3e70176552d390bebe4d2886c17745", + "cacheID": "3cb446084b7d394d00b3150a5ce104cb", "id": null, "metadata": {}, "name": "usePaginationFragmentTestStoryQuery", "operationKind": "query", - "text": "query usePaginationFragmentTestStoryQuery(\n $id: ID!\n) {\n nonNodeStory(id: $id) {\n ...usePaginationFragmentTestStoryFragment\n id\n }\n}\n\nfragment usePaginationFragmentTestStoryFragment on NonNodeStory {\n comments(first: 10) {\n edges {\n node {\n id\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n fetch_id\n __token\n}\n" + "text": "query usePaginationFragmentTestStoryQuery(\n $id: ID!\n) {\n nonNodeStory(id: $id) {\n ...usePaginationFragmentTestStoryFragment\n id\n }\n}\n\nfragment usePaginationFragmentTestStoryFragment on NonNodeStory {\n comments(first: 10) {\n edges {\n node {\n id\n __typename\n }\n cursor\n }\n pageInfo {\n endCursor\n hasNextPage\n }\n }\n fetch_id\n}\n" } }; })(); diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/usePaginationFragmentTestUserQuery.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/usePaginationFragmentTestUserQuery.graphql.js index 82dfc5b1eebe1..85d3fc89c72f6 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/usePaginationFragmentTestUserQuery.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/usePaginationFragmentTestUserQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<88786543dd9768adb7f4aba8bb9b94ea>> + * @generated SignedSource<<9a72ecaa8efe53949809b54f7d7ae1c4>> * @flow * @lightSyntaxTransform * @nogrep @@ -328,7 +328,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "e6459baa3fc32eef6945070272ab0a92"; + (node/*: any*/).hash = "6cfd2c9ec602ea2b50da57c845f4fcd4"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/usePaginationFragmentTestUserQueryNestedFragmentQuery.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/usePaginationFragmentTestUserQueryNestedFragmentQuery.graphql.js index ff318d510293a..9e772e67a6f1d 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/usePaginationFragmentTestUserQueryNestedFragmentQuery.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/usePaginationFragmentTestUserQueryNestedFragmentQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<87125350c025233be8cc40c45714e271>> + * @generated SignedSource<<724839ac735cfc3ad120b88196bc7a5d>> * @flow * @lightSyntaxTransform * @nogrep @@ -354,7 +354,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "0fda8f3b3b888c596ad22c11ba6ecf03"; + (node/*: any*/).hash = "458b4f5da9c3a7ab5f8a37261fa8b23f"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/usePaginationFragmentTestUserQueryWithLiteralArgsQuery.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/usePaginationFragmentTestUserQueryWithLiteralArgsQuery.graphql.js index 747f3d6aaa44b..a688d1838ef19 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/usePaginationFragmentTestUserQueryWithLiteralArgsQuery.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/usePaginationFragmentTestUserQueryWithLiteralArgsQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<54b7c0cb1f2a47a7d0ec1ff041181be0>> * @flow * @lightSyntaxTransform * @nogrep @@ -314,7 +314,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "a7568b8997ebfad20088741629afcfa9"; + (node/*: any*/).hash = "896f2c0d23be1936f2c2371f29128ba2"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/usePaginationFragmentTestUserQueryWithStreamingQuery.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/usePaginationFragmentTestUserQueryWithStreamingQuery.graphql.js index cef4549f3a6cc..33828f7dfc194 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/usePaginationFragmentTestUserQueryWithStreamingQuery.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/usePaginationFragmentTestUserQueryWithStreamingQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<941eeee7d34fd9a5e5e59daa54cf25ee>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -342,7 +342,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "3804d84962fa5c729fa01498dc7e4224"; + (node/*: any*/).hash = "a8aa8fb48a0000532335af4b5e6f7573"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/usePaginationFragmentTestUserQueryWithoutIDQuery.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/usePaginationFragmentTestUserQueryWithoutIDQuery.graphql.js index 5088d0cf0de3c..09582c38d33fe 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/usePaginationFragmentTestUserQueryWithoutIDQuery.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/usePaginationFragmentTestUserQueryWithoutIDQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<90524f8256ace0bbd2885c59b00e59a2>> + * @generated SignedSource<<047f8a49a087d81653faf18ca849d217>> * @flow * @lightSyntaxTransform * @nogrep @@ -337,7 +337,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "a2f6785e8ec3b4ea3f01ac2e5f4dc287"; + (node/*: any*/).hash = "13b0011aa8319dc4cdadad78cf6c67fe"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/usePrefetchableForwardPaginationFragmentRefetchQuery.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/usePrefetchableForwardPaginationFragmentRefetchQuery.graphql.js index 446a48323bcaa..4df767a71168f 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/usePrefetchableForwardPaginationFragmentRefetchQuery.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/usePrefetchableForwardPaginationFragmentRefetchQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<569024641be3b0efc2ca9e9f5b020b7e>> + * @generated SignedSource<<919069c963b6880374b876874c00ed31>> * @flow * @lightSyntaxTransform * @nogrep @@ -19,7 +19,7 @@ /*:: import type { ConcreteRequest, Query } from 'relay-runtime'; import type { FragmentType } from "relay-runtime"; -import type { usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user$fragmentType } from "./usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user.graphql"; +import type { usePrefetchableForwardPaginationFragmentTest_user$fragmentType } from "./usePrefetchableForwardPaginationFragmentTest_user.graphql"; export type usePrefetchableForwardPaginationFragmentRefetchQuery$variables = {| after?: ?string, before?: ?string, @@ -29,7 +29,7 @@ export type usePrefetchableForwardPaginationFragmentRefetchQuery$variables = {| |}; export type usePrefetchableForwardPaginationFragmentRefetchQuery$data = {| +node: ?{| - +$fragmentSpreads: usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user$fragmentType, + +$fragmentSpreads: usePrefetchableForwardPaginationFragmentTest_user$fragmentType, |}, |}; export type usePrefetchableForwardPaginationFragmentRefetchQuery = {| @@ -131,7 +131,7 @@ return { { "args": null, "kind": "FragmentSpread", - "name": "usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user" + "name": "usePrefetchableForwardPaginationFragmentTest_user" } ], "storageKey": null @@ -272,18 +272,18 @@ return { ] }, "params": { - "cacheID": "392f33f21d5b262df1a92d3547ae09bc", + "cacheID": "2f9567322cbafd63725e67eac1c69356", "id": null, "metadata": {}, "name": "usePrefetchableForwardPaginationFragmentRefetchQuery", "operationKind": "query", - "text": "query usePrefetchableForwardPaginationFragmentRefetchQuery(\n $after: ID\n $before: ID\n $first: Int\n $last: Int\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user\n id\n }\n}\n\nfragment usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user on User {\n friends(after: $after, first: $first, before: $before, last: $last) {\n edges {\n ...usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user__edges\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n\nfragment usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user__edges on FriendsEdge {\n node {\n id\n name\n __typename\n }\n cursor\n}\n" + "text": "query usePrefetchableForwardPaginationFragmentRefetchQuery(\n $after: ID\n $before: ID\n $first: Int\n $last: Int\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...usePrefetchableForwardPaginationFragmentTest_user\n id\n }\n}\n\nfragment usePrefetchableForwardPaginationFragmentTest_user on User {\n friends(after: $after, first: $first, before: $before, last: $last) {\n edges {\n ...usePrefetchableForwardPaginationFragmentTest_user__edges\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n\nfragment usePrefetchableForwardPaginationFragmentTest_user__edges on FriendsEdge {\n node {\n id\n name\n __typename\n }\n cursor\n}\n" } }; })(); if (__DEV__) { - (node/*: any*/).hash = "809455a8d7ada67cb84f3d111f6c6010"; + (node/*: any*/).hash = "b556c89ea274871519ed4779f197956d"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/usePrefetchableForwardPaginationFragmentEXPERIMENTALTestQuery.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/usePrefetchableForwardPaginationFragmentTestQuery.graphql.js similarity index 75% rename from packages/react-relay/relay-hooks/__tests__/__generated__/usePrefetchableForwardPaginationFragmentEXPERIMENTALTestQuery.graphql.js rename to packages/react-relay/relay-hooks/__tests__/__generated__/usePrefetchableForwardPaginationFragmentTestQuery.graphql.js index 21f05505b5df6..37a3629ff2598 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/usePrefetchableForwardPaginationFragmentEXPERIMENTALTestQuery.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/usePrefetchableForwardPaginationFragmentTestQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<8a5cc6645690b01d77261e637131b458>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -18,22 +18,22 @@ /*:: import type { ConcreteRequest, Query } from 'relay-runtime'; -import type { usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user$fragmentType } from "./usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user.graphql"; -export type usePrefetchableForwardPaginationFragmentEXPERIMENTALTestQuery$variables = {| +import type { usePrefetchableForwardPaginationFragmentTest_user$fragmentType } from "./usePrefetchableForwardPaginationFragmentTest_user.graphql"; +export type usePrefetchableForwardPaginationFragmentTestQuery$variables = {| after?: ?string, before?: ?string, first?: ?number, id: string, last?: ?number, |}; -export type usePrefetchableForwardPaginationFragmentEXPERIMENTALTestQuery$data = {| +export type usePrefetchableForwardPaginationFragmentTestQuery$data = {| +node: ?{| - +$fragmentSpreads: usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user$fragmentType, + +$fragmentSpreads: usePrefetchableForwardPaginationFragmentTest_user$fragmentType, |}, |}; -export type usePrefetchableForwardPaginationFragmentEXPERIMENTALTestQuery = {| - response: usePrefetchableForwardPaginationFragmentEXPERIMENTALTestQuery$data, - variables: usePrefetchableForwardPaginationFragmentEXPERIMENTALTestQuery$variables, +export type usePrefetchableForwardPaginationFragmentTestQuery = {| + response: usePrefetchableForwardPaginationFragmentTestQuery$data, + variables: usePrefetchableForwardPaginationFragmentTestQuery$variables, |}; */ @@ -117,7 +117,7 @@ return { ], "kind": "Fragment", "metadata": null, - "name": "usePrefetchableForwardPaginationFragmentEXPERIMENTALTestQuery", + "name": "usePrefetchableForwardPaginationFragmentTestQuery", "selections": [ { "alias": null, @@ -130,7 +130,7 @@ return { { "args": null, "kind": "FragmentSpread", - "name": "usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user" + "name": "usePrefetchableForwardPaginationFragmentTest_user" } ], "storageKey": null @@ -149,7 +149,7 @@ return { (v4/*: any*/) ], "kind": "Operation", - "name": "usePrefetchableForwardPaginationFragmentEXPERIMENTALTestQuery", + "name": "usePrefetchableForwardPaginationFragmentTestQuery", "selections": [ { "alias": null, @@ -271,21 +271,21 @@ return { ] }, "params": { - "cacheID": "6cd62bc50d478e401286fbd253466497", + "cacheID": "9e9b011d9b52d31f16e8e2ee376822bb", "id": null, "metadata": {}, - "name": "usePrefetchableForwardPaginationFragmentEXPERIMENTALTestQuery", + "name": "usePrefetchableForwardPaginationFragmentTestQuery", "operationKind": "query", - "text": "query usePrefetchableForwardPaginationFragmentEXPERIMENTALTestQuery(\n $id: ID!\n $after: ID\n $first: Int\n $before: ID\n $last: Int\n) {\n node(id: $id) {\n __typename\n ...usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user\n id\n }\n}\n\nfragment usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user on User {\n friends(after: $after, first: $first, before: $before, last: $last) {\n edges {\n ...usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user__edges\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n\nfragment usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user__edges on FriendsEdge {\n node {\n id\n name\n __typename\n }\n cursor\n}\n" + "text": "query usePrefetchableForwardPaginationFragmentTestQuery(\n $id: ID!\n $after: ID\n $first: Int\n $before: ID\n $last: Int\n) {\n node(id: $id) {\n __typename\n ...usePrefetchableForwardPaginationFragmentTest_user\n id\n }\n}\n\nfragment usePrefetchableForwardPaginationFragmentTest_user on User {\n friends(after: $after, first: $first, before: $before, last: $last) {\n edges {\n ...usePrefetchableForwardPaginationFragmentTest_user__edges\n }\n pageInfo {\n endCursor\n hasNextPage\n hasPreviousPage\n startCursor\n }\n }\n id\n}\n\nfragment usePrefetchableForwardPaginationFragmentTest_user__edges on FriendsEdge {\n node {\n id\n name\n __typename\n }\n cursor\n}\n" } }; })(); if (__DEV__) { - (node/*: any*/).hash = "48bd7cb20fb4977c3582b839ce846c1f"; + (node/*: any*/).hash = "785e649312a81a6861dbabc7c8c80d56"; } module.exports = ((node/*: any*/)/*: Query< - usePrefetchableForwardPaginationFragmentEXPERIMENTALTestQuery$variables, - usePrefetchableForwardPaginationFragmentEXPERIMENTALTestQuery$data, + usePrefetchableForwardPaginationFragmentTestQuery$variables, + usePrefetchableForwardPaginationFragmentTestQuery$data, >*/); diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/usePrefetchableForwardPaginationFragmentTest_user.graphql.js similarity index 74% rename from packages/react-relay/relay-hooks/__tests__/__generated__/usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user.graphql.js rename to packages/react-relay/relay-hooks/__tests__/__generated__/usePrefetchableForwardPaginationFragmentTest_user.graphql.js index 20eadaa490dfd..2e5388f8e08dc 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/usePrefetchableForwardPaginationFragmentTest_user.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<369c689b8624b2aa4e3cea4ad88d864a>> + * @generated SignedSource<<8becc1a8ce7b77e8e0c4d71b66d7450f>> * @flow * @lightSyntaxTransform * @nogrep @@ -18,15 +18,15 @@ /*:: import type { ReaderFragment, PrefetchableRefetchableFragment } from 'relay-runtime'; -import type { usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user__edges$fragmentType } from "./usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user__edges.graphql"; +import type { usePrefetchableForwardPaginationFragmentTest_user__edges$fragmentType } from "./usePrefetchableForwardPaginationFragmentTest_user__edges.graphql"; import type { FragmentType } from "relay-runtime"; -declare export opaque type usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user$fragmentType: FragmentType; +declare export opaque type usePrefetchableForwardPaginationFragmentTest_user$fragmentType: FragmentType; type usePrefetchableForwardPaginationFragmentRefetchQuery$variables = any; -type usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user__edges$data = any; -export type usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user$data = {| +type usePrefetchableForwardPaginationFragmentTest_user__edges$data = any; +export type usePrefetchableForwardPaginationFragmentTest_user$data = {| +friends: ?{| +edges: ?$ReadOnlyArray, +pageInfo: ?{| +endCursor: ?string, @@ -36,11 +36,11 @@ export type usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user$data = |}, |}, +id: string, - +$fragmentType: usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user$fragmentType, + +$fragmentType: usePrefetchableForwardPaginationFragmentTest_user$fragmentType, |}; -export type usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user$key = { - +$data?: usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user$data, - +$fragmentSpreads: usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user$fragmentType, +export type usePrefetchableForwardPaginationFragmentTest_user$key = { + +$data?: usePrefetchableForwardPaginationFragmentTest_user$data, + +$fragmentSpreads: usePrefetchableForwardPaginationFragmentTest_user$fragmentType, ... }; */ @@ -98,10 +98,10 @@ return { "identifierField": "id", "identifierQueryVariableName": "id" }, - "edgesFragment": require('./usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user__edges.graphql') + "edgesFragment": require('./usePrefetchableForwardPaginationFragmentTest_user__edges.graphql') } }, - "name": "usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user", + "name": "usePrefetchableForwardPaginationFragmentTest_user", "selections": [ { "alias": "friends", @@ -122,7 +122,7 @@ return { { "args": null, "kind": "FragmentSpread", - "name": "usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user__edges" + "name": "usePrefetchableForwardPaginationFragmentTest_user__edges" } ], "storageKey": null @@ -183,12 +183,12 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "809455a8d7ada67cb84f3d111f6c6010"; + (node/*: any*/).hash = "b556c89ea274871519ed4779f197956d"; } module.exports = ((node/*: any*/)/*: PrefetchableRefetchableFragment< - usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user$fragmentType, - usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user$data, - usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user__edges$data, + usePrefetchableForwardPaginationFragmentTest_user$fragmentType, + usePrefetchableForwardPaginationFragmentTest_user$data, + usePrefetchableForwardPaginationFragmentTest_user__edges$data, usePrefetchableForwardPaginationFragmentRefetchQuery$variables, >*/); diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user__edges.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/usePrefetchableForwardPaginationFragmentTest_user__edges.graphql.js similarity index 65% rename from packages/react-relay/relay-hooks/__tests__/__generated__/usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user__edges.graphql.js rename to packages/react-relay/relay-hooks/__tests__/__generated__/usePrefetchableForwardPaginationFragmentTest_user__edges.graphql.js index 1ebdea9b752da..741fcda827535 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user__edges.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/usePrefetchableForwardPaginationFragmentTest_user__edges.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -19,19 +19,19 @@ /*:: import type { Fragment, ReaderFragment } from 'relay-runtime'; import type { FragmentType } from "relay-runtime"; -declare export opaque type usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user__edges$fragmentType: FragmentType; -export type usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user__edges$data = $ReadOnlyArray<{| +declare export opaque type usePrefetchableForwardPaginationFragmentTest_user__edges$fragmentType: FragmentType; +export type usePrefetchableForwardPaginationFragmentTest_user__edges$data = $ReadOnlyArray<{| +cursor: ?string, +node: ?{| +__typename: "User", +id: string, +name: ?string, |}, - +$fragmentType: usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user__edges$fragmentType, + +$fragmentType: usePrefetchableForwardPaginationFragmentTest_user__edges$fragmentType, |}>; -export type usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user__edges$key = $ReadOnlyArray<{ - +$data?: usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user__edges$data, - +$fragmentSpreads: usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user__edges$fragmentType, +export type usePrefetchableForwardPaginationFragmentTest_user__edges$key = $ReadOnlyArray<{ + +$data?: usePrefetchableForwardPaginationFragmentTest_user__edges$data, + +$fragmentSpreads: usePrefetchableForwardPaginationFragmentTest_user__edges$fragmentType, ... }>; */ @@ -42,7 +42,7 @@ var node/*: ReaderFragment*/ = { "metadata": { "plural": true }, - "name": "usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user__edges", + "name": "usePrefetchableForwardPaginationFragmentTest_user__edges", "selections": [ { "alias": null, @@ -89,6 +89,6 @@ var node/*: ReaderFragment*/ = { }; module.exports = ((node/*: any*/)/*: Fragment< - usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user__edges$fragmentType, - usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user__edges$data, + usePrefetchableForwardPaginationFragmentTest_user__edges$fragmentType, + usePrefetchableForwardPaginationFragmentTest_user__edges$data, >*/); diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/usePreloadedQueryProvidedVariablesTest_Query.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/usePreloadedQueryProvidedVariablesTest_Query.graphql.js index 9d2d1501ed299..2ecce327846fd 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/usePreloadedQueryProvidedVariablesTest_Query.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/usePreloadedQueryProvidedVariablesTest_Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<1bd7619ea2f07d3500636a825112fa19>> + * @generated SignedSource<<0487396a1a0b78cbe28d4588361fc598>> * @flow * @lightSyntaxTransform * @nogrep @@ -33,8 +33,8 @@ export type usePreloadedQueryProvidedVariablesTest_Query = {| variables: usePreloadedQueryProvidedVariablesTest_Query$variables, |}; ({ - "__relay_internal__pv__RelayProvider_returnsTruerelayprovider": require('./../RelayProvider_returnsTrue.relayprovider'), - "__relay_internal__pv__RelayProvider_returnsFalserelayprovider": require('./../RelayProvider_returnsFalse.relayprovider') + "__relay_internal__pv__RelayProvider_returnsTruerelayprovider": require('../RelayProvider_returnsTrue.relayprovider'), + "__relay_internal__pv__RelayProvider_returnsFalserelayprovider": require('../RelayProvider_returnsFalse.relayprovider') }: {| +__relay_internal__pv__RelayProvider_returnsFalserelayprovider: {| +get: () => boolean, @@ -205,15 +205,15 @@ return { "operationKind": "query", "text": "query usePreloadedQueryProvidedVariablesTest_Query(\n $id: ID!\n $__relay_internal__pv__RelayProvider_returnsTruerelayprovider: Boolean!\n $__relay_internal__pv__RelayProvider_returnsFalserelayprovider: Boolean!\n) {\n node(id: $id) {\n __typename\n id\n ...usePreloadedQueryProvidedVariablesTest_Fragment\n }\n}\n\nfragment usePreloadedQueryProvidedVariablesTest_Fragment on User {\n name @include(if: $__relay_internal__pv__RelayProvider_returnsTruerelayprovider)\n firstName @include(if: $__relay_internal__pv__RelayProvider_returnsFalserelayprovider)\n lastName @skip(if: $__relay_internal__pv__RelayProvider_returnsFalserelayprovider)\n username @skip(if: $__relay_internal__pv__RelayProvider_returnsTruerelayprovider)\n}\n", "providedVariables": { - "__relay_internal__pv__RelayProvider_returnsTruerelayprovider": require('./../RelayProvider_returnsTrue.relayprovider'), - "__relay_internal__pv__RelayProvider_returnsFalserelayprovider": require('./../RelayProvider_returnsFalse.relayprovider') + "__relay_internal__pv__RelayProvider_returnsTruerelayprovider": require('../RelayProvider_returnsTrue.relayprovider'), + "__relay_internal__pv__RelayProvider_returnsFalserelayprovider": require('../RelayProvider_returnsFalse.relayprovider') } } }; })(); if (__DEV__) { - (node/*: any*/).hash = "57f29c979bd1ac2b46b7841b521d1cb2"; + (node/*: any*/).hash = "0d2bf5e56b0727c897800a81971dc7db"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/usePreloadedQueryProvidedVariablesTest_badQuery.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/usePreloadedQueryProvidedVariablesTest_badQuery.graphql.js index 3aae2d82cff4d..a9ebcbff555fc 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/usePreloadedQueryProvidedVariablesTest_badQuery.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/usePreloadedQueryProvidedVariablesTest_badQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<940e03983e6e001ebaf61ce67f5941e5>> + * @generated SignedSource<<8c98dbf87be396aee280980643b58fe5>> * @flow * @lightSyntaxTransform * @nogrep @@ -32,7 +32,7 @@ export type usePreloadedQueryProvidedVariablesTest_badQuery = {| variables: usePreloadedQueryProvidedVariablesTest_badQuery$variables, |}; ({ - "__relay_internal__pv__RelayProvider_impurerelayprovider": require('./../RelayProvider_impure.relayprovider') + "__relay_internal__pv__RelayProvider_impurerelayprovider": require('../RelayProvider_impure.relayprovider') }: {| +__relay_internal__pv__RelayProvider_impurerelayprovider: {| +get: () => number, @@ -161,14 +161,14 @@ return { "operationKind": "query", "text": "query usePreloadedQueryProvidedVariablesTest_badQuery(\n $id: ID!\n $__relay_internal__pv__RelayProvider_impurerelayprovider: Float!\n) {\n node(id: $id) {\n __typename\n ...usePreloadedQueryProvidedVariablesTest_badFragment\n id\n }\n}\n\nfragment usePreloadedQueryProvidedVariablesTest_badFragment on User {\n profile_picture(scale: $__relay_internal__pv__RelayProvider_impurerelayprovider) {\n uri\n }\n}\n", "providedVariables": { - "__relay_internal__pv__RelayProvider_impurerelayprovider": require('./../RelayProvider_impure.relayprovider') + "__relay_internal__pv__RelayProvider_impurerelayprovider": require('../RelayProvider_impure.relayprovider') } } }; })(); if (__DEV__) { - (node/*: any*/).hash = "38101eec78c6bee2608fbe821589dd15"; + (node/*: any*/).hash = "9f08a83ca6f077bedba106cc1674156d"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/usePreloadedQueryReactDoubleEffectsTestDeferQuery.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/usePreloadedQueryReactDoubleEffectsTestDeferQuery.graphql.js index 45dbbdb021a6a..ca4bdc89ca243 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/usePreloadedQueryReactDoubleEffectsTestDeferQuery.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/usePreloadedQueryReactDoubleEffectsTestDeferQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<6f689958c3f5e6e047263cd944af4020>> * @flow * @lightSyntaxTransform * @nogrep @@ -159,7 +159,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "6338c0f9d862dcd893716ffc9df2e626"; + (node/*: any*/).hash = "4b7abb047aa04c8ea7c19b5ab39e46ed"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/usePreloadedQueryReactDoubleEffectsTestQuery.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/usePreloadedQueryReactDoubleEffectsTestQuery.graphql.js index 995a602bd721c..7934b567ecce8 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/usePreloadedQueryReactDoubleEffectsTestQuery.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/usePreloadedQueryReactDoubleEffectsTestQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<567baaa0c209320f3f5e49f65363a923>> * @flow * @lightSyntaxTransform * @nogrep @@ -147,7 +147,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "0bbeeb14ab9e57bc7a6ae0d7486fcb9a"; + (node/*: any*/).hash = "5f8dbf688a5b0fad6793fda136b755ff"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/useQueryLoaderReactDoubleEffectsTestQuery.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/useQueryLoaderReactDoubleEffectsTestQuery.graphql.js index 8b0b3d80fee07..4ce470bccdf77 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/useQueryLoaderReactDoubleEffectsTestQuery.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/useQueryLoaderReactDoubleEffectsTestQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<4b7f18186ac1d4d6dae15e49aac6cc48>> + * @generated SignedSource<<10ff4ef6ce88bc1ebd070a07e4838553>> * @flow * @lightSyntaxTransform * @nogrep @@ -133,7 +133,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "813ed6f6be85c7ddc56659a2312f6190"; + (node/*: any*/).hash = "0a90fca296d573765285d246538d5929"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/useRefetchableFragmentNodeTest1Fragment.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/useRefetchableFragmentNodeTest1Fragment.graphql.js index 15d027a463743..939c0efdfde55 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/useRefetchableFragmentNodeTest1Fragment.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/useRefetchableFragmentNodeTest1Fragment.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<7c2c05ec0d836f9fab77060b8dc4447e>> + * @generated SignedSource<<074e7dac8c97944de8b6ce60fd1eb626>> * @flow * @lightSyntaxTransform * @nogrep @@ -22,7 +22,6 @@ import type { FragmentType } from "relay-runtime"; declare export opaque type useRefetchableFragmentNodeTest1Fragment$fragmentType: FragmentType; type useRefetchableFragmentNodeTest1FragmentRefetchQuery$variables = any; export type useRefetchableFragmentNodeTest1Fragment$data = {| - +__token: string, +actor: ?{| +name: ?string, |}, @@ -78,13 +77,6 @@ var node/*: ReaderFragment*/ = { "kind": "ScalarField", "name": "fetch_id", "storageKey": null - }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "__token", - "storageKey": null } ], "type": "NonNodeStory", diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/useRefetchableFragmentNodeTest1FragmentRefetchQuery.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/useRefetchableFragmentNodeTest1FragmentRefetchQuery.graphql.js index 40f48a2d39d41..86c6f8a266a60 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/useRefetchableFragmentNodeTest1FragmentRefetchQuery.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/useRefetchableFragmentNodeTest1FragmentRefetchQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -130,13 +130,6 @@ return { "name": "fetch_id", "storageKey": null }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "__token", - "storageKey": null - }, (v2/*: any*/) ], "storageKey": null @@ -144,12 +137,12 @@ return { ] }, "params": { - "cacheID": "f90df4dc16ad217f9e91605e41c7bebe", + "cacheID": "3129a94c4b427c07b464a3e4b9f27849", "id": null, "metadata": {}, "name": "useRefetchableFragmentNodeTest1FragmentRefetchQuery", "operationKind": "query", - "text": "query useRefetchableFragmentNodeTest1FragmentRefetchQuery(\n $id: ID!\n) {\n fetch__NonNodeStory(input_fetch_id: $id) {\n ...useRefetchableFragmentNodeTest1Fragment\n id\n }\n}\n\nfragment useRefetchableFragmentNodeTest1Fragment on NonNodeStory {\n actor {\n __typename\n name\n id\n }\n fetch_id\n __token\n}\n" + "text": "query useRefetchableFragmentNodeTest1FragmentRefetchQuery(\n $id: ID!\n) {\n fetch__NonNodeStory(input_fetch_id: $id) {\n ...useRefetchableFragmentNodeTest1Fragment\n id\n }\n}\n\nfragment useRefetchableFragmentNodeTest1Fragment on NonNodeStory {\n actor {\n __typename\n name\n id\n }\n fetch_id\n}\n" } }; })(); diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/useRefetchableFragmentNodeTest1Query.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/useRefetchableFragmentNodeTest1Query.graphql.js index aa19f418355bb..fc7ac95f8cf60 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/useRefetchableFragmentNodeTest1Query.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/useRefetchableFragmentNodeTest1Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<10e1a861ea49f1a624859dad7a53c95a>> + * @generated SignedSource<<2319a3d7450db8fb50d547d5a91ab33b>> * @flow * @lightSyntaxTransform * @nogrep @@ -129,13 +129,6 @@ return { "name": "fetch_id", "storageKey": null }, - { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "__token", - "storageKey": null - }, (v2/*: any*/) ], "storageKey": null @@ -143,12 +136,12 @@ return { ] }, "params": { - "cacheID": "48452883758b52f03c316637a58a7bf9", + "cacheID": "2b1fe038fd3a80f4e81b56e3b864e547", "id": null, "metadata": {}, "name": "useRefetchableFragmentNodeTest1Query", "operationKind": "query", - "text": "query useRefetchableFragmentNodeTest1Query(\n $id: ID!\n) {\n nonNodeStory(id: $id) {\n ...useRefetchableFragmentNodeTest1Fragment\n id\n }\n}\n\nfragment useRefetchableFragmentNodeTest1Fragment on NonNodeStory {\n actor {\n __typename\n name\n id\n }\n fetch_id\n __token\n}\n" + "text": "query useRefetchableFragmentNodeTest1Query(\n $id: ID!\n) {\n nonNodeStory(id: $id) {\n ...useRefetchableFragmentNodeTest1Fragment\n id\n }\n}\n\nfragment useRefetchableFragmentNodeTest1Fragment on NonNodeStory {\n actor {\n __typename\n name\n id\n }\n fetch_id\n}\n" } }; })(); diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/useRefetchableFragmentNodeTest2Query.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/useRefetchableFragmentNodeTest2Query.graphql.js index 18fa4646f72fa..36cbdc52af8a6 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/useRefetchableFragmentNodeTest2Query.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/useRefetchableFragmentNodeTest2Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<4b933e9d55257062892abd615f86047f>> + * @generated SignedSource<<7478944cd3dbb6602b6bb6cc55549dd1>> * @flow * @lightSyntaxTransform * @nogrep @@ -171,7 +171,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "cbfab6b35baf272e1919c50195df62d0"; + (node/*: any*/).hash = "c6d3e08c02e35bb5a19b2b9736e4fc14"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/useRefetchableFragmentNodeTestUserQuery.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/useRefetchableFragmentNodeTestUserQuery.graphql.js index f4d39c2d286a3..90e5d73f4bd28 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/useRefetchableFragmentNodeTestUserQuery.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/useRefetchableFragmentNodeTestUserQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<2c8119f78dc84414a2c2e3af3236b73b>> + * @generated SignedSource<<2d1f5eda44b115c1fbb3851e34cc1dbd>> * @flow * @lightSyntaxTransform * @nogrep @@ -171,7 +171,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "94c9abdfb85ce932e3a33edc6295e643"; + (node/*: any*/).hash = "57abea6eac29136d88830ce479f8e64d"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/useRefetchableFragmentNodeTestUserQueryNestedFragmentQuery.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/useRefetchableFragmentNodeTestUserQueryNestedFragmentQuery.graphql.js index 4edb91650378e..9a813bd94ae89 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/useRefetchableFragmentNodeTestUserQueryNestedFragmentQuery.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/useRefetchableFragmentNodeTestUserQueryNestedFragmentQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<732c507971e2825b8ea30317c5e1b280>> + * @generated SignedSource<<9845e8eed54c5f1856ba893ec688f537>> * @flow * @lightSyntaxTransform * @nogrep @@ -199,7 +199,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "845c776ed52ad25feb496e052f8f65cb"; + (node/*: any*/).hash = "7ca81283026cd4a1b1204958f00d095c"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/useRefetchableFragmentNodeTestUserQueryWithArgsQuery.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/useRefetchableFragmentNodeTestUserQueryWithArgsQuery.graphql.js index 45c552f48ee55..0218315156e29 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/useRefetchableFragmentNodeTestUserQueryWithArgsQuery.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/useRefetchableFragmentNodeTestUserQueryWithArgsQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -177,7 +177,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "eb3a8dd67a24e472e2e18e80041d344a"; + (node/*: any*/).hash = "f21abe6662fcea801ab93fb27b9844cc"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/useRefetchableFragmentNodeTestUserQueryWithLiteralArgsQuery.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/useRefetchableFragmentNodeTestUserQueryWithLiteralArgsQuery.graphql.js index 89c4e97e8c773..f88eff517a29c 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/useRefetchableFragmentNodeTestUserQueryWithLiteralArgsQuery.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/useRefetchableFragmentNodeTestUserQueryWithLiteralArgsQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<1d8ee696790736246cd73800b1252ada>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -171,7 +171,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "09f9c5bc18c5bfc0ded57db2a09602a9"; + (node/*: any*/).hash = "419541101379d9a2ca9bebc22e817493"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/useRefetchableFragmentNodeWithSuspenseTransitionTestUserQuery.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/useRefetchableFragmentNodeWithSuspenseTransitionTestUserQuery.graphql.js index 3f45da91d0d10..858f0543af7f5 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/useRefetchableFragmentNodeWithSuspenseTransitionTestUserQuery.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/useRefetchableFragmentNodeWithSuspenseTransitionTestUserQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<044e975f172acb4522f2ed35a1bab818>> * @flow * @lightSyntaxTransform * @nogrep @@ -171,7 +171,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "7dd300784a6642a4b84870d9abd398b9"; + (node/*: any*/).hash = "293f9ed14d99bd4ecc3eb91ad23351a2"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/__generated__/useRefetchableFragmentTestUserQuery.graphql.js b/packages/react-relay/relay-hooks/__tests__/__generated__/useRefetchableFragmentTestUserQuery.graphql.js index de3bcba6b54f5..8d422db515ba6 100644 --- a/packages/react-relay/relay-hooks/__tests__/__generated__/useRefetchableFragmentTestUserQuery.graphql.js +++ b/packages/react-relay/relay-hooks/__tests__/__generated__/useRefetchableFragmentTestUserQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<8171e4f68cc25d20cf2cc29ef7ad3c56>> * @flow * @lightSyntaxTransform * @nogrep @@ -171,7 +171,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "4420905b52cf335b2645d133241e269a"; + (node/*: any*/).hash = "d3ff1134ddf6dda8f4cc0ce4d009f6f8"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/react-relay/relay-hooks/__tests__/loadQuery-test.js b/packages/react-relay/relay-hooks/__tests__/loadQuery-test.js index 9e8b9b05acaed..a83f8e43c22d1 100644 --- a/packages/react-relay/relay-hooks/__tests__/loadQuery-test.js +++ b/packages/react-relay/relay-hooks/__tests__/loadQuery-test.js @@ -18,6 +18,7 @@ import type { } from './__generated__/loadQueryTestQuery.graphql'; import type { CacheConfig, + INetwork, LogRequestInfoFunction, Query, RequestParameters, @@ -96,6 +97,7 @@ describe('loadQuery', () => { let mockAvailability: {fetchTime?: number, status: string}; let disposeOnloadCallback; let executeOnloadCallback; + let checkOperation; beforeEach(() => { fetch = jest.fn( @@ -122,7 +124,17 @@ describe('loadQuery', () => { return observable; }, ); - environment = createMockEnvironment({network: Network.create(fetch)}); + function wrapNetworkExecute(network: INetwork): INetwork { + return { + execute: (_1, _2, _3, _4, _5, _6, _7, _checkOperation) => { + checkOperation = _checkOperation; + return network.execute(_1, _2, _3, _4, _5, _6, _7, _checkOperation); + }, + }; + } + environment = createMockEnvironment({ + network: wrapNetworkExecute(Network.create(fetch)), + }); jest.clearAllTimers(); jest.useFakeTimers(); @@ -397,6 +409,48 @@ describe('loadQuery', () => { expect(disposeEnvironmentRetain).toHaveBeenCalledTimes(1); }); }); + + describe("with fetchPolicy === 'store-and-network'", () => { + it('should call fetch if the query can be fulfilled by the store', () => { + const {source} = loadQuery( + environment, + preloadableConcreteRequest, + variables, + { + fetchPolicy: 'store-and-network', + }, + ); + expect(fetch).toHaveBeenCalled(); + // $FlowFixMe[method-unbinding] added when improving typing for this parameters + expect(environment.executeWithSource).toHaveBeenCalled(); + expect(source).toBeDefined(); + // Query should still be retained even if we don't fetch + // $FlowFixMe[method-unbinding] added when improving typing for this parameters + expect(environment.retain).toHaveBeenCalled(); + }); + + it('returns the correct operation availability (available)', () => { + loadQuery(environment, preloadableConcreteRequest, variables, { + fetchPolicy: 'store-and-network', + }); + expect(fetch).toHaveBeenCalled(); + expect(checkOperation != null && checkOperation().status).toEqual( + 'available', + ); + }); + + it('returns the correct operation availability (missing)', () => { + mockAvailability = {status: 'missing'}; + + loadQuery(environment, preloadableConcreteRequest, variables, { + fetchPolicy: 'store-and-network', + }); + expect(fetch).toHaveBeenCalled(); + expect(checkOperation != null && checkOperation().status).toEqual( + 'missing', + ); + }); + }); }); describe('when the query AST is unavailable synchronously', () => { diff --git a/packages/react-relay/relay-hooks/__tests__/preloadQuery_DEPRECATED-test.js b/packages/react-relay/relay-hooks/__tests__/preloadQuery_DEPRECATED-test.js index c68aae7492a8f..5af0af0822c68 100644 --- a/packages/react-relay/relay-hooks/__tests__/preloadQuery_DEPRECATED-test.js +++ b/packages/react-relay/relay-hooks/__tests__/preloadQuery_DEPRECATED-test.js @@ -11,7 +11,10 @@ 'use strict'; -import type {GraphQLResponse} from 'relay-runtime/network/RelayNetworkTypes'; +import type { + GraphQLResponse, + INetwork, +} from 'relay-runtime/network/RelayNetworkTypes'; const preloadQuery_DEPRECATED = require('../preloadQuery_DEPRECATED'); const { @@ -49,7 +52,7 @@ const query = graphql` query.params.id = '12345'; const params = { - kind: 'PreloadableConcreteRequest', + kind: 'PreloadableConcreteRequest' as const, params: query.params, }; @@ -76,19 +79,37 @@ describe.each(['RelayModernEnvironment', 'MultiActorEnvironment'])( let sink; let variables; let operation; + let checkOperation; beforeEach(() => { // $FlowFixMe[missing-local-annot] error found when enabling Flow LTI mode - fetch = jest.fn((_query, _variables, _cacheConfig) => { + fetch = jest.fn((_query, _variables, _cacheConfig, _4, _5) => { // $FlowFixMe[missing-local-annot] error found when enabling Flow LTI mode return Observable.create(_sink => { sink = _sink; }); }); - + function wrapNetworkExecute(network: INetwork): INetwork { + return { + execute: (_1, _2, _3, _4, _5, _6, _7, _checkOperation) => { + checkOperation = _checkOperation; + return network.execute( + _1, + _2, + _3, + _4, + _5, + _6, + _7, + _checkOperation, + ); + }, + }; + } const multiActorEnvironment = new MultiActorEnvironment({ // $FlowFixMe[invalid-tuple-arity] Error found while enabling LTI on this file - createNetworkForActor: _actorID => Network.create(fetch), + createNetworkForActor: _actorID => + wrapNetworkExecute(Network.create(fetch)), createStoreForActor: _actorID => new Store(new RecordSource(), { gcReleaseBufferSize: 1, @@ -99,7 +120,7 @@ describe.each(['RelayModernEnvironment', 'MultiActorEnvironment'])( ? multiActorEnvironment.forActor(getActorIdentifier('actor:1234')) : new Environment({ // $FlowFixMe[invalid-tuple-arity] Error found while enabling LTI on this file - network: Network.create(fetch), + network: wrapNetworkExecute(Network.create(fetch)), store: new Store(new RecordSource(), { gcReleaseBufferSize: 1, }), @@ -535,6 +556,10 @@ describe.each(['RelayModernEnvironment', 'MultiActorEnvironment'])( expect(fetch.mock.calls[0][0]).toBe(query.params); expect(fetch.mock.calls[0][1]).toEqual(variables); expect(fetch.mock.calls[0][2]).toEqual({force: true}); + expect(checkOperation && checkOperation()).toEqual({ + status: 'available', + fetchTime, + }); const [events, observer] = createObserver(); if (preloaded.source) { @@ -906,6 +931,7 @@ describe('Preload queries that use provided variables', () => { query preloadQueryDEPRECATEDTest_ProvidedVarQuery($id: ID!) { node(id: $id) { ...preloadQueryDEPRECATEDTest_ProvidedVarFragment + @dangerously_unaliased_fixme } } `; @@ -924,7 +950,7 @@ describe('Preload queries that use provided variables', () => { queryWithProvidedVar.params.id = '12346'; const paramsWithProvidedVar = { - kind: 'PreloadableConcreteRequest', + kind: 'PreloadableConcreteRequest' as const, params: queryWithProvidedVar.params, }; diff --git a/packages/react-relay/relay-hooks/__tests__/useBlockingPaginationFragment-test.js b/packages/react-relay/relay-hooks/__tests__/useBlockingPaginationFragment-test.js index 5a339424d212f..57a779bc0af15 100644 --- a/packages/react-relay/relay-hooks/__tests__/useBlockingPaginationFragment-test.js +++ b/packages/react-relay/relay-hooks/__tests__/useBlockingPaginationFragment-test.js @@ -204,6 +204,7 @@ describe('useBlockingPaginationFragment', () => { ) { node(id: $id) { ...useBlockingPaginationFragmentTestUserFragment + @dangerously_unaliased_fixme @arguments(isViewerFriendLocal: $isViewerFriend, orderby: $orderby) } } @@ -221,6 +222,7 @@ describe('useBlockingPaginationFragment', () => { node(id: $id) { actor { ...useBlockingPaginationFragmentTestUserFragment + @dangerously_unaliased_fixme @arguments( isViewerFriendLocal: $isViewerFriend orderby: $orderby @@ -242,6 +244,7 @@ describe('useBlockingPaginationFragment', () => { viewer { actor { ...useBlockingPaginationFragmentTestUserFragment + @dangerously_unaliased_fixme @arguments( isViewerFriendLocal: $isViewerFriend orderby: $orderby @@ -260,6 +263,7 @@ describe('useBlockingPaginationFragment', () => { ) { node(id: $id) { ...useBlockingPaginationFragmentTestUserFragment + @dangerously_unaliased_fixme @arguments(isViewerFriendLocal: true, orderby: ["name"]) } } diff --git a/packages/react-relay/relay-hooks/__tests__/useBlockingPaginationFragment-with-suspense-transition-test.js b/packages/react-relay/relay-hooks/__tests__/useBlockingPaginationFragment-with-suspense-transition-test.js index 06b250155f909..6aa79140a6be2 100644 --- a/packages/react-relay/relay-hooks/__tests__/useBlockingPaginationFragment-with-suspense-transition-test.js +++ b/packages/react-relay/relay-hooks/__tests__/useBlockingPaginationFragment-with-suspense-transition-test.js @@ -267,6 +267,7 @@ describe('useBlockingPaginationFragment with useTransition', () => { node(id: $id) { actor { ...useBlockingPaginationFragmentWithSuspenseTransitionTestUserFragment + @dangerously_unaliased_fixme @arguments( isViewerFriendLocal: $isViewerFriend orderby: $orderby @@ -288,6 +289,7 @@ describe('useBlockingPaginationFragment with useTransition', () => { viewer { actor { ...useBlockingPaginationFragmentWithSuspenseTransitionTestUserFragment + @dangerously_unaliased_fixme @arguments( isViewerFriendLocal: $isViewerFriend orderby: $orderby diff --git a/packages/react-relay/relay-hooks/__tests__/useEntryPointLoader-react-double-effects-test.js b/packages/react-relay/relay-hooks/__tests__/useEntryPointLoader-react-double-effects-test.js index e7eca9ef52cfb..49fd4a947b4ba 100644 --- a/packages/react-relay/relay-hooks/__tests__/useEntryPointLoader-react-double-effects-test.js +++ b/packages/react-relay/relay-hooks/__tests__/useEntryPointLoader-react-double-effects-test.js @@ -37,6 +37,8 @@ function expectToHaveFetched( liveConfigId?: ?string, metadata?: {[key: string]: mixed}, onSubscribe?: () => void, + onResume?: (pauseTimeMs: number) => void, + onPause?: (mqttConnectionIsOk: boolean, internetIsOk: boolean) => void, poll?: ?number, transactionId?: ?string, }, @@ -133,6 +135,7 @@ describe.skip('useEntryPointLoader-react-double-effects', () => { id name ...useEntryPointLoaderReactDoubleEffectsTestUserFragment + @dangerously_unaliased_fixme } } `; diff --git a/packages/react-relay/relay-hooks/__tests__/useEntryPointLoader-test.js b/packages/react-relay/relay-hooks/__tests__/useEntryPointLoader-test.js index 9475758036215..57521570c4611 100644 --- a/packages/react-relay/relay-hooks/__tests__/useEntryPointLoader-test.js +++ b/packages/react-relay/relay-hooks/__tests__/useEntryPointLoader-test.js @@ -32,8 +32,10 @@ let loadEntryPointLastReturnValue; let disposeEntryPoint; let renderCount: ?number; -let environment; -let defaultEnvironmentProvider; +let environment: IEnvironment; +let defaultEnvironmentProvider: $ReadOnly<{ + getEnvironment: (options: ?EnvironmentProviderOptions) => IEnvironment, +}>; let render; let Container; let defaultEntryPoint: any; @@ -75,12 +77,12 @@ beforeEach(() => { Container = function ({ entryPoint, environmentProvider, - }: { + }: $ReadOnly<{ entryPoint: any, - environmentProvider: { + environmentProvider: $ReadOnly<{ getEnvironment: (options: ?EnvironmentProviderOptions) => IEnvironment, - }, - }) { + }>, + }>) { renderCount = (renderCount || 0) + 1; [loadedEntryPoint, entryPointLoaderCallback, disposeEntryPoint] = // $FlowFixMe[react-rule-hook] @@ -149,6 +151,8 @@ it('disposes the entry point and nullifies the state when the disposeEntryPoint const params = {}; ReactTestRenderer.act(() => entryPointLoaderCallback(params)); expect(disposeEntryPoint).toBeDefined(); + /* $FlowFixMe[constant-condition] Error discovered during Constant Condition + * roll out. See https://fburl.com/workplace/1v97vimq. */ if (disposeEntryPoint) { expect(loadedEntryPoint).not.toBe(null); expect(dispose).not.toHaveBeenCalled(); @@ -210,7 +214,7 @@ it('does not dispose the entry point before the new component tree unsuspends in let transitionToSecondRoute; function ConcurrentWrapper() { - const [route, setRoute] = React.useState('FIRST'); + const [route, setRoute] = React.useState<'FIRST' | 'SECOND'>('FIRST'); transitionToSecondRoute = () => React.startTransition(() => setRoute('SECOND')); @@ -241,6 +245,8 @@ it('does not dispose the entry point before the new component tree unsuspends in expect(currentDispose).not.toHaveBeenCalled(); ReactTestRenderer.act(() => { + /* $FlowFixMe[constant-condition] Error discovered during Constant + * Condition roll out. See https://fburl.com/workplace/1v97vimq. */ resolve && resolve(); jest.runAllImmediates(); }); @@ -558,6 +564,7 @@ it('disposes all entry points if the callback is called, the component suspends, entryPointLoaderCallback({}); }); const secondDispose = dispose; + // $FlowFixMe[incompatible-use] expect(outerInstance.toJSON()).toEqual('fallback'); // TODO(T19754110): This fails in OSS where we have concurrent mode, but might @@ -609,6 +616,7 @@ it('disposes all entry points if the component suspends, another entry point is // *even though the component is in a suspended state.* As such, it commits and // the entry point is disposed. expect(renderCount).toBeLessThanOrEqual(2); + // $FlowFixMe[incompatible-use] expect(outerInstance.toJSON()).toEqual('fallback'); expect(dispose).not.toHaveBeenCalled(); ReactTestRenderer.act(() => outerInstance.unmount()); diff --git a/packages/react-relay/relay-hooks/__tests__/useFragment-WithOperationTrackerSuspense-test.js b/packages/react-relay/relay-hooks/__tests__/useFragment-WithOperationTrackerSuspense-test.js index e1d87e119a315..44f83167cc909 100644 --- a/packages/react-relay/relay-hooks/__tests__/useFragment-WithOperationTrackerSuspense-test.js +++ b/packages/react-relay/relay-hooks/__tests__/useFragment-WithOperationTrackerSuspense-test.js @@ -66,6 +66,7 @@ describe('useFragment with Operation Tracker and Suspense behavior', () => { node(id: $id) { __typename ...useFragmentWithOperationTrackerSuspenseTestFragment + @dangerously_unaliased_fixme } } `; diff --git a/packages/react-relay/relay-hooks/__tests__/useFragment-test.js b/packages/react-relay/relay-hooks/__tests__/useFragment-test.js index 1975bf388273a..6ccb72b9d1c4c 100644 --- a/packages/react-relay/relay-hooks/__tests__/useFragment-test.js +++ b/packages/react-relay/relay-hooks/__tests__/useFragment-test.js @@ -7,6 +7,7 @@ * @flow * @format * @oncall relay + * @jest-environment jsdom */ 'use strict'; @@ -23,9 +24,9 @@ import type {OperationDescriptor} from 'relay-runtime/store/RelayStoreTypes'; import type {Fragment} from 'relay-runtime/util/RelayRuntimeTypes'; const useFragmentImpl = require('../useFragment'); +const ReactTestingLibrary = require('@testing-library/react'); const React = require('react'); const ReactRelayContext = require('react-relay/ReactRelayContext'); -const TestRenderer = require('react-test-renderer'); const { FRAGMENT_OWNER_KEY, FRAGMENTS_KEY, @@ -33,6 +34,7 @@ const { createOperationDescriptor, graphql, } = require('relay-runtime'); +const RelayFeatureFlags = require('relay-runtime/util/RelayFeatureFlags'); const {createMockEnvironment} = require('relay-test-utils'); const { disallowConsoleErrors, @@ -57,6 +59,7 @@ let renderSpy; let SingularRenderer; let PluralRenderer; let ContextProvider; +let setEnvironment; hook useFragment( fragmentNode: @@ -79,7 +82,6 @@ hook useFragment( function assertFragmentResults(expected: any) { // This ensures that useEffect runs jest.runAllImmediates(); - expect(renderSpy).toBeCalledTimes(1); const actualData = renderSpy.mock.calls[0][0]; expect(actualData).toEqual(expected); renderSpy.mockClear(); @@ -95,260 +97,311 @@ function createFragmentRef(id: string, owner: OperationDescriptor) { }; } -beforeEach(() => { - renderSpy = jest.fn< - [useFragmentTestUserFragment$data | useFragmentTestUsersFragment$data], - mixed, - >(); +describe.each([ + ['Experimental', true], + ['Current', false], +])('useFragment (%s)', (name, ENABLE_ACTIVITY_COMPATIBILITY) => { + beforeEach(() => { + RelayFeatureFlags.ENABLE_ACTIVITY_COMPATIBILITY = + ENABLE_ACTIVITY_COMPATIBILITY; - // Set up environment and base data - environment = createMockEnvironment(); - graphql` - fragment useFragmentTestNestedUserFragment on User { - username - } - `; - singularVariables = {id: '1'}; - pluralVariables = {ids: ['1', '2']}; - gqlSingularQuery = graphql` - query useFragmentTestUserQuery($id: ID!) { - node(id: $id) { - ...useFragmentTestUserFragment + renderSpy = jest.fn< + [useFragmentTestUserFragment$data | useFragmentTestUsersFragment$data], + mixed, + >(); + + // Set up environment and base data + environment = createMockEnvironment(); + graphql` + fragment useFragmentTestNestedUserFragment on User { + username } - } - `; - gqlSingularFragment = graphql` - fragment useFragmentTestUserFragment on User { - id - name - ...useFragmentTestNestedUserFragment - } - `; - gqlPluralQuery = graphql` - query useFragmentTestUsersQuery($ids: [ID!]!) { - nodes(ids: $ids) { - ...useFragmentTestUsersFragment + `; + singularVariables = {id: '1'}; + pluralVariables = {ids: ['1', '2']}; + gqlSingularQuery = graphql` + query useFragmentTestUserQuery($id: ID!) { + node(id: $id) { + ...useFragmentTestUserFragment @dangerously_unaliased_fixme + } } - } - `; - gqlPluralFragment = graphql` - fragment useFragmentTestUsersFragment on User @relay(plural: true) { - id - name - ...useFragmentTestNestedUserFragment - } - `; - singularQuery = createOperationDescriptor( - gqlSingularQuery, - singularVariables, - ); - pluralQuery = createOperationDescriptor(gqlPluralQuery, pluralVariables); - environment.commitPayload(singularQuery, { - node: { - __typename: 'User', - id: '1', - name: 'Alice', - username: 'useralice', - }, - }); - environment.commitPayload(pluralQuery, { - nodes: [ - { + `; + gqlSingularFragment = graphql` + fragment useFragmentTestUserFragment on User { + id + name + ...useFragmentTestNestedUserFragment + } + `; + gqlPluralQuery = graphql` + query useFragmentTestUsersQuery($ids: [ID!]!) { + nodes(ids: $ids) { + ...useFragmentTestUsersFragment + } + } + `; + gqlPluralFragment = graphql` + fragment useFragmentTestUsersFragment on User @relay(plural: true) { + id + name + ...useFragmentTestNestedUserFragment + } + `; + singularQuery = createOperationDescriptor( + gqlSingularQuery, + singularVariables, + ); + pluralQuery = createOperationDescriptor(gqlPluralQuery, pluralVariables); + environment.commitPayload(singularQuery, { + node: { __typename: 'User', id: '1', name: 'Alice', username: 'useralice', - profile_picture: null, }, - { - __typename: 'User', - id: '2', - name: 'Bob', - username: 'userbob', - profile_picture: null, - }, - ], - }); + }); + environment.commitPayload(pluralQuery, { + nodes: [ + { + __typename: 'User', + id: '1', + name: 'Alice', + username: 'useralice', + profile_picture: null, + }, + { + __typename: 'User', + id: '2', + name: 'Bob', + username: 'userbob', + profile_picture: null, + }, + ], + }); - // Set up renderers - SingularRenderer = (props: { - user: ?( - | useFragmentTestUserFragment$data - | useFragmentTestUsersFragment$data - ), - }) => null; - PluralRenderer = (props: { - users: ?( - | useFragmentTestUserFragment$data - | useFragmentTestUsersFragment$data - ), - }) => null; - const SingularContainer = (props: { - userRef?: {$data?: {...}, ...}, - owner: $FlowFixMe, - ... - }) => { - // We need a render a component to run a Hook - const owner = props.owner; - const userRef = props.hasOwnProperty('userRef') - ? props.userRef - : { - [ID_KEY]: owner.request.variables.id, - [FRAGMENTS_KEY]: { - useFragmentTestUserFragment: {}, - }, - [FRAGMENT_OWNER_KEY]: owner.request, - }; - const userData = useFragment(gqlSingularFragment, userRef); - return ; - }; + // Set up renderers + SingularRenderer = (props: { + user: ?( + | useFragmentTestUserFragment$data + | useFragmentTestUsersFragment$data + ), + }) => null; + PluralRenderer = (props: { + users: ?( + | useFragmentTestUserFragment$data + | useFragmentTestUsersFragment$data + ), + }) => null; + const SingularContainer = (props: { + userRef?: {$data?: {...}, ...}, + owner: $FlowFixMe, + ... + }) => { + // We need a render a component to run a Hook + const owner = props.owner; + const userRef = props.hasOwnProperty('userRef') + ? props.userRef + : { + [ID_KEY]: owner.request.variables.id, + [FRAGMENTS_KEY]: { + useFragmentTestUserFragment: {}, + }, + [FRAGMENT_OWNER_KEY]: owner.request, + }; + const userData = useFragment(gqlSingularFragment, userRef); + return ; + }; + + const PluralContainer = (props: { + usersRef?: $ReadOnlyArray<{$data?: {...}, ...}>, + owner: $FlowFixMe, + ... + }) => { + const owner = props.owner; + const usersRef = props.hasOwnProperty('usersRef') + ? props.usersRef + : owner.request.variables.ids.map(id => ({ + [ID_KEY]: id, + [FRAGMENTS_KEY]: { + useFragmentTestUsersFragment: {}, + }, + [FRAGMENT_OWNER_KEY]: owner.request, + })); - const PluralContainer = (props: { - usersRef?: $ReadOnlyArray<{$data?: {...}, ...}>, - owner: $FlowFixMe, - ... - }) => { - const owner = props.owner; - const usersRef = props.hasOwnProperty('usersRef') - ? props.usersRef - : owner.request.variables.ids.map(id => ({ - [ID_KEY]: id, - [FRAGMENTS_KEY]: { - useFragmentTestUsersFragment: {}, - }, - [FRAGMENT_OWNER_KEY]: owner.request, - })); + const usersData = useFragment(gqlPluralFragment, usersRef); + return ; + }; - const usersData = useFragment(gqlPluralFragment, usersRef); - return ; - }; + ContextProvider = ({children}: {children: React.Node}) => { + // $FlowFixMe[react-rule-hook] + const [env, _setEnv] = React.useState(environment); + // $FlowFixMe[react-rule-hook] + const relayContext = React.useMemo(() => ({environment: env}), [env]); - const relayContext = {environment}; - ContextProvider = ({children}: {children: React.Node}) => { - return ( - - {children} - - ); - }; + setEnvironment = _setEnv; + return ( + + {children} + + ); + }; - renderSingularFragment = ( - props?: { - owner?: $FlowFixMe, - userRef?: $FlowFixMe, - ... - }, - existing: $FlowFixMe, - ) => { - const elements = ( - - - - - - ); - let ret; - TestRenderer.act(() => { - if (existing) { - existing.update(elements); - ret = existing; + renderSingularFragment = ( + props?: { + owner?: $FlowFixMe, + userRef?: $FlowFixMe, + ... + }, + rerender: $FlowFixMe, + ) => { + const elements = ( + + + + + + ); + if (rerender) { + return rerender(elements); } else { - ret = TestRenderer.create(elements); + return ReactTestingLibrary.render(elements); } - }); - return ret; - }; + }; - renderPluralFragment = ( - props?: { - owner?: $FlowFixMe, - userRef?: $FlowFixMe, - ... - }, - existing: $FlowFixMe, - ) => { - const elements = ( - - - - - - ); - let ret; - TestRenderer.act(() => { - if (existing) { - existing.update(elements); - ret = existing; + renderPluralFragment = ( + props?: { + owner?: $FlowFixMe, + userRef?: $FlowFixMe, + ... + }, + rerender: $FlowFixMe, + ) => { + const elements = ( + + + + + + ); + if (rerender) { + return rerender(elements); } else { - ret = TestRenderer.create(elements); + return ReactTestingLibrary.render(elements); } + }; + }); + + afterEach(() => { + environment.mockClear(); + renderSpy.mockClear(); + RelayFeatureFlags.ENABLE_ACTIVITY_COMPATIBILITY = false; + }); + + it('handles environnment changes', () => { + renderSingularFragment(); + assertFragmentResults({ + id: '1', + name: 'Alice', + ...createFragmentRef('1', singularQuery), }); - return ret; - }; -}); -afterEach(() => { - environment.mockClear(); - renderSpy.mockClear(); -}); + const newEnvironment = createMockEnvironment(); + newEnvironment.commitPayload(singularQuery, { + node: { + __typename: 'User', + id: '1', + name: 'Alice in a different env', + username: null, + }, + }); -it('should render singular fragment without error when data is available', () => { - renderSingularFragment(); - assertFragmentResults({ - id: '1', - name: 'Alice', - ...createFragmentRef('1', singularQuery), - }); -}); + ReactTestingLibrary.act(() => { + setEnvironment(newEnvironment); + }); -it('should return the same data object if rendered multiple times: singular fragment', () => { - const container = renderSingularFragment(); - expect(renderSpy).toBeCalledTimes(1); - const actualData = renderSpy.mock.calls[0][0]; - renderSingularFragment({}, container); - expect(renderSpy).toBeCalledTimes(2); - const actualData2 = renderSpy.mock.calls[1][0]; - expect(actualData).toBe(actualData2); -}); + assertFragmentResults({ + id: '1', + name: 'Alice in a different env', + ...createFragmentRef('1', singularQuery), + }); + + ReactTestingLibrary.act(() => { + newEnvironment.commitPayload(singularQuery, { + node: { + __typename: 'User', + id: '1', + // Update name + name: 'Alice in Wonderland', + username: null, + }, + }); + }); + assertFragmentResults({ + id: '1', + // Assert that name is updated + name: 'Alice in Wonderland', + ...createFragmentRef('1', singularQuery), + }); + }); -it('should render plural fragment without error when data is available', () => { - renderPluralFragment(); - assertFragmentResults([ - { + it('should render singular fragment without error when data is available', () => { + renderSingularFragment(); + assertFragmentResults({ id: '1', name: 'Alice', - ...createFragmentRef('1', pluralQuery), - }, - { - id: '2', - name: 'Bob', - ...createFragmentRef('2', pluralQuery), - }, - ]); -}); + ...createFragmentRef('1', singularQuery), + }); + }); -it('should return the same data object if rendered multiple times: plural fragment', () => { - const container = renderPluralFragment(); - expect(renderSpy).toBeCalledTimes(1); - const actualData = renderSpy.mock.calls[0][0]; - renderPluralFragment({}, container); - expect(renderSpy).toBeCalledTimes(2); - const actualData2 = renderSpy.mock.calls[1][0]; - expect(actualData).toBe(actualData2); -}); + it('should return the same data object if rendered multiple times: singular fragment', () => { + const result = renderSingularFragment(); + expect(renderSpy).toBeCalledTimes(1); + const actualData = renderSpy.mock.calls[0][0]; + renderSingularFragment({}, result.rerender); + expect(renderSpy).toBeCalledTimes(2); + const actualData2 = renderSpy.mock.calls[1][0]; + expect(actualData).toEqual(actualData2); + }); -it('Returns [] when the fragment ref is [] (for plural fragments)', () => { - const container = renderPluralFragment({usersRef: []}); - assertFragmentResults([]); - TestRenderer.act(() => { - container?.unmount(); + it('should render plural fragment without error when data is available', () => { + renderPluralFragment(); + assertFragmentResults([ + { + id: '1', + name: 'Alice', + ...createFragmentRef('1', pluralQuery), + }, + { + id: '2', + name: 'Bob', + ...createFragmentRef('2', pluralQuery), + }, + ]); + }); + + it('should return the same data object if rendered multiple times: plural fragment', () => { + const result = renderPluralFragment(); + expect(renderSpy).toBeCalledTimes(1); + const actualData = renderSpy.mock.calls[0][0]; + renderPluralFragment({}, result?.rerender); + expect(renderSpy).toBeCalledTimes(2); + const actualData2 = renderSpy.mock.calls[1][0]; + expect(actualData).toEqual(actualData2); }); -}); -it('Returns null when the fragment ref is null (for plural fragments)', () => { - const container = renderPluralFragment({usersRef: null}); - assertFragmentResults(null); - TestRenderer.act(() => { - container?.unmount(); + it('Returns [] when the fragment ref is [] (for plural fragments)', () => { + const container = renderPluralFragment({usersRef: []}); + assertFragmentResults([]); + ReactTestingLibrary.act(() => { + container?.unmount(); + }); + }); + + it('Returns null when the fragment ref is null (for plural fragments)', () => { + const container = renderPluralFragment({usersRef: null}); + assertFragmentResults(null); + ReactTestingLibrary.act(() => { + container?.unmount(); + }); }); }); diff --git a/packages/react-relay/relay-hooks/__tests__/useFragment-with-required-test.js b/packages/react-relay/relay-hooks/__tests__/useFragment-with-required-test.js index adb9ffeda6c1e..a516ba5f72dde 100644 --- a/packages/react-relay/relay-hooks/__tests__/useFragment-with-required-test.js +++ b/packages/react-relay/relay-hooks/__tests__/useFragment-with-required-test.js @@ -40,6 +40,7 @@ test('@required(action: LOG) gets logged even if no data is "missing"', () => { node(id: $id) { ... on User { ...useFragmentWithRequiredTestUserFragment + @dangerously_unaliased_fixme } } } diff --git a/packages/react-relay/relay-hooks/__tests__/useFragmentNode-react-double-effects-test.js b/packages/react-relay/relay-hooks/__tests__/useFragmentNode-react-double-effects-test.js index 09b26d7a12c0c..c8302b1b4d2d7 100644 --- a/packages/react-relay/relay-hooks/__tests__/useFragmentNode-react-double-effects-test.js +++ b/packages/react-relay/relay-hooks/__tests__/useFragmentNode-react-double-effects-test.js @@ -27,7 +27,6 @@ let gqlFragment; let variables; let renderSpy; -// TODO(T83890478): enable once double invoked effects lands in xplat describe.skip('useFragmentNode-react-double-effects-test', () => { beforeEach(() => { jest.mock('scheduler', () => require('../../__tests__/mockScheduler')); @@ -42,6 +41,7 @@ describe.skip('useFragmentNode-react-double-effects-test', () => { query useFragmentNodeReactDoubleEffectsTestUserQuery($id: ID!) { node(id: $id) { ...useFragmentNodeReactDoubleEffectsTestUserFragment + @dangerously_unaliased_fixme } } `; @@ -109,6 +109,7 @@ describe.skip('useFragmentNode-react-double-effects-test', () => { // Assert render state of component after double invoked effects expect(renderLogs).toEqual([ + 'render: Alice', 'render: Alice', 'commit: Alice', 'cleanup: Alice', @@ -130,6 +131,7 @@ describe.skip('useFragmentNode-react-double-effects-test', () => { // Assert render state of component after double invoked effects expect(renderLogs).toEqual([ + 'render: Alice Updated', 'render: Alice Updated', 'cleanup: Alice', 'commit: Alice Updated', diff --git a/packages/react-relay/relay-hooks/__tests__/useFragmentNode-required-test.js b/packages/react-relay/relay-hooks/__tests__/useFragmentNode-required-test.js index 624a730a6735f..8baa28178959f 100644 --- a/packages/react-relay/relay-hooks/__tests__/useFragmentNode-required-test.js +++ b/packages/react-relay/relay-hooks/__tests__/useFragmentNode-required-test.js @@ -67,7 +67,7 @@ beforeEach(() => { const gqlSingularQuery = graphql` query useFragmentNodeRequiredTestUserQuery($id: ID!) { node(id: $id) { - ...useFragmentNodeRequiredTestUserFragment + ...useFragmentNodeRequiredTestUserFragment @dangerously_unaliased_fixme } } `; diff --git a/packages/react-relay/relay-hooks/__tests__/useFragmentNode-test.js b/packages/react-relay/relay-hooks/__tests__/useFragmentNode-test.js index 8e1a821ec2679..95ae8068ea74c 100644 --- a/packages/react-relay/relay-hooks/__tests__/useFragmentNode-test.js +++ b/packages/react-relay/relay-hooks/__tests__/useFragmentNode-test.js @@ -258,7 +258,7 @@ describe.each([ gqlSingularQuery = graphql` query useFragmentNodeTestUserQuery($id: ID!, $scale: Float!) { node(id: $id) { - ...useFragmentNodeTestUserFragment + ...useFragmentNodeTestUserFragment @dangerously_unaliased_fixme } } `; diff --git a/packages/react-relay/relay-hooks/__tests__/useFragment_nullability-test.js b/packages/react-relay/relay-hooks/__tests__/useFragment_nullability-test.js index c2869175aff83..1be28c2a55599 100644 --- a/packages/react-relay/relay-hooks/__tests__/useFragment_nullability-test.js +++ b/packages/react-relay/relay-hooks/__tests__/useFragment_nullability-test.js @@ -13,10 +13,12 @@ import type {useFragmentNullabilityTestFragmentWithFieldThatThrows$key} from './__generated__/useFragmentNullabilityTestFragmentWithFieldThatThrows.graphql'; +const ReactRelayLoggingContext = require('../../ReactRelayLoggingContext'); const RelayEnvironmentProvider = require('../RelayEnvironmentProvider'); const useClientQuery = require('../useClientQuery'); const React = require('react'); const TestRenderer = require('react-test-renderer'); +const {RelayFeatureFlags} = require('relay-runtime'); const {graphql} = require('relay-runtime'); const {readFragment} = require('relay-runtime/store/ResolverFragments'); const {createMockEnvironment} = require('relay-test-utils'); @@ -71,7 +73,6 @@ describe('useFragment_nullability-test.js', () => { `Error: ${error}`}> - , , ); await TestRenderer.act(() => jest.runAllTimers()); @@ -114,6 +115,8 @@ describe('useFragment_nullability-test.js', () => { it('should not throw when a resolver in non-throwing-fragment has a throwing throwOnFieldError-fragment', async () => { const environment = createMockEnvironment(); + environment.relayFieldLogger = jest.fn(); + RelayFeatureFlags.ENABLE_UI_CONTEXT_ON_RELAY_LOGGER = true; const TestComponent = () => { const data = useClientQuery( @@ -127,17 +130,33 @@ describe('useFragment_nullability-test.js', () => { return
{data.field_with_fragment_that_throws}
; }; const renderer = TestRenderer.create( - - `Error: ${error}`}> - - - , - , + + + `Error: ${error}`}> + + + + , ); await TestRenderer.act(() => jest.runAllTimers()); expect( String(renderer.toJSON()).includes('Unexpected response payload'), ).toEqual(false); + + expect(environment.relayFieldLogger).toHaveBeenCalledWith({ + error: new Error('There was an error!'), + fieldPath: 'field_that_throws', + handled: true, + kind: 'relay_resolver.error', + owner: 'useFragmentNullabilityTestFragmentWithFieldThatThrows', + shouldThrow: true, + uiContext: { + randomKey: 'randomValue', + }, + }); }); }); diff --git a/packages/react-relay/relay-hooks/__tests__/useIsParentQueryActive-test.js b/packages/react-relay/relay-hooks/__tests__/useIsParentQueryActive-test.js index fa10600a44101..3c9a621a025b8 100644 --- a/packages/react-relay/relay-hooks/__tests__/useIsParentQueryActive-test.js +++ b/packages/react-relay/relay-hooks/__tests__/useIsParentQueryActive-test.js @@ -75,7 +75,7 @@ beforeEach(() => { query = graphql` query useIsParentQueryActiveTestUserQuery($id: ID!) { node(id: $id) { - ...useIsParentQueryActiveTestUserFragment + ...useIsParentQueryActiveTestUserFragment @dangerously_unaliased_fixme } } `; @@ -364,7 +364,9 @@ it('updates the component when a pending owner fetch with multiple payloads comp query = graphql` query useIsParentQueryActiveTestUserDeferQuery($id: ID!) { node(id: $id) { - ...useIsParentQueryActiveTestUserFragment @defer + ...useIsParentQueryActiveTestUserFragment + @dangerously_unaliased_fixme + @defer } } `; diff --git a/packages/react-relay/relay-hooks/__tests__/useLazyLoadQueryNode-activity-test.js b/packages/react-relay/relay-hooks/__tests__/useLazyLoadQueryNode-activity-test.js index 6dfcf8e9c42ca..16a17776422b9 100644 --- a/packages/react-relay/relay-hooks/__tests__/useLazyLoadQueryNode-activity-test.js +++ b/packages/react-relay/relay-hooks/__tests__/useLazyLoadQueryNode-activity-test.js @@ -177,6 +177,7 @@ beforeEach(() => { const originalDisposable = originalRetain(...args); return { dispose: () => { + // $FlowFixMe[prop-missing] release(args[0].variables); originalDisposable.dispose(); }, @@ -189,6 +190,7 @@ beforeEach(() => { id name ...useLazyLoadQueryNodeActivityTestUserFragment + @dangerously_unaliased_fixme } } `; @@ -236,12 +238,18 @@ it('fetches and renders the query data', async () => { expectToBeRendered(renderFn, data); }); -it('does not dispose and GC the query when hiding', async () => { - const store = new Store(new RecordSource(), { +it('does not dispose and GC the query when hiding within store TTL', async () => { + const queryCacheExpirationTime = 1000; + const source = new RecordSource(); + const store = new Store(source, { gcScheduler: run => run(), gcReleaseBufferSize: 0, + shouldRetainWithinTTL_EXPERIMENTAL: true, + queryCacheExpirationTime, }); jest.spyOn(store, 'scheduleGC'); + const currentTime = Date.now(); + jest.spyOn(global.Date, 'now').mockImplementation(() => currentTime); environment = createMockEnvironment({ store, }); @@ -284,22 +292,108 @@ it('does not dispose and GC the query when hiding', async () => { // Assert that GC doesn't run since the query doesn't // incorrectly get fully released (which would trigger GC) - // TODO: GC should not run here expect(store.scheduleGC).toHaveBeenCalledTimes(1); + expect(source.toJSON()).toEqual({ + '1': { + __id: '1', + __typename: 'User', + id: '1', + name: 'Bob', + }, + 'client:root': { + __id: 'client:root', + __typename: '__Root', + 'node(id:"1")': { + __ref: '1', + }, + }, + }); + + ReactTestingLibrary.act(() => { + setMode('visible'); + }); + + expect(source.toJSON()).toEqual({ + '1': { + __id: '1', + __typename: 'User', + id: '1', + name: 'Bob', + }, + 'client:root': { + __id: 'client:root', + __typename: '__Root', + 'node(id:"1")': { + __ref: '1', + }, + }, + }); +}); + +it('disposes and GCs the query when hiding past query TTL in the store', async () => { + const queryCacheExpirationTime = 1000; + const source = new RecordSource(); + const store = new Store(source, { + gcScheduler: run => run(), + gcReleaseBufferSize: 0, + shouldRetainWithinTTL_EXPERIMENTAL: true, + queryCacheExpirationTime, + }); + jest.spyOn(store, 'scheduleGC'); + let currentTime = Date.now(); + jest.spyOn(global.Date, 'now').mockImplementation(() => currentTime); + environment = createMockEnvironment({ + store, + }); + // Render the component + const instance = await render( + environment, + , + ); - // Assert that a new request was not started + expect(instance?.asFragment().textContent).toEqual('Fallback'); + expectToHaveFetched(environment, query); + expect(renderFn).not.toBeCalled(); // $FlowFixMe[method-unbinding] added when improving typing for this parameters - expect(environment.execute).toHaveBeenCalledTimes(1); // TODO: shouldn't fetch + expect(environment.retain).toHaveBeenCalledTimes(1); + // $FlowFixMe[method-unbinding] added when improving typing for this parameters + environment.execute.mockClear(); + renderFn.mockClear(); + + await ReactTestingLibrary.act(() => { + environment.mock.resolve(gqlQuery, { + data: { + node: { + __typename: 'User', + id: '1', + name: 'Bob', + }, + }, + }); + }); + + const data = environment.lookup(query.fragment).data; + expectToBeRendered(renderFn, data); + // $FlowFixMe[method-unbinding] added when improving typing for this parameters + expect(environment.retain).toHaveBeenCalledTimes(1); + renderFn.mockClear(); + + currentTime += queryCacheExpirationTime; + ReactTestingLibrary.act(() => { + setMode('hidden'); + }); + + expect(store.scheduleGC).toHaveBeenCalledTimes(1); + expect(source.toJSON()).toEqual({}); ReactTestingLibrary.act(() => { setMode('visible'); }); - // $FlowFixMe[method-unbinding] added when improving typing for this parameters - expect(environment.retain).toHaveBeenCalledTimes(2); // TODO: shouldn't need to retain again + expect(source.toJSON()).toEqual({}); }); -it('does not dispose the temporary retain when hiding before commiting', async () => { +it('does not dispose the temporary retain when hiding before committing', async () => { const instance = ReactTestingLibrary.render( diff --git a/packages/react-relay/relay-hooks/__tests__/useLazyLoadQueryNode-fast-refresh-test.js b/packages/react-relay/relay-hooks/__tests__/useLazyLoadQueryNode-fast-refresh-test.js index 79272299be85e..f54904cb2b53c 100644 --- a/packages/react-relay/relay-hooks/__tests__/useLazyLoadQueryNode-fast-refresh-test.js +++ b/packages/react-relay/relay-hooks/__tests__/useLazyLoadQueryNode-fast-refresh-test.js @@ -54,6 +54,8 @@ function expectToHaveFetched( liveConfigId?: ?string, metadata?: {[key: string]: mixed}, onSubscribe?: () => void, + onResume?: (pauseTimeMs: number) => void, + onPause?: (mqttConnectionIsOk: boolean, internetIsOk: boolean) => void, poll?: ?number, transactionId?: ?string, }, @@ -102,6 +104,7 @@ describe('useLazyLoadQueryNode-fast-refresh', () => { id name ...useLazyLoadQueryNodeFastRefreshTestUserFragment + @dangerously_unaliased_fixme } } `; diff --git a/packages/react-relay/relay-hooks/__tests__/useLazyLoadQueryNode-react-double-effects-test.js b/packages/react-relay/relay-hooks/__tests__/useLazyLoadQueryNode-react-double-effects-test.js index 34a18f86ebcfc..af890d25bb65b 100644 --- a/packages/react-relay/relay-hooks/__tests__/useLazyLoadQueryNode-react-double-effects-test.js +++ b/packages/react-relay/relay-hooks/__tests__/useLazyLoadQueryNode-react-double-effects-test.js @@ -10,9 +10,11 @@ */ 'use strict'; + import type {RelayMockEnvironment} from '../../../relay-test-utils/RelayModernMockEnvironment'; import type {useLazyLoadQueryNodeReactDoubleEffectsTestUserFragment$key} from './__generated__/useLazyLoadQueryNodeReactDoubleEffectsTestUserFragment.graphql'; import typeof useLazyLoadQueryNodeReactDoubleEffectsTestUserFragment from './__generated__/useLazyLoadQueryNodeReactDoubleEffectsTestUserFragment.graphql'; +import type {useLazyLoadQueryNodeReactDoubleEffectsTestUserQuery$variables} from './__generated__/useLazyLoadQueryNodeReactDoubleEffectsTestUserQuery.graphql'; import typeof useLazyLoadQueryNodeReactDoubleEffectsTestUserQuery from './__generated__/useLazyLoadQueryNodeReactDoubleEffectsTestUserQuery.graphql'; import typeof useLazyLoadQueryNodeReactDoubleEffectsTestUserQueryWithDeferQuery from './__generated__/useLazyLoadQueryNodeReactDoubleEffectsTestUserQueryWithDeferQuery.graphql'; import type {OperationDescriptor} from 'relay-runtime/store/RelayStoreTypes'; @@ -61,6 +63,7 @@ describe.skip('useLazyLoadQueryNode-react-double-effects', () => { id name ...useLazyLoadQueryNodeReactDoubleEffectsTestUserFragment + @dangerously_unaliased_fixme } } `; @@ -71,7 +74,9 @@ describe.skip('useLazyLoadQueryNode-react-double-effects', () => { node(id: $id) { id name - ...useLazyLoadQueryNodeReactDoubleEffectsTestUserFragment @defer + ...useLazyLoadQueryNodeReactDoubleEffectsTestUserFragment + @dangerously_unaliased_fixme + @defer } } `; @@ -82,7 +87,7 @@ describe.skip('useLazyLoadQueryNode-react-double-effects', () => { `; let query; let queryWithDefer; - let variables; + let variables: useLazyLoadQueryNodeReactDoubleEffectsTestUserQuery$variables; let release; let cancelNetworkRequest; diff --git a/packages/react-relay/relay-hooks/__tests__/useLazyLoadQueryNode-test.js b/packages/react-relay/relay-hooks/__tests__/useLazyLoadQueryNode-test.js index e6f2d66fa4723..da18434933798 100644 --- a/packages/react-relay/relay-hooks/__tests__/useLazyLoadQueryNode-test.js +++ b/packages/react-relay/relay-hooks/__tests__/useLazyLoadQueryNode-test.js @@ -42,6 +42,7 @@ const { graphql, } = require('relay-runtime'); const {createMockEnvironment} = require('relay-test-utils'); + const { disallowConsoleErrors, disallowWarnings, @@ -195,6 +196,7 @@ beforeEach(() => { const originalDisposable = originalRetain(...args); return { dispose: () => { + // $FlowFixMe[prop-missing] release(args[0].variables); originalDisposable.dispose(); }, @@ -206,7 +208,7 @@ beforeEach(() => { node(id: $id) { id name - ...useLazyLoadQueryNodeTestUserFragment + ...useLazyLoadQueryNodeTestUserFragment @dangerously_unaliased_fixme } } `; @@ -750,7 +752,9 @@ describe('with @defer and re-rendering', () => { gqlQuery = graphql` query useLazyLoadQueryNodeTest1Query($id: ID) { node(id: $id) { - ...useLazyLoadQueryNodeTestDeferFragment @defer + ...useLazyLoadQueryNodeTestDeferFragment + @dangerously_unaliased_fixme + @defer } } `; diff --git a/packages/react-relay/relay-hooks/__tests__/usePaginationFragment-test.js b/packages/react-relay/relay-hooks/__tests__/usePaginationFragment-test.js index 0e741a6b14651..7d3d57ff22956 100644 --- a/packages/react-relay/relay-hooks/__tests__/usePaginationFragment-test.js +++ b/packages/react-relay/relay-hooks/__tests__/usePaginationFragment-test.js @@ -176,6 +176,19 @@ function expectFragmentResults( renderSpy.mockClear(); } +function expectFragmentLastResult(expectedCall: { + data: $FlowFixMe, + isLoadingNext: boolean, + isLoadingPrevious: boolean, + hasNext: boolean, + hasPrevious: boolean, +}) { + TestRenderer.act(() => jest.runAllImmediates()); + const lastIdx = renderSpy.mock.calls.length - 1; + assertCall(expectedCall, lastIdx); + renderSpy.mockClear(); +} + function resolveQuery(payload: mixed) { TestRenderer.act(() => { dataSource.next(payload); @@ -218,7 +231,7 @@ function createMockEnvironment() { }, ); const environment = new Environment({ - getDataID: (data: {[string]: mixed}, typename: string) => { + getDataID: (data: {+[string]: mixed}, typename: string) => { // This is the default, but making it explicit in case we need to override return data.id; }, @@ -283,6 +296,7 @@ beforeEach(() => { ) { node(id: $id) { ...usePaginationFragmentTestUserFragment + @dangerously_unaliased_fixme @arguments(isViewerFriendLocal: $isViewerFriend, orderby: $orderby) } } @@ -300,6 +314,7 @@ beforeEach(() => { node(id: $id) { actor { ...usePaginationFragmentTestUserFragment + @dangerously_unaliased_fixme @arguments(isViewerFriendLocal: $isViewerFriend, orderby: $orderby) } } @@ -317,6 +332,7 @@ beforeEach(() => { viewer { actor { ...usePaginationFragmentTestUserFragment + @dangerously_unaliased_fixme @arguments(isViewerFriendLocal: $isViewerFriend, orderby: $orderby) } } @@ -332,6 +348,7 @@ beforeEach(() => { ) { node(id: $id) { ...usePaginationFragmentTestUserFragment + @dangerously_unaliased_fixme @arguments(isViewerFriendLocal: true, orderby: ["name"]) } } @@ -348,6 +365,7 @@ beforeEach(() => { ) { node(id: $id) { ...usePaginationFragmentTestUserFragmentWithStreaming + @dangerously_unaliased_fixme @arguments(isViewerFriendLocal: $isViewerFriend, orderby: $orderby) } } @@ -555,7 +573,7 @@ beforeEach(() => { ); const userRef = props.hasOwnProperty('userRef') ? props.userRef - : nodeUserRef ?? ownerOperationRef; + : (nodeUserRef ?? ownerOperationRef); setOwner = _setOwner; @@ -4312,6 +4330,152 @@ describe.each([ }, ]); }); + + it('resets `isLoading` to false, hen loadMore gets interrupted by refresh, and useLoadMore does not trigger a reset', () => { + RelayFeatureFlags.ENABLE_USE_PAGINATION_IS_LOADING_FIX = true; + renderFragment(); + expectFragmentResults([ + { + data: initialUser, + isLoadingNext: false, + isLoadingPrevious: false, + hasNext: true, + hasPrevious: false, + }, + ]); + + TestRenderer.act(() => { + loadNext(1); + }); + + expectFragmentResults([ + { + data: initialUser, + isLoadingNext: true, + isLoadingPrevious: false, + hasNext: true, + hasPrevious: false, + }, + ]); + fetch.mockClear(); + + TestRenderer.act(() => { + refetch( + { + cursor: null, + }, + { + fetchPolicy: 'network-only', + }, + ); + }); + + const refetchVariables = { + after: null, + before: null, + first: 1, + isViewerFriendLocal: false, + last: null, + orderby: ['name'], + scale: null, + id: '1', + }; + paginationQuery = createOperationDescriptor( + gqlPaginationQuery, + refetchVariables, + {force: true}, + ); + + const REFETCH_DATA = { + data: { + node: { + __typename: 'User', + id: '1', + name: 'Alice', + friends: { + edges: [ + { + cursor: 'cursor:100', + node: { + __typename: 'User', + id: 'node:100', + name: 'name:node:100', + username: 'username:node:100', + }, + }, + ], + pageInfo: { + endCursor: 'cursor:100', + hasNextPage: true, + hasPreviousPage: false, + startCursor: 'cursor:100', + }, + }, + }, + }, + }; + resolveQuery(REFETCH_DATA); + + const expectedUser = { + id: '1', + name: 'Alice', + friends: { + edges: [ + { + cursor: 'cursor:100', + node: { + __typename: 'User', + id: 'node:100', + name: 'name:node:100', + ...createFragmentRef('node:100', paginationQuery), + }, + }, + ], + pageInfo: { + endCursor: 'cursor:100', + hasNextPage: true, + hasPreviousPage: false, + startCursor: 'cursor:100', + }, + }, + }; + + // loadNext gets interrupted by refetch, and `reset()` in useLoadMore triggers + expectFragmentLastResult({ + data: expectedUser, + isLoadingNext: false, + isLoadingPrevious: false, + hasNext: true, + hasPrevious: false, + }); + + // loadNext gets interrupted by refetch, and `reset()` in useLoadMore doesn't trigger + // because fragmentIdentifier doesn't change + TestRenderer.act(() => { + loadNext(1); + }); + TestRenderer.act(() => { + refetch( + { + cursor: null, + }, + { + fetchPolicy: 'network-only', + }, + ); + }); + + resolveQuery(REFETCH_DATA); + expectFragmentLastResult({ + data: expectedUser, + isLoadingNext: false, + isLoadingPrevious: false, + hasNext: true, + hasPrevious: false, + }); + + RelayFeatureFlags.ENABLE_USE_PAGINATION_IS_LOADING_FIX = false; + }); }); describe('paginating @fetchable types', () => { diff --git a/packages/react-relay/relay-hooks/__tests__/usePrefetchableForwardPaginationFragment_EXPERIMENTAL-test.js b/packages/react-relay/relay-hooks/__tests__/usePrefetchableForwardPaginationFragment-test.js similarity index 87% rename from packages/react-relay/relay-hooks/__tests__/usePrefetchableForwardPaginationFragment_EXPERIMENTAL-test.js rename to packages/react-relay/relay-hooks/__tests__/usePrefetchableForwardPaginationFragment-test.js index 02f2c980ba6b4..f377c5a79cbb8 100644 --- a/packages/react-relay/relay-hooks/__tests__/usePrefetchableForwardPaginationFragment_EXPERIMENTAL-test.js +++ b/packages/react-relay/relay-hooks/__tests__/usePrefetchableForwardPaginationFragment-test.js @@ -11,9 +11,9 @@ 'use strict'; -import type {usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user$key} from './__generated__/usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user.graphql'; +import type {usePrefetchableForwardPaginationFragmentTest_user$key} from './__generated__/usePrefetchableForwardPaginationFragmentTest_user.graphql'; -const usePrefetchableForwardPaginationFragment_EXPERIMENTAL = require('../usePrefetchableForwardPaginationFragment_EXPERIMENTAL'); +const usePrefetchableForwardPaginationFragment = require('../usePrefetchableForwardPaginationFragment'); const React = require('react'); const {RelayEnvironmentProvider} = require('react-relay/hooks'); const {act, create} = require('react-test-renderer'); @@ -37,8 +37,9 @@ let hasNextSpy; let isLoadingNextSpy; component Container( - userRef: ?usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user$key, + userRef: ?usePrefetchableForwardPaginationFragmentTest_user$key, minimalEdgesToFetch: number = 1, + UNSTABLE_extraVariables?: mixed, ) { const { edges, @@ -47,9 +48,9 @@ component Container( refetch: _refetch, hasNext, isLoadingNext, - } = usePrefetchableForwardPaginationFragment_EXPERIMENTAL( + } = usePrefetchableForwardPaginationFragment( graphql` - fragment usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user on User + fragment usePrefetchableForwardPaginationFragmentTest_user on User @refetchable( queryName: "usePrefetchableForwardPaginationFragmentRefetchQuery" ) { @@ -70,7 +71,9 @@ component Container( userRef, BUFFER_SIZE, null, - {}, + { + UNSTABLE_extraVariables, + }, minimalEdgesToFetch, ); loadMore = loadNext; @@ -102,7 +105,7 @@ beforeEach(() => { query = createOperationDescriptor( graphql` - query usePrefetchableForwardPaginationFragmentEXPERIMENTALTestQuery( + query usePrefetchableForwardPaginationFragmentTestQuery( $id: ID! $after: ID $first: Int @@ -110,7 +113,8 @@ beforeEach(() => { $last: Int ) { node(id: $id) { - ...usePrefetchableForwardPaginationFragmentEXPERIMENTALTest_user + ...usePrefetchableForwardPaginationFragmentTest_user + @dangerously_unaliased_fixme } } `, @@ -997,3 +1001,109 @@ it('should fetch the amount of items enough to fulfill the product and cache', ( 4, ); }); + +it('getServerEdges should return all unfiltered server edges', () => { + const fragmentKey = environment.lookup(query.fragment).data?.node; + // render the initial page + let app; + const extraVariablesFn = jest.fn(); + // $FlowFixMe[missing-local-annot] + const getExtraVariables = function ({hasNext, getServerEdges}) { + const edges = getServerEdges(); + extraVariablesFn(hasNext, edges); + }; + act(() => { + app = create( + + + + + , + ); + }); + if (app == null) { + throw new Error('app should not be null'); + } + expect(app.toJSON()).toEqual('node1/0'); + + // Prefetches 2 more + expect(environment.mock.getAllOperations().length).toBe(1); + expect(environment.mock.getAllOperations()[0].fragment.variables.first).toBe( + 2, + ); + + expect(extraVariablesFn).toBeCalledTimes(1); + expect(extraVariablesFn).toBeCalledWith(true, [ + { + cursor: 'cursor:1', + node: {__typename: 'User', id: 'node:1', name: 'node1'}, + }, + ]); + extraVariablesFn.mockClear(); + + act(() => { + environment.mock.resolveMostRecentOperation({ + data: { + node: { + __typename: 'User', + id: '1', + name: 'Alice', + friends: { + edges: [ + { + cursor: 'cursor:2', + node: { + __typename: 'User', + id: 'node:2', + name: 'node2', + username: 'username:node:2', + }, + }, + { + cursor: 'cursor:3', + node: { + __typename: 'User', + id: 'node:3', + name: 'node3', + username: 'username:node:3', + }, + }, + ], + pageInfo: { + startCursor: 'cursor:3', + endCursor: 'cursor:3', + hasNextPage: true, + hasPreviousPage: true, + }, + }, + }, + }, + }); + }); + + act(() => { + loadMore(1); + }); + + // 2 edges in display but 3 server edges loaded in total + expect(app.toJSON()).toEqual('node1,node2/1'); + expect(extraVariablesFn).toBeCalledTimes(1); + expect(extraVariablesFn).toBeCalledWith(true, [ + { + cursor: 'cursor:1', + node: {__typename: 'User', id: 'node:1', name: 'node1'}, + }, + { + cursor: 'cursor:2', + node: {__typename: 'User', id: 'node:2', name: 'node2'}, + }, + { + cursor: 'cursor:3', + node: {__typename: 'User', id: 'node:3', name: 'node3'}, + }, + ]); +}); diff --git a/packages/react-relay/relay-hooks/__tests__/usePreloadedQuery-provided-variables-test.js b/packages/react-relay/relay-hooks/__tests__/usePreloadedQuery-provided-variables-test.js index 3bcce8c726f13..7ba7ddcf8f985 100644 --- a/packages/react-relay/relay-hooks/__tests__/usePreloadedQuery-provided-variables-test.js +++ b/packages/react-relay/relay-hooks/__tests__/usePreloadedQuery-provided-variables-test.js @@ -72,12 +72,13 @@ const queryPV = graphql` node(id: $id) { id ...usePreloadedQueryProvidedVariablesTest_Fragment + @dangerously_unaliased_fixme } } `; const preloadableConcreteRequestPV = { - kind: 'PreloadableConcreteRequest', + kind: 'PreloadableConcreteRequest' as const, params: queryPV.params, }; @@ -262,6 +263,7 @@ describe('usePreloadedQuery provided variables (%s)', () => { query usePreloadedQueryProvidedVariablesTest_badQuery($id: ID!) { node(id: $id) { ...usePreloadedQueryProvidedVariablesTest_badFragment + @dangerously_unaliased_fixme } } `; diff --git a/packages/react-relay/relay-hooks/__tests__/usePreloadedQuery-react-double-effects-test.js b/packages/react-relay/relay-hooks/__tests__/usePreloadedQuery-react-double-effects-test.js index 78cb799ca293d..90f9bb0180564 100644 --- a/packages/react-relay/relay-hooks/__tests__/usePreloadedQuery-react-double-effects-test.js +++ b/packages/react-relay/relay-hooks/__tests__/usePreloadedQuery-react-double-effects-test.js @@ -126,6 +126,7 @@ describe.skip('usePreloadedQuery-react-double-effects', () => { id name ...usePreloadedQueryReactDoubleEffectsTestFragment + @dangerously_unaliased_fixme } } `; @@ -134,7 +135,9 @@ describe.skip('usePreloadedQuery-react-double-effects', () => { node(id: $id) { id name - ...usePreloadedQueryReactDoubleEffectsTestFragment @defer + ...usePreloadedQueryReactDoubleEffectsTestFragment + @dangerously_unaliased_fixme + @defer } } `; diff --git a/packages/react-relay/relay-hooks/__tests__/useQueryLoader-live-query-test.js b/packages/react-relay/relay-hooks/__tests__/useQueryLoader-live-query-test.js index 46b45544d4327..33c8b6d6456c0 100644 --- a/packages/react-relay/relay-hooks/__tests__/useQueryLoader-live-query-test.js +++ b/packages/react-relay/relay-hooks/__tests__/useQueryLoader-live-query-test.js @@ -346,7 +346,7 @@ it('does not release or cancel the query before the new component tree unsuspend let transitionToSecondRoute; function ConcurrentWrapper() { - const [route, setRoute] = React.useState('FIRST'); + const [route, setRoute] = React.useState<'FIRST' | 'SECOND'>('FIRST'); transitionToSecondRoute = () => React.startTransition(() => setRoute('SECOND')); @@ -377,6 +377,8 @@ it('does not release or cancel the query before the new component tree unsuspend expect(currentDispose).not.toHaveBeenCalled(); ReactTestRenderer.act(() => { + /* $FlowFixMe[constant-condition] Error discovered during Constant + * Condition roll out. See https://fburl.com/workplace/1v97vimq. */ resolve && resolve(); jest.runAllImmediates(); }); diff --git a/packages/react-relay/relay-hooks/__tests__/useQueryLoader-multiple-calls-test.js b/packages/react-relay/relay-hooks/__tests__/useQueryLoader-multiple-calls-test.js index 5a8540d577d1e..4e2ab8ec7bd50 100644 --- a/packages/react-relay/relay-hooks/__tests__/useQueryLoader-multiple-calls-test.js +++ b/packages/react-relay/relay-hooks/__tests__/useQueryLoader-multiple-calls-test.js @@ -39,7 +39,7 @@ const query = graphql` `; const preloadableConcreteRequest = { - kind: 'PreloadableConcreteRequest', + kind: 'PreloadableConcreteRequest' as const, params: query.params, }; diff --git a/packages/react-relay/relay-hooks/__tests__/useQueryLoader-react-double-effects-test.js b/packages/react-relay/relay-hooks/__tests__/useQueryLoader-react-double-effects-test.js index 183feff6ac13f..5f00465c7de54 100644 --- a/packages/react-relay/relay-hooks/__tests__/useQueryLoader-react-double-effects-test.js +++ b/packages/react-relay/relay-hooks/__tests__/useQueryLoader-react-double-effects-test.js @@ -35,6 +35,8 @@ function expectToHaveFetched( liveConfigId?: ?string, metadata?: {[key: string]: mixed}, onSubscribe?: () => void, + onResume?: (pauseTimeMs: number) => void, + onPause?: (mqttConnectionIsOk: boolean, internetIsOk: boolean) => void, poll?: ?number, transactionId?: ?string, }, @@ -119,6 +121,7 @@ describe.skip('useQueryLoader-react-double-effects', () => { id name ...useQueryLoaderReactDoubleEffectsTestUserFragment + @dangerously_unaliased_fixme } } `; @@ -148,7 +151,7 @@ describe.skip('useQueryLoader-react-double-effects', () => { LoaderComponent = function TestLoaderComponent(props: any) { const [queryRef] = useQueryLoader(gqlQuery, props.initialQueryRef); - const queryRefId = queryRef == null ? 'null' : queryRef.id ?? 'Unknown'; + const queryRefId = queryRef == null ? 'null' : (queryRef.id ?? 'Unknown'); // $FlowFixMe[react-rule-hook] useEffect(() => { loaderRenderLogs.push(`commit: ${queryRefId}`); diff --git a/packages/react-relay/relay-hooks/__tests__/useQueryLoader-test.js b/packages/react-relay/relay-hooks/__tests__/useQueryLoader-test.js index 4bed819a18791..38f8da035e6df 100644 --- a/packages/react-relay/relay-hooks/__tests__/useQueryLoader-test.js +++ b/packages/react-relay/relay-hooks/__tests__/useQueryLoader-test.js @@ -405,7 +405,7 @@ describe.each([ let transitionToSecondRoute; function ConcurrentWrapper() { - const [route, setRoute] = React.useState('FIRST'); + const [route, setRoute] = React.useState<'FIRST' | 'SECOND'>('FIRST'); transitionToSecondRoute = () => React.startTransition(() => setRoute('SECOND')); @@ -436,6 +436,8 @@ describe.each([ expect(currentRelease).not.toHaveBeenCalled(); ReactTestRenderer.act(() => { + /* $FlowFixMe[constant-condition] Error discovered during Constant + * Condition roll out. See https://fburl.com/workplace/1v97vimq. */ resolve && resolve(); jest.runAllImmediates(); }); diff --git a/packages/react-relay/relay-hooks/__tests__/useRefetchableFragment-test.js b/packages/react-relay/relay-hooks/__tests__/useRefetchableFragment-test.js index 89d95e192d86a..4986d08a6db4c 100644 --- a/packages/react-relay/relay-hooks/__tests__/useRefetchableFragment-test.js +++ b/packages/react-relay/relay-hooks/__tests__/useRefetchableFragment-test.js @@ -96,7 +96,7 @@ describe('useRefetchableFragment', () => { gqlQuery = graphql` query useRefetchableFragmentTestUserQuery($id: ID!, $scale: Float!) { node(id: $id) { - ...useRefetchableFragmentTestUserFragment + ...useRefetchableFragmentTestUserFragment @dangerously_unaliased_fixme } } `; diff --git a/packages/react-relay/relay-hooks/__tests__/useRefetchableFragmentNode-test.js b/packages/react-relay/relay-hooks/__tests__/useRefetchableFragmentNode-test.js index a360ec56fe025..bee8b54577ec7 100644 --- a/packages/react-relay/relay-hooks/__tests__/useRefetchableFragmentNode-test.js +++ b/packages/react-relay/relay-hooks/__tests__/useRefetchableFragmentNode-test.js @@ -35,7 +35,12 @@ import type { useRefetchableFragmentNodeTestUserQuery$data, useRefetchableFragmentNodeTestUserQuery$variables, } from './__generated__/useRefetchableFragmentNodeTestUserQuery.graphql'; -import type {OperationDescriptor, Variables} from 'relay-runtime'; +import type { + FetchPolicy, + OperationDescriptor, + RenderPolicy, + Variables, +} from 'relay-runtime'; import type {Query} from 'relay-runtime/util/RelayRuntimeTypes'; const RelayEnvironmentProvider = require('../RelayEnvironmentProvider'); @@ -64,810 +69,1614 @@ injectPromisePolyfill__DEPRECATED(); const {useMemo, useState, useEffect} = React; -describe.each([['New', useRefetchableFragmentInternal]])( - 'useRefetchableFragmentNode (%s)', - (_hookName, useRefetchableFragmentNodeOriginal) => { - const isUsingNewImplementation = - useRefetchableFragmentNodeOriginal === useRefetchableFragmentInternal; - let environment; - let gqlQuery: - | Query< - useRefetchableFragmentNodeTest1Query$variables, - useRefetchableFragmentNodeTest1Query$data, - > - | Query< - useRefetchableFragmentNodeTest2Query$variables, - useRefetchableFragmentNodeTest2Query$data, - > - | Query< - useRefetchableFragmentNodeTestUserQuery$variables, - useRefetchableFragmentNodeTestUserQuery$data, - >; - let gqlQueryNestedFragment; - let gqlRefetchQuery: - | Query< - useRefetchableFragmentNodeTest1FragmentRefetchQuery$variables, - useRefetchableFragmentNodeTest1FragmentRefetchQuery$data, - > - | Query< - useRefetchableFragmentNodeTest3FragmentRefetchQuery$variables, - useRefetchableFragmentNodeTest3FragmentRefetchQuery$data, - > - | Query< - useRefetchableFragmentNodeTestUserFragmentRefetchQuery$variables, - useRefetchableFragmentNodeTestUserFragmentRefetchQuery$data, - >; - let gqlQueryWithArgs; - let gqlQueryWithLiteralArgs; - let gqlRefetchQueryWithArgs; - let gqlFragment; - let gqlFragmentWithArgs; - let query; - let queryNestedFragment; - let refetchQuery; - let queryWithArgs; - let queryWithLiteralArgs; - let refetchQueryWithArgs; - let variables: - | {id: string, scale: number} - | {id: string} - | {nodeID: string, scale: number}; - let variablesNestedFragment; - let forceUpdate; - let setEnvironment; - let setOwner; - let fetchPolicy; - let renderPolicy; - let renderFragment; - let commitSpy; - let refetch; - let callDuringRenderCount; - let Renderer; - - class ErrorBoundary extends React.Component { - state: {error: ?Error} = {error: null}; - componentDidCatch(error: Error) { - this.setState({error}); +describe('useRefetchableFragmentInternal (%s)', () => { + let environment; + let gqlQuery: + | Query< + useRefetchableFragmentNodeTest1Query$variables, + useRefetchableFragmentNodeTest1Query$data, + > + | Query< + useRefetchableFragmentNodeTest2Query$variables, + useRefetchableFragmentNodeTest2Query$data, + > + | Query< + useRefetchableFragmentNodeTestUserQuery$variables, + useRefetchableFragmentNodeTestUserQuery$data, + >; + let gqlQueryNestedFragment; + let gqlRefetchQuery: + | Query< + useRefetchableFragmentNodeTest1FragmentRefetchQuery$variables, + useRefetchableFragmentNodeTest1FragmentRefetchQuery$data, + > + | Query< + useRefetchableFragmentNodeTest3FragmentRefetchQuery$variables, + useRefetchableFragmentNodeTest3FragmentRefetchQuery$data, + > + | Query< + useRefetchableFragmentNodeTestUserFragmentRefetchQuery$variables, + useRefetchableFragmentNodeTestUserFragmentRefetchQuery$data, + >; + let gqlQueryWithArgs; + let gqlQueryWithLiteralArgs; + let gqlRefetchQueryWithArgs; + let gqlFragment; + let gqlFragmentWithArgs; + let query; + let queryNestedFragment; + let refetchQuery; + let queryWithArgs; + let queryWithLiteralArgs; + let refetchQueryWithArgs; + let variables: + | {id: string, scale: number} + | {id: string} + | {nodeID: string, scale: number}; + let variablesNestedFragment; + let forceUpdate; + let setEnvironment; + let setOwner; + let fetchPolicy: FetchPolicy; + let renderPolicy; + let renderFragment; + let commitSpy; + let refetch; + let callDuringRenderCount; + let Renderer; + + class ErrorBoundary extends React.Component { + state: {error: ?Error} = {error: null}; + componentDidCatch(error: Error) { + this.setState({error}); + } + render(): React.Node { + const {children, fallback: Fallback} = this.props; + const {error} = this.state; + if (error) { + return ; + } + return children; + } + } + + function useRefetchableFragmentNode(fragmentNode: any, fragmentRef: any) { + const result = useRefetchableFragmentInternal( + fragmentNode, + fragmentRef, + 'TestDisplayName', + ); + refetch = result.refetch; + useEffect(() => { + commitSpy(result.fragmentData, refetch); + }); + return result; + } + + function assertCall(expected: {data: any}, idx: number) { + const actualData = commitSpy.mock.calls[idx][0]; + + expect(actualData).toEqual(expected.data); + } + + function expectFragmentResults( + expectedCalls: $ReadOnlyArray<{data: $FlowFixMe}>, + ) { + // This ensures that useEffect runs + TestRenderer.act(() => jest.runAllImmediates()); + expect(commitSpy).toBeCalledTimes(expectedCalls.length); + expectedCalls.forEach((expected, idx) => assertCall(expected, idx)); + commitSpy.mockClear(); + } + + function createFragmentRef( + id: string, + owner: OperationDescriptor, + fragmentName: string = 'useRefetchableFragmentNodeTestNestedUserFragment', + ) { + return { + [ID_KEY]: id, + [FRAGMENTS_KEY]: { + [fragmentName]: {}, + }, + [FRAGMENT_OWNER_KEY]: owner.request, + }; + } + + beforeEach(() => { + // Set up mocks + jest.spyOn(console, 'warn').mockImplementationOnce(() => {}); + jest.mock('warning'); + jest.mock('scheduler', () => require('../../__tests__/mockScheduler')); + /* $FlowFixMe[underconstrained-implicit-instantiation] error found when + * enabling Flow LTI mode */ + commitSpy = jest.fn<_, mixed>(); + + fetchPolicy = 'store-or-network'; + renderPolicy = 'partial' as RenderPolicy; + callDuringRenderCount = 0; + + // Set up environment and base data + environment = createMockEnvironment(); + graphql` + fragment useRefetchableFragmentNodeTestNestedUserFragment on User { + username } - render(): React.Node { - const {children, fallback: Fallback} = this.props; - const {error} = this.state; - if (error) { - return ; + `; + gqlFragmentWithArgs = graphql` + fragment useRefetchableFragmentNodeTestUserFragmentWithArgs on User + @refetchable( + queryName: "useRefetchableFragmentNodeTestUserFragmentWithArgsRefetchQuery" + ) + @argumentDefinitions(scaleLocal: {type: "Float!"}) { + id + name + profile_picture(scale: $scaleLocal) { + uri } - return children; + ...useRefetchableFragmentNodeTestNestedUserFragment } - } + `; + gqlFragment = graphql` + fragment useRefetchableFragmentNodeTestUserFragment on User + @refetchable( + queryName: "useRefetchableFragmentNodeTestUserFragmentRefetchQuery" + ) { + id + name + profile_picture(scale: $scale) { + uri + } + ...useRefetchableFragmentNodeTestNestedUserFragment + } + `; + gqlQuery = graphql` + query useRefetchableFragmentNodeTestUserQuery($id: ID!, $scale: Float!) { + node(id: $id) { + ...useRefetchableFragmentNodeTestUserFragment + @dangerously_unaliased_fixme + } + } + `; + gqlQueryNestedFragment = graphql` + query useRefetchableFragmentNodeTestUserQueryNestedFragmentQuery( + $id: ID! + $scale: Float! + ) { + node(id: $id) { + actor { + ...useRefetchableFragmentNodeTestUserFragment + @dangerously_unaliased_fixme + } + } + } + `; + gqlQueryWithArgs = graphql` + query useRefetchableFragmentNodeTestUserQueryWithArgsQuery( + $id: ID! + $scale: Float! + ) { + node(id: $id) { + ...useRefetchableFragmentNodeTestUserFragmentWithArgs + @dangerously_unaliased_fixme + @arguments(scaleLocal: $scale) + } + } + `; + gqlQueryWithLiteralArgs = graphql` + query useRefetchableFragmentNodeTestUserQueryWithLiteralArgsQuery( + $id: ID! + ) { + node(id: $id) { + ...useRefetchableFragmentNodeTestUserFragmentWithArgs + @dangerously_unaliased_fixme + @arguments(scaleLocal: 16) + } + } + `; + variables = {id: '1', scale: 16}; + variablesNestedFragment = {id: '', scale: 16}; + gqlRefetchQuery = require('./__generated__/useRefetchableFragmentNodeTestUserFragmentRefetchQuery.graphql'); + gqlRefetchQueryWithArgs = require('./__generated__/useRefetchableFragmentNodeTestUserFragmentWithArgsRefetchQuery.graphql'); + + invariant( + gqlFragment.metadata?.refetch?.operation === gqlRefetchQuery, + 'useRefetchableFragment-test: Expected refetchable fragment metadata to contain operation.', + ); + invariant( + gqlFragmentWithArgs.metadata?.refetch?.operation === + gqlRefetchQueryWithArgs, + 'useRefetchableFragment-test: Expected refetchable fragment metadata to contain operation.', + ); + + query = createOperationDescriptor(gqlQuery, variables); + queryNestedFragment = createOperationDescriptor( + gqlQueryNestedFragment, + variablesNestedFragment, + ); + refetchQuery = createOperationDescriptor(gqlRefetchQuery, variables, { + force: true, + }); + queryWithArgs = createOperationDescriptor(gqlQueryWithArgs, variables); + queryWithLiteralArgs = createOperationDescriptor(gqlQueryWithLiteralArgs, { + id: variables.id, + }); + refetchQueryWithArgs = createOperationDescriptor( + gqlRefetchQueryWithArgs, + variables, + {force: true}, + ); + environment.commitPayload(query, { + node: { + __typename: 'User', + id: '1', + name: 'Alice', + username: 'useralice', + profile_picture: null, + }, + }); - function useRefetchableFragmentNode(fragmentNode: any, fragmentRef: any) { - const result = useRefetchableFragmentNodeOriginal( - fragmentNode, - fragmentRef, - 'TestDisplayName', + // Set up renderers + Renderer = (props: {user: mixed}) => null; + + const Container = (props: { + userRef?: {...}, + owner: OperationDescriptor, + fragment?: $FlowFixMe, + callDuringRenderKey?: ?number, + ... + }) => { + // We need a render a component to run a Hook + const [owner, setOwner_] = useState(props.owner); + const [, setCount_] = useState(0); + const fragment = props.fragment ?? gqlFragment; + const artificialUserRef = useMemo( + () => ({ + [ID_KEY]: + owner.request.variables.id ?? owner.request.variables.nodeID, + [FRAGMENTS_KEY]: { + // $FlowFixMe[invalid-computed-prop] Error found while enabling LTI on this file + [fragment.name]: {}, + }, + [FRAGMENT_OWNER_KEY]: owner.request, + }), + [owner, fragment.name], ); - refetch = result.refetch; - useEffect(() => { - commitSpy(result.fragmentData, refetch); - }); - return result; - } + const userRef = props.hasOwnProperty('userRef') + ? props.userRef + : artificialUserRef; - function assertCall(expected: {data: any}, idx: number) { - const actualData = commitSpy.mock.calls[idx][0]; + forceUpdate = setCount_; + setOwner = setOwner_; - expect(actualData).toEqual(expected.data); - } + const {fragmentData: userData, refetch: refetchInternal} = + useRefetchableFragmentNode(fragment, userRef); - function expectFragmentResults( - expectedCalls: $ReadOnlyArray<{data: $FlowFixMe}>, - ) { - // This ensures that useEffect runs - TestRenderer.act(() => jest.runAllImmediates()); - expect(commitSpy).toBeCalledTimes(expectedCalls.length); - expectedCalls.forEach((expected, idx) => assertCall(expected, idx)); - commitSpy.mockClear(); - } + if ( + props.callDuringRenderKey != null && + props.callDuringRenderKey !== callDuringRenderCount + ) { + callDuringRenderCount++; + refetchInternal({}); + } + return ; + }; - function createFragmentRef( - id: string, - owner: OperationDescriptor, - fragmentName: string = 'useRefetchableFragmentNodeTestNestedUserFragment', - ) { - return { - [ID_KEY]: id, - [FRAGMENTS_KEY]: { - [fragmentName]: {}, - }, - [FRAGMENT_OWNER_KEY]: owner.request, - }; - } + const ContextProvider = ({children}: {children: React.Node}) => { + const [env, _setEnv] = useState(environment); + const relayContext = useMemo(() => ({environment: env}), [env]); - beforeEach(() => { - // Set up mocks - jest.spyOn(console, 'warn').mockImplementationOnce(() => {}); - jest.mock('warning'); - jest.mock('scheduler', () => require('../../__tests__/mockScheduler')); - /* $FlowFixMe[underconstrained-implicit-instantiation] error found when - * enabling Flow LTI mode */ - commitSpy = jest.fn<_, mixed>(); - - fetchPolicy = 'store-or-network'; - renderPolicy = 'partial'; - callDuringRenderCount = 0; - - // Set up environment and base data - environment = createMockEnvironment(); - graphql` - fragment useRefetchableFragmentNodeTestNestedUserFragment on User { - username - } - `; - gqlFragmentWithArgs = graphql` - fragment useRefetchableFragmentNodeTestUserFragmentWithArgs on User - @refetchable( - queryName: "useRefetchableFragmentNodeTestUserFragmentWithArgsRefetchQuery" - ) - @argumentDefinitions(scaleLocal: {type: "Float!"}) { + setEnvironment = _setEnv; + + return ( + + {children} + + ); + }; + + const Fallback = () => { + useEffect(() => { + Scheduler.log('Fallback'); + }); + + return 'Fallback'; + }; + + renderFragment = (args?: { + isConcurrent?: boolean, + owner?: $FlowFixMe, + userRef?: $FlowFixMe, + fragment?: $FlowFixMe, + callDuringRenderKey?: ?number, + ... + }): $FlowFixMe => { + const {isConcurrent = false, ...props} = args ?? {}; + let renderer; + TestRenderer.act(() => { + renderer = TestRenderer.create( + `Error: ${error.message}`}> + }> + + + + + , + // $FlowFixMe[prop-missing] - error revealed when flow-typing ReactTestRenderer + {unstable_isConcurrent: isConcurrent}, + ); + jest.runAllImmediates(); + }); + return renderer; + }; + }); + + afterEach(() => { + environment.mockClear(); + commitSpy.mockClear(); + }); + + describe('initial render', () => { + // The bulk of initial render behavior is covered in useFragmentNode-test, + // so this suite covers the basic cases as a sanity check. + it('should throw error if fragment is plural', () => { + jest.spyOn(console, 'error').mockImplementationOnce(() => {}); + + const UserFragment = graphql` + fragment useRefetchableFragmentNodeTest4Fragment on User + @relay(plural: true) { id - name - profile_picture(scale: $scaleLocal) { - uri - } - ...useRefetchableFragmentNodeTestNestedUserFragment } `; - gqlFragment = graphql` - fragment useRefetchableFragmentNodeTestUserFragment on User - @refetchable( - queryName: "useRefetchableFragmentNodeTestUserFragmentRefetchQuery" - ) { + const renderer = renderFragment({fragment: UserFragment}); + expect( + renderer + .toJSON() + .includes('Remove `@relay(plural: true)` from fragment'), + ).toEqual(true); + }); + + it('should throw error if fragment is missing @refetchable directive', () => { + jest.spyOn(console, 'error').mockImplementationOnce(() => {}); + + const UserFragment = graphql` + fragment useRefetchableFragmentNodeTest5Fragment on User { id - name - profile_picture(scale: $scale) { - uri - } - ...useRefetchableFragmentNodeTestNestedUserFragment - } - `; - gqlQuery = graphql` - query useRefetchableFragmentNodeTestUserQuery( - $id: ID! - $scale: Float! - ) { - node(id: $id) { - ...useRefetchableFragmentNodeTestUserFragment - } - } - `; - gqlQueryNestedFragment = graphql` - query useRefetchableFragmentNodeTestUserQueryNestedFragmentQuery( - $id: ID! - $scale: Float! - ) { - node(id: $id) { - actor { - ...useRefetchableFragmentNodeTestUserFragment - } - } - } - `; - gqlQueryWithArgs = graphql` - query useRefetchableFragmentNodeTestUserQueryWithArgsQuery( - $id: ID! - $scale: Float! - ) { - node(id: $id) { - ...useRefetchableFragmentNodeTestUserFragmentWithArgs - @arguments(scaleLocal: $scale) - } - } - `; - gqlQueryWithLiteralArgs = graphql` - query useRefetchableFragmentNodeTestUserQueryWithLiteralArgsQuery( - $id: ID! - ) { - node(id: $id) { - ...useRefetchableFragmentNodeTestUserFragmentWithArgs - @arguments(scaleLocal: 16) - } } `; - variables = {id: '1', scale: 16}; - variablesNestedFragment = {id: '', scale: 16}; - gqlRefetchQuery = require('./__generated__/useRefetchableFragmentNodeTestUserFragmentRefetchQuery.graphql'); - gqlRefetchQueryWithArgs = require('./__generated__/useRefetchableFragmentNodeTestUserFragmentWithArgsRefetchQuery.graphql'); - - invariant( - gqlFragment.metadata?.refetch?.operation === gqlRefetchQuery, - 'useRefetchableFragment-test: Expected refetchable fragment metadata to contain operation.', - ); - invariant( - gqlFragmentWithArgs.metadata?.refetch?.operation === - gqlRefetchQueryWithArgs, - 'useRefetchableFragment-test: Expected refetchable fragment metadata to contain operation.', - ); + const renderer = renderFragment({fragment: UserFragment}); + expect( + renderer + .toJSON() + .includes( + 'Did you forget to add a @refetchable directive to the fragment?', + ), + ).toEqual(true); + }); - query = createOperationDescriptor(gqlQuery, variables); - queryNestedFragment = createOperationDescriptor( - gqlQueryNestedFragment, - variablesNestedFragment, - ); - refetchQuery = createOperationDescriptor(gqlRefetchQuery, variables, { - force: true, + it('should render fragment without error when data is available', () => { + renderFragment(); + expectFragmentResults([ + { + data: { + id: '1', + name: 'Alice', + profile_picture: null, + ...createFragmentRef('1', query), + }, + }, + ]); + }); + + it('should render fragment without error when ref is null', () => { + renderFragment({userRef: null}); + expectFragmentResults([{data: null}]); + }); + + it('should render fragment without error when ref is undefined', () => { + renderFragment({userRef: undefined}); + expectFragmentResults([{data: null}]); + }); + + it('should update when fragment data changes', () => { + renderFragment(); + expectFragmentResults([ + { + data: { + id: '1', + name: 'Alice', + profile_picture: null, + ...createFragmentRef('1', query), + }, + }, + ]); + + TestRenderer.act(() => { + environment.commitPayload(query, { + node: { + __typename: 'User', + id: '1', + // Update name + name: 'Alice in Wonderland', + }, + }); }); - queryWithArgs = createOperationDescriptor(gqlQueryWithArgs, variables); - queryWithLiteralArgs = createOperationDescriptor( - gqlQueryWithLiteralArgs, + expectFragmentResults([ { - id: variables.id, + data: { + id: '1', + // Assert that name is updated + name: 'Alice in Wonderland', + profile_picture: null, + ...createFragmentRef('1', query), + }, }, + ]); + }); + + it('should throw a promise if data is missing for fragment and request is in flight', () => { + // This prevents console.error output in the test, which is expected + jest.spyOn(console, 'error').mockImplementationOnce(() => {}); + + const missingDataVariables = {...variables, id: '4'}; + const missingDataQuery = createOperationDescriptor( + gqlQuery, + missingDataVariables, ); - refetchQueryWithArgs = createOperationDescriptor( - gqlRefetchQueryWithArgs, - variables, - {force: true}, - ); - environment.commitPayload(query, { - node: { - __typename: 'User', - id: '1', - name: 'Alice', - username: 'useralice', - profile_picture: null, - }, + // Commit a payload with name and profile_picture are missing + TestRenderer.act(() => { + environment.commitPayload(missingDataQuery, { + node: { + __typename: 'User', + id: '4', + }, + }); }); - // Set up renderers - Renderer = (props: {user: mixed}) => null; - - const Container = (props: { - userRef?: {...}, - owner: OperationDescriptor, - fragment?: $FlowFixMe, - callDuringRenderKey?: ?number, - ... - }) => { - // We need a render a component to run a Hook - const [owner, setOwner_] = useState(props.owner); - const [, setCount_] = useState(0); - const fragment = props.fragment ?? gqlFragment; - const artificialUserRef = useMemo( - () => ({ - [ID_KEY]: - owner.request.variables.id ?? owner.request.variables.nodeID, - [FRAGMENTS_KEY]: { - // $FlowFixMe[invalid-computed-prop] Error found while enabling LTI on this file - [fragment.name]: {}, - }, - [FRAGMENT_OWNER_KEY]: owner.request, - }), - [owner, fragment.name], - ); - const userRef = props.hasOwnProperty('userRef') - ? props.userRef - : artificialUserRef; - - forceUpdate = setCount_; - setOwner = setOwner_; - - const {fragmentData: userData, refetch: refetchInternal} = - useRefetchableFragmentNode(fragment, userRef); - - if ( - props.callDuringRenderKey != null && - props.callDuringRenderKey !== callDuringRenderCount - ) { - callDuringRenderCount++; - refetchInternal({}); - } - return ; - }; + fetchQuery(environment, missingDataQuery).subscribe({}); - const ContextProvider = ({children}: {children: React.Node}) => { - const [env, _setEnv] = useState(environment); - const relayContext = useMemo(() => ({environment: env}), [env]); + const renderer = renderFragment({owner: missingDataQuery}); + expect(renderer.toJSON()).toEqual('Fallback'); + }); + }); - setEnvironment = _setEnv; + describe('refetch', () => { + let release; + let isOperationRetained; - return ( - - {children} - - ); - }; + beforeEach(() => { + ({release_DEPRECATED: release, isOperationRetained} = + trackRetentionForEnvironment(environment)); + }); - const Fallback = () => { - useEffect(() => { - Scheduler.log('Fallback'); - }); + function expectRequestIsInFlight( + expected: any, + requestEnvironment: RelayMockEnvironment = environment, + ) { + // $FlowFixMe[method-unbinding] added when improving typing for this parameters + expect(requestEnvironment.executeWithSource).toBeCalledTimes( + expected.requestCount, + ); + expect( + requestEnvironment.mock.isLoading( + expected.gqlRefetchQuery ?? gqlRefetchQuery, + expected.refetchVariables, + {force: true}, + ), + ).toEqual(expected.inFlight); + } - return 'Fallback'; - }; + function expectFragmentIsRefetching( + renderer: any, + expected: { + refetchVariables: Variables, + refetchQuery?: OperationDescriptor, + gqlRefetchQuery?: $FlowFixMe, + }, + env: RelayMockEnvironment = environment, + ) { + expect(commitSpy).toBeCalledTimes(0); + commitSpy.mockClear(); - renderFragment = (args?: { - isConcurrent?: boolean, - owner?: $FlowFixMe, - userRef?: $FlowFixMe, - fragment?: $FlowFixMe, - callDuringRenderKey?: ?number, - ... - }): $FlowFixMe => { - const {isConcurrent = false, ...props} = args ?? {}; - let renderer; - TestRenderer.act(() => { - renderer = TestRenderer.create( - `Error: ${error.message}`}> - }> - - - - - , - // $FlowFixMe[prop-missing] - error revealed when flow-typing ReactTestRenderer - {unstable_isConcurrent: isConcurrent}, - ); - jest.runAllImmediates(); - }); - return renderer; + // Assert refetch query was fetched + expectRequestIsInFlight( + {...expected, inFlight: true, requestCount: 1}, + env, + ); + + // Assert component suspended + expect(commitSpy).toBeCalledTimes(0); + expect(renderer.toJSON()).toEqual('Fallback'); + + // Assert query is retained by loadQuery and + // temporarily retained while component is suspended + // $FlowFixMe[method-unbinding] added when improving typing for this parameters + expect(env.retain).toBeCalledTimes(2); + // $FlowFixMe[method-unbinding] added when improving typing for this parameters + expect(env.retain.mock.calls[0][0]).toEqual( + expected.refetchQuery ?? refetchQuery, + ); + } + + it('does not refetch and warns if component has unmounted', () => { + const warning = require('warning'); + // $FlowFixMe[prop-missing] + warning.mockClear(); + const renderer = renderFragment(); + const initialUser = { + id: '1', + name: 'Alice', + profile_picture: null, + ...createFragmentRef('1', query), }; - }); + expectFragmentResults([{data: initialUser}]); - afterEach(() => { - environment.mockClear(); - commitSpy.mockClear(); + TestRenderer.act(() => { + renderer.unmount(); + }); + TestRenderer.act(() => { + refetch({id: '4'}); + }); + + expect(warning).toHaveBeenCalledTimes(1); + expect( + // $FlowFixMe[prop-missing] + warning.mock.calls[0][1].includes( + 'Relay: Unexpected call to `refetch` on unmounted component', + ), + ).toEqual(true); + // $FlowFixMe[method-unbinding] added when improving typing for this parameters + expect(environment.executeWithSource).toHaveBeenCalledTimes(0); }); - describe('initial render', () => { - // The bulk of initial render behavior is covered in useFragmentNode-test, - // so this suite covers the basic cases as a sanity check. - it('should throw error if fragment is plural', () => { - jest.spyOn(console, 'error').mockImplementationOnce(() => {}); + it('warns if fragment ref passed to useRefetchableFragmentNode() was null', () => { + const warning = require('warning'); + // $FlowFixMe[prop-missing] + warning.mockClear(); - const UserFragment = graphql` - fragment useRefetchableFragmentNodeTest4Fragment on User - @relay(plural: true) { - id - } - `; - const renderer = renderFragment({fragment: UserFragment}); - expect( - renderer - .toJSON() - .includes('Remove `@relay(plural: true)` from fragment'), - ).toEqual(true); + renderFragment({userRef: null}); + expectFragmentResults([{data: null}]); + + TestRenderer.act(() => { + refetch({id: '4'}); }); - it('should throw error if fragment is missing @refetchable directive', () => { - jest.spyOn(console, 'error').mockImplementationOnce(() => {}); + expect(warning).toHaveBeenCalledTimes(1); + expect( + // $FlowFixMe[prop-missing] + warning.mock.calls[0][1].includes( + 'Relay: Unexpected call to `refetch` while using a null fragment ref', + ), + ).toEqual(true); + // $FlowFixMe[method-unbinding] added when improving typing for this parameters + expect(environment.executeWithSource).toHaveBeenCalledTimes(1); + }); + + it('throws error when error occurs during refetch', () => { + jest.spyOn(console, 'error').mockImplementationOnce(() => {}); - const UserFragment = graphql` - fragment useRefetchableFragmentNodeTest5Fragment on User { - id - } - `; - const renderer = renderFragment({fragment: UserFragment}); - expect( - renderer - .toJSON() - .includes( - 'Did you forget to add a @refetchable directive to the fragment?', - ), - ).toEqual(true); + const callback = jest.fn<[Error | null], void>(); + const renderer = renderFragment(); + const initialUser = { + id: '1', + name: 'Alice', + profile_picture: null, + ...createFragmentRef('1', query), + }; + expectFragmentResults([{data: initialUser}]); + + TestRenderer.act(() => { + refetch({id: '4'}, {onComplete: callback}); }); - it('should render fragment without error when data is available', () => { - renderFragment(); - expectFragmentResults([ - { - data: { - id: '1', - name: 'Alice', - profile_picture: null, - ...createFragmentRef('1', query), - }, - }, - ]); + // Assert that fragment is refetching with the right variables and + // suspends upon refetch + const refetchVariables = { + id: '4', + scale: 16, + }; + refetchQuery = createOperationDescriptor( + gqlRefetchQuery, + refetchVariables, + {force: true}, + ); + expectFragmentIsRefetching(renderer, { + refetchVariables, + refetchQuery, }); - it('should render fragment without error when ref is null', () => { - renderFragment({userRef: null}); - expectFragmentResults([{data: null}]); + // Mock network error + TestRenderer.act(() => { + environment.mock.reject(gqlRefetchQuery, new Error('Oops')); }); + TestRenderer.act(() => { + jest.runAllImmediates(); + }); + + // Assert error is caught in Error boundary + expect(renderer.toJSON()).toEqual('Error: Oops'); + expect(callback).toBeCalledTimes(1); + expect(callback.mock.calls[0][0]).toMatchObject({message: 'Oops'}); - it('should render fragment without error when ref is undefined', () => { - renderFragment({userRef: undefined}); - expectFragmentResults([{data: null}]); + // Assert refetch query wasn't retained + TestRenderer.act(() => { + jest.runAllTimers(); }); + expect(isOperationRetained(refetchQuery)).toBe(false); + }); - it('should update when fragment data changes', () => { - renderFragment(); - expectFragmentResults([ - { - data: { - id: '1', - name: 'Alice', - profile_picture: null, - ...createFragmentRef('1', query), - }, - }, - ]); + it('refetches new variables correctly when refetching new id', () => { + const renderer = renderFragment(); + const initialUser = { + id: '1', + name: 'Alice', + profile_picture: null, + ...createFragmentRef('1', query), + }; + expectFragmentResults([{data: initialUser}]); - TestRenderer.act(() => { - environment.commitPayload(query, { - node: { - __typename: 'User', - id: '1', - // Update name - name: 'Alice in Wonderland', - }, - }); - }); - expectFragmentResults([ - { - data: { - id: '1', - // Assert that name is updated - name: 'Alice in Wonderland', - profile_picture: null, - ...createFragmentRef('1', query), - }, - }, - ]); + TestRenderer.act(() => { + refetch({id: '4'}); }); - it('should throw a promise if data is missing for fragment and request is in flight', () => { - // This prevents console.error output in the test, which is expected - jest.spyOn(console, 'error').mockImplementationOnce(() => {}); + // Assert that fragment is refetching with the right variables and + // suspends upon refetch + const refetchVariables = { + id: '4', + scale: 16, + }; + refetchQuery = createOperationDescriptor( + gqlRefetchQuery, + refetchVariables, + {force: true}, + ); + expectFragmentIsRefetching(renderer, { + refetchVariables, + refetchQuery, + }); - const missingDataVariables = {...variables, id: '4'}; - const missingDataQuery = createOperationDescriptor( - gqlQuery, - missingDataVariables, - ); - // Commit a payload with name and profile_picture are missing - TestRenderer.act(() => { - environment.commitPayload(missingDataQuery, { + // Mock network response + TestRenderer.act(() => { + environment.mock.resolve(gqlRefetchQuery, { + data: { node: { __typename: 'User', id: '4', + name: 'Mark', + profile_picture: { + uri: 'scale16', + }, + username: 'usermark', }, - }); + }, }); + }); - fetchQuery(environment, missingDataQuery).subscribe({}); + // Assert fragment is rendered with new data + const refetchedUser = { + id: '4', + name: 'Mark', + profile_picture: { + uri: 'scale16', + }, + ...createFragmentRef('4', refetchQuery), + }; + expectFragmentResults([{data: refetchedUser}]); - const renderer = renderFragment({owner: missingDataQuery}); - expect(renderer.toJSON()).toEqual('Fallback'); - }); + // Assert refetch query was retained + expect(isOperationRetained(refetchQuery)).toBe(true); }); - describe('refetch', () => { - let release; - let isOperationRetained; + it('refetches new variables correctly when refetching same id', () => { + const renderer = renderFragment(); + const initialUser = { + id: '1', + name: 'Alice', + profile_picture: null, + ...createFragmentRef('1', query), + }; + expectFragmentResults([{data: initialUser}]); - beforeEach(() => { - ({release_DEPRECATED: release, isOperationRetained} = - trackRetentionForEnvironment(environment)); + TestRenderer.act(() => { + refetch({scale: 32}); }); - function expectRequestIsInFlight( - expected: any, - requestEnvironment: RelayMockEnvironment = environment, - ) { - // $FlowFixMe[method-unbinding] added when improving typing for this parameters - expect(requestEnvironment.executeWithSource).toBeCalledTimes( - expected.requestCount, - ); - expect( - requestEnvironment.mock.isLoading( - expected.gqlRefetchQuery ?? gqlRefetchQuery, - expected.refetchVariables, - {force: true}, - ), - ).toEqual(expected.inFlight); - } + // Assert that fragment is refetching with the right variables and + // suspends upon refetch + const refetchVariables = { + id: '1', + scale: 32, + }; + refetchQuery = createOperationDescriptor( + gqlRefetchQuery, + refetchVariables, + {force: true}, + ); + expectFragmentIsRefetching(renderer, { + refetchVariables, + refetchQuery, + }); - function expectFragmentIsRefetching( - renderer: any, - expected: { - refetchVariables: Variables, - refetchQuery?: OperationDescriptor, - gqlRefetchQuery?: $FlowFixMe, - }, - env: RelayMockEnvironment = environment, - ) { - expect(commitSpy).toBeCalledTimes(0); - commitSpy.mockClear(); + // Mock network response + TestRenderer.act(() => { + environment.mock.resolve(gqlRefetchQuery, { + data: { + node: { + __typename: 'User', + id: '1', + name: 'Alice', + profile_picture: { + uri: 'scale32', + }, + username: 'useralice', + }, + }, + }); + }); - // Assert refetch query was fetched - expectRequestIsInFlight( - {...expected, inFlight: true, requestCount: 1}, - env, - ); + // Assert fragment is rendered with new data + const refetchedUser = { + id: '1', + name: 'Alice', + profile_picture: { + uri: 'scale32', + }, + ...createFragmentRef('1', refetchQuery), + }; + expectFragmentResults([{data: refetchedUser}]); + expect(isOperationRetained(refetchQuery)).toBe(true); + }); - // Assert component suspended - expect(commitSpy).toBeCalledTimes(0); - expect(renderer.toJSON()).toEqual('Fallback'); + it('with correct id from refetchable fragment when using nested fragment', () => { + // Populate store with data for query using nested fragment + TestRenderer.act(() => { + environment.commitPayload(queryNestedFragment, { + node: { + __typename: 'Feedback', + id: '', + actor: { + __typename: 'User', + id: '1', + name: 'Alice', + username: 'useralice', + profile_picture: null, + }, + }, + }); + }); - // Assert query is retained by loadQuery and - // temporarily retained while component is suspended - // $FlowFixMe[method-unbinding] added when improving typing for this parameters - expect(env.retain).toBeCalledTimes(2); - // $FlowFixMe[method-unbinding] added when improving typing for this parameters - expect(env.retain.mock.calls[0][0]).toEqual( - expected.refetchQuery ?? refetchQuery, - ); - } + // Get fragment ref for user using nested fragment + const userRef = (environment.lookup(queryNestedFragment.fragment) + .data: $FlowFixMe)?.node?.actor; - it('does not refetch and warns if component has unmounted', () => { - const warning = require('warning'); - // $FlowFixMe[prop-missing] - warning.mockClear(); - const renderer = renderFragment(); - const initialUser = { - id: '1', - name: 'Alice', - profile_picture: null, - ...createFragmentRef('1', query), - }; - expectFragmentResults([{data: initialUser}]); + const renderer = renderFragment({owner: queryNestedFragment, userRef}); + const initialUser = { + id: '1', + name: 'Alice', + profile_picture: null, + ...createFragmentRef('1', queryNestedFragment), + }; + expectFragmentResults([{data: initialUser}]); - TestRenderer.act(() => { - renderer.unmount(); - }); - TestRenderer.act(() => { - refetch({id: '4'}); + TestRenderer.act(() => { + refetch({scale: 32}); + }); + + // Assert that fragment is refetching with the right variables and + // suspends upon refetch + const refetchVariables = { + // The id here should correspond to the user id, and not the + // feedback id from the query variables (i.e. ``) + id: '1', + scale: 32, + }; + refetchQuery = createOperationDescriptor( + gqlRefetchQuery, + refetchVariables, + {force: true}, + ); + expectFragmentIsRefetching(renderer, { + refetchVariables, + refetchQuery, + }); + + // Mock network response + TestRenderer.act(() => { + environment.mock.resolve(gqlRefetchQuery, { + data: { + node: { + __typename: 'User', + id: '1', + name: 'Alice', + profile_picture: { + uri: 'scale32', + }, + username: 'useralice', + }, + }, }); + }); - expect(warning).toHaveBeenCalledTimes(1); - expect( - // $FlowFixMe[prop-missing] - warning.mock.calls[0][1].includes( - 'Relay: Unexpected call to `refetch` on unmounted component', - ), - ).toEqual(true); - // $FlowFixMe[method-unbinding] added when improving typing for this parameters - expect(environment.executeWithSource).toHaveBeenCalledTimes(0); + // Assert fragment is rendered with new data + const refetchedUser = { + id: '1', + name: 'Alice', + profile_picture: { + uri: 'scale32', + }, + ...createFragmentRef('1', refetchQuery), + }; + expectFragmentResults([{data: refetchedUser}]); + expect(isOperationRetained(refetchQuery)).toBe(true); + }); + + it('refetches new variables correctly when using @arguments', () => { + const userRef = environment.lookup(queryWithArgs.fragment).data?.node; + const renderer = renderFragment({ + fragment: gqlFragmentWithArgs, + userRef, }); + const initialUser = { + id: '1', + name: 'Alice', + profile_picture: null, + ...createFragmentRef('1', queryWithArgs), + }; + expectFragmentResults([{data: initialUser}]); - it('warns if fragment ref passed to useRefetchableFragmentNode() was null', () => { - const warning = require('warning'); - // $FlowFixMe[prop-missing] - warning.mockClear(); + TestRenderer.act(() => { + refetch({scaleLocal: 32}); + }); - renderFragment({userRef: null}); - expectFragmentResults([{data: null}]); + // Assert that fragment is refetching with the right variables and + // suspends upon refetch + const refetchVariables = { + id: '1', + scaleLocal: 32, + }; + refetchQueryWithArgs = createOperationDescriptor( + gqlRefetchQueryWithArgs, + refetchVariables, + {force: true}, + ); + expectFragmentIsRefetching(renderer, { + refetchVariables, + refetchQuery: refetchQueryWithArgs, + gqlRefetchQuery: gqlRefetchQueryWithArgs, + }); - TestRenderer.act(() => { - refetch({id: '4'}); + // Mock network response + TestRenderer.act(() => { + environment.mock.resolve(gqlRefetchQueryWithArgs, { + data: { + node: { + __typename: 'User', + id: '1', + name: 'Alice', + profile_picture: { + uri: 'scale32', + }, + username: 'useralice', + }, + }, }); - - expect(warning).toHaveBeenCalledTimes(1); - expect( - // $FlowFixMe[prop-missing] - warning.mock.calls[0][1].includes( - 'Relay: Unexpected call to `refetch` while using a null fragment ref', - ), - ).toEqual(true); - // $FlowFixMe[method-unbinding] added when improving typing for this parameters - expect(environment.executeWithSource).toHaveBeenCalledTimes(1); }); - it('throws error when error occurs during refetch', () => { - jest.spyOn(console, 'error').mockImplementationOnce(() => {}); + // Assert fragment is rendered with new data + const refetchedUser = { + id: '1', + name: 'Alice', + profile_picture: { + uri: 'scale32', + }, + ...createFragmentRef('1', refetchQueryWithArgs), + }; + expectFragmentResults([{data: refetchedUser}]); + expect(isOperationRetained(refetchQueryWithArgs)).toBe(true); + }); - const callback = jest.fn<[Error | null], void>(); - const renderer = renderFragment(); - const initialUser = { - id: '1', - name: 'Alice', - profile_picture: null, - ...createFragmentRef('1', query), - }; - expectFragmentResults([{data: initialUser}]); + it('refetches new variables correctly when using @arguments with literal values', () => { + const userRef = environment.lookup(queryWithLiteralArgs.fragment).data + ?.node; + const renderer = renderFragment({ + fragment: gqlFragmentWithArgs, + userRef, + }); + const initialUser = { + id: '1', + name: 'Alice', + profile_picture: null, + ...createFragmentRef('1', queryWithLiteralArgs), + }; + expectFragmentResults([{data: initialUser}]); - TestRenderer.act(() => { - refetch({id: '4'}, {onComplete: callback}); - }); + TestRenderer.act(() => { + refetch({id: '4'}); + }); - // Assert that fragment is refetching with the right variables and - // suspends upon refetch - const refetchVariables = { - id: '4', - scale: 16, - }; - refetchQuery = createOperationDescriptor( - gqlRefetchQuery, - refetchVariables, - {force: true}, - ); - expectFragmentIsRefetching(renderer, { - refetchVariables, - refetchQuery, - }); + // Assert that fragment is refetching with the right variables and + // suspends upon refetch + const refetchVariables = { + id: '4', + scaleLocal: 16, + }; + refetchQueryWithArgs = createOperationDescriptor( + gqlRefetchQueryWithArgs, + refetchVariables, + {force: true}, + ); + expectFragmentIsRefetching(renderer, { + refetchVariables, + refetchQuery: refetchQueryWithArgs, + gqlRefetchQuery: gqlRefetchQueryWithArgs, + }); - // Mock network error - TestRenderer.act(() => { - environment.mock.reject(gqlRefetchQuery, new Error('Oops')); - }); - TestRenderer.act(() => { - jest.runAllImmediates(); + // Mock network response + TestRenderer.act(() => { + environment.mock.resolve(gqlRefetchQueryWithArgs, { + data: { + node: { + __typename: 'User', + id: '4', + name: 'Mark', + profile_picture: { + uri: 'scale16', + }, + username: 'usermark', + }, + }, }); + }); + + // Assert fragment is rendered with new data + const refetchedUser = { + id: '4', + name: 'Mark', + profile_picture: { + uri: 'scale16', + }, + ...createFragmentRef('4', refetchQueryWithArgs), + }; + expectFragmentResults([{data: refetchedUser}]); + expect(isOperationRetained(refetchQueryWithArgs)).toBe(true); + }); - // Assert error is caught in Error boundary - expect(renderer.toJSON()).toEqual('Error: Oops'); - expect(callback).toBeCalledTimes(1); - expect(callback.mock.calls[0][0]).toMatchObject({message: 'Oops'}); + it('subscribes to changes in refetched data', () => { + renderFragment(); + commitSpy.mockClear(); + TestRenderer.act(() => { + refetch({id: '4'}); + }); - // Assert refetch query wasn't retained - TestRenderer.act(() => { - jest.runAllTimers(); + // Mock network response + TestRenderer.act(() => { + environment.mock.resolve(gqlRefetchQuery, { + data: { + node: { + __typename: 'User', + id: '4', + name: 'Mark', + profile_picture: { + uri: 'scale16', + }, + username: 'usermark', + }, + }, }); - expect(isOperationRetained(refetchQuery)).toBe(false); }); - it('refetches new variables correctly when refetching new id', () => { - const renderer = renderFragment(); - const initialUser = { - id: '1', - name: 'Alice', - profile_picture: null, - ...createFragmentRef('1', query), - }; - expectFragmentResults([{data: initialUser}]); + // Assert fragment is rendered with new data + const refetchVariables = { + id: '4', + scale: 16, + }; + refetchQuery = createOperationDescriptor( + gqlRefetchQuery, + refetchVariables, + {force: true}, + ); + const refetchedUser = { + id: '4', + name: 'Mark', + profile_picture: { + uri: 'scale16', + }, + ...createFragmentRef('4', refetchQuery), + }; + expectFragmentResults([{data: refetchedUser}]); + expect(isOperationRetained(refetchQuery)).toBe(true); - TestRenderer.act(() => { - refetch({id: '4'}); + // Update refetched data + TestRenderer.act(() => { + environment.commitPayload(refetchQuery, { + node: { + __typename: 'User', + id: '4', + name: 'Mark Updated', + }, }); + }); - // Assert that fragment is refetching with the right variables and - // suspends upon refetch - const refetchVariables = { - id: '4', - scale: 16, - }; - refetchQuery = createOperationDescriptor( - gqlRefetchQuery, - refetchVariables, - {force: true}, - ); - expectFragmentIsRefetching(renderer, { - refetchVariables, - refetchQuery, - }); + // Assert that refetched data is updated + expectFragmentResults([ + { + data: { + id: '4', + // Name is updated + name: 'Mark Updated', + profile_picture: { + uri: 'scale16', + }, + ...createFragmentRef('4', refetchQuery), + }, + }, + ]); + }); - // Mock network response - TestRenderer.act(() => { - environment.mock.resolve(gqlRefetchQuery, { - data: { - node: { - __typename: 'User', - id: '4', - name: 'Mark', - profile_picture: { - uri: 'scale16', - }, - username: 'usermark', + it('resets to parent data when environment changes', () => { + renderFragment(); + commitSpy.mockClear(); + TestRenderer.act(() => { + refetch({id: '4'}); + }); + + // Mock network response + TestRenderer.act(() => { + environment.mock.resolve(gqlRefetchQuery, { + data: { + node: { + __typename: 'User', + id: '4', + name: 'Mark', + profile_picture: { + uri: 'scale16', }, + username: 'usermark', }, - }); - }); - - // Assert fragment is rendered with new data - const refetchedUser = { - id: '4', - name: 'Mark', - profile_picture: { - uri: 'scale16', }, - ...createFragmentRef('4', refetchQuery), - }; - expectFragmentResults([{data: refetchedUser}]); - - // Assert refetch query was retained - expect(isOperationRetained(refetchQuery)).toBe(true); + }); }); - it('refetches new variables correctly when refetching same id', () => { - const renderer = renderFragment(); - const initialUser = { + // Assert fragment is rendered with new data + const refetchVariables = { + id: '4', + scale: 16, + }; + refetchQuery = createOperationDescriptor( + gqlRefetchQuery, + refetchVariables, + {force: true}, + ); + const refetchedUser = { + id: '4', + name: 'Mark', + profile_picture: { + uri: 'scale16', + }, + ...createFragmentRef('4', refetchQuery), + }; + expectFragmentResults([{data: refetchedUser}]); + expect(isOperationRetained(refetchQuery)).toBe(true); + + // Set new environment + const newEnvironment = createMockEnvironment(); + newEnvironment.commitPayload(query, { + node: { + __typename: 'User', id: '1', - name: 'Alice', + name: 'Alice in a different env', + username: 'useralice', profile_picture: null, - ...createFragmentRef('1', query), - }; - expectFragmentResults([{data: initialUser}]); + }, + }); + TestRenderer.act(() => { + setEnvironment(newEnvironment); + }); - TestRenderer.act(() => { - refetch({scale: 32}); + // Assert that parent data is rendered + const expectedUser = { + id: '1', + name: 'Alice in a different env', + profile_picture: null, + ...createFragmentRef('1', query), + }; + expectFragmentResults([{data: expectedUser}]); + + // FIXME I think this should be false and the test was missing a bug here. + expect(isOperationRetained(refetchQuery)).toBe(true); + + // Update data in new environment + TestRenderer.act(() => { + newEnvironment.commitPayload(query, { + node: { + __typename: 'User', + id: '1', + name: 'Alice Updated', + }, }); + }); - // Assert that fragment is refetching with the right variables and - // suspends upon refetch - const refetchVariables = { + // Assert that data in new environment is updated + expectFragmentResults([ + { + data: { + id: '1', + name: 'Alice Updated', + profile_picture: null, + ...createFragmentRef('1', query), + }, + }, + ]); + }); + + it('refetches with new environment when environment changes', () => { + const renderer = renderFragment(); + const initialUser = { + id: '1', + name: 'Alice', + profile_picture: null, + ...createFragmentRef('1', query), + }; + expectFragmentResults([{data: initialUser}]); + + // Set new environment + const newEnvironment = createMockEnvironment(); + newEnvironment.commitPayload(query, { + node: { + __typename: 'User', id: '1', - scale: 32, - }; - refetchQuery = createOperationDescriptor( - gqlRefetchQuery, - refetchVariables, - {force: true}, - ); - expectFragmentIsRefetching(renderer, { - refetchVariables, + name: 'Alice in a different env', + username: 'useralice', + profile_picture: null, + }, + }); + TestRenderer.act(() => { + setEnvironment(newEnvironment); + }); + + TestRenderer.act(() => { + refetch({}, {fetchPolicy: 'network-only'}); + }); + commitSpy.mockClear(); + + // Assert fragment is refetched with new environment + expectFragmentIsRefetching( + renderer, + { + refetchVariables: variables, refetchQuery, + }, + newEnvironment, + ); + + // Mock network response + TestRenderer.act(() => { + newEnvironment.mock.resolve(gqlRefetchQuery, { + data: { + node: { + __typename: 'User', + id: '1', + name: 'Alice in a different env refetched', + profile_picture: null, + username: 'useralice', + }, + }, }); + }); - // Mock network response - TestRenderer.act(() => { - environment.mock.resolve(gqlRefetchQuery, { - data: { - node: { - __typename: 'User', - id: '1', - name: 'Alice', - profile_picture: { - uri: 'scale32', - }, - username: 'useralice', + // Assert fragment is rendered with new data + const refetchedUser = { + id: '1', + name: 'Alice in a different env refetched', + profile_picture: null, + ...createFragmentRef('1', refetchQuery), + }; + expectFragmentResults([{data: refetchedUser}]); + }); + + it('resets to parent data when parent fragment ref changes', () => { + renderFragment(); + commitSpy.mockClear(); + TestRenderer.act(() => { + refetch({id: '4'}); + }); + + // Mock network response + TestRenderer.act(() => { + environment.mock.resolve(gqlRefetchQuery, { + data: { + node: { + __typename: 'User', + id: '4', + name: 'Mark', + profile_picture: { + uri: 'scale16', }, + username: 'usermark', }, - }); + }, }); + }); - // Assert fragment is rendered with new data - const refetchedUser = { + // Assert fragment is rendered with new data + const refetchVariables = { + id: '4', + scale: 16, + }; + refetchQuery = createOperationDescriptor( + gqlRefetchQuery, + refetchVariables, + {force: true}, + ); + const refetchedUser = { + id: '4', + name: 'Mark', + profile_picture: { + uri: 'scale16', + }, + ...createFragmentRef('4', refetchQuery), + }; + expectFragmentResults([{data: refetchedUser}]); + + expect(isOperationRetained(refetchQuery)).toBe(true); + + // Pass new parent fragment ref with different variables + const newVariables = {...variables, scale: 32}; + const newQuery = createOperationDescriptor(gqlQuery, newVariables); + environment.commitPayload(newQuery, { + node: { + __typename: 'User', id: '1', name: 'Alice', + username: 'useralice', profile_picture: { - uri: 'scale32', + uri: 'uri32', }, - ...createFragmentRef('1', refetchQuery), - }; - expectFragmentResults([{data: refetchedUser}]); - expect(isOperationRetained(refetchQuery)).toBe(true); + }, + }); + TestRenderer.act(() => { + setOwner(newQuery); }); - it('with correct id from refetchable fragment when using nested fragment', () => { - // Populate store with data for query using nested fragment - TestRenderer.act(() => { - environment.commitPayload(queryNestedFragment, { + // Assert that parent data is rendered + const expectedUser = { + id: '1', + name: 'Alice', + profile_picture: { + uri: 'uri32', + }, + ...createFragmentRef('1', newQuery), + }; + expectFragmentResults([{data: expectedUser}]); + + // FIXME I think this should be false and the test was not revealing a bug here + expect(isOperationRetained(refetchQuery)).toBe(true); + + // Update new parent data + TestRenderer.act(() => { + environment.commitPayload(newQuery, { + node: { + __typename: 'User', + id: '1', + name: 'Alice Updated', + }, + }); + }); + + // Assert that new data from parent is updated + expectFragmentResults([ + { + data: { + id: '1', + name: 'Alice Updated', + profile_picture: { + uri: 'uri32', + }, + ...createFragmentRef('1', newQuery), + }, + }, + ]); + }); + + it('warns if data returned has different __typename', () => { + const warning = require('warning'); + // $FlowFixMe[prop-missing] + warning.mockClear(); + + const renderer = renderFragment(); + + const initialUser = { + id: '1', + name: 'Alice', + profile_picture: null, + ...createFragmentRef('1', query), + }; + expectFragmentResults([{data: initialUser}]); + + const refetchVariables = { + id: '1', + scale: 32, + }; + refetchQuery = createOperationDescriptor( + gqlRefetchQuery, + refetchVariables, + {force: true}, + ); + + commitSpy.mockClear(); + // $FlowFixMe[method-unbinding] added when improving typing for this parameters + environment.executeWithSource.mockClear(); + // $FlowFixMe[method-unbinding] added when improving typing for this parameters + environment.retain.mockClear(); + release.mockClear(); + + TestRenderer.act(() => { + refetch({scale: 32}, {fetchPolicy: 'network-only'}); + }); + + expectFragmentIsRefetching(renderer, { + refetchVariables, + refetchQuery, + }); + + // Mock network response + TestRenderer.act(() => { + environment.mock.resolve(gqlRefetchQuery, { + data: { node: { - __typename: 'Feedback', - id: '', - actor: { - __typename: 'User', - id: '1', - name: 'Alice', - username: 'useralice', - profile_picture: null, + __typename: 'MessagingParticipant', + id: '1', + name: 'Alice', + profile_picture: { + uri: 'scale32', }, + username: 'useralice', }, - }); + }, + }); + }); + + TestRenderer.act(() => { + jest.runAllImmediates(); + }); + + // $FlowFixMe[prop-missing] + const warningCalls = warning.mock.calls.filter(call => call[0] === false); + expect( + warningCalls.some(([_condition, format, ..._args]) => + format.includes( + 'Relay: Call to `refetch` returned data with a different __typename:', + ), + ), + ).toBe(true); + }); + + it('does not error if returned node is null', () => { + const warning = require('warning'); + // $FlowFixMe[prop-missing] + warning.mockClear(); + + const renderer = renderFragment(); + + const initialUser = { + id: '1', + name: 'Alice', + profile_picture: null, + ...createFragmentRef('1', query), + }; + expectFragmentResults([{data: initialUser}]); + + const refetchVariables = { + id: '1', + scale: 32, + }; + refetchQuery = createOperationDescriptor( + gqlRefetchQuery, + refetchVariables, + {force: true}, + ); + + commitSpy.mockClear(); + // $FlowFixMe[method-unbinding] added when improving typing for this parameters + environment.executeWithSource.mockClear(); + // $FlowFixMe[method-unbinding] added when improving typing for this parameters + environment.retain.mockClear(); + release.mockClear(); + + TestRenderer.act(() => { + refetch({scale: 32}, {fetchPolicy: 'network-only'}); + }); + + expectFragmentIsRefetching(renderer, { + refetchVariables, + refetchQuery, + }); + + // Mock network response + TestRenderer.act(() => { + environment.mock.resolve(gqlRefetchQuery, { + data: { + node: null, // Demonstrate data that is nolonger avaliable on the server + }, + }); + }); + + TestRenderer.act(() => { + jest.runAllImmediates(); + }); + + expect(renderer.toJSON()).toEqual(null); + }); + + it('warns if a different id is returned', () => { + const warning = require('warning'); + // $FlowFixMe[prop-missing] + warning.mockClear(); + const renderer = renderFragment(); + + const initialUser = { + id: '1', + name: 'Alice', + profile_picture: null, + ...createFragmentRef('1', query), + }; + expectFragmentResults([{data: initialUser}]); + + const refetchVariables = { + id: '1', + scale: 32, + }; + refetchQuery = createOperationDescriptor( + gqlRefetchQuery, + refetchVariables, + {force: true}, + ); + + commitSpy.mockClear(); + // $FlowFixMe[method-unbinding] added when improving typing for this parameters + environment.executeWithSource.mockClear(); + // $FlowFixMe[method-unbinding] added when improving typing for this parameters + environment.retain.mockClear(); + release.mockClear(); + + TestRenderer.act(() => { + refetch({scale: 32}, {fetchPolicy: 'network-only'}); + }); + + expectFragmentIsRefetching(renderer, { + refetchVariables, + refetchQuery, + }); + + TestRenderer.act(() => { + environment.mock.resolve(gqlRefetchQuery, { + data: { + node: { + __typename: 'User', + id: '2', + name: 'Mark', + profile_picture: { + uri: 'scale32', + }, + username: 'usermark', + }, + }, + }); + }); + + TestRenderer.act(() => { + jest.runAllImmediates(); + }); + + // $FlowFixMe[prop-missing] + const warningCalls = warning.mock.calls.filter(call => call[0] === false); + expect(warningCalls.length).toEqual(2); + expect( + warningCalls[0][1].includes( + 'Relay: Call to `refetch` returned a different id, expected', + ), + ).toEqual(true); + }); + + it("doesn't warn if refetching on a different id than the current one in display", () => { + const warning = require('warning'); + // $FlowFixMe[prop-missing] + warning.mockClear(); + + renderFragment(); + + const initialUser = { + id: '1', + name: 'Alice', + profile_picture: null, + ...createFragmentRef('1', query), + }; + expectFragmentResults([{data: initialUser}]); + + const refetchVariables = { + id: '1', + scale: 32, + }; + refetchQuery = createOperationDescriptor( + gqlRefetchQuery, + refetchVariables, + {force: true}, + ); + + commitSpy.mockClear(); + // $FlowFixMe[method-unbinding] added when improving typing for this parameters + environment.executeWithSource.mockClear(); + // $FlowFixMe[method-unbinding] added when improving typing for this parameters + environment.retain.mockClear(); + release.mockClear(); + + TestRenderer.act(() => { + refetch({id: '2', scale: 32}, {fetchPolicy: 'network-only'}); + jest.runAllImmediates(); + }); + + TestRenderer.act(() => { + refetch({id: '3', scale: 32}, {fetchPolicy: 'network-only'}); + }); + + TestRenderer.act(() => { + environment.mock.resolve(gqlRefetchQuery, { + data: { + node: { + __typename: 'User', + id: '3', + name: 'Mark', + profile_picture: { + uri: 'scale32', + }, + username: 'usermark', + }, + }, + }); + }); + + TestRenderer.act(() => { + jest.runAllTimers(); + }); + + expect( + // $FlowFixMe[prop-missing] + warning.mock.calls.filter(call => call[0] === false).length, + ).toEqual(0); + }); + + describe('multiple refetches', () => { + const internalRuntime = require('relay-runtime').__internal; + const originalFetchQueryDeduped = internalRuntime.fetchQueryDeduped; + const fetchSpy = jest.fn, mixed>(); + jest + .spyOn(internalRuntime, 'fetchQueryDeduped') + .mockImplementation((...args) => { + const originalObservable = originalFetchQueryDeduped(...args); + return { + ...originalObservable, + subscribe: (...subscribeArgs) => { + fetchSpy(...args); + return originalObservable.subscribe(...subscribeArgs); + }, + }; }); - // Get fragment ref for user using nested fragment - const userRef = (environment.lookup(queryNestedFragment.fragment) - .data: $FlowFixMe)?.node?.actor; + beforeEach(() => { + fetchSpy.mockClear(); + }); - const renderer = renderFragment({owner: queryNestedFragment, userRef}); + it('refetches correctly when refetching multiple times in a row', () => { + const renderer = renderFragment(); const initialUser = { id: '1', name: 'Alice', profile_picture: null, - ...createFragmentRef('1', queryNestedFragment), + ...createFragmentRef('1', query), }; expectFragmentResults([{data: initialUser}]); - TestRenderer.act(() => { - refetch({scale: 32}); - }); - - // Assert that fragment is refetching with the right variables and - // suspends upon refetch const refetchVariables = { - // The id here should correspond to the user id, and not the - // feedback id from the query variables (i.e. ``) id: '1', scale: 32, }; @@ -876,29 +1685,6 @@ describe.each([['New', useRefetchableFragmentInternal]])( refetchVariables, {force: true}, ); - expectFragmentIsRefetching(renderer, { - refetchVariables, - refetchQuery, - }); - - // Mock network response - TestRenderer.act(() => { - environment.mock.resolve(gqlRefetchQuery, { - data: { - node: { - __typename: 'User', - id: '1', - name: 'Alice', - profile_picture: { - uri: 'scale32', - }, - username: 'useralice', - }, - }, - }); - }); - - // Assert fragment is rendered with new data const refetchedUser = { id: '1', name: 'Alice', @@ -907,114 +1693,140 @@ describe.each([['New', useRefetchableFragmentInternal]])( }, ...createFragmentRef('1', refetchQuery), }; - expectFragmentResults([{data: refetchedUser}]); - expect(isOperationRetained(refetchQuery)).toBe(true); - }); - it('refetches new variables correctly when using @arguments', () => { - const userRef = environment.lookup(queryWithArgs.fragment).data?.node; - const renderer = renderFragment({ - fragment: gqlFragmentWithArgs, - userRef, - }); - const initialUser = { - id: '1', - name: 'Alice', - profile_picture: null, - ...createFragmentRef('1', queryWithArgs), + const doAndAssertRefetch = (fragmentResults: Array<{data: any}>) => { + commitSpy.mockClear(); + // $FlowFixMe[method-unbinding] added when improving typing for this parameters + environment.executeWithSource.mockClear(); + // $FlowFixMe[method-unbinding] added when improving typing for this parameters + environment.retain.mockClear(); + + TestRenderer.act(() => { + // We use fetchPolicy network-only to ensure the call to refetch + // always suspends + refetch({scale: 32}, {fetchPolicy: 'network-only'}); + }); + + // Assert that fragment is refetching with the right variables and + // suspends upon refetch + expectFragmentIsRefetching(renderer, { + refetchVariables, + refetchQuery, + }); + + // Mock network response + TestRenderer.act(() => { + environment.mock.resolve(gqlRefetchQuery, { + data: { + node: { + __typename: 'User', + id: '1', + name: 'Alice', + profile_picture: { + uri: 'scale32', + }, + username: 'useralice', + }, + }, + }); + }); + + // Assert fragment is rendered with new data + expectFragmentResults(fragmentResults); + + // Assert refetch query was retained + expect(isOperationRetained(refetchQuery)).toBe(true); }; - expectFragmentResults([{data: initialUser}]); + // Refetch once + doAndAssertRefetch([{data: refetchedUser}]); + + // Refetch twice + doAndAssertRefetch([{data: refetchedUser}]); + }); + + it('refetches correctly when a second refetch starts while the first is one suspended', () => { + const renderer = renderFragment(); + commitSpy.mockClear(); TestRenderer.act(() => { - refetch({scaleLocal: 32}); + refetch( + {id: '1'}, + { + fetchPolicy: 'network-only', + UNSTABLE_renderPolicy: renderPolicy, + }, + ); }); - // Assert that fragment is refetching with the right variables and - // suspends upon refetch - const refetchVariables = { - id: '1', - scaleLocal: 32, - }; - refetchQueryWithArgs = createOperationDescriptor( - gqlRefetchQueryWithArgs, - refetchVariables, + // Assert request is started + const refetchVariables1 = {id: '1', scale: 16}; + const refetchQuery1 = createOperationDescriptor( + gqlRefetchQuery, + refetchVariables1, {force: true}, ); + + // Assert we suspend on intial refetch request expectFragmentIsRefetching(renderer, { - refetchVariables, - refetchQuery: refetchQueryWithArgs, - gqlRefetchQuery: gqlRefetchQueryWithArgs, + refetchQuery: refetchQuery1, + refetchVariables: refetchVariables1, }); - // Mock network response + // Call refetch a second time + // $FlowFixMe[method-unbinding] added when improving typing for this parameters + environment.executeWithSource.mockClear(); + // $FlowFixMe[method-unbinding] added when improving typing for this parameters + environment.retain.mockClear(); + const refetchVariables2 = {id: '4', scale: 16}; + const refetchQuery2 = createOperationDescriptor( + gqlRefetchQuery, + refetchVariables2, + {force: true}, + ); + TestRenderer.act(() => { + refetch( + {id: '4'}, + { + fetchPolicy: 'network-only', + UNSTABLE_renderPolicy: renderPolicy, + }, + ); + }); + + // Assert we suspend on the second refetch request + expectFragmentIsRefetching(renderer, { + refetchQuery: refetchQuery2, + refetchVariables: refetchVariables2, + }); + + // Mock response for initial refetch request TestRenderer.act(() => { - environment.mock.resolve(gqlRefetchQueryWithArgs, { + environment.mock.resolve(refetchQuery1, { data: { node: { __typename: 'User', id: '1', - name: 'Alice', + name: 'User 1', profile_picture: { - uri: 'scale32', + uri: 'scale16', }, - username: 'useralice', + username: 'user1', }, }, }); }); - // Assert fragment is rendered with new data - const refetchedUser = { - id: '1', - name: 'Alice', - profile_picture: { - uri: 'scale32', - }, - ...createFragmentRef('1', refetchQueryWithArgs), - }; - expectFragmentResults([{data: refetchedUser}]); - expect(isOperationRetained(refetchQueryWithArgs)).toBe(true); - }); - - it('refetches new variables correctly when using @arguments with literal values', () => { - const userRef = environment.lookup(queryWithLiteralArgs.fragment).data - ?.node; - const renderer = renderFragment({ - fragment: gqlFragmentWithArgs, - userRef, - }); - const initialUser = { - id: '1', - name: 'Alice', - profile_picture: null, - ...createFragmentRef('1', queryWithLiteralArgs), - }; - expectFragmentResults([{data: initialUser}]); - - TestRenderer.act(() => { - refetch({id: '4'}); - }); - - // Assert that fragment is refetching with the right variables and - // suspends upon refetch - const refetchVariables = { - id: '4', - scaleLocal: 16, - }; - refetchQueryWithArgs = createOperationDescriptor( - gqlRefetchQueryWithArgs, - refetchVariables, - {force: true}, - ); + // Assert that we are still suspended the second refetch request + // since that one hasn't resolved and that's the latest one we want + // to render expectFragmentIsRefetching(renderer, { - refetchVariables, - refetchQuery: refetchQueryWithArgs, - gqlRefetchQuery: gqlRefetchQueryWithArgs, + refetchQuery: refetchQuery2, + refetchVariables: refetchVariables2, }); - // Mock network response + // Mock response for second refetch request TestRenderer.act(() => { - environment.mock.resolve(gqlRefetchQueryWithArgs, { + environment.mock.resolve(refetchQuery2, { data: { node: { __typename: 'User', @@ -1029,101 +1841,119 @@ describe.each([['New', useRefetchableFragmentInternal]])( }); }); - // Assert fragment is rendered with new data + // Assert component is rendered with data from second request const refetchedUser = { id: '4', name: 'Mark', - profile_picture: { - uri: 'scale16', - }, - ...createFragmentRef('4', refetchQueryWithArgs), + profile_picture: {uri: 'scale16'}, + ...createFragmentRef('4', refetchQuery2), }; expectFragmentResults([{data: refetchedUser}]); - expect(isOperationRetained(refetchQueryWithArgs)).toBe(true); + + expect(fetchSpy).toBeCalledTimes(4); }); - it('subscribes to changes in refetched data', () => { - renderFragment(); + it('does not re-issue initial refetch request if second refetch is interrupted by high-pri update', () => { + const renderer = renderFragment(); commitSpy.mockClear(); TestRenderer.act(() => { - refetch({id: '4'}); + refetch( + {id: '1'}, + { + fetchPolicy: 'network-only', + UNSTABLE_renderPolicy: renderPolicy, + }, + ); }); - // Mock network response - TestRenderer.act(() => { - environment.mock.resolve(gqlRefetchQuery, { - data: { - node: { - __typename: 'User', - id: '4', - name: 'Mark', - profile_picture: { - uri: 'scale16', - }, - username: 'usermark', - }, - }, - }); + // Assert request is started + const refetchVariables1 = {id: '1', scale: 16}; + const refetchQuery1 = createOperationDescriptor( + gqlRefetchQuery, + refetchVariables1, + {force: true}, + ); + + // Assert we suspend on intial refetch request + expectFragmentIsRefetching(renderer, { + refetchQuery: refetchQuery1, + refetchVariables: refetchVariables1, }); - // Assert fragment is rendered with new data - const refetchVariables = { - id: '4', - scale: 16, - }; - refetchQuery = createOperationDescriptor( + // Call refetch a second time + // $FlowFixMe[method-unbinding] added when improving typing for this parameters + environment.executeWithSource.mockClear(); + // $FlowFixMe[method-unbinding] added when improving typing for this parameters + environment.retain.mockClear(); + const refetchVariables2 = {id: '4', scale: 16}; + const refetchQuery2 = createOperationDescriptor( gqlRefetchQuery, - refetchVariables, + refetchVariables2, {force: true}, ); - const refetchedUser = { - id: '4', - name: 'Mark', - profile_picture: { - uri: 'scale16', - }, - ...createFragmentRef('4', refetchQuery), - }; - expectFragmentResults([{data: refetchedUser}]); - expect(isOperationRetained(refetchQuery)).toBe(true); + TestRenderer.act(() => { + refetch( + {id: '4'}, + { + fetchPolicy: 'network-only', + UNSTABLE_renderPolicy: renderPolicy, + }, + ); + }); + + // Assert we suspend on the second refetch request + expectFragmentIsRefetching(renderer, { + refetchQuery: refetchQuery2, + refetchVariables: refetchVariables2, + }); - // Update refetched data + // Schedule a high-pri update while the component is + // suspended on pagination TestRenderer.act(() => { - environment.commitPayload(refetchQuery, { - node: { - __typename: 'User', - id: '4', - name: 'Mark Updated', + Scheduler.unstable_runWithPriority( + Scheduler.unstable_UserBlockingPriority, + () => { + forceUpdate(prev => prev + 1); }, - }); + ); }); - // Assert that refetched data is updated - expectFragmentResults([ - { + // Assert that we are still suspended the second refetch request + // since that one hasn't resolved and that's the latest one we want + // to render + expectFragmentIsRefetching(renderer, { + refetchQuery: refetchQuery2, + refetchVariables: refetchVariables2, + }); + + // Mock response for initial refetch request + TestRenderer.act(() => { + environment.mock.resolve(refetchQuery1, { data: { - id: '4', - // Name is updated - name: 'Mark Updated', - profile_picture: { - uri: 'scale16', + node: { + __typename: 'User', + id: '1', + name: 'User 1', + profile_picture: { + uri: 'scale16', + }, + username: 'user1', }, - ...createFragmentRef('4', refetchQuery), }, - }, - ]); - }); + }); + }); - it('resets to parent data when environment changes', () => { - renderFragment(); - commitSpy.mockClear(); - TestRenderer.act(() => { - refetch({id: '4'}); + // Assert that we are still suspended the second refetch request + // since that one hasn't resolved and that's the latest one we want + // to render + expectFragmentIsRefetching(renderer, { + refetchQuery: refetchQuery2, + refetchVariables: refetchVariables2, }); - // Mock network response + // Mock response for second refetch request TestRenderer.act(() => { - environment.mock.resolve(gqlRefetchQuery, { + environment.mock.resolve(refetchQuery2, { data: { node: { __typename: 'User', @@ -1138,525 +1968,348 @@ describe.each([['New', useRefetchableFragmentInternal]])( }); }); - // Assert fragment is rendered with new data - const refetchVariables = { - id: '4', - scale: 16, - }; - refetchQuery = createOperationDescriptor( - gqlRefetchQuery, - refetchVariables, - {force: true}, - ); + // Assert component is rendered with data from second request const refetchedUser = { id: '4', name: 'Mark', - profile_picture: { - uri: 'scale16', - }, - ...createFragmentRef('4', refetchQuery), + profile_picture: {uri: 'scale16'}, + ...createFragmentRef('4', refetchQuery2), }; expectFragmentResults([{data: refetchedUser}]); - expect(isOperationRetained(refetchQuery)).toBe(true); - // Set new environment - const newEnvironment = createMockEnvironment(); - newEnvironment.commitPayload(query, { - node: { - __typename: 'User', - id: '1', - name: 'Alice in a different env', - username: 'useralice', - profile_picture: null, - }, - }); - TestRenderer.act(() => { - setEnvironment(newEnvironment); - }); + expect(fetchSpy).toBeCalledTimes(4); + }); - // Assert that parent data is rendered - const expectedUser = { - id: '1', - name: 'Alice in a different env', - profile_picture: null, - ...createFragmentRef('1', query), - }; - expectFragmentResults([{data: expectedUser}]); - - // FIXME I think this should be false and the test was missing a bug here. - expect(isOperationRetained(refetchQuery)).toBe(true); - - // Update data in new environment + it('refetches correctly when switching between multiple refetches', () => { + const renderer = renderFragment(); + commitSpy.mockClear(); TestRenderer.act(() => { - newEnvironment.commitPayload(query, { - node: { - __typename: 'User', - id: '1', - name: 'Alice Updated', + refetch( + {id: '1'}, + { + fetchPolicy: 'network-only', + UNSTABLE_renderPolicy: renderPolicy, }, - }); + ); }); - // Assert that data in new environment is updated - expectFragmentResults([ - { - data: { - id: '1', - name: 'Alice Updated', - profile_picture: null, - ...createFragmentRef('1', query), - }, - }, - ]); - }); - - it('refetches with new environment when environment changes', () => { - const renderer = renderFragment(); - const initialUser = { - id: '1', - name: 'Alice', - profile_picture: null, - ...createFragmentRef('1', query), - }; - expectFragmentResults([{data: initialUser}]); + // Assert request is started + const refetchVariables1 = {id: '1', scale: 16}; + const refetchQuery1 = createOperationDescriptor( + gqlRefetchQuery, + refetchVariables1, + {force: true}, + ); - // Set new environment - const newEnvironment = createMockEnvironment(); - newEnvironment.commitPayload(query, { - node: { - __typename: 'User', - id: '1', - name: 'Alice in a different env', - username: 'useralice', - profile_picture: null, - }, + // Assert we suspend on initial refetch request + expectFragmentIsRefetching(renderer, { + refetchQuery: refetchQuery1, + refetchVariables: refetchVariables1, }); + + // Call refetch a second time + // $FlowFixMe[method-unbinding] added when improving typing for this parameters + environment.executeWithSource.mockClear(); + // $FlowFixMe[method-unbinding] added when improving typing for this parameters + environment.retain.mockClear(); + const refetchVariables2 = {id: '4', scale: 16}; + const refetchQuery2 = createOperationDescriptor( + gqlRefetchQuery, + refetchVariables2, + {force: true}, + ); TestRenderer.act(() => { - setEnvironment(newEnvironment); + refetch( + {id: '4'}, + { + fetchPolicy: 'network-only', + UNSTABLE_renderPolicy: renderPolicy, + }, + ); }); - TestRenderer.act(() => { - refetch({}, {fetchPolicy: 'network-only'}); + // Assert we suspend on the second refetch request + expectFragmentIsRefetching(renderer, { + refetchQuery: refetchQuery2, + refetchVariables: refetchVariables2, }); - commitSpy.mockClear(); - // Assert fragment is refetched with new environment - expectFragmentIsRefetching( - renderer, - { - refetchVariables: variables, - refetchQuery, - }, - newEnvironment, - ); + // Switch back to initial refetch + // $FlowFixMe[method-unbinding] added when improving typing for this parameters + environment.retain.mockClear(); + TestRenderer.act(() => { + refetch( + {id: '1'}, + { + fetchPolicy: 'network-only', + UNSTABLE_renderPolicy: renderPolicy, + }, + ); + }); - // Mock network response + // Mock response for second refetch request TestRenderer.act(() => { - newEnvironment.mock.resolve(gqlRefetchQuery, { + environment.mock.resolve(refetchQuery2, { data: { node: { __typename: 'User', - id: '1', - name: 'Alice in a different env refetched', - profile_picture: null, - username: 'useralice', + id: '4', + name: 'Mark', + profile_picture: { + uri: 'scale16', + }, + username: 'usermark', }, }, }); }); - // Assert fragment is rendered with new data - const refetchedUser = { - id: '1', - name: 'Alice in a different env refetched', - profile_picture: null, - ...createFragmentRef('1', refetchQuery), - }; - expectFragmentResults([{data: refetchedUser}]); - }); - - it('resets to parent data when parent fragment ref changes', () => { - renderFragment(); - commitSpy.mockClear(); - TestRenderer.act(() => { - refetch({id: '4'}); + // Assert that we are still suspended the initial refetch request + // since that one hasn't resolved and that's the latest one we want + // to render + expectFragmentIsRefetching(renderer, { + refetchQuery: refetchQuery1, + refetchVariables: refetchVariables1, }); - // Mock network response + // Mock response for initial refetch request TestRenderer.act(() => { - environment.mock.resolve(gqlRefetchQuery, { + environment.mock.resolve(refetchQuery1, { data: { node: { __typename: 'User', - id: '4', - name: 'Mark', + id: '1', + name: 'User 1', profile_picture: { uri: 'scale16', }, - username: 'usermark', + username: 'user1', }, }, }); }); - // Assert fragment is rendered with new data - const refetchVariables = { - id: '4', - scale: 16, - }; - refetchQuery = createOperationDescriptor( - gqlRefetchQuery, - refetchVariables, - {force: true}, - ); + // Assert component is rendered with data from second request const refetchedUser = { - id: '4', - name: 'Mark', - profile_picture: { - uri: 'scale16', - }, - ...createFragmentRef('4', refetchQuery), - }; - expectFragmentResults([{data: refetchedUser}]); - - expect(isOperationRetained(refetchQuery)).toBe(true); - - // Pass new parent fragment ref with different variables - const newVariables = {...variables, scale: 32}; - const newQuery = createOperationDescriptor(gqlQuery, newVariables); - environment.commitPayload(newQuery, { - node: { - __typename: 'User', - id: '1', - name: 'Alice', - username: 'useralice', - profile_picture: { - uri: 'uri32', - }, - }, - }); - TestRenderer.act(() => { - setOwner(newQuery); - }); - - // Assert that parent data is rendered - const expectedUser = { id: '1', - name: 'Alice', - profile_picture: { - uri: 'uri32', - }, - ...createFragmentRef('1', newQuery), + name: 'User 1', + profile_picture: {uri: 'scale16'}, + ...createFragmentRef('1', refetchQuery1), }; - expectFragmentResults([{data: expectedUser}]); + expectFragmentResults([{data: refetchedUser}]); - // FIXME I think this should be false and the test was not revealing a bug here - expect(isOperationRetained(refetchQuery)).toBe(true); + expect(fetchSpy).toBeCalledTimes(5); + }); - // Update new parent data + it('does not dispose ongoing request if refetch is called again', () => { + const renderer = renderFragment(); + commitSpy.mockClear(); TestRenderer.act(() => { - environment.commitPayload(newQuery, { - node: { - __typename: 'User', - id: '1', - name: 'Alice Updated', + refetch( + {id: '1'}, + { + fetchPolicy: 'store-and-network', + UNSTABLE_renderPolicy: renderPolicy, }, - }); + ); }); - // Assert that new data from parent is updated - expectFragmentResults([ - { - data: { - id: '1', - name: 'Alice Updated', - profile_picture: { - uri: 'uri32', - }, - ...createFragmentRef('1', newQuery), - }, - }, - ]); - }); - - it('warns if data returned has different __typename', () => { - const warning = require('warning'); - // $FlowFixMe[prop-missing] - warning.mockClear(); - - const renderer = renderFragment(); + // Assert request is started + const refetchVariables1 = {id: '1', scale: 16}; + const refetchQuery1 = createOperationDescriptor( + gqlRefetchQuery, + refetchVariables1, + {force: true}, + ); + expectRequestIsInFlight({ + inFlight: true, + requestCount: 1, + gqlRefetchQuery, + refetchVariables: refetchVariables1, + }); - const initialUser = { + // Component renders immediately even though request is in flight + // since data is cached + const refetchingUser = { id: '1', name: 'Alice', profile_picture: null, - ...createFragmentRef('1', query), - }; - expectFragmentResults([{data: initialUser}]); - - const refetchVariables = { - id: '1', - scale: 32, + ...createFragmentRef('1', refetchQuery1), }; - refetchQuery = createOperationDescriptor( - gqlRefetchQuery, - refetchVariables, - {force: true}, - ); + expectFragmentResults([{data: refetchingUser}]); - commitSpy.mockClear(); + // Call refetch a second time // $FlowFixMe[method-unbinding] added when improving typing for this parameters environment.executeWithSource.mockClear(); - // $FlowFixMe[method-unbinding] added when improving typing for this parameters - environment.retain.mockClear(); - release.mockClear(); - + const refetchVariables2 = {id: '4', scale: 16}; TestRenderer.act(() => { - refetch({scale: 32}, {fetchPolicy: 'network-only'}); + refetch( + {id: '4'}, + {fetchPolicy, UNSTABLE_renderPolicy: renderPolicy}, + ); }); - expectFragmentIsRefetching(renderer, { - refetchVariables, - refetchQuery, + // Assert first request is not cancelled + expectRequestIsInFlight({ + inFlight: true, + requestCount: 1, + gqlRefetchQuery, + refetchVariables: refetchVariables1, }); - // Mock network response - TestRenderer.act(() => { - environment.mock.resolve(gqlRefetchQuery, { - data: { - node: { - __typename: 'MessagingParticipant', - id: '1', - name: 'Alice', - profile_picture: { - uri: 'scale32', - }, - username: 'useralice', - }, - }, - }); + // Assert second request is started + expectRequestIsInFlight({ + inFlight: true, + requestCount: 1, + gqlRefetchQuery, + refetchVariables: refetchVariables2, }); + // Assert component suspended + expect(commitSpy).toBeCalledTimes(0); + expect(renderer.toJSON()).toEqual('Fallback'); + + expect(fetchSpy).toBeCalledTimes(4); + }); + + it('preserves referential equality after refetch if data & variables have not changed', async () => { + let refetchCount = 0; + const ComponentWithUseEffectRefetch = (props: { + fragmentKey: any, + }): null => { + const {fragmentData, refetch} = useRefetchableFragmentNode( + graphql` + fragment useRefetchableFragmentNodeTestIdentityTestFragment on User + @refetchable( + queryName: "useRefetchableFragmentNodeTestIdentityTestFragmentRefetchQuery" + ) { + id + name + profile_picture(scale: $scale) { + uri + } + } + `, + props.fragmentKey, + ); + if (refetchCount > 2) { + throw new Error('Detected refetch loop.'); + } + useEffect(() => { + refetchCount++; + // $FlowFixMe useRefetchableFragmentNode is untyped + refetch({id: fragmentData.id}); + }, [fragmentData, refetch]); + return null; + }; + const variables = {id: '1', scale: 16}; + const query = createOperationDescriptor(gqlRefetchQuery, variables, {}); + environment.commitPayload(query, { + node: { + __typename: 'User', + id: '1', + name: 'Alice', + profile_picture: null, + }, + }); + let renderer; TestRenderer.act(() => { + renderer = TestRenderer.create( + `Error: ${error.message}`}> + + + + + + , + // $FlowFixMe[prop-missing] - error revealed when flow-typing ReactTestRenderer + {unstable_isConcurrent: true}, + ); jest.runAllImmediates(); }); - - // $FlowFixMe[prop-missing] - const warningCalls = warning.mock.calls.filter( - call => call[0] === false, - ); - expect( - warningCalls.some(([_condition, format, ..._args]) => - format.includes( - 'Relay: Call to `refetch` returned data with a different __typename:', - ), - ), - ).toBe(true); + expect(refetchCount).toBe(2); + expect(renderer?.toJSON()).toBe(null); }); + }); - it('warns if a different id is returned', () => { - const warning = require('warning'); - // $FlowFixMe[prop-missing] - warning.mockClear(); - const renderer = renderFragment(); - - const initialUser = { - id: '1', - name: 'Alice', - profile_picture: null, - ...createFragmentRef('1', query), - }; - expectFragmentResults([{data: initialUser}]); - - const refetchVariables = { - id: '1', - scale: 32, - }; - refetchQuery = createOperationDescriptor( - gqlRefetchQuery, - refetchVariables, - {force: true}, - ); - - commitSpy.mockClear(); - // $FlowFixMe[method-unbinding] added when improving typing for this parameters - environment.executeWithSource.mockClear(); - // $FlowFixMe[method-unbinding] added when improving typing for this parameters - environment.retain.mockClear(); - release.mockClear(); - - TestRenderer.act(() => { - refetch({scale: 32}, {fetchPolicy: 'network-only'}); - }); - - expectFragmentIsRefetching(renderer, { - refetchVariables, - refetchQuery, + describe('fetchPolicy', () => { + describe('store-or-network', () => { + beforeEach(() => { + fetchPolicy = 'store-or-network'; }); - TestRenderer.act(() => { - environment.mock.resolve(gqlRefetchQuery, { - data: { - node: { - __typename: 'User', - id: '2', - name: 'Mark', - profile_picture: { - uri: 'scale32', - }, - username: 'usermark', - }, - }, + describe('renderPolicy: partial', () => { + beforeEach(() => { + renderPolicy = 'partial'; }); - }); - - TestRenderer.act(() => { - jest.runAllImmediates(); - }); - - // $FlowFixMe[prop-missing] - const warningCalls = warning.mock.calls.filter( - call => call[0] === false, - ); - expect(warningCalls.length).toEqual(isUsingNewImplementation ? 2 : 1); - expect( - warningCalls[0][1].includes( - 'Relay: Call to `refetch` returned a different id, expected', - ), - ).toEqual(true); - }); - - it("doesn't warn if refetching on a different id than the current one in display", () => { - const warning = require('warning'); - // $FlowFixMe[prop-missing] - warning.mockClear(); - - renderFragment(); - - const initialUser = { - id: '1', - name: 'Alice', - profile_picture: null, - ...createFragmentRef('1', query), - }; - expectFragmentResults([{data: initialUser}]); - - const refetchVariables = { - id: '1', - scale: 32, - }; - refetchQuery = createOperationDescriptor( - gqlRefetchQuery, - refetchVariables, - {force: true}, - ); - - commitSpy.mockClear(); - // $FlowFixMe[method-unbinding] added when improving typing for this parameters - environment.executeWithSource.mockClear(); - // $FlowFixMe[method-unbinding] added when improving typing for this parameters - environment.retain.mockClear(); - release.mockClear(); - - TestRenderer.act(() => { - refetch({id: '2', scale: 32}, {fetchPolicy: 'network-only'}); - jest.runAllImmediates(); - }); - - TestRenderer.act(() => { - refetch({id: '3', scale: 32}, {fetchPolicy: 'network-only'}); - }); + it("doesn't start network request if refetch query is fully cached", () => { + renderFragment(); + commitSpy.mockClear(); + TestRenderer.act(() => { + refetch( + {id: '1'}, + {fetchPolicy, UNSTABLE_renderPolicy: renderPolicy}, + ); + }); - TestRenderer.act(() => { - environment.mock.resolve(gqlRefetchQuery, { - data: { - node: { - __typename: 'User', - id: '3', - name: 'Mark', - profile_picture: { - uri: 'scale32', - }, - username: 'usermark', - }, - }, - }); - }); + // Assert request is not started + const refetchVariables = {...variables}; + refetchQuery = createOperationDescriptor( + gqlRefetchQuery, + refetchVariables, + {force: true}, + ); + expectRequestIsInFlight({ + inFlight: false, + requestCount: 0, + gqlRefetchQuery, + refetchVariables, + }); - TestRenderer.act(() => { - jest.runAllTimers(); - }); - - expect( - // $FlowFixMe[prop-missing] - warning.mock.calls.filter(call => call[0] === false).length, - ).toEqual(0); - }); - - describe('multiple refetches', () => { - const internalRuntime = require('relay-runtime').__internal; - const originalFetchQueryDeduped = internalRuntime.fetchQueryDeduped; - const fetchSpy = jest.fn, mixed>(); - jest - .spyOn(internalRuntime, 'fetchQueryDeduped') - .mockImplementation((...args) => { - const originalObservable = originalFetchQueryDeduped(...args); - return { - ...originalObservable, - subscribe: (...subscribeArgs) => { - fetchSpy(...args); - return originalObservable.subscribe(...subscribeArgs); - }, + // Assert component renders immediately since data is cached + const refetchingUser = { + id: '1', + name: 'Alice', + profile_picture: null, + ...createFragmentRef('1', refetchQuery), }; + expectFragmentResults([{data: refetchingUser}]); }); - beforeEach(() => { - fetchSpy.mockClear(); - }); - - it('refetches correctly when refetching multiple times in a row', () => { - const renderer = renderFragment(); - const initialUser = { - id: '1', - name: 'Alice', - profile_picture: null, - ...createFragmentRef('1', query), - }; - expectFragmentResults([{data: initialUser}]); - - const refetchVariables = { - id: '1', - scale: 32, - }; - refetchQuery = createOperationDescriptor( - gqlRefetchQuery, - refetchVariables, - {force: true}, - ); - const refetchedUser = { - id: '1', - name: 'Alice', - profile_picture: { - uri: 'scale32', - }, - ...createFragmentRef('1', refetchQuery), - }; - - const doAndAssertRefetch = (fragmentResults: Array<{data: any}>) => { - commitSpy.mockClear(); - // $FlowFixMe[method-unbinding] added when improving typing for this parameters - environment.executeWithSource.mockClear(); - // $FlowFixMe[method-unbinding] added when improving typing for this parameters - environment.retain.mockClear(); + it('starts network request if refetch query is not fully cached and suspends if fragment has missing data', () => { + const renderer = renderFragment(); + const initialUser = { + id: '1', + name: 'Alice', + profile_picture: null, + ...createFragmentRef('1', query), + }; + expectFragmentResults([{data: initialUser}]); TestRenderer.act(() => { - // We use fetchPolicy network-only to ensure the call to refetch - // always suspends - refetch({scale: 32}, {fetchPolicy: 'network-only'}); + refetch( + {id: '4'}, + {fetchPolicy, UNSTABLE_renderPolicy: renderPolicy}, + ); }); // Assert that fragment is refetching with the right variables and // suspends upon refetch + const refetchVariables = { + id: '4', + scale: 16, + }; + refetchQuery = createOperationDescriptor( + gqlRefetchQuery, + refetchVariables, + {force: true}, + ); expectFragmentIsRefetching(renderer, { refetchVariables, refetchQuery, @@ -1668,1177 +2321,256 @@ describe.each([['New', useRefetchableFragmentInternal]])( data: { node: { __typename: 'User', - id: '1', - name: 'Alice', + id: '4', + name: 'Mark', profile_picture: { - uri: 'scale32', + uri: 'scale16', }, - username: 'useralice', + username: 'usermark', }, }, }); }); // Assert fragment is rendered with new data - expectFragmentResults(fragmentResults); + const refetchedUser = { + id: '4', + name: 'Mark', + profile_picture: { + uri: 'scale16', + }, + ...createFragmentRef('4', refetchQuery), + }; + expectFragmentResults([{data: refetchedUser}]); + }); - // Assert refetch query was retained - expect(isOperationRetained(refetchQuery)).toBe(true); - }; + it("starts network request if refetch query is not fully cached and doesn't suspend if fragment doesn't have missing data", () => { + // Cache user with missing username + const refetchVariables = {id: '4', scale: 16}; + refetchQuery = createOperationDescriptor( + gqlRefetchQuery, + refetchVariables, + {force: true}, + ); + environment.commitPayload(refetchQuery, { + node: { + __typename: 'User', + id: '4', + name: 'Mark', + profile_picture: null, + }, + }); - // Refetch once - doAndAssertRefetch([{data: refetchedUser}]); + renderFragment(); + commitSpy.mockClear(); + TestRenderer.act(() => { + refetch( + {id: '4'}, + {fetchPolicy, UNSTABLE_renderPolicy: renderPolicy}, + ); + }); - // Refetch twice - doAndAssertRefetch([{data: refetchedUser}]); - }); + // Assert request is started + expectRequestIsInFlight({ + inFlight: true, + requestCount: 1, + gqlRefetchQuery, + refetchVariables, + }); - it('refetches correctly when a second refetch starts while the first is one suspended', () => { - const renderer = renderFragment(); - commitSpy.mockClear(); - TestRenderer.act(() => { - refetch( - {id: '1'}, - { - fetchPolicy: 'network-only', - UNSTABLE_renderPolicy: renderPolicy, - }, - ); + // Assert component renders immediately since data is cached + const refetchingUser = { + id: '4', + name: 'Mark', + profile_picture: null, + ...createFragmentRef('4', refetchQuery), + }; + expectFragmentResults([{data: refetchingUser}]); }); + }); - // Assert request is started - const refetchVariables1 = {id: '1', scale: 16}; - const refetchQuery1 = createOperationDescriptor( - gqlRefetchQuery, - refetchVariables1, - {force: true}, - ); - - // Assert we suspend on intial refetch request - expectFragmentIsRefetching(renderer, { - refetchQuery: refetchQuery1, - refetchVariables: refetchVariables1, + describe('renderPolicy: full', () => { + beforeEach(() => { + renderPolicy = 'full'; }); - - // Call refetch a second time - // $FlowFixMe[method-unbinding] added when improving typing for this parameters - environment.executeWithSource.mockClear(); - // $FlowFixMe[method-unbinding] added when improving typing for this parameters - environment.retain.mockClear(); - const refetchVariables2 = {id: '4', scale: 16}; - const refetchQuery2 = createOperationDescriptor( - gqlRefetchQuery, - refetchVariables2, - {force: true}, - ); - TestRenderer.act(() => { - refetch( - {id: '4'}, - { - fetchPolicy: 'network-only', - UNSTABLE_renderPolicy: renderPolicy, - }, - ); - }); - - // Assert we suspend on the second refetch request - expectFragmentIsRefetching(renderer, { - refetchQuery: refetchQuery2, - refetchVariables: refetchVariables2, - }); - - // Mock response for initial refetch request - TestRenderer.act(() => { - environment.mock.resolve(refetchQuery1, { - data: { - node: { - __typename: 'User', - id: '1', - name: 'User 1', - profile_picture: { - uri: 'scale16', - }, - username: 'user1', - }, - }, - }); - }); - - // Assert that we are still suspended the second refetch request - // since that one hasn't resolved and that's the latest one we want - // to render - expectFragmentIsRefetching(renderer, { - refetchQuery: refetchQuery2, - refetchVariables: refetchVariables2, - }); - - // Mock response for second refetch request - TestRenderer.act(() => { - environment.mock.resolve(refetchQuery2, { - data: { - node: { - __typename: 'User', - id: '4', - name: 'Mark', - profile_picture: { - uri: 'scale16', - }, - username: 'usermark', - }, - }, - }); - }); - - // Assert component is rendered with data from second request - const refetchedUser = { - id: '4', - name: 'Mark', - profile_picture: {uri: 'scale16'}, - ...createFragmentRef('4', refetchQuery2), - }; - expectFragmentResults([{data: refetchedUser}]); - - expect(fetchSpy).toBeCalledTimes(4); - }); - - it('does not re-issue initial refetch request if second refetch is interrupted by high-pri update', () => { - const renderer = renderFragment(); - commitSpy.mockClear(); - TestRenderer.act(() => { - refetch( - {id: '1'}, - { - fetchPolicy: 'network-only', - UNSTABLE_renderPolicy: renderPolicy, - }, - ); - }); - - // Assert request is started - const refetchVariables1 = {id: '1', scale: 16}; - const refetchQuery1 = createOperationDescriptor( - gqlRefetchQuery, - refetchVariables1, - {force: true}, - ); - - // Assert we suspend on intial refetch request - expectFragmentIsRefetching(renderer, { - refetchQuery: refetchQuery1, - refetchVariables: refetchVariables1, - }); - - // Call refetch a second time - // $FlowFixMe[method-unbinding] added when improving typing for this parameters - environment.executeWithSource.mockClear(); - // $FlowFixMe[method-unbinding] added when improving typing for this parameters - environment.retain.mockClear(); - const refetchVariables2 = {id: '4', scale: 16}; - const refetchQuery2 = createOperationDescriptor( - gqlRefetchQuery, - refetchVariables2, - {force: true}, - ); - TestRenderer.act(() => { - refetch( - {id: '4'}, - { - fetchPolicy: 'network-only', - UNSTABLE_renderPolicy: renderPolicy, - }, - ); - }); - - // Assert we suspend on the second refetch request - expectFragmentIsRefetching(renderer, { - refetchQuery: refetchQuery2, - refetchVariables: refetchVariables2, - }); - - // Schedule a high-pri update while the component is - // suspended on pagination - TestRenderer.act(() => { - Scheduler.unstable_runWithPriority( - Scheduler.unstable_UserBlockingPriority, - () => { - forceUpdate(prev => prev + 1); - }, - ); - }); - - // Assert that we are still suspended the second refetch request - // since that one hasn't resolved and that's the latest one we want - // to render - expectFragmentIsRefetching(renderer, { - refetchQuery: refetchQuery2, - refetchVariables: refetchVariables2, - }); - - // Mock response for initial refetch request - TestRenderer.act(() => { - environment.mock.resolve(refetchQuery1, { - data: { - node: { - __typename: 'User', - id: '1', - name: 'User 1', - profile_picture: { - uri: 'scale16', - }, - username: 'user1', - }, - }, - }); - }); - - // Assert that we are still suspended the second refetch request - // since that one hasn't resolved and that's the latest one we want - // to render - expectFragmentIsRefetching(renderer, { - refetchQuery: refetchQuery2, - refetchVariables: refetchVariables2, - }); - - // Mock response for second refetch request - TestRenderer.act(() => { - environment.mock.resolve(refetchQuery2, { - data: { - node: { - __typename: 'User', - id: '4', - name: 'Mark', - profile_picture: { - uri: 'scale16', - }, - username: 'usermark', - }, - }, - }); - }); - - // Assert component is rendered with data from second request - const refetchedUser = { - id: '4', - name: 'Mark', - profile_picture: {uri: 'scale16'}, - ...createFragmentRef('4', refetchQuery2), - }; - expectFragmentResults([{data: refetchedUser}]); - - expect(fetchSpy).toBeCalledTimes(4); - }); - - it('refetches correctly when switching between multiple refetches', () => { - const renderer = renderFragment(); - commitSpy.mockClear(); - TestRenderer.act(() => { - refetch( - {id: '1'}, - { - fetchPolicy: 'network-only', - UNSTABLE_renderPolicy: renderPolicy, - }, - ); - }); - - // Assert request is started - const refetchVariables1 = {id: '1', scale: 16}; - const refetchQuery1 = createOperationDescriptor( - gqlRefetchQuery, - refetchVariables1, - {force: true}, - ); - - // Assert we suspend on initial refetch request - expectFragmentIsRefetching(renderer, { - refetchQuery: refetchQuery1, - refetchVariables: refetchVariables1, - }); - - // Call refetch a second time - // $FlowFixMe[method-unbinding] added when improving typing for this parameters - environment.executeWithSource.mockClear(); - // $FlowFixMe[method-unbinding] added when improving typing for this parameters - environment.retain.mockClear(); - const refetchVariables2 = {id: '4', scale: 16}; - const refetchQuery2 = createOperationDescriptor( - gqlRefetchQuery, - refetchVariables2, - {force: true}, - ); - TestRenderer.act(() => { - refetch( - {id: '4'}, - { - fetchPolicy: 'network-only', - UNSTABLE_renderPolicy: renderPolicy, - }, - ); - }); - - // Assert we suspend on the second refetch request - expectFragmentIsRefetching(renderer, { - refetchQuery: refetchQuery2, - refetchVariables: refetchVariables2, - }); - - // Switch back to initial refetch - // $FlowFixMe[method-unbinding] added when improving typing for this parameters - environment.retain.mockClear(); - TestRenderer.act(() => { - refetch( - {id: '1'}, - { - fetchPolicy: 'network-only', - UNSTABLE_renderPolicy: renderPolicy, - }, - ); - }); - - // Mock response for second refetch request - TestRenderer.act(() => { - environment.mock.resolve(refetchQuery2, { - data: { - node: { - __typename: 'User', - id: '4', - name: 'Mark', - profile_picture: { - uri: 'scale16', - }, - username: 'usermark', - }, - }, - }); - }); - - // Assert that we are still suspended the initial refetch request - // since that one hasn't resolved and that's the latest one we want - // to render - expectFragmentIsRefetching(renderer, { - refetchQuery: refetchQuery1, - refetchVariables: refetchVariables1, - }); - - // Mock response for initial refetch request - TestRenderer.act(() => { - environment.mock.resolve(refetchQuery1, { - data: { - node: { - __typename: 'User', - id: '1', - name: 'User 1', - profile_picture: { - uri: 'scale16', - }, - username: 'user1', - }, - }, - }); - }); - - // Assert component is rendered with data from second request - const refetchedUser = { - id: '1', - name: 'User 1', - profile_picture: {uri: 'scale16'}, - ...createFragmentRef('1', refetchQuery1), - }; - expectFragmentResults([{data: refetchedUser}]); - - expect(fetchSpy).toBeCalledTimes(5); - }); - - it('does not dispose ongoing request if refetch is called again', () => { - const renderer = renderFragment(); - commitSpy.mockClear(); - TestRenderer.act(() => { - refetch( - {id: '1'}, - { - fetchPolicy: 'store-and-network', - UNSTABLE_renderPolicy: renderPolicy, - }, - ); - }); - - // Assert request is started - const refetchVariables1 = {id: '1', scale: 16}; - const refetchQuery1 = createOperationDescriptor( - gqlRefetchQuery, - refetchVariables1, - {force: true}, - ); - expectRequestIsInFlight({ - inFlight: true, - requestCount: 1, - gqlRefetchQuery, - refetchVariables: refetchVariables1, - }); - - // Component renders immediately even though request is in flight - // since data is cached - const refetchingUser = { - id: '1', - name: 'Alice', - profile_picture: null, - ...createFragmentRef('1', refetchQuery1), - }; - expectFragmentResults([{data: refetchingUser}]); - - // Call refetch a second time - // $FlowFixMe[method-unbinding] added when improving typing for this parameters - environment.executeWithSource.mockClear(); - const refetchVariables2 = {id: '4', scale: 16}; - TestRenderer.act(() => { - refetch( - {id: '4'}, - {fetchPolicy, UNSTABLE_renderPolicy: renderPolicy}, - ); - }); - - // Assert first request is not cancelled - expectRequestIsInFlight({ - inFlight: true, - requestCount: 1, - gqlRefetchQuery, - refetchVariables: refetchVariables1, - }); - - // Assert second request is started - expectRequestIsInFlight({ - inFlight: true, - requestCount: 1, - gqlRefetchQuery, - refetchVariables: refetchVariables2, - }); - // Assert component suspended - expect(commitSpy).toBeCalledTimes(0); - expect(renderer.toJSON()).toEqual('Fallback'); - - expect(fetchSpy).toBeCalledTimes(4); - }); - - it('preserves referential equality after refetch if data & variables have not changed', async () => { - let refetchCount = 0; - const ComponentWithUseEffectRefetch = (props: { - fragmentKey: any, - }): null => { - const {fragmentData, refetch} = useRefetchableFragmentNode( - graphql` - fragment useRefetchableFragmentNodeTestIdentityTestFragment on User - @refetchable( - queryName: "useRefetchableFragmentNodeTestIdentityTestFragmentRefetchQuery" - ) { - id - name - profile_picture(scale: $scale) { - uri - } - } - `, - props.fragmentKey, - ); - if (refetchCount > 2) { - throw new Error('Detected refetch loop.'); - } - useEffect(() => { - refetchCount++; - refetch(fragmentData.id); - }, [fragmentData, refetch]); - - return null; - }; - const variables = {id: '1', scale: 16}; - const query = createOperationDescriptor( - gqlRefetchQuery, - variables, - {}, - ); - environment.commitPayload(query, { - node: { - __typename: 'User', - id: '1', - name: 'Alice', - profile_picture: null, - }, - }); - let renderer; - TestRenderer.act(() => { - renderer = TestRenderer.create( - `Error: ${error.message}`}> - - - - - - , - // $FlowFixMe[prop-missing] - error revealed when flow-typing ReactTestRenderer - {unstable_isConcurrent: true}, - ); - jest.runAllImmediates(); - }); - expect(refetchCount).toBe(2); - expect(renderer?.toJSON()).toBe(null); - }); - }); - - describe('fetchPolicy', () => { - describe('store-or-network', () => { - beforeEach(() => { - fetchPolicy = 'store-or-network'; - }); - - describe('renderPolicy: partial', () => { - beforeEach(() => { - renderPolicy = 'partial'; - }); - it("doesn't start network request if refetch query is fully cached", () => { - renderFragment(); - commitSpy.mockClear(); - TestRenderer.act(() => { - refetch( - {id: '1'}, - {fetchPolicy, UNSTABLE_renderPolicy: renderPolicy}, - ); - }); - - // Assert request is not started - const refetchVariables = {...variables}; - refetchQuery = createOperationDescriptor( - gqlRefetchQuery, - refetchVariables, - {force: true}, - ); - expectRequestIsInFlight({ - inFlight: false, - requestCount: 0, - gqlRefetchQuery, - refetchVariables, - }); - - // Assert component renders immediately since data is cached - const refetchingUser = { - id: '1', - name: 'Alice', - profile_picture: null, - ...createFragmentRef('1', refetchQuery), - }; - expectFragmentResults([{data: refetchingUser}]); - }); - - it('starts network request if refetch query is not fully cached and suspends if fragment has missing data', () => { - const renderer = renderFragment(); - const initialUser = { - id: '1', - name: 'Alice', - profile_picture: null, - ...createFragmentRef('1', query), - }; - expectFragmentResults([{data: initialUser}]); - - TestRenderer.act(() => { - refetch( - {id: '4'}, - {fetchPolicy, UNSTABLE_renderPolicy: renderPolicy}, - ); - }); - - // Assert that fragment is refetching with the right variables and - // suspends upon refetch - const refetchVariables = { - id: '4', - scale: 16, - }; - refetchQuery = createOperationDescriptor( - gqlRefetchQuery, - refetchVariables, - {force: true}, - ); - expectFragmentIsRefetching(renderer, { - refetchVariables, - refetchQuery, - }); - - // Mock network response - TestRenderer.act(() => { - environment.mock.resolve(gqlRefetchQuery, { - data: { - node: { - __typename: 'User', - id: '4', - name: 'Mark', - profile_picture: { - uri: 'scale16', - }, - username: 'usermark', - }, - }, - }); - }); - - // Assert fragment is rendered with new data - const refetchedUser = { - id: '4', - name: 'Mark', - profile_picture: { - uri: 'scale16', - }, - ...createFragmentRef('4', refetchQuery), - }; - expectFragmentResults([{data: refetchedUser}]); - }); - - it("starts network request if refetch query is not fully cached and doesn't suspend if fragment doesn't have missing data", () => { - // Cache user with missing username - const refetchVariables = {id: '4', scale: 16}; - refetchQuery = createOperationDescriptor( - gqlRefetchQuery, - refetchVariables, - {force: true}, - ); - environment.commitPayload(refetchQuery, { - node: { - __typename: 'User', - id: '4', - name: 'Mark', - profile_picture: null, - }, - }); - - renderFragment(); - commitSpy.mockClear(); - TestRenderer.act(() => { - refetch( - {id: '4'}, - {fetchPolicy, UNSTABLE_renderPolicy: renderPolicy}, - ); - }); - - // Assert request is started - expectRequestIsInFlight({ - inFlight: true, - requestCount: 1, - gqlRefetchQuery, - refetchVariables, - }); - - // Assert component renders immediately since data is cached - const refetchingUser = { - id: '4', - name: 'Mark', - profile_picture: null, - ...createFragmentRef('4', refetchQuery), - }; - expectFragmentResults([{data: refetchingUser}]); - }); - }); - - describe('renderPolicy: full', () => { - beforeEach(() => { - renderPolicy = 'full'; - }); - it("doesn't start network request if refetch query is fully cached", () => { - renderFragment(); - commitSpy.mockClear(); - TestRenderer.act(() => { - refetch( - {id: '1'}, - {fetchPolicy, UNSTABLE_renderPolicy: renderPolicy}, - ); - }); - - // Assert request is not started - const refetchVariables = {...variables}; - refetchQuery = createOperationDescriptor( - gqlRefetchQuery, - refetchVariables, - {force: true}, - ); - expectRequestIsInFlight({ - inFlight: false, - requestCount: 0, - gqlRefetchQuery, - refetchVariables, - }); - - // Assert component renders immediately since data is cached - const refetchingUser = { - id: '1', - name: 'Alice', - profile_picture: null, - ...createFragmentRef('1', refetchQuery), - }; - expectFragmentResults([{data: refetchingUser}]); - }); - - it('starts network request if refetch query is not fully cached and suspends if fragment has missing data', () => { - const renderer = renderFragment(); - const initialUser = { - id: '1', - name: 'Alice', - profile_picture: null, - ...createFragmentRef('1', query), - }; - expectFragmentResults([{data: initialUser}]); - - TestRenderer.act(() => { - refetch( - {id: '4'}, - {fetchPolicy, UNSTABLE_renderPolicy: renderPolicy}, - ); - }); - - // Assert that fragment is refetching with the right variables and - // suspends upon refetch - const refetchVariables = { - id: '4', - scale: 16, - }; - refetchQuery = createOperationDescriptor( - gqlRefetchQuery, - refetchVariables, - {force: true}, + it("doesn't start network request if refetch query is fully cached", () => { + renderFragment(); + commitSpy.mockClear(); + TestRenderer.act(() => { + refetch( + {id: '1'}, + {fetchPolicy, UNSTABLE_renderPolicy: renderPolicy}, ); - expectFragmentIsRefetching(renderer, { - refetchVariables, - refetchQuery, - }); - - // Mock network response - TestRenderer.act(() => { - environment.mock.resolve(gqlRefetchQuery, { - data: { - node: { - __typename: 'User', - id: '4', - name: 'Mark', - profile_picture: { - uri: 'scale16', - }, - username: 'usermark', - }, - }, - }); - }); - - // Assert fragment is rendered with new data - const refetchedUser = { - id: '4', - name: 'Mark', - profile_picture: { - uri: 'scale16', - }, - ...createFragmentRef('4', refetchQuery), - }; - expectFragmentResults([{data: refetchedUser}]); }); - it("starts network request if refetch query is not fully cached and suspends even if fragment doesn't have missing data", () => { - // Cache user with missing username - const refetchVariables = {id: '4', scale: 16}; - refetchQuery = createOperationDescriptor( - gqlRefetchQuery, - refetchVariables, - {force: true}, - ); - environment.commitPayload(refetchQuery, { - node: { - __typename: 'User', - id: '4', - name: 'Mark', - profile_picture: null, - }, - }); - - const renderer = renderFragment(); - const initialUser = { - id: '1', - name: 'Alice', - profile_picture: null, - ...createFragmentRef('1', query), - }; - expectFragmentResults([{data: initialUser}]); - - TestRenderer.act(() => { - refetch( - {id: '4'}, - {fetchPolicy, UNSTABLE_renderPolicy: renderPolicy}, - ); - }); - - expectFragmentIsRefetching(renderer, { - refetchVariables, - refetchQuery, - }); - - // Mock network response - TestRenderer.act(() => { - environment.mock.resolve(gqlRefetchQuery, { - data: { - node: { - __typename: 'User', - id: '4', - name: 'Mark', - profile_picture: { - uri: 'scale16', - }, - username: 'usermark', - }, - }, - }); - }); - - // Assert fragment is rendered with new data - const refetchedUser = { - id: '4', - name: 'Mark', - profile_picture: { - uri: 'scale16', - }, - ...createFragmentRef('4', refetchQuery), - }; - expectFragmentResults([{data: refetchedUser}]); + // Assert request is not started + const refetchVariables = {...variables}; + refetchQuery = createOperationDescriptor( + gqlRefetchQuery, + refetchVariables, + {force: true}, + ); + expectRequestIsInFlight({ + inFlight: false, + requestCount: 0, + gqlRefetchQuery, + refetchVariables, }); - }); - }); - describe('store-and-network', () => { - beforeEach(() => { - fetchPolicy = 'store-and-network'; + // Assert component renders immediately since data is cached + const refetchingUser = { + id: '1', + name: 'Alice', + profile_picture: null, + ...createFragmentRef('1', refetchQuery), + }; + expectFragmentResults([{data: refetchingUser}]); }); - describe('renderPolicy: partial', () => { - beforeEach(() => { - renderPolicy = 'partial'; - }); - - it('starts network request if refetch query is fully cached', () => { - renderFragment(); - commitSpy.mockClear(); - TestRenderer.act(() => { - refetch( - {id: '1'}, - {fetchPolicy, UNSTABLE_renderPolicy: renderPolicy}, - ); - }); + it('starts network request if refetch query is not fully cached and suspends if fragment has missing data', () => { + const renderer = renderFragment(); + const initialUser = { + id: '1', + name: 'Alice', + profile_picture: null, + ...createFragmentRef('1', query), + }; + expectFragmentResults([{data: initialUser}]); - // Assert request is not started - const refetchVariables = {...variables}; - refetchQuery = createOperationDescriptor( - gqlRefetchQuery, - refetchVariables, - {force: true}, + TestRenderer.act(() => { + refetch( + {id: '4'}, + {fetchPolicy, UNSTABLE_renderPolicy: renderPolicy}, ); - expectRequestIsInFlight({ - inFlight: true, - requestCount: 1, - gqlRefetchQuery, - refetchVariables, - }); - - // Assert component renders immediately since data is cached - const refetchingUser = { - id: '1', - name: 'Alice', - profile_picture: null, - ...createFragmentRef('1', refetchQuery), - }; - expectFragmentResults([{data: refetchingUser}]); }); - it('starts network request if refetch query is not fully cached and suspends if fragment has missing data', () => { - const renderer = renderFragment(); - const initialUser = { - id: '1', - name: 'Alice', - profile_picture: null, - ...createFragmentRef('1', query), - }; - expectFragmentResults([{data: initialUser}]); - - TestRenderer.act(() => { - refetch( - {id: '4'}, - {fetchPolicy, UNSTABLE_renderPolicy: renderPolicy}, - ); - }); - - // Assert that fragment is refetching with the right variables and - // suspends upon refetch - const refetchVariables = { - id: '4', - scale: 16, - }; - refetchQuery = createOperationDescriptor( - gqlRefetchQuery, - refetchVariables, - {force: true}, - ); - expectFragmentIsRefetching(renderer, { - refetchVariables, - refetchQuery, - }); + // Assert that fragment is refetching with the right variables and + // suspends upon refetch + const refetchVariables = { + id: '4', + scale: 16, + }; + refetchQuery = createOperationDescriptor( + gqlRefetchQuery, + refetchVariables, + {force: true}, + ); + expectFragmentIsRefetching(renderer, { + refetchVariables, + refetchQuery, + }); - // Mock network response - TestRenderer.act(() => { - environment.mock.resolve(gqlRefetchQuery, { - data: { - node: { - __typename: 'User', - id: '4', - name: 'Mark', - profile_picture: { - uri: 'scale16', - }, - username: 'usermark', + // Mock network response + TestRenderer.act(() => { + environment.mock.resolve(gqlRefetchQuery, { + data: { + node: { + __typename: 'User', + id: '4', + name: 'Mark', + profile_picture: { + uri: 'scale16', }, + username: 'usermark', }, - }); - }); - - // Assert fragment is rendered with new data - const refetchedUser = { - id: '4', - name: 'Mark', - profile_picture: { - uri: 'scale16', - }, - ...createFragmentRef('4', refetchQuery), - }; - expectFragmentResults([{data: refetchedUser}]); - }); - - it("starts network request if refetch query is not fully cached and doesn't suspend if fragment doesn't have missing data", () => { - // Cache user with missing username - const refetchVariables = {id: '4', scale: 16}; - refetchQuery = createOperationDescriptor( - gqlRefetchQuery, - refetchVariables, - {force: true}, - ); - environment.commitPayload(refetchQuery, { - node: { - __typename: 'User', - id: '4', - name: 'Mark', - profile_picture: null, }, }); + }); - renderFragment(); - commitSpy.mockClear(); - TestRenderer.act(() => { - refetch( - {id: '4'}, - {fetchPolicy, UNSTABLE_renderPolicy: renderPolicy}, - ); - }); - - // Assert request is started - expectRequestIsInFlight({ - inFlight: true, - requestCount: 1, - gqlRefetchQuery, - refetchVariables, - }); + // Assert fragment is rendered with new data + const refetchedUser = { + id: '4', + name: 'Mark', + profile_picture: { + uri: 'scale16', + }, + ...createFragmentRef('4', refetchQuery), + }; + expectFragmentResults([{data: refetchedUser}]); + }); - // Assert component renders immediately since data is cached - const refetchingUser = { + it("starts network request if refetch query is not fully cached and suspends even if fragment doesn't have missing data", () => { + // Cache user with missing username + const refetchVariables = {id: '4', scale: 16}; + refetchQuery = createOperationDescriptor( + gqlRefetchQuery, + refetchVariables, + {force: true}, + ); + environment.commitPayload(refetchQuery, { + node: { + __typename: 'User', id: '4', name: 'Mark', profile_picture: null, - ...createFragmentRef('4', refetchQuery), - }; - expectFragmentResults([{data: refetchingUser}]); - }); - }); - - describe('renderPolicy: full', () => { - beforeEach(() => { - renderPolicy = 'full'; + }, }); - it('starts network request if refetch query is fully cached', () => { - renderFragment(); - commitSpy.mockClear(); - TestRenderer.act(() => { - refetch( - {id: '1'}, - {fetchPolicy, UNSTABLE_renderPolicy: renderPolicy}, - ); - }); + const renderer = renderFragment(); + const initialUser = { + id: '1', + name: 'Alice', + profile_picture: null, + ...createFragmentRef('1', query), + }; + expectFragmentResults([{data: initialUser}]); - // Assert request is not started - const refetchVariables = {...variables}; - refetchQuery = createOperationDescriptor( - gqlRefetchQuery, - refetchVariables, - {force: true}, + TestRenderer.act(() => { + refetch( + {id: '4'}, + {fetchPolicy, UNSTABLE_renderPolicy: renderPolicy}, ); - expectRequestIsInFlight({ - inFlight: true, - requestCount: 1, - gqlRefetchQuery, - refetchVariables, - }); - - // Assert component renders immediately since data is cached - const refetchingUser = { - id: '1', - name: 'Alice', - profile_picture: null, - ...createFragmentRef('1', refetchQuery), - }; - expectFragmentResults([{data: refetchingUser}]); }); - it('starts network request if refetch query is not fully cached and suspends if fragment has missing data', () => { - const renderer = renderFragment(); - const initialUser = { - id: '1', - name: 'Alice', - profile_picture: null, - ...createFragmentRef('1', query), - }; - expectFragmentResults([{data: initialUser}]); - - TestRenderer.act(() => { - refetch( - {id: '4'}, - {fetchPolicy, UNSTABLE_renderPolicy: renderPolicy}, - ); - }); - - // Assert that fragment is refetching with the right variables and - // suspends upon refetch - const refetchVariables = { - id: '4', - scale: 16, - }; - refetchQuery = createOperationDescriptor( - gqlRefetchQuery, - refetchVariables, - {force: true}, - ); - expectFragmentIsRefetching(renderer, { - refetchVariables, - refetchQuery, - }); - - // Mock network response - TestRenderer.act(() => { - environment.mock.resolve(gqlRefetchQuery, { - data: { - node: { - __typename: 'User', - id: '4', - name: 'Mark', - profile_picture: { - uri: 'scale16', - }, - username: 'usermark', - }, - }, - }); - }); - - // Assert fragment is rendered with new data - const refetchedUser = { - id: '4', - name: 'Mark', - profile_picture: { - uri: 'scale16', - }, - ...createFragmentRef('4', refetchQuery), - }; - expectFragmentResults([{data: refetchedUser}]); + expectFragmentIsRefetching(renderer, { + refetchVariables, + refetchQuery, }); - it("starts network request if refetch query is not fully cached and doesn't suspend if fragment doesn't have missing data", () => { - // Cache user with missing username - const refetchVariables = {id: '4', scale: 16}; - refetchQuery = createOperationDescriptor( - gqlRefetchQuery, - refetchVariables, - {force: true}, - ); - environment.commitPayload(refetchQuery, { - node: { - __typename: 'User', - id: '4', - name: 'Mark', - profile_picture: null, - }, - }); - - const renderer = renderFragment(); - commitSpy.mockClear(); - TestRenderer.act(() => { - refetch( - {id: '4'}, - {fetchPolicy, UNSTABLE_renderPolicy: renderPolicy}, - ); - }); - - // Assert component suspended - expectFragmentIsRefetching(renderer, { - refetchVariables, - refetchQuery, - }); - - // Mock network response - TestRenderer.act(() => { - environment.mock.resolve(gqlRefetchQuery, { - data: { - node: { - __typename: 'User', - id: '4', - name: 'Mark', - profile_picture: { - uri: 'scale16', - }, - username: 'usermark', + // Mock network response + TestRenderer.act(() => { + environment.mock.resolve(gqlRefetchQuery, { + data: { + node: { + __typename: 'User', + id: '4', + name: 'Mark', + profile_picture: { + uri: 'scale16', }, + username: 'usermark', }, - }); - }); - - // Assert fragment is rendered with new data - const refetchedUser = { - id: '4', - name: 'Mark', - profile_picture: { - uri: 'scale16', }, - ...createFragmentRef('4', refetchQuery), - }; - expectFragmentResults([{data: refetchedUser}]); + }); }); + + // Assert fragment is rendered with new data + const refetchedUser = { + id: '4', + name: 'Mark', + profile_picture: { + uri: 'scale16', + }, + ...createFragmentRef('4', refetchQuery), + }; + expectFragmentResults([{data: refetchedUser}]); }); }); + }); - describe('network-only', () => { - beforeEach(() => { - fetchPolicy = 'network-only'; - }); + describe('store-and-network', () => { + beforeEach(() => { + fetchPolicy = 'store-and-network'; + }); - it('starts network request and suspends if refetch query is fully cached', () => { - const renderer = renderFragment(); - const initialUser = { - id: '1', - name: 'Alice', - profile_picture: null, - ...createFragmentRef('1', query), - }; - expectFragmentResults([{data: initialUser}]); + describe('renderPolicy: partial', () => { + beforeEach(() => { + renderPolicy = 'partial'; + }); + it('starts network request if refetch query is fully cached', () => { + renderFragment(); + commitSpy.mockClear(); TestRenderer.act(() => { refetch( {id: '1'}, @@ -2846,45 +2578,31 @@ describe.each([['New', useRefetchableFragmentInternal]])( ); }); - // Assert that fragment is refetching with the right variables and - // suspends upon refetch - const refetchVariables = { - ...variables, - }; + // Assert request is not started + const refetchVariables = {...variables}; refetchQuery = createOperationDescriptor( gqlRefetchQuery, refetchVariables, {force: true}, ); - expectFragmentIsRefetching(renderer, { + expectRequestIsInFlight({ + inFlight: true, + requestCount: 1, + gqlRefetchQuery, refetchVariables, - refetchQuery, - }); - - // Mock network response - TestRenderer.act(() => { - environment.mock.resolve(gqlRefetchQuery, { - data: { - node: { - __typename: 'User', - id: '1', - name: 'Alice', - profile_picture: null, - username: 'useralice', - }, - }, - }); }); - // Assert fragment is rendered with new data - const refetchedUser = { - ...initialUser, + // Assert component renders immediately since data is cached + const refetchingUser = { + id: '1', + name: 'Alice', + profile_picture: null, ...createFragmentRef('1', refetchQuery), }; - expectFragmentResults([{data: refetchedUser}]); + expectFragmentResults([{data: refetchingUser}]); }); - it('starts network request and suspends if refetch query is not fully cached', () => { + it('starts network request if refetch query is not fully cached and suspends if fragment has missing data', () => { const renderer = renderFragment(); const initialUser = { id: '1', @@ -2945,90 +2663,114 @@ describe.each([['New', useRefetchableFragmentInternal]])( }; expectFragmentResults([{data: refetchedUser}]); }); - }); - describe('store-only', () => { - beforeEach(() => { - fetchPolicy = 'store-only'; - }); + it("starts network request if refetch query is not fully cached and doesn't suspend if fragment doesn't have missing data", () => { + // Cache user with missing username + const refetchVariables = {id: '4', scale: 16}; + refetchQuery = createOperationDescriptor( + gqlRefetchQuery, + refetchVariables, + {force: true}, + ); + environment.commitPayload(refetchQuery, { + node: { + __typename: 'User', + id: '4', + name: 'Mark', + profile_picture: null, + }, + }); - it("doesn't start network request if refetch query is fully cached", () => { renderFragment(); commitSpy.mockClear(); TestRenderer.act(() => { refetch( - {id: '1'}, + {id: '4'}, {fetchPolicy, UNSTABLE_renderPolicy: renderPolicy}, ); }); - // Assert request is not started - const refetchVariables = {...variables}; - refetchQuery = createOperationDescriptor( - gqlRefetchQuery, - refetchVariables, - {force: true}, - ); + // Assert request is started expectRequestIsInFlight({ - inFlight: false, - requestCount: 0, + inFlight: true, + requestCount: 1, gqlRefetchQuery, refetchVariables, }); // Assert component renders immediately since data is cached const refetchingUser = { - id: '1', - name: 'Alice', + id: '4', + name: 'Mark', profile_picture: null, - ...createFragmentRef('1', refetchQuery), + ...createFragmentRef('4', refetchQuery), }; expectFragmentResults([{data: refetchingUser}]); }); + }); + + describe('renderPolicy: full', () => { + beforeEach(() => { + renderPolicy = 'full'; + }); - it("doesn't start network request if refetch query is not fully cached", () => { + it('starts network request if refetch query is fully cached', () => { renderFragment(); commitSpy.mockClear(); TestRenderer.act(() => { refetch( - {id: '4'}, + {id: '1'}, {fetchPolicy, UNSTABLE_renderPolicy: renderPolicy}, ); }); // Assert request is not started - const refetchVariables = {id: '4', scale: 16}; + const refetchVariables = {...variables}; refetchQuery = createOperationDescriptor( gqlRefetchQuery, refetchVariables, {force: true}, ); expectRequestIsInFlight({ - inFlight: false, - requestCount: 0, + inFlight: true, + requestCount: 1, gqlRefetchQuery, refetchVariables, }); - // Assert component renders immediately with empty data - expectFragmentResults([{data: null}]); + // Assert component renders immediately since data is cached + const refetchingUser = { + id: '1', + name: 'Alice', + profile_picture: null, + ...createFragmentRef('1', refetchQuery), + }; + expectFragmentResults([{data: refetchingUser}]); }); - it("doesn't use data from previous network fetch and releases previous query", () => { + it('starts network request if refetch query is not fully cached and suspends if fragment has missing data', () => { const renderer = renderFragment(); - commitSpy.mockClear(); + const initialUser = { + id: '1', + name: 'Alice', + profile_picture: null, + ...createFragmentRef('1', query), + }; + expectFragmentResults([{data: initialUser}]); + TestRenderer.act(() => { refetch( {id: '4'}, - { - fetchPolicy: 'network-only', - UNSTABLE_renderPolicy: renderPolicy, - }, + {fetchPolicy, UNSTABLE_renderPolicy: renderPolicy}, ); }); - // Assert initial request is started - let refetchVariables = {id: '4', scale: 16}; + // Assert that fragment is refetching with the right variables and + // suspends upon refetch + const refetchVariables = { + id: '4', + scale: 16, + }; refetchQuery = createOperationDescriptor( gqlRefetchQuery, refetchVariables, @@ -3038,8 +2780,6 @@ describe.each([['New', useRefetchableFragmentInternal]])( refetchVariables, refetchQuery, }); - // $FlowFixMe[method-unbinding] added when improving typing for this parameters - environment.executeWithSource.mockClear(); // Mock network response TestRenderer.act(() => { @@ -3068,71 +2808,86 @@ describe.each([['New', useRefetchableFragmentInternal]])( ...createFragmentRef('4', refetchQuery), }; expectFragmentResults([{data: refetchedUser}]); - // $FlowFixMe[method-unbinding] added when improving typing for this parameters - environment.retain.mockClear(); - release.mockClear(); + }); + + it("starts network request if refetch query is not fully cached and doesn't suspend if fragment doesn't have missing data", () => { + // Cache user with missing username + const refetchVariables = {id: '4', scale: 16}; + refetchQuery = createOperationDescriptor( + gqlRefetchQuery, + refetchVariables, + {force: true}, + ); + environment.commitPayload(refetchQuery, { + node: { + __typename: 'User', + id: '4', + name: 'Mark', + profile_picture: null, + }, + }); - // Call refetch again with store-only policy + const renderer = renderFragment(); commitSpy.mockClear(); TestRenderer.act(() => { refetch( - {id: '6'}, + {id: '4'}, {fetchPolicy, UNSTABLE_renderPolicy: renderPolicy}, ); }); - // Assert request is not started - refetchVariables = {id: '6', scale: 16}; - refetchQuery = createOperationDescriptor( - gqlRefetchQuery, - refetchVariables, - {force: true}, - ); - expectRequestIsInFlight({ - inFlight: false, - requestCount: 0, - gqlRefetchQuery, + // Assert component suspended + expectFragmentIsRefetching(renderer, { refetchVariables, + refetchQuery, + }); + + // Mock network response + TestRenderer.act(() => { + environment.mock.resolve(gqlRefetchQuery, { + data: { + node: { + __typename: 'User', + id: '4', + name: 'Mark', + profile_picture: { + uri: 'scale16', + }, + username: 'usermark', + }, + }, + }); }); - // Assert component renders immediately with empty data - expectFragmentResults([{data: null}]); - // FIXME should be released - expect(isOperationRetained(refetchQuery)).toBe(true); + // Assert fragment is rendered with new data + const refetchedUser = { + id: '4', + name: 'Mark', + profile_picture: { + uri: 'scale16', + }, + ...createFragmentRef('4', refetchQuery), + }; + expectFragmentResults([{data: refetchedUser}]); }); }); }); - describe('disposing', () => { - const unsubscribe = jest.fn<[], mixed>(); - jest.doMock('relay-runtime', () => { - const originalRuntime = jest.requireActual('relay-runtime'); - const originalInternal = originalRuntime.__internal; - return { - ...originalRuntime, - __internal: { - ...originalInternal, - fetchQueryDeduped: (...args) => { - const observable = originalInternal.fetchQueryDeduped(...args); - return Observable.create(sink => { - const sub = observable.subscribe(sink); - return () => { - unsubscribe(); - sub.unsubscribe(); - }; - }); - }, - }, - }; - }); + describe('network-only', () => { beforeEach(() => { - fetchPolicy = 'store-and-network'; - unsubscribe.mockClear(); + fetchPolicy = 'network-only'; }); - it('does not cancel ongoing request if environment changes', () => { - renderFragment(); - commitSpy.mockClear(); + it('starts network request and suspends if refetch query is fully cached', () => { + const renderer = renderFragment(); + const initialUser = { + id: '1', + name: 'Alice', + profile_picture: null, + ...createFragmentRef('1', query), + }; + expectFragmentResults([{data: initialUser}]); + TestRenderer.act(() => { refetch( {id: '1'}, @@ -3140,66 +2895,113 @@ describe.each([['New', useRefetchableFragmentInternal]])( ); }); - // Assert request is started - const refetchVariables = {id: '1', scale: 16}; + // Assert that fragment is refetching with the right variables and + // suspends upon refetch + const refetchVariables = { + ...variables, + }; refetchQuery = createOperationDescriptor( gqlRefetchQuery, refetchVariables, {force: true}, ); - expectRequestIsInFlight({ - inFlight: true, - requestCount: 1, - gqlRefetchQuery, + expectFragmentIsRefetching(renderer, { refetchVariables, + refetchQuery, }); - // Component renders immediately even though request is in flight - // since data is cached - const refetchingUser = { + // Mock network response + TestRenderer.act(() => { + environment.mock.resolve(gqlRefetchQuery, { + data: { + node: { + __typename: 'User', + id: '1', + name: 'Alice', + profile_picture: null, + username: 'useralice', + }, + }, + }); + }); + + // Assert fragment is rendered with new data + const refetchedUser = { + ...initialUser, + ...createFragmentRef('1', refetchQuery), + }; + expectFragmentResults([{data: refetchedUser}]); + }); + + it('starts network request and suspends if refetch query is not fully cached', () => { + const renderer = renderFragment(); + const initialUser = { id: '1', name: 'Alice', profile_picture: null, - ...createFragmentRef('1', refetchQuery), + ...createFragmentRef('1', query), }; - expectFragmentResults([{data: refetchingUser}]); + expectFragmentResults([{data: initialUser}]); - // Set new environment - const newEnvironment = createMockEnvironment(); - newEnvironment.commitPayload(query, { - node: { - __typename: 'User', - id: '1', - name: 'Alice in a different env', - username: 'useralice', - profile_picture: null, - }, - }); TestRenderer.act(() => { - setEnvironment(newEnvironment); + refetch( + {id: '4'}, + {fetchPolicy, UNSTABLE_renderPolicy: renderPolicy}, + ); }); - // Assert request is not cancelled, since useQueryLoader does not - // cancel network requests when disposing query refs. - expect(unsubscribe).toBeCalledTimes(0); - expectRequestIsInFlight({ - inFlight: true, - requestCount: 1, + // Assert that fragment is refetching with the right variables and + // suspends upon refetch + const refetchVariables = { + id: '4', + scale: 16, + }; + refetchQuery = createOperationDescriptor( gqlRefetchQuery, refetchVariables, + {force: true}, + ); + expectFragmentIsRefetching(renderer, { + refetchVariables, + refetchQuery, }); - // Assert newly rendered data - const expectedUser = { - id: '1', - name: 'Alice in a different env', - profile_picture: null, - ...createFragmentRef('1', query), + // Mock network response + TestRenderer.act(() => { + environment.mock.resolve(gqlRefetchQuery, { + data: { + node: { + __typename: 'User', + id: '4', + name: 'Mark', + profile_picture: { + uri: 'scale16', + }, + username: 'usermark', + }, + }, + }); + }); + + // Assert fragment is rendered with new data + const refetchedUser = { + id: '4', + name: 'Mark', + profile_picture: { + uri: 'scale16', + }, + ...createFragmentRef('4', refetchQuery), }; - expectFragmentResults([{data: expectedUser}]); + expectFragmentResults([{data: refetchedUser}]); + }); + }); + + describe('store-only', () => { + beforeEach(() => { + fetchPolicy = 'store-only'; }); - it('does not cancel ongoing request if fragment ref changes', () => { + it("doesn't start network request if refetch query is fully cached", () => { renderFragment(); commitSpy.mockClear(); TestRenderer.act(() => { @@ -3209,22 +3011,21 @@ describe.each([['New', useRefetchableFragmentInternal]])( ); }); - // Assert request is started - const refetchVariables = {id: '1', scale: 16}; + // Assert request is not started + const refetchVariables = {...variables}; refetchQuery = createOperationDescriptor( gqlRefetchQuery, refetchVariables, {force: true}, ); expectRequestIsInFlight({ - inFlight: true, - requestCount: 1, + inFlight: false, + requestCount: 0, gqlRefetchQuery, refetchVariables, }); - // Component renders immediately even though request is in flight - // since data is cached + // Assert component renders immediately since data is cached const refetchingUser = { id: '1', name: 'Alice', @@ -3232,828 +3033,1076 @@ describe.each([['New', useRefetchableFragmentInternal]])( ...createFragmentRef('1', refetchQuery), }; expectFragmentResults([{data: refetchingUser}]); + }); - // Pass new parent fragment ref with different variables - const newVariables = {...variables, scale: 32}; - const newQuery = createOperationDescriptor(gqlQuery, newVariables, { - force: true, - }); - environment.commitPayload(newQuery, { - node: { - __typename: 'User', - id: '1', - name: 'Alice', - username: 'useralice', - profile_picture: { - uri: 'uri32', - }, - }, - }); + it("doesn't start network request if refetch query is not fully cached", () => { + renderFragment(); + commitSpy.mockClear(); TestRenderer.act(() => { - setOwner(newQuery); + refetch( + {id: '4'}, + {fetchPolicy, UNSTABLE_renderPolicy: renderPolicy}, + ); }); - // Assert request is not cancelled, since useQueryLoader does not - // cancel network requests when disposing query refs. - expect(unsubscribe).toBeCalledTimes(0); + // Assert request is not started + const refetchVariables = {id: '4', scale: 16}; + refetchQuery = createOperationDescriptor( + gqlRefetchQuery, + refetchVariables, + {force: true}, + ); expectRequestIsInFlight({ - inFlight: true, - requestCount: 1, + inFlight: false, + requestCount: 0, gqlRefetchQuery, refetchVariables, }); - // Assert newly rendered data - const expectedUser = { - id: '1', - name: 'Alice', - profile_picture: { - uri: 'uri32', - }, - ...createFragmentRef('1', newQuery), - }; - expectFragmentResults([{data: expectedUser}]); + // Assert component renders immediately with empty data + expectFragmentResults([{data: null}]); }); - it('does not cancel ongoing request on unmount when refetch suspends', () => { + it("doesn't use data from previous network fetch and releases previous query", () => { const renderer = renderFragment(); commitSpy.mockClear(); TestRenderer.act(() => { refetch( - {id: '2'}, - {fetchPolicy, UNSTABLE_renderPolicy: renderPolicy}, + {id: '4'}, + { + fetchPolicy: 'network-only', + UNSTABLE_renderPolicy: renderPolicy, + }, ); }); - // Assert request is started - const refetchVariables = {id: '2', scale: 16}; + // Assert initial request is started + let refetchVariables = {id: '4', scale: 16}; refetchQuery = createOperationDescriptor( gqlRefetchQuery, refetchVariables, {force: true}, ); - expectFragmentIsRefetching(renderer, { refetchVariables, refetchQuery, }); + // $FlowFixMe[method-unbinding] added when improving typing for this parameters + environment.executeWithSource.mockClear(); + // Mock network response TestRenderer.act(() => { - renderer.unmount(); + environment.mock.resolve(gqlRefetchQuery, { + data: { + node: { + __typename: 'User', + id: '4', + name: 'Mark', + profile_picture: { + uri: 'scale16', + }, + username: 'usermark', + }, + }, + }); }); - // Assert request is not cancelled. useQueryLoader does not cancel - // network requests when disposing query refs. - expect(unsubscribe).toBeCalledTimes(0); - }); + // Assert fragment is rendered with new data + const refetchedUser = { + id: '4', + name: 'Mark', + profile_picture: { + uri: 'scale16', + }, + ...createFragmentRef('4', refetchQuery), + }; + expectFragmentResults([{data: refetchedUser}]); + // $FlowFixMe[method-unbinding] added when improving typing for this parameters + environment.retain.mockClear(); + release.mockClear(); - it('does not cancel ongoing request on unmount when refetch does not suspend', () => { - const renderer = renderFragment(); + // Call refetch again with store-only policy commitSpy.mockClear(); TestRenderer.act(() => { refetch( - {id: '1'}, + {id: '6'}, {fetchPolicy, UNSTABLE_renderPolicy: renderPolicy}, ); }); - // Assert request is started - const refetchVariables = {id: '1', scale: 16}; + // Assert request is not started + refetchVariables = {id: '6', scale: 16}; refetchQuery = createOperationDescriptor( gqlRefetchQuery, refetchVariables, {force: true}, ); expectRequestIsInFlight({ - inFlight: true, - requestCount: 1, + inFlight: false, + requestCount: 0, gqlRefetchQuery, refetchVariables, }); - // Component renders immediately even though request is in flight - // since data is cached - const refetchingUser = { + // Assert component renders immediately with empty data + expectFragmentResults([{data: null}]); + // FIXME should be released + expect(isOperationRetained(refetchQuery)).toBe(true); + }); + }); + }); + + describe('disposing', () => { + const unsubscribe = jest.fn<[], mixed>(); + jest.doMock('relay-runtime', () => { + const originalRuntime = jest.requireActual('relay-runtime'); + const originalInternal = originalRuntime.__internal; + return { + ...originalRuntime, + __internal: { + ...originalInternal, + fetchQueryDeduped: (...args) => { + const observable = originalInternal.fetchQueryDeduped(...args); + return Observable.create(sink => { + const sub = observable.subscribe(sink); + return () => { + unsubscribe(); + sub.unsubscribe(); + }; + }); + }, + }, + }; + }); + beforeEach(() => { + fetchPolicy = 'store-and-network'; + unsubscribe.mockClear(); + }); + + it('does not cancel ongoing request if environment changes', () => { + renderFragment(); + commitSpy.mockClear(); + TestRenderer.act(() => { + refetch( + {id: '1'}, + {fetchPolicy, UNSTABLE_renderPolicy: renderPolicy}, + ); + }); + + // Assert request is started + const refetchVariables = {id: '1', scale: 16}; + refetchQuery = createOperationDescriptor( + gqlRefetchQuery, + refetchVariables, + {force: true}, + ); + expectRequestIsInFlight({ + inFlight: true, + requestCount: 1, + gqlRefetchQuery, + refetchVariables, + }); + + // Component renders immediately even though request is in flight + // since data is cached + const refetchingUser = { + id: '1', + name: 'Alice', + profile_picture: null, + ...createFragmentRef('1', refetchQuery), + }; + expectFragmentResults([{data: refetchingUser}]); + + // Set new environment + const newEnvironment = createMockEnvironment(); + newEnvironment.commitPayload(query, { + node: { + __typename: 'User', id: '1', - name: 'Alice', + name: 'Alice in a different env', + username: 'useralice', profile_picture: null, - ...createFragmentRef('1', refetchQuery), - }; - expectFragmentResults([{data: refetchingUser}]); + }, + }); + TestRenderer.act(() => { + setEnvironment(newEnvironment); + }); - TestRenderer.act(() => { - renderer.unmount(); - }); + // Assert request is not cancelled, since useQueryLoader does not + // cancel network requests when disposing query refs. + expect(unsubscribe).toBeCalledTimes(0); + expectRequestIsInFlight({ + inFlight: true, + requestCount: 1, + gqlRefetchQuery, + refetchVariables, + }); + + // Assert newly rendered data + const expectedUser = { + id: '1', + name: 'Alice in a different env', + profile_picture: null, + ...createFragmentRef('1', query), + }; + expectFragmentResults([{data: expectedUser}]); + }); - // Assert request is not cancelled. useQueryLoader does not cancel - // network requests when disposing query refs. - expect(unsubscribe).toBeCalledTimes(0); + it('does not cancel ongoing request if fragment ref changes', () => { + renderFragment(); + commitSpy.mockClear(); + TestRenderer.act(() => { + refetch( + {id: '1'}, + {fetchPolicy, UNSTABLE_renderPolicy: renderPolicy}, + ); }); - it('disposes ongoing request if it is manually disposed when refetch suspends', () => { - const renderer = renderFragment(); - commitSpy.mockClear(); - let disposable; - TestRenderer.act(() => { - disposable = refetch( - {id: '2'}, - {fetchPolicy, UNSTABLE_renderPolicy: renderPolicy}, - ); - }); + // Assert request is started + const refetchVariables = {id: '1', scale: 16}; + refetchQuery = createOperationDescriptor( + gqlRefetchQuery, + refetchVariables, + {force: true}, + ); + expectRequestIsInFlight({ + inFlight: true, + requestCount: 1, + gqlRefetchQuery, + refetchVariables, + }); - // Assert request is started - const refetchVariables = {id: '2', scale: 16}; - refetchQuery = createOperationDescriptor( - gqlRefetchQuery, - refetchVariables, - {force: true}, + // Component renders immediately even though request is in flight + // since data is cached + const refetchingUser = { + id: '1', + name: 'Alice', + profile_picture: null, + ...createFragmentRef('1', refetchQuery), + }; + expectFragmentResults([{data: refetchingUser}]); + + // Pass new parent fragment ref with different variables + const newVariables = {...variables, scale: 32}; + const newQuery = createOperationDescriptor(gqlQuery, newVariables, { + force: true, + }); + environment.commitPayload(newQuery, { + node: { + __typename: 'User', + id: '1', + name: 'Alice', + username: 'useralice', + profile_picture: { + uri: 'uri32', + }, + }, + }); + TestRenderer.act(() => { + setOwner(newQuery); + }); + + // Assert request is not cancelled, since useQueryLoader does not + // cancel network requests when disposing query refs. + expect(unsubscribe).toBeCalledTimes(0); + expectRequestIsInFlight({ + inFlight: true, + requestCount: 1, + gqlRefetchQuery, + refetchVariables, + }); + + // Assert newly rendered data + const expectedUser = { + id: '1', + name: 'Alice', + profile_picture: { + uri: 'uri32', + }, + ...createFragmentRef('1', newQuery), + }; + expectFragmentResults([{data: expectedUser}]); + }); + + it('does not cancel ongoing request on unmount when refetch suspends', () => { + const renderer = renderFragment(); + commitSpy.mockClear(); + TestRenderer.act(() => { + refetch( + {id: '2'}, + {fetchPolicy, UNSTABLE_renderPolicy: renderPolicy}, + ); + }); + + // Assert request is started + const refetchVariables = {id: '2', scale: 16}; + refetchQuery = createOperationDescriptor( + gqlRefetchQuery, + refetchVariables, + {force: true}, + ); + + expectFragmentIsRefetching(renderer, { + refetchVariables, + refetchQuery, + }); + + TestRenderer.act(() => { + renderer.unmount(); + }); + + // Assert request is not cancelled. useQueryLoader does not cancel + // network requests when disposing query refs. + expect(unsubscribe).toBeCalledTimes(0); + }); + + it('does not cancel ongoing request on unmount when refetch does not suspend', () => { + const renderer = renderFragment(); + commitSpy.mockClear(); + TestRenderer.act(() => { + refetch( + {id: '1'}, + {fetchPolicy, UNSTABLE_renderPolicy: renderPolicy}, + ); + }); + + // Assert request is started + const refetchVariables = {id: '1', scale: 16}; + refetchQuery = createOperationDescriptor( + gqlRefetchQuery, + refetchVariables, + {force: true}, + ); + expectRequestIsInFlight({ + inFlight: true, + requestCount: 1, + gqlRefetchQuery, + refetchVariables, + }); + + // Component renders immediately even though request is in flight + // since data is cached + const refetchingUser = { + id: '1', + name: 'Alice', + profile_picture: null, + ...createFragmentRef('1', refetchQuery), + }; + expectFragmentResults([{data: refetchingUser}]); + + TestRenderer.act(() => { + renderer.unmount(); + }); + + // Assert request is not cancelled. useQueryLoader does not cancel + // network requests when disposing query refs. + expect(unsubscribe).toBeCalledTimes(0); + }); + + it('disposes ongoing request if it is manually disposed when refetch suspends', () => { + const renderer = renderFragment(); + commitSpy.mockClear(); + let disposable; + TestRenderer.act(() => { + disposable = refetch( + {id: '2'}, + {fetchPolicy, UNSTABLE_renderPolicy: renderPolicy}, ); - expectFragmentIsRefetching(renderer, { - refetchVariables, - refetchQuery, - }); + }); - TestRenderer.act(() => { - disposable && disposable.dispose(); - jest.runAllImmediates(); - }); + // Assert request is started + const refetchVariables = {id: '2', scale: 16}; + refetchQuery = createOperationDescriptor( + gqlRefetchQuery, + refetchVariables, + {force: true}, + ); + expectFragmentIsRefetching(renderer, { + refetchVariables, + refetchQuery, + }); - // The request is not able to be cancelled - // since the new query reference is never able to - // commit, and we only dispose of network requests - // in the commit phase for concurrent safety. - // From the perspective of React, the refetch never - // occurred and a new query reference was not committed, - // so there is nothing to cancel. - expect(unsubscribe).toBeCalledTimes(0); - expectRequestIsInFlight({ - inFlight: true, - requestCount: 1, - gqlRefetchQuery, - refetchVariables, - }); + TestRenderer.act(() => { + disposable && disposable.dispose(); + jest.runAllImmediates(); + }); - // Assert that when the refetch is disposed we reset to rendering the - // original data before the refetch - const initialUser = { - id: '1', - name: 'Alice', - profile_picture: null, - ...createFragmentRef('1', query), - }; - expectFragmentResults([{data: initialUser}]); + // The request is not able to be cancelled + // since the new query reference is never able to + // commit, and we only dispose of network requests + // in the commit phase for concurrent safety. + // From the perspective of React, the refetch never + // occurred and a new query reference was not committed, + // so there is nothing to cancel. + expect(unsubscribe).toBeCalledTimes(0); + expectRequestIsInFlight({ + inFlight: true, + requestCount: 1, + gqlRefetchQuery, + refetchVariables, }); - it('disposes ongoing request if it is manually disposed when refetch does not suspend', () => { - renderFragment(); - commitSpy.mockClear(); - let disposable; - TestRenderer.act(() => { - disposable = refetch( - {id: '1'}, - {fetchPolicy, UNSTABLE_renderPolicy: renderPolicy}, - ); - }); + // Assert that when the refetch is disposed we reset to rendering the + // original data before the refetch + const initialUser = { + id: '1', + name: 'Alice', + profile_picture: null, + ...createFragmentRef('1', query), + }; + expectFragmentResults([{data: initialUser}]); + }); - // Assert request is started - const refetchVariables = {id: '1', scale: 16}; - refetchQuery = createOperationDescriptor( - gqlRefetchQuery, - refetchVariables, - {force: true}, + it('disposes ongoing request if it is manually disposed when refetch does not suspend', () => { + renderFragment(); + commitSpy.mockClear(); + let disposable; + TestRenderer.act(() => { + disposable = refetch( + {id: '1'}, + {fetchPolicy, UNSTABLE_renderPolicy: renderPolicy}, ); - expectRequestIsInFlight({ - inFlight: true, - requestCount: 1, - gqlRefetchQuery, - refetchVariables, - }); - - // Component renders immediately even though request is in flight - // since data is cached - const refetchingUser = { - id: '1', - name: 'Alice', - profile_picture: null, - ...createFragmentRef('1', refetchQuery), - }; - expectFragmentResults([{data: refetchingUser}]); + }); - TestRenderer.act(() => { - disposable && disposable.dispose(); - jest.runAllImmediates(); - }); + // Assert request is started + const refetchVariables = {id: '1', scale: 16}; + refetchQuery = createOperationDescriptor( + gqlRefetchQuery, + refetchVariables, + {force: true}, + ); + expectRequestIsInFlight({ + inFlight: true, + requestCount: 1, + gqlRefetchQuery, + refetchVariables, + }); - // Assert request is not cancelled. useQueryLoader does not cancel - // network requests when disposing query refs. - expect(unsubscribe).toBeCalledTimes(0); + // Component renders immediately even though request is in flight + // since data is cached + const refetchingUser = { + id: '1', + name: 'Alice', + profile_picture: null, + ...createFragmentRef('1', refetchQuery), + }; + expectFragmentResults([{data: refetchingUser}]); - // Assert that when the refetch is disposed we reset to rendering the - // original data before the refetch - const initialUser = { - id: '1', - name: 'Alice', - profile_picture: null, - ...createFragmentRef('1', query), - }; - expectFragmentResults([{data: initialUser}]); + TestRenderer.act(() => { + disposable && disposable.dispose(); + jest.runAllImmediates(); }); + + // Assert request is not cancelled. useQueryLoader does not cancel + // network requests when disposing query refs. + expect(unsubscribe).toBeCalledTimes(0); + + // Assert that when the refetch is disposed we reset to rendering the + // original data before the refetch + const initialUser = { + id: '1', + name: 'Alice', + profile_picture: null, + ...createFragmentRef('1', query), + }; + expectFragmentResults([{data: initialUser}]); }); + }); - describe('refetching @fetchable types', () => { - beforeEach(() => { - // $FlowFixMe[prop-missing] - // $FlowFixMe[incompatible-type-arg] - gqlFragment = graphql` - fragment useRefetchableFragmentNodeTest1Fragment on NonNodeStory - @refetchable( - queryName: "useRefetchableFragmentNodeTest1FragmentRefetchQuery" - ) { - actor { - name - } + describe('refetching @fetchable types', () => { + beforeEach(() => { + // $FlowFixMe[prop-missing] + // $FlowFixMe[incompatible-type-arg] + gqlFragment = graphql` + fragment useRefetchableFragmentNodeTest1Fragment on NonNodeStory + @refetchable( + queryName: "useRefetchableFragmentNodeTest1FragmentRefetchQuery" + ) { + actor { + name } - `; + } + `; - gqlQuery = graphql` - query useRefetchableFragmentNodeTest1Query($id: ID!) { - nonNodeStory(id: $id) { - ...useRefetchableFragmentNodeTest1Fragment - } + gqlQuery = graphql` + query useRefetchableFragmentNodeTest1Query($id: ID!) { + nonNodeStory(id: $id) { + ...useRefetchableFragmentNodeTest1Fragment } - `; + } + `; - variables = {id: 'a'}; - gqlRefetchQuery = require('./__generated__/useRefetchableFragmentNodeTest1FragmentRefetchQuery.graphql'); + variables = {id: 'a'}; + gqlRefetchQuery = require('./__generated__/useRefetchableFragmentNodeTest1FragmentRefetchQuery.graphql'); - invariant( - gqlFragment.metadata?.refetch?.operation === gqlRefetchQuery, - 'useRefetchableFragment-test: Expected refetchable fragment metadata to contain operation.', - ); + invariant( + gqlFragment.metadata?.refetch?.operation === gqlRefetchQuery, + 'useRefetchableFragment-test: Expected refetchable fragment metadata to contain operation.', + ); - refetchQuery = createOperationDescriptor(gqlRefetchQuery, variables, { - force: true, - }); - query = createOperationDescriptor(gqlQuery, variables, {force: true}); - - environment.commitPayload(query, { - nonNodeStory: { - __typename: 'NonNodeStory', - id: 'a', - actor: {name: 'Alice', __typename: 'User', id: '1'}, - fetch_id: 'fetch:a', - }, - }); + refetchQuery = createOperationDescriptor(gqlRefetchQuery, variables, { + force: true, }); + query = createOperationDescriptor(gqlQuery, variables, {force: true}); - it('refetches new variables correctly when refetching new id', () => { - const renderer = renderFragment(); - const initialUser = { - actor: {name: 'Alice'}, + environment.commitPayload(query, { + nonNodeStory: { + __typename: 'NonNodeStory', + id: 'a', + actor: {name: 'Alice', __typename: 'User', id: '1'}, fetch_id: 'fetch:a', - }; - expectFragmentResults([ - { - data: initialUser, - }, - ]); - - TestRenderer.act(() => { - refetch({id: 'fetch:b'}); - }); - - // Assert that fragment is refetching with the right variables and - // suspends upon refetch - const refetchVariables = { - id: 'fetch:b', - }; - refetchQuery = createOperationDescriptor( - gqlRefetchQuery, - refetchVariables, - {force: true}, - ); - expectFragmentIsRefetching(renderer, { - refetchVariables, - refetchQuery, - }); - - // Mock network response - TestRenderer.act(() => { - environment.mock.resolve(gqlRefetchQuery, { - data: { - fetch__NonNodeStory: { - __typename: 'NonNodeStory', - id: 'b', - actor: {name: 'Mark', __typename: 'User', id: '4'}, - fetch_id: 'fetch:b', - }, - }, - }); - }); - - // Assert fragment is rendered with new data - const refetchedUser = { - actor: {name: 'Mark'}, - fetch_id: 'fetch:b', - }; - expectFragmentResults([{data: refetchedUser}]); - expect(isOperationRetained(refetchQuery)).toBe(true); + }, }); + }); - it('refetches new variables correctly when refetching same id', () => { - const renderer = renderFragment(); - const initialUser = { - actor: {name: 'Alice'}, - fetch_id: 'fetch:a', - }; - expectFragmentResults([ - { - data: initialUser, - }, - ]); - - TestRenderer.act(() => { - refetch({}, {fetchPolicy: 'network-only'}); - }); - - // Assert that fragment is refetching with the right variables and - // suspends upon refetch - const refetchVariables = { - id: 'fetch:a', - }; - refetchQuery = createOperationDescriptor( - gqlRefetchQuery, - refetchVariables, - {force: true}, - ); - expectFragmentIsRefetching(renderer, { - refetchVariables, - refetchQuery, - }); + it('refetches new variables correctly when refetching new id', () => { + const renderer = renderFragment(); + const initialUser = { + actor: {name: 'Alice'}, + fetch_id: 'fetch:a', + }; + expectFragmentResults([ + { + data: initialUser, + }, + ]); - // Mock network response - TestRenderer.act(() => { - environment.mock.resolve(gqlRefetchQuery, { - data: { - fetch__NonNodeStory: { - __typename: 'NonNodeStory', - id: 'a', - actor: {name: 'Alice (updated)', __typename: 'User', id: '1'}, - fetch_id: 'fetch:a', - }, - }, - }); - }); + TestRenderer.act(() => { + refetch({id: 'fetch:b'}); + }); - // Assert fragment is rendered with new data - const refetchedUser = { - actor: {name: 'Alice (updated)'}, - fetch_id: 'fetch:a', - }; - expectFragmentResults([{data: refetchedUser}]); - expect(isOperationRetained(refetchQuery)).toBe(true); + // Assert that fragment is refetching with the right variables and + // suspends upon refetch + const refetchVariables = { + id: 'fetch:b', + }; + refetchQuery = createOperationDescriptor( + gqlRefetchQuery, + refetchVariables, + {force: true}, + ); + expectFragmentIsRefetching(renderer, { + refetchVariables, + refetchQuery, }); - it('refetches new variables correctly when refetching after the id from the parent has changed', () => { - // add data for second query - const query2 = createOperationDescriptor( - gqlQuery, - { - id: 'b', - }, - {force: true}, - ); - environment.commitPayload(query2, { - nonNodeStory: { - __typename: 'NonNodeStory', - id: 'b', - actor: {name: 'Zuck', __typename: 'User', id: '4'}, - fetch_id: 'fetch:b', + // Mock network response + TestRenderer.act(() => { + environment.mock.resolve(gqlRefetchQuery, { + data: { + fetch__NonNodeStory: { + __typename: 'NonNodeStory', + id: 'b', + actor: {name: 'Mark', __typename: 'User', id: '4'}, + fetch_id: 'fetch:b', + }, }, }); + }); - const renderer = renderFragment(); - const initialUser = { - actor: {name: 'Alice'}, - fetch_id: 'fetch:a', - }; - expectFragmentResults([ - { - data: initialUser, - }, - ]); - - TestRenderer.act(() => { - setOwner(query2); - }); + // Assert fragment is rendered with new data + const refetchedUser = { + actor: {name: 'Mark'}, + fetch_id: 'fetch:b', + }; + expectFragmentResults([{data: refetchedUser}]); + expect(isOperationRetained(refetchQuery)).toBe(true); + }); - const nextUser = { - actor: {name: 'Zuck'}, - fetch_id: 'fetch:b', - }; - expectFragmentResults([ - { - data: nextUser, - }, - ]); - TestRenderer.act(() => { - refetch({}, {fetchPolicy: 'network-only'}); - }); + it('refetches new variables correctly when refetching same id', () => { + const renderer = renderFragment(); + const initialUser = { + actor: {name: 'Alice'}, + fetch_id: 'fetch:a', + }; + expectFragmentResults([ + { + data: initialUser, + }, + ]); - // Assert that fragment is refetching with the right variables and - // suspends upon refetch - const refetchVariables = { - id: 'fetch:b', - }; - refetchQuery = createOperationDescriptor( - gqlRefetchQuery, - refetchVariables, - {force: true}, - ); - expectFragmentIsRefetching(renderer, { - refetchVariables, - refetchQuery, - }); + TestRenderer.act(() => { + refetch({}, {fetchPolicy: 'network-only'}); + }); - // Mock network response - TestRenderer.act(() => { - environment.mock.resolve(gqlRefetchQuery, { - data: { - fetch__NonNodeStory: { - __typename: 'NonNodeStory', - id: 'b', - actor: {name: 'Zuck (updated)', __typename: 'User', id: '4'}, - fetch_id: 'fetch:b', - }, + // Assert that fragment is refetching with the right variables and + // suspends upon refetch + const refetchVariables = { + id: 'fetch:a', + }; + refetchQuery = createOperationDescriptor( + gqlRefetchQuery, + refetchVariables, + {force: true}, + ); + expectFragmentIsRefetching(renderer, { + refetchVariables, + refetchQuery, + }); + + // Mock network response + TestRenderer.act(() => { + environment.mock.resolve(gqlRefetchQuery, { + data: { + fetch__NonNodeStory: { + __typename: 'NonNodeStory', + id: 'a', + actor: {name: 'Alice (updated)', __typename: 'User', id: '1'}, + fetch_id: 'fetch:a', }, - }); + }, }); + }); - // Assert fragment is rendered with new data - const refetchedUser = { - actor: {name: 'Zuck (updated)'}, + // Assert fragment is rendered with new data + const refetchedUser = { + actor: {name: 'Alice (updated)'}, + fetch_id: 'fetch:a', + }; + expectFragmentResults([{data: refetchedUser}]); + expect(isOperationRetained(refetchQuery)).toBe(true); + }); + + it('refetches new variables correctly when refetching after the id from the parent has changed', () => { + // add data for second query + const query2 = createOperationDescriptor( + gqlQuery, + { + id: 'b', + }, + {force: true}, + ); + environment.commitPayload(query2, { + nonNodeStory: { + __typename: 'NonNodeStory', + id: 'b', + actor: {name: 'Zuck', __typename: 'User', id: '4'}, fetch_id: 'fetch:b', - }; - expectFragmentResults([{data: refetchedUser}]); - expect(isOperationRetained(refetchQuery)).toBe(true); + }, }); - }); - describe('when id variable has a different variable name in original query', () => { - beforeEach(() => { - graphql` - fragment useRefetchableFragmentNodeTest2Fragment on User { - username - } - `; - // $FlowFixMe[incompatible-type-arg] - gqlFragment = graphql` - fragment useRefetchableFragmentNodeTest3Fragment on User - @refetchable( - queryName: "useRefetchableFragmentNodeTest3FragmentRefetchQuery" - ) { - id - name - profile_picture(scale: $scale) { - uri - } - ...useRefetchableFragmentNodeTest2Fragment - } - `; - gqlQuery = graphql` - query useRefetchableFragmentNodeTest2Query( - $nodeID: ID! - $scale: Float! - ) { - node(id: $nodeID) { - ...useRefetchableFragmentNodeTest3Fragment - } - } - `; - gqlRefetchQuery = require('./__generated__/useRefetchableFragmentNodeTest3FragmentRefetchQuery.graphql'); + const renderer = renderFragment(); + const initialUser = { + actor: {name: 'Alice'}, + fetch_id: 'fetch:a', + }; + expectFragmentResults([ + { + data: initialUser, + }, + ]); - variables = {nodeID: '1', scale: 16}; + TestRenderer.act(() => { + setOwner(query2); + }); - invariant( - gqlFragment.metadata?.refetch?.operation === gqlRefetchQuery, - 'useRefetchableFragment-test: Expected refetchable fragment metadata to contain operation.', - ); + const nextUser = { + actor: {name: 'Zuck'}, + fetch_id: 'fetch:b', + }; + expectFragmentResults([ + { + data: nextUser, + }, + ]); + TestRenderer.act(() => { + refetch({}, {fetchPolicy: 'network-only'}); + }); - query = createOperationDescriptor(gqlQuery, variables, {force: true}); - refetchQuery = createOperationDescriptor(gqlRefetchQuery, variables, { - force: true, - }); + // Assert that fragment is refetching with the right variables and + // suspends upon refetch + const refetchVariables = { + id: 'fetch:b', + }; + refetchQuery = createOperationDescriptor( + gqlRefetchQuery, + refetchVariables, + {force: true}, + ); + expectFragmentIsRefetching(renderer, { + refetchVariables, + refetchQuery, + }); - environment.commitPayload(query, { - node: { - __typename: 'User', - id: '1', - name: 'Alice', - username: 'useralice', - profile_picture: null, + // Mock network response + TestRenderer.act(() => { + environment.mock.resolve(gqlRefetchQuery, { + data: { + fetch__NonNodeStory: { + __typename: 'NonNodeStory', + id: 'b', + actor: {name: 'Zuck (updated)', __typename: 'User', id: '4'}, + fetch_id: 'fetch:b', + }, }, }); }); - it('refetches new variables correctly when refetching new id', () => { - const renderer = renderFragment(); - const initialUser = { - id: '1', - name: 'Alice', - profile_picture: null, - ...createFragmentRef( - '1', - query, - 'useRefetchableFragmentNodeTest2Fragment', - ), - }; - expectFragmentResults([ - { - data: initialUser, - }, - ]); + // Assert fragment is rendered with new data + const refetchedUser = { + actor: {name: 'Zuck (updated)'}, + fetch_id: 'fetch:b', + }; + expectFragmentResults([{data: refetchedUser}]); + expect(isOperationRetained(refetchQuery)).toBe(true); + }); + }); - TestRenderer.act(() => { - refetch({id: '4'}); - }); + describe('when id variable has a different variable name in original query', () => { + beforeEach(() => { + graphql` + fragment useRefetchableFragmentNodeTest2Fragment on User { + username + } + `; + // $FlowFixMe[incompatible-type-arg] + gqlFragment = graphql` + fragment useRefetchableFragmentNodeTest3Fragment on User + @refetchable( + queryName: "useRefetchableFragmentNodeTest3FragmentRefetchQuery" + ) { + id + name + profile_picture(scale: $scale) { + uri + } + ...useRefetchableFragmentNodeTest2Fragment + } + `; + gqlQuery = graphql` + query useRefetchableFragmentNodeTest2Query( + $nodeID: ID! + $scale: Float! + ) { + node(id: $nodeID) { + ...useRefetchableFragmentNodeTest3Fragment + @dangerously_unaliased_fixme + } + } + `; + gqlRefetchQuery = require('./__generated__/useRefetchableFragmentNodeTest3FragmentRefetchQuery.graphql'); - // Assert that fragment is refetching with the right variables and - // suspends upon refetch - const refetchVariables = { - id: '4', - scale: 16, - }; - refetchQuery = createOperationDescriptor( - gqlRefetchQuery, - refetchVariables, - {force: true}, - ); - expectFragmentIsRefetching(renderer, { - refetchVariables, - refetchQuery, - }); + variables = {nodeID: '1', scale: 16}; - // Mock network response - TestRenderer.act(() => { - environment.mock.resolve(gqlRefetchQuery, { - data: { - node: { - __typename: 'User', - id: '4', - name: 'Mark', - profile_picture: { - uri: 'scale16', - }, - username: 'usermark', - }, - }, - }); - }); + invariant( + gqlFragment.metadata?.refetch?.operation === gqlRefetchQuery, + 'useRefetchableFragment-test: Expected refetchable fragment metadata to contain operation.', + ); - // Assert fragment is rendered with new data - const refetchedUser = { - id: '4', - name: 'Mark', - profile_picture: { - uri: 'scale16', - }, - ...createFragmentRef( - '4', - refetchQuery, - 'useRefetchableFragmentNodeTest2Fragment', - ), - }; - expectFragmentResults([{data: refetchedUser}]); - expect(isOperationRetained(refetchQuery)).toBe(true); + query = createOperationDescriptor(gqlQuery, variables, {force: true}); + refetchQuery = createOperationDescriptor(gqlRefetchQuery, variables, { + force: true, }); - it('refetches new variables correctly when refetching same id', () => { - const renderer = renderFragment(); - const initialUser = { + environment.commitPayload(query, { + node: { + __typename: 'User', id: '1', name: 'Alice', + username: 'useralice', profile_picture: null, - ...createFragmentRef( - '1', - query, - 'useRefetchableFragmentNodeTest2Fragment', - ), - }; - expectFragmentResults([ - { - data: initialUser, - }, - ]); + }, + }); + }); - TestRenderer.act(() => { - refetch({scale: 32}); - }); + it('refetches new variables correctly when refetching new id', () => { + const renderer = renderFragment(); + const initialUser = { + id: '1', + name: 'Alice', + profile_picture: null, + ...createFragmentRef( + '1', + query, + 'useRefetchableFragmentNodeTest2Fragment', + ), + }; + expectFragmentResults([ + { + data: initialUser, + }, + ]); - // Assert that fragment is refetching with the right variables and - // suspends upon refetch - const refetchVariables = { - id: '1', - scale: 32, - }; - refetchQuery = createOperationDescriptor( - gqlRefetchQuery, - refetchVariables, - {force: true}, - ); - expectFragmentIsRefetching(renderer, { - refetchVariables, - refetchQuery, - }); + TestRenderer.act(() => { + refetch({id: '4'}); + }); - // Mock network response - TestRenderer.act(() => { - environment.mock.resolve(gqlRefetchQuery, { - data: { - node: { - __typename: 'User', - id: '1', - name: 'Alice', - profile_picture: { - uri: 'scale32', - }, - username: 'useralice', + // Assert that fragment is refetching with the right variables and + // suspends upon refetch + const refetchVariables = { + id: '4', + scale: 16, + }; + refetchQuery = createOperationDescriptor( + gqlRefetchQuery, + refetchVariables, + {force: true}, + ); + expectFragmentIsRefetching(renderer, { + refetchVariables, + refetchQuery, + }); + + // Mock network response + TestRenderer.act(() => { + environment.mock.resolve(gqlRefetchQuery, { + data: { + node: { + __typename: 'User', + id: '4', + name: 'Mark', + profile_picture: { + uri: 'scale16', }, + username: 'usermark', }, - }); - }); - - // Assert fragment is rendered with new data - const refetchedUser = { - id: '1', - name: 'Alice', - profile_picture: { - uri: 'scale32', }, - ...createFragmentRef( - '1', - refetchQuery, - 'useRefetchableFragmentNodeTest2Fragment', - ), - }; - expectFragmentResults([{data: refetchedUser}]); - expect(isOperationRetained(refetchQuery)).toBe(true); + }); }); + + // Assert fragment is rendered with new data + const refetchedUser = { + id: '4', + name: 'Mark', + profile_picture: { + uri: 'scale16', + }, + ...createFragmentRef( + '4', + refetchQuery, + 'useRefetchableFragmentNodeTest2Fragment', + ), + }; + expectFragmentResults([{data: refetchedUser}]); + expect(isOperationRetained(refetchQuery)).toBe(true); }); - describe('internal environment option', () => { - let newRelease; - let isOperationRetainedInNewEnvironment; - let newEnvironment; + it('refetches new variables correctly when refetching same id', () => { + const renderer = renderFragment(); + const initialUser = { + id: '1', + name: 'Alice', + profile_picture: null, + ...createFragmentRef( + '1', + query, + 'useRefetchableFragmentNodeTest2Fragment', + ), + }; + expectFragmentResults([ + { + data: initialUser, + }, + ]); - beforeEach(() => { - newEnvironment = createMockEnvironment(); - ({ - release_DEPRECATED: newRelease, - isOperationRetained: isOperationRetainedInNewEnvironment, - } = trackRetentionForEnvironment(newEnvironment)); + TestRenderer.act(() => { + refetch({scale: 32}); }); - it('reloads new data into new environment, and renders successfully', () => { - const renderer = renderFragment(); - const initialUser = { - id: '1', - name: 'Alice', - profile_picture: null, - ...createFragmentRef( - '1', - query, - 'useRefetchableFragmentNodeTestNestedUserFragment', - ), - }; - // initial data on default environment - expectFragmentResults([{data: initialUser}]); + // Assert that fragment is refetching with the right variables and + // suspends upon refetch + const refetchVariables = { + id: '1', + scale: 32, + }; + refetchQuery = createOperationDescriptor( + gqlRefetchQuery, + refetchVariables, + {force: true}, + ); + expectFragmentIsRefetching(renderer, { + refetchVariables, + refetchQuery, + }); - TestRenderer.act(() => { - refetch( - {id: '1'}, - { - __environment: newEnvironment, + // Mock network response + TestRenderer.act(() => { + environment.mock.resolve(gqlRefetchQuery, { + data: { + node: { + __typename: 'User', + id: '1', + name: 'Alice', + profile_picture: { + uri: 'scale32', + }, + username: 'useralice', }, - ); + }, }); - const refetchVariables = { - id: '1', - scale: 16, - }; - refetchQuery = createOperationDescriptor( - gqlRefetchQuery, - refetchVariables, - {force: true}, - ); + }); + + // Assert fragment is rendered with new data + const refetchedUser = { + id: '1', + name: 'Alice', + profile_picture: { + uri: 'scale32', + }, + ...createFragmentRef( + '1', + refetchQuery, + 'useRefetchableFragmentNodeTest2Fragment', + ), + }; + expectFragmentResults([{data: refetchedUser}]); + expect(isOperationRetained(refetchQuery)).toBe(true); + }); + }); + + describe('internal environment option', () => { + let newRelease; + let isOperationRetainedInNewEnvironment; + let newEnvironment; + + beforeEach(() => { + newEnvironment = createMockEnvironment(); + ({ + release_DEPRECATED: newRelease, + isOperationRetained: isOperationRetainedInNewEnvironment, + } = trackRetentionForEnvironment(newEnvironment)); + }); + + it('reloads new data into new environment, and renders successfully', () => { + const renderer = renderFragment(); + const initialUser = { + id: '1', + name: 'Alice', + profile_picture: null, + ...createFragmentRef( + '1', + query, + 'useRefetchableFragmentNodeTestNestedUserFragment', + ), + }; + // initial data on default environment + expectFragmentResults([{data: initialUser}]); - // Fetch on newEnvironment - expectFragmentIsRefetching( - renderer, + TestRenderer.act(() => { + refetch( + {id: '1'}, { - refetchVariables, - refetchQuery, + __environment: newEnvironment, }, - newEnvironment, ); + }); + const refetchVariables = { + id: '1', + scale: 16, + }; + refetchQuery = createOperationDescriptor( + gqlRefetchQuery, + refetchVariables, + {force: true}, + ); - TestRenderer.act(() => { - newEnvironment.mock.resolve(gqlRefetchQuery, { - data: { - node: { - __typename: 'User', - id: '1', - name: 'Mark', - username: 'usermark', - profile_picture: { - uri: 'scale16', - }, + // Fetch on newEnvironment + expectFragmentIsRefetching( + renderer, + { + refetchVariables, + refetchQuery, + }, + newEnvironment, + ); + + TestRenderer.act(() => { + newEnvironment.mock.resolve(gqlRefetchQuery, { + data: { + node: { + __typename: 'User', + id: '1', + name: 'Mark', + username: 'usermark', + profile_picture: { + uri: 'scale16', }, }, - }); + }, }); - TestRenderer.act(() => jest.runAllImmediates()); + }); + TestRenderer.act(() => jest.runAllImmediates()); - // Data should be loaded on the newEnvironment - const dataInSource = { - __id: '1', - __typename: 'User', - 'profile_picture(scale:16)': { - __ref: 'client:1:profile_picture(scale:16)', - }, - id: '1', - name: 'Mark', - username: 'usermark', - }; - const source = newEnvironment.getStore().getSource(); - expect(source.get('1')).toEqual(dataInSource); + // Data should be loaded on the newEnvironment + const dataInSource = { + __id: '1', + __typename: 'User', + 'profile_picture(scale:16)': { + __ref: 'client:1:profile_picture(scale:16)', + }, + id: '1', + name: 'Mark', + username: 'usermark', + }; + const source = newEnvironment.getStore().getSource(); + expect(source.get('1')).toEqual(dataInSource); - // Assert refetch query was retained - expect(isOperationRetainedInNewEnvironment(refetchQuery)).toBe(true); + // Assert refetch query was retained + expect(isOperationRetainedInNewEnvironment(refetchQuery)).toBe(true); - // Should be able to use the new data if switched to new environment - commitSpy.mockClear(); - newRelease.mockClear(); - TestRenderer.act(() => { - setEnvironment(newEnvironment); - }); - // refetch on the same newEnvironment after switching should not be reset - expect(release).not.toBeCalled(); // FIXME not sure what this is trying to say + // Should be able to use the new data if switched to new environment + commitSpy.mockClear(); + newRelease.mockClear(); + TestRenderer.act(() => { + setEnvironment(newEnvironment); + }); + // refetch on the same newEnvironment after switching should not be reset + expect(release).not.toBeCalled(); // FIXME not sure what this is trying to say - const refetchedUser = { - id: '1', - name: 'Mark', - profile_picture: { - uri: 'scale16', - }, - ...createFragmentRef('1', refetchQuery), - }; + const refetchedUser = { + id: '1', + name: 'Mark', + profile_picture: { + uri: 'scale16', + }, + ...createFragmentRef('1', refetchQuery), + }; - expectFragmentResults([{data: refetchedUser}]); + expectFragmentResults([{data: refetchedUser}]); - // Refetch on another enironment afterwards should work - commitSpy.mockClear(); - // $FlowFixMe[method-unbinding] added when improving typing for this parameters - environment.executeWithSource.mockClear(); - const anotherNewEnvironment = createMockEnvironment(); - TestRenderer.act(() => jest.runAllImmediates()); + // Refetch on another enironment afterwards should work + commitSpy.mockClear(); + // $FlowFixMe[method-unbinding] added when improving typing for this parameters + environment.executeWithSource.mockClear(); + const anotherNewEnvironment = createMockEnvironment(); + TestRenderer.act(() => jest.runAllImmediates()); - TestRenderer.act(() => { - refetch( - {id: '1'}, - { - __environment: anotherNewEnvironment, - }, - ); - }); - expectFragmentIsRefetching( - renderer, + TestRenderer.act(() => { + refetch( + {id: '1'}, { - refetchVariables, - refetchQuery, + __environment: anotherNewEnvironment, }, - anotherNewEnvironment, ); + }); + expectFragmentIsRefetching( + renderer, + { + refetchVariables, + refetchQuery, + }, + anotherNewEnvironment, + ); - TestRenderer.act(() => { - anotherNewEnvironment.mock.resolve(gqlRefetchQuery, { - data: { - node: { - __typename: 'User', - id: '1', - name: 'Mark', - username: 'usermark', - profile_picture: { - uri: 'scale16', - }, + TestRenderer.act(() => { + anotherNewEnvironment.mock.resolve(gqlRefetchQuery, { + data: { + node: { + __typename: 'User', + id: '1', + name: 'Mark', + username: 'usermark', + profile_picture: { + uri: 'scale16', }, }, - }); + }, }); - expect(anotherNewEnvironment.getStore().getSource().get('1')).toEqual( - dataInSource, - ); }); + expect(anotherNewEnvironment.getStore().getSource().get('1')).toEqual( + dataInSource, + ); }); }); - }, -); + }); +}); diff --git a/packages/react-relay/relay-hooks/__tests__/useRefetchableFragmentNode-with-suspense-transition-test.js b/packages/react-relay/relay-hooks/__tests__/useRefetchableFragmentNode-with-suspense-transition-test.js index 4dc57953b52db..7d8f447612f6d 100644 --- a/packages/react-relay/relay-hooks/__tests__/useRefetchableFragmentNode-with-suspense-transition-test.js +++ b/packages/react-relay/relay-hooks/__tests__/useRefetchableFragmentNode-with-suspense-transition-test.js @@ -216,6 +216,7 @@ describe('useRefetchableFragmentNode with useTransition', () => { ) { node(id: $id) { ...useRefetchableFragmentNodeWithSuspenseTransitionTestUserFragment + @dangerously_unaliased_fixme } } `; diff --git a/packages/react-relay/relay-hooks/__tests__/useSubscribeToInvalidationState-test.js b/packages/react-relay/relay-hooks/__tests__/useSubscribeToInvalidationState-test.js index 746220c296336..58213fee45458 100644 --- a/packages/react-relay/relay-hooks/__tests__/useSubscribeToInvalidationState-test.js +++ b/packages/react-relay/relay-hooks/__tests__/useSubscribeToInvalidationState-test.js @@ -10,7 +10,9 @@ */ 'use strict'; + import type {RelayMockEnvironment} from '../../../relay-test-utils/RelayModernMockEnvironment'; +import type {RecordSourceJSON} from 'relay-runtime/store/RelayStoreTypes'; const RelayEnvironmentProvider = require('../RelayEnvironmentProvider'); const useSubscribeToInvalidationState = require('../useSubscribeToInvalidationState'); @@ -27,7 +29,7 @@ let setDataIDs; let setCallback; let disposable; let renderedInstance; -let data; +let data: RecordSourceJSON; let callback; beforeEach(() => { diff --git a/packages/react-relay/relay-hooks/getConnectionState.js b/packages/react-relay/relay-hooks/getConnectionState.js index a65fe3aee097c..a812ed61c600d 100644 --- a/packages/react-relay/relay-hooks/getConnectionState.js +++ b/packages/react-relay/relay-hooks/getConnectionState.js @@ -73,8 +73,8 @@ function getConnectionState( const cursor = direction === 'forward' - ? pageInfo[END_CURSOR] ?? null - : pageInfo[START_CURSOR] ?? null; + ? (pageInfo[END_CURSOR] ?? null) + : (pageInfo[START_CURSOR] ?? null); invariant( cursor === null || typeof cursor === 'string', 'Relay: Expected page info for connection in fragment `%s` to have a ' + diff --git a/packages/react-relay/relay-hooks/legacy/FragmentResource.js b/packages/react-relay/relay-hooks/legacy/FragmentResource.js index 4f4289db7cadf..6ff8ca2550d12 100644 --- a/packages/react-relay/relay-hooks/legacy/FragmentResource.js +++ b/packages/react-relay/relay-hooks/legacy/FragmentResource.js @@ -340,14 +340,15 @@ class FragmentResourceImpl { 'to `%s`. If the parent fragment only fetches the fragment conditionally ' + '- with e.g. `@include`, `@skip`, or inside a `... on SomeType { }` ' + 'spread - then the fragment reference will not exist. ' + - 'In this case, pass `null` if the conditions for evaluating the ' + - 'fragment are not met (e.g. if the `@include(if)` value is false.)', + 'This issue can generally be fixed by adding `@alias` after `...%s`.\n' + + 'See https://relay.dev/docs/next/guides/alias-directive/', fragmentNode.name, fragmentNode.name, componentDisplayName, fragmentNode.name, fragmentKey == null ? 'a fragment reference' : `the \`${fragmentKey}\``, componentDisplayName, + fragmentNode.name, ); let fragmentResult = null; @@ -558,13 +559,10 @@ class FragmentResourceImpl { _throwOrLogErrorsInSnapshot(snapshot: SingularOrPluralSnapshot) { if (Array.isArray(snapshot)) { snapshot.forEach(s => { - handlePotentialSnapshotErrors(this._environment, s.errorResponseFields); + handlePotentialSnapshotErrors(this._environment, s.fieldErrors); }); } else { - handlePotentialSnapshotErrors( - this._environment, - snapshot.errorResponseFields, - ); + handlePotentialSnapshotErrors(this._environment, snapshot.fieldErrors); } } @@ -763,7 +761,7 @@ class FragmentResourceImpl { missingLiveResolverFields: currentSnapshot.missingLiveResolverFields, seenRecords: currentSnapshot.seenRecords, selector: currentSnapshot.selector, - errorResponseFields: currentSnapshot.errorResponseFields, + fieldErrors: currentSnapshot.fieldErrors, }; if (updatedData !== renderData) { const result = getFragmentResult( diff --git a/packages/react-relay/relay-hooks/legacy/useBlockingPaginationFragment.js b/packages/react-relay/relay-hooks/legacy/useBlockingPaginationFragment.js index 702e4b8b6d76d..0b64a27527f76 100644 --- a/packages/react-relay/relay-hooks/legacy/useBlockingPaginationFragment.js +++ b/packages/react-relay/relay-hooks/legacy/useBlockingPaginationFragment.js @@ -14,13 +14,7 @@ import type {LoadMoreFn, UseLoadMoreFunctionArgs} from '../useLoadMoreFunction'; import type {Options} from './useRefetchableFragmentNode'; import type {RefetchableFragment} from 'relay-runtime'; -import type { - Disposable, - FragmentType, - GraphQLResponse, - Observer, - Variables, -} from 'relay-runtime'; +import type {Disposable, FragmentType, Variables} from 'relay-runtime'; const useLoadMoreFunction = require('../useLoadMoreFunction'); const useStaticFragmentNodeWarning = require('../useStaticFragmentNodeWarning'); @@ -169,16 +163,7 @@ hook useBlockingPaginationFragment< hook useLoadMore(args: { disableStoreUpdates: () => void, enableStoreUpdates: () => void, - ...$Exact< - $Diff< - UseLoadMoreFunctionArgs, - { - observer: Observer, - onReset: () => void, - ... - }, - >, - >, + ...$Exact>, }): [LoadMoreFn, boolean, () => void] { const {disableStoreUpdates, enableStoreUpdates, ...loadMoreArgs} = args; const [requestPromise, setRequestPromise] = useState>( diff --git a/packages/react-relay/relay-hooks/legacy/useRefetchableFragmentNode.js b/packages/react-relay/relay-hooks/legacy/useRefetchableFragmentNode.js index f3bac9430145f..54f76a209bb49 100644 --- a/packages/react-relay/relay-hooks/legacy/useRefetchableFragmentNode.js +++ b/packages/react-relay/relay-hooks/legacy/useRefetchableFragmentNode.js @@ -592,7 +592,7 @@ if (__DEV__) { fragmentNode: ReaderFragment, componentDisplayName: string, ): void { - if (previousIDAndTypename == null) { + if (previousIDAndTypename == null || refetchedFragmentRef == null) { return; } const {ID_KEY} = require('relay-runtime'); diff --git a/packages/react-relay/relay-hooks/loadEntryPoint.js b/packages/react-relay/relay-hooks/loadEntryPoint.js index 0a1d8ee112259..2dd0d9090025b 100644 --- a/packages/react-relay/relay-hooks/loadEntryPoint.js +++ b/packages/react-relay/relay-hooks/loadEntryPoint.js @@ -62,6 +62,12 @@ function loadEntryPoint< const {environmentProviderOptions, options, parameters, variables} = query; + // $FlowFixMe[prop-missing] Exists for types that wrap EntryPoint + if (options?.includeIf === false) { + // don't preload this query since the includeIf is false + return; + } + const environment = environmentProvider.getEnvironment( environmentProviderOptions, ); @@ -138,7 +144,7 @@ function loadEntryPoint< // that it's actually an es6 module wrapper, so unwrap it. This won't work for React classes with a static property named "default", but // that's probably a worthwhile trade-off. const component = - // $FlowIgnore[prop-missing] + // $FlowFixMe[prop-missing] componentModule.default != null ? componentModule.default : componentModule; diff --git a/packages/react-relay/relay-hooks/loadQuery.js b/packages/react-relay/relay-hooks/loadQuery.js index 978b54f89c132..3ee5f15b34cca 100644 --- a/packages/react-relay/relay-hooks/loadQuery.js +++ b/packages/react-relay/relay-hooks/loadQuery.js @@ -28,6 +28,7 @@ import type { RequestIdentifier, RequestParameters, } from 'relay-runtime'; +import type {OperationAvailability} from 'relay-runtime/store/RelayStoreTypes'; const invariant = require('invariant'); const { @@ -42,7 +43,7 @@ const { let fetchKey = 100001; -type QueryType = +export type QueryType = T extends Query ? { variables: V, @@ -125,11 +126,12 @@ function loadQuery< let networkError = null; // makeNetworkRequest will immediately start a raw network request if // one isn't already in flight and return an Observable that when - // subscribed to will replay the network events that have occured so far, + // subscribed to will replay the network events that have occurred so far, // as well as subsequent events. let didMakeNetworkRequest = false; const makeNetworkRequest = ( params: RequestParameters, + checkOperation?: () => OperationAvailability, ): Observable => { // N.B. this function is called synchronously or not at all // didMakeNetworkRequest is safe to rely on in the returned value @@ -160,7 +162,16 @@ function loadQuery< 'raw-network-request-' + getRequestIdentifier(params, variables); const observable = fetchQueryDeduped(environment, identifier, () => { const network = environment.getNetwork(); - return network.execute(params, variables, networkCacheConfig); + return network.execute( + params, + variables, + networkCacheConfig, + undefined, + undefined, + undefined, + undefined, + checkOperation, + ); }); const {unsubscribe} = observable.subscribe({ @@ -247,13 +258,18 @@ function loadQuery< // then we do nothing. const shouldFetch = fetchPolicy !== 'store-or-network' || + // environment.check can trigger store updates through missing field handlers, + // short circuiting the check avoids unnecessary updates environment.check(operation).status !== 'available'; if (shouldFetch) { executeDeduped(operation, () => { // N.B. Since we have the operation synchronously available here, // we can immediately fetch and execute the operation. - const networkObservable = makeNetworkRequest(concreteRequest.params); + const networkObservable = makeNetworkRequest( + concreteRequest.params, + () => environment.check(operation), + ); const executeObservable = executeWithNetworkSource( operation, networkObservable, @@ -335,10 +351,16 @@ function loadQuery< return; } if (didExecuteNetworkSource) { + /* $FlowFixMe[constant-condition] Error discovered during Constant + * Condition roll out. See https://fburl.com/workplace/1v97vimq. */ unsubscribeFromExecution && unsubscribeFromExecution(); } else { + /* $FlowFixMe[constant-condition] Error discovered during Constant + * Condition roll out. See https://fburl.com/workplace/1v97vimq. */ unsubscribeFromNetworkRequest && unsubscribeFromNetworkRequest(); } + /* $FlowFixMe[constant-condition] Error discovered during Constant + * Condition roll out. See https://fburl.com/workplace/1v97vimq. */ cancelOnLoadCallback && cancelOnLoadCallback(); isNetworkRequestCancelled = true; }; diff --git a/packages/react-relay/relay-hooks/preloadQuery_DEPRECATED.js b/packages/react-relay/relay-hooks/preloadQuery_DEPRECATED.js index e6ab0ec4f89ff..d42a9eaeda220 100644 --- a/packages/react-relay/relay-hooks/preloadQuery_DEPRECATED.js +++ b/packages/react-relay/relay-hooks/preloadQuery_DEPRECATED.js @@ -24,6 +24,7 @@ import type { GraphQLResponse, GraphQLTaggedNode, IEnvironment, + OperationAvailability, OperationType, Subscription, } from 'relay-runtime'; @@ -167,12 +168,17 @@ function preloadQueryDeduped( }`; const prevQueryEntry = pendingQueries.get(cacheKey); - const availability = - fetchPolicy === STORE_OR_NETWORK_DEFAULT && query != null && query != null + function checkOperation(): OperationAvailability { + return query != null ? environment.check( createOperationDescriptor(query, variables, networkCacheConfig), ) : {status: 'missing'}; + } + const availability = + fetchPolicy === STORE_OR_NETWORK_DEFAULT + ? checkOperation() + : {status: 'missing'}; let nextQueryEntry: ?PendingQueryEntry; if (availability.status === 'available' && query != null) { @@ -203,7 +209,16 @@ function preloadQueryDeduped( } } else if (prevQueryEntry == null || prevQueryEntry.kind !== 'network') { // Should fetch but we're not already fetching: fetch! - const source = network.execute(params, variables, networkCacheConfig, null); + const source = network.execute( + params, + variables, + networkCacheConfig, + null, + undefined, + undefined, + undefined, + checkOperation, + ); const subject = new ReplaySubject(); nextQueryEntry = { cacheKey, diff --git a/packages/react-relay/relay-hooks/readFragmentInternal.js b/packages/react-relay/relay-hooks/readFragmentInternal.js index 4bde1922432ae..c4e82f13c15fd 100644 --- a/packages/react-relay/relay-hooks/readFragmentInternal.js +++ b/packages/react-relay/relay-hooks/readFragmentInternal.js @@ -83,13 +83,10 @@ function handlePotentialSnapshotErrorsForState( state: FragmentState, ): void { if (state.kind === 'singular') { - handlePotentialSnapshotErrors( - environment, - state.snapshot.errorResponseFields, - ); + handlePotentialSnapshotErrors(environment, state.snapshot.fieldErrors); } else if (state.kind === 'plural') { for (const snapshot of state.snapshots) { - handlePotentialSnapshotErrors(environment, snapshot.errorResponseFields); + handlePotentialSnapshotErrors(environment, snapshot.fieldErrors); } } } @@ -201,14 +198,15 @@ function readFragmentInternal( 'to `%s`. If the parent fragment only fetches the fragment conditionally ' + '- with e.g. `@include`, `@skip`, or inside a `... on SomeType { }` ' + 'spread - then the fragment reference will not exist. ' + - 'In this case, pass `null` if the conditions for evaluating the ' + - 'fragment are not met (e.g. if the `@include(if)` value is false.)', + 'This issue can generally be fixed by adding `@alias` after `...%s`.\n' + + 'See https://relay.dev/docs/next/guides/alias-directive/', fragmentNode.name, fragmentNode.name, hookDisplayName, fragmentNode.name, fragmentKey == null ? 'a fragment reference' : `the \`${fragmentKey}\``, hookDisplayName, + fragmentNode.name, ); const state = getFragmentState(environment, fragmentSelector); @@ -216,7 +214,10 @@ function readFragmentInternal( // Handle the queries for any missing client edges; this may suspend. // FIXME handle client edges in parallel. let clientEdgeQueries = null; - if (fragmentNode.metadata?.hasClientEdges === true) { + if ( + fragmentNode.metadata?.hasClientEdges === true || + RelayFeatureFlags.CHECK_ALL_FRAGMENTS_FOR_MISSING_CLIENT_EDGES + ) { const missingClientEdges = getMissingClientEdges(state); if (missingClientEdges?.length) { clientEdgeQueries = ([]: Array); diff --git a/packages/react-relay/relay-hooks/useEntryPointLoader.js b/packages/react-relay/relay-hooks/useEntryPointLoader.js index 6be26c0f94751..af89a85199464 100644 --- a/packages/react-relay/relay-hooks/useEntryPointLoader.js +++ b/packages/react-relay/relay-hooks/useEntryPointLoader.js @@ -48,7 +48,9 @@ type UseEntryPointLoaderHookReturnType< type NullEntryPointReference = { kind: 'NullEntryPointReference', }; -const initialNullEntryPointReferenceState = {kind: 'NullEntryPointReference'}; +const initialNullEntryPointReferenceState: NullEntryPointReference = { + kind: 'NullEntryPointReference', +}; hook useLoadEntryPoint< TEntryPointParams: {...}, @@ -122,7 +124,7 @@ hook useLoadEntryPoint< const disposeEntryPoint = useCallback(() => { if (isMountedRef.current) { - const nullEntryPointReference = { + const nullEntryPointReference: NullEntryPointReference = { kind: 'NullEntryPointReference', }; undisposedEntryPointReferencesRef.current.add(nullEntryPointReference); @@ -156,7 +158,7 @@ hook useLoadEntryPoint< useEffect(() => { return () => { // Attempt to detect if the component was - // hidden (by Offscreen API), or fast refresh occured; + // hidden (by Offscreen API), or fast refresh occurred; // Only in these situations would the effect cleanup // for "unmounting" run multiple times, so if // we are ever able to read this ref with a value diff --git a/packages/react-relay/relay-hooks/useFragment.js b/packages/react-relay/relay-hooks/useFragment.js index fc22aee24cb14..56a8004ea0dc3 100644 --- a/packages/react-relay/relay-hooks/useFragment.js +++ b/packages/react-relay/relay-hooks/useFragment.js @@ -54,6 +54,7 @@ hook useFragment(fragment: GraphQLTaggedNode, key: mixed): mixed { if (__DEV__) { // eslint-disable-next-line react-hooks/rules-of-hooks // $FlowFixMe[react-rule-hook] + // $FlowFixMe[react-rule-hook-conditional] useDebugValue({fragment: fragmentNode.name, data}); } return data; diff --git a/packages/react-relay/relay-hooks/useFragmentInternal.js b/packages/react-relay/relay-hooks/useFragmentInternal.js index 6ca808b852da1..3186df50fecce 100644 --- a/packages/react-relay/relay-hooks/useFragmentInternal.js +++ b/packages/react-relay/relay-hooks/useFragmentInternal.js @@ -26,6 +26,7 @@ hook useFragmentInternal( ): ?SelectorData | Array { if (RelayFeatureFlags.ENABLE_ACTIVITY_COMPATIBILITY) { // $FlowFixMe[react-rule-hook] - the condition is static + // $FlowFixMe[react-rule-hook-conditional] return useFragmentInternal_EXPERIMENTAL( fragmentNode, fragmentRef, @@ -34,6 +35,7 @@ hook useFragmentInternal( ); } // $FlowFixMe[react-rule-hook] - the condition is static + // $FlowFixMe[react-rule-hook-conditional] return useFragmentInternal_CURRENT( fragmentNode, fragmentRef, diff --git a/packages/react-relay/relay-hooks/useFragmentInternal_CURRENT.js b/packages/react-relay/relay-hooks/useFragmentInternal_CURRENT.js index 5b8252dec4bef..d069950ca37b7 100644 --- a/packages/react-relay/relay-hooks/useFragmentInternal_CURRENT.js +++ b/packages/react-relay/relay-hooks/useFragmentInternal_CURRENT.js @@ -26,6 +26,7 @@ import type {MissingClientEdgeRequestInfo} from 'relay-runtime/store/RelayStoreT const {getQueryResourceForEnvironment} = require('./QueryResource'); const useRelayEnvironment = require('./useRelayEnvironment'); +const useRelayLoggingContext = require('./useRelayLoggingContext'); const invariant = require('invariant'); const {useDebugValue, useEffect, useMemo, useRef, useState} = require('react'); const { @@ -109,15 +110,21 @@ function getSuspendingLiveResolver( function handlePotentialSnapshotErrorsForState( environment: IEnvironment, state: FragmentState, + loggingContext: mixed | void, ): void { if (state.kind === 'singular') { handlePotentialSnapshotErrors( environment, - state.snapshot.errorResponseFields, + state.snapshot.fieldErrors, + loggingContext, ); } else if (state.kind === 'plural') { for (const snapshot of state.snapshots) { - handlePotentialSnapshotErrors(environment, snapshot.errorResponseFields); + handlePotentialSnapshotErrors( + environment, + snapshot.fieldErrors, + loggingContext, + ); } } } @@ -153,7 +160,7 @@ function handleMissedUpdates( missingLiveResolverFields: currentSnapshot.missingLiveResolverFields, seenRecords: currentSnapshot.seenRecords, selector: currentSnapshot.selector, - errorResponseFields: currentSnapshot.errorResponseFields, + fieldErrors: currentSnapshot.fieldErrors, }; return [ updatedData !== state.snapshot.data, @@ -177,7 +184,7 @@ function handleMissedUpdates( missingLiveResolverFields: currentSnapshot.missingLiveResolverFields, seenRecords: currentSnapshot.seenRecords, selector: currentSnapshot.selector, - errorResponseFields: currentSnapshot.errorResponseFields, + fieldErrors: currentSnapshot.fieldErrors, }; if (updatedData !== snapshot.data) { didMissUpdates = true; @@ -393,16 +400,23 @@ hook useFragmentInternal( 'to `%s`. If the parent fragment only fetches the fragment conditionally ' + '- with e.g. `@include`, `@skip`, or inside a `... on SomeType { }` ' + 'spread - then the fragment reference will not exist. ' + - 'In this case, pass `null` if the conditions for evaluating the ' + - 'fragment are not met (e.g. if the `@include(if)` value is false.)', + 'This issue can generally be fixed by adding `@alias` after `...%s`.\n' + + 'See https://relay.dev/docs/next/guides/alias-directive/', fragmentNode.name, fragmentNode.name, hookDisplayName, fragmentNode.name, hookDisplayName, + fragmentNode.name, ); const environment = useRelayEnvironment(); + let loggerContext; + if (RelayFeatureFlags.ENABLE_UI_CONTEXT_ON_RELAY_LOGGER) { + // $FlowFixMe[react-rule-hook] - the condition is static + // $FlowFixMe[react-rule-hook-conditional] + loggerContext = useRelayLoggingContext(); + } const [_state, setState] = useState(() => getFragmentState(environment, fragmentSelector), ); @@ -448,12 +462,16 @@ hook useFragmentInternal( // Handle the queries for any missing client edges; this may suspend. // FIXME handle client edges in parallel. - if (fragmentNode.metadata?.hasClientEdges === true) { + if ( + fragmentNode.metadata?.hasClientEdges === true || + RelayFeatureFlags.CHECK_ALL_FRAGMENTS_FOR_MISSING_CLIENT_EDGES + ) { // The fragment is validated to be static (in useFragment) and hasClientEdges is // a static (constant) property of the fragment. In practice, this effect will // always or never run for a given invocation of this hook. // eslint-disable-next-line react-hooks/rules-of-hooks // $FlowFixMe[react-rule-hook] + // $FlowFixMe[react-rule-hook-conditional] const [clientEdgeQueries, activeRequestPromises] = useMemo(() => { const missingClientEdges = getMissingClientEdges(state); // eslint-disable-next-line no-shadow @@ -485,6 +503,7 @@ hook useFragmentInternal( // See above note // eslint-disable-next-line react-hooks/rules-of-hooks // $FlowFixMe[react-rule-hook] + // $FlowFixMe[react-rule-hook-conditional] useEffect(() => { const QueryResource = getQueryResourceForEnvironment(environment); if (clientEdgeQueries?.length) { @@ -519,6 +538,7 @@ hook useFragmentInternal( if ( RelayFeatureFlags.ENABLE_RELAY_OPERATION_TRACKER_SUSPENSE || environment !== previousEnvironment || + // $FlowFixMe[sketchy-null-bool] !committedFragmentSelectorRef.current || // $FlowFixMe[react-rule-unsafe-ref] !areEqualSelectors(committedFragmentSelectorRef.current, fragmentSelector) @@ -541,7 +561,7 @@ hook useFragmentInternal( // Report required fields only if we're not suspending, since that means // they're missing even though we are out of options for possibly fetching them: - handlePotentialSnapshotErrorsForState(environment, state); + handlePotentialSnapshotErrorsForState(environment, state, loggerContext); const hasPendingStateChanges = useRef(false); @@ -594,6 +614,7 @@ hook useFragmentInternal( const fragmentRefIsNullish = fragmentRef == null; // for less sensitive memoization // eslint-disable-next-line react-hooks/rules-of-hooks // $FlowFixMe[react-rule-hook] + // $FlowFixMe[react-rule-hook-conditional] data = useMemo(() => { if (state.kind === 'bailout') { // Bailout state can happen if the fragmentRef is a plural array that is empty or has no @@ -645,6 +666,7 @@ hook useFragmentInternal( if (__DEV__) { // eslint-disable-next-line react-hooks/rules-of-hooks // $FlowFixMe[react-rule-hook] + // $FlowFixMe[react-rule-hook-conditional] useDebugValue({fragment: fragmentNode.name, data}); } diff --git a/packages/react-relay/relay-hooks/useFragmentInternal_EXPERIMENTAL.js b/packages/react-relay/relay-hooks/useFragmentInternal_EXPERIMENTAL.js index 68f35c2d65d56..4824081f5c095 100644 --- a/packages/react-relay/relay-hooks/useFragmentInternal_EXPERIMENTAL.js +++ b/packages/react-relay/relay-hooks/useFragmentInternal_EXPERIMENTAL.js @@ -26,6 +26,7 @@ import type {MissingClientEdgeRequestInfo} from 'relay-runtime/store/RelayStoreT const {getQueryResourceForEnvironment} = require('./QueryResource'); const useRelayEnvironment = require('./useRelayEnvironment'); +const useRelayLoggingContext = require('./useRelayLoggingContext'); const invariant = require('invariant'); const {useDebugValue, useEffect, useMemo, useRef, useState} = require('react'); const { @@ -121,15 +122,21 @@ function getSuspendingLiveResolver( function handlePotentialSnapshotErrorsForState( environment: IEnvironment, state: FragmentState, + loggingContext: mixed | void, ): void { if (state.kind === 'singular') { handlePotentialSnapshotErrors( environment, - state.snapshot.errorResponseFields, + state.snapshot.fieldErrors, + loggingContext, ); } else if (state.kind === 'plural') { for (const snapshot of state.snapshots) { - handlePotentialSnapshotErrors(environment, snapshot.errorResponseFields); + handlePotentialSnapshotErrors( + environment, + snapshot.fieldErrors, + loggingContext, + ); } } } @@ -165,7 +172,7 @@ function handleMissedUpdates( missingLiveResolverFields: currentSnapshot.missingLiveResolverFields, seenRecords: currentSnapshot.seenRecords, selector: currentSnapshot.selector, - errorResponseFields: currentSnapshot.errorResponseFields, + fieldErrors: currentSnapshot.fieldErrors, }; return [ updatedData !== state.snapshot.data, @@ -191,7 +198,7 @@ function handleMissedUpdates( missingLiveResolverFields: currentSnapshot.missingLiveResolverFields, seenRecords: currentSnapshot.seenRecords, selector: currentSnapshot.selector, - errorResponseFields: currentSnapshot.errorResponseFields, + fieldErrors: currentSnapshot.fieldErrors, }; if (updatedData !== snapshot.data) { didMissUpdates = true; @@ -215,13 +222,15 @@ function handleMissedUpdates( } } +type PromiseWithDisplayName = Promise & {displayName?: string}; + function handleMissingClientEdge( environment: IEnvironment, parentFragmentNode: ReaderFragment, parentFragmentRef: mixed, missingClientEdgeRequestInfo: MissingClientEdgeRequestInfo, queryOptions?: FragmentQueryOptions, -): [QueryResult, ?Promise] { +): [QueryResult, ?PromiseWithDisplayName] { const originalVariables = getVariablesFromFragment( parentFragmentNode, parentFragmentRef, @@ -246,10 +255,17 @@ function handleMissingClientEdge( queryOptions?.fetchPolicy, ); - return [ - queryResult, - getPromiseForActiveRequest(environment, queryOperationDescriptor.request), - ]; + const promise = getPromiseForActiveRequest( + environment, + queryOperationDescriptor.request, + ); + // $FlowExpectedError[prop-missing] + if (promise != null && promise.displayName == null) { + // $FlowExpectedError[prop-missing] + promise.displayName = missingClientEdgeRequestInfo.request.params.name; + } + // $FlowFixMe[incompatible-exact] - Intentionally bypassing exactness check + return [queryResult, promise]; } function subscribeToSnapshot( @@ -420,9 +436,9 @@ hook useFragmentInternal_EXPERIMENTAL( '- Conditionally fetching `%s` but unconditionally passing %s prop ' + 'to `%s`. If the parent fragment only fetches the fragment conditionally ' + '- with e.g. `@include`, `@skip`, or inside a `... on SomeType { }` ' + - 'spread - then the fragment reference will not exist. ' + - 'In this case, pass `null` if the conditions for evaluating the ' + - 'fragment are not met (e.g. if the `@include(if)` value is false.)', + 'spread - then the fragment reference will not exist. ' + + 'This issue can generally be fixed by adding `@alias` after `...%s`.\n' + + 'See https://relay.dev/docs/next/guides/alias-directive/', fragmentNode.name, fragmentNode.name, hookDisplayName, @@ -431,6 +447,12 @@ hook useFragmentInternal_EXPERIMENTAL( ); const environment = useRelayEnvironment(); + let loggerContext; + if (RelayFeatureFlags.ENABLE_UI_CONTEXT_ON_RELAY_LOGGER) { + // $FlowFixMe[react-rule-hook] - the condition is static + // $FlowFixMe[react-rule-hook-conditional] + loggerContext = useRelayLoggingContext(); + } const [_state, setState] = useState(() => getFragmentState(environment, fragmentSelector), ); @@ -463,17 +485,21 @@ hook useFragmentInternal_EXPERIMENTAL( // Handle the queries for any missing client edges; this may suspend. // FIXME handle client edges in parallel. - if (fragmentNode.metadata?.hasClientEdges === true) { + if ( + fragmentNode.metadata?.hasClientEdges === true || + RelayFeatureFlags.CHECK_ALL_FRAGMENTS_FOR_MISSING_CLIENT_EDGES + ) { // The fragment is validated to be static (in useFragment) and hasClientEdges is // a static (constant) property of the fragment. In practice, this effect will // always or never run for a given invocation of this hook. // eslint-disable-next-line react-hooks/rules-of-hooks // $FlowFixMe[react-rule-hook] + // $FlowFixMe[react-rule-hook-conditional] const [clientEdgeQueries, activeRequestPromises] = useMemo(() => { const missingClientEdges = getMissingClientEdges(state); // eslint-disable-next-line no-shadow let clientEdgeQueries; - const activeRequestPromises = []; + const activeRequestPromises: Array = []; if (missingClientEdges?.length) { clientEdgeQueries = ([]: Array); for (const edge of missingClientEdges) { @@ -494,12 +520,18 @@ hook useFragmentInternal_EXPERIMENTAL( }, [state, environment, fragmentNode, fragmentRef, queryOptions]); if (activeRequestPromises.length) { - throw Promise.all(activeRequestPromises); + const allPromises = Promise.all(activeRequestPromises); + // $FlowExpectedError[prop-missing] Expando to annotate Promises. + allPromises.displayName = `RelayClientEdge(${activeRequestPromises + .map(promise => promise.displayName) + .join(',')})`; + throw allPromises; } // See above note // eslint-disable-next-line react-hooks/rules-of-hooks // $FlowFixMe[react-rule-hook] + // $FlowFixMe[react-rule-hook-conditional] useEffect(() => { const QueryResource = getQueryResourceForEnvironment(environment); if (clientEdgeQueries?.length) { @@ -520,12 +552,15 @@ hook useFragmentInternal_EXPERIMENTAL( // Suspend if a Live Resolver within this fragment is in a suspended state: const suspendingLiveResolvers = getSuspendingLiveResolver(state); if (suspendingLiveResolvers != null && suspendingLiveResolvers.length > 0) { - throw Promise.all( + const promise = Promise.all( suspendingLiveResolvers.map(liveStateID => { // $FlowFixMe[prop-missing] This is expected to be a RelayModernStore return environment.getStore().getLiveResolverPromise(liveStateID); }), ); + // $FlowExpectedError[prop-missing] Expando to annotate Promises. + promise.displayName = 'RelayLiveResolver(' + fragmentNode.name + ')'; + throw promise; } // Suspend if an active operation bears on this fragment, either the // fragment's owner or some other mutation etc. that could affect it. @@ -534,6 +569,7 @@ hook useFragmentInternal_EXPERIMENTAL( if ( RelayFeatureFlags.ENABLE_RELAY_OPERATION_TRACKER_SUSPENSE || environment !== previousEnvironment || + // $FlowFixMe[sketchy-null-bool] !committedFragmentSelectorRef.current || // $FlowFixMe[react-rule-unsafe-ref] !areEqualSelectors(committedFragmentSelectorRef.current, fragmentSelector) @@ -556,7 +592,7 @@ hook useFragmentInternal_EXPERIMENTAL( // Report required fields only if we're not suspending, since that means // they're missing even though we are out of options for possibly fetching them: - handlePotentialSnapshotErrorsForState(environment, state); + handlePotentialSnapshotErrorsForState(environment, state, loggerContext); // We emulate CRUD effects using a ref and two effects: // - The ref tracks the current state (including updates from the subscription) @@ -577,6 +613,7 @@ hook useFragmentInternal_EXPERIMENTAL( selector: ?ReaderSelector, environment: IEnvironment, } | null>(null); + // $FlowFixMe[react-rule-hook] - the condition is static useEffect(() => { const storeSubscription = storeSubscriptionRef.current; if (storeSubscription != null) { @@ -629,6 +666,7 @@ hook useFragmentInternal_EXPERIMENTAL( environment: state.environment, }; }, [state]); + // $FlowFixMe[react-rule-hook] - the condition is static useEffect(() => { if (storeSubscriptionRef.current == null && state.kind !== 'bailout') { const dispose = subscribeToSnapshot(state.environment, state, setState); @@ -656,6 +694,7 @@ hook useFragmentInternal_EXPERIMENTAL( const fragmentRefIsNullish = fragmentRef == null; // for less sensitive memoization // eslint-disable-next-line react-hooks/rules-of-hooks // $FlowFixMe[react-rule-hook] + // $FlowFixMe[react-rule-hook-conditional] data = useMemo(() => { if (state.kind === 'bailout') { // Bailout state can happen if the fragmentRef is a plural array that is empty or has no @@ -707,6 +746,7 @@ hook useFragmentInternal_EXPERIMENTAL( if (__DEV__) { // eslint-disable-next-line react-hooks/rules-of-hooks // $FlowFixMe[react-rule-hook] + // $FlowFixMe[react-rule-hook-conditional] useDebugValue({fragment: fragmentNode.name, data}); } diff --git a/packages/react-relay/relay-hooks/useLazyLoadQuery.js b/packages/react-relay/relay-hooks/useLazyLoadQuery.js index e8a11ae60e739..bf2f70a46e628 100644 --- a/packages/react-relay/relay-hooks/useLazyLoadQuery.js +++ b/packages/react-relay/relay-hooks/useLazyLoadQuery.js @@ -32,23 +32,72 @@ const { export type UseLazyLoadQueryHookType = hook ( gqlQuery: Query, variables: TVariables, - options?: { - fetchKey?: string | number, - fetchPolicy?: FetchPolicy, - networkCacheConfig?: CacheConfig, - UNSTABLE_renderPolicy?: RenderPolicy, - }, + options?: Options, ) => TData; +type Options = { + /** + * Determines if cached data should be used, and when to send a network request based on the cached data that is currently available in the Relay store (for more details, see our [Fetch Policies](../../guided-tour/reusing-cached-data/fetch-policies) and [Garbage Collection](../../guided-tour/reusing-cached-data/presence-of-data) guides): + * * "store-or-network": _*(default)*_ *will* reuse locally cached data and will *only* send a network request if any data for the query is missing. If the query is fully cached, a network request will *not* be made. + * * "store-and-network": *will* reuse locally cached data and will *always* send a network request, regardless of whether any data was missing from the local cache or not. + * * "network-only": *will* *not* reuse locally cached data, and will *always* send a network request to fetch the query, ignoring any data that might be locally cached in Relay. + * * "store-only": *will* *only* reuse locally cached data, and will *never* send a network request to fetch the query. In this case, the responsibility of fetching the query falls to the caller, but this policy could also be used to read and operate on data that is entirely [local](../../guided-tour/updating-data/local-data-updates). + */ + +fetchPolicy?: FetchPolicy, + /** + * A `fetchKey` can be passed to force a re-evaluation of the current query and variables when the component re-renders, even if the variables didn't change, or even if the component isn't remounted (similarly to how passing a different `key` to a React component will cause it to remount). If the `fetchKey` is different from the one used in the previous render, the current query will be re-evaluated against the store, and it might be refetched depending on the current `fetchPolicy` and the state of the cache. + */ + +fetchKey?: string | number, + /** + * Default value: `{force: true}`. Object containing cache config options for the *network layer*. Note that the network layer may contain an *additional* query response cache which will reuse network responses for identical queries. If you want to bypass this cache completely (which is the default behavior), pass `{force: true}` as the value for this option. + */ + +networkCacheConfig?: CacheConfig, + /** + * Undocumented option. + */ + +UNSTABLE_renderPolicy?: RenderPolicy, +}; + +/** + * Hook used to fetch a GraphQL query during render. This hook can trigger multiple nested or waterfalling round trips if used without caution, and waits until render to start a data fetch (when it can usually start a lot sooner than render), thereby degrading performance. Instead, prefer [`usePreloadedQuery`](../use-preloaded-query). + * + * @example + * const React = require('React'); + * + * const {graphql, useLazyLoadQuery} = require('react-relay'); + * + * function App() { + * const data = useLazyLoadQuery( + * graphql` + * query AppQuery($id: ID!) { + * user(id: $id) { + * name + * } + * } + * `, + * {id: 4}, + * {fetchPolicy: 'store-or-network'}, + * ); + * + * return

{data.user?.name}

; + * } + * + * @returns - `data`: Object that contains data which has been read out from the Relay store; the object matches the shape of specified query. + * - The Flow type for data will also match this shape, and contain types derived from the GraphQL Schema. For example, the type of `data` above is: `{| user: ?{| name: ?string |} |}`. + */ hook useLazyLoadQuery( + /** + * GraphQL query specified using a `graphql` template literal. + */ gqlQuery: Query, - variables: TVariables, - options?: { - fetchKey?: string | number, - fetchPolicy?: FetchPolicy, - networkCacheConfig?: CacheConfig, - UNSTABLE_renderPolicy?: RenderPolicy, - }, + /** + * Object containing the variable values to fetch the query. These variables need to match GraphQL variables declared inside the query. + */ + variables: NoInfer, + /** + * options object + */ + options?: Options, ): TData { const environment = useRelayEnvironment(); @@ -71,4 +120,4 @@ hook useLazyLoadQuery( } // $FlowFixMe[react-rule-hook-incompatible] -module.exports = (useLazyLoadQuery: UseLazyLoadQueryHookType); +module.exports = useLazyLoadQuery; diff --git a/packages/react-relay/relay-hooks/useLazyLoadQueryNode.js b/packages/react-relay/relay-hooks/useLazyLoadQueryNode.js index 761437071e424..e28e2ac174ed9 100644 --- a/packages/react-relay/relay-hooks/useLazyLoadQueryNode.js +++ b/packages/react-relay/relay-hooks/useLazyLoadQueryNode.js @@ -78,7 +78,7 @@ hook useLazyLoadQueryNode({ useEffect(() => { return () => { // Attempt to detect if the component was - // hidden (by Offscreen API), or fast refresh occured; + // hidden (by Offscreen API), or fast refresh occurred; // Only in these situations would the effect cleanup // for "unmounting" run multiple times, so if // we are ever able to read this ref with a value diff --git a/packages/react-relay/relay-hooks/useLoadMoreFunction.js b/packages/react-relay/relay-hooks/useLoadMoreFunction.js index 633dd229346c8..e6010f0d31733 100644 --- a/packages/react-relay/relay-hooks/useLoadMoreFunction.js +++ b/packages/react-relay/relay-hooks/useLoadMoreFunction.js @@ -67,9 +67,11 @@ hook useLoadMoreFunction( ): [LoadMoreFn, boolean, () => void] { if (RelayFeatureFlags.ENABLE_ACTIVITY_COMPATIBILITY) { // $FlowFixMe[react-rule-hook] - the condition is static + // $FlowFixMe[react-rule-hook-conditional] return useLoadMoreFunction_EXPERIMENTAL(args); } // $FlowFixMe[react-rule-hook] - the condition is static + // $FlowFixMe[react-rule-hook-conditional] return useLoadMoreFunction_CURRENT(args); } @@ -138,6 +140,8 @@ hook useLoadMoreFunction_CURRENT( }; }, [disposeFetch]); + const isRequestInvalid = fragmentData == null || isParentQueryActive; + const loadMore = useCallback( ( count: number, @@ -166,11 +170,8 @@ hook useLoadMoreFunction_CURRENT( } const fragmentSelector = getSelector(fragmentNode, fragmentRef); - if ( - isFetchingRef.current === true || - fragmentData == null || - isParentQueryActive - ) { + + if (isFetchingRef.current === true || isRequestInvalid) { if (fragmentSelector == null) { warning( false, @@ -271,8 +272,7 @@ hook useLoadMoreFunction_CURRENT( disposeFetch, completeFetch, isFetchingRef, - isParentQueryActive, - fragmentData, + isRequestInvalid, fragmentNode.name, fragmentRef, componentDisplayName, diff --git a/packages/react-relay/relay-hooks/useLoadMoreFunction_EXPERIMENTAL.js b/packages/react-relay/relay-hooks/useLoadMoreFunction_EXPERIMENTAL.js index 7f6e40f1e7eb4..764152458f714 100644 --- a/packages/react-relay/relay-hooks/useLoadMoreFunction_EXPERIMENTAL.js +++ b/packages/react-relay/relay-hooks/useLoadMoreFunction_EXPERIMENTAL.js @@ -135,6 +135,8 @@ hook useLoadMoreFunction_EXPERIMENTAL( connectionPathInFragmentData, ); + const isRequestInvalid = fragmentData == null || isParentQueryActive; + const isMountedRef = useIsMountedRef(); const loadMore = useCallback( ( @@ -164,11 +166,8 @@ hook useLoadMoreFunction_EXPERIMENTAL( } const fragmentSelector = getSelector(fragmentNode, fragmentRef); - if ( - fetchStatusRef.current.kind === 'fetching' || - fragmentData == null || - isParentQueryActive - ) { + + if (fetchStatusRef.current.kind === 'fetching' || isRequestInvalid) { if (fragmentSelector == null) { warning( false, @@ -267,8 +266,7 @@ hook useLoadMoreFunction_EXPERIMENTAL( identifierValue, direction, cursor, - isParentQueryActive, - fragmentData, + isRequestInvalid, fragmentNode.name, fragmentRef, componentDisplayName, diff --git a/packages/react-relay/relay-hooks/usePaginationFragment.js b/packages/react-relay/relay-hooks/usePaginationFragment.js index 35a15b714a207..0ac525442ee9f 100644 --- a/packages/react-relay/relay-hooks/usePaginationFragment.js +++ b/packages/react-relay/relay-hooks/usePaginationFragment.js @@ -16,8 +16,6 @@ import type {Options} from './useRefetchableFragmentInternal'; import type { Disposable, FragmentType, - GraphQLResponse, - Observer, RefetchableFragment, Variables, } from 'relay-runtime'; @@ -28,6 +26,7 @@ const useRelayEnvironment = require('./useRelayEnvironment'); const useStaticFragmentNodeWarning = require('./useStaticFragmentNodeWarning'); const {useCallback, useDebugValue, useState} = require('react'); const { + RelayFeatureFlags, getFragment, getFragmentIdentifier, getPaginationMetadata, @@ -145,6 +144,7 @@ hook usePaginationFragment< if (__DEV__) { // eslint-disable-next-line react-hooks/rules-of-hooks // $FlowFixMe[react-rule-hook] + // $FlowFixMe[react-rule-hook-conditional] useDebugValue({ fragment: fragmentNode.name, data: fragmentData, @@ -168,14 +168,7 @@ hook usePaginationFragment< } hook useLoadMore( - args: $Diff< - UseLoadMoreFunctionArgs, - { - observer: Observer, - onReset: () => void, - ... - }, - >, + args: Omit, ): [LoadMoreFn, boolean, boolean, () => void] { const environment = useRelayEnvironment(); const [isLoadingMore, reallySetIsLoadingMore] = useState(false); @@ -196,6 +189,9 @@ hook useLoadMore( start: () => setIsLoadingMore(true), complete: () => setIsLoadingMore(false), error: () => setIsLoadingMore(false), + unsubscribe: RelayFeatureFlags.ENABLE_USE_PAGINATION_IS_LOADING_FIX + ? () => setIsLoadingMore(false) + : undefined, }; const handleReset = () => setIsLoadingMore(false); const [loadMore, hasMore, disposeFetch] = useLoadMoreFunction({ diff --git a/packages/react-relay/relay-hooks/usePrefetchableForwardPaginationFragment.js b/packages/react-relay/relay-hooks/usePrefetchableForwardPaginationFragment.js new file mode 100644 index 0000000000000..98c381935105c --- /dev/null +++ b/packages/react-relay/relay-hooks/usePrefetchableForwardPaginationFragment.js @@ -0,0 +1,436 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + * @oncall relay + */ + +'use strict'; + +import type {RefetchFn} from './useRefetchableFragment'; +import type {Options} from './useRefetchableFragmentInternal'; +import type {FragmentType, Variables} from 'relay-runtime'; +import type {PrefetchableRefetchableFragment} from 'relay-runtime'; + +const useFragment = require('./useFragment'); +const useLoadMoreFunction = require('./useLoadMoreFunction'); +const useRefetchableFragmentInternal = require('./useRefetchableFragmentInternal'); +const useRelayEnvironment = require('./useRelayEnvironment'); +const useStaticFragmentNodeWarning = require('./useStaticFragmentNodeWarning'); +const invariant = require('invariant'); +const { + useCallback, + useDebugValue, + useEffect, + useLayoutEffect, + useMemo, + useRef, + useState, +} = require('react'); +const { + getFragment, + getFragmentIdentifier, + getPaginationMetadata, +} = require('relay-runtime'); +const { + ConnectionInterface, + RelayFeatureFlags, + getSelector, + getValueAtPath, +} = require('relay-runtime'); + +type LoadMoreFn = ( + count: number, + options?: { + onComplete?: (Error | null) => void, + UNSTABLE_extraVariables?: Partial, + }, +) => void; + +export type ReturnType = { + // NOTE: This type ensures that the type of the returned data is either: + // - nullable if the provided ref type is nullable + // - non-nullable if the provided ref type is non-nullable + data: [+key: TKey] extends [+key: {+$fragmentSpreads: mixed, ...}] + ? TData + : ?TData, + loadNext: LoadMoreFn, + hasNext: boolean, + isLoadingNext: boolean, + refetch: RefetchFn, + edges: TEdgeData, +}; + +type LoadMoreOptions = { + UNSTABLE_extraVariables?: Partial, + onComplete?: (Error | null) => void, +}; + +export type GetExtraVariablesFn = ({ + hasNext: boolean, + data: [+key: TKey] extends [+key: {+$fragmentSpreads: mixed, ...}] + ? TData + : ?TData, + getServerEdges: () => TEdgeData, +}) => Partial; + +hook usePrefetchableForwardPaginationFragment< + TFragmentType: FragmentType, + TVariables: Variables, + TData, + TEdgeData, + TKey: ?{+$fragmentSpreads: TFragmentType, ...}, +>( + fragmentInput: PrefetchableRefetchableFragment< + TFragmentType, + TData, + TEdgeData, + TVariables, + >, + parentFragmentRef: TKey, + bufferSize: number, + initialSize?: ?number, + prefetchingLoadMoreOptions?: { + UNSTABLE_extraVariables?: + | Partial + | GetExtraVariablesFn, + onComplete?: (Error | null) => void, + }, + minimalFetchSize: number = 1, + disablePrefetching?: boolean = false, +): ReturnType { + const fragmentNode = getFragment(fragmentInput); + useStaticFragmentNodeWarning( + fragmentNode, + 'first argument of usePrefetchableForwardPaginationFragment()', + ); + const componentDisplayName = 'usePrefetchableForwardPaginationFragment()'; + + const {connectionPathInFragmentData, paginationRequest, paginationMetadata} = + getPaginationMetadata(fragmentNode, componentDisplayName); + + const {fragmentData, fragmentRef, refetch} = useRefetchableFragmentInternal< + {variables: TVariables, response: TData}, + {data?: TData}, + >(fragmentNode, parentFragmentRef, componentDisplayName); + // TODO: Get rid of `getFragmentIdentifier` + const fragmentIdentifier = getFragmentIdentifier(fragmentNode, fragmentRef); + + const edgeKeys = useMemo(() => { + const connection = getValueAtPath( + fragmentData, + connectionPathInFragmentData, + ); + if (connection == null) { + return null; + } + const {EDGES} = ConnectionInterface.get(); + // $FlowFixMe[incompatible-use] + return connection[EDGES]; + }, [connectionPathInFragmentData, fragmentData]); + + const sourceSize = edgeKeys == null ? -1 : edgeKeys.length; + + const [_numInUse, setNumInUse] = useState( + initialSize != null ? initialSize : sourceSize, + ); + let numInUse = _numInUse; + // We can only reset the source size when the component is + // updated with new edgeKeys + if (_numInUse === -1 && sourceSize !== -1) { + numInUse = initialSize != null ? initialSize : sourceSize; + setNumInUse(numInUse); + } + + const environment = useRelayEnvironment(); + const [isLoadingMore, reallySetIsLoadingMore] = useState(false); + const [isRefetching, setIsRefetching] = useState(false); + const availableSizeRef = useRef(0); + // Schedule this update since it must be observed by components at the same + // batch as when hasNext changes. hasNext is read from the store and store + // updates are scheduled, so this must be scheduled too. + const setIsLoadingMore = useCallback( + (value: boolean) => { + const schedule = environment.getScheduler()?.schedule; + if (schedule) { + schedule(() => { + reallySetIsLoadingMore(value); + }); + } else { + reallySetIsLoadingMore(value); + } + }, + [environment], + ); + + // `isLoadingMore` state is updated in a low priority, internally we need + // to synchronously get the loading state to decide whether to load more + const isLoadingMoreRef = useRef(false); + + const observer = useMemo(() => { + function setIsLoadingFalse() { + isLoadingMoreRef.current = false; + setIsLoadingMore(false); + } + return { + start: () => { + isLoadingMoreRef.current = true; + // We want to make sure that `isLoadingMore` is updated immediately, to avoid + // product code triggering multiple `loadMore` calls + reallySetIsLoadingMore(true); + }, + complete: setIsLoadingFalse, + error: setIsLoadingFalse, + unsubscribe: RelayFeatureFlags.ENABLE_USE_PAGINATION_IS_LOADING_FIX + ? setIsLoadingFalse + : undefined, + }; + }, [setIsLoadingMore]); + const handleReset = useCallback(() => { + if (!isRefetching) { + // Do not reset items count during refetching + const schedule = environment.getScheduler()?.schedule; + if (schedule) { + schedule(() => { + setNumInUse(-1); + }); + } else { + setNumInUse(-1); + } + } + isLoadingMoreRef.current = false; + setIsLoadingMore(false); + }, [environment, isRefetching, setIsLoadingMore]); + + const [loadMore, hasNext, disposeFetchNext] = useLoadMoreFunction( + { + componentDisplayName, + connectionPathInFragmentData, + direction: 'forward', + fragmentData, + fragmentIdentifier, + fragmentNode, + fragmentRef, + paginationMetadata, + paginationRequest, + observer, + onReset: handleReset, + }, + ); + + useLayoutEffect(() => { + // Make sure `availableSize` is updated before `showMore` from current render can be called + availableSizeRef.current = sourceSize - numInUse; + }, [numInUse, sourceSize]); + + const prefetchingUNSTABLE_extraVariables = + prefetchingLoadMoreOptions?.UNSTABLE_extraVariables; + const prefetchingOnComplete = prefetchingLoadMoreOptions?.onComplete; + + const showMore = useCallback( + (numToAdd: number, options?: LoadMoreOptions) => { + // Matches the behavior of `usePaginationFragment`. If there is a `loadMore` ongoing, + // the hook handles making the `loadMore` a no-op. + if (!isLoadingMoreRef.current || availableSizeRef.current >= 0) { + // Preemtively update `availableSizeRef`, so if two `loadMore` is called in the same tick, + // a second `loadMore` can be no-op + availableSizeRef.current -= numToAdd; + + setNumInUse(lastNumInUse => { + return lastNumInUse + numToAdd; + }); + + // If the product needs more items from network, load the amount needed to fullfil + // the requirement and cache, capped at the current amount defined by product + if (!isLoadingMoreRef.current && availableSizeRef.current < 0) { + loadMore( + Math.max( + minimalFetchSize, + Math.min(numToAdd, bufferSize - availableSizeRef.current), + ), + // Keep options For backward compatibility + options ?? { + onComplete: prefetchingOnComplete, + UNSTABLE_extraVariables: + typeof prefetchingUNSTABLE_extraVariables === 'function' + ? // $FlowFixMe[incompatible-call] + prefetchingUNSTABLE_extraVariables({ + hasNext, + // $FlowFixMe[incompatible-call] + data: fragmentData, + getServerEdges: () => { + const selector = getSelector( + // $FlowFixMe[incompatible-call] + edgesFragment, + edgeKeys, + ); + if (selector == null) { + // $FlowFixMe[incompatible-call] + return []; + } + invariant( + selector.kind === 'PluralReaderSelector', + 'Expected a plural selector', + ); + // $FlowFixMe[incompatible-call] + return selector.selectors.map( + sel => environment.lookup(sel).data, + ); + }, + }) + : prefetchingUNSTABLE_extraVariables, + }, + ); + } + } + }, + [ + bufferSize, + loadMore, + minimalFetchSize, + edgeKeys, + fragmentData, + prefetchingUNSTABLE_extraVariables, + prefetchingOnComplete, + ], + ); + + const edgesFragment = fragmentInput.metadata?.refetch?.edgesFragment; + invariant( + edgesFragment != null, + 'usePrefetchableForwardPaginationFragment: Expected the edge fragment to be defined, ' + + 'please make sure you have added `prefetchable_pagination: true` to `@connection`', + ); + + // Always try to keep `bufferSize` items in the buffer + // Or load the number of items that have been registred to show + useEffect(() => { + if ( + // Check the ref to avoid infinite `loadMore`, when a `loadMore` has started, + // but `isLoadingMore` isn't updated + !isLoadingMoreRef.current && + // Check the original `isLoadingMore` so when `loadMore` is called, the internal + // `loadMore` hook has been updated with the latest cursor + !isLoadingMore && + !isRefetching && + !disablePrefetching && + hasNext && + (sourceSize - numInUse < bufferSize || numInUse > sourceSize) + ) { + const onComplete = prefetchingOnComplete; + loadMore( + Math.max( + bufferSize - Math.max(sourceSize - numInUse, 0), + numInUse - sourceSize, + minimalFetchSize, + ), + { + onComplete, + UNSTABLE_extraVariables: + typeof prefetchingUNSTABLE_extraVariables === 'function' + ? // $FlowFixMe[incompatible-call] + prefetchingUNSTABLE_extraVariables({ + hasNext, + // $FlowFixMe[incompatible-call] + data: fragmentData, + getServerEdges: () => { + const selector = getSelector(edgesFragment, edgeKeys); + if (selector == null) { + // $FlowFixMe[incompatible-call] + return []; + } + invariant( + selector.kind === 'PluralReaderSelector', + 'Expected a plural selector', + ); + // $FlowFixMe[incompatible-call] + return selector.selectors.map( + sel => environment.lookup(sel).data, + ); + }, + }) + : prefetchingUNSTABLE_extraVariables, + }, + ); + } + }, [ + hasNext, + bufferSize, + isRefetching, + loadMore, + numInUse, + prefetchingUNSTABLE_extraVariables, + prefetchingOnComplete, + sourceSize, + edgeKeys, + isLoadingMore, + minimalFetchSize, + environment, + edgesFragment, + ]); + + const realNumInUse = Math.min(numInUse, sourceSize); + + const derivedEdgeKeys: $ReadOnlyArray = useMemo( + () => edgeKeys?.slice(0, realNumInUse) ?? [], + [edgeKeys, realNumInUse], + ); + + // $FlowExpectedError[incompatible-call] - we know derivedEdgeKeys are the correct keys + const edges: TEdgeData = useFragment(edgesFragment, derivedEdgeKeys); + + const refetchPagination = useCallback( + (variables: TVariables, options?: Options) => { + disposeFetchNext(); + setIsRefetching(true); + return refetch(variables, { + ...options, + onComplete: maybeError => { + // Need to be batched with the store update + const schedule = environment.getScheduler()?.schedule; + if (schedule) { + schedule(() => { + setIsRefetching(false); + setNumInUse(-1); + }); + } else { + setIsRefetching(false); + setNumInUse(-1); + } + options?.onComplete?.(maybeError); + }, + __environment: undefined, + }); + }, + [disposeFetchNext, environment, refetch], + ); + + if (__DEV__) { + // $FlowFixMe[react-rule-hook] + // $FlowFixMe[react-rule-hook-conditional] + useDebugValue({ + fragment: fragmentNode.name, + data: fragmentData, + hasNext, + isLoadingNext: isLoadingMore, + }); + } + + return { + edges, + // $FlowFixMe[incompatible-return] + data: fragmentData, + loadNext: showMore, + hasNext: hasNext || sourceSize > numInUse, + // Only reflect `isLoadingMore` if the product depends on it, do not refelect + // `isLoaindgMore` state if it is for fufilling the buffer + isLoadingNext: isLoadingMore && numInUse > sourceSize, + refetch: refetchPagination, + }; +} + +module.exports = usePrefetchableForwardPaginationFragment; diff --git a/packages/react-relay/relay-hooks/usePrefetchableForwardPaginationFragment_EXPERIMENTAL.js b/packages/react-relay/relay-hooks/usePrefetchableForwardPaginationFragment_EXPERIMENTAL.js index e8dacf7ab1f9f..8ddba5e91bdc8 100644 --- a/packages/react-relay/relay-hooks/usePrefetchableForwardPaginationFragment_EXPERIMENTAL.js +++ b/packages/react-relay/relay-hooks/usePrefetchableForwardPaginationFragment_EXPERIMENTAL.js @@ -38,6 +38,7 @@ const { } = require('relay-runtime'); const { ConnectionInterface, + RelayFeatureFlags, getSelector, getValueAtPath, } = require('relay-runtime'); @@ -170,25 +171,25 @@ hook usePrefetchableForwardPaginationFragment_EXPERIMENTAL< // to synchronously get the loading state to decide whether to load more const isLoadingMoreRef = useRef(false); - const observer = useMemo( - () => ({ + const observer = useMemo(() => { + function setIsLoadingFalse() { + isLoadingMoreRef.current = false; + setIsLoadingMore(false); + } + return { start: () => { isLoadingMoreRef.current = true; // We want to make sure that `isLoadingMore` is updated immediately, to avoid // product code triggering multiple `loadMore` calls reallySetIsLoadingMore(true); }, - complete: () => { - isLoadingMoreRef.current = false; - setIsLoadingMore(false); - }, - error: () => { - isLoadingMoreRef.current = false; - setIsLoadingMore(false); - }, - }), - [setIsLoadingMore], - ); + complete: setIsLoadingFalse, + error: setIsLoadingFalse, + unsubscribe: RelayFeatureFlags.ENABLE_USE_PAGINATION_IS_LOADING_FIX + ? setIsLoadingFalse + : undefined, + }; + }, [setIsLoadingMore]); const handleReset = useCallback(() => { if (!isRefetching) { // Do not reset items count during refetching @@ -409,6 +410,7 @@ hook usePrefetchableForwardPaginationFragment_EXPERIMENTAL< if (__DEV__) { // $FlowFixMe[react-rule-hook] + // $FlowFixMe[react-rule-hook-conditional] useDebugValue({ fragment: fragmentNode.name, data: fragmentData, diff --git a/packages/react-relay/relay-hooks/usePreloadedQuery.js b/packages/react-relay/relay-hooks/usePreloadedQuery.js index c5978c1e0972b..7b7f8f5710ac1 100644 --- a/packages/react-relay/relay-hooks/usePreloadedQuery.js +++ b/packages/react-relay/relay-hooks/usePreloadedQuery.js @@ -16,7 +16,7 @@ import type { PreloadedQueryInner, PreloadedQueryInner_DEPRECATED, } from './EntryPointTypes.flow'; -import type {Query, RenderPolicy, Variables} from 'relay-runtime'; +import type {ClientQuery, Query, RenderPolicy, Variables} from 'relay-runtime'; const useLazyLoadQueryNode = require('./useLazyLoadQueryNode'); const useMemoOperationDescriptor = require('./useMemoOperationDescriptor'); @@ -56,7 +56,9 @@ hook usePreloadedQuery< TData, TRawResponse: ?{...} = void, >( - gqlQuery: Query, + gqlQuery: + | Query + | ClientQuery, preloadedQuery: PreloadedQuery< TVariables, TData, @@ -126,7 +128,7 @@ hook usePreloadedQuery< // context, we cannot re-use the existing preloaded query. // Instead, we need to fall back and re-execute and de-dupe the query with // the new environment (at render time). - // TODO T68036756 track occurences of this warning and turn it into a hard error + // TODO T68036756 track occurrences of this warning and turn it into a hard error warning( false, 'usePreloadedQuery(): usePreloadedQuery was passed a preloaded query ' + @@ -155,11 +157,14 @@ hook usePreloadedQuery< variables: TVariables, response: TData, rawResponse?: $NonMaybeType, + /* $FlowFixMe[incompatible-call] Natural Inference rollout. See + * https://fburl.com/gdoc/y8dn025u */ }>(useLazyLoadQueryNodeParams); if (__DEV__) { // eslint-disable-next-line react-hooks/rules-of-hooks // $FlowFixMe[react-rule-hook] + // $FlowFixMe[react-rule-hook-conditional] useDebugValue({ query: preloadedQuery.name, variables: preloadedQuery.variables, diff --git a/packages/react-relay/relay-hooks/useQueryLoader.js b/packages/react-relay/relay-hooks/useQueryLoader.js index 62b09f8607482..9a206cc7f2218 100644 --- a/packages/react-relay/relay-hooks/useQueryLoader.js +++ b/packages/react-relay/relay-hooks/useQueryLoader.js @@ -46,7 +46,9 @@ export type UseQueryLoaderLoadQueryOptions = $ReadOnly<{ export type NullQueryReference = { kind: 'NullQueryReference', }; -const initialNullQueryReferenceState = {kind: 'NullQueryReference'}; +const initialNullQueryReferenceState: NullQueryReference = { + kind: 'NullQueryReference', +}; function requestIsLiveQuery< TVariables: Variables, @@ -119,12 +121,14 @@ hook useQueryLoader( ): UseQueryLoaderHookReturnType { if (RelayFeatureFlags.ENABLE_ACTIVITY_COMPATIBILITY) { // $FlowFixMe[react-rule-hook] - the condition is static + // $FlowFixMe[react-rule-hook-conditional] return useQueryLoader_EXPERIMENTAL( preloadableRequest, initialQueryReference, ); } // $FlowFixMe[react-rule-hook] - the condition is static + // $FlowFixMe[react-rule-hook-conditional] return useQueryLoader_CURRENT(preloadableRequest, initialQueryReference); } @@ -232,7 +236,7 @@ hook useQueryLoader_CURRENT< useEffect(() => { return () => { // Attempt to detect if the component was - // hidden (by Offscreen API), or fast refresh occured; + // hidden (by Offscreen API), or fast refresh occurred; // Only in these situations would the effect cleanup // for "unmounting" run multiple times, so if // we are ever able to read this ref with a value diff --git a/packages/react-relay/relay-hooks/useQueryLoader_EXPERIMENTAL.js b/packages/react-relay/relay-hooks/useQueryLoader_EXPERIMENTAL.js index b060b37740e89..e6c34259e0e22 100644 --- a/packages/react-relay/relay-hooks/useQueryLoader_EXPERIMENTAL.js +++ b/packages/react-relay/relay-hooks/useQueryLoader_EXPERIMENTAL.js @@ -35,7 +35,9 @@ const { } = require('react'); const {getRequest} = require('relay-runtime'); -const initialNullQueryReferenceState = {kind: 'NullQueryReference'}; +const initialNullQueryReferenceState: NullQueryReference = { + kind: 'NullQueryReference', +}; function requestIsLiveQuery< TVariables: Variables, diff --git a/packages/react-relay/relay-hooks/useRefetchableFragment.js b/packages/react-relay/relay-hooks/useRefetchableFragment.js index 69c99c39d78da..9e7ac737e83c5 100644 --- a/packages/react-relay/relay-hooks/useRefetchableFragment.js +++ b/packages/react-relay/relay-hooks/useRefetchableFragment.js @@ -85,6 +85,7 @@ hook useRefetchableFragment< if (__DEV__) { // eslint-disable-next-line react-hooks/rules-of-hooks // $FlowFixMe[react-rule-hook] + // $FlowFixMe[react-rule-hook-conditional] useDebugValue({fragment: fragmentNode.name, data: fragmentData}); } // $FlowFixMe[incompatible-return] diff --git a/packages/react-relay/relay-hooks/useRefetchableFragmentInternal.js b/packages/react-relay/relay-hooks/useRefetchableFragmentInternal.js index f82dcc25d1b0f..ab6b8dfa6b467 100644 --- a/packages/react-relay/relay-hooks/useRefetchableFragmentInternal.js +++ b/packages/react-relay/relay-hooks/useRefetchableFragmentInternal.js @@ -160,7 +160,7 @@ function reducer(state: RefetchState, action: Action): RefetchState { } } -hook useRefetchableFragmentNode< +hook useRefetchableFragmentInternal< TQuery: OperationType, TKey: ?{+$data?: mixed, ...}, >( @@ -582,7 +582,7 @@ if (__DEV__) { fragmentNode: ReaderFragment, componentDisplayName: string, ): void { - if (previousIDAndTypename == null) { + if (previousIDAndTypename == null || refetchedFragmentRef == null) { return; } const {ID_KEY} = require('relay-runtime'); @@ -605,4 +605,4 @@ if (__DEV__) { }; } -module.exports = useRefetchableFragmentNode; +module.exports = useRefetchableFragmentInternal; diff --git a/packages/react-relay/relay-hooks/useRelayEnvironment.js b/packages/react-relay/relay-hooks/useRelayEnvironment.js index ddb2a8e41b96d..ca5efe9eb3a23 100644 --- a/packages/react-relay/relay-hooks/useRelayEnvironment.js +++ b/packages/react-relay/relay-hooks/useRelayEnvironment.js @@ -17,6 +17,28 @@ const ReactRelayContext = require('./../ReactRelayContext'); const invariant = require('invariant'); const {useContext} = require('react'); +/** + * Hook used to access a Relay environment that was set by a [`RelayEnvironmentProvider`](../relay-environment-provider): + * + * @example + * const React = require('React'); + * + * const {useRelayEnvironment} = require('react-relay'); + * + * function MyComponent() { + * const environment = useRelayEnvironment(); + * + * const handler = useCallback(() => { + * // For example, can be used to pass the environment to functions + * // that require a Relay environment. + * commitMutation(environment, ...); + * }, [environment]) + * + * return (...); + * } + * + * module.exports = MyComponent; + */ hook useRelayEnvironment(): IEnvironment { const context = useContext(ReactRelayContext); invariant( diff --git a/packages/react-relay/relay-hooks/useRelayLoggingContext.js b/packages/react-relay/relay-hooks/useRelayLoggingContext.js new file mode 100644 index 0000000000000..4f466c9547889 --- /dev/null +++ b/packages/react-relay/relay-hooks/useRelayLoggingContext.js @@ -0,0 +1,21 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + * @oncall relay + */ + +'use strict'; + +const ReactRelayLoggingContext = require('./../ReactRelayLoggingContext'); +const {useContext} = require('react'); + +hook useRelayLoggingContext(): mixed | void { + return useContext(ReactRelayLoggingContext); +} + +module.exports = useRelayLoggingContext; diff --git a/packages/react-relay/relay-hooks/useStaticFragmentNodeWarning.js b/packages/react-relay/relay-hooks/useStaticFragmentNodeWarning.js index cbed8efb8cce6..077a416008ebd 100644 --- a/packages/react-relay/relay-hooks/useStaticFragmentNodeWarning.js +++ b/packages/react-relay/relay-hooks/useStaticFragmentNodeWarning.js @@ -26,6 +26,7 @@ hook useStaticFragmentNodeWarning( // cost of `useRef` in development mode to produce the warning. // eslint-disable-next-line react-hooks/rules-of-hooks // $FlowFixMe[react-rule-hook] + // $FlowFixMe[react-rule-hook-conditional] const initialPropRef = useUnsafeRef_DEPRECATED(fragmentNode.name); warning( // $FlowFixMe[react-rule-unsafe-ref] diff --git a/packages/relay-compiler/README.md b/packages/relay-compiler/README.md index 3213c568b1baf..ebe9dc2d6fd87 100644 --- a/packages/relay-compiler/README.md +++ b/packages/relay-compiler/README.md @@ -1,127 +1,3 @@ # Relay Compiler -Relay-Compiler is a code-generation toolkit for GraphQL. It contains the core -functionalities of GraphQL code-gen, including file parsing, validation, syntax -tree parsing and transformation. - -## Configuration in package.json - -The simplest way to configure relay is to add a new `relay` section to your -`package.json` that contains the relay config. - -At minimum, the relay config must specify where to find source files (i.e. files -containing `graphql` literals) and the GraphQL schema for the project. - -``` -// adding new section to package json -{ - ... - "scripts": { - "relay": "relay-compiler" - }, - ... - // relay configuration - "relay": { - "src": "./src", - "schema": "./src/schema/app_schema.graphql" - } -} -``` - -Relay Compiler will automatically discover the config if: - -- There is a `relay.config.json`, `relay.config.js` file at the root of the - project (i.e. in the same folder as the `package.json` file). -- The `package.json` file contains a `"relay"` key. - -Alternatively, the path to a configuration file can be specified as an argument: - -```shell -npm run relay ./relay.json -``` - -or with yarn - -```shell -yarn relay ./relay.json -``` - -Please note, in this case you'll need to provide a separate configuration for -the [babel plugin](https://www.npmjs.com/package/babel-plugin-relay). - -## File Finder - -Relay compiler uses [`watchman`](https://facebook.github.io/watchman/) to find -file sources, and "listen" to the file changes in the "watch" mode. If -`watchman` is not available, the compiler will use -[glob](https://docs.rs/glob/latest/glob/) to query the filesystem for files. - -## Configuration - -### Supported compiler configuration options - -- `src` Root directory of application code. [string] [required] -- `schema` Relative path to the file with GraphQL SDL file. [string] [required] -- `language` The name of the language used for input files and generated - artifacts. ["javascript" | "typescript" | "flow"] [required]. -- `artifactDirectory` A specific directory to output all artifacts to. When - enabling this the babel plugin needs `artifactDirectory` to be set as well. - [string] -- `excludes` Directories to ignore under `src`. [array] [default: - ["\*\*/node_modules/\*\*", "\*\*/__mocks__/\*\*", "\*\*/__generated__/\*\*"]] -- `schemaExtensions` List of directories with schema extensions. [array] -- `schemaConfig` - - `nodeInterfaceIdField` Configure the name of the globally unique ID field on - the Node interface. Useful if you can't use the default `id` field name. - - `nodeInterfaceIdVariableName` Specifies the name of the variable expected by the `node` query to pass the Node id. - [string][default: "id"] - - `nonNodeIdFields` Restricts the type of all fields named `id` to `ID`. - - `allowedIdTypes` Mappings from types in your schema to allowed types - for their fields named `id` (e.g. "ObjectType": "CustomIdType"). [object] -- `noFutureProofEnums` This option controls whether or not a - catch-all entry is added to enum type definitions values that may be added in - the future. Enabling this means you will have to update your application - whenever the GraphQL server schema adds new enum values to prevent it from - breaking. [boolean][default: false] -- `customScalarTypes` Mappings from custom scalars in your schema to built-in - GraphQL types, for type emission purposes (eg. {"GqlScalar": "TStype"}). [object] -- `eagerEsModules` This option enables emitting ES modules artifacts. - [boolean][default: false] -- `persistConfig` Relay supports two versions of the config: -- - **Remote Persisting:** - - - `url` String, URL to send a POST request to to persist. This field is - required in `persistConfig` [string] - - `params` The document will be in a `POST` parameter `text`. This map can - contain additional parameters to send. [object] - - `concurrency` The maximum number concurrent requests that will be made to - `url`. Use a value greater than 0. [number] - - `include_query_text` Boolean, whether to include the query text in the - generated files. [boolean] [default: false] -- - **Local Persisting:** - - `file` Path for the JSON file that will contain operations map. Compiler - will write queries in the format: { "md5(queryText) => "queryText", ...}. - [string] - - `include_query_text` Boolean, whether to include the query text in the - generated files. [boolean] [default: false] - -- `codegenCommand` Command name that for relay compiler. [string] - -- `isDevVariableName` Name of the global variable for dev mode (`__DEV__`). - [string] -- `jsModuleFormat` Formatting style for generated files. `commonjs` or `haste`. - Default is `commonjs`. [string] -- `diagnosticReportConfig` Options for configuring the output of compiler - diagnostics. [object] - - `criticalLevel` The severity level of diagnostics that will cause the - compiler to error out on. ["error" | "warning" | "info"] - -### CLI Arguments - -- `--repersist` Run the persister even if the query has not changed. -- `--watch` Run compiler in `watch` mode. Requires - [`watchman`](https://facebook.github.io/watchman/) to be installed. -- `--output` Output format of the compiler. Supported options: `debug` | - `verbose` | `quiet` | `quietWithErrors`. The default value is `verbose`. -- `--validate` Looks for pending changes and exits with non-zero code instead of - writing to disk. +See https://relay.dev/docs/next/guides/compiler/ diff --git a/packages/relay-compiler/package.json b/packages/relay-compiler/package.json index aeadc44ddacd0..e98753a6b4ca1 100644 --- a/packages/relay-compiler/package.json +++ b/packages/relay-compiler/package.json @@ -1,7 +1,7 @@ { "name": "relay-compiler", "description": "A compiler tool for building GraphQL-driven applications.", - "version": "18.2.0", + "version": "20.1.1", "keywords": [ "graphql", "relay" diff --git a/packages/relay-runtime/experimental.js b/packages/relay-runtime/experimental.js index 2b0dda983d6b0..f9f041945f198 100644 --- a/packages/relay-runtime/experimental.js +++ b/packages/relay-runtime/experimental.js @@ -16,6 +16,7 @@ import type {DataID} from './util/RelayRuntimeTypes'; const resolverDataInjector = require('./store/live-resolvers/resolverDataInjector'); const {observeFragment} = require('./store/observeFragmentExperimental'); +const {observeQuery} = require('./store/observeQueryExperimental'); const {waitForFragmentData} = require('./store/waitForFragmentExperimental'); // Annotates a strong object return type, where `A` is the GraphQL typename and `Typename` is the @@ -43,13 +44,13 @@ export type IdOf = [ export type RelayResolverValue
= $NonMaybeType; type ErrorResult = { - ok: false, - errors: $ReadOnlyArray, + +ok: false, + +errors: $ReadOnlyArray, }; type OkayResult = { - ok: true, - value: T, + +ok: true, + +value: T, }; export type Result = OkayResult | ErrorResult; @@ -57,19 +58,20 @@ export type Result = OkayResult | ErrorResult; function isValueResult( input: Result, ): input is OkayResult { - return input.ok === true; + return input.ok === (true as const); } function isErrorResult( input: Result, ): input is ErrorResult { - return input.ok === false; + return input.ok === (false as const); } module.exports = { resolverDataInjector, isValueResult, isErrorResult, + observeQuery, observeFragment, waitForFragmentData, }; diff --git a/packages/relay-runtime/handlers/connection/__tests__/ConnectionHandler-test.js b/packages/relay-runtime/handlers/connection/__tests__/ConnectionHandler-test.js index bb7399b48b27e..d2485b483492c 100644 --- a/packages/relay-runtime/handlers/connection/__tests__/ConnectionHandler-test.js +++ b/packages/relay-runtime/handlers/connection/__tests__/ConnectionHandler-test.js @@ -72,7 +72,7 @@ describe('ConnectionHandler', () => { }); sinkSource = new RelayRecordSource({}); mutator = new RelayRecordSourceMutator(baseSource, sinkSource); - proxy = new RelayRecordSourceProxy(mutator, defaultGetDataID); + proxy = new RelayRecordSourceProxy(mutator, defaultGetDataID, null); ConnectionQuery = graphql` query ConnectionHandlerTestConnectionQuery( @@ -296,7 +296,7 @@ describe('ConnectionHandler', () => { baseSource = new RelayRecordSource(baseSource.toJSON()); sinkSource = new RelayRecordSource({}); mutator = new RelayRecordSourceMutator(baseSource, sinkSource); - proxy = new RelayRecordSourceProxy(mutator, defaultGetDataID); + proxy = new RelayRecordSourceProxy(mutator, defaultGetDataID, null); connection = ConnectionHandler.getConnection( proxy.get('4'), @@ -425,7 +425,7 @@ describe('ConnectionHandler', () => { baseSource = new RelayRecordSource(simpleClone(baseSource.toJSON())); sinkSource = new RelayRecordSource({}); mutator = new RelayRecordSourceMutator(baseSource, sinkSource); - proxy = new RelayRecordSourceProxy(mutator, defaultGetDataID); + proxy = new RelayRecordSourceProxy(mutator, defaultGetDataID, null); connection = ConnectionHandler.getConnection( proxy.get('4'), @@ -553,7 +553,7 @@ describe('ConnectionHandler', () => { baseSource = new RelayRecordSource(simpleClone(baseSource.toJSON())); sinkSource = new RelayRecordSource({}); mutator = new RelayRecordSourceMutator(baseSource, sinkSource); - proxy = new RelayRecordSourceProxy(mutator, defaultGetDataID); + proxy = new RelayRecordSourceProxy(mutator, defaultGetDataID, null); connection = ConnectionHandler.getConnection( proxy.get('4'), @@ -867,7 +867,7 @@ describe('ConnectionHandler', () => { baseSource = new RelayRecordSource(simpleClone(baseSource.toJSON())); sinkSource = new RelayRecordSource({}); mutator = new RelayRecordSourceMutator(baseSource, sinkSource); - proxy = new RelayRecordSourceProxy(mutator, defaultGetDataID); + proxy = new RelayRecordSourceProxy(mutator, defaultGetDataID, null); }); it('appends new edges', () => { diff --git a/packages/relay-runtime/index.js b/packages/relay-runtime/index.js index 6a5d7c33d5717..074d4c53f9533 100644 --- a/packages/relay-runtime/index.js +++ b/packages/relay-runtime/index.js @@ -35,6 +35,7 @@ const { } = require('./store/ClientID'); const createFragmentSpecResolver = require('./store/createFragmentSpecResolver'); const createRelayContext = require('./store/createRelayContext'); +const createRelayLoggingContext = require('./store/createRelayLoggingContext'); const isRelayModernEnvironment = require('./store/isRelayModernEnvironment'); const { isSuspenseSentinel, @@ -316,6 +317,7 @@ module.exports = { suspenseSentinel, isRequest: GraphQLTag.isRequest, readInlineData, + readFragment: ResolverFragments.readFragment, // Declarative mutation API MutationTypes: RelayDeclarativeMutationConfig.MutationTypes, @@ -379,6 +381,7 @@ module.exports = { ResolverFragments, OperationTracker: RelayOperationTracker, createRelayContext: createRelayContext, + createRelayLoggingContext: createRelayLoggingContext, getOperationVariables: RelayConcreteVariables.getOperationVariables, getLocalVariables: RelayConcreteVariables.getLocalVariables, fetchQuery: fetchQueryInternal.fetchQuery, diff --git a/packages/relay-runtime/multi-actor-environment/ActorSpecificEnvironment.js b/packages/relay-runtime/multi-actor-environment/ActorSpecificEnvironment.js index 422102c3884cf..15e1579fe965e 100644 --- a/packages/relay-runtime/multi-actor-environment/ActorSpecificEnvironment.js +++ b/packages/relay-runtime/multi-actor-environment/ActorSpecificEnvironment.js @@ -87,6 +87,7 @@ class ActorSpecificEnvironment implements IActorEnvironment { config.handlerProvider, defaultGetDataID, config.missingFieldHandlers, + this.__log, ); this._defaultRenderPolicy = config.defaultRenderPolicy; // TODO:T92305692 Remove `options` in favor of directly using `actorIdentifier` on the environment diff --git a/packages/relay-runtime/multi-actor-environment/__tests__/MultiActorEnvironment-ExecuteMutation-test.js b/packages/relay-runtime/multi-actor-environment/__tests__/MultiActorEnvironment-ExecuteMutation-test.js index 4f3d6e45f080b..40ff0ad56cd0b 100644 --- a/packages/relay-runtime/multi-actor-environment/__tests__/MultiActorEnvironment-ExecuteMutation-test.js +++ b/packages/relay-runtime/multi-actor-environment/__tests__/MultiActorEnvironment-ExecuteMutation-test.js @@ -90,6 +90,7 @@ describe('executeMutation()', () => { node(id: $id) { id ...MultiActorEnvironmentExecuteMutationTestCommentFragment + @dangerously_unaliased_fixme } } `; diff --git a/packages/relay-runtime/multi-actor-environment/__tests__/__generated__/MultiActorEnvironmentExecuteMutationTestCommentQuery.graphql.js b/packages/relay-runtime/multi-actor-environment/__tests__/__generated__/MultiActorEnvironmentExecuteMutationTestCommentQuery.graphql.js index 6a28916b7b44d..f5a94a729107d 100644 --- a/packages/relay-runtime/multi-actor-environment/__tests__/__generated__/MultiActorEnvironmentExecuteMutationTestCommentQuery.graphql.js +++ b/packages/relay-runtime/multi-actor-environment/__tests__/__generated__/MultiActorEnvironmentExecuteMutationTestCommentQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -148,7 +148,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "dd845fc72f2a9b2b232f69a75bfae3f7"; + (node/*: any*/).hash = "40c037e72d602053337597c799fb840d"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/mutations/RelayRecordSourceProxy.js b/packages/relay-runtime/mutations/RelayRecordSourceProxy.js index dceb5c0d5e9ad..e81b4104ea525 100644 --- a/packages/relay-runtime/mutations/RelayRecordSourceProxy.js +++ b/packages/relay-runtime/mutations/RelayRecordSourceProxy.js @@ -18,6 +18,7 @@ import type { FragmentType, HandleFieldPayload, HasUpdatableSpread, + LogFunction, MissingFieldHandler, RecordProxy, RecordSource, @@ -53,12 +54,14 @@ class RelayRecordSourceProxy implements RecordSourceProxy { _invalidatedStore: boolean; _idsMarkedForInvalidation: DataIDSet; _missingFieldHandlers: $ReadOnlyArray; + _log: LogFunction; constructor( mutator: RelayRecordSourceMutator, getDataID: GetDataID, handlerProvider?: ?HandlerProvider, missingFieldHandlers: $ReadOnlyArray, + log: ?LogFunction, ) { this.__mutator = mutator; this._handlerProvider = handlerProvider || null; @@ -67,6 +70,7 @@ class RelayRecordSourceProxy implements RecordSourceProxy { this._invalidatedStore = false; this._idsMarkedForInvalidation = new Set(); this._missingFieldHandlers = missingFieldHandlers; + this._log = log ?? (LogEvent => {}); } publishSource( diff --git a/packages/relay-runtime/mutations/__tests__/RelayRecordSourceProxy-test.js b/packages/relay-runtime/mutations/__tests__/RelayRecordSourceProxy-test.js index 77bc183bb5d41..a5e11413a4f24 100644 --- a/packages/relay-runtime/mutations/__tests__/RelayRecordSourceProxy-test.js +++ b/packages/relay-runtime/mutations/__tests__/RelayRecordSourceProxy-test.js @@ -11,6 +11,8 @@ 'use strict'; +import type {RecordSourceJSON} from '../../store/RelayStoreTypes'; + const defaultGetDataID = require('../../store/defaultGetDataID'); const RelayModernRecord = require('../../store/RelayModernRecord'); const RelayRecordSource = require('../../store/RelayRecordSource'); @@ -28,7 +30,7 @@ describe('RelayRecordSourceProxy', () => { let mutator; let store; let sinkSource; - const initialData = { + const initialData: RecordSourceJSON = { '4': { [ID_KEY]: '4', [TYPENAME_KEY]: 'User', @@ -80,6 +82,7 @@ describe('RelayRecordSourceProxy', () => { defaultGetDataID, undefined, [], + null, ); }); diff --git a/packages/relay-runtime/mutations/__tests__/__generated__/readUpdatableQueryTestRegularQuery.graphql.js b/packages/relay-runtime/mutations/__tests__/__generated__/readUpdatableQueryTestRegularQuery.graphql.js index b4a1a3f0e18fd..a8e4cbf05780f 100644 --- a/packages/relay-runtime/mutations/__tests__/__generated__/readUpdatableQueryTestRegularQuery.graphql.js +++ b/packages/relay-runtime/mutations/__tests__/__generated__/readUpdatableQueryTestRegularQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<6a07a0613287459204a27bf01dd58957>> + * @generated SignedSource<<834b1cc235e29a0149d68f66240f01ad>> * @flow * @lightSyntaxTransform * @nogrep @@ -352,7 +352,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "bb6d520f1d382edceed7c6abb54ac6bb"; + (node/*: any*/).hash = "46eea2f5976dc4bcb1af1d5b7479d9b8"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/mutations/__tests__/__generated__/validateMutationTestIsEntitySpreadFragmentMutation.graphql.js b/packages/relay-runtime/mutations/__tests__/__generated__/validateMutationTestIsEntitySpreadFragmentMutation.graphql.js index 5e6f311862944..15431239fb38b 100644 --- a/packages/relay-runtime/mutations/__tests__/__generated__/validateMutationTestIsEntitySpreadFragmentMutation.graphql.js +++ b/packages/relay-runtime/mutations/__tests__/__generated__/validateMutationTestIsEntitySpreadFragmentMutation.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<0248d6513837bacbfbff93448840442e>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -172,7 +172,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "ce0ae1ce1272a3055e3e6a83c36a8c6a"; + (node/*: any*/).hash = "91935f764b37e5a36ce360731e9f75ed"; } module.exports = ((node/*: any*/)/*: Mutation< diff --git a/packages/relay-runtime/mutations/__tests__/commitMutation-test.js b/packages/relay-runtime/mutations/__tests__/commitMutation-test.js index 4cecfe2ea0cce..690ae802bdaed 100644 --- a/packages/relay-runtime/mutations/__tests__/commitMutation-test.js +++ b/packages/relay-runtime/mutations/__tests__/commitMutation-test.js @@ -13,6 +13,7 @@ import type {GraphQLResponseWithoutData} from '../../network/RelayNetworkTypes'; import type {Snapshot} from '../../store/RelayStoreTypes'; import type {RecordSourceSelectorProxy} from '../../store/RelayStoreTypes'; +import type {DeclarativeMutationConfig} from '../RelayDeclarativeMutationConfig'; import type { commitMutationTest4Query$data, commitMutationTest4Query$variables, @@ -21,6 +22,7 @@ import type { commitMutationTest5Query$data, commitMutationTest5Query$variables, } from './__generated__/commitMutationTest5Query.graphql'; +import type {commitMutationTest6Mutation$variables} from './__generated__/commitMutationTest6Mutation.graphql'; import type {CacheConfig, Query} from 'relay-runtime/util/RelayRuntimeTypes'; const ConnectionHandler = require('../../handlers/connection/ConnectionHandler'); @@ -116,7 +118,7 @@ describe('Configs: NODE_DELETE', () => { }, }, }); - const configs = [ + const configs: Array = [ { type: 'NODE_DELETE', deletedIDFieldName: 'deletedCommentId', @@ -144,12 +146,12 @@ describe('Configs: NODE_DELETE', () => { store.subscribe(snapshot, callback); commitMutation(environment, { configs, - /* $FlowFixMe[prop-missing] error exposed when improving flow typing of - * commitMutation */ mutation, optimisticResponse, optimisticUpdater, updater, + /* $FlowFixMe[prop-missing] error exposed when improving flow typing of + * commitMutation */ variables, }); expect(callback.mock.calls.length).toBe(1); @@ -216,7 +218,7 @@ describe('Configs: RANGE_DELETE', () => { }, }, }; - const configs = [ + const configs: Array = [ { type: 'RANGE_DELETE', parentName: 'feedback', @@ -341,7 +343,7 @@ describe('Configs: RANGE_DELETE', () => { } } `; - const configs = [ + const configs: Array = [ { type: 'RANGE_DELETE', parentName: 'actor', @@ -570,7 +572,7 @@ describe('Configs: RANGE_ADD', () => { }); it('appends new edge', () => { - const configs = [ + const configs: Array = [ { type: 'RANGE_ADD', connectionName: 'topLevelComments', @@ -597,14 +599,14 @@ describe('Configs: RANGE_ADD', () => { store.subscribe(snapshot, callback); commitMutation(environment, { configs, - /* $FlowFixMe[prop-missing] error exposed when improving flow typing of - * commitMutation */ - /* $FlowFixMe[incompatible-call] error exposed when improving flow typing - * of commitMutation */ mutation, optimisticResponse, optimisticUpdater, updater, + /* $FlowFixMe[prop-missing] error exposed when improving flow typing of + * commitMutation */ + /* $FlowFixMe[incompatible-call] error exposed when improving flow typing + * of commitMutation */ variables, }); // Optimistically appends @@ -621,7 +623,7 @@ describe('Configs: RANGE_ADD', () => { }); it('does not overwrite previous edge when appended multiple times', () => { - const configs = [ + const configs: Array = [ { type: 'RANGE_ADD', connectionName: 'topLevelComments', @@ -657,11 +659,11 @@ describe('Configs: RANGE_ADD', () => { // send mutation commitMutation(environment, { configs, + mutation, /* $FlowFixMe[prop-missing] error exposed when improving flow typing of * commitMutation */ /* $FlowFixMe[incompatible-call] error exposed when improving flow typing * of commitMutation */ - mutation, variables, }); @@ -746,11 +748,11 @@ describe('Configs: RANGE_ADD', () => { // send the same mutation again commitMutation(environment, { configs, + mutation, /* $FlowFixMe[prop-missing] error exposed when improving flow typing of * commitMutation */ /* $FlowFixMe[incompatible-call] error exposed when improving flow typing * of commitMutation */ - mutation, variables, }); environment.mock.resolve(operation, serverResponse); @@ -803,7 +805,7 @@ describe('Configs: RANGE_ADD', () => { }); it('prepends new edge', () => { - const configs = [ + const configs: Array = [ { type: 'RANGE_ADD', connectionName: 'topLevelComments', @@ -830,14 +832,14 @@ describe('Configs: RANGE_ADD', () => { store.subscribe(snapshot, callback); commitMutation(environment, { configs, - /* $FlowFixMe[prop-missing] error exposed when improving flow typing of - * commitMutation */ - /* $FlowFixMe[incompatible-call] error exposed when improving flow typing - * of commitMutation */ mutation, optimisticResponse, optimisticUpdater, updater, + /* $FlowFixMe[prop-missing] error exposed when improving flow typing of + * commitMutation */ + /* $FlowFixMe[incompatible-call] error exposed when improving flow typing + * of commitMutation */ variables, }); // Optimistically prepends @@ -854,7 +856,7 @@ describe('Configs: RANGE_ADD', () => { }); it('filters connections then applies the rangeBehavior', () => { - const configs = [ + const configs: Array = [ { type: 'RANGE_ADD', connectionName: 'topLevelComments', @@ -899,14 +901,14 @@ describe('Configs: RANGE_ADD', () => { store.subscribe(snapshot, callback); commitMutation(environment, { configs, - /* $FlowFixMe[prop-missing] error exposed when improving flow typing of - * commitMutation */ - /* $FlowFixMe[incompatible-call] error exposed when improving flow typing - * of commitMutation */ mutation, optimisticResponse, optimisticUpdater, updater, + /* $FlowFixMe[prop-missing] error exposed when improving flow typing of + * commitMutation */ + /* $FlowFixMe[incompatible-call] error exposed when improving flow typing + * of commitMutation */ variables, }); // Optimistically appends orderBy(chronological) @@ -958,11 +960,11 @@ describe('Configs: RANGE_ADD', () => { // send mutation commitMutation(environment, { updater, + mutation, /* $FlowFixMe[prop-missing] error exposed when improving flow typing of * commitMutation */ /* $FlowFixMe[incompatible-call] error exposed when improving flow typing * of commitMutation */ - mutation, variables, }); @@ -1053,11 +1055,11 @@ describe('Configs: RANGE_ADD', () => { // send the same mutation again commitMutation(environment, { updater, + mutation, /* $FlowFixMe[prop-missing] error exposed when improving flow typing of * commitMutation */ /* $FlowFixMe[incompatible-call] error exposed when improving flow typing * of commitMutation */ - mutation, variables, }); environment.mock.resolve(operation, serverResponse); @@ -1204,7 +1206,7 @@ describe('commitMutation()', () => { let onCompleted; let onError; let onNext; - let variables; + let variables: commitMutationTest6Mutation$variables; beforeEach(() => { fragment = graphql` @@ -1531,7 +1533,7 @@ describe('commitMutation() cacheConfig', () => { let environment; let fragment; let mutation; - let variables; + let variables: commitMutationTest6Mutation$variables; beforeEach(() => { fragment = graphql` diff --git a/packages/relay-runtime/mutations/__tests__/readUpdatableFragment-test.js b/packages/relay-runtime/mutations/__tests__/readUpdatableFragment-test.js index 45a9fff50c7c2..4d4ac47a27ef3 100644 --- a/packages/relay-runtime/mutations/__tests__/readUpdatableFragment-test.js +++ b/packages/relay-runtime/mutations/__tests__/readUpdatableFragment-test.js @@ -30,6 +30,7 @@ const RelayModernStore = require('../../store/RelayModernStore'); const RelayReader = require('../../store/RelayReader'); const RelayRecordSource = require('../../store/RelayRecordSource'); const commitLocalUpdate = require('../commitLocalUpdate'); + const regularQuery = graphql` query readUpdatableFragmentTestRegularQuery($if2: Boolean, $if3: Boolean) { me { diff --git a/packages/relay-runtime/mutations/__tests__/readUpdatableQuery-test.js b/packages/relay-runtime/mutations/__tests__/readUpdatableQuery-test.js index f135da58c1f21..29650cc329048 100644 --- a/packages/relay-runtime/mutations/__tests__/readUpdatableQuery-test.js +++ b/packages/relay-runtime/mutations/__tests__/readUpdatableQuery-test.js @@ -119,7 +119,7 @@ const regularQuery = graphql` } } node(id: "4") { - ...readUpdatableQueryTest_user + ...readUpdatableQueryTest_user @dangerously_unaliased_fixme ... on User { name } diff --git a/packages/relay-runtime/mutations/__tests__/validateMutation-test.js b/packages/relay-runtime/mutations/__tests__/validateMutation-test.js index fe54f29d60252..e7db3b14998a0 100644 --- a/packages/relay-runtime/mutations/__tests__/validateMutation-test.js +++ b/packages/relay-runtime/mutations/__tests__/validateMutation-test.js @@ -760,6 +760,7 @@ describe('validateOptimisticResponse', () => { actorNameChange(input: $input) { actor { ...validateMutationTestEntityFragement + @dangerously_unaliased_fixme } } } diff --git a/packages/relay-runtime/mutations/commitMutation.js b/packages/relay-runtime/mutations/commitMutation.js index 00754e506b829..d80e72d161d80 100644 --- a/packages/relay-runtime/mutations/commitMutation.js +++ b/packages/relay-runtime/mutations/commitMutation.js @@ -37,7 +37,7 @@ const validateMutation = require('./validateMutation'); const invariant = require('invariant'); const warning = require('warning'); -export type MutationConfig = { +export type MutationConfig = $ReadOnly<{ cacheConfig?: CacheConfig, configs?: Array, mutation: GraphQLTaggedNode, @@ -57,9 +57,9 @@ export type MutationConfig = { updater?: ?SelectorStoreUpdater, uploadables?: UploadableMap, variables: TMutation['variables'], -}; +}>; -export type CommitMutationConfig = { +export type CommitMutationConfig = $ReadOnly<{ cacheConfig?: CacheConfig, configs?: Array, mutation: Mutation, @@ -71,8 +71,8 @@ export type CommitMutationConfig = { optimisticUpdater?: ?SelectorStoreUpdater, updater?: ?SelectorStoreUpdater, uploadables?: UploadableMap, - variables: TVariables, -}; + variables: NoInfer, +}>; /** * Higher-level helper function to execute a mutation against a specific diff --git a/packages/relay-runtime/mutations/createUpdatableProxy.js b/packages/relay-runtime/mutations/createUpdatableProxy.js index bf1ea3cde711f..62536a04548e6 100644 --- a/packages/relay-runtime/mutations/createUpdatableProxy.js +++ b/packages/relay-runtime/mutations/createUpdatableProxy.js @@ -181,11 +181,11 @@ function updateProxyFromSelections( case 'ClientEdgeToServerObject': case 'Defer': case 'ModuleImport': - case 'RelayLiveResolver': case 'RequiredField': case 'CatchField': case 'Stream': case 'RelayResolver': + case 'RelayLiveResolver': // These types of reader nodes are not currently handled. throw new Error( 'Encountered an unexpected ReaderSelection variant in RelayRecordSourceProxy. This indicates a bug in Relay.', diff --git a/packages/relay-runtime/mutations/validateMutation.js b/packages/relay-runtime/mutations/validateMutation.js index 9b1f50b965872..06877bdba1474 100644 --- a/packages/relay-runtime/mutations/validateMutation.js +++ b/packages/relay-runtime/mutations/validateMutation.js @@ -145,13 +145,13 @@ if (__DEV__) { return validateModuleImport(context); case 'TypeDiscriminator': return validateAbstractKey(context, selection.abstractKey); - case 'RelayResolver': - case 'RelayLiveResolver': case 'ClientEdgeToClientObject': case 'LinkedHandle': case 'ScalarHandle': case 'Defer': - case 'Stream': { + case 'Stream': + case 'RelayResolver': + case 'RelayLiveResolver': { // TODO(T35864292) - Add missing validations for these types return; } diff --git a/packages/relay-runtime/network/RelayNetworkTypes.js b/packages/relay-runtime/network/RelayNetworkTypes.js index 244edae3fcfed..73fd7ede607ec 100644 --- a/packages/relay-runtime/network/RelayNetworkTypes.js +++ b/packages/relay-runtime/network/RelayNetworkTypes.js @@ -11,6 +11,7 @@ 'use strict'; +import type {OperationAvailability} from '../store/RelayStoreTypes'; import type {RequestParameters} from '../util/RelayConcreteNode'; import type {CacheConfig, Variables} from '../util/RelayRuntimeTypes'; import type RelayObservable, {ObservableFromValue} from './RelayObservable'; @@ -25,7 +26,7 @@ export interface INetwork { export type LogRequestInfoFunction = mixed => void; -export type PayloadData = {[key: string]: mixed}; +export type PayloadData = {+[key: string]: mixed}; export type PayloadError = interface { message: string, @@ -104,6 +105,8 @@ export type ExecuteFunction = ( logRequestInfo?: ?LogRequestInfoFunction, encryptedVariables?: ?string, preprocessResponse?: ?preprocessResponseFunction, + // Run datachecker on the current operation and returns the OperationAvailability + checkOperation?: () => OperationAvailability, ) => RelayObservable; /** @@ -131,4 +134,4 @@ export type SubscribeFunction = ( ) => RelayObservable; export type Uploadable = File | Blob; -export type UploadableMap = {[key: string]: Uploadable}; +export type UploadableMap = {+[key: string]: Uploadable}; diff --git a/packages/relay-runtime/network/RelayObservable.js b/packages/relay-runtime/network/RelayObservable.js index 6d5a11547a320..e155135e2623a 100644 --- a/packages/relay-runtime/network/RelayObservable.js +++ b/packages/relay-runtime/network/RelayObservable.js @@ -614,11 +614,7 @@ if (__DEV__) { // Default implementation of HostReportErrors() in development builds. // Can be replaced by the host application environment. RelayObservable.onUnhandledError((error, isUncaughtThrownError) => { - declare function fail(string): void; - if (typeof fail === 'function') { - // In test environments (Jest), fail() immediately fails the current test. - fail(String(error)); - } else if (isUncaughtThrownError) { + if (isUncaughtThrownError) { // Rethrow uncaught thrown errors on the next frame to avoid breaking // current logic. setTimeout(() => { diff --git a/packages/relay-runtime/network/wrapNetworkWithLogObserver.js b/packages/relay-runtime/network/wrapNetworkWithLogObserver.js index aea0a0f88380a..94918a8b941a6 100644 --- a/packages/relay-runtime/network/wrapNetworkWithLogObserver.js +++ b/packages/relay-runtime/network/wrapNetworkWithLogObserver.js @@ -12,12 +12,17 @@ 'use strict'; import type ActorSpecificEnvironment from '../multi-actor-environment/ActorSpecificEnvironment'; import type RelayModernEnvironment from '../store/RelayModernEnvironment'; +import type { + LogRequestInfoFunction, + OperationAvailability, +} from '../store/RelayStoreTypes'; import type {RequestParameters} from '../util/RelayConcreteNode'; import type {CacheConfig, Variables} from '../util/RelayRuntimeTypes'; import type { GraphQLResponse, INetwork, UploadableMap, + preprocessResponseFunction, } from './RelayNetworkTypes'; import type RelayObservable from './RelayObservable'; import type {Subscription} from './RelayObservable'; @@ -42,6 +47,10 @@ function wrapNetworkWithLogObserver( variables: Variables, cacheConfig: CacheConfig, uploadables?: ?UploadableMap, + _?: ?LogRequestInfoFunction, + encryptedVariables?: ?string, + preprocessResponse?: ?preprocessResponseFunction, + checkOperation?: () => OperationAvailability, ): RelayObservable { const networkRequestId = generateID(); const logObserver = { @@ -89,7 +98,16 @@ function wrapNetworkWithLogObserver( }); }; return network - .execute(params, variables, cacheConfig, uploadables, logRequestInfo) + .execute( + params, + variables, + cacheConfig, + uploadables, + logRequestInfo, + encryptedVariables, + preprocessResponse, + checkOperation, + ) .do(logObserver); }, }; diff --git a/packages/relay-runtime/package.json b/packages/relay-runtime/package.json index 4751454c40125..a27289c7a2237 100644 --- a/packages/relay-runtime/package.json +++ b/packages/relay-runtime/package.json @@ -1,7 +1,7 @@ { "name": "relay-runtime", "description": "A core runtime for building GraphQL-driven applications.", - "version": "18.2.0", + "version": "20.1.1", "keywords": [ "graphql", "relay" diff --git a/packages/relay-runtime/query/__tests__/fetchQueryInternal-test.js b/packages/relay-runtime/query/__tests__/fetchQueryInternal-test.js index 63e9e1f2bc5fa..3266c8aa1fc17 100644 --- a/packages/relay-runtime/query/__tests__/fetchQueryInternal-test.js +++ b/packages/relay-runtime/query/__tests__/fetchQueryInternal-test.js @@ -10,7 +10,10 @@ */ 'use strict'; -import type {GraphQLResponse} from '../../network/RelayNetworkTypes'; +import type { + GraphQLResponse, + GraphQLSingularResponse, +} from '../../network/RelayNetworkTypes'; import type {Subscription} from '../../network/RelayObservable'; import type {Observer} from '../../network/RelayObservable'; import type {NormalizationSplitOperation} from '../../util/NormalizationNode'; @@ -41,7 +44,7 @@ const { injectPromisePolyfill__DEPRECATED(); -let response; +let response: GraphQLSingularResponse; let gqlQuery: | Query< fetchQueryInternalTest1Query$variables, @@ -1119,7 +1122,7 @@ describe('getObservableForActiveRequest', () => { null, ); - expect(events).toEqual(['next', 'complete']); + expect(events).toEqual(['complete']); }); it('returns observable if module still loading after final payload', () => { diff --git a/packages/relay-runtime/query/fetchQuery.js b/packages/relay-runtime/query/fetchQuery.js index ae2df100f9e19..a9ab2ba4444ad 100644 --- a/packages/relay-runtime/query/fetchQuery.js +++ b/packages/relay-runtime/query/fetchQuery.js @@ -115,7 +115,7 @@ const invariant = require('invariant'); function fetchQuery( environment: IEnvironment, query: Query, - variables: TVariables, + variables: NoInfer, options?: $ReadOnly<{ fetchPolicy?: FetchQueryFetchPolicy, networkCacheConfig?: CacheConfig, @@ -138,7 +138,7 @@ function fetchQuery( const fetchPolicy = options?.fetchPolicy ?? 'network-only'; function readData(snapshot: Snapshot): TData { - handlePotentialSnapshotErrors(environment, snapshot.errorResponseFields); + handlePotentialSnapshotErrors(environment, snapshot.fieldErrors); /* $FlowFixMe[incompatible-return] we assume readData returns the right * data just having written it from network or checked availability. */ return snapshot.data; diff --git a/packages/relay-runtime/store/DataChecker.js b/packages/relay-runtime/store/DataChecker.js index 28a6a748d4c6b..b8abcc8144e86 100644 --- a/packages/relay-runtime/store/DataChecker.js +++ b/packages/relay-runtime/store/DataChecker.js @@ -73,6 +73,7 @@ function check( getDataID: GetDataID, shouldProcessClientComponents: ?boolean, log: ?LogFunction, + useExecTimeResolvers: ?boolean, ): Availability { if (log != null) { log({ @@ -90,6 +91,8 @@ function check( operationLoader, getDataID, shouldProcessClientComponents, + log, + useExecTimeResolvers, ); const result = checker.check(node, dataID); if (log != null) { @@ -112,6 +115,7 @@ class DataChecker { _recordSourceProxy: RelayRecordSourceProxy; _recordWasMissing: boolean; _source: RecordSource; + _useExecTimeResolvers: boolean; _variables: Variables; _shouldProcessClientComponents: ?boolean; +_getSourceForActor: (actorIdentifier: ActorIdentifier) => RecordSource; @@ -123,6 +127,7 @@ class DataChecker { ActorIdentifier, [RelayRecordSourceMutator, RelayRecordSourceProxy], >; + _log: ?LogFunction; constructor( getSourceForActor: (actorIdentifier: ActorIdentifier) => RecordSource, @@ -135,6 +140,8 @@ class DataChecker { operationLoader: ?OperationLoader, getDataID: GetDataID, shouldProcessClientComponents: ?boolean, + log: ?LogFunction, + useExecTimeResolvers: ?boolean, ) { this._getSourceForActor = getSourceForActor; this._getTargetForActor = getTargetForActor; @@ -144,6 +151,7 @@ class DataChecker { const [mutator, recordSourceProxy] = this._getMutatorAndRecordProxyForActor( defaultActorIdentifier, ); + this._useExecTimeResolvers = useExecTimeResolvers ?? false; this._mostRecentlyInvalidatedAt = null; this._handlers = handlers; this._mutator = mutator; @@ -152,6 +160,7 @@ class DataChecker { this._recordWasMissing = false; this._variables = variables; this._shouldProcessClientComponents = shouldProcessClientComponents; + this._log = log; } _getMutatorAndRecordProxyForActor( @@ -170,6 +179,7 @@ class DataChecker { this._getDataID, undefined, this._handlers, + this._log, ); tuple = [mutator, recordSourceProxy]; this._mutatorRecordSourceProxyCache.set(actorIdentifier, tuple); @@ -449,13 +459,15 @@ class DataChecker { this._traverseSelections(selection.fragment.selections, dataID); break; case 'RelayResolver': - this._checkResolver(selection, dataID); - break; case 'RelayLiveResolver': - this._checkResolver(selection, dataID); + if (!this._useExecTimeResolvers) { + this._checkResolver(selection, dataID); + } break; case 'ClientEdgeToClientObject': - this._checkResolver(selection.backingField, dataID); + if (!this._useExecTimeResolvers) { + this._checkResolver(selection.backingField, dataID); + } break; default: (selection: empty); diff --git a/packages/relay-runtime/store/OperationExecutor.js b/packages/relay-runtime/store/OperationExecutor.js index 5878a64603dec..3a1f16f13897a 100644 --- a/packages/relay-runtime/store/OperationExecutor.js +++ b/packages/relay-runtime/store/OperationExecutor.js @@ -138,6 +138,7 @@ class Executor { _operationTracker: OperationTracker; _operationUpdateEpochs: Map; _optimisticUpdates: null | Array>; + _useExecTimeResolvers: boolean; _pendingModulePayloadsCount: number; +_getPublishQueue: (actorIdentifier: ActorIdentifier) => PublishQueue; _shouldProcessClientComponents: ?boolean; @@ -158,6 +159,7 @@ class Executor { +_isSubscriptionOperation: boolean; +_seenActors: Set; _normalizeResponse: NormalizeResponseFunction; + _execTimeResolverResponseComplete: boolean; constructor({ actorIdentifier, @@ -193,6 +195,12 @@ class Executor { this._operationTracker = operationTracker; this._operationUpdateEpochs = new Map(); this._optimisticUpdates = null; + this._useExecTimeResolvers = + this._operation.request.node.operation.use_exec_time_resolvers ?? + this._operation.request.node.operation.exec_time_resolvers_enabled_provider?.get() === + true ?? + false; + this._execTimeResolverResponseComplete = false; this._pendingModulePayloadsCount = 0; this._getPublishQueue = getPublishQueue; this._scheduler = scheduler; @@ -245,6 +253,12 @@ class Executor { cacheConfig: this._operation.request.cacheConfig ?? {}, }); }, + unsubscribe: () => { + this._log({ + name: 'execute.unsubscribe', + executeId: this._executeId, + }); + }, }); if ( @@ -293,7 +307,7 @@ class Executor { } _updateActiveState(): void { - let activeState; + let activeState: ActiveState; switch (this._state) { case 'started': { activeState = 'active'; @@ -309,7 +323,11 @@ class Executor { } case 'loading_final': { activeState = - this._pendingModulePayloadsCount > 0 ? 'active' : 'inactive'; + this._pendingModulePayloadsCount > 0 || + (this._useExecTimeResolvers && + !this._execTimeResolverResponseComplete) + ? 'active' + : 'inactive'; break; } default: @@ -510,9 +528,10 @@ class Executor { return; } - const [nonIncrementalResponses, incrementalResponses] = + const [nonIncrementalResponses, incrementalResponses, normalizedResponses] = partitionGraphQLResponses(responsesWithData); const hasNonIncrementalResponses = nonIncrementalResponses.length > 0; + const hasNormalizedResponses = normalizedResponses.length > 0; // In theory this doesn't preserve the ordering of the batch. // The idea is that a batch is always: @@ -547,6 +566,37 @@ class Executor { this._processPayloadFollowups(payloadFollowups); } + if (hasNormalizedResponses) { + const payloadFollowups = []; + for (let i = 0; i < normalizedResponses.length; i++) { + const response = normalizedResponses[i]; + const source = new RelayRecordSource( + response.data as $FlowExpectedError, + ); + const isFinal = response.extensions?.is_final === true; + const payload: RelayResponsePayload = { + errors: [], + fieldPayloads: [], + followupPayloads: [], + incrementalPlaceholders: [], + isFinal, + source, + }; + this._getPublishQueueAndSaveActor().commitPayload( + this._operation, + payload, + this._updater, + ); + payloadFollowups.push(payload); + this._execTimeResolverResponseComplete = isFinal; + if (isFinal) { + // Need to update the active state to mark the query as inactive, + // incase server payloads have completed + this._updateActiveState(); + } + } + } + if (incrementalResponses.length > 0) { const payloadFollowups = this._processIncrementalResponses(incrementalResponses); @@ -572,7 +622,9 @@ class Executor { // the publish queue here, which will later be passed to the store (via // notify) to indicate that this operation caused the store to update const updatedOwners = this._runPublishQueue( - hasNonIncrementalResponses ? this._operation : undefined, + hasNonIncrementalResponses || hasNormalizedResponses + ? this._operation + : undefined, ); if (hasNonIncrementalResponses) { @@ -606,10 +658,12 @@ class Executor { { actorIdentifier: this._actorIdentifier, getDataID: this._getDataID, + log: this._log, path: [], shouldProcessClientComponents: this._shouldProcessClientComponents, treatMissingFieldsAsNull, }, + this._useExecTimeResolvers, ); validateOptimisticResponsePayload(payload); optimisticUpdates.push({ @@ -708,17 +762,26 @@ class Executor { followupPayload.dataID, variables, ); + const nextResponse: GraphQLResponseWithData = { + data: followupPayload.data, + // `is_final` flag needs to be set for processing nested defer and 3D + // when the server doesn't support streaming + extensions: + this._state === 'loading_final' ? {is_final: true} : undefined, + }; return this._normalizeResponse( - {data: followupPayload.data}, + nextResponse, selector, followupPayload.typeName, { actorIdentifier: this._actorIdentifier, getDataID: this._getDataID, + log: this._log, path: followupPayload.path, treatMissingFieldsAsNull: this._treatMissingFieldsAsNull, shouldProcessClientComponents: this._shouldProcessClientComponents, }, + this._useExecTimeResolvers, ); } @@ -797,10 +860,12 @@ class Executor { { actorIdentifier: this._actorIdentifier, getDataID: this._getDataID, + log: this._log, path: [], treatMissingFieldsAsNull: this._treatMissingFieldsAsNull, shouldProcessClientComponents: this._shouldProcessClientComponents, }, + this._useExecTimeResolvers, ); this._getPublishQueueAndSaveActor().commitPayload( this._operation, @@ -875,7 +940,8 @@ class Executor { placeholder.label, placeholder.path, placeholder, - {data: placeholder.data}, + // `is_final` flag needs to be set for processing nested defer payloads + {data: placeholder.data, extensions: {is_final: true}}, ), ); } @@ -889,7 +955,14 @@ class Executor { } _maybeCompleteSubscriptionOperationTracking() { - if (!this._isSubscriptionOperation) { + if ( + !this._isSubscriptionOperation && + !( + this._useExecTimeResolvers && + this._execTimeResolverResponseComplete && + this._state === 'loading_final' + ) + ) { return; } if ( @@ -1253,10 +1326,12 @@ class Executor { { actorIdentifier: this._actorIdentifier, getDataID: this._getDataID, + log: this._log, path: placeholder.path, treatMissingFieldsAsNull: this._treatMissingFieldsAsNull, shouldProcessClientComponents: this._shouldProcessClientComponents, }, + this._useExecTimeResolvers, ); this._getPublishQueueAndSaveActor().commitPayload( this._operation, @@ -1477,13 +1552,20 @@ class Executor { record: nextParentRecord, fieldPayloads, }); - const relayPayload = this._normalizeResponse(response, selector, typeName, { - actorIdentifier: this._actorIdentifier, - getDataID: this._getDataID, - path: [...normalizationPath, responseKey, String(itemIndex)], - treatMissingFieldsAsNull: this._treatMissingFieldsAsNull, - shouldProcessClientComponents: this._shouldProcessClientComponents, - }); + const relayPayload = this._normalizeResponse( + response, + selector, + typeName, + { + actorIdentifier: this._actorIdentifier, + getDataID: this._getDataID, + log: this._log, + path: [...normalizationPath, responseKey, String(itemIndex)], + treatMissingFieldsAsNull: this._treatMissingFieldsAsNull, + shouldProcessClientComponents: this._shouldProcessClientComponents, + }, + this._useExecTimeResolvers, + ); return { fieldPayloads, itemID, @@ -1586,9 +1668,11 @@ function partitionGraphQLResponses( ): [ $ReadOnlyArray, $ReadOnlyArray, + $ReadOnlyArray, ] { const nonIncrementalResponses: Array = []; const incrementalResponses: Array = []; + const normalizedResponses: Array = []; responses.forEach(response => { if (response.path != null || response.label != null) { const {label, path} = response; @@ -1606,11 +1690,13 @@ function partitionGraphQLResponses( path, response, }); + } else if (response.extensions?.is_normalized === true) { + normalizedResponses.push(response); } else { nonIncrementalResponses.push(response); } }); - return [nonIncrementalResponses, incrementalResponses]; + return [nonIncrementalResponses, incrementalResponses, normalizedResponses]; } function stableStringify(value: mixed): string { diff --git a/packages/relay-runtime/store/RelayExperimentalGraphResponseTransform.js b/packages/relay-runtime/store/RelayExperimentalGraphResponseTransform.js index 4a95d12c23b0e..a224401c0a33a 100644 --- a/packages/relay-runtime/store/RelayExperimentalGraphResponseTransform.js +++ b/packages/relay-runtime/store/RelayExperimentalGraphResponseTransform.js @@ -222,10 +222,10 @@ export class GraphModeNormalizer { $streamID, __id: dataID, __typename: ROOT_TYPE, - }; + } as RecordChunk; yield { $kind: 'Complete', - }; + } as CompleteChunk; } *_flushFields( @@ -247,9 +247,9 @@ export class GraphModeNormalizer { __typename: typename, __id: cacheKey, $streamID, - }; + } as RecordChunk; } else if (Object.keys(fields).length > 0) { - yield {...fields, $kind: 'Extend', $streamID}; + yield {...fields, $kind: 'Extend', $streamID} as ExtendChunk; } return $streamID; } diff --git a/packages/relay-runtime/store/RelayModernEnvironment.js b/packages/relay-runtime/store/RelayModernEnvironment.js index 2a9a708bd1711..484f29e998579 100644 --- a/packages/relay-runtime/store/RelayModernEnvironment.js +++ b/packages/relay-runtime/store/RelayModernEnvironment.js @@ -58,6 +58,7 @@ const defaultGetDataID = require('./defaultGetDataID'); const defaultRelayFieldLogger = require('./defaultRelayFieldLogger'); const normalizeResponse = require('./normalizeResponse'); const OperationExecutor = require('./OperationExecutor'); +const RelayModernStore = require('./RelayModernStore'); const RelayPublishQueue = require('./RelayPublishQueue'); const RelayRecordSource = require('./RelayRecordSource'); const invariant = require('invariant'); @@ -71,7 +72,7 @@ export type EnvironmentConfig = { +network: INetwork, +normalizeResponse?: ?NormalizeResponseFunction, +scheduler?: ?TaskScheduler, - +store: Store, + +store?: Store, +missingFieldHandlers?: ?$ReadOnlyArray, +operationTracker?: ?OperationTracker, +getDataID?: ?GetDataID, @@ -118,6 +119,15 @@ class RelayModernEnvironment implements IEnvironment { ); } } + const store = + config.store ?? + new RelayModernStore(new RelayRecordSource(), { + log: config.log, + operationLoader: config.operationLoader, + getDataID: config.getDataID, + shouldProcessClientComponents: config.shouldProcessClientComponents, + }); + this.__log = config.log ?? emptyFunction; this.relayFieldLogger = config.relayFieldLogger ?? defaultRelayFieldLogger; this._defaultRenderPolicy = @@ -128,13 +138,14 @@ class RelayModernEnvironment implements IEnvironment { this._getDataID = config.getDataID ?? defaultGetDataID; this._missingFieldHandlers = config.missingFieldHandlers ?? []; this._publishQueue = new RelayPublishQueue( - config.store, + store, config.handlerProvider ?? RelayDefaultHandlerProvider, this._getDataID, this._missingFieldHandlers, + this.__log, ); this._scheduler = config.scheduler ?? null; - this._store = config.store; + this._store = store; this.options = config.options; this._isServer = config.isServer ?? false; this._normalizeResponse = config.normalizeResponse ?? normalizeResponse; @@ -327,13 +338,18 @@ class RelayModernEnvironment implements IEnvironment { operation: OperationDescriptor, }): RelayObservable { return this._execute({ - createSource: () => - this.getNetwork().execute( + createSource: () => { + return this.getNetwork().execute( operation.request.node.params, operation.request.variables, operation.request.cacheConfig || {}, null, - ), + undefined, + undefined, + undefined, + () => this.check(operation), + ); + }, isClientPayload: false, operation, optimisticConfig: null, diff --git a/packages/relay-runtime/store/RelayModernFragmentSpecResolver.js b/packages/relay-runtime/store/RelayModernFragmentSpecResolver.js index b2acf3590d314..5354a2cfcb004 100644 --- a/packages/relay-runtime/store/RelayModernFragmentSpecResolver.js +++ b/packages/relay-runtime/store/RelayModernFragmentSpecResolver.js @@ -14,7 +14,7 @@ import type {ConcreteRequest} from '../util/RelayConcreteNode'; import type {Disposable, Variables} from '../util/RelayRuntimeTypes'; import type { - ErrorResponseFields, + FieldErrors, FragmentMap, FragmentSpecResolver, FragmentSpecResults, @@ -228,7 +228,7 @@ class SelectorResolver { _data: ?SelectorData; _environment: IEnvironment; _isMissingData: boolean; - _errorResponseFields: ?ErrorResponseFields; + _fieldErrors: ?FieldErrors; _rootIsQueryRenderer: boolean; _selector: SingularReaderSelector; _subscription: ?Disposable; @@ -244,7 +244,7 @@ class SelectorResolver { this._callback = callback; this._data = snapshot.data; this._isMissingData = snapshot.isMissingData; - this._errorResponseFields = snapshot.errorResponseFields; + this._fieldErrors = snapshot.fieldErrors; this._environment = environment; this._rootIsQueryRenderer = rootIsQueryRenderer; this._selector = selector; @@ -325,7 +325,7 @@ class SelectorResolver { } } } - handlePotentialSnapshotErrors(this._environment, this._errorResponseFields); + handlePotentialSnapshotErrors(this._environment, this._fieldErrors); return this._data; } @@ -340,7 +340,7 @@ class SelectorResolver { const snapshot = this._environment.lookup(selector); this._data = recycleNodesInto(this._data, snapshot.data); this._isMissingData = snapshot.isMissingData; - this._errorResponseFields = snapshot.errorResponseFields; + this._fieldErrors = snapshot.fieldErrors; this._selector = selector; this._subscription = this._environment.subscribe(snapshot, this._onChange); } @@ -376,7 +376,7 @@ class SelectorResolver { _onChange = (snapshot: Snapshot): void => { this._data = snapshot.data; this._isMissingData = snapshot.isMissingData; - this._errorResponseFields = snapshot.errorResponseFields; + this._fieldErrors = snapshot.fieldErrors; this._callback(); }; } diff --git a/packages/relay-runtime/store/RelayModernRecord.js b/packages/relay-runtime/store/RelayModernRecord.js index ebc5f406e729d..f387b494aaeb2 100644 --- a/packages/relay-runtime/store/RelayModernRecord.js +++ b/packages/relay-runtime/store/RelayModernRecord.js @@ -443,7 +443,7 @@ function merge(record1: Record, record2: Record): Record { if (ERRORS_KEY in record1 || ERRORS_KEY in record2) { const {[ERRORS_KEY]: errors1, ...fields1} = record1; const {[ERRORS_KEY]: errors2, ...fields2} = record2; - // $FlowIssue[cannot-spread-indexer] + // $FlowFixMe[cannot-spread-indexer] const updated: Record = {...fields1, ...fields2}; if (errors1 == null && errors2 == null) { return updated; @@ -465,7 +465,7 @@ function merge(record1: Record, record2: Record): Record { } return updated; } else { - // $FlowIssue[cannot-spread-indexer] + // $FlowFixMe[cannot-spread-indexer] return {...record1, ...record2}; } } diff --git a/packages/relay-runtime/store/RelayModernSelector.js b/packages/relay-runtime/store/RelayModernSelector.js index 99a2211ed0bad..73a25ee7e08d9 100644 --- a/packages/relay-runtime/store/RelayModernSelector.js +++ b/packages/relay-runtime/store/RelayModernSelector.js @@ -340,6 +340,7 @@ function getVariablesFromObject( const fragment = fragments[key]; const item = object[key]; const itemVariables = getVariablesFromFragment(fragment, item); + // $FlowFixMe[unsafe-object-assign] Object.assign(variables, itemVariables); } } @@ -397,6 +398,7 @@ function getVariablesFromPluralFragment( if (value != null) { const itemVariables = getVariablesFromSingularFragment(fragment, value); if (itemVariables != null) { + // $FlowFixMe[unsafe-object-assign] Object.assign(variables, itemVariables); } } diff --git a/packages/relay-runtime/store/RelayModernStore.js b/packages/relay-runtime/store/RelayModernStore.js index f7deeb40081d4..6a5c7212a73e2 100644 --- a/packages/relay-runtime/store/RelayModernStore.js +++ b/packages/relay-runtime/store/RelayModernStore.js @@ -107,6 +107,7 @@ class RelayModernStore implements Store { fetchTime: ?number, }, >; + _shouldRetainWithinTTL_EXPERIMENTAL: boolean; _shouldScheduleGC: boolean; _storeSubscriptions: StoreSubscriptions; _updatedRecordIDs: DataIDSet; @@ -127,6 +128,9 @@ class RelayModernStore implements Store { shouldProcessClientComponents?: ?boolean, resolverContext?: ResolverContext, + // Experimental + shouldRetainWithinTTL_EXPERIMENTAL?: boolean, + // These additional config options are only used if the experimental // @outputType resolver feature is used treatMissingFieldsAsNull?: ?boolean, @@ -147,6 +151,8 @@ class RelayModernStore implements Store { this._gcHoldCounter = 0; this._gcReleaseBufferSize = options?.gcReleaseBufferSize ?? DEFAULT_RELEASE_BUFFER_SIZE; + this._shouldRetainWithinTTL_EXPERIMENTAL = + options?.shouldRetainWithinTTL_EXPERIMENTAL ?? false; this._gcRun = null; this._gcScheduler = options?.gcScheduler ?? resolveImmediate; this._getDataID = options?.getDataID ?? defaultGetDataID; @@ -165,14 +171,15 @@ class RelayModernStore implements Store { () => this._getMutableRecordSource(), this, ); + this._resolverContext = options?.resolverContext; this._storeSubscriptions = new RelayStoreSubscriptions( options?.log, this._resolverCache, + this._resolverContext, ); this._updatedRecordIDs = new Set(); this._shouldProcessClientComponents = options?.shouldProcessClientComponents ?? false; - this._resolverContext = options?.resolverContext; this._treatMissingFieldsAsNull = options?.treatMissingFieldsAsNull ?? false; this._actorIdentifier = options?.actorIdentifier; @@ -235,6 +242,11 @@ class RelayModernStore implements Store { const selector = operation.root; const source = this._getMutableRecordSource(); const globalInvalidationEpoch = this._globalInvalidationEpoch; + const useExecTimeResolvers = + operation.request.node.operation.use_exec_time_resolvers ?? + operation.request.node.operation.exec_time_resolvers_enabled_provider?.get() === + true ?? + false; const rootEntry = this._roots.get(operation.request.identifier); const operationLastWrittenAt = rootEntry != null ? rootEntry.epoch : null; @@ -279,6 +291,7 @@ class RelayModernStore implements Store { this._getDataID, this._shouldProcessClientComponents, this.__log, + useExecTimeResolvers, ); return getAvailabilityStatus( @@ -315,7 +328,9 @@ class RelayModernStore implements Store { rootEntry.fetchTime <= Date.now() - _queryCacheExpirationTime; if (rootEntryIsStale) { - this._roots.delete(id); + if (!this._shouldRetainWithinTTL_EXPERIMENTAL) { + this._roots.delete(id); + } this.scheduleGC(); } else { this._releaseBuffer.push(id); @@ -325,8 +340,10 @@ class RelayModernStore implements Store { // buffer have a refCount of 0. if (this._releaseBuffer.length > this._gcReleaseBufferSize) { const _id = this._releaseBuffer.shift(); - // $FlowFixMe[incompatible-call] - this._roots.delete(_id); + if (!this._shouldRetainWithinTTL_EXPERIMENTAL) { + // $FlowFixMe[incompatible-call] + this._roots.delete(_id); + } this.scheduleGC(); } } @@ -453,6 +470,8 @@ class RelayModernStore implements Store { fetchTime: Date.now(), }; this._releaseBuffer.push(id); + /* $FlowFixMe[incompatible-call] Natural Inference rollout. See + * https://fburl.com/gdoc/y8dn025u */ this._roots.set(id, temporaryRootEntry); } } @@ -688,6 +707,14 @@ class RelayModernStore implements Store { }; *_collect(): Generator { + if ( + this._shouldRetainWithinTTL_EXPERIMENTAL && + this._queryCacheExpirationTime == null + ) { + // Null expiration time indicates infinite TTL, so we don't need to + // run GC. + return; + } /* eslint-disable no-labels */ const log = this.__log; top: while (true) { @@ -699,15 +726,43 @@ class RelayModernStore implements Store { const startEpoch = this._currentWriteEpoch; const references = new Set(); - // Mark all records that are traversable from a root - for (const {operation} of this._roots.values()) { + for (const [ + dataID, + {operation, refCount, fetchTime}, + ] of this._roots.entries()) { + if (this._shouldRetainWithinTTL_EXPERIMENTAL) { + // Do not mark records that should be garbage collected + const {_queryCacheExpirationTime} = this; + invariant( + _queryCacheExpirationTime != null, + 'Query cache expiration time should be non-null if executing GC', + ); + const recordHasExpired = + fetchTime == null || + fetchTime <= Date.now() - _queryCacheExpirationTime; + const recordShouldBeCollected = + recordHasExpired && + refCount === 0 && + !this._releaseBuffer.includes(dataID); + if (recordShouldBeCollected) { + continue; + } + } + + // Mark all records that are traversable from a root that is still valid const selector = operation.root; + const useExecTimeResolvers = + operation.request.node.operation.use_exec_time_resolvers ?? + operation.request.node.operation.exec_time_resolvers_enabled_provider?.get() === + true ?? + false; RelayReferenceMarker.mark( this._recordSource, selector, references, this._operationLoader, this._shouldProcessClientComponents, + useExecTimeResolvers, ); // Yield for other work after each operation yield; @@ -723,31 +778,36 @@ class RelayModernStore implements Store { } } - // Sweep records without references - if (references.size === 0) { - // Short-circuit if *nothing* is referenced - this._recordSource.clear(); - } else { - // Evict any unreferenced nodes - const storeIDs = this._recordSource.getRecordIDs(); - for (let ii = 0; ii < storeIDs.length; ii++) { - const dataID = storeIDs[ii]; - if (!references.has(dataID)) { - const record = this._recordSource.get(dataID); - if (record != null) { - const maybeResolverSubscription = RelayModernRecord.getValue( - record, - RELAY_RESOLVER_LIVE_STATE_SUBSCRIPTION_KEY, - ); - if (maybeResolverSubscription != null) { - // $FlowFixMe - this value if it is not null, it is a function - maybeResolverSubscription(); - } + // NOTE: It may be tempting to use `this._recordSource.clear()` + // when no references are found, but that would prevent calling + // maybeResolverSubscription() on any records that have an active + // resolver subscription. This would result in a memory leak. + + // Evict any unreferenced nodes + const storeIDs = this._recordSource.getRecordIDs(); + for (let ii = 0; ii < storeIDs.length; ii++) { + const dataID = storeIDs[ii]; + if (!references.has(dataID)) { + const record = this._recordSource.get(dataID); + if (record != null) { + const maybeResolverSubscription = RelayModernRecord.getValue( + record, + RELAY_RESOLVER_LIVE_STATE_SUBSCRIPTION_KEY, + ); + if (maybeResolverSubscription != null) { + // $FlowFixMe - this value if it is not null, it is a function + maybeResolverSubscription(); } - this._recordSource.remove(dataID); + } + this._recordSource.remove(dataID); + if (this._shouldRetainWithinTTL_EXPERIMENTAL) { + // Note: A record that was never retained will not be in the roots map + // but the following line should not throw + this._roots.delete(dataID); } } } + if (log != null) { log({ name: 'store.gc.end', @@ -765,6 +825,7 @@ class RelayModernStore implements Store { return { path, getDataID: this._getDataID, + log: this.__log, treatMissingFieldsAsNull: this._treatMissingFieldsAsNull, shouldProcessClientComponents: this._shouldProcessClientComponents, actorIdentifier: this._actorIdentifier, diff --git a/packages/relay-runtime/store/RelayPublishQueue.js b/packages/relay-runtime/store/RelayPublishQueue.js index b842ea09d1ee8..862e84aac4eec 100644 --- a/packages/relay-runtime/store/RelayPublishQueue.js +++ b/packages/relay-runtime/store/RelayPublishQueue.js @@ -15,6 +15,7 @@ import type {HandlerProvider} from '../handlers/RelayDefaultHandlerProvider'; import type {Disposable} from '../util/RelayRuntimeTypes'; import type {GetDataID} from './RelayResponseNormalizer'; import type { + LogFunction, MissingFieldHandler, MutationParameters, OperationDescriptor, @@ -33,6 +34,7 @@ import type { const RelayRecordSourceMutator = require('../mutations/RelayRecordSourceMutator'); const RelayRecordSourceProxy = require('../mutations/RelayRecordSourceProxy'); const RelayRecordSourceSelectorProxy = require('../mutations/RelayRecordSourceSelectorProxy'); +const RelayFeatureFlags = require('../util/RelayFeatureFlags'); const RelayReader = require('./RelayReader'); const RelayRecordSource = require('./RelayRecordSource'); const invariant = require('invariant'); @@ -60,8 +62,10 @@ type PendingUpdater = { const _global: typeof global | $FlowFixMe = typeof global !== 'undefined' ? global - : typeof window !== 'undefined' - ? window + : // $FlowFixMe[cannot-resolve-name] + typeof window !== 'undefined' + ? // $FlowFixMe[cannot-resolve-name] + window : undefined; const applyWithGuard = @@ -84,6 +88,7 @@ class RelayPublishQueue implements PublishQueue { _handlerProvider: ?HandlerProvider; _missingFieldHandlers: $ReadOnlyArray; _getDataID: GetDataID; + _log: ?LogFunction; _hasStoreSnapshot: boolean; // True if the next `run()` should apply the backup and rerun all optimistic @@ -112,6 +117,7 @@ class RelayPublishQueue implements PublishQueue { handlerProvider?: ?HandlerProvider, getDataID: GetDataID, missingFieldHandlers: $ReadOnlyArray, + log: LogFunction, ) { this._hasStoreSnapshot = false; this._handlerProvider = handlerProvider || null; @@ -123,6 +129,7 @@ class RelayPublishQueue implements PublishQueue { this._gcHold = null; this._getDataID = getDataID; this._missingFieldHandlers = missingFieldHandlers; + this._log = log; } /** @@ -219,24 +226,27 @@ class RelayPublishQueue implements PublishQueue { this._pendingOptimisticUpdates.size === 0 && !runWillClearGcHold; - if (__DEV__) { - warning( - !runIsANoop, - 'RelayPublishQueue.run was called, but the call would have been a noop.', - ); - warning( - this._isRunning !== true, - 'A store update was detected within another store update. Please ' + - "make sure new store updates aren't being executed within an " + - 'updater function for a different update.', - ); - this._isRunning = true; - } + warning( + !runIsANoop, + 'RelayPublishQueue.run was called, but the call would have been a noop.', + ); + RelayFeatureFlags.DISALLOW_NESTED_UPDATES + ? invariant( + this._isRunning !== true, + 'A store update was detected within another store update. Please ' + + "make sure new store updates aren't being executed within an " + + 'updater function for a different update.', + ) + : warning( + this._isRunning !== true, + 'A store update was detected within another store update. Please ' + + "make sure new store updates aren't being executed within an " + + 'updater function for a different update.', + ); + this._isRunning = true; if (runIsANoop) { - if (__DEV__) { - this._isRunning = false; - } + this._isRunning = false; return []; } @@ -268,9 +278,7 @@ class RelayPublishQueue implements PublishQueue { this._gcHold = null; } } - if (__DEV__) { - this._isRunning = false; - } + this._isRunning = false; return this._store.notify(sourceOperation, invalidatedStore); } @@ -292,6 +300,7 @@ class RelayPublishQueue implements PublishQueue { this._getDataID, this._handlerProvider, this._missingFieldHandlers, + this._log, ); if (fieldPayloads && fieldPayloads.length) { fieldPayloads.forEach(fieldPayload => { @@ -355,6 +364,7 @@ class RelayPublishQueue implements PublishQueue { this._getDataID, this._handlerProvider, this._missingFieldHandlers, + this._log, ); applyWithGuard( updater, @@ -388,6 +398,7 @@ class RelayPublishQueue implements PublishQueue { this._getDataID, this._handlerProvider, this._missingFieldHandlers, + this._log, ); // $FlowFixMe[unclear-type] see explanation above. diff --git a/packages/relay-runtime/store/RelayReader.js b/packages/relay-runtime/store/RelayReader.js index 9d22714aeca9e..27004ecabcc5d 100644 --- a/packages/relay-runtime/store/RelayReader.js +++ b/packages/relay-runtime/store/RelayReader.js @@ -35,8 +35,8 @@ import type {DataID, Variables} from '../util/RelayRuntimeTypes'; import type { ClientEdgeTraversalInfo, DataIDSet, - ErrorResponseField, - ErrorResponseFields, + FieldError, + FieldErrors, MissingClientEdgeRequestInfo, Record, RecordSource, @@ -49,6 +49,7 @@ import type { import type {Arguments} from './RelayStoreUtils'; import type {EvaluationResult, ResolverCache} from './ResolverCache'; +const RelayFeatureFlags = require('../util/RelayFeatureFlags'); const { isSuspenseSentinel, } = require('./live-resolvers/LiveResolverSuspenseSentinel'); @@ -98,7 +99,7 @@ class RelayReader { _missingClientEdges: Array; _missingLiveResolverFields: Array; _isWithinUnmatchedTypeRefinement: boolean; - _errorResponseFields: ?ErrorResponseFields; + _fieldErrors: ?FieldErrors; _owner: RequestDescriptor; // Exec time resolvers are run before reaching the Relay store so the store already contains // the normalized data; the same as if the data were sent from the server. However, since a @@ -128,10 +129,13 @@ class RelayReader { this._missingLiveResolverFields = []; this._isMissingData = false; this._isWithinUnmatchedTypeRefinement = false; - this._errorResponseFields = null; + this._fieldErrors = null; this._owner = selector.owner; this._useExecTimeResolvers = - this._owner.node.operation.use_exec_time_resolvers ?? false; + this._owner.node.operation.use_exec_time_resolvers ?? + this._owner.node.operation.exec_time_resolvers_enabled_provider?.get() === + true ?? + false; this._recordSource = recordSource; this._seenRecords = new Set(); this._selector = selector; @@ -205,11 +209,11 @@ class RelayReader { missingLiveResolverFields: this._missingLiveResolverFields, seenRecords: this._seenRecords, selector: this._selector, - errorResponseFields: this._errorResponseFields, + fieldErrors: this._fieldErrors, }; } - _maybeAddErrorResponseFields(record: Record, storageKey: string): void { + _maybeAddFieldErrors(record: Record, storageKey: string): void { const errors = RelayModernRecord.getErrors(record, storageKey); if (errors == null) { @@ -217,17 +221,21 @@ class RelayReader { } const owner = this._fragmentName; - if (this._errorResponseFields == null) { - this._errorResponseFields = []; + if (this._fieldErrors == null) { + this._fieldErrors = []; } - for (const error of errors) { - this._errorResponseFields.push({ + for (let i = 0; i < errors.length; i++) { + const error = errors[i]; + this._fieldErrors.push({ kind: 'relay_field_payload.error', owner, fieldPath: (error.path ?? []).join('.'), error, shouldThrow: this._selector.node.metadata?.throwOnFieldError ?? false, handled: false, + // the uiContext is always undefined here. + // the loggingContext is provided by hooks - and assigned to uiContext in handlePotentialSnapshotErrors + uiContext: undefined, }); } } @@ -236,22 +244,32 @@ class RelayReader { if (this._isWithinUnmatchedTypeRefinement) { return; } - if (this._errorResponseFields == null) { - this._errorResponseFields = []; + if (this._fieldErrors == null) { + this._fieldErrors = []; } // we will add the path later const owner = this._fragmentName; - this._errorResponseFields.push( - this._selector.node.metadata?.throwOnFieldError ?? false + this._fieldErrors.push( + (this._selector.node.metadata?.throwOnFieldError ?? false) ? { kind: 'missing_expected_data.throw', owner, fieldPath: fieldName, handled: false, + // the uiContext is always undefined here. + // the loggingContext is provided by hooks - and assigned to uiContext in handlePotentialSnapshotErrors + uiContext: undefined, } - : {kind: 'missing_expected_data.log', owner, fieldPath: fieldName}, + : { + kind: 'missing_expected_data.log', + owner, + fieldPath: fieldName, + // the uiContext is always undefined here. + // the loggingContext is provided by hooks - and assigned to uiContext in handlePotentialSnapshotErrors + uiContext: undefined, + }, ); this._isMissingData = true; @@ -282,6 +300,7 @@ class RelayReader { if (record === undefined) { this._markDataAsMissing(''); } + // $FlowFixMe[incompatible-return] return record; } const data = prevData || {}; @@ -308,8 +327,8 @@ class RelayReader { } const owner = this._fragmentName; - if (this._errorResponseFields == null) { - this._errorResponseFields = []; + if (this._fieldErrors == null) { + this._fieldErrors = []; } let fieldName: string; @@ -322,18 +341,24 @@ class RelayReader { switch (selection.action) { case 'THROW': - this._errorResponseFields.push({ + this._fieldErrors.push({ kind: 'missing_required_field.throw', fieldPath: fieldName, owner, handled: false, + // the uiContext is always undefined here. + // the loggingContext is provided by hooks - and assigned to uiContext in handlePotentialSnapshotErrors + uiContext: undefined, }); return; case 'LOG': - this._errorResponseFields.push({ + this._fieldErrors.push({ kind: 'missing_required_field.log', fieldPath: fieldName, owner, + // the uiContext is always undefined here. + // the loggingContext is provided by hooks - and assigned to uiContext in handlePotentialSnapshotErrors + uiContext: undefined, }); return; default: @@ -361,7 +386,7 @@ class RelayReader { * any fields within them. * * 1. Before traversing into the selection(s) marked as `@catch`, the caller - * stores the previous field errors (`this._errorResponseFields`) in a + * stores the previous field errors (`this._fieldErrors`) in a * variable. * 2. After traversing into the selection(s) marked as `@catch`, the caller * calls this method with the resulting value, the `to` value from the @@ -376,7 +401,7 @@ class RelayReader { _catchErrors( _value: T, to: CatchFieldTo, - previousResponseFields: ?ErrorResponseFields, + previousResponseFields: ?FieldErrors, ): ?T | Result { let value: T | null | Result = _value; switch (to) { @@ -384,10 +409,7 @@ class RelayReader { value = this._asResult(_value); break; case 'NULL': - if ( - this._errorResponseFields != null && - this._errorResponseFields.length > 0 - ) { + if (this._fieldErrors != null && this._fieldErrors.length > 0) { value = null; } break; @@ -395,22 +417,22 @@ class RelayReader { (to: empty); } - const childrenErrorResponseFields = this._errorResponseFields; + const childrenFieldErrors = this._fieldErrors; - this._errorResponseFields = previousResponseFields; + this._fieldErrors = previousResponseFields; // Merge any errors encountered within the @catch with the previous field // errors, but mark them as "handled" first. - if (childrenErrorResponseFields != null) { - if (this._errorResponseFields == null) { - this._errorResponseFields = []; + if (childrenFieldErrors != null) { + if (this._fieldErrors == null) { + this._fieldErrors = []; } - for (let i = 0; i < childrenErrorResponseFields.length; i++) { + for (let i = 0; i < childrenFieldErrors.length; i++) { // We mark any errors encountered within the @catch as "handled" // to ensure that they don't cause the reader to throw, but can // still be logged. - this._errorResponseFields.push( - markFieldErrorHasHandled(childrenErrorResponseFields[i]), + this._fieldErrors.push( + markFieldErrorHasHandled(childrenFieldErrors[i]), ); } } @@ -419,21 +441,18 @@ class RelayReader { /** * Convert a value into a Result object based on the presence of errors in the - * `this._errorResponseFields` array. + * `this._fieldErrors` array. * * **Note**: This method does _not_ mark errors as handled. It is the caller's * responsibility to ensure that errors are marked as handled. */ _asResult(value: T): Result { - if ( - this._errorResponseFields == null || - this._errorResponseFields.length === 0 - ) { + if (this._fieldErrors == null || this._fieldErrors.length === 0) { return {ok: true, value}; } // TODO: Should we be hiding log level events here? - const errors = this._errorResponseFields + const errors = this._fieldErrors .map(error => { switch (error.kind) { case 'relay_field_payload.error': @@ -461,7 +480,7 @@ class RelayReader { (error.kind: empty); invariant( false, - 'Unexpected error errorResponseField kind: %s', + 'Unexpected error fieldError kind: %s', error.kind, ); } @@ -491,9 +510,9 @@ class RelayReader { } break; case 'CatchField': { - const previousResponseFields = this._errorResponseFields; + const previousResponseFields = this._fieldErrors; - this._errorResponseFields = null; + this._fieldErrors = null; const catchFieldValue = this._readClientSideDirectiveField( selection, @@ -662,13 +681,31 @@ class RelayReader { } else { return this._readLink(selection.field, record, data); } + case 'RelayResolver': - return this._readResolverField(selection.field, record, data); - case 'RelayLiveResolver': - return this._readResolverField(selection.field, record, data); + case 'RelayLiveResolver': { + if (this._useExecTimeResolvers) { + return this._readScalar(selection.field, record, data); + } else { + return this._readResolverField(selection.field, record, data); + } + } case 'ClientEdgeToClientObject': case 'ClientEdgeToServerObject': - return this._readClientEdge(selection.field, record, data); + if ( + this._useExecTimeResolvers && + (selection.field.backingField.kind === 'RelayResolver' || + selection.field.backingField.kind === 'RelayLiveResolver') + ) { + const {field} = selection; + if (field.linkedField.plural) { + return this._readPluralLink(field.linkedField, record, data); + } else { + return this._readLink(field.linkedField, record, data); + } + } else { + return this._readClientEdge(selection.field, record, data); + } case 'AliasedInlineFragmentSpread': return this._readAliasedInlineFragment(selection.field, record, data); default: @@ -687,8 +724,8 @@ class RelayReader { data: SelectorData, ): mixed { const parentRecordID = RelayModernRecord.getDataID(record); - const prevErrors = this._errorResponseFields; - this._errorResponseFields = null; + const prevErrors = this._fieldErrors; + this._fieldErrors = null; const result = this._readResolverFieldImpl(field, parentRecordID); const fieldName = field.alias ?? field.name; @@ -726,7 +763,7 @@ class RelayReader { return { data: snapshot.data, isMissingData: snapshot.isMissingData, - errorResponseFields: snapshot.errorResponseFields, + fieldErrors: snapshot.fieldErrors, }; } @@ -739,7 +776,7 @@ class RelayReader { return { data: snapshot.data, isMissingData: snapshot.isMissingData, - errorResponseFields: snapshot.errorResponseFields, + fieldErrors: snapshot.fieldErrors, }; }; @@ -751,7 +788,7 @@ class RelayReader { // `getResolverValue`) and converted into an error object. const evaluate = (): EvaluationResult => { if (fragment != null) { - const key = { + const key: SelectorData = { __id: parentRecordID, __fragmentOwner: this._owner, __fragments: { @@ -760,6 +797,14 @@ class RelayReader { : {}, }, }; + if ( + this._clientEdgeTraversalPath.length > 0 && + this._clientEdgeTraversalPath[ + this._clientEdgeTraversalPath.length - 1 + ] !== null + ) { + key[CLIENT_EDGE_TRAVERSAL_PATH] = [...this._clientEdgeTraversalPath]; + } const resolverContext = {getDataForResolverFragment}; return withResolverContext(resolverContext, () => { const [resolverResult, resolverError] = getResolverValue( @@ -824,7 +869,8 @@ class RelayReader { // upwards to mimic the behavior of having traversed into that fragment directly. if (cachedSnapshot != null) { if (cachedSnapshot.missingClientEdges != null) { - for (const missing of cachedSnapshot.missingClientEdges) { + for (let i = 0; i < cachedSnapshot.missingClientEdges.length; i++) { + const missing = cachedSnapshot.missingClientEdges[i]; this._missingClientEdges.push(missing); } } @@ -833,27 +879,34 @@ class RelayReader { this._isMissingData || cachedSnapshot.missingLiveResolverFields.length > 0; - for (const missingResolverField of cachedSnapshot.missingLiveResolverFields) { + for ( + let i = 0; + i < cachedSnapshot.missingLiveResolverFields.length; + i++ + ) { + const missingResolverField = + cachedSnapshot.missingLiveResolverFields[i]; this._missingLiveResolverFields.push(missingResolverField); } } - if (cachedSnapshot.errorResponseFields != null) { - if (this._errorResponseFields == null) { - this._errorResponseFields = []; + if (cachedSnapshot.fieldErrors != null) { + if (this._fieldErrors == null) { + this._fieldErrors = []; } - for (const error of cachedSnapshot.errorResponseFields) { + for (let i = 0; i < cachedSnapshot.fieldErrors.length; i++) { + const error = cachedSnapshot.fieldErrors[i]; if (this._selector.node.metadata?.throwOnFieldError === true) { // If this fragment is @throwOnFieldError, any destructive error // encountered inside a resolver's fragment is equivilent to the // resolver field having a field error, and we want that to cause this // fragment to throw. So, we propagate all errors as is. - this._errorResponseFields.push(error); + this._fieldErrors.push(error); } else { // If this fragment is _not_ @throwOnFieldError, we will simply // accept that any destructive errors encountered in the resolver's // root fragment will cause the resolver to return null, and well // pass the errors along to the logger marked as "handled". - this._errorResponseFields.push(markFieldErrorHasHandled(error)); + this._fieldErrors.push(markFieldErrorHasHandled(error)); } } } @@ -864,18 +917,21 @@ class RelayReader { // the errors can be attached to this read's snapshot. This allows the error // to be logged. if (resolverError) { - const errorEvent = { + const errorEvent: FieldError = { kind: 'relay_resolver.error', fieldPath, owner: this._fragmentName, error: resolverError, shouldThrow: this._selector.node.metadata?.throwOnFieldError ?? false, handled: false, + // the uiContext is always undefined here. + // the loggingContext is provided by hooks - and assigned to uiContext in handlePotentialSnapshotErrors + uiContext: undefined, }; - if (this._errorResponseFields == null) { - this._errorResponseFields = [errorEvent]; + if (this._fieldErrors == null) { + this._fieldErrors = [errorEvent]; } else { - this._errorResponseFields.push(errorEvent); + this._fieldErrors.push(errorEvent); } } @@ -896,9 +952,11 @@ class RelayReader { this._missingLiveResolverFields.push(suspenseID); } if (updatedDataIDs != null) { - for (const recordID of updatedDataIDs) { + // Iterating a Set with for of is okay + // eslint-disable-next-line relay-internal/no-for-of-loops + updatedDataIDs.forEach(recordID => { this._updatedDataIDs.add(recordID); - } + }); } } @@ -1068,8 +1126,8 @@ class RelayReader { RelayModernRecord.getDataID(record), prevData, ); - const prevErrors = this._errorResponseFields; - this._errorResponseFields = null; + const prevErrors = this._fieldErrors; + this._fieldErrors = null; const edgeValue = this._traverse( field.linkedField, storeID, @@ -1091,8 +1149,13 @@ class RelayReader { const fieldName = field.alias ?? field.name; const storageKey = getStorageKey(field, this._variables); const value = RelayModernRecord.getValue(record, storageKey); - if (value === null) { - this._maybeAddErrorResponseFields(record, storageKey); + if ( + value === null || + (RelayFeatureFlags.ENABLE_NONCOMPLIANT_ERROR_HANDLING_ON_LISTS && + Array.isArray(value) && + value.length === 0) + ) { + this._maybeAddFieldErrors(record, storageKey); } else if (value === undefined) { this._markDataAsMissing(fieldName); } @@ -1111,7 +1174,7 @@ class RelayReader { if (linkedID == null) { data[fieldName] = linkedID; if (linkedID === null) { - this._maybeAddErrorResponseFields(record, storageKey); + this._maybeAddFieldErrors(record, storageKey); } else if (linkedID === undefined) { this._markDataAsMissing(fieldName); } @@ -1128,8 +1191,8 @@ class RelayReader { RelayModernRecord.getDataID(record), prevData, ); - const prevErrors = this._errorResponseFields; - this._errorResponseFields = null; + const prevErrors = this._fieldErrors; + this._fieldErrors = null; // $FlowFixMe[incompatible-variance] const value = this._traverse(field, linkedID, prevData); @@ -1139,7 +1202,7 @@ class RelayReader { } /** - * Adds a set of field errors to `this._errorResponseFields`, ensuring the + * Adds a set of field errors to `this._fieldErrors`, ensuring the * `fieldPath` property of existing field errors are prefixed with the given * `fieldNameOrIndex`. * @@ -1158,8 +1221,8 @@ class RelayReader { * To achieve this, named field readers must do the following to correctly * track error filePaths: * - * 1. Stash the value of `this._errorResponseFields` in a local variable - * 2. Set `this._errorResponseFields` to `null` + * 1. Stash the value of `this._fieldErrors` in a local variable + * 2. Set `this._fieldErrors` to `null` * 3. Traverse into the field * 4. Call this method with the stashed errors and the field's name * @@ -1171,12 +1234,12 @@ class RelayReader { * field error paths. */ _prependPreviousErrors( - prevErrors: ?Array, + prevErrors: ?Array, fieldNameOrIndex: string | number, ): void { - if (this._errorResponseFields != null) { - for (let i = 0; i < this._errorResponseFields.length; i++) { - const event = this._errorResponseFields[i]; + if (this._fieldErrors != null) { + for (let i = 0; i < this._fieldErrors.length; i++) { + const event = this._fieldErrors[i]; if ( event.owner === this._fragmentName && (event.kind === 'missing_expected_data.throw' || @@ -1188,13 +1251,13 @@ class RelayReader { } } if (prevErrors != null) { - for (let i = this._errorResponseFields.length - 1; i >= 0; i--) { - prevErrors.push(this._errorResponseFields[i]); + for (let i = this._fieldErrors.length - 1; i >= 0; i--) { + prevErrors.push(this._fieldErrors[i]); } - this._errorResponseFields = prevErrors; + this._fieldErrors = prevErrors; } } else { - this._errorResponseFields = prevErrors; + this._fieldErrors = prevErrors; } } @@ -1215,7 +1278,7 @@ class RelayReader { if (externalRef === undefined) { this._markDataAsMissing(fieldName); } else if (externalRef === null) { - this._maybeAddErrorResponseFields(record, storageKey); + this._maybeAddFieldErrors(record, storageKey); } return data[fieldName]; } @@ -1243,8 +1306,13 @@ class RelayReader { ): ?mixed { const storageKey = getStorageKey(field, this._variables); const linkedIDs = RelayModernRecord.getLinkedRecordIDs(record, storageKey); - if (linkedIDs === null) { - this._maybeAddErrorResponseFields(record, storageKey); + if ( + linkedIDs === null || + (RelayFeatureFlags.ENABLE_NONCOMPLIANT_ERROR_HANDLING_ON_LISTS && + Array.isArray(linkedIDs) && + linkedIDs.length === 0) + ) { + this._maybeAddFieldErrors(record, storageKey); } return this._readLinkedIds(field, linkedIDs, record, data); } @@ -1274,8 +1342,8 @@ class RelayReader { RelayModernRecord.getDataID(record), prevData, ); - const prevErrors = this._errorResponseFields; - this._errorResponseFields = null; + const prevErrors = this._fieldErrors; + this._fieldErrors = null; const linkedArray = prevData || []; linkedIDs.forEach((linkedID, nextIndex) => { if (linkedID == null) { @@ -1295,8 +1363,8 @@ class RelayReader { RelayModernRecord.getDataID(record), prevItem, ); - const prevErrors = this._errorResponseFields; - this._errorResponseFields = null; + const prevErrors = this._fieldErrors; + this._fieldErrors = null; // $FlowFixMe[cannot-write] // $FlowFixMe[incompatible-variance] linkedArray[nextIndex] = this._traverse(field, linkedID, prevItem); @@ -1319,10 +1387,15 @@ class RelayReader { // Determine the component module from the store: if the field is missing // it means we don't know what component to render the match with. const componentKey = getModuleComponentKey(moduleImport.documentName); + const relayStoreComponent = RelayModernRecord.getValue( + record, + componentKey, + ); // componentModuleProvider is used by Client 3D for read time resolvers. const component = - moduleImport.componentModuleProvider ?? - RelayModernRecord.getValue(record, componentKey); + relayStoreComponent !== undefined + ? relayStoreComponent + : moduleImport.componentModuleProvider; if (component == null) { if (component === undefined) { this._markDataAsMissing(''); @@ -1366,8 +1439,8 @@ class RelayReader { record: Record, data: SelectorData, ) { - const prevErrors = this._errorResponseFields; - this._errorResponseFields = null; + const prevErrors = this._fieldErrors; + this._fieldErrors = null; let fieldValue = this._readInlineFragment( aliasedInlineFragment.fragment, record, @@ -1597,9 +1670,7 @@ class RelayReader { } } -function markFieldErrorHasHandled( - event: ErrorResponseField, -): ErrorResponseField { +function markFieldErrorHasHandled(event: FieldError): FieldError { switch (event.kind) { case 'missing_expected_data.throw': case 'missing_required_field.throw': diff --git a/packages/relay-runtime/store/RelayRecordSource.js b/packages/relay-runtime/store/RelayRecordSource.js index 00e25cbe31f6e..c17146328a110 100644 --- a/packages/relay-runtime/store/RelayRecordSource.js +++ b/packages/relay-runtime/store/RelayRecordSource.js @@ -86,7 +86,7 @@ class RelayRecordSource implements MutableRecordSource { } toJSON(): RecordSourceJSON { - const obj: RecordSourceJSON = {}; + const obj: {...RecordSourceJSON} = {}; for (const [key, record] of this._records) { obj[key] = RelayModernRecord.toJSON(record); } diff --git a/packages/relay-runtime/store/RelayReferenceMarker.js b/packages/relay-runtime/store/RelayReferenceMarker.js index 8fec7f88f358d..e1d1cadb7f7dc 100644 --- a/packages/relay-runtime/store/RelayReferenceMarker.js +++ b/packages/relay-runtime/store/RelayReferenceMarker.js @@ -38,7 +38,8 @@ const RelayStoreUtils = require('./RelayStoreUtils'); const {generateTypeID} = require('./TypeID'); const invariant = require('invariant'); -const {getStorageKey, getModuleOperationKey} = RelayStoreUtils; +const {getReadTimeResolverStorageKey, getStorageKey, getModuleOperationKey} = + RelayStoreUtils; function mark( recordSource: RecordSource, @@ -46,6 +47,7 @@ function mark( references: DataIDSet, operationLoader: ?OperationLoader, shouldProcessClientComponents: ?boolean, + useExecTimeResolvers: ?boolean, ): void { const {dataID, node, variables} = selector; const marker = new RelayReferenceMarker( @@ -54,6 +56,7 @@ function mark( references, operationLoader, shouldProcessClientComponents, + useExecTimeResolvers, ); marker.mark(node, dataID); } @@ -67,6 +70,7 @@ class RelayReferenceMarker { _recordSource: RecordSource; _references: DataIDSet; _variables: Variables; + _useExecTimeResolvers: boolean; _shouldProcessClientComponents: ?boolean; constructor( @@ -75,9 +79,11 @@ class RelayReferenceMarker { references: DataIDSet, operationLoader: ?OperationLoader, shouldProcessClientComponents: ?boolean, + useExecTimeResolvers: ?boolean, ) { this._operationLoader = operationLoader ?? null; this._operationName = null; + this._useExecTimeResolvers = useExecTimeResolvers ?? false; this._recordSource = recordSource; this._references = references; this._variables = variables; @@ -216,8 +222,6 @@ class RelayReferenceMarker { this._traverseSelections(selection.fragment.selections, record); break; case 'RelayResolver': - this._traverseResolverField(selection, record); - break; case 'RelayLiveResolver': this._traverseResolverField(selection, record); break; @@ -239,6 +243,10 @@ class RelayReferenceMarker { field: NormalizationClientEdgeToClientObject, record: Record, ): void { + if (this._useExecTimeResolvers) { + this._traverseLink(field.linkedField, record); + return; + } const dataID = this._traverseResolverField(field.backingField, record); if (dataID == null) { return; @@ -291,7 +299,10 @@ class RelayReferenceMarker { field: NormalizationResolverField | NormalizationLiveResolverField, record: Record, ): ?DataID { - const storageKey = getStorageKey(field, this._variables); + if (this._useExecTimeResolvers) { + return; + } + const storageKey = getReadTimeResolverStorageKey(field, this._variables); const dataID = RelayModernRecord.getLinkedRecordID(record, storageKey); // If the resolver value has been created, we should retain it. @@ -306,7 +317,6 @@ class RelayReferenceMarker { // Mark the contents of the resolver's data dependencies. this._traverseSelections([fragment], record); } - return dataID; } diff --git a/packages/relay-runtime/store/RelayResponseNormalizer.js b/packages/relay-runtime/store/RelayResponseNormalizer.js index 62da388182a6e..8566741f969f0 100644 --- a/packages/relay-runtime/store/RelayResponseNormalizer.js +++ b/packages/relay-runtime/store/RelayResponseNormalizer.js @@ -29,7 +29,9 @@ import type {RelayErrorTrie} from './RelayErrorTrie'; import type { FollowupPayload, HandleFieldPayload, + IdCollisionTypenameLogEvent, IncrementalDataPlaceholder, + LogFunction, MutableRecordSource, NormalizationSelector, Record, @@ -65,13 +67,14 @@ const invariant = require('invariant'); const warning = require('warning'); export type GetDataID = ( - fieldValue: {[string]: mixed}, + fieldValue: {+[string]: mixed}, typeName: string, ) => mixed; export type NormalizationOptions = { +getDataID: GetDataID, +treatMissingFieldsAsNull: boolean, + +log: ?LogFunction, +path?: $ReadOnlyArray, +shouldProcessClientComponents?: ?boolean, +actorIdentifier?: ?ActorIdentifier, @@ -87,12 +90,14 @@ function normalize( response: PayloadData, options: NormalizationOptions, errors?: Array, + useExecTimeResolvers?: boolean, ): RelayResponsePayload { const {dataID, node, variables} = selector; const normalizer = new RelayResponseNormalizer( recordSource, variables, options, + useExecTimeResolvers ?? false, ); return normalizer.normalizeResponse(node, dataID, response, errors); } @@ -114,13 +119,16 @@ class RelayResponseNormalizer { _path: Array; _recordSource: MutableRecordSource; _variables: Variables; + _useExecTimeResolvers: boolean; _shouldProcessClientComponents: ?boolean; _errorTrie: RelayErrorTrie | null; + _log: ?LogFunction; constructor( recordSource: MutableRecordSource, variables: Variables, options: NormalizationOptions, + useExecTimeResolvers: boolean, ) { this._actorIdentifier = options.actorIdentifier; this._getDataId = options.getDataID; @@ -129,11 +137,13 @@ class RelayResponseNormalizer { this._incrementalPlaceholders = []; this._isClientExtension = false; this._isUnmatchedAbstractType = false; + this._useExecTimeResolvers = useExecTimeResolvers; this._followupPayloads = []; this._path = options.path ? [...options.path] : []; this._recordSource = recordSource; this._variables = variables; this._shouldProcessClientComponents = options.shouldProcessClientComponents; + this._log = options.log; } normalizeResponse( @@ -240,7 +250,11 @@ class RelayResponseNormalizer { this._traverseSelections(selection, record, data); } } else { - const implementsInterface = data.hasOwnProperty(abstractKey); + // $FlowFixMe[method-unbinding] - data could be prototype less + const implementsInterface = Object.prototype.hasOwnProperty.call( + data, + abstractKey, + ); const typeName = RelayModernRecord.getType(record); const typeID = generateTypeID(typeName); let typeRecord = this._recordSource.get(typeID); @@ -261,7 +275,11 @@ class RelayResponseNormalizer { } case 'TypeDiscriminator': { const {abstractKey} = selection; - const implementsInterface = data.hasOwnProperty(abstractKey); + // $FlowFixMe[method-unbinding] - data could be prototype less + const implementsInterface = Object.prototype.hasOwnProperty.call( + data, + abstractKey, + ); const typeName = RelayModernRecord.getType(record); const typeID = generateTypeID(typeName); let typeRecord = this._recordSource.get(typeID); @@ -319,13 +337,15 @@ class RelayResponseNormalizer { this._normalizeActorChange(selection, record, data); break; case 'RelayResolver': - this._normalizeResolver(selection, record, data); - break; case 'RelayLiveResolver': - this._normalizeResolver(selection, record, data); + if (!this._useExecTimeResolvers) { + this._normalizeResolver(selection, record, data); + } break; case 'ClientEdgeToClientObject': - this._normalizeResolver(selection.backingField, record, data); + if (!this._useExecTimeResolvers) { + this._normalizeResolver(selection.backingField, record, data); + } break; default: (selection: empty); @@ -472,12 +492,11 @@ class RelayResponseNormalizer { const responseKey = selection.alias || selection.name; const storageKey = getStorageKey(selection, this._variables); const fieldValue = data[responseKey]; - if ( - fieldValue == null || - (RelayFeatureFlags.ENABLE_NONCOMPLIANT_ERROR_HANDLING_ON_LISTS && - Array.isArray(fieldValue) && - fieldValue.length === 0) - ) { + const isNoncompliantlyNullish = + RelayFeatureFlags.ENABLE_NONCOMPLIANT_ERROR_HANDLING_ON_LISTS && + Array.isArray(fieldValue) && + fieldValue.length === 0; + if (fieldValue == null || isNoncompliantlyNullish) { if (fieldValue === undefined) { // Fields may be missing in the response in two main cases: // - Inside a client extension: the server will not generally return @@ -511,20 +530,27 @@ class RelayResponseNormalizer { return; } } - if (__DEV__) { - if (selection.kind === 'ScalarField') { - this._validateConflictingFieldsWithIdenticalId( - record, - storageKey, - // When using `treatMissingFieldsAsNull` the conflicting validation raises a false positive - // because the value is set using `null` but validated using `fieldValue` which at this point - // will be `undefined`. - // Setting this to `null` matches the value that we actually set to the `fieldValue`. - null, - ); + if (selection.kind === 'ScalarField') { + this._validateConflictingFieldsWithIdenticalId( + record, + storageKey, + // When using `treatMissingFieldsAsNull` the conflicting validation raises a false positive + // because the value is set using `null` but validated using `fieldValue` which at this point + // will be `undefined`. + // Setting this to `null` matches the value that we actually set to the `fieldValue`. + null, + ); + } + if (isNoncompliantlyNullish) { + // We need to preserve the fact that we received an empty list + if (selection.kind === 'LinkedField') { + RelayModernRecord.setLinkedRecordIDs(record, storageKey, []); + } else { + RelayModernRecord.setValue(record, storageKey, []); } + } else { + RelayModernRecord.setValue(record, storageKey, null); } - RelayModernRecord.setValue(record, storageKey, null); const errorTrie = this._errorTrie; if (errorTrie != null) { const errors = getErrorsByKey(errorTrie, responseKey); @@ -536,13 +562,11 @@ class RelayResponseNormalizer { } if (selection.kind === 'ScalarField') { - if (__DEV__) { - this._validateConflictingFieldsWithIdenticalId( - record, - storageKey, - fieldValue, - ); - } + this._validateConflictingFieldsWithIdenticalId( + record, + storageKey, + fieldValue, + ); RelayModernRecord.setValue(record, storageKey, fieldValue); } else if (selection.kind === 'LinkedField') { this._path.push(responseKey); @@ -686,13 +710,11 @@ class RelayResponseNormalizer { 'RelayResponseNormalizer: Expected id on field `%s` to be a string.', storageKey, ); - if (__DEV__) { - this._validateConflictingLinkedFieldsWithIdenticalId( - RelayModernRecord.getLinkedRecordID(record, storageKey), - nextID, - storageKey, - ); - } + this._validateConflictingLinkedFieldsWithIdenticalId( + RelayModernRecord.getLinkedRecordID(record, storageKey), + nextID, + storageKey, + ); RelayModernRecord.setLinkedRecordID(record, storageKey, nextID); let nextRecord = this._recordSource.get(nextID); if (!nextRecord) { @@ -700,7 +722,7 @@ class RelayResponseNormalizer { const typeName = field.concreteType || this._getRecordType(fieldValue); nextRecord = RelayModernRecord.create(nextID, typeName); this._recordSource.set(nextID, nextRecord); - } else if (__DEV__) { + } else { this._validateRecordType(nextRecord, field, fieldValue); } // $FlowFixMe[incompatible-variance] @@ -766,19 +788,15 @@ class RelayResponseNormalizer { const typeName = field.concreteType || this._getRecordType(item); nextRecord = RelayModernRecord.create(nextID, typeName); this._recordSource.set(nextID, nextRecord); - } else if (__DEV__) { + } else { this._validateRecordType(nextRecord, field, item); } - // NOTE: the check to strip __DEV__ code only works for simple - // `if (__DEV__)` - if (__DEV__) { - if (prevIDs) { - this._validateConflictingLinkedFieldsWithIdenticalId( - prevIDs[nextIndex], - nextID, - storageKey, - ); - } + if (prevIDs) { + this._validateConflictingLinkedFieldsWithIdenticalId( + prevIDs[nextIndex], + nextID, + storageKey, + ); } // $FlowFixMe[incompatible-variance] this._traverseSelections(field, nextRecord, item); @@ -796,20 +814,42 @@ class RelayResponseNormalizer { field: NormalizationLinkedField, payload: Object, ): void { - const typeName = field.concreteType ?? this._getRecordType(payload); - const dataID = RelayModernRecord.getDataID(record); - warning( - (isClientID(dataID) && dataID !== ROOT_ID) || - RelayModernRecord.getType(record) === typeName, - 'RelayResponseNormalizer: Invalid record `%s`. Expected %s to be ' + - 'consistent, but the record was assigned conflicting types `%s` ' + - 'and `%s`. The GraphQL server likely violated the globally unique ' + - 'id requirement by returning the same id for different objects.', - dataID, - TYPENAME_KEY, - RelayModernRecord.getType(record), - typeName, - ); + if (RelayFeatureFlags.ENABLE_STORE_ID_COLLISION_LOGGING) { + const typeName = field.concreteType ?? this._getRecordType(payload); + const dataID = RelayModernRecord.getDataID(record); + const expected = + (isClientID(dataID) && dataID !== ROOT_ID) || + RelayModernRecord.getType(record) === typeName; + if (!expected) { + const logEvent: IdCollisionTypenameLogEvent = { + name: 'idCollision.typename', + previous_typename: RelayModernRecord.getType(record), + new_typename: typeName, + }; + if (this._log != null) { + this._log(logEvent); + } + } + } + // NOTE: Only emit a warning in DEV + if (__DEV__) { + const typeName = field.concreteType ?? this._getRecordType(payload); + const dataID = RelayModernRecord.getDataID(record); + const expected = + (isClientID(dataID) && dataID !== ROOT_ID) || + RelayModernRecord.getType(record) === typeName; + warning( + expected, + 'RelayResponseNormalizer: Invalid record `%s`. Expected %s to be ' + + 'consistent, but the record was assigned conflicting types `%s` ' + + 'and `%s`. The GraphQL server likely violated the globally unique ' + + 'id requirement by returning the same id for different objects.', + dataID, + TYPENAME_KEY, + RelayModernRecord.getType(record), + typeName, + ); + } } /** @@ -820,14 +860,16 @@ class RelayResponseNormalizer { storageKey: string, fieldValue: mixed, ): void { - // NOTE: Only call this function in DEV + // NOTE: Only emit a warning in DEV if (__DEV__) { + const previousValue = RelayModernRecord.getValue(record, storageKey); const dataID = RelayModernRecord.getDataID(record); - var previousValue = RelayModernRecord.getValue(record, storageKey); - warning( + const expected = storageKey === TYPENAME_KEY || - previousValue === undefined || - areEqual(previousValue, fieldValue), + previousValue === undefined || + areEqual(previousValue, fieldValue); + warning( + expected, 'RelayResponseNormalizer: Invalid record. The record contains two ' + 'instances of the same id: `%s` with conflicting field, %s and its values: %s and %s. ' + 'If two fields are different but share ' + @@ -848,10 +890,11 @@ class RelayResponseNormalizer { nextID: DataID, storageKey: string, ): void { - // NOTE: Only call this function in DEV + // NOTE: Only emit a warning in DEV if (__DEV__) { + const expected = prevID === undefined || prevID === nextID; warning( - prevID === undefined || prevID === nextID, + expected, 'RelayResponseNormalizer: Invalid record. The record contains ' + 'references to the conflicting field, %s and its id values: %s and %s. ' + 'We need to make sure that the record the field points ' + diff --git a/packages/relay-runtime/store/RelayStoreSubscriptions.js b/packages/relay-runtime/store/RelayStoreSubscriptions.js index bfdd489656bb3..7957787c68cb9 100644 --- a/packages/relay-runtime/store/RelayStoreSubscriptions.js +++ b/packages/relay-runtime/store/RelayStoreSubscriptions.js @@ -115,7 +115,7 @@ class RelayStoreSubscriptions implements StoreSubscriptions { missingLiveResolverFields: backup.missingLiveResolverFields, seenRecords: backup.seenRecords, selector: backup.selector, - errorResponseFields: backup.errorResponseFields, + fieldErrors: backup.fieldErrors, }; } else { // This subscription was created during the optimisitic state. We should @@ -185,7 +185,7 @@ class RelayStoreSubscriptions implements StoreSubscriptions { missingLiveResolverFields: nextSnapshot.missingLiveResolverFields, seenRecords: nextSnapshot.seenRecords, selector: nextSnapshot.selector, - errorResponseFields: nextSnapshot.errorResponseFields, + fieldErrors: nextSnapshot.fieldErrors, }: Snapshot); if (__DEV__) { deepFreeze(nextSnapshot); diff --git a/packages/relay-runtime/store/RelayStoreTypes.js b/packages/relay-runtime/store/RelayStoreTypes.js index d2d04fae95b99..4de34e28d8291 100644 --- a/packages/relay-runtime/store/RelayStoreTypes.js +++ b/packages/relay-runtime/store/RelayStoreTypes.js @@ -116,7 +116,7 @@ export type NormalizationSelector = { +variables: Variables, }; -export type ErrorResponseField = +export type FieldError = | RelayFieldPayloadErrorEvent | MissingExpectedDataLogEvent | MissingExpectedDataThrowEvent @@ -124,7 +124,7 @@ export type ErrorResponseField = | MissingRequiredFieldLogEvent | MissingRequiredFieldThrowEvent; -export type ErrorResponseFields = Array; +export type FieldErrors = Array; export type ClientEdgeTraversalInfo = { +readerClientEdge: ReaderClientEdgeToServerObject, @@ -149,7 +149,7 @@ export type Snapshot = { +missingClientEdges: null | $ReadOnlyArray, +seenRecords: DataIDSet, +selector: SingularReaderSelector, - +errorResponseFields: ?ErrorResponseFields, + +fieldErrors: ?FieldErrors, }; /** @@ -245,7 +245,7 @@ export interface RecordSource { /** * A collection of records keyed by id. */ -export type RecordSourceJSON = {[DataID]: ?RecordJSON}; +export type RecordSourceJSON = {+[DataID]: ?RecordJSON}; /** * A read/write interface for accessing and updating graph data. @@ -258,10 +258,10 @@ export interface MutableRecordSource extends RecordSource { } export type CheckOptions = { - handlers: $ReadOnlyArray, - defaultActorIdentifier: ActorIdentifier, - getTargetForActor: (actorIdentifier: ActorIdentifier) => MutableRecordSource, - getSourceForActor: (actorIdentifier: ActorIdentifier) => RecordSource, + +handlers: $ReadOnlyArray, + +defaultActorIdentifier: ActorIdentifier, + +getTargetForActor: (actorIdentifier: ActorIdentifier) => MutableRecordSource, + +getSourceForActor: (actorIdentifier: ActorIdentifier) => RecordSource, }; export type OperationAvailability = @@ -684,6 +684,11 @@ export type ExecuteCompleteLogEvent = { +executeId: number, }; +export type ExecuteUnsubscribeLogEvent = { + +name: 'execute.unsubscribe', + +executeId: number, +}; + export type ExecuteNormalizeStart = { +name: 'execute.normalize.start', +operation: OperationDescriptor, @@ -781,12 +786,23 @@ export type UseFragmentSubscriptionMissedUpdates = { +hasDataChanges: boolean, }; +/** + * This event is logged when two strong objects share the same id, + * but have different types, resulting in an collision in the store. + */ +export type IdCollisionTypenameLogEvent = { + +name: 'idCollision.typename', + +previous_typename: string, + +new_typename: string, +}; + export type LogEvent = | SuspenseFragmentLogEvent | SuspenseQueryLogEvent | QueryResourceFetchLogEvent | QueryResourceRetainLogEvent | FragmentResourceMissingDataLogEvent + | IdCollisionTypenameLogEvent | PendingOperationFoundLogEvent | NetworkInfoLogEvent | NetworkStartLogEvent @@ -800,6 +816,7 @@ export type LogEvent = | ExecuteAsyncModuleLogEvent | ExecuteErrorLogEvent | ExecuteCompleteLogEvent + | ExecuteUnsubscribeLogEvent | ExecuteNormalizeStart | ExecuteNormalizeEnd | StoreDataCheckerStartEvent @@ -1155,6 +1172,7 @@ export type NormalizeResponseFunction = ( selector: NormalizationSelector, typeName: string, options: NormalizationOptions, + useExecTimeResolvers: boolean, ) => RelayResponsePayload; /** @@ -1270,6 +1288,8 @@ export type MissingExpectedDataLogEvent = { +kind: 'missing_expected_data.log', +owner: string, fieldPath: string, // Purposefully mutable to allow lazy construction in RelayReader + // To populate this, you should pass the value to a ReactRelayLoggingContext + +uiContext: mixed | void, }; /** @@ -1297,6 +1317,8 @@ export type MissingExpectedDataThrowEvent = { +owner: string, fieldPath: string, // Purposefully mutable to allow lazy construction in RelayReader +handled: boolean, + // To populate this, you should pass the value to a ReactRelayLoggingContext + +uiContext: mixed | void, }; /** @@ -1307,6 +1329,8 @@ export type MissingRequiredFieldLogEvent = { +kind: 'missing_required_field.log', +owner: string, fieldPath: string, // Purposefully mutable to allow lazy construction in RelayReader + // To populate this, you should pass the value to a ReactRelayLoggingContext + +uiContext: mixed | void, }; /** @@ -1325,6 +1349,8 @@ export type MissingRequiredFieldThrowEvent = { +owner: string, fieldPath: string, // Purposefully mutable to allow lazy construction in RelayReader +handled: boolean, + // To populate this, you should pass the value to a ReactRelayLoggingContext + +uiContext: mixed | void, }; /** @@ -1346,6 +1372,8 @@ export type RelayResolverErrorEvent = { +error: Error, +shouldThrow: boolean, +handled: boolean, + // To populate this, you should pass the value to a ReactRelayLoggingContext + +uiContext: mixed | void, }; /** @@ -1372,6 +1400,8 @@ export type RelayFieldPayloadErrorEvent = { +error: TRelayFieldError, +shouldThrow: boolean, +handled: boolean, + // To populate this, you should pass the value to a ReactRelayLoggingContext + +uiContext: mixed | void, }; /** @@ -1481,7 +1511,7 @@ export type ConcreteClientEdgeResolverReturnType = { * returns a callback which should be called when the value _may_ have changed. * * While over-notification (subscription notifications when the read value has - * not actually changed) is suported, for performance reasons, it is recommended + * not actually changed) is supported, for performance reasons, it is recommended * that the provider of the LiveState value confirms that the value has indeed * change before notifying Relay of the change. */ diff --git a/packages/relay-runtime/store/RelayStoreUtils.js b/packages/relay-runtime/store/RelayStoreUtils.js index 49ce5e05a770b..f28b68860b8a8 100644 --- a/packages/relay-runtime/store/RelayStoreUtils.js +++ b/packages/relay-runtime/store/RelayStoreUtils.js @@ -15,6 +15,8 @@ import type { NormalizationArgument, NormalizationField, NormalizationHandle, + NormalizationLiveResolverField, + NormalizationResolverField, } from '../util/NormalizationNode'; import type { ReaderActorChange, @@ -28,6 +30,7 @@ import type {Variables} from '../util/RelayRuntimeTypes'; const getRelayHandleKey = require('../util/getRelayHandleKey'); const RelayConcreteNode = require('../util/RelayConcreteNode'); +const RelayFeatureFlags = require('../util/RelayFeatureFlags'); const {stableCopy} = require('../util/stableCopy'); const invariant = require('invariant'); @@ -42,6 +45,8 @@ const ERRORS_KEY: '__errors' = '__errors'; const MODULE_COMPONENT_KEY_PREFIX = '__module_component_'; const MODULE_OPERATION_KEY_PREFIX = '__module_operation_'; +const RELAY_READ_TIME_RESOLVER_KEY_PREFIX = '$r:'; + function getArgumentValue( arg: NormalizationArgument | ReaderArgument, variables: Variables, @@ -163,6 +168,28 @@ function getStorageKey( : name; } +/** + * This is a special case of getStorageKey that should be used when dealing with + * read time resolver fields. A resolver may be used at both exec time and at read + * time within the same project. However, the value of the read time resolver is + * wrapped while the value of the exec time resolver is a standard Relay object. To + * disambiguate in the case that both types may exist on the same record, the read + * time resolver storage keys are prefixed. + */ +function getReadTimeResolverStorageKey( + field: + | ReaderRelayResolver + | ReaderRelayLiveResolver + | NormalizationResolverField + | NormalizationLiveResolverField, + variables: Variables, +): string { + const storageKey = getStorageKey(field, variables); + return RelayFeatureFlags.ENABLE_READ_TIME_RESOLVER_STORAGE_KEY_PREFIX + ? '$r:' + storageKey // Using inlined string to test the performance impact + : storageKey; +} + /** * Given a field the method returns an array of arguments. * For Relay resolver fields, we store arguments on the field and fragment @@ -271,12 +298,14 @@ const RelayStoreUtils = { RELAY_RESOLVER_SNAPSHOT_KEY: '__resolverSnapshot', RELAY_RESOLVER_ERROR_KEY: '__resolverError', RELAY_RESOLVER_OUTPUT_TYPE_RECORD_IDS: '__resolverOutputTypeRecordIDs', + RELAY_READ_TIME_RESOLVER_KEY_PREFIX, formatStorageKey, getArgumentValue, getArgumentValues, getHandleStorageKey, getStorageKey, + getReadTimeResolverStorageKey, getStableStorageKey, getModuleComponentKey, getModuleOperationKey, diff --git a/packages/relay-runtime/store/ResolverCache.js b/packages/relay-runtime/store/ResolverCache.js index 91b2b1101adc0..316d4c0ea45cb 100644 --- a/packages/relay-runtime/store/ResolverCache.js +++ b/packages/relay-runtime/store/ResolverCache.js @@ -18,7 +18,7 @@ import type { import type {DataID, Variables} from '../util/RelayRuntimeTypes'; import type { DataIDSet, - ErrorResponseFields, + FieldErrors, SingularReaderSelector, Snapshot, } from './RelayStoreTypes'; @@ -32,11 +32,11 @@ export type EvaluationResult = { error: ?Error, }; -export type ResolverFragmentResult = { +export type ResolverFragmentResult = $ReadOnly<{ data: mixed, isMissingData: boolean, - errorResponseFields: ?ErrorResponseFields, -}; + fieldErrors: ?FieldErrors, +}>; export type GetDataForResolverFragmentFn = SingularReaderSelector => ResolverFragmentResult; diff --git a/packages/relay-runtime/store/ResolverFragments.js b/packages/relay-runtime/store/ResolverFragments.js index 9e1256533f12b..a55c224314884 100644 --- a/packages/relay-runtime/store/ResolverFragments.js +++ b/packages/relay-runtime/store/ResolverFragments.js @@ -112,12 +112,14 @@ function readFragment( fragmentSelector.kind === 'SingularReaderSelector', `Expected a singular reader selector for the fragment of the resolver ${fragmentNode.name}, but it was plural.`, ); - const {data, isMissingData, errorResponseFields} = - context.getDataForResolverFragment(fragmentSelector, fragmentKey); + const {data, isMissingData, fieldErrors} = context.getDataForResolverFragment( + fragmentSelector, + fragmentKey, + ); if ( isMissingData || - (errorResponseFields != null && errorResponseFields.some(eventShouldThrow)) + (fieldErrors != null && fieldErrors.some(eventShouldThrow)) ) { throw RESOLVER_FRAGMENT_ERRORED_SENTINEL; } diff --git a/packages/relay-runtime/store/StoreInspector.js b/packages/relay-runtime/store/StoreInspector.js index 15ff2125feadf..6d96ba585b00a 100644 --- a/packages/relay-runtime/store/StoreInspector.js +++ b/packages/relay-runtime/store/StoreInspector.js @@ -32,11 +32,14 @@ if (__DEV__) { } formattersInstalled = true; // $FlowFixMe[incompatible-use] D61394600 + // $FlowFixMe[cannot-resolve-name] if (window.devtoolsFormatters == null) { // $FlowFixMe[incompatible-use] D61394600 + // $FlowFixMe[cannot-resolve-name] window.devtoolsFormatters = []; } // $FlowFixMe[incompatible-use] D61394600 + // $FlowFixMe[cannot-resolve-name] if (!Array.isArray(window.devtoolsFormatters)) { return; } @@ -47,6 +50,7 @@ if (__DEV__) { 'section.', ); // $FlowFixMe[incompatible-use] D61394600 + // $FlowFixMe[cannot-resolve-name] window.devtoolsFormatters.push(...createFormatters()); }; @@ -137,6 +141,7 @@ if (__DEV__) { ): ?{[string]: mixed} => { const record = source.get(dataID); if (record == null) { + // $FlowFixMe[incompatible-return] return record; } return new Proxy( diff --git a/packages/relay-runtime/store/__tests__/ClientEdgeToClientObject-test.js b/packages/relay-runtime/store/__tests__/ClientEdgeToClientObject-test.js index e670f2b49e013..a5edbafce77c0 100644 --- a/packages/relay-runtime/store/__tests__/ClientEdgeToClientObject-test.js +++ b/packages/relay-runtime/store/__tests__/ClientEdgeToClientObject-test.js @@ -12,7 +12,10 @@ 'use strict'; import type {ClientEdgeToClientObjectTest3Query$data} from './__generated__/ClientEdgeToClientObjectTest3Query.graphql'; +import type {ClientEdgeToClientObjectTestClientRootFragment$key} from './__generated__/ClientEdgeToClientObjectTestClientRootFragment.graphql'; +import type {ClientEdgeToClientObjectTestClientRootNameFragment$key} from './__generated__/ClientEdgeToClientObjectTestClientRootNameFragment.graphql'; +const {readFragment} = require('../ResolverFragments'); const {commitLocalUpdate} = require('relay-runtime'); const RelayNetwork = require('relay-runtime/network/RelayNetwork'); const {graphql} = require('relay-runtime/query/GraphQLTag'); @@ -265,3 +268,89 @@ test('Uses an existing client record if it already exists', () => { }, }); }); + +type Account = { + account_name: string, +}; +/** + * @RelayResolver Query.account: ClientAccount + */ +function account(): {id: string} { + return {id: '1'}; +} + +/** + * @RelayResolver ClientAccount.self: RelayResolverValue + * @rootFragment ClientEdgeToClientObjectTestClientRootFragment + */ +function self( + fragmentKey: ClientEdgeToClientObjectTestClientRootFragment$key, +): Account { + const data = readFragment( + graphql` + fragment ClientEdgeToClientObjectTestClientRootFragment on ClientAccount { + id @required(action: THROW) + } + `, + fragmentKey, + ); + return {account_name: JSON.stringify(data)}; +} + +/** + * @RelayResolver ClientAccount.account_name: String + * @rootFragment ClientEdgeToClientObjectTestClientRootNameFragment + */ +function account_name( + fragmentKey: ClientEdgeToClientObjectTestClientRootNameFragment$key, +): ?string { + const acct = readFragment( + graphql` + fragment ClientEdgeToClientObjectTestClientRootNameFragment on ClientAccount { + self + } + `, + fragmentKey, + ); + return acct.self?.account_name; +} + +test('it can read a rootFragment on a client type defined on client schema', () => { + const AccountQuery = graphql` + query ClientEdgeToClientObjectTestClientRootFragmentQuery { + account { + __id + id + account_name + } + } + `; + + const source = RelayRecordSource.create(); + + const operation = createOperationDescriptor(AccountQuery, {}); + const liveStore = new RelayModernStore(source, { + gcReleaseBufferSize: 0, + }); + + const environment = new RelayModernEnvironment({ + network: RelayNetwork.create(jest.fn()), + store: liveStore, + }); + + const data = environment.lookup(operation.fragment).data; + + expect(data).toEqual({ + account: { + __id: 'client:ClientAccount:1', + account_name: '{"id":"1"}', + id: '1', + }, + }); +}); + +module.exports = { + account, + self, + account_name, +}; diff --git a/packages/relay-runtime/store/__tests__/DataChecker-test.js b/packages/relay-runtime/store/__tests__/DataChecker-test.js index ee81e14ef22cb..4a9aa1aa9af9d 100644 --- a/packages/relay-runtime/store/__tests__/DataChecker-test.js +++ b/packages/relay-runtime/store/__tests__/DataChecker-test.js @@ -13,7 +13,7 @@ import type {NormalizationLinkedField} from '../../util/NormalizationNode'; import type {ReaderLinkedField} from '../../util/ReaderNode'; import type {Variables} from '../../util/RelayRuntimeTypes'; -import type {ReadOnlyRecordProxy} from '../RelayStoreTypes'; +import type {ReadOnlyRecordProxy, RecordSourceJSON} from '../RelayStoreTypes'; import type { DataCheckerTest10Query$data, DataCheckerTest10Query$variables, @@ -70,7 +70,7 @@ describe('check()', () => { DataCheckerTestQuery$variables, DataCheckerTestQuery$data, >; - let sampleData; + let sampleData: RecordSourceJSON; beforeEach(() => { sampleData = { '1': { @@ -176,7 +176,7 @@ describe('check()', () => { }); it('reads fragment data', () => { - const data = { + const data: RecordSourceJSON = { '1': { __id: '1', id: '1', @@ -251,12 +251,13 @@ describe('check()', () => { it('reads handle fields in fragment', () => { const handleKey = getRelayHandleKey('test', null, 'profilePicture'); - const data = { + const data: RecordSourceJSON = { '1': { __id: '1', id: '1', __typename: 'User', 'profilePicture(size:32)': {__ref: 'client:1'}, + // $FlowFixMe[invalid-computed-prop] [handleKey]: {__ref: 'client:3'}, }, 'client:1': { @@ -296,7 +297,7 @@ describe('check()', () => { }); it('reads handle fields in fragment and checks missing', () => { - const data = { + const data: RecordSourceJSON = { '1': { __id: '1', id: '1', @@ -342,12 +343,13 @@ describe('check()', () => { it('reads handle fields in fragment and checks missing sub field', () => { const handleKey = getRelayHandleKey('test', null, 'profilePicture'); - const data = { + const data: RecordSourceJSON = { '1': { __id: '1', id: '1', __typename: 'User', 'profilePicture(size:32)': {__ref: 'client:1'}, + // $FlowFixMe[invalid-computed-prop] [handleKey]: {__ref: 'client:3'}, }, 'client:1': { @@ -388,7 +390,7 @@ describe('check()', () => { it('reads handle fields in operation', () => { const handleKey = getRelayHandleKey('test', null, 'profilePicture'); - const data = { + const data: RecordSourceJSON = { 'client:root': { __id: 'client:root', __typename: '__Root', @@ -399,6 +401,7 @@ describe('check()', () => { id: '1', __typename: 'User', 'profilePicture(size:32)': {__ref: 'client:1'}, + // $FlowFixMe[invalid-computed-prop] [handleKey]: {__ref: 'client:3'}, }, 'client:1': { @@ -447,7 +450,7 @@ describe('check()', () => { }); it('reads handle fields in operation and checks missing', () => { - const data = { + const data: RecordSourceJSON = { 'client:root': { __id: 'client:root', __typename: '__Root', @@ -506,7 +509,7 @@ describe('check()', () => { it('reads handle fields in operation and checks missing sub field', () => { const handleKey = getRelayHandleKey('test', null, 'profilePicture'); - const data = { + const data: RecordSourceJSON = { 'client:root': { __id: 'client:root', __typename: '__Root', @@ -517,6 +520,7 @@ describe('check()', () => { id: '1', __typename: 'User', 'profilePicture(size:32)': {__ref: 'client:1'}, + // $FlowFixMe[invalid-computed-prop] [handleKey]: {__ref: 'client:3'}, }, 'client:1': { @@ -565,7 +569,7 @@ describe('check()', () => { it('reads scalar handle fields in operation and checks presence', () => { const handleKey = getRelayHandleKey('test', null, 'uri'); - const data = { + const data: RecordSourceJSON = { 'client:root': { __id: 'client:root', __typename: '__Root', @@ -581,6 +585,7 @@ describe('check()', () => { __id: 'client:2', __typename: 'Photo', uri: 'https://...', + // $FlowFixMe[invalid-computed-prop] [handleKey]: 'https://...', }, }; @@ -618,7 +623,7 @@ describe('check()', () => { }); it('reads scalar handle fields in operation and checks missing', () => { - const data = { + const data: RecordSourceJSON = { 'client:root': { __id: 'client:root', __typename: '__Root', @@ -708,7 +713,7 @@ describe('check()', () => { BarQuery = graphql` query DataCheckerTest4Query($id: ID!) { node(id: $id) { - ...DataCheckerTest4Fragment + ...DataCheckerTest4Fragment @dangerously_unaliased_fixme } } `; @@ -731,7 +736,7 @@ describe('check()', () => { it('returns true when the match field/record exist and match a supported type (plaintext)', () => { // When the type matches PlainUserNameRenderer - const storeData = { + const storeData: RecordSourceJSON = { '1': { __id: '1', id: '1', @@ -792,7 +797,7 @@ describe('check()', () => { it('returns true when the match field/record exist and match a supported type (markdown)', () => { // When the type matches MarkdownUserNameRenderer - const storeData = { + const storeData: RecordSourceJSON = { '1': { __id: '1', id: '1', @@ -851,7 +856,7 @@ describe('check()', () => { // The field returned the MarkdownUserNameRenderer type, but the module for that branch // has not been loaded. The assumption is that the data cannot have been processed in that // case and therefore the markdown field is missing in the store. - const storeData = { + const storeData: RecordSourceJSON = { '1': { __id: '1', id: '1', @@ -902,7 +907,7 @@ describe('check()', () => { it('returns false when the match field/record exist but a scalar field is missing', () => { // the `data` field for the MarkdownUserNameRenderer is missing - const storeData = { + const storeData: RecordSourceJSON = { '1': { __id: '1', id: '1', @@ -956,7 +961,7 @@ describe('check()', () => { it('returns false when the match field/record exist but a linked field is missing', () => { // the `data` field for the MarkdownUserNameRenderer is missing - const storeData = { + const storeData: RecordSourceJSON = { '1': { __id: '1', id: '1', @@ -1003,7 +1008,7 @@ describe('check()', () => { }); it('returns true when the match field/record exist but do not match a supported type', () => { - const storeData = { + const storeData: RecordSourceJSON = { '1': { __id: '1', id: '1', @@ -1048,7 +1053,7 @@ describe('check()', () => { }); it('returns true when the match field is non-existent (null)', () => { - const storeData = { + const storeData: RecordSourceJSON = { '1': { __id: '1', id: '1', @@ -1086,7 +1091,7 @@ describe('check()', () => { }); it('returns false when the match field is not fetched (undefined)', () => { - const storeData = { + const storeData: RecordSourceJSON = { '1': { __id: '1', id: '1', @@ -1160,7 +1165,7 @@ describe('check()', () => { BarQuery = graphql` query DataCheckerTest5Query($id: ID!) { node(id: $id) { - ...DataCheckerTest5Fragment + ...DataCheckerTest5Fragment @dangerously_unaliased_fixme } } `; @@ -1183,7 +1188,7 @@ describe('check()', () => { it('returns true when the field/record exists and matches the @module type (plaintext)', () => { // When the type matches PlainUserNameRenderer - const storeData = { + const storeData: RecordSourceJSON = { '1': { __id: '1', id: '1', @@ -1244,7 +1249,7 @@ describe('check()', () => { it('returns true when the field/record exist and matches the @module type (markdown)', () => { // When the type matches MarkdownUserNameRenderer - const storeData = { + const storeData: RecordSourceJSON = { '1': { __id: '1', id: '1', @@ -1303,7 +1308,7 @@ describe('check()', () => { // The field returned the MarkdownUserNameRenderer type, but the module for that branch // has not been loaded. The assumption is that the data cannot have been processed in that // case and therefore the markdown field is missing in the store. - const storeData = { + const storeData: RecordSourceJSON = { '1': { __id: '1', id: '1', @@ -1354,7 +1359,7 @@ describe('check()', () => { it('returns false when the field/record exists but a scalar field is missing', () => { // the `data` field for the MarkdownUserNameRenderer is missing - const storeData = { + const storeData: RecordSourceJSON = { '1': { __id: '1', id: '1', @@ -1408,7 +1413,7 @@ describe('check()', () => { it('returns false when the field/record exists but a linked field is missing', () => { // the `data` field for the MarkdownUserNameRenderer is missing - const storeData = { + const storeData: RecordSourceJSON = { '1': { __id: '1', id: '1', @@ -1455,7 +1460,7 @@ describe('check()', () => { }); it('returns true when the field/record exists but does not match any @module selection', () => { - const storeData = { + const storeData: RecordSourceJSON = { '1': { __id: '1', id: '1', @@ -1512,14 +1517,16 @@ describe('check()', () => { Query = graphql` query DataCheckerTest9Query($id: ID!) { node(id: $id) { - ...DataCheckerTest6Fragment @defer(label: "TestFragment") + ...DataCheckerTest6Fragment + @dangerously_unaliased_fixme + @defer(label: "TestFragment") } } `; }); it('returns true when deferred selections are fetched', () => { - const storeData = { + const storeData: RecordSourceJSON = { '1': { __id: '1', __typename: 'User', @@ -1555,7 +1562,7 @@ describe('check()', () => { }); it('returns false when deferred selections are not fetched', () => { - const storeData = { + const storeData: RecordSourceJSON = { '1': { __id: '1', __typename: 'User', @@ -1604,14 +1611,14 @@ describe('check()', () => { Query = graphql` query DataCheckerTest6Query($id: ID!) { node(id: $id) { - ...DataCheckerTest7Fragment + ...DataCheckerTest7Fragment @dangerously_unaliased_fixme } } `; }); it('returns true when streamed selections are fetched', () => { - const storeData = { + const storeData: RecordSourceJSON = { '1': { __id: '1', __typename: 'Feedback', @@ -1653,7 +1660,7 @@ describe('check()', () => { }); it('returns false when streamed selections are not fetched', () => { - const storeData = { + const storeData: RecordSourceJSON = { '1': { __id: '1', __typename: 'Feedback', @@ -1721,7 +1728,7 @@ describe('check()', () => { describe('when some data is missing', () => { it('returns missing on missing records', () => { - const data = { + const data: RecordSourceJSON = { '1': { __id: '1', id: '1', @@ -1760,7 +1767,7 @@ describe('check()', () => { }); it('returns missing on missing fields', () => { - const data = { + const data: RecordSourceJSON = { '1': { __id: '1', id: '1', @@ -1802,7 +1809,7 @@ describe('check()', () => { }); it('allows handlers to supplement missing scalar fields', () => { - const data = { + const data: RecordSourceJSON = { '1': { __id: '1', id: '1', @@ -1907,7 +1914,7 @@ describe('check()', () => { ])( 'linked field handler handler that returns %s', (_name, {handleReturnValue, expectedStatus, updatedHometown}) => { - const data = { + const data: RecordSourceJSON = { user1: { __id: 'user1', id: 'user1', @@ -2066,7 +2073,7 @@ describe('check()', () => { ])( 'plural linked field handler handler that returns %s', (_name, {handleReturnValue, expectedStatus, updatedScreennames}) => { - const data = { + const data: RecordSourceJSON = { user1: { __id: 'user1', id: 'user1', @@ -2142,7 +2149,7 @@ describe('check()', () => { ); it('returns modified records with the target', () => { - const data = { + const data: RecordSourceJSON = { '1': { __id: '1', id: '1', @@ -2217,7 +2224,7 @@ describe('check()', () => { }); it('returns available even when client field is missing', () => { - const data = { + const data: RecordSourceJSON = { '1': { __id: '1', id: '1', @@ -2584,7 +2591,7 @@ describe('check()', () => { } `; - const data = { + const data: RecordSourceJSON = { 'client:root': { __id: 'client:root', __typename: 'Query', @@ -2636,7 +2643,7 @@ describe('check()', () => { `; const typeID = generateTypeID('User'); - const data = { + const data: RecordSourceJSON = { 'client:root': { __id: 'client:root', __typename: 'Query', @@ -2648,6 +2655,7 @@ describe('check()', () => { name: 'Alice', // no `id` value }, + // $FlowFixMe[invalid-computed-prop] [typeID]: { __id: typeID, __typename: TYPE_SCHEMA_TYPE, @@ -2691,7 +2699,7 @@ describe('check()', () => { `; const typeID = generateTypeID('User'); - const data = { + const data: RecordSourceJSON = { 'client:root': { __id: 'client:root', __typename: 'Query', @@ -2703,6 +2711,7 @@ describe('check()', () => { name: 'Alice', id: '1', }, + // $FlowFixMe[invalid-computed-prop] [typeID]: { __id: typeID, __typename: TYPE_SCHEMA_TYPE, @@ -2747,7 +2756,7 @@ describe('check()', () => { `; const typeID = generateTypeID('NonNodeNoID'); - const data = { + const data: RecordSourceJSON = { 'client:root': { __id: 'client:root', __typename: 'Query', @@ -2759,6 +2768,7 @@ describe('check()', () => { // no 'id' bc not a Node name: 'Not a Node!', }, + // $FlowFixMe[invalid-computed-prop] [typeID]: { __id: typeID, __typename: TYPE_SCHEMA_TYPE, @@ -2814,7 +2824,7 @@ describe('check()', () => { }); it('should be able to handle multi-actor stores', () => { - const data = { + const data: RecordSourceJSON = { 'client:root': { __id: 'client:root', __typename: '__Root', @@ -2862,6 +2872,7 @@ describe('check()', () => { __typename: 'Text', text: 'Hello, Antonio', }, + // $FlowFixMe[invalid-computed-prop] [typeID]: { __id: typeID, __typename: TYPE_SCHEMA_TYPE, @@ -2891,7 +2902,7 @@ describe('check()', () => { }); it('should report missing data in multi-actor stores', () => { - const data = { + const data: RecordSourceJSON = { 'client:root': { __id: 'client:root', __typename: '__Root', @@ -3055,4 +3066,160 @@ describe('check()', () => { ); expect(target.toJSON()).toEqual({}); }); + + describe('exec time resolvers', () => { + describe('client query', () => { + const Query = graphql` + query DataCheckerTestExecQuery @exec_time_resolvers { + RelayReaderExecResolversTest_user_one { + name + best_friend { + name + best_friend { + name + } + } + } + } + `; + + it('should return available when all data is available', () => { + const source = RelayRecordSource.create({ + 'client:root': { + __id: 'client:root', + __typename: '__Root', + RelayReaderExecResolversTest_user_one: {__ref: '1'}, + }, + '1': { + __id: '1', + name: 'Alice', + best_friend: {__ref: '2'}, + }, + '2': { + __id: '2', + name: 'Bob', + best_fried: {__ref: '3'}, + }, + '3': { + __id: '3', + name: 'Zuck', + }, + }); + const target = RelayRecordSource.create(); + const status = check( + () => source, + () => target, + INTERNAL_ACTOR_IDENTIFIER_DO_NOT_USE, + createNormalizationSelector(getRequest(Query).operation, ROOT_ID, {}), + [], + null, + defaultGetDataID, + ); + expect(status.status).toBe('available'); + }); + + it('should return available when only client data is missing', () => { + const source = RelayRecordSource.create({ + 'client:root': { + __id: 'client:root', + __typename: '__Root', + RelayReaderExecResolversTest_user_one: {__ref: '1'}, + }, + '1': { + __id: '1', + name: 'Alice', + best_friend: {__ref: '2'}, + }, + '2': { + __id: '2', + name: 'Bob', + best_fried: undefined, + }, + }); + const target = RelayRecordSource.create(); + const status = check( + () => source, + () => target, + INTERNAL_ACTOR_IDENTIFIER_DO_NOT_USE, + createNormalizationSelector(getRequest(Query).operation, ROOT_ID, {}), + [], + null, + defaultGetDataID, + ); + expect(status.status).toBe('available'); + }); + }); + + describe('server and client query', () => { + const Query = graphql` + query DataCheckerTestExecWithServerDataQuery @exec_time_resolvers { + RelayReaderExecResolversTest_user_one { + name + best_friend { + name + best_friend { + name + } + } + } + me { + name + } + } + `; + it('should return available when server data is available', () => { + const source = RelayRecordSource.create({ + 'client:root': { + __id: 'client:root', + __typename: '__Root', + RelayReaderExecResolversTest_user_one: undefined, + me: {__ref: '0'}, + }, + '0': { + __id: '0', + id: '0', + name: 'Zuck', + }, + }); + const target = RelayRecordSource.create(); + const status = check( + () => source, + () => target, + INTERNAL_ACTOR_IDENTIFIER_DO_NOT_USE, + createNormalizationSelector(getRequest(Query).operation, ROOT_ID, {}), + [], + null, + defaultGetDataID, + ); + expect(status.status).toBe('available'); + }); + + it('should return missing when server data is missing', () => { + const source = RelayRecordSource.create({ + 'client:root': { + __id: 'client:root', + __typename: '__Root', + RelayReaderExecResolversTest_user_one: undefined, + me: {__ref: '0'}, + }, + '0': { + __id: '0', + id: '0', + name: undefined, + }, + }); + const target = RelayRecordSource.create(); + const status = check( + () => source, + () => target, + INTERNAL_ACTOR_IDENTIFIER_DO_NOT_USE, + createNormalizationSelector(getRequest(Query).operation, ROOT_ID, {}), + [], + null, + defaultGetDataID, + ); + expect(status.status).toBe('missing'); + }); + }); + }); }); diff --git a/packages/relay-runtime/store/__tests__/RelayExperimentalGraphResponseHandler-test.js b/packages/relay-runtime/store/__tests__/RelayExperimentalGraphResponseHandler-test.js index 5d4a4ba41eadc..5fc33667000d8 100644 --- a/packages/relay-runtime/store/__tests__/RelayExperimentalGraphResponseHandler-test.js +++ b/packages/relay-runtime/store/__tests__/RelayExperimentalGraphResponseHandler-test.js @@ -30,6 +30,7 @@ const {getRequest} = require('relay-runtime/query/GraphQLTag'); const defaultOptions = { getDataID: defaultGetDataID, treatMissingFieldsAsNull: false, + log: null, }; function applyTransform( diff --git a/packages/relay-runtime/store/__tests__/RelayExperimentalGraphResponseTransform-test.js b/packages/relay-runtime/store/__tests__/RelayExperimentalGraphResponseTransform-test.js index 43bff654705e3..eb4247937d0ca 100644 --- a/packages/relay-runtime/store/__tests__/RelayExperimentalGraphResponseTransform-test.js +++ b/packages/relay-runtime/store/__tests__/RelayExperimentalGraphResponseTransform-test.js @@ -32,6 +32,7 @@ const {ROOT_ID} = require('relay-runtime/store/RelayStoreUtils'); const defaultOptions = { getDataID: defaultGetDataID, treatMissingFieldsAsNull: false, + log: null, }; function applyTransform( @@ -588,6 +589,7 @@ test('Fragment Spread (gets inlined into `InlineFragment`)', () => { query RelayExperimentalGraphResponseTransformTestFragmentSpreadQuery { node(id: "10") { ...RelayExperimentalGraphResponseTransformTest_user_name + @dangerously_unaliased_fixme } } `; @@ -644,6 +646,7 @@ test('Fragment Spread @no_inline', () => { query RelayExperimentalGraphResponseTransformTestFragmentSpreadNoInlineQuery { node(id: "10") { ...RelayExperimentalGraphResponseTransformTest_no_inline_user_name + @dangerously_unaliased_fixme } } `; @@ -702,6 +705,7 @@ test('Traverses when @defer is disabled', () => { ) { node(id: $id) { ...RelayExperimentalGraphResponseTransformTest_condition + @dangerously_unaliased_fixme @defer(label: "TestFragment", if: $enableDefer) } } diff --git a/packages/relay-runtime/store/__tests__/RelayModernEnvironMent-Constructor-test.js b/packages/relay-runtime/store/__tests__/RelayModernEnvironMent-Constructor-test.js new file mode 100644 index 0000000000000..0084b50958920 --- /dev/null +++ b/packages/relay-runtime/store/__tests__/RelayModernEnvironMent-Constructor-test.js @@ -0,0 +1,41 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + * @oncall relay + */ + +const RelayNetwork = require('../../network/RelayNetwork'); +const RelayModernEnvironment = require('../RelayModernEnvironment'); +const RelayModernStore = require('../RelayModernStore'); + +describe('new Environment()', () => { + it('creates a properly configured Relay Store if one is not provided', () => { + const log = () => {}; + const operationLoader = { + get: jest.fn(), + load: jest.fn(), + }; + const getDataID = () => 'lol'; + const shouldProcessClientComponents = true; + const network = RelayNetwork.create(jest.fn()); + const environment = new RelayModernEnvironment({ + network, + log, + operationLoader, + getDataID, + shouldProcessClientComponents, + }); + + const store = environment.getStore(); + + if (!(store instanceof RelayModernStore)) { + throw new Error('Expected store to be an instance of RelayModernStore'); + } + expect(store.__log).toBe(log); + }); +}); diff --git a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ApplyMutation-test.js b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ApplyMutation-test.js index dfb6f0229ef2c..af46400e6560d 100644 --- a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ApplyMutation-test.js +++ b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ApplyMutation-test.js @@ -90,6 +90,7 @@ describe.each(['RelayModernEnvironment', 'MultiActorEnvironment'])( node(id: $id) { id ...RelayModernEnvironmentApplyMutationTestFragment + @dangerously_unaliased_fixme } } `; diff --git a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-Connection-test.js b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-Connection-test.js index dcb96cd423de7..e61d9983f0464 100644 --- a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-Connection-test.js +++ b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-Connection-test.js @@ -57,6 +57,7 @@ describe.each(['RelayModernEnvironment', 'MultiActorEnvironment'])( query RelayModernEnvironmentConnectionTestFeedbackQuery($id: ID!) { node(id: $id) { ...RelayModernEnvironmentConnectionTestFeedbackFragment + @dangerously_unaliased_fixme } } `; @@ -68,6 +69,7 @@ describe.each(['RelayModernEnvironment', 'MultiActorEnvironment'])( ) { node(id: $id) { ...RelayModernEnvironmentConnectionTestFeedbackFragment + @dangerously_unaliased_fixme @arguments(count: $count, cursor: $cursor) } } diff --git a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ConnectionAndRequired-test.js b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ConnectionAndRequired-test.js index d1a8a0664ed64..4cd1e6e23c4ad 100644 --- a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ConnectionAndRequired-test.js +++ b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ConnectionAndRequired-test.js @@ -50,6 +50,7 @@ describe.each(['RelayModernEnvironment', 'MultiActorEnvironment'])( ) { node(id: $id) { ...RelayModernEnvironmentConnectionAndRequiredTestFeedbackFragment + @dangerously_unaliased_fixme } } `; @@ -130,7 +131,7 @@ describe.each(['RelayModernEnvironment', 'MultiActorEnvironment'])( getSingularSelector(fragment, nextOperationSnapshot.data?.node), ); const snapshot = environment.lookup(selector); - expect(snapshot.errorResponseFields).toEqual([ + expect(snapshot.fieldErrors).toEqual([ { kind: 'missing_required_field.log', owner: diff --git a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-DynamicConnectionKey-test.js b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-DynamicConnectionKey-test.js index c230bdddb83a5..53d607e69dbce 100644 --- a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-DynamicConnectionKey-test.js +++ b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-DynamicConnectionKey-test.js @@ -61,6 +61,7 @@ describe.each(['RelayModernEnvironment', 'MultiActorEnvironment'])( ) { node(id: $id) { ...RelayModernEnvironmentDynamicConnectionKeyTestFeedbackFragment + @dangerously_unaliased_fixme } } `; @@ -73,6 +74,7 @@ describe.each(['RelayModernEnvironment', 'MultiActorEnvironment'])( ) { node(id: $id) { ...RelayModernEnvironmentDynamicConnectionKeyTestFeedbackFragment + @dangerously_unaliased_fixme @arguments(count: $count, cursor: $cursor) } } diff --git a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteMutation-WithLocalInvalidation-test.js b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteMutation-WithLocalInvalidation-test.js index 92756c69ac7ba..1f1ad481a0336 100644 --- a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteMutation-WithLocalInvalidation-test.js +++ b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteMutation-WithLocalInvalidation-test.js @@ -85,6 +85,7 @@ describe.each(['RelayModernEnvironment', 'MultiActorEnvironment'])( node(id: $id) { id ...RelayModernEnvironmentExecuteMutationWithLocalInvalidationTestCommentFragment + @dangerously_unaliased_fixme } } `; diff --git a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteMutation-test.js b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteMutation-test.js index ea8e3be40d71c..9dab1564ec33a 100644 --- a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteMutation-test.js +++ b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteMutation-test.js @@ -105,6 +105,7 @@ describe.each(['RelayModernEnvironment', 'MultiActorEnvironment'])( node(id: $id) { id ...RelayModernEnvironmentExecuteMutationTestCommentFragment + @dangerously_unaliased_fixme } } `; diff --git a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteMutationWithGlobalInvalidation-test.js b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteMutationWithGlobalInvalidation-test.js index 4cec04d91125e..829416d42c55e 100644 --- a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteMutationWithGlobalInvalidation-test.js +++ b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteMutationWithGlobalInvalidation-test.js @@ -87,6 +87,7 @@ describe('executeMutation() with global invalidation', () => { node(id: $id) { id ...RelayModernEnvironmentExecuteMutationWithGlobalInvalidationTestCommentFragment + @dangerously_unaliased_fixme } } `, diff --git a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteMutationWithMatch-test.js b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteMutationWithMatch-test.js index 658456ce3ed17..2d2fe0ba6f7b7 100644 --- a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteMutationWithMatch-test.js +++ b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteMutationWithMatch-test.js @@ -145,6 +145,7 @@ describe.each(['RelayModernEnvironment', 'MultiActorEnvironment'])( node(id: $id) { id ...RelayModernEnvironmentExecuteMutationWithMatchTestCommentFragment + @dangerously_unaliased_fixme } } `; diff --git a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteSubscription-test.js b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteSubscription-test.js index c7e8ef8977deb..60cf788d7fb1f 100644 --- a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteSubscription-test.js +++ b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteSubscription-test.js @@ -75,6 +75,7 @@ describe('execute()', () => { node(id: $id) { id ...RelayModernEnvironmentExecuteSubscriptionTestCommentFragment + @dangerously_unaliased_fixme } } `; diff --git a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteSubscriptionWithDefer-test.js b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteSubscriptionWithDefer-test.js index 922b32ebc597f..847d19e2cb0b5 100644 --- a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteSubscriptionWithDefer-test.js +++ b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteSubscriptionWithDefer-test.js @@ -88,6 +88,7 @@ describe.each(['RelayModernEnvironment', 'MultiActorEnvironment'])( node(id: $id) { id ...RelayModernEnvironmentExecuteSubscriptionWithDeferTestCommentFragment + @dangerously_unaliased_fixme } } `; diff --git a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteSubscriptionWithMatch-test.js b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteSubscriptionWithMatch-test.js index 3745e3bdd58ff..b3470e4814e98 100644 --- a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteSubscriptionWithMatch-test.js +++ b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteSubscriptionWithMatch-test.js @@ -144,6 +144,7 @@ describe.each(['RelayModernEnvironment', 'MultiActorEnvironment'])( node(id: $id) { id ...RelayModernEnvironmentExecuteSubscriptionWithMatchTestCommentFragment + @dangerously_unaliased_fixme } } `; diff --git a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteSubscriptionWithStream-test.js b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteSubscriptionWithStream-test.js index 33b767cc4b854..de51793b1f354 100644 --- a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteSubscriptionWithStream-test.js +++ b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteSubscriptionWithStream-test.js @@ -81,6 +81,7 @@ describe('executeSubscrption() with @stream', () => { node(id: $id) { id ...RelayModernEnvironmentExecuteSubscriptionWithStreamTestFeedbackFragment + @dangerously_unaliased_fixme } } `; diff --git a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteWithCheck-test.js b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteWithCheck-test.js new file mode 100644 index 0000000000000..9526d7fb1b4ea --- /dev/null +++ b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteWithCheck-test.js @@ -0,0 +1,97 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + * @oncall relay + */ + +'use strict'; +import type {GraphQLResponse} from '../../network/RelayNetworkTypes'; + +const RelayObservable = require('../../network/RelayObservable'); +const {graphql} = require('../../query/GraphQLTag'); +const RelayModernEnvironment = require('../RelayModernEnvironment'); +const { + createOperationDescriptor, +} = require('../RelayModernOperationDescriptor'); +const RelayModernStore = require('../RelayModernStore'); +const RelayRecordSource = require('../RelayRecordSource'); +const {disallowWarnings} = require('relay-test-utils-internal'); + +disallowWarnings(); + +describe('execute() provides a `check` function for the network layer to determine availability of data in store', () => { + let callbacks; + let complete; + let environment; + let error; + let next; + let operation; + let query; + let source; + let store; + let subject; + let variables; + let network; + let check; + beforeEach(() => { + query = graphql` + query RelayModernEnvironmentExecuteWithCheckTestQuery( + $fetchSize: Boolean! + ) { + me { + name + profilePicture(size: 42) @include(if: $fetchSize) { + uri + } + } + } + `; + variables = {fetchSize: false}; + operation = createOperationDescriptor(query, variables); + + complete = jest.fn<[], mixed>(); + error = jest.fn<[Error], mixed>(); + next = jest.fn<[GraphQLResponse], mixed>(); + callbacks = {complete, error, next}; + + network = { + execute: jest.fn( + (_query, _variables, _cacheConfig, _1, _2, _3, _4, _check) => { + check = _check; + return RelayObservable.create(sink => { + subject = sink; + }); + }, + ), + }; + source = RelayRecordSource.create(); + store = new RelayModernStore(source); + environment = new RelayModernEnvironment({ + network, + store, + }); + }); + + it('returns the correct availability in the check function', () => { + environment.execute({operation}).subscribe(callbacks); + expect(check().status).toBe('missing'); + subject.next({ + data: { + me: { + id: '842472', + __typename: 'User', + name: 'Joe', + }, + }, + }); + jest.runAllTimers(); + + environment.execute({operation}).subscribe(callbacks); + expect(check().status).toBe('available'); + }); +}); diff --git a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteWithDefer-test.js b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteWithDefer-test.js index 90bbd3c1f1c9b..5d3d3ad19f311 100644 --- a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteWithDefer-test.js +++ b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteWithDefer-test.js @@ -37,7 +37,11 @@ const { const {createReaderSelector} = require('../RelayModernSelector'); const RelayModernStore = require('../RelayModernStore'); const RelayRecordSource = require('../RelayRecordSource'); -const {disallowWarnings, expectToWarn} = require('relay-test-utils-internal'); +const { + disallowWarnings, + expectToWarn, + expectToWarnMany, +} = require('relay-test-utils-internal'); disallowWarnings(); @@ -67,6 +71,7 @@ describe.each(['RelayModernEnvironment', 'MultiActorEnvironment'])( query RelayModernEnvironmentExecuteWithDeferTestUserQuery($id: ID!) { node(id: $id) { ...RelayModernEnvironmentExecuteWithDeferTestUserFragment + @dangerously_unaliased_fixme @defer(label: "UserFragment") } } @@ -250,6 +255,278 @@ describe.each(['RelayModernEnvironment', 'MultiActorEnvironment'])( }); }); + it('processes deferred payloads mixed with normalized response payloads', () => { + const initialSnapshot = environment.lookup(selector); + const callback = jest.fn<[Snapshot], void>(); + environment.subscribe(initialSnapshot, callback); + + environment.execute({operation}).subscribe(callbacks); + dataSource.next({ + data: { + node: { + id: '1', + __typename: 'User', + }, + }, + }); + jest.runAllTimers(); + next.mockClear(); + callback.mockClear(); + + const extensionsPayload = { + data: { + '1': { + __id: '1', + __typename: 'User', + extra_data: 'Zuck', + }, + }, + extensions: {is_normalized: true}, + }; + dataSource.next(extensionsPayload); + expect(callback).toBeCalledTimes(0); + expect(next).toBeCalledTimes(1); + expect(next.mock.calls[0][0]).toBe(extensionsPayload); + next.mockClear(); + + dataSource.next({ + data: { + id: '1', + __typename: 'User', + name: 'joe', + }, + label: + 'RelayModernEnvironmentExecuteWithDeferTestUserQuery$defer$UserFragment', + path: ['node'], + }); + + expect(complete).toBeCalledTimes(0); + expect(error).toBeCalledTimes(0); + expect(next).toBeCalledTimes(1); + expect(callback).toBeCalledTimes(1); + const snapshot = callback.mock.calls[0][0]; + expect(snapshot.isMissingData).toBe(false); + expect(snapshot.data).toEqual({ + id: '1', + name: 'JOE', + }); + }); + + describe('Query with exec time resolvers', () => { + let resolverOperation; + beforeEach(() => { + const resolverQuery = graphql` + query RelayModernEnvironmentExecuteWithDeferTestResolverQuery( + $id: ID! + ) @exec_time_resolvers { + node(id: $id) { + ...RelayModernEnvironmentExecuteWithDeferTestUserFragment + @dangerously_unaliased_fixme + @defer(label: "UserFragment") + } + } + `; + variables = {id: '1'}; + resolverOperation = createOperationDescriptor( + resolverQuery, + variables, + ); + selector = createReaderSelector( + fragment, + '1', + {}, + resolverOperation.request, + ); + }); + + it('goes out of loading state if all initial payloads are received in an exec time query, but stay active when server or client is loading', () => { + const initialSnapshot = environment.lookup(selector); + const callback = jest.fn<[Snapshot], void>(); + environment.subscribe(initialSnapshot, callback); + + environment + .execute({operation: resolverOperation}) + .subscribe(callbacks); + dataSource.next({ + data: { + node: { + id: '1', + __typename: 'User', + }, + }, + }); + jest.runAllTimers(); + next.mockClear(); + callback.mockClear(); + + dataSource.next({ + data: { + id: '1', + __typename: 'User', + name: 'joe', + }, + label: + 'RelayModernEnvironmentExecuteWithDeferTestResolverQuery$defer$UserFragment', + path: ['node'], + extensions: { + // The server response needs to contain a marker for the final incremental payload + is_final: true, + }, + }); + + expect(complete).toBeCalledTimes(0); + expect(error).toBeCalledTimes(0); + expect(next).toBeCalledTimes(1); + expect(callback).toBeCalledTimes(1); + const snapshot = callback.mock.calls[0][0]; + expect(snapshot.isMissingData).toBe(false); + expect(snapshot.data).toEqual({ + id: '1', + name: 'JOE', + }); + + // Server payloads have finished, but we are still waiting on the resolver payloads + expect( + environment.isRequestActive(resolverOperation.request.identifier), + ).toBe(true); + + // Finishes the exec time query + callback.mockClear(); + next.mockClear(); + const extensionsPayload = { + data: { + '1': { + __id: '1', + __typename: 'User', + // __name_name_handler is where the name gets stored in the current test + // due to the usage of the field handler + __name_name_handler: 'Zuck', + }, + }, + extensions: { + is_normalized: true, + is_final: true, + }, + }; + dataSource.next(extensionsPayload); + expect(callback).toBeCalledTimes(1); + expect(callback.mock.calls[0][0].data).toEqual({ + id: '1', + name: 'Zuck', + }); + + // At this point, the deferred payload and the exec time query payload + // have all been resolved, the query should no longer be treated as pending + expect( + environment + .getOperationTracker() + .getPendingOperationsAffectingOwner(resolverOperation.request), + ).toBe(null); + expect( + environment.isRequestActive(resolverOperation.request.identifier), + ).toBe(false); + }); + + it('Stay active when server or client is loading, when client finishes first', () => { + const initialSnapshot = environment.lookup(selector); + const callback = jest.fn<[Snapshot], void>(); + environment.subscribe(initialSnapshot, callback); + + environment + .execute({operation: resolverOperation}) + .subscribe(callbacks); + + const extensionsPayload = { + data: { + '1': { + id: '1', + __id: '1', + __typename: 'User', + // __name_name_handler is where the name gets stored in the current test + // due to the usage of the field handler + __name_name_handler: 'Zuck', + }, + }, + extensions: { + is_normalized: true, + is_final: true, + }, + }; + + dataSource.next(extensionsPayload); + expect(callback).toBeCalledTimes(1); + expect(callback.mock.calls[0][0].data).toEqual({ + id: '1', + name: 'Zuck', + }); + next.mockClear(); + callback.mockClear(); + + // Client payloads have finished, but we are still waiting on the server payloads + expect( + environment.isRequestActive(resolverOperation.request.identifier), + ).toBe(true); + expect( + environment + .getOperationTracker() + .getPendingOperationsAffectingOwner(resolverOperation.request), + ).not.toBe(null); + dataSource.next({ + data: { + node: { + id: '1', + __typename: 'User', + }, + }, + }); + jest.runAllTimers(); + next.mockClear(); + callback.mockClear(); + expect( + environment + .getOperationTracker() + .getPendingOperationsAffectingOwner(resolverOperation.request), + ).not.toBe(null); + + dataSource.next({ + data: { + id: '1', + __typename: 'User', + name: 'joe', + }, + label: + 'RelayModernEnvironmentExecuteWithDeferTestResolverQuery$defer$UserFragment', + path: ['node'], + extensions: { + // The server response needs to contain a marker for the final incremental payload + is_final: true, + }, + }); + + expect(complete).toBeCalledTimes(0); + expect(error).toBeCalledTimes(0); + expect(next).toBeCalledTimes(1); + expect(callback).toBeCalledTimes(1); + const snapshot = callback.mock.calls[0][0]; + expect(snapshot.isMissingData).toBe(false); + expect(snapshot.data).toEqual({ + id: '1', + name: 'JOE', + }); + + // At this point, the deferred payload and the exec time query payload + // have all been resolved, the query should no longer be treated as pending + expect( + environment + .getOperationTracker() + .getPendingOperationsAffectingOwner(resolverOperation.request), + ).toBe(null); + expect( + environment.isRequestActive(resolverOperation.request.identifier), + ).toBe(false); + }); + }); + describe('when using a scheduler', () => { let taskID; let tasks; @@ -679,6 +956,109 @@ describe.each(['RelayModernEnvironment', 'MultiActorEnvironment'])( name: 'ALICE', }); }); + + it('warns if nested defer is executed in non-streaming mode and processes deferred selections', () => { + const query = graphql` + query RelayModernEnvironmentExecuteWithDeferTestNestedUserQuery( + $id: ID! + ) { + node(id: $id) { + ...RelayModernEnvironmentExecuteWithDeferTestNestedUserFragment + @dangerously_unaliased_fixme + @defer(label: "UserFragment") + } + } + `; + const fragment = graphql` + fragment RelayModernEnvironmentExecuteWithDeferTestNestedUserFragment on User { + id + ...RelayModernEnvironmentExecuteWithDeferTestNestedInnerUserFragment + @defer + } + `; + const fragmentInner = graphql` + fragment RelayModernEnvironmentExecuteWithDeferTestNestedInnerUserFragment on User { + name + ...RelayModernEnvironmentExecuteWithDeferTestNestedInnerInner2UserFragment + @defer + } + `; + const fragmentInnerInner2 = graphql` + fragment RelayModernEnvironmentExecuteWithDeferTestNestedInnerInner2UserFragment on User { + lastName + } + `; + variables = {id: '1'}; + operation = createOperationDescriptor(query, variables); + selector = createReaderSelector(fragment, '1', {}, operation.request); + + const initialSnapshot = environment.lookup(selector); + const callback = jest.fn<[Snapshot], void>(); + environment.subscribe(initialSnapshot, callback); + + environment.execute({operation}).subscribe(callbacks); + const payload = { + data: { + node: { + id: '1', + __typename: 'User', + name: 'Alice', + lastName: 'Bob', + }, + }, + extensions: { + is_final: true, + }, + }; + + expectToWarnMany( + [ + 'RelayModernEnvironment: Operation `RelayModernEnvironmentExecuteWithDeferTestNestedUserQuery` contains @defer/@stream ' + + 'directives but was executed in non-streaming mode. See ' + + 'https://fburl.com/relay-incremental-delivery-non-streaming-warning.', + 'RelayModernEnvironment: Operation `RelayModernEnvironmentExecuteWithDeferTestNestedUserQuery` contains @defer/@stream ' + + 'directives but was executed in non-streaming mode. See ' + + 'https://fburl.com/relay-incremental-delivery-non-streaming-warning.', + 'RelayModernEnvironment: Operation `RelayModernEnvironmentExecuteWithDeferTestNestedUserQuery` contains @defer/@stream ' + + 'directives but was executed in non-streaming mode. See ' + + 'https://fburl.com/relay-incremental-delivery-non-streaming-warning.', + ], + () => { + dataSource.next(payload); + }, + ); + + expect(complete).not.toBeCalled(); + expect(error).not.toBeCalled(); + expect(next.mock.calls.length).toBe(1); + + expect(callback.mock.calls.length).toBe(1); + const snapshot = callback.mock.calls[0][0]; + expect(snapshot.isMissingData).toBe(false); + expect(snapshot.data?.id).toBe('1'); + + const innerSelector = createReaderSelector( + fragmentInner, + '1', + {}, + operation.request, + ); + const innerSnapshot = environment.lookup(innerSelector); + expect(innerSnapshot.isMissingData).toBe(false); + expect(innerSnapshot.data?.name).toEqual('Alice'); + + const innerInner2Selector = createReaderSelector( + fragmentInnerInner2, + '1', + {}, + operation.request, + ); + const innerInner2Snapshot = environment.lookup(innerInner2Selector); + expect(innerInner2Snapshot.isMissingData).toBe(false); + expect(innerInner2Snapshot.data).toEqual({ + lastName: 'Bob', + }); + }); }); }, ); diff --git a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteWithDeferAndModule-test.js b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteWithDeferAndModule-test.js index a137de1099217..f771770dfc4d7 100644 --- a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteWithDeferAndModule-test.js +++ b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteWithDeferAndModule-test.js @@ -37,6 +37,7 @@ const RelayModernStore = require('../RelayModernStore'); const RelayRecordSource = require('../RelayRecordSource'); const QueryUserNormalizationFragment = require('./__generated__/RelayModernEnvironmentExecuteWithDeferAndModuleTestQuery_user$normalization.graphql'); const {graphql} = require('relay-runtime'); +const {expectToWarn, expectToWarnMany} = require('relay-test-utils-internal'); const { disallowWarnings, injectPromisePolyfill__DEPRECATED, @@ -333,6 +334,168 @@ describe.each(['RelayModernEnvironment', 'MultiActorEnvironment'])( expect(operationLoader.get).toBeCalledTimes(1); operationCallback.mockClear(); }); + + it('warns if executed in non-streaming mode and processes both defer and 3D', () => { + const initialSnapshot = environment.lookup(selector); + const callback = jest.fn<[Snapshot], void>(); + environment.subscribe(initialSnapshot, callback); + + environment.execute({operation}).subscribe(callbacks); + jest + .spyOn(operationLoader, 'get') + .mockImplementationOnce(() => QueryUserNormalizationFragment); + + expectToWarn( + 'RelayModernEnvironment: Operation `RelayModernEnvironmentExecuteWithDeferAndModuleTestQuery` contains @defer/@stream ' + + 'directives but was executed in non-streaming mode. See ' + + 'https://fburl.com/relay-incremental-delivery-non-streaming-warning.', + () => { + dataSource.next([ + { + data: { + node: { + id: '1', + __typename: 'User', + name: 'joe', + __module_component_RelayModernEnvironmentExecuteWithDeferAndModuleTestQuery: + 'User.react', + __module_operation_RelayModernEnvironmentExecuteWithDeferAndModuleTestQuery: + 'RelayModernEnvironmentExecuteWithDeferAndModuleTestQuery_user$normalization.graphql', + }, + }, + extensions: { + is_final: true, + }, + }, + ]); + }, + ); + + expect(callbacks.error).not.toBeCalled(); + expect(callback).toHaveBeenCalledTimes(1); + const snapshot = callback.mock.calls[0][0]; + expect(snapshot.isMissingData).toBe(false); + expect(snapshot.data).toEqual({ + id: '1', + name: 'joe', + }); + expect(operationLoader.get).toBeCalledTimes(1); + operationCallback.mockClear(); + }); + + it('warns if nested defer is executed in non-streaming mode and processes deferred selections', () => { + const query = graphql` + query RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery( + $id: ID! + ) { + node(id: $id) { + ...RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery_user + @defer( + label: "RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery_user" + ) + @module(name: "User.react") + } + } + `; + const fragment = graphql` + fragment RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery_user on User { + id + ...RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInnerUserFragment + @defer + } + `; + const fragmentInner = graphql` + fragment RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInnerUserFragment on User { + name + ...RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInner2UserFragment + @defer + } + `; + const fragmentInnerInner2 = graphql` + fragment RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInner2UserFragment on User { + lastName + } + `; + variables = {id: '1'}; + operation = createOperationDescriptor(query, variables); + selector = createReaderSelector(fragment, '1', {}, operation.request); + + const initialSnapshot = environment.lookup(selector); + const callback = jest.fn<[Snapshot], void>(); + environment.subscribe(initialSnapshot, callback); + + environment.execute({operation}).subscribe(callbacks); + jest + .spyOn(operationLoader, 'get') + .mockImplementationOnce(() => + require('./__generated__/RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery_user$normalization.graphql.js'), + ); + + expectToWarnMany( + [ + 'RelayModernEnvironment: Operation `RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery` contains @defer/@stream ' + + 'directives but was executed in non-streaming mode. See ' + + 'https://fburl.com/relay-incremental-delivery-non-streaming-warning.', + 'RelayModernEnvironment: Operation `RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery` contains @defer/@stream ' + + 'directives but was executed in non-streaming mode. See ' + + 'https://fburl.com/relay-incremental-delivery-non-streaming-warning.', + 'RelayModernEnvironment: Operation `RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery` contains @defer/@stream ' + + 'directives but was executed in non-streaming mode. See ' + + 'https://fburl.com/relay-incremental-delivery-non-streaming-warning.', + ], + () => { + dataSource.next([ + { + data: { + node: { + id: '1', + __typename: 'User', + name: 'joe', + lastName: 'eoj', + __module_component_RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery: + 'User.react', + __module_operation_RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery: + 'RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery$normalization.graphql', + }, + }, + extensions: { + is_final: true, + }, + }, + ]); + }, + ); + + expect(callbacks.error).not.toBeCalled(); + expect(callback).toHaveBeenCalledTimes(1); + const snapshot = callback.mock.calls[0][0]; + expect(snapshot.isMissingData).toBe(false); + expect(snapshot.data?.id).toEqual('1'); + expect(operationLoader.get).toBeCalledTimes(1); + operationCallback.mockClear(); + + const innerSelector = createReaderSelector( + fragmentInner, + '1', + {}, + operation.request, + ); + const innerSnapshot = environment.lookup(innerSelector); + expect(innerSnapshot.isMissingData).toBe(false); + expect(innerSnapshot.data?.name).toEqual('joe'); + + const innerInner2Selector = createReaderSelector( + fragmentInnerInner2, + '1', + {}, + operation.request, + ); + const innerInner2Snapshot = environment.lookup(innerInner2Selector); + expect(innerInner2Snapshot.isMissingData).toBe(false); + expect(innerInner2Snapshot.data).toEqual({ + lastName: 'eoj', + }); + }); }); }, ); diff --git a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteWithOverlappingStream-test.js b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteWithOverlappingStream-test.js index 28fd36854d482..f28ee0b2ff18c 100644 --- a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteWithOverlappingStream-test.js +++ b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteWithOverlappingStream-test.js @@ -65,6 +65,7 @@ describe('execute() a query with multiple @stream selections on the same record' ) { node(id: $id) { ...RelayModernEnvironmentExecuteWithOverlappingStreamTestFeedbackFragment + @dangerously_unaliased_fixme } } `; diff --git a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteWithProvidedVariable-test.js b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteWithProvidedVariable-test.js index 03468c2e5453a..c5d3e448eb30f 100644 --- a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteWithProvidedVariable-test.js +++ b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteWithProvidedVariable-test.js @@ -118,6 +118,7 @@ describe('query with fragments that use provided variables', () => { ) { node(id: $id) { ...RelayModernEnvironmentExecuteWithProvidedVariableTest_profile1 + @dangerously_unaliased_fixme } } `; @@ -128,8 +129,11 @@ describe('query with fragments that use provided variables', () => { ) { node(id: $id) { ...RelayModernEnvironmentExecuteWithProvidedVariableTest_profile1 + @dangerously_unaliased_fixme ...RelayModernEnvironmentExecuteWithProvidedVariableTest_profile2 + @dangerously_unaliased_fixme ...RelayModernEnvironmentExecuteWithProvidedVariableTest_profile3 + @dangerously_unaliased_fixme } } `; diff --git a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteWithSource-test.js b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteWithSource-test.js index 39ec51e2816f4..0b9f436e68357 100644 --- a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteWithSource-test.js +++ b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteWithSource-test.js @@ -203,6 +203,53 @@ describe.each(['RelayModernEnvironment', 'MultiActorEnvironment'])( }, }); }); + + it('calls next() and publishes normalized response to the store', () => { + const selector = createReaderSelector( + query.fragment, + ROOT_ID, + variables, + operation.request, + ); + const snapshot = environment.lookup(selector); + const callback = jest.fn<[Snapshot], void>(); + environment.subscribe(snapshot, callback); + + environment + .executeWithSource({operation, source: fetchSource}) + .subscribe(callbacks); + const payload = { + data: { + '842472': { + __id: '842472', + __typename: 'User', + name: 'Joe', + id: '842472', + }, + 'client:root': { + __id: 'client:root', + __typename: '__Root', + me: {__ref: '842472'}, + }, + }, + extensions: { + is_normalized: true, + }, + }; + subject.next(payload); + jest.runAllTimers(); + + expect(next.mock.calls.length).toBe(1); + expect(next).toBeCalledWith(payload); + expect(complete).not.toBeCalled(); + expect(error).not.toBeCalled(); + expect(callback.mock.calls.length).toBe(1); + expect(callback.mock.calls[0][0].data).toEqual({ + me: { + name: 'Joe', + }, + }); + }); }); }, ); diff --git a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteWithStream-test.js b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteWithStream-test.js index 27b23668b6c9a..3c797f77a6467 100644 --- a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteWithStream-test.js +++ b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteWithStream-test.js @@ -62,6 +62,7 @@ describe('execute() a query with @stream', () => { ) { node(id: $id) { ...RelayModernEnvironmentExecuteWithStreamTestFeedbackFragment + @dangerously_unaliased_fixme } } `; @@ -98,7 +99,7 @@ describe('execute() a query with @stream', () => { }, }; - function getDataID(data: {[string]: mixed}, typename: string) { + function getDataID(data: {+[string]: mixed}, typename: string) { if (typename === 'MessagingParticipant') { return `${typename}:${String(data.id)}`; } diff --git a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteWithStreamAndRequired-test.js b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteWithStreamAndRequired-test.js index 52b81726d03d9..cc82b36b59d44 100644 --- a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteWithStreamAndRequired-test.js +++ b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteWithStreamAndRequired-test.js @@ -50,6 +50,7 @@ describe('execute() a query with @stream and @required', () => { ) { node(id: $id) { ...RelayModernEnvironmentExecuteWithStreamAndRequiredTestFeedbackFragment + @dangerously_unaliased_fixme } } `; @@ -111,7 +112,7 @@ describe('execute() a query with @stream and @required', () => { jest.runAllTimers(); const snapshot = callback.mock.calls[0][0]; - expect(snapshot.errorResponseFields).toEqual([ + expect(snapshot.fieldErrors).toEqual([ { kind: 'missing_required_field.log', owner: diff --git a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteWithStreamWithHandler-test.js b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteWithStreamWithHandler-test.js index a485d0b6270b2..294880d3f8d40 100644 --- a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteWithStreamWithHandler-test.js +++ b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteWithStreamWithHandler-test.js @@ -61,6 +61,7 @@ describe('execute() a query with @stream with handler', () => { ) { node(id: $id) { ...RelayModernEnvironmentExecuteWithStreamWithHandlerTestFeedbackFragment + @dangerously_unaliased_fixme } } `; diff --git a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteWithUndeclaredUnusedArgument-test.js b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteWithUndeclaredUnusedArgument-test.js index 13db879a0a3ca..6f53dc602d1b2 100644 --- a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteWithUndeclaredUnusedArgument-test.js +++ b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-ExecuteWithUndeclaredUnusedArgument-test.js @@ -46,6 +46,7 @@ describe('query with undeclared, unused fragment argument', () => { ) { node(id: $id) { ...RelayModernEnvironmentExecuteWithUndeclaredUnusedArgumentTestProfile + @dangerously_unaliased_fixme } } `; diff --git a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-NoInline-test.js b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-NoInline-test.js index a545ec7d81de9..87a5a92dbf74c 100644 --- a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-NoInline-test.js +++ b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-NoInline-test.js @@ -64,6 +64,7 @@ const NoInlineFragment = graphql` } } ...RelayModernEnvironmentNoInlineTest_inner + @dangerously_unaliased_fixme @arguments(cond: true, preset: $preset, fileExtension: JPG) } `; @@ -373,6 +374,7 @@ describe.each(['RelayModernEnvironment', 'MultiActorEnvironment'])( } } ...RelayModernEnvironmentNoInlineTest_inner + @dangerously_unaliased_fixme @arguments( cond: $cond preset: $preset @@ -542,14 +544,17 @@ describe.each(['RelayModernEnvironment', 'MultiActorEnvironment'])( @argumentDefinitions(cond: {type: "Boolean!"}) { mark: username(name: "Mark") { ...RelayModernEnvironmentNoInlineTest_nestedNoInline + @dangerously_unaliased_fixme @arguments(cond: $global_cond) } zuck: username(name: "Zuck") { ...RelayModernEnvironmentNoInlineTest_nestedNoInline + @dangerously_unaliased_fixme @arguments(cond: false) } joe: username(name: "Joe") { ...RelayModernEnvironmentNoInlineTest_nestedNoInline + @dangerously_unaliased_fixme @arguments(cond: $cond) } } @@ -703,6 +708,7 @@ describe.each(['RelayModernEnvironment', 'MultiActorEnvironment'])( ) { node(id: "1") { ...RelayModernEnvironmentNoInlineTestStream_feedback + @dangerously_unaliased_fixme @arguments(cond: $cond) } } diff --git a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-SubscriptionWithResolverContext-test.js b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-SubscriptionWithResolverContext-test.js new file mode 100644 index 0000000000000..a2a2b7fbc29fe --- /dev/null +++ b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-SubscriptionWithResolverContext-test.js @@ -0,0 +1,94 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + * @oncall relay + */ + +'use strict'; +import type {Snapshot} from '../RelayStoreTypes'; +import type {RecordSourceProxy} from 'relay-runtime/store/RelayStoreTypes'; + +const RelayNetwork = require('../../network/RelayNetwork'); +const {graphql} = require('../../query/GraphQLTag'); +const RelayModernEnvironment = require('../RelayModernEnvironment'); +const { + createOperationDescriptor, +} = require('../RelayModernOperationDescriptor'); +const {createReaderSelector} = require('../RelayModernSelector'); +const RelayModernStore = require('../RelayModernStore'); +const RelayRecordSource = require('../RelayRecordSource'); +const {disallowWarnings} = require('relay-test-utils-internal'); + +disallowWarnings(); + +describe('RelayContext works when applying updates and gets passed through to the RelayStoreSubscriptions', () => { + it('correctly using the resolver data from the context', () => { + const ParentQuery = graphql` + query RelayModernEnvironmentSubscriptionWithResolverContextTestParentQuery { + me { + ...RelayModernEnvironmentSubscriptionWithResolverContextTestUserFragment + } + } + `; + const UserFragment = graphql` + fragment RelayModernEnvironmentSubscriptionWithResolverContextTestUserFragment on User { + id + name + age + } + `; + + const source = RelayRecordSource.create(); + const store = new RelayModernStore(source, { + resolverContext: { + age: 42, + }, + }); + const environment = new RelayModernEnvironment({ + network: RelayNetwork.create(jest.fn()), + store, + }); + const operation = createOperationDescriptor(ParentQuery, {}); + + const selector = createReaderSelector( + UserFragment, + '4', + {}, + operation.request, + ); + const callback = jest.fn<[Snapshot], void>(); + const snapshot = environment.lookup(selector); + environment.subscribe(snapshot, callback); + + callback.mockClear(); + const updater = { + storeUpdater: (proxyStore: RecordSourceProxy) => { + const zuck = proxyStore.create('4', 'User'); + zuck.setValue('4', 'id'); + }, + }; + environment.applyUpdate(updater); + environment.replaceUpdate(updater, { + storeUpdater: proxyStore => { + const zuck = proxyStore.create('4', 'User'); + zuck.setValue('4', 'id'); + zuck.setValue('zuck', 'name'); + }, + }); + expect(callback.mock.calls.length).toBe(2); + expect(callback.mock.calls[0][0].data).toEqual({ + id: '4', + age: 42, + }); + expect(callback.mock.calls[1][0].data).toEqual({ + id: '4', + name: 'zuck', + age: 42, + }); + }); +}); diff --git a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-TypeRefinement-test.js b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-TypeRefinement-test.js index 29deb890686c7..e90cb2d123362 100644 --- a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-TypeRefinement-test.js +++ b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-TypeRefinement-test.js @@ -109,9 +109,13 @@ describe('missing data detection', () => { query RelayModernEnvironmentTypeRefinementTestParentQuery { userOrPage(id: "abc") { ...RelayModernEnvironmentTypeRefinementTestConcreteUserFragment + @dangerously_unaliased_fixme ...RelayModernEnvironmentTypeRefinementTestConcreteInlineRefinementFragment + @dangerously_unaliased_fixme ...RelayModernEnvironmentTypeRefinementTestAbstractActorFragment + @dangerously_unaliased_fixme ...RelayModernEnvironmentTypeRefinementTestAbstractInlineRefinementFragment + @dangerously_unaliased_fixme } } `; @@ -121,7 +125,9 @@ describe('missing data detection', () => { query RelayModernEnvironmentTypeRefinementTestConcreteQuery { userOrPage(id: "abc") { ...RelayModernEnvironmentTypeRefinementTestConcreteUserFragment + @dangerously_unaliased_fixme ...RelayModernEnvironmentTypeRefinementTestConcreteInlineRefinementFragment + @dangerously_unaliased_fixme } } `; @@ -131,7 +137,9 @@ describe('missing data detection', () => { query RelayModernEnvironmentTypeRefinementTestAbstractQuery { userOrPage(id: "abc") { ...RelayModernEnvironmentTypeRefinementTestAbstractActorFragment + @dangerously_unaliased_fixme ...RelayModernEnvironmentTypeRefinementTestAbstractInlineRefinementFragment + @dangerously_unaliased_fixme } } `; @@ -1021,6 +1029,7 @@ describe('missing data detection', () => { query RelayModernEnvironmentTypeRefinementTest2Query { userOrPage(id: "abc") { ...RelayModernEnvironmentTypeRefinementTest3Fragment + @dangerously_unaliased_fixme } } `; @@ -1237,6 +1246,7 @@ describe('missing data detection', () => { query RelayModernEnvironmentTypeRefinementTest3Query { userOrPage(id: "abc") { ...RelayModernEnvironmentTypeRefinementTest5Fragment + @dangerously_unaliased_fixme } } `; @@ -1245,6 +1255,7 @@ describe('missing data detection', () => { id lastName ...RelayModernEnvironmentTypeRefinementTest6Fragment + @dangerously_unaliased_fixme } `; NestedNamedFragment = graphql` @@ -1456,6 +1467,7 @@ describe('missing data detection', () => { query RelayModernEnvironmentTypeRefinementTest4Query { userOrPage(id: "abc") { ...RelayModernEnvironmentTypeRefinementTest7Fragment + @dangerously_unaliased_fixme } } `; @@ -1677,6 +1689,7 @@ describe('missing data detection', () => { query RelayModernEnvironmentTypeRefinementTest5Query { userOrPage(id: "abc") { ...RelayModernEnvironmentTypeRefinementTest9Fragment + @dangerously_unaliased_fixme } } `; @@ -1685,6 +1698,7 @@ describe('missing data detection', () => { id lastName ...RelayModernEnvironmentTypeRefinementTest10Fragment + @dangerously_unaliased_fixme } `; NestedUserFragment = graphql` @@ -1794,6 +1808,7 @@ describe('missing data detection', () => { query RelayModernEnvironmentTypeRefinementTest6Query { userOrPage(id: "abc") { ...RelayModernEnvironmentTypeRefinementTest11Fragment + @dangerously_unaliased_fixme } } `; diff --git a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-Viewer-test.js b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-Viewer-test.js index 75d830a6a190c..65039802345ae 100644 --- a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-Viewer-test.js +++ b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-Viewer-test.js @@ -126,6 +126,8 @@ describe('Mutations on viewer', () => { callback.mockClear(); commitMutation(environment, { mutation, + /* $FlowFixMe[incompatible-call] Natural Inference rollout. See + * https://fburl.com/gdoc/y8dn025u */ variables, onCompleted, onError, diff --git a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-WithOperationTracker-test.js b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-WithOperationTracker-test.js index 7242a46e5d046..105f91f366260 100644 --- a/packages/relay-runtime/store/__tests__/RelayModernEnvironment-WithOperationTracker-test.js +++ b/packages/relay-runtime/store/__tests__/RelayModernEnvironment-WithOperationTracker-test.js @@ -502,6 +502,7 @@ describe.each([true, false])( @relay_test_operation { node(id: $id) { ...RelayModernEnvironmentWithOperationTrackerTestFeedbackFragment + @dangerously_unaliased_fixme } } `; diff --git a/packages/relay-runtime/store/__tests__/RelayModernFragmentSpecResolver-WithFragmentOwnership-test.js b/packages/relay-runtime/store/__tests__/RelayModernFragmentSpecResolver-WithFragmentOwnership-test.js index 6a450e4a3c0c6..73f49dce3ec35 100644 --- a/packages/relay-runtime/store/__tests__/RelayModernFragmentSpecResolver-WithFragmentOwnership-test.js +++ b/packages/relay-runtime/store/__tests__/RelayModernFragmentSpecResolver-WithFragmentOwnership-test.js @@ -98,7 +98,9 @@ describe('RelayModernFragmentSpecResolver with fragment ownership', () => { ) { node(id: $id) { ...RelayModernFragmentSpecResolverWithFragmentOwnershipTestUserFragment + @dangerously_unaliased_fixme ...RelayModernFragmentSpecResolverWithFragmentOwnershipTestUsersFragment + @dangerously_unaliased_fixme } } `; diff --git a/packages/relay-runtime/store/__tests__/RelayModernFragmentSpecResolver-test.js b/packages/relay-runtime/store/__tests__/RelayModernFragmentSpecResolver-test.js index b32ebcc1bc576..06823d39ef1a7 100644 --- a/packages/relay-runtime/store/__tests__/RelayModernFragmentSpecResolver-test.js +++ b/packages/relay-runtime/store/__tests__/RelayModernFragmentSpecResolver-test.js @@ -83,7 +83,9 @@ describe('RelayModernFragmentSpecResolver', () => { ) { node(id: $id) { ...RelayModernFragmentSpecResolverTestQueryUserFragment + @dangerously_unaliased_fixme ...RelayModernFragmentSpecResolverTestQueryUsersFragment + @dangerously_unaliased_fixme } } `; @@ -874,7 +876,9 @@ describe('RelayModernFragmentSpecResolver', () => { ) { node(id: $id) { ...RelayModernFragmentSpecResolverTestQueryUserFragment + @dangerously_unaliased_fixme ...RelayModernFragmentSpecResolverTestQueryUsersFragment + @dangerously_unaliased_fixme } } `; diff --git a/packages/relay-runtime/store/__tests__/RelayModernFragmentSpecResolverRequiredField-test.js b/packages/relay-runtime/store/__tests__/RelayModernFragmentSpecResolverRequiredField-test.js index 9825403c150d4..d9ac965460f19 100644 --- a/packages/relay-runtime/store/__tests__/RelayModernFragmentSpecResolverRequiredField-test.js +++ b/packages/relay-runtime/store/__tests__/RelayModernFragmentSpecResolverRequiredField-test.js @@ -54,6 +54,7 @@ describe('RelayModernFragmentSpecResolver', () => { ) { node(id: $id) { ...RelayModernFragmentSpecResolverRequiredFieldTestUserFragment + @dangerously_unaliased_fixme } } `; diff --git a/packages/relay-runtime/store/__tests__/RelayModernFragmentSpecResolverRequiredFieldNoLogger-test.js b/packages/relay-runtime/store/__tests__/RelayModernFragmentSpecResolverRequiredFieldNoLogger-test.js index d6afca959519d..8f8f21a358ca2 100644 --- a/packages/relay-runtime/store/__tests__/RelayModernFragmentSpecResolverRequiredFieldNoLogger-test.js +++ b/packages/relay-runtime/store/__tests__/RelayModernFragmentSpecResolverRequiredFieldNoLogger-test.js @@ -55,6 +55,7 @@ describe('RelayModernFragmentSpecResolver', () => { ) { node(id: $id) { ...RelayModernFragmentSpecResolverRequiredFieldNoLoggerTestUserFragment + @dangerously_unaliased_fixme } } `; diff --git a/packages/relay-runtime/store/__tests__/RelayModernSelector-test.js b/packages/relay-runtime/store/__tests__/RelayModernSelector-test.js index e6dfe61fc4981..8876fe69fffee 100644 --- a/packages/relay-runtime/store/__tests__/RelayModernSelector-test.js +++ b/packages/relay-runtime/store/__tests__/RelayModernSelector-test.js @@ -56,8 +56,8 @@ describe('RelayModernSelector', () => { $cond: Boolean! ) { node(id: $id) { - ...RelayModernSelectorTestUserFragment - ...RelayModernSelectorTestUsersFragment + ...RelayModernSelectorTestUserFragment @dangerously_unaliased_fixme + ...RelayModernSelectorTestUsersFragment @dangerously_unaliased_fixme } } `; diff --git a/packages/relay-runtime/store/__tests__/RelayModernStore-Subscriptions-test.js b/packages/relay-runtime/store/__tests__/RelayModernStore-Subscriptions-test.js index 67f27036848b8..ee54a39cbc2db 100644 --- a/packages/relay-runtime/store/__tests__/RelayModernStore-Subscriptions-test.js +++ b/packages/relay-runtime/store/__tests__/RelayModernStore-Subscriptions-test.js @@ -371,7 +371,7 @@ function cloneEventWithSets(event: LogEvent) { ...snapshot, missingLiveResolverFields: [], isMissingData: false, - errorResponseFields: null, + fieldErrors: null, data: { name: 'Zuck', profilePicture: { @@ -415,7 +415,7 @@ function cloneEventWithSets(event: LogEvent) { }, missingLiveResolverFields: [], isMissingData: true, - errorResponseFields: [ + fieldErrors: [ { fieldPath: 'profilePicture', kind: 'missing_expected_data.log', @@ -464,7 +464,7 @@ function cloneEventWithSets(event: LogEvent) { name: 'Joe', profilePicture: undefined, }, - errorResponseFields: [ + fieldErrors: [ { fieldPath: 'profilePicture', kind: 'missing_expected_data.log', diff --git a/packages/relay-runtime/store/__tests__/RelayModernStore-test.js b/packages/relay-runtime/store/__tests__/RelayModernStore-test.js index 2ef5803e28304..9a8ec497ac5dd 100644 --- a/packages/relay-runtime/store/__tests__/RelayModernStore-test.js +++ b/packages/relay-runtime/store/__tests__/RelayModernStore-test.js @@ -103,7 +103,9 @@ function cloneEventWithSets(event: LogEvent) { describe('constructor', () => { it('creates the root record upon store initialization', () => { const source = getRecordSourceImplementation({}); - const store = new RelayModernStore(source, {gcReleaseBufferSize: 0}); + const store = new RelayModernStore(source, { + gcReleaseBufferSize: 0, + }); expect(store.getSource().get(ROOT_ID)).toEqual({ __id: ROOT_ID, __typename: ROOT_TYPE, @@ -145,11 +147,14 @@ function cloneEventWithSets(event: LogEvent) { }; initialData = simpleClone(data); source = getRecordSourceImplementation(data); - store = new RelayModernStore(source, {gcReleaseBufferSize: 0}); + store = new RelayModernStore(source, { + gcReleaseBufferSize: 0, + queryCacheExpirationTime: 0, + }); UserQuery = graphql` query RelayModernStoreTest1Query($id: ID!, $size: [Int]) { node(id: $id) { - ...RelayModernStoreTest1Fragment + ...RelayModernStoreTest1Fragment @dangerously_unaliased_fixme } } `; @@ -252,7 +257,9 @@ function cloneEventWithSets(event: LogEvent) { }, }; source = getRecordSourceImplementation(data); - store = new RelayModernStore(source, {gcReleaseBufferSize: 0}); + store = new RelayModernStore(source, { + gcReleaseBufferSize: 0, + }); UserFragment = graphql` fragment RelayModernStoreTest2Fragment on User { name @@ -288,7 +295,7 @@ function cloneEventWithSets(event: LogEvent) { }, }, seenRecords: new Set(Object.keys(data)), - errorResponseFields: null, + fieldErrors: null, missingLiveResolverFields: [], missingClientEdges: null, isMissingData: false, @@ -341,7 +348,7 @@ function cloneEventWithSets(event: LogEvent) { __fragmentOwner: owner.request, }, seenRecords: new Set(Object.keys(data)), - errorResponseFields: null, + fieldErrors: null, missingLiveResolverFields: [], missingClientEdges: null, isMissingData: false, @@ -398,7 +405,7 @@ function cloneEventWithSets(event: LogEvent) { }, }, seenRecords: new Set(['client:2', '4']), - errorResponseFields: null, + fieldErrors: null, missingLiveResolverFields: [], missingClientEdges: null, isMissingData: false, @@ -448,6 +455,7 @@ function cloneEventWithSets(event: LogEvent) { >); source = getRecordSourceImplementation(data); store = new RelayModernStore(source, { + shouldRetainWithinTTL_EXPERIMENTAL: true, log: event => { logEvents.push(cloneEventWithSets(event)); }, @@ -654,7 +662,10 @@ function cloneEventWithSets(event: LogEvent) { }, }; source = getRecordSourceImplementation(dataObj); - store = new RelayModernStore(source, {gcReleaseBufferSize: 0}); + store = new RelayModernStore(source, { + gcReleaseBufferSize: 0, + shouldRetainWithinTTL_EXPERIMENTAL: true, + }); const owner = createOperationDescriptor(UserQuery, {}); const selector = createReaderSelector( UserFragment, @@ -682,7 +693,7 @@ function cloneEventWithSets(event: LogEvent) { ...snapshot, missingClientEdges: null, isMissingData: false, - errorResponseFields: null, + fieldErrors: null, data: { name: 'Zuck', profilePicture: { @@ -724,7 +735,7 @@ function cloneEventWithSets(event: LogEvent) { name: 'Joe', profilePicture: undefined, }, - errorResponseFields: [ + fieldErrors: [ { owner: 'RelayModernStoreTest5Fragment', kind: 'missing_expected_data.log', @@ -778,7 +789,7 @@ function cloneEventWithSets(event: LogEvent) { }, missingClientEdges: null, isMissingData: true, - errorResponseFields: [ + fieldErrors: [ { owner: 'RelayModernStoreTest5Fragment', kind: 'missing_expected_data.log', @@ -1184,11 +1195,14 @@ function cloneEventWithSets(event: LogEvent) { }, }; source = getRecordSourceImplementation(data); - store = new RelayModernStore(source, {gcReleaseBufferSize: 0}); + store = new RelayModernStore(source, { + gcReleaseBufferSize: 0, + shouldRetainWithinTTL_EXPERIMENTAL: true, + }); UserQuery = graphql` query RelayModernStoreTest6Query($id: ID!, $size: [Int]) { node(id: $id) { - ...RelayModernStoreTest7Fragment + ...RelayModernStoreTest7Fragment @dangerously_unaliased_fixme } } `; @@ -1242,7 +1256,10 @@ function cloneEventWithSets(event: LogEvent) { // $FlowFixMe[incompatible-type] found deploying v0.109.0 delete data['client:1']; // profile picture source = getRecordSourceImplementation(data); - store = new RelayModernStore(source, {gcReleaseBufferSize: 0}); + store = new RelayModernStore(source, { + gcReleaseBufferSize: 0, + shouldRetainWithinTTL_EXPERIMENTAL: true, + }); const operation = createOperationDescriptor(UserQuery, { id: '4', size: 32, @@ -1265,6 +1282,7 @@ function cloneEventWithSets(event: LogEvent) { jest.spyOn(global.Date, 'now').mockImplementation(() => currentTime); store = new RelayModernStore(source, { + shouldRetainWithinTTL_EXPERIMENTAL: true, queryCacheExpirationTime: QUERY_CACHE_EXPIRATION_TIME, gcReleaseBufferSize: 0, }); @@ -1291,6 +1309,39 @@ function cloneEventWithSets(event: LogEvent) { }); }); + describe('with infinite queryCacheExpirationTime', () => { + it('always returns available', () => { + let currentTime = Date.now(); + jest.spyOn(global.Date, 'now').mockImplementation(() => currentTime); + + store = new RelayModernStore(source, { + shouldRetainWithinTTL_EXPERIMENTAL: true, + queryCacheExpirationTime: null, + gcReleaseBufferSize: 0, + }); + const operation = createOperationDescriptor(UserQuery, { + id: '4', + size: 32, + }); + store.retain(operation); + store.publish(source); + store.notify(operation); + + expect(store.check(operation)).toEqual({ + status: 'available', + fetchTime: currentTime, + }); + + const fetchTime = currentTime; + currentTime += 10000; // arbitrary number + + expect(store.check(operation)).toEqual({ + status: 'available', + fetchTime, + }); + }); + }); + describe('with global store invalidation', () => { describe("when query hasn't been written to the store before", () => { it('returns stale if data is cached and store has been invalidated', () => { @@ -1383,6 +1434,7 @@ function cloneEventWithSets(event: LogEvent) { it('returns available if data is cached and store was invalidated before query was written (query not retained)', () => { store = new RelayModernStore(source, { + shouldRetainWithinTTL_EXPERIMENTAL: true, gcReleaseBufferSize: 1, }); environment = createMockEnvironment({store}); @@ -1640,7 +1692,10 @@ function cloneEventWithSets(event: LogEvent) { }, }; source = getRecordSourceImplementation(data); - store = new RelayModernStore(source, {gcReleaseBufferSize: 0}); + store = new RelayModernStore(source, { + gcReleaseBufferSize: 0, + shouldRetainWithinTTL_EXPERIMENTAL: true, + }); environment = createMockEnvironment({store}); }); @@ -2070,13 +2125,14 @@ function cloneEventWithSets(event: LogEvent) { initialData = simpleClone(data); source = getRecordSourceImplementation(data); store = new RelayModernStore(source, { + shouldRetainWithinTTL_EXPERIMENTAL: true, gcReleaseBufferSize: 1, queryCacheExpirationTime: QUERY_CACHE_EXPIRATION_TIME, }); UserQuery = graphql` query RelayModernStoreTest7Query($id: ID!, $size: [Int]) { node(id: $id) { - ...RelayModernStoreTest8Fragment + ...RelayModernStoreTest8Fragment @dangerously_unaliased_fixme } } `; @@ -2173,6 +2229,9 @@ function cloneEventWithSets(event: LogEvent) { }); it('releases the operation and collects data after release buffer reaches capacity', () => { + let fetchTime = Date.now(); + jest.spyOn(global.Date, 'now').mockImplementation(() => fetchTime); + const disposable = store.retain( createOperationDescriptor(UserQuery, {id: '4', size: 32}), ); @@ -2195,6 +2254,10 @@ function cloneEventWithSets(event: LogEvent) { // Releasing second operation should cause release buffer to // go over capacity disposable2.dispose(); + + // TTL for all operations has passed + fetchTime += QUERY_CACHE_EXPIRATION_TIME; + jest.runAllTimers(); // Assert that the data for the first operation is collected, while // data for second operation is still retained via the release buffer @@ -2220,6 +2283,9 @@ function cloneEventWithSets(event: LogEvent) { }); it('when same operation retained multiple times, data is only collected until fully released from buffer', () => { + let fetchTime = Date.now(); + jest.spyOn(global.Date, 'now').mockImplementation(() => fetchTime); + const disposable = store.retain( createOperationDescriptor(UserQuery, {id: '4', size: 32}), ); @@ -2255,6 +2321,10 @@ function cloneEventWithSets(event: LogEvent) { // Releasing different operation should cause release buffer to // go over capacity disposable3.dispose(); + + // TTL for all operations has passed + fetchTime += QUERY_CACHE_EXPIRATION_TIME; + jest.runAllTimers(); // Assert that the data for the first operation is collected, while // data for secont operation is still retained via the release buffer @@ -2282,7 +2352,10 @@ function cloneEventWithSets(event: LogEvent) { it('does not free data if previously disposed query is retained again', () => { // Disposing and re-retaining an operation should cause that query to *not* count // toward the release buffer capacity. - store = new RelayModernStore(source, {gcReleaseBufferSize: 2}); + store = new RelayModernStore(source, { + gcReleaseBufferSize: 2, + shouldRetainWithinTTL_EXPERIMENTAL: true, + }); const operation1 = createOperationDescriptor(UserQuery, { id: '1', size: 32, @@ -2363,6 +2436,7 @@ function cloneEventWithSets(event: LogEvent) { [ROOT_ID]: { __id: ROOT_ID, __typename: ROOT_TYPE, + // $FlowFixMe[invalid-computed-prop] [`node(id:"${nodeID}")`]: {__ref: nodeID}, }, }); @@ -2378,6 +2452,7 @@ function cloneEventWithSets(event: LogEvent) { store = new RelayModernStore(source, { gcScheduler: mockScheduler, gcReleaseBufferSize: 0, + queryCacheExpirationTime: 0, }); }); @@ -2513,11 +2588,14 @@ function cloneEventWithSets(event: LogEvent) { }; initialData = simpleClone(data); source = getRecordSourceImplementation(data); - store = new RelayModernStore(source, {gcReleaseBufferSize: 0}); + store = new RelayModernStore(source, { + gcReleaseBufferSize: 0, + queryCacheExpirationTime: 0, + }); UserQuery = graphql` query RelayModernStoreTest9Query($id: ID!, $size: [Int]) { node(id: $id) { - ...RelayModernStoreTest9Fragment + ...RelayModernStoreTest9Fragment @dangerously_unaliased_fixme } } `; diff --git a/packages/relay-runtime/store/__tests__/RelayPublishQueue-test.js b/packages/relay-runtime/store/__tests__/RelayPublishQueue-test.js index 01cea50611a5b..c7dbbc564bad1 100644 --- a/packages/relay-runtime/store/__tests__/RelayPublishQueue-test.js +++ b/packages/relay-runtime/store/__tests__/RelayPublishQueue-test.js @@ -133,7 +133,7 @@ describe('RelayPublishQueue', () => { }); it('runs an `storeUpdater` and applies the changes to the store', () => { - const queue = new RelayPublishQueue(store, null, defaultGetDataID); + const queue = new RelayPublishQueue(store, null, defaultGetDataID, null); const optimisticUpdate = { storeUpdater: storeProxy => { const zuck = storeProxy.get('4'); @@ -155,7 +155,7 @@ describe('RelayPublishQueue', () => { }); it('runs an `selectorStoreUpdater` and applies the changes to the store', () => { - const queue = new RelayPublishQueue(store, null, defaultGetDataID); + const queue = new RelayPublishQueue(store, null, defaultGetDataID, null); const payload = normalizeRelayPayload( operationDescriptor.root, { @@ -184,7 +184,7 @@ describe('RelayPublishQueue', () => { }); it('handles aliases correctly when used with optimistic update', () => { - const queue = new RelayPublishQueue(store, null, defaultGetDataID); + const queue = new RelayPublishQueue(store, null, defaultGetDataID, null); const payload = normalizeRelayPayload( operationDescriptorAliased.root, { @@ -213,7 +213,7 @@ describe('RelayPublishQueue', () => { }); it('unpublishes changes from `storeUpdater` when reverted in the same run()', () => { - const queue = new RelayPublishQueue(store, null, defaultGetDataID); + const queue = new RelayPublishQueue(store, null, defaultGetDataID, null); const optimisticUpdate = { storeUpdater: storeProxy => { const zuck = storeProxy.get('4'); @@ -233,7 +233,7 @@ describe('RelayPublishQueue', () => { }); it('unpublishes changes from `selectorStoreUpdater` when reverted in the same run()', () => { - const queue = new RelayPublishQueue(store, null, defaultGetDataID); + const queue = new RelayPublishQueue(store, null, defaultGetDataID, null); const payload = normalizeRelayPayload( operationDescriptor.root, { @@ -265,7 +265,7 @@ describe('RelayPublishQueue', () => { }); it('unpublishes changes from `storeUpdater` when reverted in a subsequent run()', () => { - const queue = new RelayPublishQueue(store, null, defaultGetDataID); + const queue = new RelayPublishQueue(store, null, defaultGetDataID, null); const optimisticUpdate = { storeUpdater: storeProxy => { const zuck = storeProxy.get('4'); @@ -283,7 +283,7 @@ describe('RelayPublishQueue', () => { }); it('unpublishes changes from `selectorStoreUpdater` when reverted in a subsequent run()', () => { - const queue = new RelayPublishQueue(store, null, defaultGetDataID); + const queue = new RelayPublishQueue(store, null, defaultGetDataID, null); const payload = normalizeRelayPayload( operationDescriptor.root, { @@ -314,7 +314,7 @@ describe('RelayPublishQueue', () => { }); it('applies multiple updaters in the same run()', () => { - const queue = new RelayPublishQueue(store, null, defaultGetDataID); + const queue = new RelayPublishQueue(store, null, defaultGetDataID, null); const payload = normalizeRelayPayload( operationDescriptor.root, { @@ -348,7 +348,7 @@ describe('RelayPublishQueue', () => { }); it('applies updates in subsequent run()s (payload then updater)', () => { - const queue = new RelayPublishQueue(store, null, defaultGetDataID); + const queue = new RelayPublishQueue(store, null, defaultGetDataID, null); const payload = normalizeRelayPayload( operationDescriptor.root, { @@ -381,7 +381,7 @@ describe('RelayPublishQueue', () => { }); it('applies updates in subsequent run()s (updater then updater)', () => { - const queue = new RelayPublishQueue(store, null, defaultGetDataID); + const queue = new RelayPublishQueue(store, null, defaultGetDataID, null); const optimisticUpdate = { storeUpdater: storeProxy => { const zuck = storeProxy.get('4'); @@ -409,7 +409,7 @@ describe('RelayPublishQueue', () => { }); it('applies updates in subsequent run()s (payload then payload)', () => { - const queue = new RelayPublishQueue(store, null, defaultGetDataID); + const queue = new RelayPublishQueue(store, null, defaultGetDataID, null); // First set `name`. const nameMutation = graphql` mutation RelayPublishQueueTest3Mutation($input: ActorNameChangeInput!) { @@ -500,7 +500,7 @@ describe('RelayPublishQueue', () => { }); it('rebases changes when an earlier change is reverted', () => { - const queue = new RelayPublishQueue(store, null, defaultGetDataID); + const queue = new RelayPublishQueue(store, null, defaultGetDataID, null); const payload = normalizeRelayPayload( operationDescriptor.root, { @@ -543,7 +543,7 @@ describe('RelayPublishQueue', () => { }); it('rebases multiple changes on the same value', () => { - const queue = new RelayPublishQueue(store, null, defaultGetDataID); + const queue = new RelayPublishQueue(store, null, defaultGetDataID, null); const incrementPopulation = { storeUpdater: storeProxy => { const mpk = storeProxy.get('mpk'); @@ -583,7 +583,7 @@ describe('RelayPublishQueue', () => { it('unpublishes previously rebased changes when reverted', () => { // Test that backups are created correctly during a rebase - const queue = new RelayPublishQueue(store, null, defaultGetDataID); + const queue = new RelayPublishQueue(store, null, defaultGetDataID, null); const mutation1 = { storeUpdater: storeProxy => { const zuck = storeProxy.get('4'); @@ -637,7 +637,7 @@ describe('RelayPublishQueue', () => { it('reverts executed changes', () => { const publish = jest.spyOn(store, 'publish'); const restore = jest.spyOn(store, 'restore'); - const queue = new RelayPublishQueue(store, null, defaultGetDataID); + const queue = new RelayPublishQueue(store, null, defaultGetDataID, null); // Run the updates queue.applyUpdate({ storeUpdater: storeProxy => { @@ -670,7 +670,7 @@ describe('RelayPublishQueue', () => { it('reverts partially executed/unexecuted changes', () => { const publish = jest.spyOn(store, 'publish'); const restore = jest.spyOn(store, 'restore'); - const queue = new RelayPublishQueue(store, null, defaultGetDataID); + const queue = new RelayPublishQueue(store, null, defaultGetDataID, null); // Run the first update queue.applyUpdate({ storeUpdater: storeProxy => { @@ -702,7 +702,7 @@ describe('RelayPublishQueue', () => { it('reverts unexecuted changes', () => { store.publish = jest.fn(store.publish.bind(store)); - const queue = new RelayPublishQueue(store, null, defaultGetDataID); + const queue = new RelayPublishQueue(store, null, defaultGetDataID, null); // Apply but don't run the updates queue.applyUpdate({ storeUpdater: storeProxy => { @@ -728,7 +728,7 @@ describe('RelayPublishQueue', () => { it('reverts addition of new fields', () => { store.publish = jest.fn(store.publish.bind(store)); - const queue = new RelayPublishQueue(store, null, defaultGetDataID); + const queue = new RelayPublishQueue(store, null, defaultGetDataID, null); queue.applyUpdate({ storeUpdater: storeProxy => { const zuck = storeProxy.get('4'); @@ -746,7 +746,7 @@ describe('RelayPublishQueue', () => { it('reverts addition of linked field', () => { store.publish = jest.fn(store.publish.bind(store)); - const queue = new RelayPublishQueue(store, null, defaultGetDataID); + const queue = new RelayPublishQueue(store, null, defaultGetDataID, null); queue.applyUpdate({ storeUpdater: storeProxy => { const date = storeProxy.create('fookey', 'Date'); @@ -766,7 +766,7 @@ describe('RelayPublishQueue', () => { it('reverts addition of linked fields', () => { store.publish = jest.fn(store.publish.bind(store)); - const queue = new RelayPublishQueue(store, null, defaultGetDataID); + const queue = new RelayPublishQueue(store, null, defaultGetDataID, null); queue.applyUpdate({ storeUpdater: storeProxy => { const phone1 = storeProxy.create('fookey1', 'Phone'); @@ -800,7 +800,7 @@ describe('RelayPublishQueue', () => { restore: jest.fn(), snapshot: jest.fn(() => []), }; - const queue = new RelayPublishQueue(store, null, defaultGetDataID); + const queue = new RelayPublishQueue(store, null, defaultGetDataID, null); const publishSource = new RelayRecordSource(); const ActorQuery = graphql` query RelayPublishQueueTest1Query { @@ -833,7 +833,7 @@ describe('RelayPublishQueue', () => { restore: jest.fn(), snapshot: jest.fn(() => []), }; - const queue = new RelayPublishQueue(store, null, defaultGetDataID); + const queue = new RelayPublishQueue(store, null, defaultGetDataID, null); graphql` fragment RelayPublishQueueTest1Fragment on User { username @@ -1066,7 +1066,7 @@ describe('RelayPublishQueue', () => { }; const source = new RelayRecordSource(simpleClone(initialData)); const store = new RelayModernStore(source); - const queue = new RelayPublishQueue(store, null, defaultGetDataID); + const queue = new RelayPublishQueue(store, null, defaultGetDataID, null); // Set name to 'MARK' *without* running the update queue.applyUpdate({ storeUpdater: storeProxy => { @@ -1123,7 +1123,7 @@ describe('RelayPublishQueue', () => { }; const source = new RelayRecordSource(simpleClone(initialData)); const store = new RelayModernStore(source); - const queue = new RelayPublishQueue(store, null, defaultGetDataID); + const queue = new RelayPublishQueue(store, null, defaultGetDataID, null); // Set name to 'MARK', running the update immediately queue.applyUpdate({ storeUpdater: storeProxy => { @@ -1181,7 +1181,7 @@ describe('RelayPublishQueue', () => { }; const source = new RelayRecordSource(simpleClone(initialData)); const store = new RelayModernStore(source); - const queue = new RelayPublishQueue(store, null, defaultGetDataID); + const queue = new RelayPublishQueue(store, null, defaultGetDataID, null); // Set name to 'MARK' const mutation = { storeUpdater: storeProxy => { @@ -1240,7 +1240,7 @@ describe('RelayPublishQueue', () => { }, }); const store = new RelayModernStore(source); - const queue = new RelayPublishQueue(store, null, defaultGetDataID); + const queue = new RelayPublishQueue(store, null, defaultGetDataID, null); const increaseVolumeUpdater = { storeUpdater: storeProxy => { @@ -1286,7 +1286,7 @@ describe('RelayPublishQueue', () => { }; const source = new RelayRecordSource(simpleClone(initialData)); const store = new RelayModernStore(source); - const queue = new RelayPublishQueue(store, null, defaultGetDataID); + const queue = new RelayPublishQueue(store, null, defaultGetDataID, null); const buggyUpdater = storeProxy => { invariant(false, 'buggy updater throwing error'); }; @@ -1345,7 +1345,7 @@ describe('RelayPublishQueue', () => { restore: jest.fn(), snapshot: jest.fn(() => []), }; - const queue = new RelayPublishQueue(store, null, defaultGetDataID); + const queue = new RelayPublishQueue(store, null, defaultGetDataID, null); graphql` fragment RelayPublishQueueTest2Fragment on User { username @@ -1412,7 +1412,7 @@ describe('RelayPublishQueue', () => { restore: jest.fn(), snapshot: jest.fn(() => []), }; - const queue = new RelayPublishQueue(store, null, defaultGetDataID); + const queue = new RelayPublishQueue(store, null, defaultGetDataID, null); graphql` fragment RelayPublishQueueTest3Fragment on User { username @@ -1486,7 +1486,7 @@ describe('RelayPublishQueue', () => { restore: jest.fn(), snapshot: jest.fn(() => []), }; - const queue = new RelayPublishQueue(store, null, defaultGetDataID); + const queue = new RelayPublishQueue(store, null, defaultGetDataID, null); const source = new RelayRecordSource(); const user = RelayModernRecord.create('1364586419', 'User'); @@ -1511,7 +1511,7 @@ describe('RelayPublishQueue', () => { beforeEach(() => { source = new RelayRecordSource({}); store = new RelayModernStore(source); - queue = new RelayPublishQueue(store, null, defaultGetDataID); + queue = new RelayPublishQueue(store, null, defaultGetDataID, null); const nameQuery = graphql` query RelayPublishQueueTest11Query { me { @@ -1593,7 +1593,7 @@ describe('RelayPublishQueue', () => { }; const source = new RelayRecordSource(simpleClone(initialData)); const store = new RelayModernStore(source); - const queue = new RelayPublishQueue(store, null, defaultGetDataID); + const queue = new RelayPublishQueue(store, null, defaultGetDataID, null); // Set name to 'MARK', running the update immediately queue.applyUpdate({ storeUpdater: storeProxy => { @@ -1637,7 +1637,7 @@ describe('RelayPublishQueue', () => { }; const source = new RelayRecordSource(simpleClone(initialData)); const store = new RelayModernStore(source); - const queue = new RelayPublishQueue(store, null, defaultGetDataID); + const queue = new RelayPublishQueue(store, null, defaultGetDataID, null); // Set name to 'MARK' const mutation = { storeUpdater: storeProxy => { @@ -1687,7 +1687,7 @@ describe('RelayPublishQueue', () => { restore: jest.fn(), snapshot: jest.fn(() => []), }; - const queue = new RelayPublishQueue(store, null, defaultGetDataID); + const queue = new RelayPublishQueue(store, null, defaultGetDataID, null); queue.commitUpdate(storeProxy => { const user = storeProxy.create('1364586419', 'User'); user.setValue('Jan', 'name'); @@ -1717,7 +1717,7 @@ describe('RelayPublishQueue', () => { restore: jest.fn(), snapshot: jest.fn(() => []), }; - const queue = new RelayPublishQueue(store, null, defaultGetDataID); + const queue = new RelayPublishQueue(store, null, defaultGetDataID, null); queue.commitUpdate(storeProxy => { storeProxy.invalidateStore(); }); @@ -1742,7 +1742,7 @@ describe('RelayPublishQueue', () => { restore: jest.fn(), snapshot: jest.fn(() => []), }; - const queue = new RelayPublishQueue(store, null, defaultGetDataID); + const queue = new RelayPublishQueue(store, null, defaultGetDataID, null); queue.commitUpdate(storeProxy => { const user = storeProxy.create('1364586419', 'User'); user.setValue('Jan', 'name'); @@ -1772,7 +1772,7 @@ describe('RelayPublishQueue', () => { restore: jest.fn(), snapshot: jest.fn(() => []), }; - const queue = new RelayPublishQueue(store, null, defaultGetDataID); + const queue = new RelayPublishQueue(store, null, defaultGetDataID, null); expectWarningWillFire( 'RelayPublishQueue.run was called, but the call would have been a noop.', ); @@ -1793,7 +1793,7 @@ describe('RelayPublishQueue', () => { restore: jest.fn(), snapshot: jest.fn(() => []), }; - const queue = new RelayPublishQueue(store, null, defaultGetDataID); + const queue = new RelayPublishQueue(store, null, defaultGetDataID, null); queue.applyUpdate({ storeUpdater: storeProxy => { storeProxy.create('4', 'User'); @@ -1817,7 +1817,7 @@ describe('RelayPublishQueue', () => { restore, snapshot: jest.fn(() => []), }; - const queue = new RelayPublishQueue(store, null, defaultGetDataID); + const queue = new RelayPublishQueue(store, null, defaultGetDataID, null); const mutation = { storeUpdater: storeProxy => { storeProxy.create('4', 'User'); @@ -1847,7 +1847,7 @@ describe('RelayPublishQueue', () => { restore: jest.fn(), snapshot: jest.fn(() => []), }; - const queue = new RelayPublishQueue(store, null, defaultGetDataID); + const queue = new RelayPublishQueue(store, null, defaultGetDataID, null); const NameQuery = graphql` query RelayPublishQueueTest10Query { @@ -1882,7 +1882,7 @@ describe('RelayPublishQueue', () => { restore: jest.fn(), snapshot: jest.fn(() => []), }; - const queue = new RelayPublishQueue(store, null, defaultGetDataID); + const queue = new RelayPublishQueue(store, null, defaultGetDataID, null); const mutation = { storeUpdater: storeProxy => { storeProxy.create('4', 'User'); @@ -1904,7 +1904,7 @@ describe('RelayPublishQueue', () => { restore: jest.fn(), snapshot: jest.fn(() => []), }; - const queue = new RelayPublishQueue(store, null, defaultGetDataID); + const queue = new RelayPublishQueue(store, null, defaultGetDataID, null); expectWarningWillFire( 'RelayPublishQueue.run was called, but the call would have been a noop.', ); @@ -1926,7 +1926,7 @@ describe('RelayPublishQueue', () => { restore: jest.fn(), snapshot: jest.fn(() => []), }; - const queue = new RelayPublishQueue(store, null, defaultGetDataID); + const queue = new RelayPublishQueue(store, null, defaultGetDataID, null); const mutation = { storeUpdater: storeProxy => { storeProxy.create('4', 'User'); @@ -1953,7 +1953,7 @@ describe('RelayPublishQueue', () => { it('should warn if run() is called during a run()', () => { const source = new RelayRecordSource(); const store = new RelayModernStore(source); - const queue = new RelayPublishQueue(store, null, defaultGetDataID); + const queue = new RelayPublishQueue(store, null, defaultGetDataID, null); let runInUpdaterOnce = false; queue.applyUpdate({ storeUpdater: storeProxy => { diff --git a/packages/relay-runtime/store/__tests__/RelayReader-AliasedFragments-test.js b/packages/relay-runtime/store/__tests__/RelayReader-AliasedFragments-test.js index 6395068654d58..a8deb79f329e0 100644 --- a/packages/relay-runtime/store/__tests__/RelayReader-AliasedFragments-test.js +++ b/packages/relay-runtime/store/__tests__/RelayReader-AliasedFragments-test.js @@ -906,8 +906,8 @@ describe('Inline Fragments', () => { `; const operation = createOperationDescriptor(FooQuery, {id: '1'}); const snapshot = read(source, operation.fragment); - const {data, isMissingData, errorResponseFields} = snapshot; - expect(errorResponseFields).toBe(null); + const {data, isMissingData, fieldErrors} = snapshot; + expect(fieldErrors).toBe(null); expect(isMissingData).toBe(false); expect(data).toEqual({ node: { @@ -1035,11 +1035,8 @@ describe('Inline Fragments', () => { } `; const operation = createOperationDescriptor(FooQuery, {id: '1'}); - const {data, isMissingData, errorResponseFields} = read( - source, - operation.fragment, - ); - expect(errorResponseFields).toEqual([ + const {data, isMissingData, fieldErrors} = read(source, operation.fragment); + expect(fieldErrors).toEqual([ { fieldPath: 'node.aliased_fragment.name', kind: 'missing_expected_data.log', @@ -1095,11 +1092,8 @@ describe('Inline Fragments', () => { } `; const operation = createOperationDescriptor(FooQuery, {id: '1'}); - const {data, isMissingData, errorResponseFields} = read( - source, - operation.fragment, - ); - expect(errorResponseFields).toEqual([ + const {data, isMissingData, fieldErrors} = read(source, operation.fragment); + expect(fieldErrors).toEqual([ { fieldPath: 'node.aliased_fragment.', kind: 'missing_expected_data.log', diff --git a/packages/relay-runtime/store/__tests__/RelayReader-CatchFields-test.js b/packages/relay-runtime/store/__tests__/RelayReader-CatchFields-test.js index 2ec131d0ccde0..1e01dbc783e26 100644 --- a/packages/relay-runtime/store/__tests__/RelayReader-CatchFields-test.js +++ b/packages/relay-runtime/store/__tests__/RelayReader-CatchFields-test.js @@ -41,8 +41,8 @@ describe('RelayReader @catch', () => { } `; const operation = createOperationDescriptor(FooQuery, {id: '1'}); - const {data, errorResponseFields} = read(source, operation.fragment); - expect(errorResponseFields).toEqual(null); + const {data, fieldErrors} = read(source, operation.fragment); + expect(fieldErrors).toEqual(null); expect(data).toEqual({me: {lastName: null}}); }); @@ -77,7 +77,7 @@ describe('RelayReader @catch', () => { } `; const operation = createOperationDescriptor(FooQuery, {id: '1'}); - const {data, errorResponseFields} = read(source, operation.fragment); + const {data, fieldErrors} = read(source, operation.fragment); expect(data).toEqual({ me: { lastName: { @@ -91,7 +91,7 @@ describe('RelayReader @catch', () => { }, }); - expect(errorResponseFields).toEqual([ + expect(fieldErrors).toEqual([ { error: {message: 'There was an error!', path: ['me', 'lastName']}, fieldPath: 'me.lastName', @@ -136,7 +136,7 @@ describe('RelayReader @catch', () => { } `; const operation = createOperationDescriptor(FooQuery, {id: '1'}); - const {data, errorResponseFields} = read(source, operation.fragment); + const {data, fieldErrors} = read(source, operation.fragment); expect(data).toEqual({ me: { lastName: null, @@ -147,7 +147,7 @@ describe('RelayReader @catch', () => { }, }); - expect(errorResponseFields).toEqual([ + expect(fieldErrors).toEqual([ { fieldPath: 'me.lastName', error: { @@ -191,7 +191,7 @@ describe('RelayReader @catch', () => { } `; const operation = createOperationDescriptor(FooQuery, {id: '1'}); - const {data, errorResponseFields} = read(source, operation.fragment); + const {data, fieldErrors} = read(source, operation.fragment); expect(data).toEqual({ alsoMe: null, me: { @@ -205,7 +205,7 @@ describe('RelayReader @catch', () => { }, }); - expect(errorResponseFields).toEqual([ + expect(fieldErrors).toEqual([ { kind: 'missing_required_field.log', fieldPath: 'alsoMe.lastName', @@ -243,12 +243,12 @@ describe('RelayReader @catch', () => { } `; const operation = createOperationDescriptor(FooQuery, {id: '1'}); - const {data, errorResponseFields} = read(source, operation.fragment); + const {data, fieldErrors} = read(source, operation.fragment); expect(data).toEqual({ me: null, }); - expect(errorResponseFields).toEqual([ + expect(fieldErrors).toEqual([ { fieldPath: 'me.firstName', handled: true, @@ -281,17 +281,14 @@ describe('RelayReader @catch', () => { } `; const operation = createOperationDescriptor(FooQuery, {id: '1'}); - const {data, errorResponseFields, isMissingData} = read( - source, - operation.fragment, - ); + const {data, fieldErrors, isMissingData} = read(source, operation.fragment); expect(data).toEqual({me: null}); // We still need to ensure that we will suspend if there is a request in flight. expect(isMissingData).toEqual(true); - expect(errorResponseFields).toEqual([ + expect(fieldErrors).toEqual([ { fieldPath: 'me.firstName', kind: 'missing_expected_data.log', @@ -324,17 +321,14 @@ describe('RelayReader @catch', () => { } `; const operation = createOperationDescriptor(FooQuery, {}); - const {data, errorResponseFields, isMissingData} = read( - source, - operation.fragment, - ); + const {data, fieldErrors, isMissingData} = read(source, operation.fragment); expect(data).toEqual(null); // We still need to ensure that we will suspend if there is a request in flight. expect(isMissingData).toEqual(true); - expect(errorResponseFields).toEqual([ + expect(fieldErrors).toEqual([ { fieldPath: 'me.firstName', kind: 'missing_expected_data.log', @@ -367,17 +361,14 @@ describe('RelayReader @catch', () => { } `; const operation = createOperationDescriptor(FooQuery, {}); - const {data, errorResponseFields, isMissingData} = read( - source, - operation.fragment, - ); + const {data, fieldErrors, isMissingData} = read(source, operation.fragment); expect(data).toEqual({errors: [{path: ['me', 'firstName']}], ok: false}); // We still need to ensure that we will suspend if there is a request in flight. expect(isMissingData).toEqual(true); - expect(errorResponseFields).toEqual([ + expect(fieldErrors).toEqual([ { fieldPath: 'me.firstName', kind: 'missing_expected_data.log', @@ -417,7 +408,7 @@ describe('RelayReader @catch', () => { `; const owner = createOperationDescriptor(FooQuery, {}); - const {data, errorResponseFields, isMissingData} = read( + const {data, fieldErrors, isMissingData} = read( source, createReaderSelector(FooFragment, 'client:root', {}, owner.request), ); @@ -427,7 +418,7 @@ describe('RelayReader @catch', () => { // We still need to ensure that we will suspend if there is a request in flight. expect(isMissingData).toEqual(true); - expect(errorResponseFields).toEqual([ + expect(fieldErrors).toEqual([ { fieldPath: 'me.firstName', kind: 'missing_expected_data.log', @@ -465,7 +456,7 @@ describe('RelayReader @catch', () => { `; const owner = createOperationDescriptor(FooQuery, {}); - const {data, errorResponseFields, isMissingData} = read( + const {data, fieldErrors, isMissingData} = read( source, createReaderSelector(FooFragment, 'client:root', {}, owner.request), ); @@ -475,7 +466,7 @@ describe('RelayReader @catch', () => { // We still need to ensure that we will suspend if there is a request in flight. expect(isMissingData).toEqual(true); - expect(errorResponseFields).toEqual([ + expect(fieldErrors).toEqual([ { fieldPath: 'me.firstName', kind: 'missing_expected_data.log', @@ -509,17 +500,14 @@ describe('RelayReader @catch', () => { } `; const operation = createOperationDescriptor(FooQuery, {}); - const {data, errorResponseFields, isMissingData} = read( - source, - operation.fragment, - ); + const {data, fieldErrors, isMissingData} = read(source, operation.fragment); expect(data).toEqual({me: {myAlias: null}}); // We still need to ensure that we will suspend if there is a request in flight. expect(isMissingData).toEqual(true); - expect(errorResponseFields).toEqual([ + expect(fieldErrors).toEqual([ { fieldPath: 'me.myAlias.firstName', kind: 'missing_expected_data.log', @@ -554,10 +542,7 @@ describe('RelayReader @catch', () => { } `; const operation = createOperationDescriptor(FooQuery, {}); - const {data, errorResponseFields, isMissingData} = read( - source, - operation.fragment, - ); + const {data, fieldErrors, isMissingData} = read(source, operation.fragment); expect(data).toEqual({ me: {myAlias: {ok: false, errors: [{path: ['myAlias', 'firstName']}]}}, @@ -566,7 +551,7 @@ describe('RelayReader @catch', () => { // We still need to ensure that we will suspend if there is a request in flight. expect(isMissingData).toEqual(true); - expect(errorResponseFields).toEqual([ + expect(fieldErrors).toEqual([ { fieldPath: 'me.myAlias.firstName', kind: 'missing_expected_data.log', @@ -599,7 +584,7 @@ describe('RelayReader @catch', () => { } `; const operation = createOperationDescriptor(FooQuery, {id: '1'}); - const {data, errorResponseFields} = read(source, operation.fragment); + const {data, fieldErrors} = read(source, operation.fragment); expect(data).toEqual({ me: { lastName: { @@ -609,7 +594,7 @@ describe('RelayReader @catch', () => { }, }); - expect(errorResponseFields).toBeNull(); + expect(fieldErrors).toBeNull(); }); it('if linked has catch to RESULT - but no error, response should reflect', () => { @@ -635,7 +620,7 @@ describe('RelayReader @catch', () => { } `; const operation = createOperationDescriptor(FooQuery, {id: '1'}); - const {data, errorResponseFields} = read(source, operation.fragment); + const {data, fieldErrors} = read(source, operation.fragment); expect(data).toEqual({ me: { ok: true, @@ -645,7 +630,7 @@ describe('RelayReader @catch', () => { }, }); - expect(errorResponseFields).toBeNull(); + expect(fieldErrors).toBeNull(); }); it('if linked has catch to RESULT - with error, response should reflect', () => { @@ -679,7 +664,7 @@ describe('RelayReader @catch', () => { } `; const operation = createOperationDescriptor(FooQuery, {id: '1'}); - const {data, errorResponseFields} = read(source, operation.fragment); + const {data, fieldErrors} = read(source, operation.fragment); expect(data).toEqual({ me: { ok: false, @@ -691,7 +676,7 @@ describe('RelayReader @catch', () => { }, }); - expect(errorResponseFields).toEqual([ + expect(fieldErrors).toEqual([ { error: {message: 'There was an error!', path: ['me', 'lastName']}, fieldPath: 'me.lastName', @@ -726,7 +711,7 @@ describe('RelayReader @catch', () => { } `; const operation = createOperationDescriptor(FooQuery, {id: '1'}); - const {data, errorResponseFields} = read(source, operation.fragment); + const {data, fieldErrors} = read(source, operation.fragment); expect(data).toEqual({ me: { errors: [ @@ -739,7 +724,7 @@ describe('RelayReader @catch', () => { }, }); - expect(errorResponseFields).toEqual([ + expect(fieldErrors).toEqual([ { fieldPath: 'me.lastName', handled: true, diff --git a/packages/relay-runtime/store/__tests__/RelayReader-ClientEdges-test.js b/packages/relay-runtime/store/__tests__/RelayReader-ClientEdges-test.js index c2fb5da5206f6..e993ab8f8e882 100644 --- a/packages/relay-runtime/store/__tests__/RelayReader-ClientEdges-test.js +++ b/packages/relay-runtime/store/__tests__/RelayReader-ClientEdges-test.js @@ -25,6 +25,9 @@ const { const RelayStore = require('relay-runtime/store/RelayModernStore'); const {read} = require('relay-runtime/store/RelayReader'); const RelayRecordSource = require('relay-runtime/store/RelayRecordSource'); +const { + RELAY_READ_TIME_RESOLVER_KEY_PREFIX, +} = require('relay-runtime/store/RelayStoreUtils'); const { disallowConsoleErrors, disallowWarnings, @@ -154,7 +157,7 @@ describe('RelayReader Client Edges behavior', () => { expect(Array.from(seenRecords).sort()).toEqual([ '1', '1337', - 'client:1:client_edge', + `client:1:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}client_edge`, 'client:root', ]); expect(missingClientEdges?.length ?? 0).toEqual(0); @@ -189,7 +192,7 @@ describe('RelayReader Client Edges behavior', () => { expect(Array.from(seenRecords).sort()).toEqual([ '1', '1337', - 'client:1:client_edge', + `client:1:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}client_edge`, 'client:root', ]); expect(missingClientEdges?.length).toEqual(1); @@ -289,7 +292,7 @@ describe('RelayReader Client Edges behavior', () => { expect(Array.from(seenRecords).sort()).toEqual([ '1', '1337', - 'client:1:client_edge', + `client:1:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}client_edge`, 'client:root', ]); expect(missingClientEdges?.length ?? 0).toEqual(0); @@ -323,7 +326,7 @@ describe('RelayReader Client Edges behavior', () => { expect(me?.null_client_edge).toBe(null); expect(Array.from(seenRecords).sort()).toEqual([ '1', - 'client:1:null_client_edge', + `client:1:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}null_client_edge`, 'client:root', ]); expect(missingClientEdges?.length ?? 0).toEqual(0); @@ -366,7 +369,7 @@ describe('RelayReader Client Edges behavior', () => { '1', '1337', '1338', - 'client:1:client_edge', + `client:1:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}client_edge`, 'client:root', ]); expect(missingClientEdges?.length).toEqual(1); @@ -451,8 +454,8 @@ describe('RelayReader Client Edges behavior', () => { '1', '1337', '1338', - 'client:1337:another_client_edge', - 'client:1:client_edge', + `client:1337:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}another_client_edge`, + `client:1:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}client_edge`, 'client:root', ]); expect(missingClientEdges?.length).toEqual(1); @@ -504,7 +507,7 @@ describe('RelayReader Client Edges behavior', () => { '1', '1337', '1338', - 'client:1338:client_edge', + `client:1338:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}client_edge`, 'client:root', ]); expect(missingClientEdges?.length).toEqual(1); diff --git a/packages/relay-runtime/store/__tests__/RelayReader-ExecResolvers-test.js b/packages/relay-runtime/store/__tests__/RelayReader-ExecResolvers-test.js index fee0843462c67..d7881e3d0e590 100644 --- a/packages/relay-runtime/store/__tests__/RelayReader-ExecResolvers-test.js +++ b/packages/relay-runtime/store/__tests__/RelayReader-ExecResolvers-test.js @@ -36,7 +36,12 @@ export type TReaderTestUser = { name: ?string, }; -const modelMock = jest.fn(); +let modelMock; +let nameMock; +let bestFriendMock; +let friendsMock; +let user_oneMock; + /** * @RelayResolver RelayReaderExecResolversTestUser */ @@ -47,7 +52,6 @@ export function RelayReaderExecResolversTestUser(id: DataID): TReaderTestUser { }; } -const nameMock = jest.fn(); /** * @RelayResolver RelayReaderExecResolversTestUser.name: String */ @@ -56,7 +60,6 @@ export function name(user: TReaderTestUser): ?string { return user.name; } -const bestFriendMock = jest.fn(); /** * @RelayResolver RelayReaderExecResolversTestUser.best_friend: RelayReaderExecResolversTestUser */ @@ -67,7 +70,6 @@ export function best_friend( return {id: '2'}; } -const friendsMock = jest.fn(); /** * @RelayResolver RelayReaderExecResolversTestUser.friends: [RelayReaderExecResolversTestUser] */ @@ -78,7 +80,6 @@ export function friends( return [{id: '2'}, {id: '3'}, {id: '4'}]; } -const user_oneMock = jest.fn(); /** * @RelayResolver Query.RelayReaderExecResolversTest_user_one: RelayReaderExecResolversTestUser */ @@ -87,6 +88,14 @@ export function RelayReaderExecResolversTest_user_one(): IdOf<'RelayReaderExecRe return {id: '1'}; } +beforeEach(() => { + modelMock = jest.fn(); + nameMock = jest.fn(); + bestFriendMock = jest.fn(); + friendsMock = jest.fn(); + user_oneMock = jest.fn(); +}); + /** * Note that the reading of exec time resolvers is expected to be the same as * the reading of standard server queries. The main purpose of testing is to ensure @@ -94,9 +103,12 @@ export function RelayReaderExecResolversTest_user_one(): IdOf<'RelayReaderExecRe * not as read time resolver queries. */ describe('RelayReaderExecResolvers', () => { - it('reads exec_time_resolvers without calling the resolvers', () => { + it('reads exec_time_resolvers without calling the resolvers when provider returns true', () => { const Query = graphql` - query RelayReaderExecResolversTestRunsQuery @exec_time_resolvers { + query RelayReaderExecResolversTestQuery + @exec_time_resolvers( + enabledProvider: "relayReaderTestExecTimeResolversTrueProvider" + ) { RelayReaderExecResolversTest_user_one { name best_friend { @@ -151,4 +163,139 @@ describe('RelayReaderExecResolvers', () => { }, }); }); + + it('reads read time resolvers when exec time resolvers provider returns false', () => { + const Query = graphql` + query RelayReaderExecResolversTestFalseProviderQuery + @exec_time_resolvers( + enabledProvider: "relayReaderTestExecTimeResolversFalseProvider" + ) { + RelayReaderExecResolversTest_user_one { + name + best_friend { + name + } + friends { + name + } + } + } + `; + const operation = createOperationDescriptor(Query, {}); + const source = new RelayRecordSource({ + 'client:root': { + __id: 'client:root', + __typename: '__Root', + RelayReaderExecResolversTest_user_one: {__ref: '1'}, + }, + '1': { + __id: '1', + name: 'Alice', + friends: {__refs: ['2', '3', '4']}, + }, + '2': { + __id: '2', + name: 'Bob', + }, + '3': { + __id: '3', + name: 'Claire', + }, + '4': { + __id: '4', + name: 'Dennis', + }, + }); + const resolverStore = new RelayModernStore(source); + const {data} = read( + source, + operation.fragment, + new LiveResolverCache(() => source, resolverStore), + ); + + expect(modelMock).toBeCalled(); + expect(nameMock).toBeCalled(); + expect(user_oneMock).toBeCalled(); + expect(friendsMock).toBeCalled(); + expect(data).toEqual({ + RelayReaderExecResolversTest_user_one: { + best_friend: { + name: 'Bob', + }, + friends: [ + { + name: 'Bob', + }, + { + name: 'Claire', + }, + { + name: 'Dennis', + }, + ], + name: 'Alice', + }, + }); + }); + + it('reads exec time resolvers data correctly when client side directives are present, like @required', () => { + const Query = graphql` + query RelayReaderExecResolversTestClientDirectiveQuery + @exec_time_resolvers( + enabledProvider: "relayReaderTestExecTimeResolversTrueProvider" + ) { + RelayReaderExecResolversTest_user_one { + name @required(action: THROW) + best_friend { + name + } + friends { + name + } + } + } + `; + const operation = createOperationDescriptor(Query, {}); + const source = new RelayRecordSource({ + 'client:root': { + __id: 'client:root', + __typename: '__Root', + RelayReaderExecResolversTest_user_one: {__ref: '1'}, + }, + '1': { + __id: '1', + name: 'Alice', + friends: {__refs: ['2', '3', '4']}, + }, + '2': { + __id: '2', + name: 'Bob', + }, + '3': { + __id: '3', + name: 'Claire', + }, + '4': { + __id: '4', + name: 'Dennis', + }, + }); + const resolverStore = new RelayModernStore(source); + const {data} = read( + source, + operation.fragment, + new LiveResolverCache(() => source, resolverStore), + ); + + expect(modelMock).not.toBeCalled(); + expect(nameMock).not.toBeCalled(); + expect(user_oneMock).not.toBeCalled(); + expect(friendsMock).not.toBeCalled(); + expect(data).toEqual({ + RelayReaderExecResolversTest_user_one: { + name: 'Alice', + friends: [{name: 'Bob'}, {name: 'Claire'}, {name: 'Dennis'}], + }, + }); + }); }); diff --git a/packages/relay-runtime/store/__tests__/RelayReader-RelayErrorHandling-test.js b/packages/relay-runtime/store/__tests__/RelayReader-RelayErrorHandling-test.js index c6431d06b7827..49ce8682c544c 100644 --- a/packages/relay-runtime/store/__tests__/RelayReader-RelayErrorHandling-test.js +++ b/packages/relay-runtime/store/__tests__/RelayReader-RelayErrorHandling-test.js @@ -11,6 +11,7 @@ 'use strict'; const {graphql} = require('../../query/GraphQLTag'); +const RelayFeatureFlags = require('../../util/RelayFeatureFlags'); const { createOperationDescriptor, } = require('../RelayModernOperationDescriptor'); @@ -19,7 +20,7 @@ const {read} = require('../RelayReader'); const RelayRecordSource = require('../RelayRecordSource'); describe('RelayReader error fields', () => { - it('adds the errors to errorResponseFields', () => { + it('adds the errors to fieldErrors', () => { const source = RelayRecordSource.create({ 'client:root': { __id: 'client:root', @@ -50,9 +51,9 @@ describe('RelayReader error fields', () => { } `; const operation = createOperationDescriptor(FooQuery, {id: '1'}); - const {data, errorResponseFields} = read(source, operation.fragment); + const {data, fieldErrors} = read(source, operation.fragment); expect(data).toEqual({me: {lastName: null}}); - expect(errorResponseFields).toEqual([ + expect(fieldErrors).toEqual([ { owner: 'RelayReaderRelayErrorHandlingTest1Query', fieldPath: 'me.lastName', @@ -67,7 +68,7 @@ describe('RelayReader error fields', () => { ]); }); - it('adds the errors to errorResponseFields including missingData - without @catch', () => { + it('adds the errors to fieldErrors including missingData - without @catch', () => { const source = RelayRecordSource.create({ 'client:root': { __id: 'client:root', @@ -102,9 +103,9 @@ describe('RelayReader error fields', () => { } `; const operation = createOperationDescriptor(FooQuery, {size: 42}); - const {errorResponseFields} = read(source, operation.fragment); + const {fieldErrors} = read(source, operation.fragment); - expect(errorResponseFields).toEqual([ + expect(fieldErrors).toEqual([ { owner: 'RelayReaderRelayErrorHandlingTest4Query', fieldPath: 'me.lastName', @@ -125,7 +126,7 @@ describe('RelayReader error fields', () => { ]); }); - it('adds the errors to errorResponseFields including missingData within plural fields - without @catch', () => { + it('adds the errors to fieldErrors including missingData within plural fields - without @catch', () => { const source = RelayRecordSource.create({ 'client:root': { __id: 'client:root', @@ -160,9 +161,9 @@ describe('RelayReader error fields', () => { } `; const operation = createOperationDescriptor(FooQuery, {size: 42}); - const {errorResponseFields} = read(source, operation.fragment); + const {fieldErrors} = read(source, operation.fragment); - expect(errorResponseFields).toEqual([ + expect(fieldErrors).toEqual([ { owner: 'RelayReaderRelayErrorHandlingTestMissingPluralQuery', fieldPath: 'me.lastName', @@ -183,7 +184,7 @@ describe('RelayReader error fields', () => { ]); }); - it('adds the errors to errorResponseFields including missingData - with @catch', () => { + it('adds the errors to fieldErrors including missingData - with @catch', () => { const source = RelayRecordSource.create({ 'client:root': { __id: 'client:root', @@ -217,7 +218,7 @@ describe('RelayReader error fields', () => { } `; const operation = createOperationDescriptor(FooQuery, {size: 42}); - const {data, errorResponseFields} = read(source, operation.fragment); + const {data, fieldErrors} = read(source, operation.fragment); // we have a task out for adding path to missingData. Meantime that array is empty. expect(data).toEqual({ @@ -234,7 +235,7 @@ describe('RelayReader error fields', () => { }, }); - expect(errorResponseFields).toEqual([ + expect(fieldErrors).toEqual([ { error: {message: 'There was an error!', path: ['me', 'lastName']}, fieldPath: 'me.lastName', @@ -282,7 +283,7 @@ describe('RelayReader error fields', () => { `; const operation = createOperationDescriptor(FooQuery, {size: 42}); - const {data, errorResponseFields} = read(source, operation.fragment); + const {data, fieldErrors} = read(source, operation.fragment); expect(data).toEqual({ me: { @@ -290,7 +291,7 @@ describe('RelayReader error fields', () => { }, }); - expect(errorResponseFields).toEqual([ + expect(fieldErrors).toEqual([ { error: {message: 'There was an error!', path: ['me', 'lastName']}, fieldPath: 'me.lastName', @@ -336,9 +337,9 @@ describe('RelayReader error fields', () => { `; const operation = createOperationDescriptor(FooQuery, {size: 42}); - const {errorResponseFields} = read(source, operation.fragment); + const {fieldErrors} = read(source, operation.fragment); - expect(errorResponseFields).toEqual([ + expect(fieldErrors).toEqual([ { error: {message: 'There was an error!', path: ['me', 'lastName']}, fieldPath: 'me.lastName', @@ -385,9 +386,9 @@ describe('RelayReader error fields', () => { `; const operation = createOperationDescriptor(FooQuery, {size: 42}); - const {errorResponseFields} = read(source, operation.fragment); + const {fieldErrors} = read(source, operation.fragment); - expect(errorResponseFields).toEqual([ + expect(fieldErrors).toEqual([ { fieldPath: 'me.client_edge.firstName', kind: 'missing_expected_data.throw', @@ -427,20 +428,21 @@ describe('RelayReader error fields', () => { @throwOnFieldError { me { astrological_sign { - notes # Not in the store! + # Compiler forces us to use @catch here + notes @catch(to: NULL) # Not in the store! } } } `; const operation = createOperationDescriptor(FooQuery, {}); - const {errorResponseFields} = store.lookup(operation.fragment); + const {fieldErrors} = store.lookup(operation.fragment); - expect(errorResponseFields).toEqual([ + expect(fieldErrors).toEqual([ { fieldPath: 'me.astrological_sign.notes', kind: 'missing_expected_data.throw', - handled: false, + handled: true, owner: 'RelayReaderRelayErrorHandlingTestResolverClientEdgeClientObjectWithMissingDataQuery', }, @@ -465,17 +467,18 @@ describe('RelayReader error fields', () => { query RelayReaderRelayErrorHandlingTestResolverClientPluralEdgeClientObjectWithMissingDataQuery @throwOnFieldError { all_astrological_signs { - notes # Not in the store! + # Compiler forces us to use @catch here + notes @catch(to: NULL) # Not in the store! } } `; const operation = createOperationDescriptor(FooQuery, {}); - const {errorResponseFields} = store.lookup(operation.fragment); - for (let i = 0; i < errorResponseFields.length; i++) { - expect(errorResponseFields[i]).toEqual({ + const {fieldErrors} = store.lookup(operation.fragment); + for (let i = 0; i < fieldErrors.length; i++) { + expect(fieldErrors[i]).toEqual({ fieldPath: `all_astrological_signs.${i}.notes`, kind: 'missing_expected_data.throw', - handled: false, + handled: true, owner: 'RelayReaderRelayErrorHandlingTestResolverClientPluralEdgeClientObjectWithMissingDataQuery', }); @@ -512,13 +515,10 @@ describe('RelayReader error fields', () => { } `; const operation = createOperationDescriptor(FooQuery, {}); - const {errorResponseFields, isMissingData} = read( - source, - operation.fragment, - ); + const {fieldErrors, isMissingData} = read(source, operation.fragment); expect(isMissingData).toBe(false); - expect(errorResponseFields).toEqual(null); + expect(fieldErrors).toEqual(null); }); it('does report missing data within an inline fragment that does match', () => { @@ -552,13 +552,10 @@ describe('RelayReader error fields', () => { } `; const operation = createOperationDescriptor(FooQuery, {}); - const {errorResponseFields, isMissingData} = read( - source, - operation.fragment, - ); + const {fieldErrors, isMissingData} = read(source, operation.fragment); expect(isMissingData).toBe(true); - expect(errorResponseFields).toEqual([ + expect(fieldErrors).toEqual([ // We are missing the metadata bout the interface { fieldPath: 'node.', @@ -608,13 +605,10 @@ describe('RelayReader error fields', () => { } `; const operation = createOperationDescriptor(FooQuery, {}); - const {errorResponseFields, isMissingData} = read( - source, - operation.fragment, - ); + const {fieldErrors, isMissingData} = read(source, operation.fragment); expect(isMissingData).toBe(true); - expect(errorResponseFields).toEqual([ + expect(fieldErrors).toEqual([ { fieldPath: 'also_me.name', handled: false, @@ -635,4 +629,251 @@ describe('RelayReader error fields', () => { }, ]); }); + + let wasNoncompliantErrorHandlingOnListsEnabled; + + beforeEach(() => { + wasNoncompliantErrorHandlingOnListsEnabled = + RelayFeatureFlags.ENABLE_NONCOMPLIANT_ERROR_HANDLING_ON_LISTS; + }); + + afterEach(() => { + RelayFeatureFlags.ENABLE_NONCOMPLIANT_ERROR_HANDLING_ON_LISTS = + wasNoncompliantErrorHandlingOnListsEnabled; + }); + + describe('when noncompliant error handling on lists is enabled', () => { + beforeEach(() => { + RelayFeatureFlags.ENABLE_NONCOMPLIANT_ERROR_HANDLING_ON_LISTS = true; + }); + + describe('when query has @throwOnFieldError directive', () => { + it('has errors that will throw when the linked field is an empty list', () => { + const source = RelayRecordSource.create({ + '1': { + __id: '1', + __typename: 'User', + 'friends(first:3)': { + __ref: 'client:1:friends(first:3)', + }, + id: '1', + }, + 'client:1:friends(first:3)': { + __id: 'client:1:friends(first:3)', + __typename: 'FriendsConnection', + __errors: { + edges: [ + { + message: 'There was an error!', + }, + ], + }, + edges: { + __refs: [], + }, + }, + 'client:root': { + __id: 'client:root', + __typename: '__Root', + 'node(id:"1")': {__ref: '1'}, + }, + }); + + const FooQuery = graphql` + query RelayReaderRelayErrorHandlingTestNoncompliantEmptyLinkedFieldWithThrowOnFieldErrorQuery + @throwOnFieldError { + node(id: "1") { + id + __typename + ... on User { + friends(first: 3) { + edges { + cursor + } + } + } + } + } + `; + const operation = createOperationDescriptor(FooQuery, {}); + const {fieldErrors} = read(source, operation.fragment); + + expect(fieldErrors).toEqual([ + { + fieldPath: '', + handled: false, + error: {message: 'There was an error!'}, + kind: 'relay_field_payload.error', + owner: + 'RelayReaderRelayErrorHandlingTestNoncompliantEmptyLinkedFieldWithThrowOnFieldErrorQuery', + shouldThrow: true, + }, + ]); + }); + + it('has errors that will throw when the scalar field is an empty list', () => { + const source = RelayRecordSource.create({ + '1': { + __id: '1', + __typename: 'User', + __errors: { + emailAddresses: [ + { + message: 'There was an error!', + }, + ], + }, + id: '1', + emailAddresses: [], + }, + 'client:root': { + __id: 'client:root', + __typename: '__Root', + 'node(id:"1")': {__ref: '1'}, + }, + }); + + const FooQuery = graphql` + query RelayReaderRelayErrorHandlingTestNoncompliantEmptyScalarFieldWithThrowOnFieldErrorQuery + @throwOnFieldError { + node(id: "1") { + id + __typename + ... on User { + emailAddresses + } + } + } + `; + const operation = createOperationDescriptor(FooQuery, {}); + const {fieldErrors} = read(source, operation.fragment); + + expect(fieldErrors).toEqual([ + { + fieldPath: '', + handled: false, + error: {message: 'There was an error!'}, + kind: 'relay_field_payload.error', + owner: + 'RelayReaderRelayErrorHandlingTestNoncompliantEmptyScalarFieldWithThrowOnFieldErrorQuery', + shouldThrow: true, + }, + ]); + }); + }); + + describe('when query does not have the @throwOnFieldError directive', () => { + it('has errors that wont throw when the linked field is an empty list', () => { + const source = RelayRecordSource.create({ + '1': { + __id: '1', + __typename: 'User', + 'friends(first:3)': { + __ref: 'client:1:friends(first:3)', + }, + id: '1', + }, + 'client:1:friends(first:3)': { + __id: 'client:1:friends(first:3)', + __typename: 'FriendsConnection', + __errors: { + edges: [ + { + message: 'There was an error!', + }, + ], + }, + edges: { + __refs: [], + }, + }, + 'client:root': { + __id: 'client:root', + __typename: '__Root', + 'node(id:"1")': {__ref: '1'}, + }, + }); + + const FooQuery = graphql` + query RelayReaderRelayErrorHandlingTestNoncompliantEmptyLinkedFieldWithoutThrowOnFieldErrorQuery { + node(id: "1") { + id + __typename + ... on User { + friends(first: 3) { + edges { + cursor + } + } + } + } + } + `; + const operation = createOperationDescriptor(FooQuery, {}); + const {data, fieldErrors} = read(source, operation.fragment); + + expect(data.node.friends.edges).toEqual([]); + expect(fieldErrors).toEqual([ + { + fieldPath: '', + handled: false, + error: {message: 'There was an error!'}, + kind: 'relay_field_payload.error', + owner: + 'RelayReaderRelayErrorHandlingTestNoncompliantEmptyLinkedFieldWithoutThrowOnFieldErrorQuery', + shouldThrow: false, + }, + ]); + }); + + it('has errors that wont throw when the scalar field is an empty list', () => { + const source = RelayRecordSource.create({ + '1': { + __id: '1', + __typename: 'User', + __errors: { + emailAddresses: [ + { + message: 'There was an error!', + }, + ], + }, + id: '1', + emailAddresses: [], + }, + 'client:root': { + __id: 'client:root', + __typename: '__Root', + 'node(id:"1")': {__ref: '1'}, + }, + }); + + const FooQuery = graphql` + query RelayReaderRelayErrorHandlingTestNoncompliantEmptyScalarFieldWithoutThrowOnFieldErrorQuery { + node(id: "1") { + id + __typename + ... on User { + emailAddresses + } + } + } + `; + const operation = createOperationDescriptor(FooQuery, {}); + const {data, fieldErrors} = read(source, operation.fragment); + expect(data.node.emailAddresses).toEqual([]); + expect(fieldErrors).toEqual([ + { + fieldPath: '', + handled: false, + error: {message: 'There was an error!'}, + kind: 'relay_field_payload.error', + owner: + 'RelayReaderRelayErrorHandlingTestNoncompliantEmptyScalarFieldWithoutThrowOnFieldErrorQuery', + shouldThrow: false, + }, + ]); + }); + }); + }); }); diff --git a/packages/relay-runtime/store/__tests__/RelayReader-RequiredFields-test.js b/packages/relay-runtime/store/__tests__/RelayReader-RequiredFields-test.js index 2cec55e88c78b..afce9652b2877 100644 --- a/packages/relay-runtime/store/__tests__/RelayReader-RequiredFields-test.js +++ b/packages/relay-runtime/store/__tests__/RelayReader-RequiredFields-test.js @@ -21,6 +21,9 @@ const { LiveResolverCache, } = require('relay-runtime/store/live-resolvers/LiveResolverCache'); const RelayModernStore = require('relay-runtime/store/RelayModernStore'); +const { + RELAY_READ_TIME_RESOLVER_KEY_PREFIX, +} = require('relay-runtime/store/RelayStoreUtils'); describe('RelayReader @required', () => { it('bubbles @required(action: LOG) scalars up to LinkedField', () => { @@ -143,9 +146,9 @@ describe('RelayReader @required', () => { } `; const operation = createOperationDescriptor(FooQuery, {id: '1'}); - const {data, errorResponseFields} = read(source, operation.fragment); + const {data, fieldErrors} = read(source, operation.fragment); expect(data).toEqual(null); - expect(errorResponseFields[0].fieldPath).toBe('me.lastName'); + expect(fieldErrors[0].fieldPath).toBe('me.lastName'); }); it('bubbles @required(action: LOG) scalars up to LinkedField even if subsequent fields are not unexpectedly null', () => { @@ -893,7 +896,7 @@ describe('RelayReader @required', () => { const store = new RelayModernStore(source); const operation = createOperationDescriptor(FooQuery, {}); const resolverCache = new LiveResolverCache(() => source, store); - const {data, errorResponseFields} = read( + const {data, fieldErrors} = read( source, operation.fragment, resolverCache, @@ -910,7 +913,7 @@ describe('RelayReader @required', () => { }, }); // these are "handled" because the field with the required error was caught - expect(errorResponseFields).toEqual([ + expect(fieldErrors).toEqual([ { fieldPath: 'me.client_object', handled: true, @@ -946,12 +949,8 @@ describe('RelayReader @required', () => { const store = new RelayModernStore(source); const operation = createOperationDescriptor(FooQuery, {}); const resolverCache = new LiveResolverCache(() => source, store); - const {errorResponseFields} = read( - source, - operation.fragment, - resolverCache, - ); - expect(errorResponseFields).toEqual([ + const {fieldErrors} = read(source, operation.fragment, resolverCache); + expect(fieldErrors).toEqual([ { fieldPath: 'me.client_object', kind: 'missing_required_field.throw', @@ -992,13 +991,13 @@ describe('RelayReader @required', () => { const store = new RelayModernStore(source); const operation = createOperationDescriptor(FooQuery, {}); const resolverCache = new LiveResolverCache(() => source, store); - const {data, errorResponseFields} = read( + const {data, fieldErrors} = read( source, operation.fragment, resolverCache, ); expect(data).toEqual({me: {astrological_sign: {name: 'Pisces'}}}); - expect(errorResponseFields).toBe(null); + expect(fieldErrors).toBe(null); }); test('does not throw when required plural field is present', () => { @@ -1025,13 +1024,13 @@ describe('RelayReader @required', () => { const store = new RelayModernStore(source); const operation = createOperationDescriptor(FooQuery, {}); const resolverCache = new LiveResolverCache(() => source, store); - const {data, errorResponseFields} = read( + const {data, fieldErrors} = read( source, operation.fragment, resolverCache, ); expect(data.all_astrological_signs.length).toBe(12); - expect(errorResponseFields).toBe(null); + expect(fieldErrors).toBe(null); }); test('does not throw when @live required field is suspended', () => { @@ -1054,9 +1053,9 @@ describe('RelayReader @required', () => { const operation = createOperationDescriptor(FooQuery, {}); const resolverCache = new LiveResolverCache(() => source, store); const snapshot = read(source, operation.fragment, resolverCache); - expect(snapshot.errorResponseFields).toEqual(null); + expect(snapshot.fieldErrors).toEqual(null); expect(snapshot.missingLiveResolverFields).toEqual([ - 'client:root:live_user_resolver_always_suspend', + `client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}live_user_resolver_always_suspend`, ]); }); }); diff --git a/packages/relay-runtime/store/__tests__/RelayReader-Resolver-test.js b/packages/relay-runtime/store/__tests__/RelayReader-Resolver-test.js index 89e69dbeb4b87..8f6faf98f54e4 100644 --- a/packages/relay-runtime/store/__tests__/RelayReader-Resolver-test.js +++ b/packages/relay-runtime/store/__tests__/RelayReader-Resolver-test.js @@ -10,6 +10,7 @@ */ 'use strict'; + import type {Snapshot} from '../RelayStoreTypes'; const { @@ -35,6 +36,7 @@ const RelayStore = require('relay-runtime/store/RelayModernStore'); const {read} = require('relay-runtime/store/RelayReader'); const RelayRecordSource = require('relay-runtime/store/RelayRecordSource'); const { + RELAY_READ_TIME_RESOLVER_KEY_PREFIX, RELAY_RESOLVER_INVALIDATION_KEY, } = require('relay-runtime/store/RelayStoreUtils'); const { @@ -86,7 +88,7 @@ it('returns the result of the resolver function', () => { expect(Array.from(seenRecords).sort()).toEqual([ '1', - 'client:1:greeting', + `client:1:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}greeting`, 'client:root', ]); }); @@ -183,8 +185,8 @@ describe('Relay resolver - Field Error Handling', () => { const operation = createOperationDescriptor(FooQuery, {}); const store = new RelayStore(source, {gcReleaseBufferSize: 0}); - const {errorResponseFields} = store.lookup(operation.fragment); - expect(errorResponseFields).toEqual([ + const {fieldErrors} = store.lookup(operation.fragment); + expect(fieldErrors).toEqual([ { fieldPath: 'me.lastName', kind: 'relay_field_payload.error', @@ -221,8 +223,8 @@ it('propagates @required errors from the resolver up to the reader', () => { const operation = createOperationDescriptor(FooQuery, {}); const store = new RelayStore(source, {gcReleaseBufferSize: 0}); - const {errorResponseFields} = store.lookup(operation.fragment); - expect(errorResponseFields).toEqual([ + const {fieldErrors} = store.lookup(operation.fragment); + expect(fieldErrors).toEqual([ { kind: 'missing_required_field.log', owner: 'UserRequiredNameResolver', @@ -240,7 +242,7 @@ it('propagates @required errors from the resolver up to the reader', () => { // Lookup a second time to ensure that we still report the missing fields when // reading from the cache. - const {errorResponseFields: missingRequiredFieldsTakeTwo} = store.lookup( + const {fieldErrors: missingRequiredFieldsTakeTwo} = store.lookup( operation.fragment, ); @@ -320,8 +322,8 @@ it('merges @required logs from resolver field with parent', () => { const operation = createOperationDescriptor(FooQuery, {}); const store = new RelayStore(source, {gcReleaseBufferSize: 0}); - const {errorResponseFields} = store.lookup(operation.fragment); - expect(errorResponseFields).toEqual([ + const {fieldErrors} = store.lookup(operation.fragment); + expect(fieldErrors).toEqual([ { kind: 'missing_required_field.log', owner: 'UserRequiredNameResolver', @@ -344,7 +346,7 @@ it('merges @required logs from resolver field with parent', () => { // Lookup a second time to ensure that we still report the missing fields when // reading from the cache. - const {errorResponseFields: missingRequiredFieldsTakeTwo} = store.lookup( + const {fieldErrors: missingRequiredFieldsTakeTwo} = store.lookup( operation.fragment, ); @@ -396,10 +398,10 @@ it('propagates @required(action: THROW) errors from the resolver up to the reade const store = new RelayStore(source, {gcReleaseBufferSize: 0}); const beforeCallCount = requiredThrowNameCalls.count; - const {errorResponseFields, data} = store.lookup(operation.fragment); + const {fieldErrors, data} = store.lookup(operation.fragment); expect(data).toEqual({me: {required_throw_name: null}}); expect(requiredThrowNameCalls.count).toBe(beforeCallCount); - expect(errorResponseFields).toEqual([ + expect(fieldErrors).toEqual([ { kind: 'missing_required_field.throw', owner: 'UserRequiredThrowNameResolver', @@ -410,7 +412,7 @@ it('propagates @required(action: THROW) errors from the resolver up to the reade // Lookup a second time to ensure that we still report the missing fields when // reading from the cache. - const {errorResponseFields: missingRequiredFieldsTakeTwo, data: dataTakeTwo} = + const {fieldErrors: missingRequiredFieldsTakeTwo, data: dataTakeTwo} = store.lookup(operation.fragment); expect(dataTakeTwo).toEqual({me: {required_throw_name: null}}); @@ -618,7 +620,7 @@ it.each([true, false])( const resolverCacheRecord = environment .getStore() .getSource() - .get('client:1:constant_dependent'); + .get(`client:1:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}constant_dependent`); invariant(resolverCacheRecord != null, 'Expected a resolver cache record'); const isMaybeInvalid = RelayModernRecord.getValue( @@ -1038,14 +1040,10 @@ it('Returns null and includes errors when the resolver throws', () => { const operation = createOperationDescriptor(FooQuery, {id: '1'}); - const {data, errorResponseFields} = read( - source, - operation.fragment, - resolverCache, - ); + const {data, fieldErrors} = read(source, operation.fragment, resolverCache); expect(data).toEqual({me: {always_throws: null}}); // Resolver result - expect(errorResponseFields).toMatchInlineSnapshot(` + expect(fieldErrors).toMatchInlineSnapshot(` Array [ Object { "error": [Error: I always throw. What did you expect?], @@ -1054,12 +1052,13 @@ it('Returns null and includes errors when the resolver throws', () => { "kind": "relay_resolver.error", "owner": "RelayReaderResolverTest12Query", "shouldThrow": false, + "uiContext": undefined, }, ] `); // Subsequent read should also read the same error/path - const {data: data2, errorResponseFields: relayResolverErrors2} = read( + const {data: data2, fieldErrors: relayResolverErrors2} = read( source, operation.fragment, resolverCache, @@ -1075,6 +1074,7 @@ it('Returns null and includes errors when the resolver throws', () => { "kind": "relay_resolver.error", "owner": "RelayReaderResolverTest12Query", "shouldThrow": false, + "uiContext": undefined, }, ] `); @@ -1106,14 +1106,10 @@ it('Returns null and includes errors when a transitive resolver throws', () => { const operation = createOperationDescriptor(FooQuery, {id: '1'}); - const {data, errorResponseFields} = read( - source, - operation.fragment, - resolverCache, - ); + const {data, fieldErrors} = read(source, operation.fragment, resolverCache); expect(data).toEqual({me: {always_throws_transitively: null}}); // Resolver result - expect(errorResponseFields).toMatchInlineSnapshot(` + expect(fieldErrors).toMatchInlineSnapshot(` Array [ Object { "error": [Error: I always throw. What did you expect?], @@ -1122,12 +1118,13 @@ it('Returns null and includes errors when a transitive resolver throws', () => { "kind": "relay_resolver.error", "owner": "UserAlwaysThrowsTransitivelyResolver", "shouldThrow": false, + "uiContext": undefined, }, ] `); // Subsequent read should also read the same error/path - const {data: data2, errorResponseFields: relayResolverErrors2} = read( + const {data: data2, fieldErrors: relayResolverErrors2} = read( source, operation.fragment, resolverCache, @@ -1143,6 +1140,7 @@ it('Returns null and includes errors when a transitive resolver throws', () => { "kind": "relay_resolver.error", "owner": "UserAlwaysThrowsTransitivelyResolver", "shouldThrow": false, + "uiContext": undefined, }, ] `); @@ -1168,14 +1166,10 @@ it('Catches errors thrown before calling readFragment', () => { const operation = createOperationDescriptor(FooQuery, {}); - const {data, errorResponseFields} = read( - source, - operation.fragment, - resolverCache, - ); + const {data, fieldErrors} = read(source, operation.fragment, resolverCache); expect(data).toEqual({throw_before_read: null}); // Resolver result - expect(errorResponseFields).toMatchInlineSnapshot(` + expect(fieldErrors).toMatchInlineSnapshot(` Array [ Object { "error": [Error: Purposefully throwing before reading to exercise an edge case.], @@ -1184,6 +1178,7 @@ it('Catches errors thrown before calling readFragment', () => { "kind": "relay_resolver.error", "owner": "RelayReaderResolverTest14Query", "shouldThrow": false, + "uiContext": undefined, }, ] `); diff --git a/packages/relay-runtime/store/__tests__/RelayReader-test.js b/packages/relay-runtime/store/__tests__/RelayReader-test.js index 46dbe8bb2d16c..ae269c292e8fc 100644 --- a/packages/relay-runtime/store/__tests__/RelayReader-test.js +++ b/packages/relay-runtime/store/__tests__/RelayReader-test.js @@ -1481,6 +1481,7 @@ describe('RelayReader', () => { viewer { actor { ...RelayReaderTestShouldNotConsiderDataMissingIfTheFragmentTypeDoesNotMatchTheDataUserProfile + @dangerously_unaliased_fixme } } } @@ -1620,6 +1621,7 @@ describe('RelayReader', () => { query RelayReaderTestStreamConnectionUserQuery($id: ID!) { node(id: $id) { ...RelayReaderTestStreamConnectionUserProfile + @dangerously_unaliased_fixme } } `; @@ -1828,7 +1830,7 @@ describe('RelayReader', () => { query RelayReaderTestActorChangeQuery { viewer { actor @fb_actor_change { - ...RelayReaderTestActorChangeFragment + ...RelayReaderTestActorChangeFragment @dangerously_unaliased_fixme } } } diff --git a/packages/relay-runtime/store/__tests__/RelayReferenceMarker-test.js b/packages/relay-runtime/store/__tests__/RelayReferenceMarker-test.js index 24c01f82c19a2..82e9531d7ee2e 100644 --- a/packages/relay-runtime/store/__tests__/RelayReferenceMarker-test.js +++ b/packages/relay-runtime/store/__tests__/RelayReferenceMarker-test.js @@ -11,6 +11,7 @@ 'use strict'; +import type {RecordSourceJSON} from '../RelayStoreTypes'; import type {DataID} from 'relay-runtime/util/RelayRuntimeTypes'; import RelayNetwork from '../../network/RelayNetwork'; @@ -21,13 +22,13 @@ import {createNormalizationSelector} from '../RelayModernSelector'; import RelayModernStore from '../RelayModernStore'; import RelayRecordSource from '../RelayRecordSource'; import {mark} from '../RelayReferenceMarker'; -import {ROOT_ID} from '../RelayStoreUtils'; +import {RELAY_READ_TIME_RESOLVER_KEY_PREFIX, ROOT_ID} from '../RelayStoreUtils'; describe('RelayReferenceMarker', () => { let source; beforeEach(() => { - const data = { + const data: RecordSourceJSON = { '1': { __id: '1', id: '1', @@ -93,7 +94,9 @@ describe('RelayReferenceMarker', () => { name } } - ...RelayReferenceMarkerTest1Fragment @arguments(size: $size) + ...RelayReferenceMarkerTest1Fragment + @dangerously_unaliased_fixme + @arguments(size: $size) } } `; @@ -139,7 +142,7 @@ describe('RelayReferenceMarker', () => { }); it('marks "handle" nodes for queries', () => { - const data = { + const data: RecordSourceJSON = { '1': { __id: '1', __typename: 'User', @@ -227,7 +230,7 @@ describe('RelayReferenceMarker', () => { }); it('marks "handle" nodes with key and filters for queries', () => { - const data = { + const data: RecordSourceJSON = { '1': { __id: '1', __typename: 'User', @@ -348,7 +351,7 @@ describe('RelayReferenceMarker', () => { }); it('marks referenced records for client field', () => { - const data = { + const data: RecordSourceJSON = { '1': { __id: '1', id: '1', @@ -405,7 +408,7 @@ describe('RelayReferenceMarker', () => { node(id: $id) { id __typename - ...RelayReferenceMarkerTest2Fragment + ...RelayReferenceMarkerTest2Fragment @dangerously_unaliased_fixme } } `; @@ -493,7 +496,7 @@ describe('RelayReferenceMarker', () => { BarQuery = graphql` query RelayReferenceMarkerTest5Query($id: ID!) { node(id: $id) { - ...RelayReferenceMarkerTest3Fragment + ...RelayReferenceMarkerTest3Fragment @dangerously_unaliased_fixme } } `; @@ -511,7 +514,7 @@ describe('RelayReferenceMarker', () => { it('marks references when the match field/record exist and match a supported type (plaintext)', () => { // When the type matches PlainUserNameRenderer - const storeData = { + const storeData: RecordSourceJSON = { '1': { __id: '1', id: '1', @@ -561,7 +564,7 @@ describe('RelayReferenceMarker', () => { it('marks references when the match field/record exist and match a supported type (2)', () => { // When the type matches MarkdownUserNameRenderer - const storeData = { + const storeData: RecordSourceJSON = { '1': { __id: '1', id: '1', @@ -613,7 +616,7 @@ describe('RelayReferenceMarker', () => { // The field returned the MarkdownUserNameRenderer type, but the module for that branch // has not been loaded. The assumption is that the data cannot have been processed in that // case and therefore the markdown field is missing in the store. - const storeData = { + const storeData: RecordSourceJSON = { '1': { __id: '1', id: '1', @@ -656,7 +659,7 @@ describe('RelayReferenceMarker', () => { it('marks references when the match field/record exist but a scalar field is missing', () => { // the `data` field for the MarkdownUserNameRenderer is missing - const storeData = { + const storeData: RecordSourceJSON = { '1': { __id: '1', id: '1', @@ -706,7 +709,7 @@ describe('RelayReferenceMarker', () => { it('marks references when the match field/record exist but a linked field is missing', () => { // the `data` field for the MarkdownUserNameRenderer is missing - const storeData = { + const storeData: RecordSourceJSON = { '1': { __id: '1', id: '1', @@ -745,7 +748,7 @@ describe('RelayReferenceMarker', () => { }); it('marks references when the match field/record exist but do not match a supported type', () => { - const storeData = { + const storeData: RecordSourceJSON = { '1': { __id: '1', id: '1', @@ -783,7 +786,7 @@ describe('RelayReferenceMarker', () => { }); it('marks references when the match field is non-existent (null)', () => { - const storeData = { + const storeData: RecordSourceJSON = { '1': { __id: '1', id: '1', @@ -810,7 +813,7 @@ describe('RelayReferenceMarker', () => { }); it('marks references when the match field is not fetched (undefined)', () => { - const storeData = { + const storeData: RecordSourceJSON = { '1': { __id: '1', id: '1', @@ -837,15 +840,18 @@ describe('RelayReferenceMarker', () => { }); describe('Relay Resolver', () => { it('with no fragments is retained', () => { - const storeData = { + const storeData: RecordSourceJSON = { 'client:root': { __id: 'client:root', __typename: '__Root', - counter_no_fragment: { - __ref: 'client:root:counter_no_fragment', + // $FlowFixMe[invalid-computed-prop] + [`${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}counter_no_fragment`]: { + __ref: `client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}counter_no_fragment`, }, }, - 'client:root:counter_no_fragment': {}, + // $FlowFixMe[invalid-computed-prop] + [`client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}counter_no_fragment`]: + {}, }; const nodes = { FooQuery: graphql` @@ -879,24 +885,26 @@ describe('RelayReferenceMarker', () => { ); expect(Array.from(references).sort()).toEqual([ 'client:root', - 'client:root:counter_no_fragment', + `client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}counter_no_fragment`, ]); }); it('with fragment dependency is retained', () => { - const storeData = { + const storeData: RecordSourceJSON = { 'client:root': { __id: 'client:root', __typename: 'Query', me: {__ref: '1'}, - counter: { - __ref: 'client:root:counter', + // $FlowFixMe[invalid-computed-prop] + [`${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}counter`]: { + __ref: `client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}counter`, }, }, '1': { __id: '1', __typename: 'User', }, - 'client:root:counter': {}, + // $FlowFixMe[invalid-computed-prop] + [`client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}counter`]: {}, }; const nodes = { FooQuery: graphql` @@ -931,11 +939,11 @@ describe('RelayReferenceMarker', () => { expect(Array.from(references).sort()).toEqual([ '1', 'client:root', - 'client:root:counter', + `client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}counter`, ]); }); it('with @edgeTo client object is retained', () => { - const storeData = { + const storeData: RecordSourceJSON = { 'client:root': { __id: 'client:root', __typename: 'Query', @@ -1005,7 +1013,7 @@ describe('RelayReferenceMarker', () => { 'client:AstrologicalSign:Taurus', 'client:AstrologicalSign:Virgo', 'client:root', - 'client:root:all_astrological_signs', + `client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}all_astrological_signs`, ]); }); }); @@ -1048,7 +1056,7 @@ describe('RelayReferenceMarker', () => { BarQuery = graphql` query RelayReferenceMarkerTest6Query($id: ID!) { node(id: $id) { - ...RelayReferenceMarkerTest4Fragment + ...RelayReferenceMarkerTest4Fragment @dangerously_unaliased_fixme } } `; @@ -1066,7 +1074,7 @@ describe('RelayReferenceMarker', () => { it('marks references when the field/record exists and matches a @module selection (plaintext)', () => { // When the type matches PlainUserNameRenderer - const storeData = { + const storeData: RecordSourceJSON = { '1': { __id: '1', id: '1', @@ -1116,7 +1124,7 @@ describe('RelayReferenceMarker', () => { it('marks references when the field/record exists and matches a @module selection (markdown)', () => { // When the type matches MarkdownUserNameRenderer - const storeData = { + const storeData: RecordSourceJSON = { '1': { __id: '1', id: '1', @@ -1168,7 +1176,7 @@ describe('RelayReferenceMarker', () => { // The field returned the MarkdownUserNameRenderer type, but the module for that branch // has not been loaded. The assumption is that the data cannot have been processed in that // case and therefore the markdown field is missing in the store. - const storeData = { + const storeData: RecordSourceJSON = { '1': { __id: '1', id: '1', @@ -1211,7 +1219,7 @@ describe('RelayReferenceMarker', () => { it('marks references when the field/record exists but a scalar field is missing', () => { // the `data` field for the MarkdownUserNameRenderer is missing - const storeData = { + const storeData: RecordSourceJSON = { '1': { __id: '1', id: '1', @@ -1261,7 +1269,7 @@ describe('RelayReferenceMarker', () => { it('marks references when the field/record exists but a linked field is missing', () => { // the `data` field for the MarkdownUserNameRenderer is missing - const storeData = { + const storeData: RecordSourceJSON = { '1': { __id: '1', id: '1', @@ -1300,7 +1308,7 @@ describe('RelayReferenceMarker', () => { }); it('marks references when the field/record exists but do not match any @module selection', () => { - const storeData = { + const storeData: RecordSourceJSON = { '1': { __id: '1', id: '1', @@ -1338,7 +1346,7 @@ describe('RelayReferenceMarker', () => { }); it('throws if no operation loader is provided', () => { - const storeData = { + const storeData: RecordSourceJSON = { '1': { __id: '1', id: '1', @@ -1402,14 +1410,16 @@ describe('RelayReferenceMarker', () => { Query = graphql` query RelayReferenceMarkerTest7Query($id: ID!) { node(id: $id) { - ...RelayReferenceMarkerTest5Fragment @defer(label: "TestFragment") + ...RelayReferenceMarkerTest5Fragment + @dangerously_unaliased_fixme + @defer(label: "TestFragment") } } `; }); it('marks references when deferred selections are fetched', () => { - const storeData = { + const storeData: RecordSourceJSON = { '1': { __id: '1', __typename: 'Feedback', @@ -1439,7 +1449,7 @@ describe('RelayReferenceMarker', () => { }); it('marks references when deferred selections are not fetched', () => { - const storeData = { + const storeData: RecordSourceJSON = { '1': { __id: '1', __typename: 'Feedback', @@ -1478,14 +1488,14 @@ describe('RelayReferenceMarker', () => { Query = graphql` query RelayReferenceMarkerTest8Query($id: ID!) { node(id: $id) { - ...RelayReferenceMarkerTest6Fragment + ...RelayReferenceMarkerTest6Fragment @dangerously_unaliased_fixme } } `; }); it('marks references when streamed selections are fetched', () => { - const storeData = { + const storeData: RecordSourceJSON = { '1': { __id: '1', __typename: 'Feedback', @@ -1515,7 +1525,7 @@ describe('RelayReferenceMarker', () => { }); it('marks references when streamed selections are not fetched', () => { - const storeData = { + const storeData: RecordSourceJSON = { '1': { __id: '1', __typename: 'Feedback', diff --git a/packages/relay-runtime/store/__tests__/RelayResponseNormalizer-test.js b/packages/relay-runtime/store/__tests__/RelayResponseNormalizer-test.js index c658a1d48a2bb..6f6e07bc42752 100644 --- a/packages/relay-runtime/store/__tests__/RelayResponseNormalizer-test.js +++ b/packages/relay-runtime/store/__tests__/RelayResponseNormalizer-test.js @@ -11,6 +11,9 @@ 'use strict'; +import type {PayloadData, PayloadError} from '../../network/RelayNetworkTypes'; +import type {RecordSourceJSON} from '../RelayStoreTypes'; + const { getActorIdentifier, } = require('../../multi-actor-environment/ActorIdentifier'); @@ -37,6 +40,7 @@ describe('RelayResponseNormalizer', () => { const defaultOptions = { getDataID: defaultGetDataID, treatMissingFieldsAsNull: false, + log: null, }; it('normalizes queries', () => { @@ -135,12 +139,14 @@ describe('RelayResponseNormalizer', () => { __refs: [edgeID1, null, edgeID2], }, }, + // $FlowFixMe[invalid-computed-prop] [edgeID1]: { __id: edgeID1, __typename: 'FriendsEdge', cursor: 'cursor:2', node: {__ref: '2'}, }, + // $FlowFixMe[invalid-computed-prop] [edgeID2]: { __id: edgeID2, __typename: 'FriendsEdge', @@ -397,7 +403,7 @@ describe('RelayResponseNormalizer', () => { BarQuery = graphql` query RelayResponseNormalizerTest4Query($id: ID!) { node(id: $id) { - ...RelayResponseNormalizerTestFragment + ...RelayResponseNormalizerTestFragment @dangerously_unaliased_fixme } } `; @@ -674,7 +680,7 @@ describe('RelayResponseNormalizer', () => { BarQuery = graphql` query RelayResponseNormalizerTest5Query($id: ID!) { node(id: $id) { - ...RelayResponseNormalizerTest1Fragment + ...RelayResponseNormalizerTest1Fragment @dangerously_unaliased_fixme } } `; @@ -897,6 +903,7 @@ describe('RelayResponseNormalizer', () => { ) { node(id: $id) { ...RelayResponseNormalizerTest2Fragment + @dangerously_unaliased_fixme @defer(label: "TestFragment", if: $enableDefer) } } @@ -947,6 +954,7 @@ describe('RelayResponseNormalizer', () => { query RelayResponseNormalizerTest7Query($id: ID!) { node(id: $id) { ...RelayResponseNormalizerTest3Fragment + @dangerously_unaliased_fixme @defer(label: "TestFragment", if: true) } } @@ -1012,6 +1020,7 @@ describe('RelayResponseNormalizer', () => { ) { node(id: $id) { ...RelayResponseNormalizerTest4Fragment + @dangerously_unaliased_fixme @defer(label: "TestFragment", if: $enableDefer) } } @@ -1077,6 +1086,7 @@ describe('RelayResponseNormalizer', () => { ... on Feedback { actors { ...RelayResponseNormalizerTest5Fragment + @dangerously_unaliased_fixme @defer(label: "TestFragment", if: true) } } @@ -1169,6 +1179,7 @@ describe('RelayResponseNormalizer', () => { query RelayResponseNormalizerTest10Query($id: ID!) { node(id: $id) { ...RelayResponseNormalizerTest6Fragment + @dangerously_unaliased_fixme @defer(label: "TestFragment") } } @@ -1225,7 +1236,7 @@ describe('RelayResponseNormalizer', () => { $enableStream: Boolean! ) { node(id: $id) { - ...RelayResponseNormalizerTest7Fragment + ...RelayResponseNormalizerTest7Fragment @dangerously_unaliased_fixme } } `; @@ -1282,7 +1293,7 @@ describe('RelayResponseNormalizer', () => { const Query = graphql` query RelayResponseNormalizerTestQuery($id: ID!) { node(id: $id) { - ...RelayResponseNormalizerTest8Fragment + ...RelayResponseNormalizerTest8Fragment @dangerously_unaliased_fixme } } `; @@ -1351,7 +1362,7 @@ describe('RelayResponseNormalizer', () => { $enableStream: Boolean! ) { node(id: $id) { - ...RelayResponseNormalizerTest9Fragment + ...RelayResponseNormalizerTest9Fragment @dangerously_unaliased_fixme } } `; @@ -1424,6 +1435,7 @@ describe('RelayResponseNormalizer', () => { query RelayResponseNormalizerTest13Query($id: ID!) { node(id: $id) { ...RelayResponseNormalizerTest10Fragment + @dangerously_unaliased_fixme } } `; @@ -1508,6 +1520,7 @@ describe('RelayResponseNormalizer', () => { query RelayResponseNormalizerTest14Query($id: ID!) { node(id: $id) { ...RelayResponseNormalizerTest11Fragment + @dangerously_unaliased_fixme } } `; @@ -1835,8 +1848,8 @@ describe('RelayResponseNormalizer', () => { const getDataID = jest.fn( ( - fieldValue: string | {[string]: mixed}, - typename: string | {[string]: mixed}, + fieldValue: string | {+[string]: mixed}, + typename: string | {+[string]: mixed}, ) => { return `${ typeof fieldValue === 'string' ? fieldValue : String(fieldValue.id) @@ -1846,8 +1859,8 @@ describe('RelayResponseNormalizer', () => { const getNullAsDataID = jest.fn( ( - fieldValue: string | {[string]: mixed}, - typename: string | {[string]: mixed}, + fieldValue: string | {+[string]: mixed}, + typename: string | {+[string]: mixed}, ) => { return null; }, @@ -1941,7 +1954,7 @@ describe('RelayResponseNormalizer', () => { id: '1', }), fooPayload, - {getDataID, treatMissingFieldsAsNull: false}, + {getDataID, treatMissingFieldsAsNull: false, log: null}, ); expect(recordSource.toJSON()).toEqual({ 'client:root': { @@ -2014,7 +2027,7 @@ describe('RelayResponseNormalizer', () => { id: '1', }), fooPayload0, - {getDataID, treatMissingFieldsAsNull: false}, + {getDataID, treatMissingFieldsAsNull: false, log: null}, ); normalize( recordSource, @@ -2022,7 +2035,7 @@ describe('RelayResponseNormalizer', () => { id: '1', }), fooPayload1, - {getDataID, treatMissingFieldsAsNull: false}, + {getDataID, treatMissingFieldsAsNull: false, log: null}, ); expect(recordSource.toJSON()).toEqual({ 'client:root': { @@ -2062,7 +2075,7 @@ describe('RelayResponseNormalizer', () => { id: '1', }), payload, - {getDataID, treatMissingFieldsAsNull: false}, + {getDataID, treatMissingFieldsAsNull: false, log: null}, ); expect(recordSource.toJSON()).toEqual({ '1:Page': { @@ -2093,7 +2106,7 @@ describe('RelayResponseNormalizer', () => { }); it('falls through to previously generated ID if function returns null ', () => { - const previousData = { + const previousData: RecordSourceJSON = { 'client:root': { __id: 'client:root', __typename: '__Root', @@ -2131,7 +2144,11 @@ describe('RelayResponseNormalizer', () => { id: '1', }), payload, - {getDataID: getNullAsDataID, treatMissingFieldsAsNull: false}, + { + getDataID: getNullAsDataID, + treatMissingFieldsAsNull: false, + log: null, + }, ); expect(recordSource.toJSON()).toEqual(expectedData); expect(getNullAsDataID).toBeCalledTimes(3); @@ -2144,7 +2161,11 @@ describe('RelayResponseNormalizer', () => { id: '1', }), payload, - {getDataID: getNullAsDataID, treatMissingFieldsAsNull: false}, + { + getDataID: getNullAsDataID, + treatMissingFieldsAsNull: false, + log: null, + }, ); expect(recordSource.toJSON()).toEqual({ 'client:root': { @@ -2220,7 +2241,7 @@ describe('RelayResponseNormalizer', () => { id: '1', }), payload, - {getDataID, treatMissingFieldsAsNull: false}, + {getDataID, treatMissingFieldsAsNull: false, log: null}, ); expect(recordSource.toJSON()).toEqual({ '1:Page': { @@ -2253,7 +2274,7 @@ describe('RelayResponseNormalizer', () => { }); it('uses cached IDs if they were generated before and the function returns null', () => { - const previousData = { + const previousData: RecordSourceJSON = { 'client:root': { __id: 'client:root', __typename: '__Root', @@ -2291,14 +2312,18 @@ describe('RelayResponseNormalizer', () => { id: '1', }), payload, - {getDataID: getNullAsDataID, treatMissingFieldsAsNull: false}, + { + getDataID: getNullAsDataID, + treatMissingFieldsAsNull: false, + log: null, + }, ); expect(recordSource.toJSON()).toEqual(expectedData); expect(getNullAsDataID).toBeCalledTimes(3); }); it('falls through to generateClientID when the function returns null and there is one new field in stored plural links', () => { - const data = { + const data: RecordSourceJSON = { 'client:root': { __id: 'client:root', __typename: '__Root', @@ -2327,7 +2352,11 @@ describe('RelayResponseNormalizer', () => { id: '1', }), payload, - {getDataID: getNullAsDataID, treatMissingFieldsAsNull: false}, + { + getDataID: getNullAsDataID, + treatMissingFieldsAsNull: false, + log: null, + }, ); const result = recordSource.toJSON(); expect(result['test:root:node(id:"1")']).toEqual({ @@ -2356,7 +2385,11 @@ describe('RelayResponseNormalizer', () => { id: '1', }), payload, - {getDataID: getNullAsDataID, treatMissingFieldsAsNull: false}, + { + getDataID: getNullAsDataID, + treatMissingFieldsAsNull: false, + log: null, + }, ); expect(recordSource.toJSON()).toEqual({ 'client:root': { @@ -2443,7 +2476,7 @@ describe('RelayResponseNormalizer', () => { id: '1', }), payload0, - {getDataID, treatMissingFieldsAsNull: false}, + {getDataID, treatMissingFieldsAsNull: false, log: null}, ); normalize( recordSource, @@ -2451,7 +2484,7 @@ describe('RelayResponseNormalizer', () => { id: '1', }), payload1, - {getDataID, treatMissingFieldsAsNull: false}, + {getDataID, treatMissingFieldsAsNull: false, log: null}, ); expect(recordSource.toJSON()).toEqual({ '1:Page': { @@ -2518,7 +2551,7 @@ describe('RelayResponseNormalizer', () => { query RelayResponseNormalizerTest_pvQuery($id: ID!) { node(id: $id) { id - ...RelayResponseNormalizerTest_pvFragment + ...RelayResponseNormalizerTest_pvFragment @dangerously_unaliased_fixme } } `; @@ -3670,6 +3703,7 @@ describe('RelayResponseNormalizer', () => { viewer { actor @fb_actor_change { ...RelayResponseNormalizerTestActorChangeFragment + @dangerously_unaliased_fixme } } } @@ -3999,6 +4033,7 @@ describe('RelayResponseNormalizer', () => { } actor @fb_actor_change { ...RelayResponseNormalizerTestActorChangeFragment + @dangerously_unaliased_fixme } } } @@ -4102,7 +4137,7 @@ describe('RelayResponseNormalizer', () => { } `; - const payload = {}; + const payload: PayloadData = {}; const recordSource = new RelayRecordSource(); recordSource.set(ROOT_ID, RelayModernRecord.create(ROOT_ID, ROOT_TYPE)); @@ -4165,7 +4200,7 @@ describe('RelayResponseNormalizer', () => { } } `; - const payload = { + const payload: PayloadData = { node: { id: '1', __typename: 'User', @@ -4201,7 +4236,7 @@ describe('RelayResponseNormalizer', () => { }, }, }; - const errors = [ + const errors: Array = [ { message: "No one knows Kramer's first name until season six!", path: ['node', 'friends', 'edges', 1, 'node', 'firstName'], @@ -4285,18 +4320,21 @@ describe('RelayResponseNormalizer', () => { __refs: [edge0ID, edge1ID, edge2ID], }, }, + // $FlowFixMe[invalid-computed-prop] [edge0ID]: { __id: edge0ID, __typename: 'FriendsEdge', cursor: 'cursor:2', node: {__ref: '2'}, }, + // $FlowFixMe[invalid-computed-prop] [edge1ID]: { __id: edge1ID, __typename: 'FriendsEdge', cursor: 'cursor:3', node: {__ref: '3'}, }, + // $FlowFixMe[invalid-computed-prop] [edge2ID]: { __id: edge2ID, __typename: 'FriendsEdge', @@ -4342,7 +4380,7 @@ describe('RelayResponseNormalizer', () => { friends: null, }, }; - const errors = [ + const errors: Array = [ { message: "No one knows Kramer's first name until season six!", path: ['node', 'friends', 'edges', 1, 'node', 'firstName'], @@ -4443,7 +4481,7 @@ describe('RelayResponseNormalizer', () => { }, }, }; - const errors = [ + const errors: Array = [ { message: 'There was an error!', path: ['node', 'friends', 'edges'], @@ -4491,7 +4529,7 @@ describe('RelayResponseNormalizer', () => { RelayFeatureFlags.ENABLE_NONCOMPLIANT_ERROR_HANDLING_ON_LISTS = true; }); - it('stores field errors on an empty list', () => { + it('stores field errors on an linked field that is an empty list', () => { const FooQuery = graphql` query RelayResponseNormalizerTest40Query($id: ID!) { node(id: $id) { @@ -4516,7 +4554,7 @@ describe('RelayResponseNormalizer', () => { }, }, }; - const errors = [ + const errors: Array = [ { message: 'There was an error!', path: ['node', 'friends', 'edges'], @@ -4553,7 +4591,68 @@ describe('RelayResponseNormalizer', () => { }, ], }, - edges: null, + edges: { + __refs: [], + }, + }, + 'client:root': { + __id: 'client:root', + __typename: '__Root', + 'node(id:"1")': {__ref: '1'}, + }, + }); + }); + + it('stores field errors on an scalar field that is an empty list', () => { + const FooQuery = graphql` + query RelayResponseNormalizerTest41Query($id: ID!) { + node(id: $id) { + id + __typename + ... on User { + emailAddresses + } + } + } + `; + const payload = { + node: { + id: '1', + __typename: 'User', + emailAddresses: [], + }, + }; + const errors: Array = [ + { + message: 'There was an error!', + path: ['node', 'emailAddresses'], + }, + ]; + const recordSource = new RelayRecordSource(); + recordSource.set(ROOT_ID, RelayModernRecord.create(ROOT_ID, ROOT_TYPE)); + normalize( + recordSource, + createNormalizationSelector(FooQuery.operation, ROOT_ID, { + id: '1', + size: 32, + }), + payload, + defaultOptions, + errors, + ); + expect(recordSource.toJSON()).toEqual({ + '1': { + __id: '1', + __typename: 'User', + __errors: { + emailAddresses: [ + { + message: 'There was an error!', + }, + ], + }, + id: '1', + emailAddresses: [], }, 'client:root': { __id: 'client:root', @@ -4564,4 +4663,113 @@ describe('RelayResponseNormalizer', () => { }); }); }); + + describe('Prototype-less objects (e.g., from graphql-js executor)', () => { + const createPrototypeLessObject = (data: {[string]: mixed}) => { + const obj = Object.create(null); + // $FlowFixMe[unsafe-object-assign] - assigning to prototype-less object + Object.assign(obj, data); + return obj; + }; + + it('normalizes prototype-less payloads with type discriminator', () => { + const query = graphql` + query RelayResponseNormalizerTest42Query($id: ID!) { + node(id: $id) { + ...RelayResponseNormalizerTest42Fragment + } + } + `; + + graphql` + fragment RelayResponseNormalizerTest42Fragment on Node { + id + ... on User { + name + } + } + `; + + const payload = { + node: createPrototypeLessObject({ + __typename: 'Page', + id: '1', + }), + }; + + const recordSource = new RelayRecordSource(); + recordSource.set(ROOT_ID, RelayModernRecord.create(ROOT_ID, ROOT_TYPE)); + + normalize( + recordSource, + createNormalizationSelector(query.operation, ROOT_ID, {id: '1'}), + payload, + defaultOptions, + ); + + expect(recordSource.toJSON()).toEqual({ + '1': { + __id: '1', + __typename: 'Page', + id: '1', + }, + 'client:root': { + __id: 'client:root', + __typename: '__Root', + 'node(id:"1")': {__ref: '1'}, + }, + 'client:__type:Page': { + __id: 'client:__type:Page', + __typename: '__TypeSchema', + __isNode: false, + }, + }); + }); + + it('normalizes prototype-less payloads for union types', () => { + const query = graphql` + query RelayResponseNormalizerTest43Query($id: ID!) { + userOrPage(id: $id) { + ... on User { + id + } + } + } + `; + + const payload = { + userOrPage: createPrototypeLessObject({ + __typename: 'Page', + id: '1', + }), + }; + + const recordSource = new RelayRecordSource(); + recordSource.set(ROOT_ID, RelayModernRecord.create(ROOT_ID, ROOT_TYPE)); + + normalize( + recordSource, + createNormalizationSelector(query.operation, ROOT_ID, {id: '1'}), + payload, + defaultOptions, + ); + + expect(recordSource.toJSON()).toEqual({ + '1': { + __id: '1', + __typename: 'Page', + }, + 'client:root': { + __id: 'client:root', + __typename: '__Root', + 'userOrPage(id:"1")': {__ref: '1'}, + }, + 'client:__type:Page': { + __id: 'client:__type:Page', + __typename: '__TypeSchema', + __isNode: false, + }, + }); + }); + }); }); diff --git a/packages/relay-runtime/store/__tests__/__generated__/ClientEdgeQuery_observeQueryTestClientEdgeToServerQuery_me__client_edge.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/ClientEdgeQuery_observeQueryTestClientEdgeToServerQuery_me__client_edge.graphql.js new file mode 100644 index 0000000000000..7ed98e8c744bf --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/ClientEdgeQuery_observeQueryTestClientEdgeToServerQuery_me__client_edge.graphql.js @@ -0,0 +1,143 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ConcreteRequest, Query } from 'relay-runtime'; +import type { RefetchableClientEdgeQuery_observeQueryTestClientEdgeToServerQuery_me__client_edge$fragmentType } from "./RefetchableClientEdgeQuery_observeQueryTestClientEdgeToServerQuery_me__client_edge.graphql"; +export type ClientEdgeQuery_observeQueryTestClientEdgeToServerQuery_me__client_edge$variables = {| + id: string, +|}; +export type ClientEdgeQuery_observeQueryTestClientEdgeToServerQuery_me__client_edge$data = {| + +node: ?{| + +$fragmentSpreads: RefetchableClientEdgeQuery_observeQueryTestClientEdgeToServerQuery_me__client_edge$fragmentType, + |}, +|}; +export type ClientEdgeQuery_observeQueryTestClientEdgeToServerQuery_me__client_edge = {| + response: ClientEdgeQuery_observeQueryTestClientEdgeToServerQuery_me__client_edge$data, + variables: ClientEdgeQuery_observeQueryTestClientEdgeToServerQuery_me__client_edge$variables, +|}; +*/ + +var node/*: ConcreteRequest*/ = (function(){ +var v0 = [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "id" + } +], +v1 = [ + { + "kind": "Variable", + "name": "id", + "variableName": "id" + } +]; +return { + "fragment": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Fragment", + "metadata": null, + "name": "ClientEdgeQuery_observeQueryTestClientEdgeToServerQuery_me__client_edge", + "selections": [ + { + "alias": null, + "args": (v1/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + { + "args": null, + "kind": "FragmentSpread", + "name": "RefetchableClientEdgeQuery_observeQueryTestClientEdgeToServerQuery_me__client_edge" + } + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Operation", + "name": "ClientEdgeQuery_observeQueryTestClientEdgeToServerQuery_me__client_edge", + "selections": [ + { + "alias": null, + "args": (v1/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + }, + { + "kind": "InlineFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + } + ], + "type": "User", + "abstractKey": null + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "24cc155fed24ded00a99f90e67a86182", + "id": null, + "metadata": {}, + "name": "ClientEdgeQuery_observeQueryTestClientEdgeToServerQuery_me__client_edge", + "operationKind": "query", + "text": "query ClientEdgeQuery_observeQueryTestClientEdgeToServerQuery_me__client_edge(\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...RefetchableClientEdgeQuery_observeQueryTestClientEdgeToServerQuery_me__client_edge\n id\n }\n}\n\nfragment RefetchableClientEdgeQuery_observeQueryTestClientEdgeToServerQuery_me__client_edge on User {\n name\n id\n}\n" + } +}; +})(); + +if (__DEV__) { + (node/*: any*/).hash = "ffa93f1454a0796bf5a92612348c1069"; +} + +module.exports = ((node/*: any*/)/*: Query< + ClientEdgeQuery_observeQueryTestClientEdgeToServerQuery_me__client_edge$variables, + ClientEdgeQuery_observeQueryTestClientEdgeToServerQuery_me__client_edge$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/ClientEdgeToClientObjectTest1Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/ClientEdgeToClientObjectTest1Query.graphql.js index c9acca027c773..921dde0802c72 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/ClientEdgeToClientObjectTest1Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/ClientEdgeToClientObjectTest1Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<23f93b7b8672df5dd26cabb896319e70>> * @flow * @lightSyntaxTransform * @nogrep @@ -192,7 +192,7 @@ return { }, "kind": "RelayResolver", "name": "astrological_sign", - "resolverModule": require('./../resolvers/UserAstrologicalSignResolver').astrological_sign, + "resolverModule": require('../resolvers/UserAstrologicalSignResolver').astrological_sign, "path": "me.astrological_sign" }, "linkedField": { @@ -210,7 +210,7 @@ return { "fragment": (v1/*: any*/), "kind": "RelayResolver", "name": "name", - "resolverModule": require('./../resolvers/AstrologicalSignNameResolver').name, + "resolverModule": require('../resolvers/AstrologicalSignNameResolver').name, "path": "me.astrological_sign.name" }, { @@ -219,7 +219,7 @@ return { "fragment": (v2/*: any*/), "kind": "RelayResolver", "name": "house", - "resolverModule": require('./../resolvers/AstrologicalSignHouseResolver').house, + "resolverModule": require('../resolvers/AstrologicalSignHouseResolver').house, "path": "me.astrological_sign.house" }, { @@ -232,7 +232,7 @@ return { "fragment": (v3/*: any*/), "kind": "RelayResolver", "name": "opposite", - "resolverModule": require('./../resolvers/AstrologicalSignOppositeResolver').opposite, + "resolverModule": require('../resolvers/AstrologicalSignOppositeResolver').opposite, "path": "me.astrological_sign.opposite" }, "linkedField": { @@ -250,7 +250,7 @@ return { "fragment": (v1/*: any*/), "kind": "RelayResolver", "name": "name", - "resolverModule": require('./../resolvers/AstrologicalSignNameResolver').name, + "resolverModule": require('../resolvers/AstrologicalSignNameResolver').name, "path": "me.astrological_sign.opposite.name" }, { @@ -259,7 +259,7 @@ return { "fragment": (v2/*: any*/), "kind": "RelayResolver", "name": "house", - "resolverModule": require('./../resolvers/AstrologicalSignHouseResolver').house, + "resolverModule": require('../resolvers/AstrologicalSignHouseResolver').house, "path": "me.astrological_sign.opposite.house" }, { @@ -272,7 +272,7 @@ return { "fragment": (v3/*: any*/), "kind": "RelayResolver", "name": "opposite", - "resolverModule": require('./../resolvers/AstrologicalSignOppositeResolver').opposite, + "resolverModule": require('../resolvers/AstrologicalSignOppositeResolver').opposite, "path": "me.astrological_sign.opposite.opposite" }, "linkedField": { @@ -290,7 +290,7 @@ return { "fragment": (v1/*: any*/), "kind": "RelayResolver", "name": "name", - "resolverModule": require('./../resolvers/AstrologicalSignNameResolver').name, + "resolverModule": require('../resolvers/AstrologicalSignNameResolver').name, "path": "me.astrological_sign.opposite.opposite.name" } ], diff --git a/packages/relay-runtime/store/__tests__/__generated__/ClientEdgeToClientObjectTest2Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/ClientEdgeToClientObjectTest2Query.graphql.js index a1584d38c39dd..1fa7ffab55b21 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/ClientEdgeToClientObjectTest2Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/ClientEdgeToClientObjectTest2Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<5e727aff07b90ca7a4e0cc8e092a7830>> * @flow * @lightSyntaxTransform * @nogrep @@ -83,7 +83,7 @@ return { }, "kind": "RelayResolver", "name": "all_astrological_signs", - "resolverModule": require('./../resolvers/QueryAllAstrologicalSignsResolver').all_astrological_signs, + "resolverModule": require('../resolvers/QueryAllAstrologicalSignsResolver').all_astrological_signs, "path": "all_astrological_signs" }, "linkedField": { @@ -104,7 +104,7 @@ return { }, "kind": "RelayResolver", "name": "name", - "resolverModule": require('./../resolvers/AstrologicalSignNameResolver').name, + "resolverModule": require('../resolvers/AstrologicalSignNameResolver').name, "path": "all_astrological_signs.name" } ], diff --git a/packages/relay-runtime/store/__tests__/__generated__/ClientEdgeToClientObjectTest3Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/ClientEdgeToClientObjectTest3Query.graphql.js index bfa613cb51ab8..a73014bd60364 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/ClientEdgeToClientObjectTest3Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/ClientEdgeToClientObjectTest3Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<2305e9de2c013f48bf2d4b2e3d222bce>> * @flow * @lightSyntaxTransform * @nogrep @@ -109,7 +109,7 @@ return { }, "kind": "RelayResolver", "name": "astrological_sign", - "resolverModule": require('./../resolvers/UserAstrologicalSignResolver').astrological_sign, + "resolverModule": require('../resolvers/UserAstrologicalSignResolver').astrological_sign, "path": "me.astrological_sign" }, "linkedField": { @@ -131,7 +131,7 @@ return { }, "kind": "RelayResolver", "name": "name", - "resolverModule": require('./../resolvers/AstrologicalSignNameResolver').name, + "resolverModule": require('../resolvers/AstrologicalSignNameResolver').name, "path": "me.astrological_sign.name" }, (v1/*: any*/) diff --git a/packages/relay-runtime/store/__tests__/__generated__/ClientEdgeToClientObjectTestClientRootFragment.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/ClientEdgeToClientObjectTestClientRootFragment.graphql.js new file mode 100644 index 0000000000000..05d84d3dc13fd --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/ClientEdgeToClientObjectTestClientRootFragment.graphql.js @@ -0,0 +1,68 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<7ee97a7492fdfb892ba011e0510d4533>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { Fragment, ReaderFragment } from 'relay-runtime'; +import type { FragmentType } from "relay-runtime"; +declare export opaque type ClientEdgeToClientObjectTestClientRootFragment$fragmentType: FragmentType; +export type ClientEdgeToClientObjectTestClientRootFragment$data = {| + +id: string, + +$fragmentType: ClientEdgeToClientObjectTestClientRootFragment$fragmentType, +|}; +export type ClientEdgeToClientObjectTestClientRootFragment$key = { + +$data?: ClientEdgeToClientObjectTestClientRootFragment$data, + +$fragmentSpreads: ClientEdgeToClientObjectTestClientRootFragment$fragmentType, + ... +}; +*/ + +var node/*: ReaderFragment*/ = { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "ClientEdgeToClientObjectTestClientRootFragment", + "selections": [ + { + "kind": "ClientExtension", + "selections": [ + { + "kind": "RequiredField", + "field": { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + }, + "action": "THROW" + } + ] + } + ], + "type": "ClientAccount", + "abstractKey": null +}; + +if (__DEV__) { + (node/*: any*/).hash = "fc0607064767927b602ce35d27d276db"; +} + +module.exports = ((node/*: any*/)/*: Fragment< + ClientEdgeToClientObjectTestClientRootFragment$fragmentType, + ClientEdgeToClientObjectTestClientRootFragment$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/ClientEdgeToClientObjectTestClientRootFragmentQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/ClientEdgeToClientObjectTestClientRootFragmentQuery.graphql.js new file mode 100644 index 0000000000000..eea782273a2e3 --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/ClientEdgeToClientObjectTestClientRootFragmentQuery.graphql.js @@ -0,0 +1,202 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<9ee6298ab2ab61c14148a3dcad8bb88f>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ClientRequest, ClientQuery } from 'relay-runtime'; +import type { DataID } from "relay-runtime"; +import type { ClientEdgeToClientObjectTestClientRootNameFragment$key } from "./ClientEdgeToClientObjectTestClientRootNameFragment.graphql"; +import {account_name as clientAccountAccountNameResolverType} from "../ClientEdgeToClientObject-test.js"; +import type { TestResolverContextType } from "../../../mutations/__tests__/TestResolverContextType"; +// Type assertion validating that `clientAccountAccountNameResolverType` resolver is correctly implemented. +// A type error here indicates that the type signature of the resolver module is incorrect. +(clientAccountAccountNameResolverType: ( + rootKey: ClientEdgeToClientObjectTestClientRootNameFragment$key, + args: void, + context: TestResolverContextType, +) => ?string); +import {account as queryAccountResolverType} from "../ClientEdgeToClientObject-test.js"; +// Type assertion validating that `queryAccountResolverType` resolver is correctly implemented. +// A type error here indicates that the type signature of the resolver module is incorrect. +(queryAccountResolverType: ( + args: void, + context: TestResolverContextType, +) => ?{| + +id: DataID, +|}); +export type ClientEdgeToClientObjectTestClientRootFragmentQuery$variables = {||}; +export type ClientEdgeToClientObjectTestClientRootFragmentQuery$data = {| + +account: ?{| + +__id: string, + +account_name: ?string, + +id: ?string, + |}, +|}; +export type ClientEdgeToClientObjectTestClientRootFragmentQuery = {| + response: ClientEdgeToClientObjectTestClientRootFragmentQuery$data, + variables: ClientEdgeToClientObjectTestClientRootFragmentQuery$variables, +|}; +*/ + +var node/*: ClientRequest*/ = (function(){ +var v0 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__id", + "storageKey": null +}, +v1 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null +}; +return { + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": { + "hasClientEdges": true + }, + "name": "ClientEdgeToClientObjectTestClientRootFragmentQuery", + "selections": [ + { + "kind": "ClientEdgeToClientObject", + "concreteType": "ClientAccount", + "modelResolvers": null, + "backingField": { + "alias": null, + "args": null, + "fragment": null, + "kind": "RelayResolver", + "name": "account", + "resolverModule": require('../ClientEdgeToClientObject-test').account, + "path": "account" + }, + "linkedField": { + "alias": null, + "args": null, + "concreteType": "ClientAccount", + "kind": "LinkedField", + "name": "account", + "plural": false, + "selections": [ + (v0/*: any*/), + (v1/*: any*/), + { + "alias": null, + "args": null, + "fragment": { + "args": null, + "kind": "FragmentSpread", + "name": "ClientEdgeToClientObjectTestClientRootNameFragment" + }, + "kind": "RelayResolver", + "name": "account_name", + "resolverModule": require('../ClientEdgeToClientObject-test').account_name, + "path": "account.account_name" + } + ], + "storageKey": null + } + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [], + "kind": "Operation", + "name": "ClientEdgeToClientObjectTestClientRootFragmentQuery", + "selections": [ + { + "kind": "ClientEdgeToClientObject", + "backingField": { + "name": "account", + "args": null, + "fragment": null, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": false + }, + "linkedField": { + "alias": null, + "args": null, + "concreteType": "ClientAccount", + "kind": "LinkedField", + "name": "account", + "plural": false, + "selections": [ + (v0/*: any*/), + (v1/*: any*/), + { + "name": "account_name", + "args": null, + "fragment": { + "kind": "InlineFragment", + "selections": [ + { + "name": "self", + "args": null, + "fragment": { + "kind": "InlineFragment", + "selections": [ + (v1/*: any*/) + ], + "type": "ClientAccount", + "abstractKey": null + }, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": true + } + ], + "type": "ClientAccount", + "abstractKey": null + }, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": true + } + ], + "storageKey": null + } + } + ] + }, + "params": { + "cacheID": "ffa6b940b1204ad6a4b1699197a63b1b", + "id": null, + "metadata": {}, + "name": "ClientEdgeToClientObjectTestClientRootFragmentQuery", + "operationKind": "query", + "text": null + } +}; +})(); + +if (__DEV__) { + (node/*: any*/).hash = "c9e60b20e5eac19fb837bd03824b4ff2"; +} + +module.exports = ((node/*: any*/)/*: ClientQuery< + ClientEdgeToClientObjectTestClientRootFragmentQuery$variables, + ClientEdgeToClientObjectTestClientRootFragmentQuery$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/ClientEdgeToClientObjectTestClientRootNameFragment.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/ClientEdgeToClientObjectTestClientRootNameFragment.graphql.js new file mode 100644 index 0000000000000..9675d2ba35ead --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/ClientEdgeToClientObjectTestClientRootNameFragment.graphql.js @@ -0,0 +1,75 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<0794561cff1453f31703ce695e38c6b2>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { Fragment, ReaderFragment } from 'relay-runtime'; +import type { ClientEdgeToClientObjectTestClientRootFragment$key } from "./ClientEdgeToClientObjectTestClientRootFragment.graphql"; +import type { FragmentType } from "relay-runtime"; +import {self as clientAccountSelfResolverType} from "../ClientEdgeToClientObject-test.js"; +import type { TestResolverContextType } from "../../../mutations/__tests__/TestResolverContextType"; +// Type assertion validating that `clientAccountSelfResolverType` resolver is correctly implemented. +// A type error here indicates that the type signature of the resolver module is incorrect. +(clientAccountSelfResolverType: ( + rootKey: ClientEdgeToClientObjectTestClientRootFragment$key, + args: void, + context: TestResolverContextType, +) => ?mixed); +declare export opaque type ClientEdgeToClientObjectTestClientRootNameFragment$fragmentType: FragmentType; +export type ClientEdgeToClientObjectTestClientRootNameFragment$data = {| + +self: ?ReturnType, + +$fragmentType: ClientEdgeToClientObjectTestClientRootNameFragment$fragmentType, +|}; +export type ClientEdgeToClientObjectTestClientRootNameFragment$key = { + +$data?: ClientEdgeToClientObjectTestClientRootNameFragment$data, + +$fragmentSpreads: ClientEdgeToClientObjectTestClientRootNameFragment$fragmentType, + ... +}; +*/ + +var node/*: ReaderFragment*/ = { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "ClientEdgeToClientObjectTestClientRootNameFragment", + "selections": [ + { + "alias": null, + "args": null, + "fragment": { + "args": null, + "kind": "FragmentSpread", + "name": "ClientEdgeToClientObjectTestClientRootFragment" + }, + "kind": "RelayResolver", + "name": "self", + "resolverModule": require('../ClientEdgeToClientObject-test').self, + "path": "self" + } + ], + "type": "ClientAccount", + "abstractKey": null +}; + +if (__DEV__) { + (node/*: any*/).hash = "22c8af82f4599e2fe1b29ccf8da33b51"; +} + +module.exports = ((node/*: any*/)/*: Fragment< + ClientEdgeToClientObjectTestClientRootNameFragment$fragmentType, + ClientEdgeToClientObjectTestClientRootNameFragment$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/DataCheckerTest4Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/DataCheckerTest4Query.graphql.js index 5402279a15563..21f997df23695 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/DataCheckerTest4Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/DataCheckerTest4Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<63df037fd0f87eebae863494a08cb57f>> * @flow * @lightSyntaxTransform * @nogrep @@ -176,7 +176,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "a9b9699255a75903b81e31abf46a581d"; + (node/*: any*/).hash = "15016c70d2233156a2b3cfba19ebded8"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/DataCheckerTest5Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/DataCheckerTest5Query.graphql.js index dbe1d2a45032d..551f2b5d3c5b9 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/DataCheckerTest5Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/DataCheckerTest5Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<6e6c389d54741d86e0a51358267c1b5d>> * @flow * @lightSyntaxTransform * @nogrep @@ -170,7 +170,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "eb95da17a46437e27a3ea0ccf845ea21"; + (node/*: any*/).hash = "204ef3451e43e60e858d6d974ae1ad2a"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/DataCheckerTest6Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/DataCheckerTest6Query.graphql.js index 3741f1ffa7973..8dc3dbc7e1656 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/DataCheckerTest6Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/DataCheckerTest6Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<295d4fe133262868316c6d296ed14ec2>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -156,7 +156,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "e8bc7d9a84fa2e9536aed16887b17a29"; + (node/*: any*/).hash = "f1d10f99d8e1782c2705acbf1604957e"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/DataCheckerTest9Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/DataCheckerTest9Query.graphql.js index ba00181e79d0a..9f10e8221e1b0 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/DataCheckerTest9Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/DataCheckerTest9Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<416eb6b4ce3b9801bc42f80ee03261ad>> + * @generated SignedSource<<036174fe99b28ff7cb858124f057acdd>> * @flow * @lightSyntaxTransform * @nogrep @@ -148,7 +148,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "ae64013fff9f02d31b27ea607016ea03"; + (node/*: any*/).hash = "b046fd7374d1a7ea5d22b481c0edd547"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/DataCheckerTestExecQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/DataCheckerTestExecQuery.graphql.js new file mode 100644 index 0000000000000..2c5f47615b998 --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/DataCheckerTestExecQuery.graphql.js @@ -0,0 +1,354 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ClientRequest, ClientQuery } from 'relay-runtime'; +import type { DataID } from "relay-runtime"; +import type { RelayReaderExecResolversTestUser____relay_model_instance$data } from "./RelayReaderExecResolversTestUser____relay_model_instance.graphql"; +import {RelayReaderExecResolversTest_user_one as queryRelayReaderExecResolversTestUserOneResolverType} from "../RelayReader-ExecResolvers-test.js"; +import type { TestResolverContextType } from "../../../mutations/__tests__/TestResolverContextType"; +// Type assertion validating that `queryRelayReaderExecResolversTestUserOneResolverType` resolver is correctly implemented. +// A type error here indicates that the type signature of the resolver module is incorrect. +(queryRelayReaderExecResolversTestUserOneResolverType: ( + args: void, + context: TestResolverContextType, +) => ?{| + +id: DataID, +|}); +import {best_friend as relayReaderExecResolversTestUserBestFriendResolverType} from "../RelayReader-ExecResolvers-test.js"; +// Type assertion validating that `relayReaderExecResolversTestUserBestFriendResolverType` resolver is correctly implemented. +// A type error here indicates that the type signature of the resolver module is incorrect. +(relayReaderExecResolversTestUserBestFriendResolverType: ( + __relay_model_instance: RelayReaderExecResolversTestUser____relay_model_instance$data['__relay_model_instance'], + args: void, + context: TestResolverContextType, +) => ?{| + +id: DataID, +|}); +import {name as relayReaderExecResolversTestUserNameResolverType} from "../RelayReader-ExecResolvers-test.js"; +// Type assertion validating that `relayReaderExecResolversTestUserNameResolverType` resolver is correctly implemented. +// A type error here indicates that the type signature of the resolver module is incorrect. +(relayReaderExecResolversTestUserNameResolverType: ( + __relay_model_instance: RelayReaderExecResolversTestUser____relay_model_instance$data['__relay_model_instance'], + args: void, + context: TestResolverContextType, +) => ?string); +export type DataCheckerTestExecQuery$variables = {||}; +export type DataCheckerTestExecQuery$data = {| + +RelayReaderExecResolversTest_user_one: ?{| + +best_friend: ?{| + +best_friend: ?{| + +name: ?string, + |}, + +name: ?string, + |}, + +name: ?string, + |}, +|}; +export type DataCheckerTestExecQuery = {| + response: DataCheckerTestExecQuery$data, + variables: DataCheckerTestExecQuery$variables, +|}; +*/ + +var node/*: ClientRequest*/ = (function(){ +var v0 = { + "args": null, + "kind": "FragmentSpread", + "name": "RelayReaderExecResolversTestUser__id" +}, +v1 = { + "args": null, + "kind": "FragmentSpread", + "name": "RelayReaderExecResolversTestUser____relay_model_instance" +}, +v2 = { + "name": "name", + "args": null, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": true, + "resolverInfo": { + "resolverFunction": require('../RelayReader-ExecResolvers-test').name, + "rootFragment": null + } +}, +v3 = { + "RelayReaderExecResolversTestUser": { + "resolverModule": require('../RelayReader-ExecResolvers-test').RelayReaderExecResolversTestUser + } +}, +v4 = { + "name": "best_friend", + "args": null, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": false, + "resolverInfo": { + "resolverFunction": require('../RelayReader-ExecResolvers-test').best_friend, + "rootFragment": null + } +}, +v5 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null +}; +return { + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": { + "hasClientEdges": true + }, + "name": "DataCheckerTestExecQuery", + "selections": [ + { + "kind": "ClientEdgeToClientObject", + "concreteType": "RelayReaderExecResolversTestUser", + "modelResolvers": { + "RelayReaderExecResolversTestUser": { + "alias": null, + "args": null, + "fragment": (v0/*: any*/), + "kind": "RelayResolver", + "name": "__relay_model_instance", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser__id.graphql'), require('../RelayReader-ExecResolvers-test').RelayReaderExecResolversTestUser, 'id', true), + "path": "RelayReaderExecResolversTest_user_one.__relay_model_instance" + } + }, + "backingField": { + "alias": null, + "args": null, + "fragment": null, + "kind": "RelayResolver", + "name": "RelayReaderExecResolversTest_user_one", + "resolverModule": require('../RelayReader-ExecResolvers-test').RelayReaderExecResolversTest_user_one, + "path": "RelayReaderExecResolversTest_user_one" + }, + "linkedField": { + "alias": null, + "args": null, + "concreteType": "RelayReaderExecResolversTestUser", + "kind": "LinkedField", + "name": "RelayReaderExecResolversTest_user_one", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "fragment": (v1/*: any*/), + "kind": "RelayResolver", + "name": "name", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser____relay_model_instance.graphql'), require('../RelayReader-ExecResolvers-test').name, '__relay_model_instance', true), + "path": "RelayReaderExecResolversTest_user_one.name" + }, + { + "kind": "ClientEdgeToClientObject", + "concreteType": "RelayReaderExecResolversTestUser", + "modelResolvers": { + "RelayReaderExecResolversTestUser": { + "alias": null, + "args": null, + "fragment": (v0/*: any*/), + "kind": "RelayResolver", + "name": "__relay_model_instance", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser__id.graphql'), require('../RelayReader-ExecResolvers-test').RelayReaderExecResolversTestUser, 'id', true), + "path": "RelayReaderExecResolversTest_user_one.best_friend.__relay_model_instance" + } + }, + "backingField": { + "alias": null, + "args": null, + "fragment": (v1/*: any*/), + "kind": "RelayResolver", + "name": "best_friend", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser____relay_model_instance.graphql'), require('../RelayReader-ExecResolvers-test').best_friend, '__relay_model_instance', true), + "path": "RelayReaderExecResolversTest_user_one.best_friend" + }, + "linkedField": { + "alias": null, + "args": null, + "concreteType": "RelayReaderExecResolversTestUser", + "kind": "LinkedField", + "name": "best_friend", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "fragment": (v1/*: any*/), + "kind": "RelayResolver", + "name": "name", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser____relay_model_instance.graphql'), require('../RelayReader-ExecResolvers-test').name, '__relay_model_instance', true), + "path": "RelayReaderExecResolversTest_user_one.best_friend.name" + }, + { + "kind": "ClientEdgeToClientObject", + "concreteType": "RelayReaderExecResolversTestUser", + "modelResolvers": { + "RelayReaderExecResolversTestUser": { + "alias": null, + "args": null, + "fragment": (v0/*: any*/), + "kind": "RelayResolver", + "name": "__relay_model_instance", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser__id.graphql'), require('../RelayReader-ExecResolvers-test').RelayReaderExecResolversTestUser, 'id', true), + "path": "RelayReaderExecResolversTest_user_one.best_friend.best_friend.__relay_model_instance" + } + }, + "backingField": { + "alias": null, + "args": null, + "fragment": (v1/*: any*/), + "kind": "RelayResolver", + "name": "best_friend", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser____relay_model_instance.graphql'), require('../RelayReader-ExecResolvers-test').best_friend, '__relay_model_instance', true), + "path": "RelayReaderExecResolversTest_user_one.best_friend.best_friend" + }, + "linkedField": { + "alias": null, + "args": null, + "concreteType": "RelayReaderExecResolversTestUser", + "kind": "LinkedField", + "name": "best_friend", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "fragment": (v1/*: any*/), + "kind": "RelayResolver", + "name": "name", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser____relay_model_instance.graphql'), require('../RelayReader-ExecResolvers-test').name, '__relay_model_instance', true), + "path": "RelayReaderExecResolversTest_user_one.best_friend.best_friend.name" + } + ], + "storageKey": null + } + } + ], + "storageKey": null + } + } + ], + "storageKey": null + } + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [], + "kind": "Operation", + "name": "DataCheckerTestExecQuery", + "selections": [ + { + "kind": "ClientEdgeToClientObject", + "modelResolvers": { + "RelayReaderExecResolversTestUser": { + "resolverModule": require('../RelayReader-ExecResolvers-test').RelayReaderExecResolversTestUser + } + }, + "backingField": { + "name": "RelayReaderExecResolversTest_user_one", + "args": null, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": false, + "resolverInfo": { + "resolverFunction": require('../RelayReader-ExecResolvers-test').RelayReaderExecResolversTest_user_one, + "rootFragment": null + } + }, + "linkedField": { + "alias": null, + "args": null, + "concreteType": "RelayReaderExecResolversTestUser", + "kind": "LinkedField", + "name": "RelayReaderExecResolversTest_user_one", + "plural": false, + "selections": [ + (v2/*: any*/), + { + "kind": "ClientEdgeToClientObject", + "modelResolvers": (v3/*: any*/), + "backingField": (v4/*: any*/), + "linkedField": { + "alias": null, + "args": null, + "concreteType": "RelayReaderExecResolversTestUser", + "kind": "LinkedField", + "name": "best_friend", + "plural": false, + "selections": [ + (v2/*: any*/), + { + "kind": "ClientEdgeToClientObject", + "modelResolvers": (v3/*: any*/), + "backingField": (v4/*: any*/), + "linkedField": { + "alias": null, + "args": null, + "concreteType": "RelayReaderExecResolversTestUser", + "kind": "LinkedField", + "name": "best_friend", + "plural": false, + "selections": [ + (v2/*: any*/), + (v5/*: any*/) + ], + "storageKey": null + } + }, + (v5/*: any*/) + ], + "storageKey": null + } + }, + (v5/*: any*/) + ], + "storageKey": null + } + } + ], + "use_exec_time_resolvers": true + }, + "params": { + "cacheID": "a997fdfcc0dc79ab47ceea5ffbd6c1cc", + "id": null, + "metadata": {}, + "name": "DataCheckerTestExecQuery", + "operationKind": "query", + "text": null + } +}; +})(); + +if (__DEV__) { + (node/*: any*/).hash = "c26914a95b9bc6165ea7513805119b77"; +} + +module.exports = ((node/*: any*/)/*: ClientQuery< + DataCheckerTestExecQuery$variables, + DataCheckerTestExecQuery$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/DataCheckerTestExecWithServerDataQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/DataCheckerTestExecWithServerDataQuery.graphql.js new file mode 100644 index 0000000000000..06cff792c946e --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/DataCheckerTestExecWithServerDataQuery.graphql.js @@ -0,0 +1,389 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<7af240671135c6959803e2a97db63e21>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ConcreteRequest, Query } from 'relay-runtime'; +import type { DataID } from "relay-runtime"; +import type { RelayReaderExecResolversTestUser____relay_model_instance$data } from "./RelayReaderExecResolversTestUser____relay_model_instance.graphql"; +import {RelayReaderExecResolversTest_user_one as queryRelayReaderExecResolversTestUserOneResolverType} from "../RelayReader-ExecResolvers-test.js"; +import type { TestResolverContextType } from "../../../mutations/__tests__/TestResolverContextType"; +// Type assertion validating that `queryRelayReaderExecResolversTestUserOneResolverType` resolver is correctly implemented. +// A type error here indicates that the type signature of the resolver module is incorrect. +(queryRelayReaderExecResolversTestUserOneResolverType: ( + args: void, + context: TestResolverContextType, +) => ?{| + +id: DataID, +|}); +import {best_friend as relayReaderExecResolversTestUserBestFriendResolverType} from "../RelayReader-ExecResolvers-test.js"; +// Type assertion validating that `relayReaderExecResolversTestUserBestFriendResolverType` resolver is correctly implemented. +// A type error here indicates that the type signature of the resolver module is incorrect. +(relayReaderExecResolversTestUserBestFriendResolverType: ( + __relay_model_instance: RelayReaderExecResolversTestUser____relay_model_instance$data['__relay_model_instance'], + args: void, + context: TestResolverContextType, +) => ?{| + +id: DataID, +|}); +import {name as relayReaderExecResolversTestUserNameResolverType} from "../RelayReader-ExecResolvers-test.js"; +// Type assertion validating that `relayReaderExecResolversTestUserNameResolverType` resolver is correctly implemented. +// A type error here indicates that the type signature of the resolver module is incorrect. +(relayReaderExecResolversTestUserNameResolverType: ( + __relay_model_instance: RelayReaderExecResolversTestUser____relay_model_instance$data['__relay_model_instance'], + args: void, + context: TestResolverContextType, +) => ?string); +export type DataCheckerTestExecWithServerDataQuery$variables = {||}; +export type DataCheckerTestExecWithServerDataQuery$data = {| + +RelayReaderExecResolversTest_user_one: ?{| + +best_friend: ?{| + +best_friend: ?{| + +name: ?string, + |}, + +name: ?string, + |}, + +name: ?string, + |}, + +me: ?{| + +name: ?string, + |}, +|}; +export type DataCheckerTestExecWithServerDataQuery = {| + response: DataCheckerTestExecWithServerDataQuery$data, + variables: DataCheckerTestExecWithServerDataQuery$variables, +|}; +*/ + +var node/*: ConcreteRequest*/ = (function(){ +var v0 = { + "args": null, + "kind": "FragmentSpread", + "name": "RelayReaderExecResolversTestUser__id" +}, +v1 = { + "args": null, + "kind": "FragmentSpread", + "name": "RelayReaderExecResolversTestUser____relay_model_instance" +}, +v2 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null +}, +v3 = { + "name": "name", + "args": null, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": true, + "resolverInfo": { + "resolverFunction": require('../RelayReader-ExecResolvers-test').name, + "rootFragment": null + } +}, +v4 = { + "RelayReaderExecResolversTestUser": { + "resolverModule": require('../RelayReader-ExecResolvers-test').RelayReaderExecResolversTestUser + } +}, +v5 = { + "name": "best_friend", + "args": null, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": false, + "resolverInfo": { + "resolverFunction": require('../RelayReader-ExecResolvers-test').best_friend, + "rootFragment": null + } +}, +v6 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null +}; +return { + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": { + "hasClientEdges": true + }, + "name": "DataCheckerTestExecWithServerDataQuery", + "selections": [ + { + "kind": "ClientEdgeToClientObject", + "concreteType": "RelayReaderExecResolversTestUser", + "modelResolvers": { + "RelayReaderExecResolversTestUser": { + "alias": null, + "args": null, + "fragment": (v0/*: any*/), + "kind": "RelayResolver", + "name": "__relay_model_instance", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser__id.graphql'), require('../RelayReader-ExecResolvers-test').RelayReaderExecResolversTestUser, 'id', true), + "path": "RelayReaderExecResolversTest_user_one.__relay_model_instance" + } + }, + "backingField": { + "alias": null, + "args": null, + "fragment": null, + "kind": "RelayResolver", + "name": "RelayReaderExecResolversTest_user_one", + "resolverModule": require('../RelayReader-ExecResolvers-test').RelayReaderExecResolversTest_user_one, + "path": "RelayReaderExecResolversTest_user_one" + }, + "linkedField": { + "alias": null, + "args": null, + "concreteType": "RelayReaderExecResolversTestUser", + "kind": "LinkedField", + "name": "RelayReaderExecResolversTest_user_one", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "fragment": (v1/*: any*/), + "kind": "RelayResolver", + "name": "name", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser____relay_model_instance.graphql'), require('../RelayReader-ExecResolvers-test').name, '__relay_model_instance', true), + "path": "RelayReaderExecResolversTest_user_one.name" + }, + { + "kind": "ClientEdgeToClientObject", + "concreteType": "RelayReaderExecResolversTestUser", + "modelResolvers": { + "RelayReaderExecResolversTestUser": { + "alias": null, + "args": null, + "fragment": (v0/*: any*/), + "kind": "RelayResolver", + "name": "__relay_model_instance", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser__id.graphql'), require('../RelayReader-ExecResolvers-test').RelayReaderExecResolversTestUser, 'id', true), + "path": "RelayReaderExecResolversTest_user_one.best_friend.__relay_model_instance" + } + }, + "backingField": { + "alias": null, + "args": null, + "fragment": (v1/*: any*/), + "kind": "RelayResolver", + "name": "best_friend", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser____relay_model_instance.graphql'), require('../RelayReader-ExecResolvers-test').best_friend, '__relay_model_instance', true), + "path": "RelayReaderExecResolversTest_user_one.best_friend" + }, + "linkedField": { + "alias": null, + "args": null, + "concreteType": "RelayReaderExecResolversTestUser", + "kind": "LinkedField", + "name": "best_friend", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "fragment": (v1/*: any*/), + "kind": "RelayResolver", + "name": "name", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser____relay_model_instance.graphql'), require('../RelayReader-ExecResolvers-test').name, '__relay_model_instance', true), + "path": "RelayReaderExecResolversTest_user_one.best_friend.name" + }, + { + "kind": "ClientEdgeToClientObject", + "concreteType": "RelayReaderExecResolversTestUser", + "modelResolvers": { + "RelayReaderExecResolversTestUser": { + "alias": null, + "args": null, + "fragment": (v0/*: any*/), + "kind": "RelayResolver", + "name": "__relay_model_instance", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser__id.graphql'), require('../RelayReader-ExecResolvers-test').RelayReaderExecResolversTestUser, 'id', true), + "path": "RelayReaderExecResolversTest_user_one.best_friend.best_friend.__relay_model_instance" + } + }, + "backingField": { + "alias": null, + "args": null, + "fragment": (v1/*: any*/), + "kind": "RelayResolver", + "name": "best_friend", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser____relay_model_instance.graphql'), require('../RelayReader-ExecResolvers-test').best_friend, '__relay_model_instance', true), + "path": "RelayReaderExecResolversTest_user_one.best_friend.best_friend" + }, + "linkedField": { + "alias": null, + "args": null, + "concreteType": "RelayReaderExecResolversTestUser", + "kind": "LinkedField", + "name": "best_friend", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "fragment": (v1/*: any*/), + "kind": "RelayResolver", + "name": "name", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser____relay_model_instance.graphql'), require('../RelayReader-ExecResolvers-test').name, '__relay_model_instance', true), + "path": "RelayReaderExecResolversTest_user_one.best_friend.best_friend.name" + } + ], + "storageKey": null + } + } + ], + "storageKey": null + } + } + ], + "storageKey": null + } + }, + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "me", + "plural": false, + "selections": [ + (v2/*: any*/) + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [], + "kind": "Operation", + "name": "DataCheckerTestExecWithServerDataQuery", + "selections": [ + { + "kind": "ClientEdgeToClientObject", + "modelResolvers": { + "RelayReaderExecResolversTestUser": { + "resolverModule": require('../RelayReader-ExecResolvers-test').RelayReaderExecResolversTestUser + } + }, + "backingField": { + "name": "RelayReaderExecResolversTest_user_one", + "args": null, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": false, + "resolverInfo": { + "resolverFunction": require('../RelayReader-ExecResolvers-test').RelayReaderExecResolversTest_user_one, + "rootFragment": null + } + }, + "linkedField": { + "alias": null, + "args": null, + "concreteType": "RelayReaderExecResolversTestUser", + "kind": "LinkedField", + "name": "RelayReaderExecResolversTest_user_one", + "plural": false, + "selections": [ + (v3/*: any*/), + { + "kind": "ClientEdgeToClientObject", + "modelResolvers": (v4/*: any*/), + "backingField": (v5/*: any*/), + "linkedField": { + "alias": null, + "args": null, + "concreteType": "RelayReaderExecResolversTestUser", + "kind": "LinkedField", + "name": "best_friend", + "plural": false, + "selections": [ + (v3/*: any*/), + { + "kind": "ClientEdgeToClientObject", + "modelResolvers": (v4/*: any*/), + "backingField": (v5/*: any*/), + "linkedField": { + "alias": null, + "args": null, + "concreteType": "RelayReaderExecResolversTestUser", + "kind": "LinkedField", + "name": "best_friend", + "plural": false, + "selections": [ + (v3/*: any*/), + (v6/*: any*/) + ], + "storageKey": null + } + }, + (v6/*: any*/) + ], + "storageKey": null + } + }, + (v6/*: any*/) + ], + "storageKey": null + } + }, + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "me", + "plural": false, + "selections": [ + (v2/*: any*/), + (v6/*: any*/) + ], + "storageKey": null + } + ], + "use_exec_time_resolvers": true + }, + "params": { + "cacheID": "ff49275f22dc118a4a5554e2bb81b93a", + "id": null, + "metadata": {}, + "name": "DataCheckerTestExecWithServerDataQuery", + "operationKind": "query", + "text": "query DataCheckerTestExecWithServerDataQuery {\n me {\n name\n id\n }\n}\n" + } +}; +})(); + +if (__DEV__) { + (node/*: any*/).hash = "6e8ef036cc7105493415758b86368fe7"; +} + +module.exports = ((node/*: any*/)/*: Query< + DataCheckerTestExecWithServerDataQuery$variables, + DataCheckerTestExecWithServerDataQuery$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/RefetchableClientEdgeQuery_RelayReaderClientEdgesTest4Query_me__client_edge.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RefetchableClientEdgeQuery_RelayReaderClientEdgesTest4Query_me__client_edge.graphql.js index fcf97ccc1eeaa..009e1c356a1db 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RefetchableClientEdgeQuery_RelayReaderClientEdgesTest4Query_me__client_edge.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RefetchableClientEdgeQuery_RelayReaderClientEdgesTest4Query_me__client_edge.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -79,7 +79,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "another_client_edge", - "resolverModule": require('./../resolvers/UserAnotherClientEdgeResolver').another_client_edge, + "resolverModule": require('../resolvers/UserAnotherClientEdgeResolver').another_client_edge, "path": "another_client_edge" }, "linkedField": { diff --git a/packages/relay-runtime/store/__tests__/__generated__/RefetchableClientEdgeQuery_observeQueryTestClientEdgeToServerQuery_me__client_edge.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RefetchableClientEdgeQuery_observeQueryTestClientEdgeToServerQuery_me__client_edge.graphql.js new file mode 100644 index 0000000000000..1239fb8b8fd77 --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/RefetchableClientEdgeQuery_observeQueryTestClientEdgeToServerQuery_me__client_edge.graphql.js @@ -0,0 +1,81 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<226a0cdb23eb7b5c221a6700e57968fb>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ReaderFragment, RefetchableFragment } from 'relay-runtime'; +import type { FragmentType } from "relay-runtime"; +declare export opaque type RefetchableClientEdgeQuery_observeQueryTestClientEdgeToServerQuery_me__client_edge$fragmentType: FragmentType; +type ClientEdgeQuery_observeQueryTestClientEdgeToServerQuery_me__client_edge$variables = any; +export type RefetchableClientEdgeQuery_observeQueryTestClientEdgeToServerQuery_me__client_edge$data = {| + +id: string, + +name: ?string, + +$fragmentType: RefetchableClientEdgeQuery_observeQueryTestClientEdgeToServerQuery_me__client_edge$fragmentType, +|}; +export type RefetchableClientEdgeQuery_observeQueryTestClientEdgeToServerQuery_me__client_edge$key = { + +$data?: RefetchableClientEdgeQuery_observeQueryTestClientEdgeToServerQuery_me__client_edge$data, + +$fragmentSpreads: RefetchableClientEdgeQuery_observeQueryTestClientEdgeToServerQuery_me__client_edge$fragmentType, + ... +}; +*/ + +var node/*: ReaderFragment*/ = { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": { + "refetch": { + "connection": null, + "fragmentPathInResult": [ + "node" + ], + "operation": require('./ClientEdgeQuery_observeQueryTestClientEdgeToServerQuery_me__client_edge.graphql'), + "identifierInfo": { + "identifierField": "id", + "identifierQueryVariableName": "id" + } + } + }, + "name": "RefetchableClientEdgeQuery_observeQueryTestClientEdgeToServerQuery_me__client_edge", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + } + ], + "type": "User", + "abstractKey": null +}; + +if (__DEV__) { + (node/*: any*/).hash = "ffa93f1454a0796bf5a92612348c1069"; +} + +module.exports = ((node/*: any*/)/*: RefetchableFragment< + RefetchableClientEdgeQuery_observeQueryTestClientEdgeToServerQuery_me__client_edge$fragmentType, + RefetchableClientEdgeQuery_observeQueryTestClientEdgeToServerQuery_me__client_edge$data, + ClientEdgeQuery_observeQueryTestClientEdgeToServerQuery_me__client_edge$variables, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayExperimentalGraphResponseTransformTestConditionQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayExperimentalGraphResponseTransformTestConditionQuery.graphql.js index a0062840650e7..0b942696dcaac 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayExperimentalGraphResponseTransformTestConditionQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayExperimentalGraphResponseTransformTestConditionQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<9eccd1713096134c15cd489f9e9fe697>> * @flow * @lightSyntaxTransform * @nogrep @@ -156,7 +156,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "6d02d262365a960c063af8b116377d2a"; + (node/*: any*/).hash = "43f766e8c0e9e4f2e6f15baa96299bd7"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayExperimentalGraphResponseTransformTestFragmentSpreadNoInlineQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayExperimentalGraphResponseTransformTestFragmentSpreadNoInlineQuery.graphql.js index e71d4311115e1..a5c372960777c 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayExperimentalGraphResponseTransformTestFragmentSpreadNoInlineQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayExperimentalGraphResponseTransformTestFragmentSpreadNoInlineQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<60953e8f5be888efc4ef6882def9344f>> * @flow * @lightSyntaxTransform * @nogrep @@ -116,7 +116,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "fbfe5e49950cb623a46a3c6e4377ee0c"; + (node/*: any*/).hash = "c757231a3cc1f2f44b4d7cee9b204962"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayExperimentalGraphResponseTransformTestFragmentSpreadQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayExperimentalGraphResponseTransformTestFragmentSpreadQuery.graphql.js index a4f744cf9ac30..11f10dbc5eeec 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayExperimentalGraphResponseTransformTestFragmentSpreadQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayExperimentalGraphResponseTransformTestFragmentSpreadQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<8d2c652e3c2d567f03599f65dc3440b9>> + * @generated SignedSource<<7b62202591e6eb6fbb653a813be7e9b1>> * @flow * @lightSyntaxTransform * @nogrep @@ -125,7 +125,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "f90934a2aacb7ebd18141c401c9db833"; + (node/*: any*/).hash = "106c74251d41691aaeeadf9000b01b0b"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentApplyMutationTest1Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentApplyMutationTest1Query.graphql.js index 35bee013f5508..072d07e9708b8 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentApplyMutationTest1Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentApplyMutationTest1Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<24b2e65a3906df27c6360e61e8684c70>> + * @generated SignedSource<<1aec509db4488a518078481b3df62723>> * @flow * @lightSyntaxTransform * @nogrep @@ -148,7 +148,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "cae70132f7b7d25f52ad8f7c8aa563f2"; + (node/*: any*/).hash = "96c07bb47444d2b07d42e670403f73e1"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentConnectionAndRequiredTestFeedbackQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentConnectionAndRequiredTestFeedbackQuery.graphql.js index 4341dea7d4267..7e46dbe28831e 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentConnectionAndRequiredTestFeedbackQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentConnectionAndRequiredTestFeedbackQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<482934cded369211b5d8c3468652e5d9>> * @flow * @lightSyntaxTransform * @nogrep @@ -219,7 +219,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "c71f667104383509795bb812551f3700"; + (node/*: any*/).hash = "97edae9a405d1bc18c11c249c60d9972"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentConnectionTestFeedbackQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentConnectionTestFeedbackQuery.graphql.js index 137e84746bc39..ea19b4e2a27bb 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentConnectionTestFeedbackQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentConnectionTestFeedbackQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<35bfd5bd4610bca89d68da74240b1135>> * @flow * @lightSyntaxTransform * @nogrep @@ -219,7 +219,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "2ab9d05a46f4c1a555cdbc247557cb8a"; + (node/*: any*/).hash = "4f550a20c8be8b72e6a11173729f2f0d"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentConnectionTestPaginationQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentConnectionTestPaginationQuery.graphql.js index 832cfde5ad974..d8d731b2b53f2 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentConnectionTestPaginationQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentConnectionTestPaginationQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<1092790224fe96fae775da11bd2b74b7>> * @flow * @lightSyntaxTransform * @nogrep @@ -253,7 +253,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "a076190aa4106d4efc94c7044acda9d3"; + (node/*: any*/).hash = "3c7371dcfecf7c2f23845a4381e25223"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentDynamicConnectionKeyTestFeedbackQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentDynamicConnectionKeyTestFeedbackQuery.graphql.js index f48ff1378f07b..12c98358d9d37 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentDynamicConnectionKeyTestFeedbackQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentDynamicConnectionKeyTestFeedbackQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<6df53546f89a0e3fe557a85da970ddde>> * @flow * @lightSyntaxTransform * @nogrep @@ -234,7 +234,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "93a8dd6942de5fbbc353f66e3f7d0dc6"; + (node/*: any*/).hash = "b57fa82fe4a28fee879d610ee77676d0"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentDynamicConnectionKeyTestPaginationQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentDynamicConnectionKeyTestPaginationQuery.graphql.js index 62297ac3da331..37f058999e043 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentDynamicConnectionKeyTestPaginationQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentDynamicConnectionKeyTestPaginationQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<3f41e8f77728614235040452435a09e0>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -266,7 +266,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "f1f26c129bc5e5ba17abf584fb161608"; + (node/*: any*/).hash = "2548fae49c8b458eaff80fd9f0b9fe02"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteMutationTestCommentQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteMutationTestCommentQuery.graphql.js index 5f12a3417887f..8fd8233462f3e 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteMutationTestCommentQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteMutationTestCommentQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<77de5013d91b19b8444a8a7e66ccbc54>> * @flow * @lightSyntaxTransform * @nogrep @@ -148,7 +148,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "cf4dd571769bdce0442d52bbcf1043b1"; + (node/*: any*/).hash = "525f90d51bcf7f9c6f2ee29ca2aee518"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteMutationWithGlobalInvalidationTestCommentQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteMutationWithGlobalInvalidationTestCommentQuery.graphql.js index 314f32f77acd6..cc77228d615d5 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteMutationWithGlobalInvalidationTestCommentQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteMutationWithGlobalInvalidationTestCommentQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<6cba849fbd6297647b406fe60c204681>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -148,7 +148,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "7f5195e44dbcc52111a870ea10a689a9"; + (node/*: any*/).hash = "c1fd73322dacd90462ec736c1a7e75a4"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteMutationWithLocalInvalidationTestCommentQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteMutationWithLocalInvalidationTestCommentQuery.graphql.js index 76f025d7c9806..ae9d7fcec8f11 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteMutationWithLocalInvalidationTestCommentQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteMutationWithLocalInvalidationTestCommentQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<9f105fc814f498470d1e2aae145d616c>> + * @generated SignedSource<<0a5999522e6127ab9a1aab5f7468d7bb>> * @flow * @lightSyntaxTransform * @nogrep @@ -148,7 +148,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "7ea2a20a76665e224878c2c3c64ca7af"; + (node/*: any*/).hash = "01697c8ccf4980cfb1a655bad4327f7d"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteMutationWithMatchTestCommentQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteMutationWithMatchTestCommentQuery.graphql.js index 68c5c953d36f4..760258ffe56ec 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteMutationWithMatchTestCommentQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteMutationWithMatchTestCommentQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<971fe46e359244d6b6f6b67ef992985e>> + * @generated SignedSource<<9f89469fc7b0e2e684fd5a8576302ccd>> * @flow * @lightSyntaxTransform * @nogrep @@ -199,7 +199,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "85aee16ac1e83847bd25d209521afc27"; + (node/*: any*/).hash = "4c4a8a3b814b6d49815f670d2296d1ff"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteSubscriptionTestCommentQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteSubscriptionTestCommentQuery.graphql.js index 429fb6f64dfa0..c22a7febbbd9e 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteSubscriptionTestCommentQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteSubscriptionTestCommentQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<72188650656e03e287d0ee66de681e42>> * @flow * @lightSyntaxTransform * @nogrep @@ -148,7 +148,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "19ec2bd10802d7b3486c8eacf7bc0e37"; + (node/*: any*/).hash = "0d4b8e1bf2b61e9a7c5c448f5737a807"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteSubscriptionWithDeferTestCommentQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteSubscriptionWithDeferTestCommentQuery.graphql.js index 3f358063295dd..d97b2079bb423 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteSubscriptionWithDeferTestCommentQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteSubscriptionWithDeferTestCommentQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -160,7 +160,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "4e3ef6893b11d6b8e50305bb90fa383d"; + (node/*: any*/).hash = "8c04dd87122dd2f2e6d81c26c26b2e1e"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteSubscriptionWithMatchTestCommentQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteSubscriptionWithMatchTestCommentQuery.graphql.js index be1655b720e85..5c2f644281113 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteSubscriptionWithMatchTestCommentQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteSubscriptionWithMatchTestCommentQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<37d87a29127c66506f4f587bbf3ce34b>> + * @generated SignedSource<<084c6feb9c37b0d0fcf2519ec85db7a9>> * @flow * @lightSyntaxTransform * @nogrep @@ -199,7 +199,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "aee303d0628e74c59aab556254f67057"; + (node/*: any*/).hash = "6867c6d5b6b3b26fbbd58906a450e38c"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteSubscriptionWithStreamTestFeedbackQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteSubscriptionWithStreamTestFeedbackQuery.graphql.js index e05dc416273e8..ec1e6bf968b33 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteSubscriptionWithStreamTestFeedbackQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteSubscriptionWithStreamTestFeedbackQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<82a1b78ff2ee6768bd6e08fb87d7dd91>> * @flow * @lightSyntaxTransform * @nogrep @@ -167,7 +167,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "1e9ff23e175d055a8cf9262725de7afa"; + (node/*: any*/).hash = "6a889db656eb4f7bedb8485781900af3"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithCheckTestQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithCheckTestQuery.graphql.js new file mode 100644 index 0000000000000..94d47b93c7f98 --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithCheckTestQuery.graphql.js @@ -0,0 +1,154 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<0b5f3fbd1ff42a527c41fa99087ac16c>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ConcreteRequest, Query } from 'relay-runtime'; +export type RelayModernEnvironmentExecuteWithCheckTestQuery$variables = {| + fetchSize: boolean, +|}; +export type RelayModernEnvironmentExecuteWithCheckTestQuery$data = {| + +me: ?{| + +name: ?string, + +profilePicture?: ?{| + +uri: ?string, + |}, + |}, +|}; +export type RelayModernEnvironmentExecuteWithCheckTestQuery = {| + response: RelayModernEnvironmentExecuteWithCheckTestQuery$data, + variables: RelayModernEnvironmentExecuteWithCheckTestQuery$variables, +|}; +*/ + +var node/*: ConcreteRequest*/ = (function(){ +var v0 = [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "fetchSize" + } +], +v1 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null +}, +v2 = { + "condition": "fetchSize", + "kind": "Condition", + "passingValue": true, + "selections": [ + { + "alias": null, + "args": [ + { + "kind": "Literal", + "name": "size", + "value": 42 + } + ], + "concreteType": "Image", + "kind": "LinkedField", + "name": "profilePicture", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "uri", + "storageKey": null + } + ], + "storageKey": "profilePicture(size:42)" + } + ] +}; +return { + "fragment": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Fragment", + "metadata": null, + "name": "RelayModernEnvironmentExecuteWithCheckTestQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "me", + "plural": false, + "selections": [ + (v1/*: any*/), + (v2/*: any*/) + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Operation", + "name": "RelayModernEnvironmentExecuteWithCheckTestQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "me", + "plural": false, + "selections": [ + (v1/*: any*/), + (v2/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "5dcc671b805f5953c02f9d662ee151ef", + "id": null, + "metadata": {}, + "name": "RelayModernEnvironmentExecuteWithCheckTestQuery", + "operationKind": "query", + "text": "query RelayModernEnvironmentExecuteWithCheckTestQuery(\n $fetchSize: Boolean!\n) {\n me {\n name\n profilePicture(size: 42) @include(if: $fetchSize) {\n uri\n }\n id\n }\n}\n" + } +}; +})(); + +if (__DEV__) { + (node/*: any*/).hash = "abc9670c9c2810d76f7450bfcf6cd79e"; +} + +module.exports = ((node/*: any*/)/*: Query< + RelayModernEnvironmentExecuteWithCheckTestQuery$variables, + RelayModernEnvironmentExecuteWithCheckTestQuery$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInner2UserFragment.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInner2UserFragment.graphql.js new file mode 100644 index 0000000000000..d37b293658540 --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInner2UserFragment.graphql.js @@ -0,0 +1,59 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<1224bc9882f8bdc2a7678dc7d8096cdc>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { Fragment, ReaderFragment } from 'relay-runtime'; +import type { FragmentType } from "relay-runtime"; +declare export opaque type RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInner2UserFragment$fragmentType: FragmentType; +export type RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInner2UserFragment$data = {| + +lastName: ?string, + +$fragmentType: RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInner2UserFragment$fragmentType, +|}; +export type RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInner2UserFragment$key = { + +$data?: RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInner2UserFragment$data, + +$fragmentSpreads: RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInner2UserFragment$fragmentType, + ... +}; +*/ + +var node/*: ReaderFragment*/ = { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInner2UserFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "lastName", + "storageKey": null + } + ], + "type": "User", + "abstractKey": null +}; + +if (__DEV__) { + (node/*: any*/).hash = "250fcb30498857f0c3b233255aa0e57b"; +} + +module.exports = ((node/*: any*/)/*: Fragment< + RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInner2UserFragment$fragmentType, + RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInner2UserFragment$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInnerUserFragment.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInnerUserFragment.graphql.js new file mode 100644 index 0000000000000..b5fe0060044d0 --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInnerUserFragment.graphql.js @@ -0,0 +1,71 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { Fragment, ReaderFragment } from 'relay-runtime'; +import type { RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInner2UserFragment$fragmentType } from "./RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInner2UserFragment.graphql"; +import type { FragmentType } from "relay-runtime"; +declare export opaque type RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInnerUserFragment$fragmentType: FragmentType; +export type RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInnerUserFragment$data = {| + +name: ?string, + +$fragmentSpreads: RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInner2UserFragment$fragmentType, + +$fragmentType: RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInnerUserFragment$fragmentType, +|}; +export type RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInnerUserFragment$key = { + +$data?: RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInnerUserFragment$data, + +$fragmentSpreads: RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInnerUserFragment$fragmentType, + ... +}; +*/ + +var node/*: ReaderFragment*/ = { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInnerUserFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + }, + { + "kind": "Defer", + "selections": [ + { + "args": null, + "kind": "FragmentSpread", + "name": "RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInner2UserFragment" + } + ] + } + ], + "type": "User", + "abstractKey": null +}; + +if (__DEV__) { + (node/*: any*/).hash = "5bc0a65c0dd61e3b1be27f59fcd9bb36"; +} + +module.exports = ((node/*: any*/)/*: Fragment< + RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInnerUserFragment$fragmentType, + RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInnerUserFragment$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery.graphql.js new file mode 100644 index 0000000000000..8bd0d2f856431 --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery.graphql.js @@ -0,0 +1,154 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<6d1ca0804e52b8e33179e46bc5e6343f>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +// @dataDrivenDependency RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery.node {"branches":{"User":{"component":"User.react","fragment":"RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery_user$normalization.graphql"}},"plural":false} + +/*:: +import type { ConcreteRequest, Query } from 'relay-runtime'; +import type { RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery_user$fragmentType } from "./RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery_user.graphql"; +export type RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery$variables = {| + id: string, +|}; +export type RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery$data = {| + +node: ?{| + +__fragmentPropName?: ?string, + +__module_component?: ?string, + +$fragmentSpreads: RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery_user$fragmentType, + |}, +|}; +export type RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery = {| + response: RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery$data, + variables: RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery$variables, +|}; +*/ + +var node/*: ConcreteRequest*/ = (function(){ +var v0 = [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "id" + } +], +v1 = [ + { + "kind": "Variable", + "name": "id", + "variableName": "id" + } +], +v2 = [ + { + "kind": "InlineFragment", + "selections": [ + { + "args": null, + "documentName": "RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery", + "fragmentName": "RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery_user", + "fragmentPropName": "user", + "kind": "ModuleImport" + } + ], + "type": "User", + "abstractKey": null + } +]; +return { + "fragment": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Fragment", + "metadata": null, + "name": "RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery", + "selections": [ + { + "alias": null, + "args": (v1/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + { + "kind": "Defer", + "selections": (v2/*: any*/) + } + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Operation", + "name": "RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery", + "selections": [ + { + "alias": null, + "args": (v1/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null + }, + { + "if": null, + "kind": "Defer", + "label": "RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery$defer$RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery_user", + "selections": (v2/*: any*/) + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "a16923b5bc118000c7d645a32ed29c3b", + "id": null, + "metadata": {}, + "name": "RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery", + "operationKind": "query", + "text": "query RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery(\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ... @defer(label: \"RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery$defer$RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery_user\") {\n ... on User {\n ...RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery_user\n __module_operation_RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery: js(module: \"RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery_user$normalization.graphql\", id: \"RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery.node\")\n __module_component_RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery: js(module: \"User.react\", id: \"RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery.node\")\n }\n }\n id\n }\n}\n\nfragment RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInner2UserFragment on User {\n lastName\n}\n\nfragment RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInnerUserFragment on User {\n name\n ...RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInner2UserFragment @defer(label: \"RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInnerUserFragment$defer$RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInner2UserFragment\")\n}\n\nfragment RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery_user on User {\n id\n ...RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInnerUserFragment @defer(label: \"RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery_user$defer$RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInnerUserFragment\")\n}\n" + } +}; +})(); + +if (__DEV__) { + (node/*: any*/).hash = "68b0b6cd9b9dfde4d6098cd27348c45f"; +} + +module.exports = ((node/*: any*/)/*: Query< + RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery$variables, + RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery_user$normalization.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery_user$normalization.graphql.js new file mode 100644 index 0000000000000..161ace0956a24 --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery_user$normalization.graphql.js @@ -0,0 +1,71 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<1241bb19782d20a294c2136151af1872>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { NormalizationSplitOperation } from 'relay-runtime'; + +*/ + +var node/*: NormalizationSplitOperation*/ = { + "kind": "SplitOperation", + "metadata": {}, + "name": "RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery_user$normalization", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + }, + { + "if": null, + "kind": "Defer", + "label": "RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery_user$defer$RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInnerUserFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + }, + { + "if": null, + "kind": "Defer", + "label": "RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInnerUserFragment$defer$RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInner2UserFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "lastName", + "storageKey": null + } + ] + } + ] + } + ] +}; + +if (__DEV__) { + (node/*: any*/).hash = "e6803e7a0a81e2b0acd328649b47099a"; +} + +module.exports = node; diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery_user.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery_user.graphql.js new file mode 100644 index 0000000000000..092619ee8d313 --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery_user.graphql.js @@ -0,0 +1,71 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<87fc263ce85a725a19ce73b30df01401>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { Fragment, ReaderFragment } from 'relay-runtime'; +import type { RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInnerUserFragment$fragmentType } from "./RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInnerUserFragment.graphql"; +import type { FragmentType } from "relay-runtime"; +declare export opaque type RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery_user$fragmentType: FragmentType; +export type RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery_user$data = {| + +id: string, + +$fragmentSpreads: RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInnerUserFragment$fragmentType, + +$fragmentType: RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery_user$fragmentType, +|}; +export type RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery_user$key = { + +$data?: RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery_user$data, + +$fragmentSpreads: RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery_user$fragmentType, + ... +}; +*/ + +var node/*: ReaderFragment*/ = { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery_user", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + }, + { + "kind": "Defer", + "selections": [ + { + "args": null, + "kind": "FragmentSpread", + "name": "RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedInnerUserFragment" + } + ] + } + ], + "type": "User", + "abstractKey": null +}; + +if (__DEV__) { + (node/*: any*/).hash = "e6803e7a0a81e2b0acd328649b47099a"; +} + +module.exports = ((node/*: any*/)/*: Fragment< + RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery_user$fragmentType, + RelayModernEnvironmentExecuteWithDeferAndModuleTestNestedQuery_user$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithDeferTestNestedInnerInner2UserFragment.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithDeferTestNestedInnerInner2UserFragment.graphql.js new file mode 100644 index 0000000000000..213a1cd1679b1 --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithDeferTestNestedInnerInner2UserFragment.graphql.js @@ -0,0 +1,59 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { Fragment, ReaderFragment } from 'relay-runtime'; +import type { FragmentType } from "relay-runtime"; +declare export opaque type RelayModernEnvironmentExecuteWithDeferTestNestedInnerInner2UserFragment$fragmentType: FragmentType; +export type RelayModernEnvironmentExecuteWithDeferTestNestedInnerInner2UserFragment$data = {| + +lastName: ?string, + +$fragmentType: RelayModernEnvironmentExecuteWithDeferTestNestedInnerInner2UserFragment$fragmentType, +|}; +export type RelayModernEnvironmentExecuteWithDeferTestNestedInnerInner2UserFragment$key = { + +$data?: RelayModernEnvironmentExecuteWithDeferTestNestedInnerInner2UserFragment$data, + +$fragmentSpreads: RelayModernEnvironmentExecuteWithDeferTestNestedInnerInner2UserFragment$fragmentType, + ... +}; +*/ + +var node/*: ReaderFragment*/ = { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "RelayModernEnvironmentExecuteWithDeferTestNestedInnerInner2UserFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "lastName", + "storageKey": null + } + ], + "type": "User", + "abstractKey": null +}; + +if (__DEV__) { + (node/*: any*/).hash = "701e0240757ebe2212402e86773bdfeb"; +} + +module.exports = ((node/*: any*/)/*: Fragment< + RelayModernEnvironmentExecuteWithDeferTestNestedInnerInner2UserFragment$fragmentType, + RelayModernEnvironmentExecuteWithDeferTestNestedInnerInner2UserFragment$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithDeferTestNestedInnerUserFragment.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithDeferTestNestedInnerUserFragment.graphql.js new file mode 100644 index 0000000000000..9421c339d9a3b --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithDeferTestNestedInnerUserFragment.graphql.js @@ -0,0 +1,71 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<94e69170c00d3eac10df3669abe39d29>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { Fragment, ReaderFragment } from 'relay-runtime'; +import type { RelayModernEnvironmentExecuteWithDeferTestNestedInnerInner2UserFragment$fragmentType } from "./RelayModernEnvironmentExecuteWithDeferTestNestedInnerInner2UserFragment.graphql"; +import type { FragmentType } from "relay-runtime"; +declare export opaque type RelayModernEnvironmentExecuteWithDeferTestNestedInnerUserFragment$fragmentType: FragmentType; +export type RelayModernEnvironmentExecuteWithDeferTestNestedInnerUserFragment$data = {| + +name: ?string, + +$fragmentSpreads: RelayModernEnvironmentExecuteWithDeferTestNestedInnerInner2UserFragment$fragmentType, + +$fragmentType: RelayModernEnvironmentExecuteWithDeferTestNestedInnerUserFragment$fragmentType, +|}; +export type RelayModernEnvironmentExecuteWithDeferTestNestedInnerUserFragment$key = { + +$data?: RelayModernEnvironmentExecuteWithDeferTestNestedInnerUserFragment$data, + +$fragmentSpreads: RelayModernEnvironmentExecuteWithDeferTestNestedInnerUserFragment$fragmentType, + ... +}; +*/ + +var node/*: ReaderFragment*/ = { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "RelayModernEnvironmentExecuteWithDeferTestNestedInnerUserFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + }, + { + "kind": "Defer", + "selections": [ + { + "args": null, + "kind": "FragmentSpread", + "name": "RelayModernEnvironmentExecuteWithDeferTestNestedInnerInner2UserFragment" + } + ] + } + ], + "type": "User", + "abstractKey": null +}; + +if (__DEV__) { + (node/*: any*/).hash = "5286c825004180b1a4eeac0e404602c4"; +} + +module.exports = ((node/*: any*/)/*: Fragment< + RelayModernEnvironmentExecuteWithDeferTestNestedInnerUserFragment$fragmentType, + RelayModernEnvironmentExecuteWithDeferTestNestedInnerUserFragment$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithDeferTestNestedUserFragment.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithDeferTestNestedUserFragment.graphql.js new file mode 100644 index 0000000000000..4b334eb1c592a --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithDeferTestNestedUserFragment.graphql.js @@ -0,0 +1,71 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { Fragment, ReaderFragment } from 'relay-runtime'; +import type { RelayModernEnvironmentExecuteWithDeferTestNestedInnerUserFragment$fragmentType } from "./RelayModernEnvironmentExecuteWithDeferTestNestedInnerUserFragment.graphql"; +import type { FragmentType } from "relay-runtime"; +declare export opaque type RelayModernEnvironmentExecuteWithDeferTestNestedUserFragment$fragmentType: FragmentType; +export type RelayModernEnvironmentExecuteWithDeferTestNestedUserFragment$data = {| + +id: string, + +$fragmentSpreads: RelayModernEnvironmentExecuteWithDeferTestNestedInnerUserFragment$fragmentType, + +$fragmentType: RelayModernEnvironmentExecuteWithDeferTestNestedUserFragment$fragmentType, +|}; +export type RelayModernEnvironmentExecuteWithDeferTestNestedUserFragment$key = { + +$data?: RelayModernEnvironmentExecuteWithDeferTestNestedUserFragment$data, + +$fragmentSpreads: RelayModernEnvironmentExecuteWithDeferTestNestedUserFragment$fragmentType, + ... +}; +*/ + +var node/*: ReaderFragment*/ = { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "RelayModernEnvironmentExecuteWithDeferTestNestedUserFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + }, + { + "kind": "Defer", + "selections": [ + { + "args": null, + "kind": "FragmentSpread", + "name": "RelayModernEnvironmentExecuteWithDeferTestNestedInnerUserFragment" + } + ] + } + ], + "type": "User", + "abstractKey": null +}; + +if (__DEV__) { + (node/*: any*/).hash = "c4051f6b233af6bcc5106d1160dac7db"; +} + +module.exports = ((node/*: any*/)/*: Fragment< + RelayModernEnvironmentExecuteWithDeferTestNestedUserFragment$fragmentType, + RelayModernEnvironmentExecuteWithDeferTestNestedUserFragment$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithDeferTestNestedUserQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithDeferTestNestedUserQuery.graphql.js new file mode 100644 index 0000000000000..65fc8a73fde7e --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithDeferTestNestedUserQuery.graphql.js @@ -0,0 +1,178 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ConcreteRequest, Query } from 'relay-runtime'; +import type { RelayModernEnvironmentExecuteWithDeferTestNestedUserFragment$fragmentType } from "./RelayModernEnvironmentExecuteWithDeferTestNestedUserFragment.graphql"; +export type RelayModernEnvironmentExecuteWithDeferTestNestedUserQuery$variables = {| + id: string, +|}; +export type RelayModernEnvironmentExecuteWithDeferTestNestedUserQuery$data = {| + +node: ?{| + +$fragmentSpreads: RelayModernEnvironmentExecuteWithDeferTestNestedUserFragment$fragmentType, + |}, +|}; +export type RelayModernEnvironmentExecuteWithDeferTestNestedUserQuery = {| + response: RelayModernEnvironmentExecuteWithDeferTestNestedUserQuery$data, + variables: RelayModernEnvironmentExecuteWithDeferTestNestedUserQuery$variables, +|}; +*/ + +var node/*: ConcreteRequest*/ = (function(){ +var v0 = [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "id" + } +], +v1 = [ + { + "kind": "Variable", + "name": "id", + "variableName": "id" + } +], +v2 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null +}; +return { + "fragment": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Fragment", + "metadata": null, + "name": "RelayModernEnvironmentExecuteWithDeferTestNestedUserQuery", + "selections": [ + { + "alias": null, + "args": (v1/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + { + "kind": "Defer", + "selections": [ + { + "args": null, + "kind": "FragmentSpread", + "name": "RelayModernEnvironmentExecuteWithDeferTestNestedUserFragment" + } + ] + } + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Operation", + "name": "RelayModernEnvironmentExecuteWithDeferTestNestedUserQuery", + "selections": [ + { + "alias": null, + "args": (v1/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null + }, + { + "if": null, + "kind": "Defer", + "label": "RelayModernEnvironmentExecuteWithDeferTestNestedUserQuery$defer$UserFragment", + "selections": [ + { + "kind": "InlineFragment", + "selections": [ + (v2/*: any*/), + { + "if": null, + "kind": "Defer", + "label": "RelayModernEnvironmentExecuteWithDeferTestNestedUserFragment$defer$RelayModernEnvironmentExecuteWithDeferTestNestedInnerUserFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + }, + { + "if": null, + "kind": "Defer", + "label": "RelayModernEnvironmentExecuteWithDeferTestNestedInnerUserFragment$defer$RelayModernEnvironmentExecuteWithDeferTestNestedInnerInner2UserFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "lastName", + "storageKey": null + } + ] + } + ] + } + ], + "type": "User", + "abstractKey": null + } + ] + }, + (v2/*: any*/) + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "efa1d471949d3dec258c20b998586f7e", + "id": null, + "metadata": {}, + "name": "RelayModernEnvironmentExecuteWithDeferTestNestedUserQuery", + "operationKind": "query", + "text": "query RelayModernEnvironmentExecuteWithDeferTestNestedUserQuery(\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...RelayModernEnvironmentExecuteWithDeferTestNestedUserFragment @defer(label: \"RelayModernEnvironmentExecuteWithDeferTestNestedUserQuery$defer$UserFragment\")\n id\n }\n}\n\nfragment RelayModernEnvironmentExecuteWithDeferTestNestedInnerInner2UserFragment on User {\n lastName\n}\n\nfragment RelayModernEnvironmentExecuteWithDeferTestNestedInnerUserFragment on User {\n name\n ...RelayModernEnvironmentExecuteWithDeferTestNestedInnerInner2UserFragment @defer(label: \"RelayModernEnvironmentExecuteWithDeferTestNestedInnerUserFragment$defer$RelayModernEnvironmentExecuteWithDeferTestNestedInnerInner2UserFragment\")\n}\n\nfragment RelayModernEnvironmentExecuteWithDeferTestNestedUserFragment on User {\n id\n ...RelayModernEnvironmentExecuteWithDeferTestNestedInnerUserFragment @defer(label: \"RelayModernEnvironmentExecuteWithDeferTestNestedUserFragment$defer$RelayModernEnvironmentExecuteWithDeferTestNestedInnerUserFragment\")\n}\n" + } +}; +})(); + +if (__DEV__) { + (node/*: any*/).hash = "d746549fdd29f74e59ee289c7b65d167"; +} + +module.exports = ((node/*: any*/)/*: Query< + RelayModernEnvironmentExecuteWithDeferTestNestedUserQuery$variables, + RelayModernEnvironmentExecuteWithDeferTestNestedUserQuery$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithDeferTestResolverQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithDeferTestResolverQuery.graphql.js new file mode 100644 index 0000000000000..aa218afa9d315 --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithDeferTestResolverQuery.graphql.js @@ -0,0 +1,167 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ConcreteRequest, Query } from 'relay-runtime'; +import type { RelayModernEnvironmentExecuteWithDeferTestUserFragment$fragmentType } from "./RelayModernEnvironmentExecuteWithDeferTestUserFragment.graphql"; +export type RelayModernEnvironmentExecuteWithDeferTestResolverQuery$variables = {| + id: string, +|}; +export type RelayModernEnvironmentExecuteWithDeferTestResolverQuery$data = {| + +node: ?{| + +$fragmentSpreads: RelayModernEnvironmentExecuteWithDeferTestUserFragment$fragmentType, + |}, +|}; +export type RelayModernEnvironmentExecuteWithDeferTestResolverQuery = {| + response: RelayModernEnvironmentExecuteWithDeferTestResolverQuery$data, + variables: RelayModernEnvironmentExecuteWithDeferTestResolverQuery$variables, +|}; +*/ + +var node/*: ConcreteRequest*/ = (function(){ +var v0 = [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "id" + } +], +v1 = [ + { + "kind": "Variable", + "name": "id", + "variableName": "id" + } +], +v2 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null +}; +return { + "fragment": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Fragment", + "metadata": null, + "name": "RelayModernEnvironmentExecuteWithDeferTestResolverQuery", + "selections": [ + { + "alias": null, + "args": (v1/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + { + "kind": "Defer", + "selections": [ + { + "args": null, + "kind": "FragmentSpread", + "name": "RelayModernEnvironmentExecuteWithDeferTestUserFragment" + } + ] + } + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Operation", + "name": "RelayModernEnvironmentExecuteWithDeferTestResolverQuery", + "selections": [ + { + "alias": null, + "args": (v1/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null + }, + { + "if": null, + "kind": "Defer", + "label": "RelayModernEnvironmentExecuteWithDeferTestResolverQuery$defer$UserFragment", + "selections": [ + { + "kind": "InlineFragment", + "selections": [ + (v2/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + }, + { + "alias": null, + "args": null, + "filters": null, + "handle": "name_handler", + "key": "", + "kind": "ScalarHandle", + "name": "name" + } + ], + "type": "User", + "abstractKey": null + } + ] + }, + (v2/*: any*/) + ], + "storageKey": null + } + ], + "use_exec_time_resolvers": true + }, + "params": { + "cacheID": "9a09609b9ab83ed0f5a23b28a0be2a87", + "id": null, + "metadata": {}, + "name": "RelayModernEnvironmentExecuteWithDeferTestResolverQuery", + "operationKind": "query", + "text": "query RelayModernEnvironmentExecuteWithDeferTestResolverQuery(\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...RelayModernEnvironmentExecuteWithDeferTestUserFragment @defer(label: \"RelayModernEnvironmentExecuteWithDeferTestResolverQuery$defer$UserFragment\")\n id\n }\n}\n\nfragment RelayModernEnvironmentExecuteWithDeferTestUserFragment on User {\n id\n name\n}\n" + } +}; +})(); + +if (__DEV__) { + (node/*: any*/).hash = "56440e2904f55cb46bcb26cb00575108"; +} + +module.exports = ((node/*: any*/)/*: Query< + RelayModernEnvironmentExecuteWithDeferTestResolverQuery$variables, + RelayModernEnvironmentExecuteWithDeferTestResolverQuery$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithDeferTestUserQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithDeferTestUserQuery.graphql.js index a559e8ec7e470..6f5999cf5a115 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithDeferTestUserQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithDeferTestUserQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<357eaacf7dd0604d7eca85220fb4e66e>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -157,7 +157,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "f0274d924fe95386193769563e6421fc"; + (node/*: any*/).hash = "79772170e0d7da0eac141c284c15e489"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithOverlappingStreamTestFeedbackQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithOverlappingStreamTestFeedbackQuery.graphql.js index f1068ba8d697b..e2971df6b8aa1 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithOverlappingStreamTestFeedbackQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithOverlappingStreamTestFeedbackQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -218,7 +218,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "f23e7dbdffffaa06c49725c9cceca25c"; + (node/*: any*/).hash = "06b066155ac2bffd6466e49b3836ff99"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithProvidedVariableTest_UserArgManyFragmentsQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithProvidedVariableTest_UserArgManyFragmentsQuery.graphql.js index 8fd77d65ae7f1..bdf5d6197482e 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithProvidedVariableTest_UserArgManyFragmentsQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithProvidedVariableTest_UserArgManyFragmentsQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<6713b0a8e08ed95789878b165ffac985>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -34,8 +34,8 @@ export type RelayModernEnvironmentExecuteWithProvidedVariableTest_UserArgManyFra variables: RelayModernEnvironmentExecuteWithProvidedVariableTest_UserArgManyFragmentsQuery$variables, |}; ({ - "__relay_internal__pv__RelayProvider_returnsTruerelayprovider": require('./../RelayProvider_returnsTrue.relayprovider'), - "__relay_internal__pv__RelayProvider_pictureScalerelayprovider": require('./../RelayProvider_pictureScale.relayprovider') + "__relay_internal__pv__RelayProvider_returnsTruerelayprovider": require('../RelayProvider_returnsTrue.relayprovider'), + "__relay_internal__pv__RelayProvider_pictureScalerelayprovider": require('../RelayProvider_pictureScale.relayprovider') }: {| +__relay_internal__pv__RelayProvider_pictureScalerelayprovider: {| +get: () => number, @@ -228,15 +228,15 @@ return { "operationKind": "query", "text": "query RelayModernEnvironmentExecuteWithProvidedVariableTest_UserArgManyFragmentsQuery(\n $id: ID!\n $__relay_internal__pv__RelayProvider_returnsTruerelayprovider: Boolean!\n $__relay_internal__pv__RelayProvider_pictureScalerelayprovider: Float!\n) {\n node(id: $id) {\n __typename\n ...RelayModernEnvironmentExecuteWithProvidedVariableTest_profile1\n ...RelayModernEnvironmentExecuteWithProvidedVariableTest_profile2\n ...RelayModernEnvironmentExecuteWithProvidedVariableTest_profile3\n id\n }\n}\n\nfragment RelayModernEnvironmentExecuteWithProvidedVariableTest_profile1 on User {\n id\n name @include(if: $__relay_internal__pv__RelayProvider_returnsTruerelayprovider)\n username @skip(if: $__relay_internal__pv__RelayProvider_returnsTruerelayprovider)\n profilePicture {\n uri\n }\n}\n\nfragment RelayModernEnvironmentExecuteWithProvidedVariableTest_profile2 on User {\n name @include(if: $__relay_internal__pv__RelayProvider_returnsTruerelayprovider)\n alternate_name @include(if: $__relay_internal__pv__RelayProvider_returnsTruerelayprovider)\n}\n\nfragment RelayModernEnvironmentExecuteWithProvidedVariableTest_profile3 on User {\n profile_picture(scale: $__relay_internal__pv__RelayProvider_pictureScalerelayprovider) {\n uri\n }\n}\n", "providedVariables": { - "__relay_internal__pv__RelayProvider_returnsTruerelayprovider": require('./../RelayProvider_returnsTrue.relayprovider'), - "__relay_internal__pv__RelayProvider_pictureScalerelayprovider": require('./../RelayProvider_pictureScale.relayprovider') + "__relay_internal__pv__RelayProvider_returnsTruerelayprovider": require('../RelayProvider_returnsTrue.relayprovider'), + "__relay_internal__pv__RelayProvider_pictureScalerelayprovider": require('../RelayProvider_pictureScale.relayprovider') } } }; })(); if (__DEV__) { - (node/*: any*/).hash = "354b41b8063702301345c74af4c434f7"; + (node/*: any*/).hash = "27f90d252de1907833b6031e90a40707"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithProvidedVariableTest_UserArgSingleFragmentQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithProvidedVariableTest_UserArgSingleFragmentQuery.graphql.js index f426f2dc21593..e8668d3975509 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithProvidedVariableTest_UserArgSingleFragmentQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithProvidedVariableTest_UserArgSingleFragmentQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<202e4fac8401882f52063c14c4da0afc>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -32,7 +32,7 @@ export type RelayModernEnvironmentExecuteWithProvidedVariableTest_UserArgSingleF variables: RelayModernEnvironmentExecuteWithProvidedVariableTest_UserArgSingleFragmentQuery$variables, |}; ({ - "__relay_internal__pv__RelayProvider_returnsTruerelayprovider": require('./../RelayProvider_returnsTrue.relayprovider') + "__relay_internal__pv__RelayProvider_returnsTruerelayprovider": require('../RelayProvider_returnsTrue.relayprovider') }: {| +__relay_internal__pv__RelayProvider_returnsTruerelayprovider: {| +get: () => boolean, @@ -183,14 +183,14 @@ return { "operationKind": "query", "text": "query RelayModernEnvironmentExecuteWithProvidedVariableTest_UserArgSingleFragmentQuery(\n $id: ID!\n $__relay_internal__pv__RelayProvider_returnsTruerelayprovider: Boolean!\n) {\n node(id: $id) {\n __typename\n ...RelayModernEnvironmentExecuteWithProvidedVariableTest_profile1\n id\n }\n}\n\nfragment RelayModernEnvironmentExecuteWithProvidedVariableTest_profile1 on User {\n id\n name @include(if: $__relay_internal__pv__RelayProvider_returnsTruerelayprovider)\n username @skip(if: $__relay_internal__pv__RelayProvider_returnsTruerelayprovider)\n profilePicture {\n uri\n }\n}\n", "providedVariables": { - "__relay_internal__pv__RelayProvider_returnsTruerelayprovider": require('./../RelayProvider_returnsTrue.relayprovider') + "__relay_internal__pv__RelayProvider_returnsTruerelayprovider": require('../RelayProvider_returnsTrue.relayprovider') } } }; })(); if (__DEV__) { - (node/*: any*/).hash = "28c63ee2f6ef93b2c67bcded12b3940e"; + (node/*: any*/).hash = "ceb095ac103f8ed7a4155eead5586b81"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithStreamAndRequiredTestFeedbackQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithStreamAndRequiredTestFeedbackQuery.graphql.js index 912966a25d108..e4c952fcc80f3 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithStreamAndRequiredTestFeedbackQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithStreamAndRequiredTestFeedbackQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<9c52616bbf6a11a2378e9570663828c2>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -166,7 +166,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "ab3c3dfb9f6ec864b836f85e13609fb0"; + (node/*: any*/).hash = "0eb364a40bfac00046a99af9460d1057"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithStreamTestFeedbackQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithStreamTestFeedbackQuery.graphql.js index 432c21d129d59..d27fd3190c73a 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithStreamTestFeedbackQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithStreamTestFeedbackQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<82b783bb63411bd4c967ef938e0e55a6>> * @flow * @lightSyntaxTransform * @nogrep @@ -175,7 +175,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "91e0270ce8d4cdca76615d176c0e0c7c"; + (node/*: any*/).hash = "f5dbd4f27aae2c8c13d4b0978ad92446"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithStreamWithHandlerTestFeedbackQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithStreamWithHandlerTestFeedbackQuery.graphql.js index 5f8db36e99486..04e97b9960bb1 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithStreamWithHandlerTestFeedbackQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithStreamWithHandlerTestFeedbackQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<1a0084c00b0e1e4c5f8db223a8ff72a3>> * @flow * @lightSyntaxTransform * @nogrep @@ -184,7 +184,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "448b460c0771d55eb7b0ec5c53bd1c59"; + (node/*: any*/).hash = "02faeaf93f1d34d7ca7c354c522426cd"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithUndeclaredUnusedArgumentTestQueryWithUnusedFragmentArgumentDefinitionQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithUndeclaredUnusedArgumentTestQueryWithUnusedFragmentArgumentDefinitionQuery.graphql.js index 34a30c7d7d01b..cd1ffb2d12ab9 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithUndeclaredUnusedArgumentTestQueryWithUnusedFragmentArgumentDefinitionQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentExecuteWithUndeclaredUnusedArgumentTestQueryWithUnusedFragmentArgumentDefinitionQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<5730f1912881607dacef35d4a8046c40>> * @flow * @lightSyntaxTransform * @nogrep @@ -160,7 +160,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "00358e8069835783f936954905b1cf85"; + (node/*: any*/).hash = "fc8a112e7a02ef04ca3b597f2095096d"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentNoInlineTestStreamQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentNoInlineTestStreamQuery.graphql.js index 6cd72b13aba9d..e0ad83cf86e92 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentNoInlineTestStreamQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentNoInlineTestStreamQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<191b4e64f5bfa318669814916590702d>> * @flow * @lightSyntaxTransform * @nogrep @@ -137,7 +137,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "07e00f103682c152c4770a0e200cd6e7"; + (node/*: any*/).hash = "ef3b0ee1286de679a140741dc9baed1d"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentNoInlineTestWithArgs_noInline$normalization.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentNoInlineTestWithArgs_noInline$normalization.graphql.js index cdaccd4e2fab1..4dab6e5aa66a1 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentNoInlineTestWithArgs_noInline$normalization.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentNoInlineTestWithArgs_noInline$normalization.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<87f1ddb67fbaba61b623b5fa008cafab>> * @flow * @lightSyntaxTransform * @nogrep @@ -113,7 +113,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "8541107c8da22deef0a7e355409ab1d0"; + (node/*: any*/).hash = "c176d8758682964446ea51b58b3a3f76"; } module.exports = node; diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentNoInlineTestWithArgs_noInline.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentNoInlineTestWithArgs_noInline.graphql.js index 572234fa02960..d736e2940adb2 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentNoInlineTestWithArgs_noInline.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentNoInlineTestWithArgs_noInline.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<5f4b6a1cbab020dbfb770b817af05746>> + * @generated SignedSource<<9355ef70efb895879886c3eefbbb8e36>> * @flow * @lightSyntaxTransform * @nogrep @@ -127,7 +127,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "8541107c8da22deef0a7e355409ab1d0"; + (node/*: any*/).hash = "c176d8758682964446ea51b58b3a3f76"; } module.exports = ((node/*: any*/)/*: Fragment< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentNoInlineTest_nestedNoInlineParent$normalization.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentNoInlineTest_nestedNoInlineParent$normalization.graphql.js index cc6d513438dc6..8734627e0118b 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentNoInlineTest_nestedNoInlineParent$normalization.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentNoInlineTest_nestedNoInlineParent$normalization.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<1f1eaaa0873019c487ebbca315662cd4>> + * @generated SignedSource<<933cf55a67414825e194a14592aaee42>> * @flow * @lightSyntaxTransform * @nogrep @@ -143,7 +143,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "d674bb7d0e482ecdd681a2e8574ff5fe"; + (node/*: any*/).hash = "80e0fda8b8d8bb4e7200ea5387229c4c"; } module.exports = node; diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentNoInlineTest_nestedNoInlineParent.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentNoInlineTest_nestedNoInlineParent.graphql.js index 64d1db3cab621..3d8e35bbad772 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentNoInlineTest_nestedNoInlineParent.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentNoInlineTest_nestedNoInlineParent.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<97802b4904faf63be406cd26668c8d35>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -146,7 +146,7 @@ var node/*: ReaderFragment*/ = { }; if (__DEV__) { - (node/*: any*/).hash = "d674bb7d0e482ecdd681a2e8574ff5fe"; + (node/*: any*/).hash = "80e0fda8b8d8bb4e7200ea5387229c4c"; } module.exports = ((node/*: any*/)/*: Fragment< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentNoInlineTest_noInline$normalization.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentNoInlineTest_noInline$normalization.graphql.js index 8efb5f04dba04..0e4aae4af33d1 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentNoInlineTest_noInline$normalization.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentNoInlineTest_noInline$normalization.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<4ad9e981e86b2be0673b5e990a0ae87d>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -94,7 +94,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "6bd80bb9c64c065763c7ddc0ef046c62"; + (node/*: any*/).hash = "e69851050102bb98a80e11aa48993f20"; } module.exports = node; diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentNoInlineTest_noInline.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentNoInlineTest_noInline.graphql.js index 774f131aecf6f..f86ba161c5b50 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentNoInlineTest_noInline.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentNoInlineTest_noInline.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -117,7 +117,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "6bd80bb9c64c065763c7ddc0ef046c62"; + (node/*: any*/).hash = "e69851050102bb98a80e11aa48993f20"; } module.exports = ((node/*: any*/)/*: Fragment< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentSubscriptionWithResolverContextTestParentQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentSubscriptionWithResolverContextTestParentQuery.graphql.js new file mode 100644 index 0000000000000..8e04259baef57 --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentSubscriptionWithResolverContextTestParentQuery.graphql.js @@ -0,0 +1,124 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ConcreteRequest, Query } from 'relay-runtime'; +import type { RelayModernEnvironmentSubscriptionWithResolverContextTestUserFragment$fragmentType } from "./RelayModernEnvironmentSubscriptionWithResolverContextTestUserFragment.graphql"; +export type RelayModernEnvironmentSubscriptionWithResolverContextTestParentQuery$variables = {||}; +export type RelayModernEnvironmentSubscriptionWithResolverContextTestParentQuery$data = {| + +me: ?{| + +$fragmentSpreads: RelayModernEnvironmentSubscriptionWithResolverContextTestUserFragment$fragmentType, + |}, +|}; +export type RelayModernEnvironmentSubscriptionWithResolverContextTestParentQuery = {| + response: RelayModernEnvironmentSubscriptionWithResolverContextTestParentQuery$data, + variables: RelayModernEnvironmentSubscriptionWithResolverContextTestParentQuery$variables, +|}; +*/ + +var node/*: ConcreteRequest*/ = { + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "RelayModernEnvironmentSubscriptionWithResolverContextTestParentQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "me", + "plural": false, + "selections": [ + { + "args": null, + "kind": "FragmentSpread", + "name": "RelayModernEnvironmentSubscriptionWithResolverContextTestUserFragment" + } + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [], + "kind": "Operation", + "name": "RelayModernEnvironmentSubscriptionWithResolverContextTestParentQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "me", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + }, + { + "kind": "ClientExtension", + "selections": [ + { + "name": "age", + "args": null, + "fragment": null, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": true + } + ] + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "c498bb05241f280bc2224c29335322d9", + "id": null, + "metadata": {}, + "name": "RelayModernEnvironmentSubscriptionWithResolverContextTestParentQuery", + "operationKind": "query", + "text": "query RelayModernEnvironmentSubscriptionWithResolverContextTestParentQuery {\n me {\n ...RelayModernEnvironmentSubscriptionWithResolverContextTestUserFragment\n id\n }\n}\n\nfragment RelayModernEnvironmentSubscriptionWithResolverContextTestUserFragment on User {\n id\n name\n}\n" + } +}; + +if (__DEV__) { + (node/*: any*/).hash = "51bb39abe5fa87455d7805c59b5def94"; +} + +module.exports = ((node/*: any*/)/*: Query< + RelayModernEnvironmentSubscriptionWithResolverContextTestParentQuery$variables, + RelayModernEnvironmentSubscriptionWithResolverContextTestParentQuery$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentSubscriptionWithResolverContextTestUserFragment.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentSubscriptionWithResolverContextTestUserFragment.graphql.js new file mode 100644 index 0000000000000..e6f1138e113e3 --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentSubscriptionWithResolverContextTestUserFragment.graphql.js @@ -0,0 +1,90 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<8e6c9a64b2d02bc018f3af54839ad5fa>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { Fragment, ReaderFragment } from 'relay-runtime'; +import type { FragmentType } from "relay-runtime"; +import {age as userAgeResolverType} from "../resolvers/UserAgeResolvers.js"; +import type { TestResolverContextType } from "../../../mutations/__tests__/TestResolverContextType"; +// Type assertion validating that `userAgeResolverType` resolver is correctly implemented. +// A type error here indicates that the type signature of the resolver module is incorrect. +(userAgeResolverType: ( + args: void, + context: TestResolverContextType, +) => ?number); +declare export opaque type RelayModernEnvironmentSubscriptionWithResolverContextTestUserFragment$fragmentType: FragmentType; +export type RelayModernEnvironmentSubscriptionWithResolverContextTestUserFragment$data = {| + +age: ?number, + +id: string, + +name: ?string, + +$fragmentType: RelayModernEnvironmentSubscriptionWithResolverContextTestUserFragment$fragmentType, +|}; +export type RelayModernEnvironmentSubscriptionWithResolverContextTestUserFragment$key = { + +$data?: RelayModernEnvironmentSubscriptionWithResolverContextTestUserFragment$data, + +$fragmentSpreads: RelayModernEnvironmentSubscriptionWithResolverContextTestUserFragment$fragmentType, + ... +}; +*/ + +var node/*: ReaderFragment*/ = { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "RelayModernEnvironmentSubscriptionWithResolverContextTestUserFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + }, + { + "kind": "ClientExtension", + "selections": [ + { + "alias": null, + "args": null, + "fragment": null, + "kind": "RelayResolver", + "name": "age", + "resolverModule": require('../resolvers/UserAgeResolvers').age, + "path": "age" + } + ] + } + ], + "type": "User", + "abstractKey": null +}; + +if (__DEV__) { + (node/*: any*/).hash = "66a7aeaa5486c95229787503c12b1aa0"; +} + +module.exports = ((node/*: any*/)/*: Fragment< + RelayModernEnvironmentSubscriptionWithResolverContextTestUserFragment$fragmentType, + RelayModernEnvironmentSubscriptionWithResolverContextTestUserFragment$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentTypeRefinementTest2Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentTypeRefinementTest2Query.graphql.js index 62fc2496a1d2a..5938104634e5a 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentTypeRefinementTest2Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentTypeRefinementTest2Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<3ead466eab2ca6cd7f6c2bac7cc3dac9>> + * @generated SignedSource<<82d72327169930be140c163243815eb3>> * @flow * @lightSyntaxTransform * @nogrep @@ -148,7 +148,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "4deb6feeb5af2831d313645105f8165c"; + (node/*: any*/).hash = "1fab375c7c967318ceb32778cf782bd9"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentTypeRefinementTest3Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentTypeRefinementTest3Query.graphql.js index 76e6fc11da654..a54a6d50335b4 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentTypeRefinementTest3Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentTypeRefinementTest3Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<558eb35151893792bd27750bc3121534>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -148,7 +148,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "69d71c1ff828bb46f4cd52ffb90e99fa"; + (node/*: any*/).hash = "c564d7a87f1534ebaab63cb02181ea1d"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentTypeRefinementTest4Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentTypeRefinementTest4Query.graphql.js index 9fde344b0acaa..56e9f3c0e10c9 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentTypeRefinementTest4Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentTypeRefinementTest4Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<401f3ba45f54554d399a07a8b989acdd>> * @flow * @lightSyntaxTransform * @nogrep @@ -155,7 +155,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "a58fd2e641e7c6652209199221c50e34"; + (node/*: any*/).hash = "73a67c0a4ac2f622587eea77a3a86f76"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentTypeRefinementTest5Fragment.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentTypeRefinementTest5Fragment.graphql.js index 68dad5ea28f02..a78e5a8348b82 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentTypeRefinementTest5Fragment.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentTypeRefinementTest5Fragment.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -65,7 +65,7 @@ var node/*: ReaderFragment*/ = { }; if (__DEV__) { - (node/*: any*/).hash = "96697de654c1f1d642048b41e5eaa8c7"; + (node/*: any*/).hash = "356c08c8ef540e0358e184a181394edc"; } module.exports = ((node/*: any*/)/*: Fragment< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentTypeRefinementTest5Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentTypeRefinementTest5Query.graphql.js index 92b8ad1068c43..d862766b94d07 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentTypeRefinementTest5Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentTypeRefinementTest5Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<9d79c27b86f19bbabd551680392a8e54>> + * @generated SignedSource<<5dbfe9a1ca28a6e36ee7f05b2f4717f3>> * @flow * @lightSyntaxTransform * @nogrep @@ -148,7 +148,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "44a6f6a48ad431517c8ff66ac7b1e475"; + (node/*: any*/).hash = "550955aca4916dc529f623ad058f3149"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentTypeRefinementTest6Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentTypeRefinementTest6Query.graphql.js index 524d3e5a2fc59..0d5549fe11039 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentTypeRefinementTest6Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentTypeRefinementTest6Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -155,7 +155,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "8a7b0811d0016fa43cbdb871c6825a8e"; + (node/*: any*/).hash = "019de924708dbca8b54a4b8760c50998"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentTypeRefinementTest9Fragment.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentTypeRefinementTest9Fragment.graphql.js index 098d5e9f8b350..220212c377b5c 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentTypeRefinementTest9Fragment.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentTypeRefinementTest9Fragment.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<735f291c29b9a6f94a0818606b6d033d>> + * @generated SignedSource<<1fac305c2c65adfa6d0349805dfd6b5c>> * @flow * @lightSyntaxTransform * @nogrep @@ -65,7 +65,7 @@ var node/*: ReaderFragment*/ = { }; if (__DEV__) { - (node/*: any*/).hash = "7006df6bd31b24b2a89e742500e6165a"; + (node/*: any*/).hash = "eb358a93c13d1baeb0a769c284f8b440"; } module.exports = ((node/*: any*/)/*: Fragment< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentTypeRefinementTestAbstractQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentTypeRefinementTestAbstractQuery.graphql.js index 232bce9e28be4..396f9a7cb6a3d 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentTypeRefinementTestAbstractQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentTypeRefinementTestAbstractQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<3c0e836b2db9f6c315ac20f510f0d014>> + * @generated SignedSource<<85d7cecfc3bb841c247b5d2fd950cab7>> * @flow * @lightSyntaxTransform * @nogrep @@ -147,7 +147,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "aff59a67ff8458a227e246a603d9ee26"; + (node/*: any*/).hash = "8035321d8e2ae9f9cb97b1d0deca22d6"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentTypeRefinementTestConcreteQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentTypeRefinementTestConcreteQuery.graphql.js index dddec4ac8867f..9a17a54876bfb 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentTypeRefinementTestConcreteQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentTypeRefinementTestConcreteQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<52d1dc3f0bfcbd2c058cf348b1bfe165>> + * @generated SignedSource<<7d64ac33464a98083aa1105f78eff313>> * @flow * @lightSyntaxTransform * @nogrep @@ -147,7 +147,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "08f8aa409b7c457e4992582d6eb9d7a5"; + (node/*: any*/).hash = "9a839040e1d7a6501f9080b5b8ff38db"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentTypeRefinementTestParentQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentTypeRefinementTestParentQuery.graphql.js index 06be6b006b547..dcf93ac30459b 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentTypeRefinementTestParentQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentTypeRefinementTestParentQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<75f751c4837ec6f4b06ff7391619f1f4>> * @flow * @lightSyntaxTransform * @nogrep @@ -177,7 +177,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "689f4ef00bbe32b65200f7bf0b18600d"; + (node/*: any*/).hash = "d976516696559b4da133ef37cf797609"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentWithOperationTrackerTestQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentWithOperationTrackerTestQuery.graphql.js index badc8f111ad23..46f44c63b2d8e 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentWithOperationTrackerTestQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernEnvironmentWithOperationTrackerTestQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<0bd821834d2425699b7cf0ece8c733ff>> + * @generated SignedSource<<646d5dd7a58d8d074c1ea4c14a3d3544>> * @flow * @lightSyntaxTransform * @nogrep @@ -327,7 +327,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "a7167fdd5f3eacc9c1ed47a342eb2fa0"; + (node/*: any*/).hash = "f99896bd25a8f9b54139a54ecb15defe"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernFragmentSpecResolverRequiredFieldNoLoggerTestUserQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernFragmentSpecResolverRequiredFieldNoLoggerTestUserQuery.graphql.js index 552872750afa7..3fcadb326666c 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernFragmentSpecResolverRequiredFieldNoLoggerTestUserQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernFragmentSpecResolverRequiredFieldNoLoggerTestUserQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<2f3f8c1a93cfc0e00653aa88cfb1d550>> + * @generated SignedSource<<133c9604c3033d80c01c583ce030b26d>> * @flow * @lightSyntaxTransform * @nogrep @@ -134,7 +134,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "509d57fcdf4027461a38077ba36fc41b"; + (node/*: any*/).hash = "5772f2b553a33bce8df54b6f1c7032ae"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernFragmentSpecResolverRequiredFieldTestUserQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernFragmentSpecResolverRequiredFieldTestUserQuery.graphql.js index c65ee991c98c8..0bf63d9cc77b5 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernFragmentSpecResolverRequiredFieldTestUserQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernFragmentSpecResolverRequiredFieldTestUserQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<169fc6b05f4f8676b7309e500241dd89>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -141,7 +141,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "7a8f1c7d05c71461bd82aa5538c96b3c"; + (node/*: any*/).hash = "624e9bc603f1fb6687879a87884278c8"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernFragmentSpecResolverTestAffectingQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernFragmentSpecResolverTestAffectingQuery.graphql.js index 1ff5d00455d84..990103252eaf7 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernFragmentSpecResolverTestAffectingQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernFragmentSpecResolverTestAffectingQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<4c8f71814d406e87ff29593bc0a61082>> * @flow * @lightSyntaxTransform * @nogrep @@ -189,7 +189,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "ded358125ddc82c32d526451fd5f1a0e"; + (node/*: any*/).hash = "503f6130ff2642a6155862ad71840e2b"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernFragmentSpecResolverTestQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernFragmentSpecResolverTestQuery.graphql.js index c6fbacb14a8f5..ee0ac4c82477f 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernFragmentSpecResolverTestQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernFragmentSpecResolverTestQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<95f7085fa90bf1186a200e4bb7a0bd75>> * @flow * @lightSyntaxTransform * @nogrep @@ -189,7 +189,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "861d571e1856374cfbff7ce6564c306c"; + (node/*: any*/).hash = "927b2880628e050a5b8104216923a40b"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernFragmentSpecResolverWithFragmentOwnershipTestUserQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernFragmentSpecResolverWithFragmentOwnershipTestUserQuery.graphql.js index 85080688c6b07..6a2eb6b015e41 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernFragmentSpecResolverWithFragmentOwnershipTestUserQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernFragmentSpecResolverWithFragmentOwnershipTestUserQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<7c26cc8690db0d9e24fe78d43eabfeb3>> + * @generated SignedSource<<1e7d62a97f7fd9493e545e9da3dfb270>> * @flow * @lightSyntaxTransform * @nogrep @@ -196,7 +196,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "0ac3466c6ebc673ebbcfbbf5e1fead2b"; + (node/*: any*/).hash = "011f30616d7a8c12b013883ec537f28c"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernOperationDescriptorTestCycleWithPVQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernOperationDescriptorTestCycleWithPVQuery.graphql.js index 05b5fe9378617..1c38b7f13dc4d 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernOperationDescriptorTestCycleWithPVQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernOperationDescriptorTestCycleWithPVQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<305bc4cb44edc8ea237917371c400367>> * @flow * @lightSyntaxTransform * @nogrep @@ -30,7 +30,7 @@ export type RelayModernOperationDescriptorTestCycleWithPVQuery = {| variables: RelayModernOperationDescriptorTestCycleWithPVQuery$variables, |}; ({ - "__relay_internal__pv__RelayProvider_returnsCyclicrelayprovider": require('./../RelayProvider_returnsCyclic.relayprovider') + "__relay_internal__pv__RelayProvider_returnsCyclicrelayprovider": require('../RelayProvider_returnsCyclic.relayprovider') }: {| +__relay_internal__pv__RelayProvider_returnsCyclicrelayprovider: {| +get: () => boolean, @@ -119,7 +119,7 @@ var node/*: ConcreteRequest*/ = { "operationKind": "query", "text": "query RelayModernOperationDescriptorTestCycleWithPVQuery(\n $__relay_internal__pv__RelayProvider_returnsCyclicrelayprovider: Boolean!\n) {\n me {\n ...RelayModernOperationDescriptorTestCycleQuery_fragment\n id\n }\n}\n\nfragment RelayModernOperationDescriptorTestCycleQuery_fragment on User {\n name @include(if: $__relay_internal__pv__RelayProvider_returnsCyclicrelayprovider)\n}\n", "providedVariables": { - "__relay_internal__pv__RelayProvider_returnsCyclicrelayprovider": require('./../RelayProvider_returnsCyclic.relayprovider') + "__relay_internal__pv__RelayProvider_returnsCyclicrelayprovider": require('../RelayProvider_returnsCyclic.relayprovider') } } }; diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernSelectorTestUserQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernSelectorTestUserQuery.graphql.js index df98e5cad3e78..78bd9899201d2 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernSelectorTestUserQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernSelectorTestUserQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<6e1039059f1c88ea8e6e5c3c64cbec32>> * @flow * @lightSyntaxTransform * @nogrep @@ -189,7 +189,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "afbaf504a28e027de34401ff4a82e567"; + (node/*: any*/).hash = "56dee97a569ea3090f0026337129c6e2"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernStoreTest1Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernStoreTest1Query.graphql.js index d4be05a2cd896..05ec0fbe6df33 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernStoreTest1Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernStoreTest1Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<8645349c6f6b388cf6aca8ae954919b8>> + * @generated SignedSource<<4e75eb0df9c99a7434eabf7ff7db7942>> * @flow * @lightSyntaxTransform * @nogrep @@ -164,7 +164,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "7fdabede719577afaae8855a27396b98"; + (node/*: any*/).hash = "04b19cd56ddc2656fb74b7223e570e06"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernStoreTest6Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernStoreTest6Query.graphql.js index 1e79b634b4d17..e0d8dce0c0b84 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernStoreTest6Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernStoreTest6Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<39f352fd5b09c8333f7f007876341503>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -164,7 +164,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "c84a88e1af0fcca4eb3739581f34d286"; + (node/*: any*/).hash = "f080f5a0fc584f99ea0b4d1bbff0d87f"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernStoreTest7Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernStoreTest7Query.graphql.js index 39e96bb29a481..bda7411dfb383 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernStoreTest7Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernStoreTest7Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<1098cb2a455fdad1e5a6cd753dab70f3>> + * @generated SignedSource<<97892b0488c3259e6b5c855efa860d6c>> * @flow * @lightSyntaxTransform * @nogrep @@ -164,7 +164,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "2bfbf713695a9ce69b8e915e0ec020f4"; + (node/*: any*/).hash = "8de95361d09966d41d9b73fad489d534"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayModernStoreTest9Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayModernStoreTest9Query.graphql.js index 3fae0521cd84b..3b23ac27643b4 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayModernStoreTest9Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayModernStoreTest9Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -164,7 +164,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "7ae41adfd80d75a8e28e02f6df5baf47"; + (node/*: any*/).hash = "b32973d64f3cb9d3955e0aab2c9a36f2"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderClientEdgesTest1Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderClientEdgesTest1Query.graphql.js index 56328e186f186..4ed0321a7cef6 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderClientEdgesTest1Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderClientEdgesTest1Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -85,7 +85,7 @@ return { }, "kind": "RelayResolver", "name": "client_edge", - "resolverModule": require('./../resolvers/UserClientEdgeResolver').client_edge, + "resolverModule": require('../resolvers/UserClientEdgeResolver').client_edge, "path": "me.client_edge" }, "linkedField": { diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderClientEdgesTest2Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderClientEdgesTest2Query.graphql.js index 16e66e3c90bff..9ba95ac7dfed1 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderClientEdgesTest2Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderClientEdgesTest2Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -87,7 +87,7 @@ return { }, "kind": "RelayResolver", "name": "client_edge", - "resolverModule": require('./../resolvers/UserClientEdgeResolver').client_edge, + "resolverModule": require('../resolvers/UserClientEdgeResolver').client_edge, "path": "me.client_edge" }, "linkedField": { diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderClientEdgesTest3Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderClientEdgesTest3Query.graphql.js index 02f7066ecf00f..e294b39a86b80 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderClientEdgesTest3Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderClientEdgesTest3Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<63a4aae0094d1dfafe0d309e05e8dd6b>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -76,7 +76,7 @@ var node/*: ConcreteRequest*/ = { }, "kind": "RelayResolver", "name": "client_edge", - "resolverModule": require('./../resolvers/UserClientEdgeResolver').client_edge, + "resolverModule": require('../resolvers/UserClientEdgeResolver').client_edge, "path": "me.client_edge" }, "linkedField": { diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderClientEdgesTest4Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderClientEdgesTest4Query.graphql.js index 5275fc51c2072..fc91bc7e71164 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderClientEdgesTest4Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderClientEdgesTest4Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<47d4bb0b033077758cd753348f4ccf51>> + * @generated SignedSource<<83279bcac3598c8eadc0729662f83169>> * @flow * @lightSyntaxTransform * @nogrep @@ -98,7 +98,7 @@ return { }, "kind": "RelayResolver", "name": "client_edge", - "resolverModule": require('./../resolvers/UserClientEdgeResolver').client_edge, + "resolverModule": require('../resolvers/UserClientEdgeResolver').client_edge, "path": "me.client_edge" }, "linkedField": { @@ -122,7 +122,7 @@ return { }, "kind": "RelayResolver", "name": "another_client_edge", - "resolverModule": require('./../resolvers/UserAnotherClientEdgeResolver').another_client_edge, + "resolverModule": require('../resolvers/UserAnotherClientEdgeResolver').another_client_edge, "path": "me.client_edge.another_client_edge" }, "linkedField": { diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderClientEdgesTest5Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderClientEdgesTest5Query.graphql.js index 550f70435ada4..a31f70d3677da 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderClientEdgesTest5Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderClientEdgesTest5Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<82ac191dc7d5e5b2999778a0e554f4a2>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -105,7 +105,7 @@ return { }, "kind": "RelayResolver", "name": "client_edge", - "resolverModule": require('./../resolvers/UserClientEdgeResolver').client_edge, + "resolverModule": require('../resolvers/UserClientEdgeResolver').client_edge, "path": "me.client_extension_linked_field.client_edge" }, "linkedField": { diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderClientEdgesTest6Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderClientEdgesTest6Query.graphql.js index b208dadce702a..d44c7cbcbc4e1 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderClientEdgesTest6Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderClientEdgesTest6Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<21758827b5cb487b34bb9543e2d32e8a>> + * @generated SignedSource<<59385e49fa4328495f39cae77292ff3f>> * @flow * @lightSyntaxTransform * @nogrep @@ -85,7 +85,7 @@ return { }, "kind": "RelayResolver", "name": "client_edge", - "resolverModule": require('./../resolvers/UserClientEdgeResolver').client_edge, + "resolverModule": require('../resolvers/UserClientEdgeResolver').client_edge, "path": "me.the_alias" }, "linkedField": { diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderClientEdgesTest7Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderClientEdgesTest7Query.graphql.js index 6c959cc99b06c..5758a13fa1df6 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderClientEdgesTest7Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderClientEdgesTest7Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<4d23ce70b24e0f34666e98c3cc8b13ef>> + * @generated SignedSource<<374706acddfeb3e3f32f646af5b4fc33>> * @flow * @lightSyntaxTransform * @nogrep @@ -85,7 +85,7 @@ return { }, "kind": "RelayResolver", "name": "null_client_edge", - "resolverModule": require('./../resolvers/UserNullClientEdgeResolver').null_client_edge, + "resolverModule": require('../resolvers/UserNullClientEdgeResolver').null_client_edge, "path": "me.null_client_edge" }, "linkedField": { diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderClientEdgesTestMissingClientEdgeDataQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderClientEdgesTestMissingClientEdgeDataQuery.graphql.js index a1e82f1feab75..43d9747ffbd3f 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderClientEdgesTestMissingClientEdgeDataQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderClientEdgesTestMissingClientEdgeDataQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<4c502d58be4f765622e5610e1a77c28f>> * @flow * @lightSyntaxTransform * @nogrep @@ -65,7 +65,7 @@ var node/*: ConcreteRequest*/ = { }, "kind": "RelayResolver", "name": "reads_client_edge", - "resolverModule": require('./../resolvers/UserReadsClientEdgeResolver').reads_client_edge, + "resolverModule": require('../resolvers/UserReadsClientEdgeResolver').reads_client_edge, "path": "me.reads_client_edge" } ], diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderExecResolversTestClientDirectiveQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderExecResolversTestClientDirectiveQuery.graphql.js new file mode 100644 index 0000000000000..7093740c4e48b --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderExecResolversTestClientDirectiveQuery.graphql.js @@ -0,0 +1,408 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ClientRequest, ClientQuery } from 'relay-runtime'; +import type { DataID } from "relay-runtime"; +import type { RelayReaderExecResolversTestUser____relay_model_instance$data } from "./RelayReaderExecResolversTestUser____relay_model_instance.graphql"; +import {RelayReaderExecResolversTest_user_one as queryRelayReaderExecResolversTestUserOneResolverType} from "../RelayReader-ExecResolvers-test.js"; +import type { TestResolverContextType } from "../../../mutations/__tests__/TestResolverContextType"; +// Type assertion validating that `queryRelayReaderExecResolversTestUserOneResolverType` resolver is correctly implemented. +// A type error here indicates that the type signature of the resolver module is incorrect. +(queryRelayReaderExecResolversTestUserOneResolverType: ( + args: void, + context: TestResolverContextType, +) => ?{| + +id: DataID, +|}); +import {best_friend as relayReaderExecResolversTestUserBestFriendResolverType} from "../RelayReader-ExecResolvers-test.js"; +// Type assertion validating that `relayReaderExecResolversTestUserBestFriendResolverType` resolver is correctly implemented. +// A type error here indicates that the type signature of the resolver module is incorrect. +(relayReaderExecResolversTestUserBestFriendResolverType: ( + __relay_model_instance: RelayReaderExecResolversTestUser____relay_model_instance$data['__relay_model_instance'], + args: void, + context: TestResolverContextType, +) => ?{| + +id: DataID, +|}); +import {friends as relayReaderExecResolversTestUserFriendsResolverType} from "../RelayReader-ExecResolvers-test.js"; +// Type assertion validating that `relayReaderExecResolversTestUserFriendsResolverType` resolver is correctly implemented. +// A type error here indicates that the type signature of the resolver module is incorrect. +(relayReaderExecResolversTestUserFriendsResolverType: ( + __relay_model_instance: RelayReaderExecResolversTestUser____relay_model_instance$data['__relay_model_instance'], + args: void, + context: TestResolverContextType, +) => ?$ReadOnlyArray); +import {name as relayReaderExecResolversTestUserNameResolverType} from "../RelayReader-ExecResolvers-test.js"; +// Type assertion validating that `relayReaderExecResolversTestUserNameResolverType` resolver is correctly implemented. +// A type error here indicates that the type signature of the resolver module is incorrect. +(relayReaderExecResolversTestUserNameResolverType: ( + __relay_model_instance: RelayReaderExecResolversTestUser____relay_model_instance$data['__relay_model_instance'], + args: void, + context: TestResolverContextType, +) => ?string); +export type RelayReaderExecResolversTestClientDirectiveQuery$variables = {||}; +export type RelayReaderExecResolversTestClientDirectiveQuery$data = {| + +RelayReaderExecResolversTest_user_one: ?{| + +best_friend: ?{| + +name: ?string, + |}, + +friends: ?$ReadOnlyArray, + +name: $NonMaybeType, + |}, +|}; +export type RelayReaderExecResolversTestClientDirectiveQuery = {| + response: RelayReaderExecResolversTestClientDirectiveQuery$data, + variables: RelayReaderExecResolversTestClientDirectiveQuery$variables, +|}; +*/ + +var node/*: ClientRequest*/ = (function(){ +var v0 = { + "args": null, + "kind": "FragmentSpread", + "name": "RelayReaderExecResolversTestUser__id" +}, +v1 = { + "args": null, + "kind": "FragmentSpread", + "name": "RelayReaderExecResolversTestUser____relay_model_instance" +}, +v2 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null +}, +v3 = { + "kind": "InlineFragment", + "selections": [ + { + "name": "__relay_model_instance", + "args": null, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": false, + "resolverInfo": { + "resolverFunction": require('../RelayReader-ExecResolvers-test').RelayReaderExecResolversTestUser, + "rootFragment": null + }, + "fragment": { + "kind": "InlineFragment", + "selections": [ + (v2/*: any*/) + ], + "type": "RelayReaderExecResolversTestUser", + "abstractKey": null + } + } + ], + "type": "RelayReaderExecResolversTestUser", + "abstractKey": null +}, +v4 = { + "name": "name", + "args": null, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": true, + "resolverInfo": { + "resolverFunction": require('../RelayReader-ExecResolvers-test').name, + "rootFragment": null + }, + "fragment": (v3/*: any*/) +}, +v5 = [ + (v4/*: any*/), + (v2/*: any*/) +]; +return { + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": { + "hasClientEdges": true + }, + "name": "RelayReaderExecResolversTestClientDirectiveQuery", + "selections": [ + { + "kind": "ClientEdgeToClientObject", + "concreteType": "RelayReaderExecResolversTestUser", + "modelResolvers": { + "RelayReaderExecResolversTestUser": { + "alias": null, + "args": null, + "fragment": (v0/*: any*/), + "kind": "RelayResolver", + "name": "__relay_model_instance", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser__id.graphql'), require('../RelayReader-ExecResolvers-test').RelayReaderExecResolversTestUser, 'id', true), + "path": "RelayReaderExecResolversTest_user_one.__relay_model_instance" + } + }, + "backingField": { + "alias": null, + "args": null, + "fragment": null, + "kind": "RelayResolver", + "name": "RelayReaderExecResolversTest_user_one", + "resolverModule": require('../RelayReader-ExecResolvers-test').RelayReaderExecResolversTest_user_one, + "path": "RelayReaderExecResolversTest_user_one" + }, + "linkedField": { + "alias": null, + "args": null, + "concreteType": "RelayReaderExecResolversTestUser", + "kind": "LinkedField", + "name": "RelayReaderExecResolversTest_user_one", + "plural": false, + "selections": [ + { + "kind": "RequiredField", + "field": { + "alias": null, + "args": null, + "fragment": (v1/*: any*/), + "kind": "RelayResolver", + "name": "name", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser____relay_model_instance.graphql'), require('../RelayReader-ExecResolvers-test').name, '__relay_model_instance', true), + "path": "RelayReaderExecResolversTest_user_one.name" + }, + "action": "THROW" + }, + { + "kind": "ClientEdgeToClientObject", + "concreteType": "RelayReaderExecResolversTestUser", + "modelResolvers": { + "RelayReaderExecResolversTestUser": { + "alias": null, + "args": null, + "fragment": (v0/*: any*/), + "kind": "RelayResolver", + "name": "__relay_model_instance", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser__id.graphql'), require('../RelayReader-ExecResolvers-test').RelayReaderExecResolversTestUser, 'id', true), + "path": "RelayReaderExecResolversTest_user_one.best_friend.__relay_model_instance" + } + }, + "backingField": { + "alias": null, + "args": null, + "fragment": (v1/*: any*/), + "kind": "RelayResolver", + "name": "best_friend", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser____relay_model_instance.graphql'), require('../RelayReader-ExecResolvers-test').best_friend, '__relay_model_instance', true), + "path": "RelayReaderExecResolversTest_user_one.best_friend" + }, + "linkedField": { + "alias": null, + "args": null, + "concreteType": "RelayReaderExecResolversTestUser", + "kind": "LinkedField", + "name": "best_friend", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "fragment": (v1/*: any*/), + "kind": "RelayResolver", + "name": "name", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser____relay_model_instance.graphql'), require('../RelayReader-ExecResolvers-test').name, '__relay_model_instance', true), + "path": "RelayReaderExecResolversTest_user_one.best_friend.name" + } + ], + "storageKey": null + } + }, + { + "kind": "ClientEdgeToClientObject", + "concreteType": "RelayReaderExecResolversTestUser", + "modelResolvers": { + "RelayReaderExecResolversTestUser": { + "alias": null, + "args": null, + "fragment": (v0/*: any*/), + "kind": "RelayResolver", + "name": "__relay_model_instance", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser__id.graphql'), require('../RelayReader-ExecResolvers-test').RelayReaderExecResolversTestUser, 'id', true), + "path": "RelayReaderExecResolversTest_user_one.friends.__relay_model_instance" + } + }, + "backingField": { + "alias": null, + "args": null, + "fragment": (v1/*: any*/), + "kind": "RelayResolver", + "name": "friends", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser____relay_model_instance.graphql'), require('../RelayReader-ExecResolvers-test').friends, '__relay_model_instance', true), + "path": "RelayReaderExecResolversTest_user_one.friends" + }, + "linkedField": { + "alias": null, + "args": null, + "concreteType": "RelayReaderExecResolversTestUser", + "kind": "LinkedField", + "name": "friends", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "fragment": (v1/*: any*/), + "kind": "RelayResolver", + "name": "name", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser____relay_model_instance.graphql'), require('../RelayReader-ExecResolvers-test').name, '__relay_model_instance', true), + "path": "RelayReaderExecResolversTest_user_one.friends.name" + } + ], + "storageKey": null + } + } + ], + "storageKey": null + } + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [], + "kind": "Operation", + "name": "RelayReaderExecResolversTestClientDirectiveQuery", + "selections": [ + { + "kind": "ClientEdgeToClientObject", + "modelResolvers": { + "RelayReaderExecResolversTestUser": { + "resolverModule": require('../RelayReader-ExecResolvers-test').RelayReaderExecResolversTestUser + } + }, + "backingField": { + "name": "RelayReaderExecResolversTest_user_one", + "args": null, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": false, + "resolverInfo": { + "resolverFunction": require('../RelayReader-ExecResolvers-test').RelayReaderExecResolversTest_user_one, + "rootFragment": null + }, + "fragment": null + }, + "linkedField": { + "alias": null, + "args": null, + "concreteType": "RelayReaderExecResolversTestUser", + "kind": "LinkedField", + "name": "RelayReaderExecResolversTest_user_one", + "plural": false, + "selections": [ + (v4/*: any*/), + { + "kind": "ClientEdgeToClientObject", + "modelResolvers": { + "RelayReaderExecResolversTestUser": { + "resolverModule": require('../RelayReader-ExecResolvers-test').RelayReaderExecResolversTestUser + } + }, + "backingField": { + "name": "best_friend", + "args": null, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": false, + "resolverInfo": { + "resolverFunction": require('../RelayReader-ExecResolvers-test').best_friend, + "rootFragment": null + }, + "fragment": (v3/*: any*/) + }, + "linkedField": { + "alias": null, + "args": null, + "concreteType": "RelayReaderExecResolversTestUser", + "kind": "LinkedField", + "name": "best_friend", + "plural": false, + "selections": (v5/*: any*/), + "storageKey": null + } + }, + { + "kind": "ClientEdgeToClientObject", + "modelResolvers": { + "RelayReaderExecResolversTestUser": { + "resolverModule": require('../RelayReader-ExecResolvers-test').RelayReaderExecResolversTestUser + } + }, + "backingField": { + "name": "friends", + "args": null, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": false, + "resolverInfo": { + "resolverFunction": require('../RelayReader-ExecResolvers-test').friends, + "rootFragment": null + }, + "fragment": (v3/*: any*/) + }, + "linkedField": { + "alias": null, + "args": null, + "concreteType": "RelayReaderExecResolversTestUser", + "kind": "LinkedField", + "name": "friends", + "plural": true, + "selections": (v5/*: any*/), + "storageKey": null + } + }, + (v2/*: any*/) + ], + "storageKey": null + } + } + ], + "exec_time_resolvers_enabled_provider": require('../relayReaderTestExecTimeResolversTrueProvider') + }, + "params": { + "cacheID": "93766bd16ee00d3863eac4f5873050f1", + "id": null, + "metadata": {}, + "name": "RelayReaderExecResolversTestClientDirectiveQuery", + "operationKind": "query", + "text": null + } +}; +})(); + +if (__DEV__) { + (node/*: any*/).hash = "6a6cebf94e46a5bc998e01e8c6a67718"; +} + +module.exports = ((node/*: any*/)/*: ClientQuery< + RelayReaderExecResolversTestClientDirectiveQuery$variables, + RelayReaderExecResolversTestClientDirectiveQuery$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderExecResolversTestFalseProviderQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderExecResolversTestFalseProviderQuery.graphql.js new file mode 100644 index 0000000000000..e9f68f11fdd3b --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderExecResolversTestFalseProviderQuery.graphql.js @@ -0,0 +1,404 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<1599a48f4deb59d5828dc9f1c2c5a999>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ClientRequest, ClientQuery } from 'relay-runtime'; +import type { DataID } from "relay-runtime"; +import type { RelayReaderExecResolversTestUser____relay_model_instance$data } from "./RelayReaderExecResolversTestUser____relay_model_instance.graphql"; +import {RelayReaderExecResolversTest_user_one as queryRelayReaderExecResolversTestUserOneResolverType} from "../RelayReader-ExecResolvers-test.js"; +import type { TestResolverContextType } from "../../../mutations/__tests__/TestResolverContextType"; +// Type assertion validating that `queryRelayReaderExecResolversTestUserOneResolverType` resolver is correctly implemented. +// A type error here indicates that the type signature of the resolver module is incorrect. +(queryRelayReaderExecResolversTestUserOneResolverType: ( + args: void, + context: TestResolverContextType, +) => ?{| + +id: DataID, +|}); +import {best_friend as relayReaderExecResolversTestUserBestFriendResolverType} from "../RelayReader-ExecResolvers-test.js"; +// Type assertion validating that `relayReaderExecResolversTestUserBestFriendResolverType` resolver is correctly implemented. +// A type error here indicates that the type signature of the resolver module is incorrect. +(relayReaderExecResolversTestUserBestFriendResolverType: ( + __relay_model_instance: RelayReaderExecResolversTestUser____relay_model_instance$data['__relay_model_instance'], + args: void, + context: TestResolverContextType, +) => ?{| + +id: DataID, +|}); +import {friends as relayReaderExecResolversTestUserFriendsResolverType} from "../RelayReader-ExecResolvers-test.js"; +// Type assertion validating that `relayReaderExecResolversTestUserFriendsResolverType` resolver is correctly implemented. +// A type error here indicates that the type signature of the resolver module is incorrect. +(relayReaderExecResolversTestUserFriendsResolverType: ( + __relay_model_instance: RelayReaderExecResolversTestUser____relay_model_instance$data['__relay_model_instance'], + args: void, + context: TestResolverContextType, +) => ?$ReadOnlyArray); +import {name as relayReaderExecResolversTestUserNameResolverType} from "../RelayReader-ExecResolvers-test.js"; +// Type assertion validating that `relayReaderExecResolversTestUserNameResolverType` resolver is correctly implemented. +// A type error here indicates that the type signature of the resolver module is incorrect. +(relayReaderExecResolversTestUserNameResolverType: ( + __relay_model_instance: RelayReaderExecResolversTestUser____relay_model_instance$data['__relay_model_instance'], + args: void, + context: TestResolverContextType, +) => ?string); +export type RelayReaderExecResolversTestFalseProviderQuery$variables = {||}; +export type RelayReaderExecResolversTestFalseProviderQuery$data = {| + +RelayReaderExecResolversTest_user_one: ?{| + +best_friend: ?{| + +name: ?string, + |}, + +friends: ?$ReadOnlyArray, + +name: ?string, + |}, +|}; +export type RelayReaderExecResolversTestFalseProviderQuery = {| + response: RelayReaderExecResolversTestFalseProviderQuery$data, + variables: RelayReaderExecResolversTestFalseProviderQuery$variables, +|}; +*/ + +var node/*: ClientRequest*/ = (function(){ +var v0 = { + "args": null, + "kind": "FragmentSpread", + "name": "RelayReaderExecResolversTestUser__id" +}, +v1 = { + "args": null, + "kind": "FragmentSpread", + "name": "RelayReaderExecResolversTestUser____relay_model_instance" +}, +v2 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null +}, +v3 = { + "kind": "InlineFragment", + "selections": [ + { + "name": "__relay_model_instance", + "args": null, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": false, + "resolverInfo": { + "resolverFunction": require('../RelayReader-ExecResolvers-test').RelayReaderExecResolversTestUser, + "rootFragment": null + }, + "fragment": { + "kind": "InlineFragment", + "selections": [ + (v2/*: any*/) + ], + "type": "RelayReaderExecResolversTestUser", + "abstractKey": null + } + } + ], + "type": "RelayReaderExecResolversTestUser", + "abstractKey": null +}, +v4 = { + "name": "name", + "args": null, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": true, + "resolverInfo": { + "resolverFunction": require('../RelayReader-ExecResolvers-test').name, + "rootFragment": null + }, + "fragment": (v3/*: any*/) +}, +v5 = [ + (v4/*: any*/), + (v2/*: any*/) +]; +return { + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": { + "hasClientEdges": true + }, + "name": "RelayReaderExecResolversTestFalseProviderQuery", + "selections": [ + { + "kind": "ClientEdgeToClientObject", + "concreteType": "RelayReaderExecResolversTestUser", + "modelResolvers": { + "RelayReaderExecResolversTestUser": { + "alias": null, + "args": null, + "fragment": (v0/*: any*/), + "kind": "RelayResolver", + "name": "__relay_model_instance", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser__id.graphql'), require('../RelayReader-ExecResolvers-test').RelayReaderExecResolversTestUser, 'id', true), + "path": "RelayReaderExecResolversTest_user_one.__relay_model_instance" + } + }, + "backingField": { + "alias": null, + "args": null, + "fragment": null, + "kind": "RelayResolver", + "name": "RelayReaderExecResolversTest_user_one", + "resolverModule": require('../RelayReader-ExecResolvers-test').RelayReaderExecResolversTest_user_one, + "path": "RelayReaderExecResolversTest_user_one" + }, + "linkedField": { + "alias": null, + "args": null, + "concreteType": "RelayReaderExecResolversTestUser", + "kind": "LinkedField", + "name": "RelayReaderExecResolversTest_user_one", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "fragment": (v1/*: any*/), + "kind": "RelayResolver", + "name": "name", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser____relay_model_instance.graphql'), require('../RelayReader-ExecResolvers-test').name, '__relay_model_instance', true), + "path": "RelayReaderExecResolversTest_user_one.name" + }, + { + "kind": "ClientEdgeToClientObject", + "concreteType": "RelayReaderExecResolversTestUser", + "modelResolvers": { + "RelayReaderExecResolversTestUser": { + "alias": null, + "args": null, + "fragment": (v0/*: any*/), + "kind": "RelayResolver", + "name": "__relay_model_instance", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser__id.graphql'), require('../RelayReader-ExecResolvers-test').RelayReaderExecResolversTestUser, 'id', true), + "path": "RelayReaderExecResolversTest_user_one.best_friend.__relay_model_instance" + } + }, + "backingField": { + "alias": null, + "args": null, + "fragment": (v1/*: any*/), + "kind": "RelayResolver", + "name": "best_friend", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser____relay_model_instance.graphql'), require('../RelayReader-ExecResolvers-test').best_friend, '__relay_model_instance', true), + "path": "RelayReaderExecResolversTest_user_one.best_friend" + }, + "linkedField": { + "alias": null, + "args": null, + "concreteType": "RelayReaderExecResolversTestUser", + "kind": "LinkedField", + "name": "best_friend", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "fragment": (v1/*: any*/), + "kind": "RelayResolver", + "name": "name", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser____relay_model_instance.graphql'), require('../RelayReader-ExecResolvers-test').name, '__relay_model_instance', true), + "path": "RelayReaderExecResolversTest_user_one.best_friend.name" + } + ], + "storageKey": null + } + }, + { + "kind": "ClientEdgeToClientObject", + "concreteType": "RelayReaderExecResolversTestUser", + "modelResolvers": { + "RelayReaderExecResolversTestUser": { + "alias": null, + "args": null, + "fragment": (v0/*: any*/), + "kind": "RelayResolver", + "name": "__relay_model_instance", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser__id.graphql'), require('../RelayReader-ExecResolvers-test').RelayReaderExecResolversTestUser, 'id', true), + "path": "RelayReaderExecResolversTest_user_one.friends.__relay_model_instance" + } + }, + "backingField": { + "alias": null, + "args": null, + "fragment": (v1/*: any*/), + "kind": "RelayResolver", + "name": "friends", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser____relay_model_instance.graphql'), require('../RelayReader-ExecResolvers-test').friends, '__relay_model_instance', true), + "path": "RelayReaderExecResolversTest_user_one.friends" + }, + "linkedField": { + "alias": null, + "args": null, + "concreteType": "RelayReaderExecResolversTestUser", + "kind": "LinkedField", + "name": "friends", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "fragment": (v1/*: any*/), + "kind": "RelayResolver", + "name": "name", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser____relay_model_instance.graphql'), require('../RelayReader-ExecResolvers-test').name, '__relay_model_instance', true), + "path": "RelayReaderExecResolversTest_user_one.friends.name" + } + ], + "storageKey": null + } + } + ], + "storageKey": null + } + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [], + "kind": "Operation", + "name": "RelayReaderExecResolversTestFalseProviderQuery", + "selections": [ + { + "kind": "ClientEdgeToClientObject", + "modelResolvers": { + "RelayReaderExecResolversTestUser": { + "resolverModule": require('../RelayReader-ExecResolvers-test').RelayReaderExecResolversTestUser + } + }, + "backingField": { + "name": "RelayReaderExecResolversTest_user_one", + "args": null, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": false, + "resolverInfo": { + "resolverFunction": require('../RelayReader-ExecResolvers-test').RelayReaderExecResolversTest_user_one, + "rootFragment": null + }, + "fragment": null + }, + "linkedField": { + "alias": null, + "args": null, + "concreteType": "RelayReaderExecResolversTestUser", + "kind": "LinkedField", + "name": "RelayReaderExecResolversTest_user_one", + "plural": false, + "selections": [ + (v4/*: any*/), + { + "kind": "ClientEdgeToClientObject", + "modelResolvers": { + "RelayReaderExecResolversTestUser": { + "resolverModule": require('../RelayReader-ExecResolvers-test').RelayReaderExecResolversTestUser + } + }, + "backingField": { + "name": "best_friend", + "args": null, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": false, + "resolverInfo": { + "resolverFunction": require('../RelayReader-ExecResolvers-test').best_friend, + "rootFragment": null + }, + "fragment": (v3/*: any*/) + }, + "linkedField": { + "alias": null, + "args": null, + "concreteType": "RelayReaderExecResolversTestUser", + "kind": "LinkedField", + "name": "best_friend", + "plural": false, + "selections": (v5/*: any*/), + "storageKey": null + } + }, + { + "kind": "ClientEdgeToClientObject", + "modelResolvers": { + "RelayReaderExecResolversTestUser": { + "resolverModule": require('../RelayReader-ExecResolvers-test').RelayReaderExecResolversTestUser + } + }, + "backingField": { + "name": "friends", + "args": null, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": false, + "resolverInfo": { + "resolverFunction": require('../RelayReader-ExecResolvers-test').friends, + "rootFragment": null + }, + "fragment": (v3/*: any*/) + }, + "linkedField": { + "alias": null, + "args": null, + "concreteType": "RelayReaderExecResolversTestUser", + "kind": "LinkedField", + "name": "friends", + "plural": true, + "selections": (v5/*: any*/), + "storageKey": null + } + }, + (v2/*: any*/) + ], + "storageKey": null + } + } + ], + "exec_time_resolvers_enabled_provider": require('../relayReaderTestExecTimeResolversFalseProvider') + }, + "params": { + "cacheID": "5b04e4ba9c8655b7b3a2cbce5a48eea8", + "id": null, + "metadata": {}, + "name": "RelayReaderExecResolversTestFalseProviderQuery", + "operationKind": "query", + "text": null + } +}; +})(); + +if (__DEV__) { + (node/*: any*/).hash = "ecae75fae0ced72851da368b4e1e828d"; +} + +module.exports = ((node/*: any*/)/*: ClientQuery< + RelayReaderExecResolversTestFalseProviderQuery$variables, + RelayReaderExecResolversTestFalseProviderQuery$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderExecResolversTestRunsQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderExecResolversTestQuery.graphql.js similarity index 75% rename from packages/relay-runtime/store/__tests__/__generated__/RelayReaderExecResolversTestRunsQuery.graphql.js rename to packages/relay-runtime/store/__tests__/__generated__/RelayReaderExecResolversTestQuery.graphql.js index 3fed50cb4c71d..ec7b5c00bb177 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderExecResolversTestRunsQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderExecResolversTestQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<857f9de42d12580d9d937fdcc8300a2e>> * @flow * @lightSyntaxTransform * @nogrep @@ -58,8 +58,8 @@ import {name as relayReaderExecResolversTestUserNameResolverType} from "../Relay args: void, context: TestResolverContextType, ) => ?string); -export type RelayReaderExecResolversTestRunsQuery$variables = {||}; -export type RelayReaderExecResolversTestRunsQuery$data = {| +export type RelayReaderExecResolversTestQuery$variables = {||}; +export type RelayReaderExecResolversTestQuery$data = {| +RelayReaderExecResolversTest_user_one: ?{| +best_friend: ?{| +name: ?string, @@ -70,9 +70,9 @@ export type RelayReaderExecResolversTestRunsQuery$data = {| +name: ?string, |}, |}; -export type RelayReaderExecResolversTestRunsQuery = {| - response: RelayReaderExecResolversTestRunsQuery$data, - variables: RelayReaderExecResolversTestRunsQuery$variables, +export type RelayReaderExecResolversTestQuery = {| + response: RelayReaderExecResolversTestQuery$data, + variables: RelayReaderExecResolversTestQuery$variables, |}; */ @@ -88,26 +88,53 @@ v1 = { "name": "RelayReaderExecResolversTestUser____relay_model_instance" }, v2 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null +}, +v3 = { + "kind": "InlineFragment", + "selections": [ + { + "name": "__relay_model_instance", + "args": null, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": false, + "resolverInfo": { + "resolverFunction": require('../RelayReader-ExecResolvers-test').RelayReaderExecResolversTestUser, + "rootFragment": null + }, + "fragment": { + "kind": "InlineFragment", + "selections": [ + (v2/*: any*/) + ], + "type": "RelayReaderExecResolversTestUser", + "abstractKey": null + } + } + ], + "type": "RelayReaderExecResolversTestUser", + "abstractKey": null +}, +v4 = { "name": "name", "args": null, "kind": "RelayResolver", "storageKey": null, "isOutputType": true, "resolverInfo": { - "resolverFunction": require('./../RelayReader-ExecResolvers-test').name, + "resolverFunction": require('../RelayReader-ExecResolvers-test').name, "rootFragment": null - } -}, -v3 = { - "alias": null, - "args": null, - "kind": "ScalarField", - "name": "id", - "storageKey": null + }, + "fragment": (v3/*: any*/) }, -v4 = [ - (v2/*: any*/), - (v3/*: any*/) +v5 = [ + (v4/*: any*/), + (v2/*: any*/) ]; return { "fragment": { @@ -116,7 +143,7 @@ return { "metadata": { "hasClientEdges": true }, - "name": "RelayReaderExecResolversTestRunsQuery", + "name": "RelayReaderExecResolversTestQuery", "selections": [ { "kind": "ClientEdgeToClientObject", @@ -127,8 +154,8 @@ return { "args": null, "fragment": (v0/*: any*/), "kind": "RelayResolver", - "name": "RelayReaderExecResolversTest_user_one", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser__id.graphql'), require('./../RelayReader-ExecResolvers-test').RelayReaderExecResolversTestUser, 'id', true), + "name": "__relay_model_instance", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser__id.graphql'), require('../RelayReader-ExecResolvers-test').RelayReaderExecResolversTestUser, 'id', true), "path": "RelayReaderExecResolversTest_user_one.__relay_model_instance" } }, @@ -138,7 +165,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "RelayReaderExecResolversTest_user_one", - "resolverModule": require('./../RelayReader-ExecResolvers-test').RelayReaderExecResolversTest_user_one, + "resolverModule": require('../RelayReader-ExecResolvers-test').RelayReaderExecResolversTest_user_one, "path": "RelayReaderExecResolversTest_user_one" }, "linkedField": { @@ -155,7 +182,7 @@ return { "fragment": (v1/*: any*/), "kind": "RelayResolver", "name": "name", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser____relay_model_instance.graphql'), require('./../RelayReader-ExecResolvers-test').name, '__relay_model_instance', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser____relay_model_instance.graphql'), require('../RelayReader-ExecResolvers-test').name, '__relay_model_instance', true), "path": "RelayReaderExecResolversTest_user_one.name" }, { @@ -167,8 +194,8 @@ return { "args": null, "fragment": (v0/*: any*/), "kind": "RelayResolver", - "name": "best_friend", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser__id.graphql'), require('./../RelayReader-ExecResolvers-test').RelayReaderExecResolversTestUser, 'id', true), + "name": "__relay_model_instance", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser__id.graphql'), require('../RelayReader-ExecResolvers-test').RelayReaderExecResolversTestUser, 'id', true), "path": "RelayReaderExecResolversTest_user_one.best_friend.__relay_model_instance" } }, @@ -178,7 +205,7 @@ return { "fragment": (v1/*: any*/), "kind": "RelayResolver", "name": "best_friend", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser____relay_model_instance.graphql'), require('./../RelayReader-ExecResolvers-test').best_friend, '__relay_model_instance', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser____relay_model_instance.graphql'), require('../RelayReader-ExecResolvers-test').best_friend, '__relay_model_instance', true), "path": "RelayReaderExecResolversTest_user_one.best_friend" }, "linkedField": { @@ -195,7 +222,7 @@ return { "fragment": (v1/*: any*/), "kind": "RelayResolver", "name": "name", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser____relay_model_instance.graphql'), require('./../RelayReader-ExecResolvers-test').name, '__relay_model_instance', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser____relay_model_instance.graphql'), require('../RelayReader-ExecResolvers-test').name, '__relay_model_instance', true), "path": "RelayReaderExecResolversTest_user_one.best_friend.name" } ], @@ -211,8 +238,8 @@ return { "args": null, "fragment": (v0/*: any*/), "kind": "RelayResolver", - "name": "friends", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser__id.graphql'), require('./../RelayReader-ExecResolvers-test').RelayReaderExecResolversTestUser, 'id', true), + "name": "__relay_model_instance", + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser__id.graphql'), require('../RelayReader-ExecResolvers-test').RelayReaderExecResolversTestUser, 'id', true), "path": "RelayReaderExecResolversTest_user_one.friends.__relay_model_instance" } }, @@ -222,7 +249,7 @@ return { "fragment": (v1/*: any*/), "kind": "RelayResolver", "name": "friends", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser____relay_model_instance.graphql'), require('./../RelayReader-ExecResolvers-test').friends, '__relay_model_instance', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser____relay_model_instance.graphql'), require('../RelayReader-ExecResolvers-test').friends, '__relay_model_instance', true), "path": "RelayReaderExecResolversTest_user_one.friends" }, "linkedField": { @@ -239,7 +266,7 @@ return { "fragment": (v1/*: any*/), "kind": "RelayResolver", "name": "name", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser____relay_model_instance.graphql'), require('./../RelayReader-ExecResolvers-test').name, '__relay_model_instance', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser____relay_model_instance.graphql'), require('../RelayReader-ExecResolvers-test').name, '__relay_model_instance', true), "path": "RelayReaderExecResolversTest_user_one.friends.name" } ], @@ -258,13 +285,13 @@ return { "operation": { "argumentDefinitions": [], "kind": "Operation", - "name": "RelayReaderExecResolversTestRunsQuery", + "name": "RelayReaderExecResolversTestQuery", "selections": [ { "kind": "ClientEdgeToClientObject", "modelResolvers": { "RelayReaderExecResolversTestUser": { - "resolverModule": require('./../RelayReader-ExecResolvers-test').RelayReaderExecResolversTestUser + "resolverModule": require('../RelayReader-ExecResolvers-test').RelayReaderExecResolversTestUser } }, "backingField": { @@ -274,9 +301,10 @@ return { "storageKey": null, "isOutputType": false, "resolverInfo": { - "resolverFunction": require('./../RelayReader-ExecResolvers-test').RelayReaderExecResolversTest_user_one, + "resolverFunction": require('../RelayReader-ExecResolvers-test').RelayReaderExecResolversTest_user_one, "rootFragment": null - } + }, + "fragment": null }, "linkedField": { "alias": null, @@ -286,12 +314,12 @@ return { "name": "RelayReaderExecResolversTest_user_one", "plural": false, "selections": [ - (v2/*: any*/), + (v4/*: any*/), { "kind": "ClientEdgeToClientObject", "modelResolvers": { "RelayReaderExecResolversTestUser": { - "resolverModule": require('./../RelayReader-ExecResolvers-test').RelayReaderExecResolversTestUser + "resolverModule": require('../RelayReader-ExecResolvers-test').RelayReaderExecResolversTestUser } }, "backingField": { @@ -301,9 +329,10 @@ return { "storageKey": null, "isOutputType": false, "resolverInfo": { - "resolverFunction": require('./../RelayReader-ExecResolvers-test').best_friend, + "resolverFunction": require('../RelayReader-ExecResolvers-test').best_friend, "rootFragment": null - } + }, + "fragment": (v3/*: any*/) }, "linkedField": { "alias": null, @@ -312,7 +341,7 @@ return { "kind": "LinkedField", "name": "best_friend", "plural": false, - "selections": (v4/*: any*/), + "selections": (v5/*: any*/), "storageKey": null } }, @@ -320,7 +349,7 @@ return { "kind": "ClientEdgeToClientObject", "modelResolvers": { "RelayReaderExecResolversTestUser": { - "resolverModule": require('./../RelayReader-ExecResolvers-test').RelayReaderExecResolversTestUser + "resolverModule": require('../RelayReader-ExecResolvers-test').RelayReaderExecResolversTestUser } }, "backingField": { @@ -330,9 +359,10 @@ return { "storageKey": null, "isOutputType": false, "resolverInfo": { - "resolverFunction": require('./../RelayReader-ExecResolvers-test').friends, + "resolverFunction": require('../RelayReader-ExecResolvers-test').friends, "rootFragment": null - } + }, + "fragment": (v3/*: any*/) }, "linkedField": { "alias": null, @@ -341,23 +371,23 @@ return { "kind": "LinkedField", "name": "friends", "plural": true, - "selections": (v4/*: any*/), + "selections": (v5/*: any*/), "storageKey": null } }, - (v3/*: any*/) + (v2/*: any*/) ], "storageKey": null } } ], - "use_exec_time_resolvers": true + "exec_time_resolvers_enabled_provider": require('../relayReaderTestExecTimeResolversTrueProvider') }, "params": { - "cacheID": "3f73e57d52c3f79eecc247feb0f865c5", + "cacheID": "bd1a2a7e4d80e5e5a017a7ff107bd87f", "id": null, "metadata": {}, - "name": "RelayReaderExecResolversTestRunsQuery", + "name": "RelayReaderExecResolversTestQuery", "operationKind": "query", "text": null } @@ -365,10 +395,10 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "578779b7fde7d06fa0d924a675453176"; + (node/*: any*/).hash = "de0d2442f1700c6fe635c62a4d645b91"; } module.exports = ((node/*: any*/)/*: ClientQuery< - RelayReaderExecResolversTestRunsQuery$variables, - RelayReaderExecResolversTestRunsQuery$data, + RelayReaderExecResolversTestQuery$variables, + RelayReaderExecResolversTestQuery$data, >*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderExecResolversTestUser____relay_model_instance.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderExecResolversTestUser____relay_model_instance.graphql.js index b56b9250851e9..b470869983abc 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderExecResolversTestUser____relay_model_instance.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderExecResolversTestUser____relay_model_instance.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<3df2bd80911e18192fca1e2d65d81cb5>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -57,7 +57,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "__relay_model_instance", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser__id.graphql'), require('./../RelayReader-ExecResolvers-test').RelayReaderExecResolversTestUser, 'id', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./RelayReaderExecResolversTestUser__id.graphql'), require('../RelayReader-ExecResolvers-test').RelayReaderExecResolversTestUser, 'id', true), "path": "__relay_model_instance" } ], diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRelayErrorHandlingTest2Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRelayErrorHandlingTest2Query.graphql.js index aef10180a4520..ebdb567350576 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRelayErrorHandlingTest2Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRelayErrorHandlingTest2Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<0f6b6d8a2ae71fa1597f832cb58c570f>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -65,7 +65,7 @@ var node/*: ConcreteRequest*/ = { }, "kind": "RelayResolver", "name": "last_name_throw_on_field_error", - "resolverModule": require('./../resolvers/UserLastNameThrowOnFieldError').last_name_throw_on_field_error, + "resolverModule": require('../resolvers/UserLastNameThrowOnFieldError').last_name_throw_on_field_error, "path": "me.last_name_throw_on_field_error" } ], diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRelayErrorHandlingTest5Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRelayErrorHandlingTest5Query.graphql.js index e491b34872af0..1aaba2043c839 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRelayErrorHandlingTest5Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRelayErrorHandlingTest5Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<190b58c7b51df5a80474a8eda840b44b>> + * @generated SignedSource<<88410745e66537fa4174c9fe5183f8e8>> * @flow * @lightSyntaxTransform * @nogrep @@ -67,7 +67,7 @@ var node/*: ConcreteRequest*/ = { }, "kind": "RelayResolver", "name": "last_name_throw_on_field_error", - "resolverModule": require('./../resolvers/UserLastNameThrowOnFieldError').last_name_throw_on_field_error, + "resolverModule": require('../resolvers/UserLastNameThrowOnFieldError').last_name_throw_on_field_error, "path": "me.last_name_throw_on_field_error" } ], diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRelayErrorHandlingTestNoncompliantEmptyLinkedFieldWithThrowOnFieldErrorQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRelayErrorHandlingTestNoncompliantEmptyLinkedFieldWithThrowOnFieldErrorQuery.graphql.js new file mode 100644 index 0000000000000..c6b07289258b1 --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRelayErrorHandlingTestNoncompliantEmptyLinkedFieldWithThrowOnFieldErrorQuery.graphql.js @@ -0,0 +1,152 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<520b92b838637598ce6b190feda24304>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ConcreteRequest, Query } from 'relay-runtime'; +export type RelayReaderRelayErrorHandlingTestNoncompliantEmptyLinkedFieldWithThrowOnFieldErrorQuery$variables = {||}; +export type RelayReaderRelayErrorHandlingTestNoncompliantEmptyLinkedFieldWithThrowOnFieldErrorQuery$data = {| + +node: ?{| + +__typename: string, + +friends?: ?{| + +edges: ?$ReadOnlyArray, + |}, + +id: string, + |}, +|}; +export type RelayReaderRelayErrorHandlingTestNoncompliantEmptyLinkedFieldWithThrowOnFieldErrorQuery = {| + response: RelayReaderRelayErrorHandlingTestNoncompliantEmptyLinkedFieldWithThrowOnFieldErrorQuery$data, + variables: RelayReaderRelayErrorHandlingTestNoncompliantEmptyLinkedFieldWithThrowOnFieldErrorQuery$variables, +|}; +*/ + +var node/*: ConcreteRequest*/ = (function(){ +var v0 = [ + { + "alias": null, + "args": [ + { + "kind": "Literal", + "name": "id", + "value": "1" + } + ], + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null + }, + { + "kind": "InlineFragment", + "selections": [ + { + "alias": null, + "args": [ + { + "kind": "Literal", + "name": "first", + "value": 3 + } + ], + "concreteType": "FriendsConnection", + "kind": "LinkedField", + "name": "friends", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "FriendsEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "cursor", + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": "friends(first:3)" + } + ], + "type": "User", + "abstractKey": null + } + ], + "storageKey": "node(id:\"1\")" + } +]; +return { + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": { + "throwOnFieldError": true + }, + "name": "RelayReaderRelayErrorHandlingTestNoncompliantEmptyLinkedFieldWithThrowOnFieldErrorQuery", + "selections": (v0/*: any*/), + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [], + "kind": "Operation", + "name": "RelayReaderRelayErrorHandlingTestNoncompliantEmptyLinkedFieldWithThrowOnFieldErrorQuery", + "selections": (v0/*: any*/) + }, + "params": { + "cacheID": "b4e49b016710bab100689bfb7b5fd99a", + "id": null, + "metadata": {}, + "name": "RelayReaderRelayErrorHandlingTestNoncompliantEmptyLinkedFieldWithThrowOnFieldErrorQuery", + "operationKind": "query", + "text": "query RelayReaderRelayErrorHandlingTestNoncompliantEmptyLinkedFieldWithThrowOnFieldErrorQuery {\n node(id: \"1\") {\n id\n __typename\n ... on User {\n friends(first: 3) {\n edges {\n cursor\n }\n }\n }\n }\n}\n" + } +}; +})(); + +if (__DEV__) { + (node/*: any*/).hash = "f545f883732869b6e4527c5f09a6b5eb"; +} + +module.exports = ((node/*: any*/)/*: Query< + RelayReaderRelayErrorHandlingTestNoncompliantEmptyLinkedFieldWithThrowOnFieldErrorQuery$variables, + RelayReaderRelayErrorHandlingTestNoncompliantEmptyLinkedFieldWithThrowOnFieldErrorQuery$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRelayErrorHandlingTestNoncompliantEmptyLinkedFieldWithoutThrowOnFieldErrorQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRelayErrorHandlingTestNoncompliantEmptyLinkedFieldWithoutThrowOnFieldErrorQuery.graphql.js new file mode 100644 index 0000000000000..5db2b2bc612b1 --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRelayErrorHandlingTestNoncompliantEmptyLinkedFieldWithoutThrowOnFieldErrorQuery.graphql.js @@ -0,0 +1,150 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<2c4e7ac15042028a5c73454531cbb12b>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ConcreteRequest, Query } from 'relay-runtime'; +export type RelayReaderRelayErrorHandlingTestNoncompliantEmptyLinkedFieldWithoutThrowOnFieldErrorQuery$variables = {||}; +export type RelayReaderRelayErrorHandlingTestNoncompliantEmptyLinkedFieldWithoutThrowOnFieldErrorQuery$data = {| + +node: ?{| + +__typename: string, + +friends?: ?{| + +edges: ?$ReadOnlyArray, + |}, + +id: string, + |}, +|}; +export type RelayReaderRelayErrorHandlingTestNoncompliantEmptyLinkedFieldWithoutThrowOnFieldErrorQuery = {| + response: RelayReaderRelayErrorHandlingTestNoncompliantEmptyLinkedFieldWithoutThrowOnFieldErrorQuery$data, + variables: RelayReaderRelayErrorHandlingTestNoncompliantEmptyLinkedFieldWithoutThrowOnFieldErrorQuery$variables, +|}; +*/ + +var node/*: ConcreteRequest*/ = (function(){ +var v0 = [ + { + "alias": null, + "args": [ + { + "kind": "Literal", + "name": "id", + "value": "1" + } + ], + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null + }, + { + "kind": "InlineFragment", + "selections": [ + { + "alias": null, + "args": [ + { + "kind": "Literal", + "name": "first", + "value": 3 + } + ], + "concreteType": "FriendsConnection", + "kind": "LinkedField", + "name": "friends", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "FriendsEdge", + "kind": "LinkedField", + "name": "edges", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "cursor", + "storageKey": null + } + ], + "storageKey": null + } + ], + "storageKey": "friends(first:3)" + } + ], + "type": "User", + "abstractKey": null + } + ], + "storageKey": "node(id:\"1\")" + } +]; +return { + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "RelayReaderRelayErrorHandlingTestNoncompliantEmptyLinkedFieldWithoutThrowOnFieldErrorQuery", + "selections": (v0/*: any*/), + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [], + "kind": "Operation", + "name": "RelayReaderRelayErrorHandlingTestNoncompliantEmptyLinkedFieldWithoutThrowOnFieldErrorQuery", + "selections": (v0/*: any*/) + }, + "params": { + "cacheID": "9bcd1fa083106133acd9429ae203b4c6", + "id": null, + "metadata": {}, + "name": "RelayReaderRelayErrorHandlingTestNoncompliantEmptyLinkedFieldWithoutThrowOnFieldErrorQuery", + "operationKind": "query", + "text": "query RelayReaderRelayErrorHandlingTestNoncompliantEmptyLinkedFieldWithoutThrowOnFieldErrorQuery {\n node(id: \"1\") {\n id\n __typename\n ... on User {\n friends(first: 3) {\n edges {\n cursor\n }\n }\n }\n }\n}\n" + } +}; +})(); + +if (__DEV__) { + (node/*: any*/).hash = "f4834de5be0525aba139b83eb190e02f"; +} + +module.exports = ((node/*: any*/)/*: Query< + RelayReaderRelayErrorHandlingTestNoncompliantEmptyLinkedFieldWithoutThrowOnFieldErrorQuery$variables, + RelayReaderRelayErrorHandlingTestNoncompliantEmptyLinkedFieldWithoutThrowOnFieldErrorQuery$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRelayErrorHandlingTestNoncompliantEmptyScalarFieldWithThrowOnFieldErrorQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRelayErrorHandlingTestNoncompliantEmptyScalarFieldWithThrowOnFieldErrorQuery.graphql.js new file mode 100644 index 0000000000000..a8ec8384776a7 --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRelayErrorHandlingTestNoncompliantEmptyScalarFieldWithThrowOnFieldErrorQuery.graphql.js @@ -0,0 +1,120 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<7d7421d8f95edf6cade0e79bbabb6616>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ConcreteRequest, Query } from 'relay-runtime'; +export type RelayReaderRelayErrorHandlingTestNoncompliantEmptyScalarFieldWithThrowOnFieldErrorQuery$variables = {||}; +export type RelayReaderRelayErrorHandlingTestNoncompliantEmptyScalarFieldWithThrowOnFieldErrorQuery$data = {| + +node: ?{| + +__typename: string, + +emailAddresses?: ?$ReadOnlyArray, + +id: string, + |}, +|}; +export type RelayReaderRelayErrorHandlingTestNoncompliantEmptyScalarFieldWithThrowOnFieldErrorQuery = {| + response: RelayReaderRelayErrorHandlingTestNoncompliantEmptyScalarFieldWithThrowOnFieldErrorQuery$data, + variables: RelayReaderRelayErrorHandlingTestNoncompliantEmptyScalarFieldWithThrowOnFieldErrorQuery$variables, +|}; +*/ + +var node/*: ConcreteRequest*/ = (function(){ +var v0 = [ + { + "alias": null, + "args": [ + { + "kind": "Literal", + "name": "id", + "value": "1" + } + ], + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null + }, + { + "kind": "InlineFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "emailAddresses", + "storageKey": null + } + ], + "type": "User", + "abstractKey": null + } + ], + "storageKey": "node(id:\"1\")" + } +]; +return { + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": { + "throwOnFieldError": true + }, + "name": "RelayReaderRelayErrorHandlingTestNoncompliantEmptyScalarFieldWithThrowOnFieldErrorQuery", + "selections": (v0/*: any*/), + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [], + "kind": "Operation", + "name": "RelayReaderRelayErrorHandlingTestNoncompliantEmptyScalarFieldWithThrowOnFieldErrorQuery", + "selections": (v0/*: any*/) + }, + "params": { + "cacheID": "2f8f0fea05a87b91ed80d5f62dd9308c", + "id": null, + "metadata": {}, + "name": "RelayReaderRelayErrorHandlingTestNoncompliantEmptyScalarFieldWithThrowOnFieldErrorQuery", + "operationKind": "query", + "text": "query RelayReaderRelayErrorHandlingTestNoncompliantEmptyScalarFieldWithThrowOnFieldErrorQuery {\n node(id: \"1\") {\n id\n __typename\n ... on User {\n emailAddresses\n }\n }\n}\n" + } +}; +})(); + +if (__DEV__) { + (node/*: any*/).hash = "39bcfd3ec0582f9ae2c60746ee9c98a3"; +} + +module.exports = ((node/*: any*/)/*: Query< + RelayReaderRelayErrorHandlingTestNoncompliantEmptyScalarFieldWithThrowOnFieldErrorQuery$variables, + RelayReaderRelayErrorHandlingTestNoncompliantEmptyScalarFieldWithThrowOnFieldErrorQuery$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRelayErrorHandlingTestNoncompliantEmptyScalarFieldWithoutThrowOnFieldErrorQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRelayErrorHandlingTestNoncompliantEmptyScalarFieldWithoutThrowOnFieldErrorQuery.graphql.js new file mode 100644 index 0000000000000..e9908223116f6 --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRelayErrorHandlingTestNoncompliantEmptyScalarFieldWithoutThrowOnFieldErrorQuery.graphql.js @@ -0,0 +1,118 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<515ab98f99462586696aee002f3bf31f>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ConcreteRequest, Query } from 'relay-runtime'; +export type RelayReaderRelayErrorHandlingTestNoncompliantEmptyScalarFieldWithoutThrowOnFieldErrorQuery$variables = {||}; +export type RelayReaderRelayErrorHandlingTestNoncompliantEmptyScalarFieldWithoutThrowOnFieldErrorQuery$data = {| + +node: ?{| + +__typename: string, + +emailAddresses?: ?$ReadOnlyArray, + +id: string, + |}, +|}; +export type RelayReaderRelayErrorHandlingTestNoncompliantEmptyScalarFieldWithoutThrowOnFieldErrorQuery = {| + response: RelayReaderRelayErrorHandlingTestNoncompliantEmptyScalarFieldWithoutThrowOnFieldErrorQuery$data, + variables: RelayReaderRelayErrorHandlingTestNoncompliantEmptyScalarFieldWithoutThrowOnFieldErrorQuery$variables, +|}; +*/ + +var node/*: ConcreteRequest*/ = (function(){ +var v0 = [ + { + "alias": null, + "args": [ + { + "kind": "Literal", + "name": "id", + "value": "1" + } + ], + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null + }, + { + "kind": "InlineFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "emailAddresses", + "storageKey": null + } + ], + "type": "User", + "abstractKey": null + } + ], + "storageKey": "node(id:\"1\")" + } +]; +return { + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "RelayReaderRelayErrorHandlingTestNoncompliantEmptyScalarFieldWithoutThrowOnFieldErrorQuery", + "selections": (v0/*: any*/), + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [], + "kind": "Operation", + "name": "RelayReaderRelayErrorHandlingTestNoncompliantEmptyScalarFieldWithoutThrowOnFieldErrorQuery", + "selections": (v0/*: any*/) + }, + "params": { + "cacheID": "a278c7d054186ae97ab78b656d9a57c6", + "id": null, + "metadata": {}, + "name": "RelayReaderRelayErrorHandlingTestNoncompliantEmptyScalarFieldWithoutThrowOnFieldErrorQuery", + "operationKind": "query", + "text": "query RelayReaderRelayErrorHandlingTestNoncompliantEmptyScalarFieldWithoutThrowOnFieldErrorQuery {\n node(id: \"1\") {\n id\n __typename\n ... on User {\n emailAddresses\n }\n }\n}\n" + } +}; +})(); + +if (__DEV__) { + (node/*: any*/).hash = "fbd6702b1d66c9949d49205fa661c44f"; +} + +module.exports = ((node/*: any*/)/*: Query< + RelayReaderRelayErrorHandlingTestNoncompliantEmptyScalarFieldWithoutThrowOnFieldErrorQuery$variables, + RelayReaderRelayErrorHandlingTestNoncompliantEmptyScalarFieldWithoutThrowOnFieldErrorQuery$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRelayErrorHandlingTestResolverClientEdgeClientObjectWithMissingDataQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRelayErrorHandlingTestResolverClientEdgeClientObjectWithMissingDataQuery.graphql.js index 9fc8f1701e5d6..94a76023776a8 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRelayErrorHandlingTestResolverClientEdgeClientObjectWithMissingDataQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRelayErrorHandlingTestResolverClientEdgeClientObjectWithMissingDataQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -92,7 +92,7 @@ return { }, "kind": "RelayResolver", "name": "astrological_sign", - "resolverModule": require('./../resolvers/UserAstrologicalSignResolver').astrological_sign, + "resolverModule": require('../resolvers/UserAstrologicalSignResolver').astrological_sign, "path": "me.astrological_sign" }, "linkedField": { @@ -103,7 +103,11 @@ return { "name": "astrological_sign", "plural": false, "selections": [ - (v0/*: any*/) + { + "kind": "CatchField", + "field": (v0/*: any*/), + "to": "NULL" + } ], "storageKey": null } @@ -202,7 +206,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "f58793dca8d722401223f2d241a24705"; + (node/*: any*/).hash = "10b9776fee65d766ecdf6dec167c69da"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRelayErrorHandlingTestResolverClientEdgeWithMissingDataQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRelayErrorHandlingTestResolverClientEdgeWithMissingDataQuery.graphql.js index 2a0ea0068246b..4897aab92b457 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRelayErrorHandlingTestResolverClientEdgeWithMissingDataQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRelayErrorHandlingTestResolverClientEdgeWithMissingDataQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<78b08a8df8f4ca89e6a949e9e8ff4453>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -76,7 +76,7 @@ var node/*: ConcreteRequest*/ = { }, "kind": "RelayResolver", "name": "client_edge", - "resolverModule": require('./../resolvers/UserClientEdgeResolver').client_edge, + "resolverModule": require('../resolvers/UserClientEdgeResolver').client_edge, "path": "me.client_edge" }, "linkedField": { diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRelayErrorHandlingTestResolverClientPluralEdgeClientObjectWithMissingDataQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRelayErrorHandlingTestResolverClientPluralEdgeClientObjectWithMissingDataQuery.graphql.js index cdf1f359f9466..30eb8e21adf9e 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRelayErrorHandlingTestResolverClientPluralEdgeClientObjectWithMissingDataQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRelayErrorHandlingTestResolverClientPluralEdgeClientObjectWithMissingDataQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -82,7 +82,7 @@ return { }, "kind": "RelayResolver", "name": "all_astrological_signs", - "resolverModule": require('./../resolvers/QueryAllAstrologicalSignsResolver').all_astrological_signs, + "resolverModule": require('../resolvers/QueryAllAstrologicalSignsResolver').all_astrological_signs, "path": "all_astrological_signs" }, "linkedField": { @@ -93,7 +93,11 @@ return { "name": "all_astrological_signs", "plural": true, "selections": [ - (v0/*: any*/) + { + "kind": "CatchField", + "field": (v0/*: any*/), + "to": "NULL" + } ], "storageKey": null } @@ -171,7 +175,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "301d67aac4a492fec857e800bb8ce687"; + (node/*: any*/).hash = "643cec3823d195129218fe86f024fb41"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRequiredFieldsTest25Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRequiredFieldsTest25Query.graphql.js index 46d817cd3a497..66e9189f77e27 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRequiredFieldsTest25Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRequiredFieldsTest25Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<999988a6c8306737a4a984e4a6491875>> * @flow * @lightSyntaxTransform * @nogrep @@ -98,7 +98,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "client_object", - "resolverModule": require('./../resolvers/UserClientEdgeClientObjectResolver').client_object, + "resolverModule": require('../resolvers/UserClientEdgeClientObjectResolver').client_object, "path": "me.client_object", "normalizationInfo": { "kind": "OutputType", diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRequiredFieldsTest26Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRequiredFieldsTest26Query.graphql.js index 2107e5cd30a37..fd8164839afe6 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRequiredFieldsTest26Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRequiredFieldsTest26Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<67eabe2dab391aeb987ef97e421c357f>> + * @generated SignedSource<<4d2ea4a7f825be91b9b872b750f8eb5e>> * @flow * @lightSyntaxTransform * @nogrep @@ -95,7 +95,7 @@ return { }, "kind": "RelayResolver", "name": "astrological_sign", - "resolverModule": require('./../resolvers/UserAstrologicalSignResolver').astrological_sign, + "resolverModule": require('../resolvers/UserAstrologicalSignResolver').astrological_sign, "path": "me.astrological_sign" }, "linkedField": { @@ -116,7 +116,7 @@ return { }, "kind": "RelayResolver", "name": "name", - "resolverModule": require('./../resolvers/AstrologicalSignNameResolver').name, + "resolverModule": require('../resolvers/AstrologicalSignNameResolver').name, "path": "me.astrological_sign.name" } ], diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRequiredFieldsTest27Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRequiredFieldsTest27Query.graphql.js index 6d3ef3c5044cc..4dcc005458960 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRequiredFieldsTest27Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRequiredFieldsTest27Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<529e4e92ddb5997e328edcbec79f8ea5>> + * @generated SignedSource<<66373637d46c7080dd277569b7235e8f>> * @flow * @lightSyntaxTransform * @nogrep @@ -85,7 +85,7 @@ return { }, "kind": "RelayResolver", "name": "all_astrological_signs", - "resolverModule": require('./../resolvers/QueryAllAstrologicalSignsResolver').all_astrological_signs, + "resolverModule": require('../resolvers/QueryAllAstrologicalSignsResolver').all_astrological_signs, "path": "all_astrological_signs" }, "linkedField": { @@ -106,7 +106,7 @@ return { }, "kind": "RelayResolver", "name": "name", - "resolverModule": require('./../resolvers/AstrologicalSignNameResolver').name, + "resolverModule": require('../resolvers/AstrologicalSignNameResolver').name, "path": "all_astrological_signs.name" } ], diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRequiredFieldsTest28Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRequiredFieldsTest28Query.graphql.js index eec0d1ff6ce42..970f6b718ab47 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRequiredFieldsTest28Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRequiredFieldsTest28Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<3341a2b08cf9bda5adb3b7f9763c432b>> + * @generated SignedSource<<1794e78bb43d7aee1a7a2741f6b03bf8>> * @flow * @lightSyntaxTransform * @nogrep @@ -61,7 +61,7 @@ var node/*: ClientRequest*/ = { "fragment": null, "kind": "RelayLiveResolver", "name": "live_user_resolver_always_suspend", - "resolverModule": require('./../resolvers/LiveUserAlwaysSuspendResolver').live_user_resolver_always_suspend, + "resolverModule": require('../resolvers/LiveUserAlwaysSuspendResolver').live_user_resolver_always_suspend, "path": "live_user_resolver_always_suspend" }, "linkedField": { diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRequiredFieldsTest29Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRequiredFieldsTest29Query.graphql.js index 2b0ab61c8f8a2..d30fea14800af 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRequiredFieldsTest29Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderRequiredFieldsTest29Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<0957bb9e4355a585335953ebed19566e>> + * @generated SignedSource<<9c56d5cc14302b306bbb11420772888b>> * @flow * @lightSyntaxTransform * @nogrep @@ -102,7 +102,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "client_object", - "resolverModule": require('./../resolvers/UserClientEdgeClientObjectResolver').client_object, + "resolverModule": require('../resolvers/UserClientEdgeClientObjectResolver').client_object, "path": "me.client_object", "normalizationInfo": { "kind": "OutputType", diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest10Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest10Query.graphql.js index fa6886a26322c..5ca5254df6c55 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest10Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest10Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<620ed64200414e5c87e929f17794fb53>> + * @generated SignedSource<<44059fbcd557a595e9beafcc7cf1a312>> * @flow * @lightSyntaxTransform * @nogrep @@ -65,7 +65,7 @@ var node/*: ConcreteRequest*/ = { }, "kind": "RelayResolver", "name": "greeting", - "resolverModule": require('./../resolvers/UserGreetingResolver').greeting, + "resolverModule": require('../resolvers/UserGreetingResolver').greeting, "path": "me.greeting" } ], diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest11Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest11Query.graphql.js index 501ce75ec243b..0ba2f11522ef7 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest11Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest11Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<48390822d868667ba649139c0026c342>> * @flow * @lightSyntaxTransform * @nogrep @@ -65,7 +65,7 @@ var node/*: ConcreteRequest*/ = { }, "kind": "RelayResolver", "name": "greeting", - "resolverModule": require('./../resolvers/UserGreetingResolver').greeting, + "resolverModule": require('../resolvers/UserGreetingResolver').greeting, "path": "me.the_alias" } ], diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest12Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest12Query.graphql.js index e4691e9bf2901..559cb3a4474a5 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest12Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest12Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -65,7 +65,7 @@ var node/*: ConcreteRequest*/ = { }, "kind": "RelayResolver", "name": "always_throws", - "resolverModule": require('./../resolvers/UserAlwaysThrowsResolver').always_throws, + "resolverModule": require('../resolvers/UserAlwaysThrowsResolver').always_throws, "path": "me.always_throws" } ], diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest13Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest13Query.graphql.js index 167af1c98c5fe..5bb17eeee8dfd 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest13Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest13Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<497c9028fad4da1b26032b742e458f71>> + * @generated SignedSource<<96f66fcf8bb432a16fd9d81f37fd05cf>> * @flow * @lightSyntaxTransform * @nogrep @@ -65,7 +65,7 @@ var node/*: ConcreteRequest*/ = { }, "kind": "RelayResolver", "name": "always_throws_transitively", - "resolverModule": require('./../resolvers/UserAlwaysThrowsTransitivelyResolver').always_throws_transitively, + "resolverModule": require('../resolvers/UserAlwaysThrowsTransitivelyResolver').always_throws_transitively, "path": "me.always_throws_transitively" } ], diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest14Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest14Query.graphql.js index 0757a50fe3b08..6ef476649c3e3 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest14Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest14Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<1460dd145afb22d6e34e70c030c1e4c2>> * @flow * @lightSyntaxTransform * @nogrep @@ -55,7 +55,7 @@ var node/*: ConcreteRequest*/ = { }, "kind": "RelayResolver", "name": "throw_before_read", - "resolverModule": require('./../resolvers/ThrowBeforeReadResolver').throw_before_read, + "resolverModule": require('../resolvers/ThrowBeforeReadResolver').throw_before_read, "path": "throw_before_read" } ], diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest15Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest15Query.graphql.js index 6d7766f963d4c..2ac18528888b7 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest15Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest15Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<98c6563426ccf43f8cae68b86f433d24>> * @flow * @lightSyntaxTransform * @nogrep @@ -55,7 +55,7 @@ var node/*: ConcreteRequest*/ = { }, "kind": "RelayResolver", "name": "undefined_field", - "resolverModule": require('./../resolvers/UndefinedFieldResolver').undefined_field, + "resolverModule": require('../resolvers/UndefinedFieldResolver').undefined_field, "path": "undefined_field" } ], diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest16Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest16Query.graphql.js index 073dabc6a8fb6..fc6dbe0d3b7c0 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest16Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest16Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<732af378734f7a81afa5b0455b34c25f>> + * @generated SignedSource<<8781ad779d8e2adb7024a442021265ad>> * @flow * @lightSyntaxTransform * @nogrep @@ -84,7 +84,7 @@ return { }, "kind": "RelayResolver", "name": "user_profile_picture_uri_with_scale", - "resolverModule": require('./../resolvers/UserProfilePictureResolver').user_profile_picture_uri_with_scale, + "resolverModule": require('../resolvers/UserProfilePictureResolver').user_profile_picture_uri_with_scale, "path": "me.user_profile_picture_uri_with_scale" } ], diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest17Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest17Query.graphql.js index aebabfea012a1..edbed66339405 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest17Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest17Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<37be28848d26c929fa7f4db4c38beeb7>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -67,7 +67,7 @@ var node/*: ConcreteRequest*/ = { }, "kind": "RelayResolver", "name": "user_profile_picture_uri_with_scale_and_default_value", - "resolverModule": require('./../resolvers/UserProfilePictureWithDefaultValueResolver').user_profile_picture_uri_with_scale_and_default_value, + "resolverModule": require('../resolvers/UserProfilePictureWithDefaultValueResolver').user_profile_picture_uri_with_scale_and_default_value, "path": "me.user_profile_picture_uri_with_scale_and_default_value" } ], diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest18Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest18Query.graphql.js index ba5c6477f1f78..1965ff38bfbe5 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest18Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest18Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<3989404a0632d3d51bfe4d6519816278>> + * @generated SignedSource<<235c5114cf434e58b89746d516a0eae6>> * @flow * @lightSyntaxTransform * @nogrep @@ -75,7 +75,7 @@ return { }, "kind": "RelayResolver", "name": "user_profile_picture_uri_with_scale_and_default_value", - "resolverModule": require('./../resolvers/UserProfilePictureWithDefaultValueResolver').user_profile_picture_uri_with_scale_and_default_value, + "resolverModule": require('../resolvers/UserProfilePictureWithDefaultValueResolver').user_profile_picture_uri_with_scale_and_default_value, "path": "me.profile_picture2" } ], diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest19Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest19Query.graphql.js index dfec13e39c080..f16cefc6671f6 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest19Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest19Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<2b3ae6f88bceffe51b14344044ea9357>> + * @generated SignedSource<<1d0177901587791799ef7a9767c11622>> * @flow * @lightSyntaxTransform * @nogrep @@ -112,7 +112,7 @@ return { }, "kind": "RelayResolver", "name": "user_profile_picture_uri_with_scale_and_default_value", - "resolverModule": require('./../resolvers/UserProfilePictureWithDefaultValueResolver').user_profile_picture_uri_with_scale_and_default_value, + "resolverModule": require('../resolvers/UserProfilePictureWithDefaultValueResolver').user_profile_picture_uri_with_scale_and_default_value, "path": "me.profile_picture2" }, (v3/*: any*/) diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest1Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest1Query.graphql.js index 7ce65ab5acf38..89f6f8d74f647 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest1Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest1Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<87993e2b88ba6a400a535cdc56a8df33>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -65,7 +65,7 @@ var node/*: ConcreteRequest*/ = { }, "kind": "RelayResolver", "name": "greeting", - "resolverModule": require('./../resolvers/UserGreetingResolver').greeting, + "resolverModule": require('../resolvers/UserGreetingResolver').greeting, "path": "me.greeting" } ], diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest20Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest20Query.graphql.js index 85fe34d3ba0af..867fb8d2406df 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest20Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest20Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<5f7402f232c5b23124229dd5a279f852>> + * @generated SignedSource<<81e37610e1ce42aee65534a7f7b4945b>> * @flow * @lightSyntaxTransform * @nogrep @@ -84,7 +84,7 @@ return { }, "kind": "RelayResolver", "name": "user_profile_picture_uri_with_scale", - "resolverModule": require('./../resolvers/UserProfilePictureResolver').user_profile_picture_uri_with_scale, + "resolverModule": require('../resolvers/UserProfilePictureResolver').user_profile_picture_uri_with_scale, "path": "me.profile_picture" } ], diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest21Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest21Query.graphql.js index c3d228dbbe174..c29d1fad88a30 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest21Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest21Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<2b49a447d417455857952ac5becc6df9>> + * @generated SignedSource<<4bd6e021a50e5a68f54574bf5244bdd8>> * @flow * @lightSyntaxTransform * @nogrep @@ -84,7 +84,7 @@ return { }, "kind": "RelayResolver", "name": "user_profile_picture_uri_with_scale", - "resolverModule": require('./../resolvers/UserProfilePictureResolver').user_profile_picture_uri_with_scale, + "resolverModule": require('../resolvers/UserProfilePictureResolver').user_profile_picture_uri_with_scale, "path": "me.profile_picture" } ], diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest22Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest22Query.graphql.js index 519c8b86ec575..07e42c2910da7 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest22Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest22Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -99,7 +99,7 @@ return { }, "kind": "RelayResolver", "name": "user_profile_picture_uri_with_scale_and_additional_argument", - "resolverModule": require('./../resolvers/UserProfilePictureWithRuntimeArgumentResolver').user_profile_picture_uri_with_scale_and_additional_argument, + "resolverModule": require('../resolvers/UserProfilePictureWithRuntimeArgumentResolver').user_profile_picture_uri_with_scale_and_additional_argument, "path": "me.profile_picture" } ], diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest24Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest24Query.graphql.js index 1c772f8016fd8..eb9c7271603de 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest24Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest24Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<9f553ae2e533f771e1091d967ec3ae58>> * @flow * @lightSyntaxTransform * @nogrep @@ -75,7 +75,7 @@ var node/*: ConcreteRequest*/ = { }, "kind": "RelayResolver", "name": "client_edge", - "resolverModule": require('./../resolvers/UserClientEdgeResolver').client_edge, + "resolverModule": require('../resolvers/UserClientEdgeResolver').client_edge, "path": "me.client_edge" }, "linkedField": { diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest2Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest2Query.graphql.js index 0a439770042d5..2aced860f1b36 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest2Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest2Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<60f62f906d38fb0b33e62d87e40efff5>> * @flow * @lightSyntaxTransform * @nogrep @@ -65,7 +65,7 @@ var node/*: ConcreteRequest*/ = { }, "kind": "RelayResolver", "name": "constant_dependent", - "resolverModule": require('./../resolvers/UserConstantDependentResolver').constant_dependent, + "resolverModule": require('../resolvers/UserConstantDependentResolver').constant_dependent, "path": "me.constant_dependent" } ], diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest3Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest3Query.graphql.js index 4daad956cf759..f8acb4bbfa92a 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest3Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest3Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<225f127331f1b473f04b0634c7c52f4a>> * @flow * @lightSyntaxTransform * @nogrep @@ -65,7 +65,7 @@ var node/*: ConcreteRequest*/ = { }, "kind": "RelayResolver", "name": "greeting", - "resolverModule": require('./../resolvers/UserGreetingResolver').greeting, + "resolverModule": require('../resolvers/UserGreetingResolver').greeting, "path": "me.greeting" } ], diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest4Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest4Query.graphql.js index f04a9903ed87e..bdeebea42bc44 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest4Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest4Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<6a4354458303c284c4110c2eca722504>> + * @generated SignedSource<<14534e07cee00ca4a0e69b16777a2a44>> * @flow * @lightSyntaxTransform * @nogrep @@ -73,7 +73,7 @@ return { }, "kind": "RelayResolver", "name": "best_friend_greeting", - "resolverModule": require('./../resolvers/UserBestFriendGreetingResolver').best_friend_greeting, + "resolverModule": require('../resolvers/UserBestFriendGreetingResolver').best_friend_greeting, "path": "me.best_friend_greeting" } ], diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest5Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest5Query.graphql.js index ec4bcbef1809a..a3700689e1a1e 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest5Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest5Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<8575f7903ffc9f18712b2845ecd8afca>> + * @generated SignedSource<<96c3ffb47638b01931d8b0504df03a24>> * @flow * @lightSyntaxTransform * @nogrep @@ -65,7 +65,7 @@ var node/*: ConcreteRequest*/ = { }, "kind": "RelayResolver", "name": "shouted_greeting", - "resolverModule": require('./../resolvers/UserShoutedGreetingResolver').shouted_greeting, + "resolverModule": require('../resolvers/UserShoutedGreetingResolver').shouted_greeting, "path": "me.shouted_greeting" } ], diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest6Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest6Query.graphql.js index b793aa7415509..1c319208d4849 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest6Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest6Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<98e4bfbb28332b751e6a803728b28fde>> * @flow * @lightSyntaxTransform * @nogrep @@ -73,7 +73,7 @@ return { }, "kind": "RelayResolver", "name": "best_friend_shouted_greeting", - "resolverModule": require('./../resolvers/UserBestFriendShoutedGreetingResolver').best_friend_shouted_greeting, + "resolverModule": require('../resolvers/UserBestFriendShoutedGreetingResolver').best_friend_shouted_greeting, "path": "me.best_friend_shouted_greeting" } ], diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest8Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest8Query.graphql.js index aae36f4485b9a..8fcf75b57233f 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest8Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest8Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<309cb9691fcc3100cdc2371e35bd3d4b>> + * @generated SignedSource<<0f51d603b9eb4efd57fa5e2131bea477>> * @flow * @lightSyntaxTransform * @nogrep @@ -67,7 +67,7 @@ var node/*: ConcreteRequest*/ = { }, "kind": "RelayResolver", "name": "name_passthrough", - "resolverModule": require('./../resolvers/UserNamePassthroughResolver').name_passthrough, + "resolverModule": require('../resolvers/UserNamePassthroughResolver').name_passthrough, "path": "me.name_passthrough" }, "action": "NONE" diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest9Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest9Query.graphql.js index 355cd19952cc4..1d9796eaa8b38 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest9Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTest9Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<8062ebaab2bafdcbb879b50e94fd331b>> + * @generated SignedSource<<338c8f51f3947e661000e491b43e5e55>> * @flow * @lightSyntaxTransform * @nogrep @@ -65,7 +65,7 @@ var node/*: ConcreteRequest*/ = { }, "kind": "RelayResolver", "name": "greeting", - "resolverModule": require('./../resolvers/UserGreetingResolver').greeting, + "resolverModule": require('../resolvers/UserGreetingResolver').greeting, "path": "me.greeting" } ], diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTestCustomGreetingDynamicQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTestCustomGreetingDynamicQuery.graphql.js index 3d5dafa9fe698..cc6364193d0b2 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTestCustomGreetingDynamicQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTestCustomGreetingDynamicQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<0d2e5e18104d446088f8d64b7418c8a7>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -115,7 +115,7 @@ return { "fragment": (v2/*: any*/), "kind": "RelayResolver", "name": "custom_greeting", - "resolverModule": require('./../resolvers/UserCustomGreetingResolver').custom_greeting, + "resolverModule": require('../resolvers/UserCustomGreetingResolver').custom_greeting, "path": "me.dynamic_greeting" }, { @@ -124,7 +124,7 @@ return { "fragment": (v2/*: any*/), "kind": "RelayResolver", "name": "custom_greeting", - "resolverModule": require('./../resolvers/UserCustomGreetingResolver').custom_greeting, + "resolverModule": require('../resolvers/UserCustomGreetingResolver').custom_greeting, "path": "me.greetz" }, { @@ -133,7 +133,7 @@ return { "fragment": (v2/*: any*/), "kind": "RelayResolver", "name": "custom_greeting", - "resolverModule": require('./../resolvers/UserCustomGreetingResolver').custom_greeting, + "resolverModule": require('../resolvers/UserCustomGreetingResolver').custom_greeting, "path": "me.willkommen" } ], diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTestMarkCleanQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTestMarkCleanQuery.graphql.js index 64e8d85f43110..a4ee29a6e0bf9 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTestMarkCleanQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTestMarkCleanQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -65,7 +65,7 @@ var node/*: ConcreteRequest*/ = { }, "kind": "RelayResolver", "name": "constant_dependent", - "resolverModule": require('./../resolvers/UserConstantDependentResolver').constant_dependent, + "resolverModule": require('../resolvers/UserConstantDependentResolver').constant_dependent, "path": "me.constant_dependent" } ], diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTestMissingDataQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTestMissingDataQuery.graphql.js index a4f5f97e582b9..ed8a3dafa79ef 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTestMissingDataQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTestMissingDataQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<2ca05376bbfe36dc574e0cca8fe617e9>> + * @generated SignedSource<<4cfe1987721ecf743542a1d126a45943>> * @flow * @lightSyntaxTransform * @nogrep @@ -65,7 +65,7 @@ var node/*: ConcreteRequest*/ = { }, "kind": "RelayResolver", "name": "greeting", - "resolverModule": require('./../resolvers/UserGreetingResolver').greeting, + "resolverModule": require('../resolvers/UserGreetingResolver').greeting, "path": "me.greeting" } ], diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTestRequiredQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTestRequiredQuery.graphql.js index fa93597db2dd0..9e695da17fa8d 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTestRequiredQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTestRequiredQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<16e0b88bdf081092bdc07505722066a6>> + * @generated SignedSource<<9276f54a3796592510241a29eb43416c>> * @flow * @lightSyntaxTransform * @nogrep @@ -65,7 +65,7 @@ var node/*: ConcreteRequest*/ = { }, "kind": "RelayResolver", "name": "required_name", - "resolverModule": require('./../resolvers/UserRequiredNameResolver').required_name, + "resolverModule": require('../resolvers/UserRequiredNameResolver').required_name, "path": "me.required_name" } ], diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTestRequiredThrowQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTestRequiredThrowQuery.graphql.js index 8a1bb2b5f151e..80a0c23d8196f 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTestRequiredThrowQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTestRequiredThrowQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<6fdb211810bba759d4a03e5e17338e6f>> * @flow * @lightSyntaxTransform * @nogrep @@ -65,7 +65,7 @@ var node/*: ConcreteRequest*/ = { }, "kind": "RelayResolver", "name": "required_throw_name", - "resolverModule": require('./../resolvers/UserRequiredThrowNameResolver').required_throw_name, + "resolverModule": require('../resolvers/UserRequiredThrowNameResolver').required_throw_name, "path": "me.required_throw_name" } ], diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTestRequiredWithParentQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTestRequiredWithParentQuery.graphql.js index e59d8bd3f249e..94f4e89179d21 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTestRequiredWithParentQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderResolverTestRequiredWithParentQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<516c4f6c3ab7c89130c7970ce174b60f>> + * @generated SignedSource<<5dfdd138a8df8ffd8a59b6e5e458f8d9>> * @flow * @lightSyntaxTransform * @nogrep @@ -74,7 +74,7 @@ return { }, "kind": "RelayResolver", "name": "required_name", - "resolverModule": require('./../resolvers/UserRequiredNameResolver').required_name, + "resolverModule": require('../resolvers/UserRequiredNameResolver').required_name, "path": "me.required_name" }, { diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderTestActorChangeQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderTestActorChangeQuery.graphql.js index 00b43b35d3b47..be427446176aa 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderTestActorChangeQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderTestActorChangeQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<35ee3663996ad62dfe90561eef84920c>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -148,7 +148,7 @@ var node/*: ConcreteRequest*/ = { }; if (__DEV__) { - (node/*: any*/).hash = "773250516ecbc96c49a303ebd13fe989"; + (node/*: any*/).hash = "cc9c977360e5fb08288904484b1cc752"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderTestShouldNotConsiderDataMissingIfTheFragmentTypeDoesNotMatchTheDataActorQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderTestShouldNotConsiderDataMissingIfTheFragmentTypeDoesNotMatchTheDataActorQuery.graphql.js index a149e56b7b6ba..c0ad450cfb828 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderTestShouldNotConsiderDataMissingIfTheFragmentTypeDoesNotMatchTheDataActorQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderTestShouldNotConsiderDataMissingIfTheFragmentTypeDoesNotMatchTheDataActorQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<84e48e1e61d861745a24b057b9d1b5ce>> + * @generated SignedSource<<9f3ee690ec2d2556c725757e6abf21be>> * @flow * @lightSyntaxTransform * @nogrep @@ -140,7 +140,7 @@ var node/*: ConcreteRequest*/ = { }; if (__DEV__) { - (node/*: any*/).hash = "de40442fc17a47c16c0809a3cdc9acf7"; + (node/*: any*/).hash = "f1d5b3be29b85d51aafb11465e94b2fa"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderTestStreamConnectionUserQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderTestStreamConnectionUserQuery.graphql.js index c0fbacdd29033..54207aabffbc8 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReaderTestStreamConnectionUserQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReaderTestStreamConnectionUserQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<7d744f11f5417a944e40cb2005515511>> * @flow * @lightSyntaxTransform * @nogrep @@ -233,7 +233,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "49574b98d8f989c7596cd7bf981f5a7e"; + (node/*: any*/).hash = "6fea4f22e3efe059099ef5cfa350fc9c"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReferenceMarkerTest1Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReferenceMarkerTest1Query.graphql.js index a751545a8fbfb..d7e5f24ff58d4 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReferenceMarkerTest1Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReferenceMarkerTest1Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<560e11fb1bcfe70ea2dc5a656d17514e>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -270,7 +270,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "c93bd2646732b404b960bb12692d825a"; + (node/*: any*/).hash = "4aafabe534bb518cb3c2f17ba46c0a5b"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReferenceMarkerTest4Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReferenceMarkerTest4Query.graphql.js index 4401eb7d64cb8..6fe19267b1d0a 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReferenceMarkerTest4Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReferenceMarkerTest4Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<6329211e2fa9fffb6347d49ec17a98fb>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -251,7 +251,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "c0a1e569d98cf3c0c2b4a559325ed687"; + (node/*: any*/).hash = "3a3b10327f7853be9a3e2b6bf0a76a7d"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReferenceMarkerTest5Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReferenceMarkerTest5Query.graphql.js index fafddc097def5..3b1062777f876 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReferenceMarkerTest5Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReferenceMarkerTest5Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<6aeb66a6feca27660bbb6bce58a88f58>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -176,7 +176,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "0788b1f4742c878888dfe9389e4d9de4"; + (node/*: any*/).hash = "7b4c00aa61d93ed96cf7e372fcaf9a6d"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReferenceMarkerTest6Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReferenceMarkerTest6Query.graphql.js index 22cda40059d88..bdddf055af89e 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReferenceMarkerTest6Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReferenceMarkerTest6Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<97f6e0668378af5c991ff2bf56fd9504>> + * @generated SignedSource<<66589315ffaeb5dae7119d5549099af0>> * @flow * @lightSyntaxTransform * @nogrep @@ -170,7 +170,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "f6c61057e6890addba331b5b9df9fbb6"; + (node/*: any*/).hash = "c11d69bf3fe2868cfc206f53c56cbc72"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReferenceMarkerTest7Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReferenceMarkerTest7Query.graphql.js index c2c316acaf2f4..7a439338d3b6b 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReferenceMarkerTest7Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReferenceMarkerTest7Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<09693c211de3d81762b00236529c01b4>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -162,7 +162,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "e9f37482cdecc3ffcf60ff0a5957ffab"; + (node/*: any*/).hash = "19bfe0e6b96ae4a7feb5a3270cdd2153"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReferenceMarkerTest8Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReferenceMarkerTest8Query.graphql.js index 9d00bce836a8e..d1ffe6c397863 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReferenceMarkerTest8Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReferenceMarkerTest8Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<549f8082c61c89821da3f96db64fe1f5>> + * @generated SignedSource<<6f4ca7565504d82900ed85b322ecac08>> * @flow * @lightSyntaxTransform * @nogrep @@ -156,7 +156,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "30e3b31096520d2b76871a970230f544"; + (node/*: any*/).hash = "a0e01b7c5010f85f17c18767d704425a"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReferenceMarkerTestResolverWithEdgeToClientQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReferenceMarkerTestResolverWithEdgeToClientQuery.graphql.js index 14af6a0db8e03..0043fd067b7af 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReferenceMarkerTestResolverWithEdgeToClientQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReferenceMarkerTestResolverWithEdgeToClientQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<62709e70d1e5e472c7a1b4fe632551e5>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -86,7 +86,7 @@ return { }, "kind": "RelayResolver", "name": "all_astrological_signs", - "resolverModule": require('./../resolvers/QueryAllAstrologicalSignsResolver').all_astrological_signs, + "resolverModule": require('../resolvers/QueryAllAstrologicalSignsResolver').all_astrological_signs, "path": "all_astrological_signs" }, "linkedField": (v1/*: any*/) diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReferenceMarkerTestResolverWithFragmentDependencyQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReferenceMarkerTestResolverWithFragmentDependencyQuery.graphql.js index 1adb10ae616a7..c4cf340a013e2 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReferenceMarkerTestResolverWithFragmentDependencyQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReferenceMarkerTestResolverWithFragmentDependencyQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -56,7 +56,7 @@ var node/*: ConcreteRequest*/ = { }, "kind": "RelayLiveResolver", "name": "counter", - "resolverModule": require('./../resolvers/LiveCounterResolver').counter, + "resolverModule": require('../resolvers/LiveCounterResolver').counter, "path": "counter" } ], diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayReferenceMarkerTestResolverWithNoFragmentQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayReferenceMarkerTestResolverWithNoFragmentQuery.graphql.js index d8d01553e8830..d27518e972f0b 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayReferenceMarkerTestResolverWithNoFragmentQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayReferenceMarkerTestResolverWithNoFragmentQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<65bf29cfa8527bcc637dab5296140227>> * @flow * @lightSyntaxTransform * @nogrep @@ -53,7 +53,7 @@ var node/*: ClientRequest*/ = { "fragment": null, "kind": "RelayLiveResolver", "name": "counter_no_fragment", - "resolverModule": require('./../resolvers/LiveCounterNoFragment').counter_no_fragment, + "resolverModule": require('../resolvers/LiveCounterNoFragment').counter_no_fragment, "path": "counter_no_fragment" } ] diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest10Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest10Query.graphql.js index 56698c9f1be69..d072b0742214d 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest10Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest10Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<4e400210e107bc7880b9b3b576cbb140>> + * @generated SignedSource<<0261c5ad4124d3fb09a73393b12b2219>> * @flow * @lightSyntaxTransform * @nogrep @@ -148,7 +148,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "25cdf6541b8272d4a77a346d81a442eb"; + (node/*: any*/).hash = "317e0d4a0d4e1c2fdceea8578eae916f"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest11Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest11Query.graphql.js index 988615ad85a1a..376910cddd55c 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest11Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest11Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<52e593a20fd57ecb4271b570ed043c01>> + * @generated SignedSource<<5e6d5c92de75ebab71a0880be83345ca>> * @flow * @lightSyntaxTransform * @nogrep @@ -166,7 +166,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "49b6f26955af4b56db48dc9bb544abdf"; + (node/*: any*/).hash = "4c5a0863a3eb64b65d62c3e333d69a06"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest12Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest12Query.graphql.js index 56b6f9999a074..31cca2748250f 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest12Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest12Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<4e8b698509bf6cbc4fdb043ee5b73a93>> * @flow * @lightSyntaxTransform * @nogrep @@ -166,7 +166,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "e2566f3cbdd63fc7ea2c74c81703d8e8"; + (node/*: any*/).hash = "086b7fb8e257be2c1e7e5baa629f7f3b"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest13Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest13Query.graphql.js index 231022e027651..28b34b4ac7aa7 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest13Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest13Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -178,7 +178,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "d9638dd9dcaa4dd9c95795b109acd65c"; + (node/*: any*/).hash = "b1fa2fdbc204811f15d320177b37e116"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest14Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest14Query.graphql.js index 8cbb4868ae0d4..12e65a0354215 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest14Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest14Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<8024bd1cfdbdf62c7064a0614c5e605b>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -156,7 +156,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "c173a8d4e918b5aaecb1da24d9f8f854"; + (node/*: any*/).hash = "16e672d11491bc0974f701a971e9bd97"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest41Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest41Query.graphql.js new file mode 100644 index 0000000000000..2b4e7504a417e --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest41Query.graphql.js @@ -0,0 +1,127 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ConcreteRequest, Query } from 'relay-runtime'; +export type RelayResponseNormalizerTest41Query$variables = {| + id: string, +|}; +export type RelayResponseNormalizerTest41Query$data = {| + +node: ?{| + +__typename: string, + +emailAddresses?: ?$ReadOnlyArray, + +id: string, + |}, +|}; +export type RelayResponseNormalizerTest41Query = {| + response: RelayResponseNormalizerTest41Query$data, + variables: RelayResponseNormalizerTest41Query$variables, +|}; +*/ + +var node/*: ConcreteRequest*/ = (function(){ +var v0 = [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "id" + } +], +v1 = [ + { + "alias": null, + "args": [ + { + "kind": "Variable", + "name": "id", + "variableName": "id" + } + ], + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null + }, + { + "kind": "InlineFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "emailAddresses", + "storageKey": null + } + ], + "type": "User", + "abstractKey": null + } + ], + "storageKey": null + } +]; +return { + "fragment": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Fragment", + "metadata": null, + "name": "RelayResponseNormalizerTest41Query", + "selections": (v1/*: any*/), + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Operation", + "name": "RelayResponseNormalizerTest41Query", + "selections": (v1/*: any*/) + }, + "params": { + "cacheID": "f46c4a92011b2ed6e4222a610f749577", + "id": null, + "metadata": {}, + "name": "RelayResponseNormalizerTest41Query", + "operationKind": "query", + "text": "query RelayResponseNormalizerTest41Query(\n $id: ID!\n) {\n node(id: $id) {\n id\n __typename\n ... on User {\n emailAddresses\n }\n }\n}\n" + } +}; +})(); + +if (__DEV__) { + (node/*: any*/).hash = "3bf9e0ffd23df3d6711a66bca7879ca7"; +} + +module.exports = ((node/*: any*/)/*: Query< + RelayResponseNormalizerTest41Query$variables, + RelayResponseNormalizerTest41Query$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest42Fragment.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest42Fragment.graphql.js new file mode 100644 index 0000000000000..4a2518264c9bd --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest42Fragment.graphql.js @@ -0,0 +1,74 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<75b5cc0be88d4b06a0383561c063320c>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { Fragment, ReaderFragment } from 'relay-runtime'; +import type { FragmentType } from "relay-runtime"; +declare export opaque type RelayResponseNormalizerTest42Fragment$fragmentType: FragmentType; +export type RelayResponseNormalizerTest42Fragment$data = {| + +id: string, + +name?: ?string, + +$fragmentType: RelayResponseNormalizerTest42Fragment$fragmentType, +|}; +export type RelayResponseNormalizerTest42Fragment$key = { + +$data?: RelayResponseNormalizerTest42Fragment$data, + +$fragmentSpreads: RelayResponseNormalizerTest42Fragment$fragmentType, + ... +}; +*/ + +var node/*: ReaderFragment*/ = { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "RelayResponseNormalizerTest42Fragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + }, + { + "kind": "InlineFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + } + ], + "type": "User", + "abstractKey": null + } + ], + "type": "Node", + "abstractKey": "__isNode" +}; + +if (__DEV__) { + (node/*: any*/).hash = "591f4f73e73d3aa4fa8dbc86ca5c9690"; +} + +module.exports = ((node/*: any*/)/*: Fragment< + RelayResponseNormalizerTest42Fragment$fragmentType, + RelayResponseNormalizerTest42Fragment$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest42Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest42Query.graphql.js new file mode 100644 index 0000000000000..c963755bf7ea6 --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest42Query.graphql.js @@ -0,0 +1,147 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ConcreteRequest, Query } from 'relay-runtime'; +import type { RelayResponseNormalizerTest42Fragment$fragmentType } from "./RelayResponseNormalizerTest42Fragment.graphql"; +export type RelayResponseNormalizerTest42Query$variables = {| + id: string, +|}; +export type RelayResponseNormalizerTest42Query$data = {| + +node: ?{| + +$fragmentSpreads: RelayResponseNormalizerTest42Fragment$fragmentType, + |}, +|}; +export type RelayResponseNormalizerTest42Query = {| + response: RelayResponseNormalizerTest42Query$data, + variables: RelayResponseNormalizerTest42Query$variables, +|}; +*/ + +var node/*: ConcreteRequest*/ = (function(){ +var v0 = [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "id" + } +], +v1 = [ + { + "kind": "Variable", + "name": "id", + "variableName": "id" + } +]; +return { + "fragment": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Fragment", + "metadata": null, + "name": "RelayResponseNormalizerTest42Query", + "selections": [ + { + "alias": null, + "args": (v1/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + { + "args": null, + "kind": "FragmentSpread", + "name": "RelayResponseNormalizerTest42Fragment" + } + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Operation", + "name": "RelayResponseNormalizerTest42Query", + "selections": [ + { + "alias": null, + "args": (v1/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "node", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null + }, + { + "kind": "TypeDiscriminator", + "abstractKey": "__isNode" + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + }, + { + "kind": "InlineFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + } + ], + "type": "User", + "abstractKey": null + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "88937bb7d3f011243792258ece6f7218", + "id": null, + "metadata": {}, + "name": "RelayResponseNormalizerTest42Query", + "operationKind": "query", + "text": "query RelayResponseNormalizerTest42Query(\n $id: ID!\n) {\n node(id: $id) {\n __typename\n ...RelayResponseNormalizerTest42Fragment\n id\n }\n}\n\nfragment RelayResponseNormalizerTest42Fragment on Node {\n __isNode: __typename\n id\n ... on User {\n name\n }\n}\n" + } +}; +})(); + +if (__DEV__) { + (node/*: any*/).hash = "df5d2a14838f0b7c2322699b0f2aaf26"; +} + +module.exports = ((node/*: any*/)/*: Query< + RelayResponseNormalizerTest42Query$variables, + RelayResponseNormalizerTest42Query$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest43Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest43Query.graphql.js new file mode 100644 index 0000000000000..5b818c64ca9f9 --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest43Query.graphql.js @@ -0,0 +1,139 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ConcreteRequest, Query } from 'relay-runtime'; +export type RelayResponseNormalizerTest43Query$variables = {| + id: string, +|}; +export type RelayResponseNormalizerTest43Query$data = {| + +userOrPage: ?{| + +id?: string, + |}, +|}; +export type RelayResponseNormalizerTest43Query = {| + response: RelayResponseNormalizerTest43Query$data, + variables: RelayResponseNormalizerTest43Query$variables, +|}; +*/ + +var node/*: ConcreteRequest*/ = (function(){ +var v0 = [ + { + "defaultValue": null, + "kind": "LocalArgument", + "name": "id" + } +], +v1 = [ + { + "kind": "Variable", + "name": "id", + "variableName": "id" + } +], +v2 = [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + } +], +v3 = { + "kind": "InlineFragment", + "selections": (v2/*: any*/), + "type": "User", + "abstractKey": null +}; +return { + "fragment": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Fragment", + "metadata": null, + "name": "RelayResponseNormalizerTest43Query", + "selections": [ + { + "alias": null, + "args": (v1/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "userOrPage", + "plural": false, + "selections": [ + (v3/*: any*/) + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": (v0/*: any*/), + "kind": "Operation", + "name": "RelayResponseNormalizerTest43Query", + "selections": [ + { + "alias": null, + "args": (v1/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "userOrPage", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null + }, + (v3/*: any*/), + { + "kind": "InlineFragment", + "selections": (v2/*: any*/), + "type": "Node", + "abstractKey": "__isNode" + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "1ce76a46dba4f92ff0d73f009a2e5d30", + "id": null, + "metadata": {}, + "name": "RelayResponseNormalizerTest43Query", + "operationKind": "query", + "text": "query RelayResponseNormalizerTest43Query(\n $id: ID!\n) {\n userOrPage(id: $id) {\n __typename\n ... on User {\n id\n }\n ... on Node {\n __isNode: __typename\n id\n }\n }\n}\n" + } +}; +})(); + +if (__DEV__) { + (node/*: any*/).hash = "1f32c7b33101aed4f9650bfc68e12aad"; +} + +module.exports = ((node/*: any*/)/*: Query< + RelayResponseNormalizerTest43Query$variables, + RelayResponseNormalizerTest43Query$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest4Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest4Query.graphql.js index 0530cfbe1e8d3..5913ff02ea373 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest4Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest4Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<1fef67311a2b88600ec8009d0f4dbe33>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -176,7 +176,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "325e859306b977210ac702e455a74207"; + (node/*: any*/).hash = "89fbaf152e9bd6bab74a5baad7e66dd1"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest5Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest5Query.graphql.js index 8bef8c3f6d46f..269cf1ed55bc0 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest5Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest5Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<4bedebbbabf24313bb3b68bc30c72a16>> * @flow * @lightSyntaxTransform * @nogrep @@ -170,7 +170,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "80a957b7f697c6ec8835d41f8e5cfecd"; + (node/*: any*/).hash = "de5b8815affe3b2ecc363ecebd2f06f1"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest6Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest6Query.graphql.js index 167b06a432550..0d07f3b2de17b 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest6Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest6Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -158,7 +158,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "6d72ae05b718f7c9fee011a92865e4b5"; + (node/*: any*/).hash = "391601564014900fbc9cceac74ffdcda"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest7Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest7Query.graphql.js index 589654dce888c..29c75d8a9de21 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest7Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest7Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<88a3db989d98394c766a0827b01066c5>> + * @generated SignedSource<<7c25b9b49e57d42c452f91038da43589>> * @flow * @lightSyntaxTransform * @nogrep @@ -148,7 +148,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "1b55aa70053112dca44375471deb0e3d"; + (node/*: any*/).hash = "1136d0edd20f9ef4cb63a1d100b394d9"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest8Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest8Query.graphql.js index 81605924a947e..e3a89265745ca 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest8Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest8Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -158,7 +158,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "636c5d32b4dd2e8695c5f9e9db86fa08"; + (node/*: any*/).hash = "38ba556a089ea79dcb91395dc95db966"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest9Query.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest9Query.graphql.js index 73489b2dcd9cc..70a5dcf2217dd 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest9Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest9Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<1c36a7296c1a1d33e3ad450d0f699c8b>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -188,7 +188,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "f2dffeabae2388e74241a6adde2168d4"; + (node/*: any*/).hash = "8c92153864fe205ace6fefbd5c53fdc4"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTestActorChangeQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTestActorChangeQuery.graphql.js index 1bee0bb1a0864..6c7d96aa2c518 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTestActorChangeQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTestActorChangeQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<74ff7d309bac0d5896b6ddb964b4fdca>> + * @generated SignedSource<<4eb7f6555f0bdefabc5f3320e520fb5b>> * @flow * @lightSyntaxTransform * @nogrep @@ -148,7 +148,7 @@ var node/*: ConcreteRequest*/ = { }; if (__DEV__) { - (node/*: any*/).hash = "0cca5a4e676a8ee5984e81c0ceda21ef"; + (node/*: any*/).hash = "35c07e751da3f8a045a2c9f9b099edde"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTestActorChangeWithAliasQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTestActorChangeWithAliasQuery.graphql.js index 2a5e073ef90f7..ae200f4f6b548 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTestActorChangeWithAliasQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTestActorChangeWithAliasQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<9b69ef76e0822508b19814660f2210d4>> + * @generated SignedSource<<5bef053c1adfdb1ffa0313258be7e692>> * @flow * @lightSyntaxTransform * @nogrep @@ -181,7 +181,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "4d26736797149779b0919c716b985847"; + (node/*: any*/).hash = "8ae75ed6d55511537f85750f1ff71def"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTestQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTestQuery.graphql.js index a8c2f71433ba8..66fce1d9bc16e 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTestQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTestQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<9403bf41a2f46b24c1b361b32f5a865d>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -156,7 +156,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "a4ef71f79408fa39d099bcbaa28d35e4"; + (node/*: any*/).hash = "596bef72849e29e5582e4bd84aae6f33"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest_pvQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest_pvQuery.graphql.js index 64cb9cd64628e..2f511d4f9c269 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest_pvQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/RelayResponseNormalizerTest_pvQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -33,8 +33,8 @@ export type RelayResponseNormalizerTest_pvQuery = {| variables: RelayResponseNormalizerTest_pvQuery$variables, |}; ({ - "__relay_internal__pv__RelayProvider_returnsTruerelayprovider": require('./../RelayProvider_returnsTrue.relayprovider'), - "__relay_internal__pv__RelayProvider_returnsFalserelayprovider": require('./../RelayProvider_returnsFalse.relayprovider') + "__relay_internal__pv__RelayProvider_returnsTruerelayprovider": require('../RelayProvider_returnsTrue.relayprovider'), + "__relay_internal__pv__RelayProvider_returnsFalserelayprovider": require('../RelayProvider_returnsFalse.relayprovider') }: {| +__relay_internal__pv__RelayProvider_returnsFalserelayprovider: {| +get: () => boolean, @@ -205,15 +205,15 @@ return { "operationKind": "query", "text": "query RelayResponseNormalizerTest_pvQuery(\n $id: ID!\n $__relay_internal__pv__RelayProvider_returnsTruerelayprovider: Boolean!\n $__relay_internal__pv__RelayProvider_returnsFalserelayprovider: Boolean!\n) {\n node(id: $id) {\n __typename\n id\n ...RelayResponseNormalizerTest_pvFragment\n }\n}\n\nfragment RelayResponseNormalizerTest_pvFragment on User {\n name @include(if: $__relay_internal__pv__RelayProvider_returnsTruerelayprovider)\n firstName @include(if: $__relay_internal__pv__RelayProvider_returnsFalserelayprovider)\n lastName @skip(if: $__relay_internal__pv__RelayProvider_returnsFalserelayprovider)\n username @skip(if: $__relay_internal__pv__RelayProvider_returnsTruerelayprovider)\n}\n", "providedVariables": { - "__relay_internal__pv__RelayProvider_returnsTruerelayprovider": require('./../RelayProvider_returnsTrue.relayprovider'), - "__relay_internal__pv__RelayProvider_returnsFalserelayprovider": require('./../RelayProvider_returnsFalse.relayprovider') + "__relay_internal__pv__RelayProvider_returnsTruerelayprovider": require('../RelayProvider_returnsTrue.relayprovider'), + "__relay_internal__pv__RelayProvider_returnsFalserelayprovider": require('../RelayProvider_returnsFalse.relayprovider') } } }; })(); if (__DEV__) { - (node/*: any*/).hash = "9177ab3c4ded4d7e93ff9712ce8a59c0"; + (node/*: any*/).hash = "3d11c5d77a6b30dd28ee9a5eb421373d"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestClientEdgeToServerFragment.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestClientEdgeToServerFragment.graphql.js index 18bf1288df621..8858d1f72993e 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestClientEdgeToServerFragment.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestClientEdgeToServerFragment.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<47f818db2f3c03fb471e6c9dee98d892>> * @flow * @lightSyntaxTransform * @nogrep @@ -66,7 +66,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "client_edge", - "resolverModule": require('./../resolvers/UserClientEdgeResolver').client_edge, + "resolverModule": require('../resolvers/UserClientEdgeResolver').client_edge, "path": "client_edge" }, "linkedField": { diff --git a/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestListUpdateFragment.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestListUpdateFragment.graphql.js new file mode 100644 index 0000000000000..dd62e819b9696 --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestListUpdateFragment.graphql.js @@ -0,0 +1,61 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { Fragment, ReaderFragment } from 'relay-runtime'; +import type { FragmentType } from "relay-runtime"; +declare export opaque type observeFragmentTestListUpdateFragment$fragmentType: FragmentType; +export type observeFragmentTestListUpdateFragment$data = $ReadOnlyArray<{| + +name: ?string, + +$fragmentType: observeFragmentTestListUpdateFragment$fragmentType, +|}>; +export type observeFragmentTestListUpdateFragment$key = $ReadOnlyArray<{ + +$data?: observeFragmentTestListUpdateFragment$data, + +$fragmentSpreads: observeFragmentTestListUpdateFragment$fragmentType, + ... +}>; +*/ + +var node/*: ReaderFragment*/ = { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": { + "plural": true + }, + "name": "observeFragmentTestListUpdateFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + } + ], + "type": "User", + "abstractKey": null +}; + +if (__DEV__) { + (node/*: any*/).hash = "30d272ba4e5c5a9eb1d9a79015a69ec3"; +} + +module.exports = ((node/*: any*/)/*: Fragment< + observeFragmentTestListUpdateFragment$fragmentType, + observeFragmentTestListUpdateFragment$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestListUpdateQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestListUpdateQuery.graphql.js new file mode 100644 index 0000000000000..70cac22b20f6e --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestListUpdateQuery.graphql.js @@ -0,0 +1,137 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<4337e2999734809a390206fb499f653e>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ConcreteRequest, Query } from 'relay-runtime'; +import type { observeFragmentTestListUpdateFragment$fragmentType } from "./observeFragmentTestListUpdateFragment.graphql"; +export type observeFragmentTestListUpdateQuery$variables = {||}; +export type observeFragmentTestListUpdateQuery$data = {| + +nodes: ?$ReadOnlyArray, +|}; +export type observeFragmentTestListUpdateQuery = {| + response: observeFragmentTestListUpdateQuery$data, + variables: observeFragmentTestListUpdateQuery$variables, +|}; +*/ + +var node/*: ConcreteRequest*/ = (function(){ +var v0 = [ + { + "kind": "Literal", + "name": "ids", + "value": [ + "1", + "2" + ] + } +]; +return { + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "observeFragmentTestListUpdateQuery", + "selections": [ + { + "alias": null, + "args": (v0/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "nodes", + "plural": true, + "selections": [ + { + "args": null, + "kind": "FragmentSpread", + "name": "observeFragmentTestListUpdateFragment" + } + ], + "storageKey": "nodes(ids:[\"1\",\"2\"])" + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [], + "kind": "Operation", + "name": "observeFragmentTestListUpdateQuery", + "selections": [ + { + "alias": null, + "args": (v0/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "nodes", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null + }, + { + "kind": "InlineFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + } + ], + "type": "User", + "abstractKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + } + ], + "storageKey": "nodes(ids:[\"1\",\"2\"])" + } + ] + }, + "params": { + "cacheID": "17a222c7e13bc4a3b9c0c108377514da", + "id": null, + "metadata": {}, + "name": "observeFragmentTestListUpdateQuery", + "operationKind": "query", + "text": "query observeFragmentTestListUpdateQuery {\n nodes(ids: [\"1\", \"2\"]) {\n __typename\n ...observeFragmentTestListUpdateFragment\n id\n }\n}\n\nfragment observeFragmentTestListUpdateFragment on User {\n name\n}\n" + } +}; +})(); + +if (__DEV__) { + (node/*: any*/).hash = "493ccdbc127bfccc347fc16107f21b79"; +} + +module.exports = ((node/*: any*/)/*: Query< + observeFragmentTestListUpdateQuery$variables, + observeFragmentTestListUpdateQuery$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestMissingRequiredPluralFragment.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestMissingRequiredPluralFragment.graphql.js new file mode 100644 index 0000000000000..ba317d7cf63f9 --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestMissingRequiredPluralFragment.graphql.js @@ -0,0 +1,65 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<80e54ca26f89d2d71c3bb618efa15d49>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { Fragment, ReaderFragment } from 'relay-runtime'; +import type { FragmentType } from "relay-runtime"; +declare export opaque type observeFragmentTestMissingRequiredPluralFragment$fragmentType: FragmentType; +export type observeFragmentTestMissingRequiredPluralFragment$data = $ReadOnlyArray<{| + +name: string, + +$fragmentType: observeFragmentTestMissingRequiredPluralFragment$fragmentType, +|}>; +export type observeFragmentTestMissingRequiredPluralFragment$key = $ReadOnlyArray<{ + +$data?: observeFragmentTestMissingRequiredPluralFragment$data, + +$fragmentSpreads: observeFragmentTestMissingRequiredPluralFragment$fragmentType, + ... +}>; +*/ + +var node/*: ReaderFragment*/ = { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": { + "plural": true + }, + "name": "observeFragmentTestMissingRequiredPluralFragment", + "selections": [ + { + "kind": "RequiredField", + "field": { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + }, + "action": "THROW" + } + ], + "type": "User", + "abstractKey": null +}; + +if (__DEV__) { + (node/*: any*/).hash = "0a62ca17583bf06a225f706e103dc11e"; +} + +module.exports = ((node/*: any*/)/*: Fragment< + observeFragmentTestMissingRequiredPluralFragment$fragmentType, + observeFragmentTestMissingRequiredPluralFragment$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestMissingRequiredPluralQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestMissingRequiredPluralQuery.graphql.js new file mode 100644 index 0000000000000..d14055391be61 --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestMissingRequiredPluralQuery.graphql.js @@ -0,0 +1,137 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<0ee8f9cd4d1a4269af40172b0c55884d>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ConcreteRequest, Query } from 'relay-runtime'; +import type { observeFragmentTestMissingRequiredPluralFragment$fragmentType } from "./observeFragmentTestMissingRequiredPluralFragment.graphql"; +export type observeFragmentTestMissingRequiredPluralQuery$variables = {||}; +export type observeFragmentTestMissingRequiredPluralQuery$data = {| + +nodes: ?$ReadOnlyArray, +|}; +export type observeFragmentTestMissingRequiredPluralQuery = {| + response: observeFragmentTestMissingRequiredPluralQuery$data, + variables: observeFragmentTestMissingRequiredPluralQuery$variables, +|}; +*/ + +var node/*: ConcreteRequest*/ = (function(){ +var v0 = [ + { + "kind": "Literal", + "name": "ids", + "value": [ + "1", + "2" + ] + } +]; +return { + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "observeFragmentTestMissingRequiredPluralQuery", + "selections": [ + { + "alias": null, + "args": (v0/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "nodes", + "plural": true, + "selections": [ + { + "args": null, + "kind": "FragmentSpread", + "name": "observeFragmentTestMissingRequiredPluralFragment" + } + ], + "storageKey": "nodes(ids:[\"1\",\"2\"])" + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [], + "kind": "Operation", + "name": "observeFragmentTestMissingRequiredPluralQuery", + "selections": [ + { + "alias": null, + "args": (v0/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "nodes", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null + }, + { + "kind": "InlineFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + } + ], + "type": "User", + "abstractKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + } + ], + "storageKey": "nodes(ids:[\"1\",\"2\"])" + } + ] + }, + "params": { + "cacheID": "d1213ecc01790c61de1e12d97a40c349", + "id": null, + "metadata": {}, + "name": "observeFragmentTestMissingRequiredPluralQuery", + "operationKind": "query", + "text": "query observeFragmentTestMissingRequiredPluralQuery {\n nodes(ids: [\"1\", \"2\"]) {\n __typename\n ...observeFragmentTestMissingRequiredPluralFragment\n id\n }\n}\n\nfragment observeFragmentTestMissingRequiredPluralFragment on User {\n name\n}\n" + } +}; +})(); + +if (__DEV__) { + (node/*: any*/).hash = "1eb9c5256c37c4d0e28695bb4dd64fa8"; +} + +module.exports = ((node/*: any*/)/*: Query< + observeFragmentTestMissingRequiredPluralQuery$variables, + observeFragmentTestMissingRequiredPluralQuery$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestNetworkErrorFragment.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestNetworkErrorFragment.graphql.js new file mode 100644 index 0000000000000..827cf692163a4 --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestNetworkErrorFragment.graphql.js @@ -0,0 +1,72 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<63c81af48f685c620838abbe0a6ff26e>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { Fragment, ReaderFragment } from 'relay-runtime'; +import type { FragmentType } from "relay-runtime"; +declare export opaque type observeFragmentTestNetworkErrorFragment$fragmentType: FragmentType; +export type observeFragmentTestNetworkErrorFragment$data = {| + +me: ?{| + +name: ?string, + |}, + +$fragmentType: observeFragmentTestNetworkErrorFragment$fragmentType, +|}; +export type observeFragmentTestNetworkErrorFragment$key = { + +$data?: observeFragmentTestNetworkErrorFragment$data, + +$fragmentSpreads: observeFragmentTestNetworkErrorFragment$fragmentType, + ... +}; +*/ + +var node/*: ReaderFragment*/ = { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "observeFragmentTestNetworkErrorFragment", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "me", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + } + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null +}; + +if (__DEV__) { + (node/*: any*/).hash = "08727ee370f3e8859731dc1535a5c718"; +} + +module.exports = ((node/*: any*/)/*: Fragment< + observeFragmentTestNetworkErrorFragment$fragmentType, + observeFragmentTestNetworkErrorFragment$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestNetworkErrorQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestNetworkErrorQuery.graphql.js new file mode 100644 index 0000000000000..d48d6132b41c6 --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestNetworkErrorQuery.graphql.js @@ -0,0 +1,98 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ConcreteRequest, Query } from 'relay-runtime'; +import type { observeFragmentTestNetworkErrorFragment$fragmentType } from "./observeFragmentTestNetworkErrorFragment.graphql"; +export type observeFragmentTestNetworkErrorQuery$variables = {||}; +export type observeFragmentTestNetworkErrorQuery$data = {| + +$fragmentSpreads: observeFragmentTestNetworkErrorFragment$fragmentType, +|}; +export type observeFragmentTestNetworkErrorQuery = {| + response: observeFragmentTestNetworkErrorQuery$data, + variables: observeFragmentTestNetworkErrorQuery$variables, +|}; +*/ + +var node/*: ConcreteRequest*/ = { + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "observeFragmentTestNetworkErrorQuery", + "selections": [ + { + "args": null, + "kind": "FragmentSpread", + "name": "observeFragmentTestNetworkErrorFragment" + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [], + "kind": "Operation", + "name": "observeFragmentTestNetworkErrorQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "me", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "ea59f42835e1af47e66fc74d78ea34fb", + "id": null, + "metadata": {}, + "name": "observeFragmentTestNetworkErrorQuery", + "operationKind": "query", + "text": "query observeFragmentTestNetworkErrorQuery {\n ...observeFragmentTestNetworkErrorFragment\n}\n\nfragment observeFragmentTestNetworkErrorFragment on Query {\n me {\n name\n id\n }\n}\n" + } +}; + +if (__DEV__) { + (node/*: any*/).hash = "ab0f402fbc738d3f90847297471bd4b3"; +} + +module.exports = ((node/*: any*/)/*: Query< + observeFragmentTestNetworkErrorQuery$variables, + observeFragmentTestNetworkErrorQuery$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestPluralFragment.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestPluralFragment.graphql.js new file mode 100644 index 0000000000000..f5d80766c2a15 --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestPluralFragment.graphql.js @@ -0,0 +1,61 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { Fragment, ReaderFragment } from 'relay-runtime'; +import type { FragmentType } from "relay-runtime"; +declare export opaque type observeFragmentTestPluralFragment$fragmentType: FragmentType; +export type observeFragmentTestPluralFragment$data = $ReadOnlyArray<{| + +name: ?string, + +$fragmentType: observeFragmentTestPluralFragment$fragmentType, +|}>; +export type observeFragmentTestPluralFragment$key = $ReadOnlyArray<{ + +$data?: observeFragmentTestPluralFragment$data, + +$fragmentSpreads: observeFragmentTestPluralFragment$fragmentType, + ... +}>; +*/ + +var node/*: ReaderFragment*/ = { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": { + "plural": true + }, + "name": "observeFragmentTestPluralFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + } + ], + "type": "User", + "abstractKey": null +}; + +if (__DEV__) { + (node/*: any*/).hash = "3b473578ee9b2f35ed7214e714f68334"; +} + +module.exports = ((node/*: any*/)/*: Fragment< + observeFragmentTestPluralFragment$fragmentType, + observeFragmentTestPluralFragment$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestPluralQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestPluralQuery.graphql.js new file mode 100644 index 0000000000000..d6ed1fc180fc6 --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestPluralQuery.graphql.js @@ -0,0 +1,137 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<8ed3e238203c49288429f434ff0ef253>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ConcreteRequest, Query } from 'relay-runtime'; +import type { observeFragmentTestPluralFragment$fragmentType } from "./observeFragmentTestPluralFragment.graphql"; +export type observeFragmentTestPluralQuery$variables = {||}; +export type observeFragmentTestPluralQuery$data = {| + +nodes: ?$ReadOnlyArray, +|}; +export type observeFragmentTestPluralQuery = {| + response: observeFragmentTestPluralQuery$data, + variables: observeFragmentTestPluralQuery$variables, +|}; +*/ + +var node/*: ConcreteRequest*/ = (function(){ +var v0 = [ + { + "kind": "Literal", + "name": "ids", + "value": [ + "1", + "2" + ] + } +]; +return { + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "observeFragmentTestPluralQuery", + "selections": [ + { + "alias": null, + "args": (v0/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "nodes", + "plural": true, + "selections": [ + { + "args": null, + "kind": "FragmentSpread", + "name": "observeFragmentTestPluralFragment" + } + ], + "storageKey": "nodes(ids:[\"1\",\"2\"])" + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [], + "kind": "Operation", + "name": "observeFragmentTestPluralQuery", + "selections": [ + { + "alias": null, + "args": (v0/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "nodes", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null + }, + { + "kind": "InlineFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + } + ], + "type": "User", + "abstractKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + } + ], + "storageKey": "nodes(ids:[\"1\",\"2\"])" + } + ] + }, + "params": { + "cacheID": "ccff1412490c5e62d70031d41db0c7e4", + "id": null, + "metadata": {}, + "name": "observeFragmentTestPluralQuery", + "operationKind": "query", + "text": "query observeFragmentTestPluralQuery {\n nodes(ids: [\"1\", \"2\"]) {\n __typename\n ...observeFragmentTestPluralFragment\n id\n }\n}\n\nfragment observeFragmentTestPluralFragment on User {\n name\n}\n" + } +}; +})(); + +if (__DEV__) { + (node/*: any*/).hash = "c1a2f68df2ec25bc00b077d6cdecdce4"; +} + +module.exports = ((node/*: any*/)/*: Query< + observeFragmentTestPluralQuery$variables, + observeFragmentTestPluralQuery$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestPluralThrowOnFieldErrorFragment.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestPluralThrowOnFieldErrorFragment.graphql.js new file mode 100644 index 0000000000000..122853d5c2de7 --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestPluralThrowOnFieldErrorFragment.graphql.js @@ -0,0 +1,62 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<73dfa993cd14eb071971ec8ae446eea0>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { Fragment, ReaderFragment } from 'relay-runtime'; +import type { FragmentType } from "relay-runtime"; +declare export opaque type observeFragmentTestPluralThrowOnFieldErrorFragment$fragmentType: FragmentType; +export type observeFragmentTestPluralThrowOnFieldErrorFragment$data = $ReadOnlyArray<{| + +name: ?string, + +$fragmentType: observeFragmentTestPluralThrowOnFieldErrorFragment$fragmentType, +|}>; +export type observeFragmentTestPluralThrowOnFieldErrorFragment$key = $ReadOnlyArray<{ + +$data?: observeFragmentTestPluralThrowOnFieldErrorFragment$data, + +$fragmentSpreads: observeFragmentTestPluralThrowOnFieldErrorFragment$fragmentType, + ... +}>; +*/ + +var node/*: ReaderFragment*/ = { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": { + "plural": true, + "throwOnFieldError": true + }, + "name": "observeFragmentTestPluralThrowOnFieldErrorFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + } + ], + "type": "User", + "abstractKey": null +}; + +if (__DEV__) { + (node/*: any*/).hash = "531291e1335ff8e4ffacf60c7a6064ed"; +} + +module.exports = ((node/*: any*/)/*: Fragment< + observeFragmentTestPluralThrowOnFieldErrorFragment$fragmentType, + observeFragmentTestPluralThrowOnFieldErrorFragment$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestPluralThrowOnFieldErrorQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestPluralThrowOnFieldErrorQuery.graphql.js new file mode 100644 index 0000000000000..a7a23d3f32ffe --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestPluralThrowOnFieldErrorQuery.graphql.js @@ -0,0 +1,137 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ConcreteRequest, Query } from 'relay-runtime'; +import type { observeFragmentTestPluralThrowOnFieldErrorFragment$fragmentType } from "./observeFragmentTestPluralThrowOnFieldErrorFragment.graphql"; +export type observeFragmentTestPluralThrowOnFieldErrorQuery$variables = {||}; +export type observeFragmentTestPluralThrowOnFieldErrorQuery$data = {| + +nodes: ?$ReadOnlyArray, +|}; +export type observeFragmentTestPluralThrowOnFieldErrorQuery = {| + response: observeFragmentTestPluralThrowOnFieldErrorQuery$data, + variables: observeFragmentTestPluralThrowOnFieldErrorQuery$variables, +|}; +*/ + +var node/*: ConcreteRequest*/ = (function(){ +var v0 = [ + { + "kind": "Literal", + "name": "ids", + "value": [ + "1", + "2" + ] + } +]; +return { + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "observeFragmentTestPluralThrowOnFieldErrorQuery", + "selections": [ + { + "alias": null, + "args": (v0/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "nodes", + "plural": true, + "selections": [ + { + "args": null, + "kind": "FragmentSpread", + "name": "observeFragmentTestPluralThrowOnFieldErrorFragment" + } + ], + "storageKey": "nodes(ids:[\"1\",\"2\"])" + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [], + "kind": "Operation", + "name": "observeFragmentTestPluralThrowOnFieldErrorQuery", + "selections": [ + { + "alias": null, + "args": (v0/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "nodes", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null + }, + { + "kind": "InlineFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + } + ], + "type": "User", + "abstractKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + } + ], + "storageKey": "nodes(ids:[\"1\",\"2\"])" + } + ] + }, + "params": { + "cacheID": "1e83abfe97b66be09a6642b3332e4d09", + "id": null, + "metadata": {}, + "name": "observeFragmentTestPluralThrowOnFieldErrorQuery", + "operationKind": "query", + "text": "query observeFragmentTestPluralThrowOnFieldErrorQuery {\n nodes(ids: [\"1\", \"2\"]) {\n __typename\n ...observeFragmentTestPluralThrowOnFieldErrorFragment\n id\n }\n}\n\nfragment observeFragmentTestPluralThrowOnFieldErrorFragment on User {\n name\n}\n" + } +}; +})(); + +if (__DEV__) { + (node/*: any*/).hash = "36cd146fd2db4ac80dfe226a3e20dd3e"; +} + +module.exports = ((node/*: any*/)/*: Query< + observeFragmentTestPluralThrowOnFieldErrorQuery$variables, + observeFragmentTestPluralThrowOnFieldErrorQuery$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestResolverErrorWithPluralThrowOnFieldErrorFragment.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestResolverErrorWithPluralThrowOnFieldErrorFragment.graphql.js new file mode 100644 index 0000000000000..ba9df1771894e --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestResolverErrorWithPluralThrowOnFieldErrorFragment.graphql.js @@ -0,0 +1,78 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { Fragment, ReaderFragment } from 'relay-runtime'; +import type { UserAlwaysThrowsResolver$key } from "./../resolvers/__generated__/UserAlwaysThrowsResolver.graphql"; +import type { FragmentType } from "relay-runtime"; +import {always_throws as userAlwaysThrowsResolverType} from "../resolvers/UserAlwaysThrowsResolver.js"; +import type { TestResolverContextType } from "../../../mutations/__tests__/TestResolverContextType"; +// Type assertion validating that `userAlwaysThrowsResolverType` resolver is correctly implemented. +// A type error here indicates that the type signature of the resolver module is incorrect. +(userAlwaysThrowsResolverType: ( + rootKey: UserAlwaysThrowsResolver$key, + args: void, + context: TestResolverContextType, +) => ?string); +declare export opaque type observeFragmentTestResolverErrorWithPluralThrowOnFieldErrorFragment$fragmentType: FragmentType; +export type observeFragmentTestResolverErrorWithPluralThrowOnFieldErrorFragment$data = $ReadOnlyArray<{| + +always_throws: ?string, + +$fragmentType: observeFragmentTestResolverErrorWithPluralThrowOnFieldErrorFragment$fragmentType, +|}>; +export type observeFragmentTestResolverErrorWithPluralThrowOnFieldErrorFragment$key = $ReadOnlyArray<{ + +$data?: observeFragmentTestResolverErrorWithPluralThrowOnFieldErrorFragment$data, + +$fragmentSpreads: observeFragmentTestResolverErrorWithPluralThrowOnFieldErrorFragment$fragmentType, + ... +}>; +*/ + +var node/*: ReaderFragment*/ = { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": { + "plural": true, + "throwOnFieldError": true + }, + "name": "observeFragmentTestResolverErrorWithPluralThrowOnFieldErrorFragment", + "selections": [ + { + "alias": null, + "args": null, + "fragment": { + "args": null, + "kind": "FragmentSpread", + "name": "UserAlwaysThrowsResolver" + }, + "kind": "RelayResolver", + "name": "always_throws", + "resolverModule": require('../resolvers/UserAlwaysThrowsResolver').always_throws, + "path": "always_throws" + } + ], + "type": "User", + "abstractKey": null +}; + +if (__DEV__) { + (node/*: any*/).hash = "59a6f5ddf61e54affd5726b8cf322183"; +} + +module.exports = ((node/*: any*/)/*: Fragment< + observeFragmentTestResolverErrorWithPluralThrowOnFieldErrorFragment$fragmentType, + observeFragmentTestResolverErrorWithPluralThrowOnFieldErrorFragment$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestResolverErrorWithPluralThrowOnFieldErrorQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestResolverErrorWithPluralThrowOnFieldErrorQuery.graphql.js new file mode 100644 index 0000000000000..924a7c4f2ce5a --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestResolverErrorWithPluralThrowOnFieldErrorQuery.graphql.js @@ -0,0 +1,146 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<88f631a8a727158afc72c5ed15b804a4>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ConcreteRequest, Query } from 'relay-runtime'; +import type { observeFragmentTestResolverErrorWithPluralThrowOnFieldErrorFragment$fragmentType } from "./observeFragmentTestResolverErrorWithPluralThrowOnFieldErrorFragment.graphql"; +export type observeFragmentTestResolverErrorWithPluralThrowOnFieldErrorQuery$variables = {||}; +export type observeFragmentTestResolverErrorWithPluralThrowOnFieldErrorQuery$data = {| + +nodes: ?$ReadOnlyArray, +|}; +export type observeFragmentTestResolverErrorWithPluralThrowOnFieldErrorQuery = {| + response: observeFragmentTestResolverErrorWithPluralThrowOnFieldErrorQuery$data, + variables: observeFragmentTestResolverErrorWithPluralThrowOnFieldErrorQuery$variables, +|}; +*/ + +var node/*: ConcreteRequest*/ = (function(){ +var v0 = [ + { + "kind": "Literal", + "name": "ids", + "value": [ + "7", + "8" + ] + } +], +v1 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null +}; +return { + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "observeFragmentTestResolverErrorWithPluralThrowOnFieldErrorQuery", + "selections": [ + { + "alias": null, + "args": (v0/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "nodes", + "plural": true, + "selections": [ + { + "args": null, + "kind": "FragmentSpread", + "name": "observeFragmentTestResolverErrorWithPluralThrowOnFieldErrorFragment" + } + ], + "storageKey": "nodes(ids:[\"7\",\"8\"])" + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [], + "kind": "Operation", + "name": "observeFragmentTestResolverErrorWithPluralThrowOnFieldErrorQuery", + "selections": [ + { + "alias": null, + "args": (v0/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "nodes", + "plural": true, + "selections": [ + (v1/*: any*/), + { + "kind": "InlineFragment", + "selections": [ + { + "name": "always_throws", + "args": null, + "fragment": { + "kind": "InlineFragment", + "selections": [ + (v1/*: any*/) + ], + "type": "User", + "abstractKey": null + }, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": true + } + ], + "type": "User", + "abstractKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + } + ], + "storageKey": "nodes(ids:[\"7\",\"8\"])" + } + ] + }, + "params": { + "cacheID": "cda0054b75d8a0bc0abaacbb14186b4a", + "id": null, + "metadata": {}, + "name": "observeFragmentTestResolverErrorWithPluralThrowOnFieldErrorQuery", + "operationKind": "query", + "text": "query observeFragmentTestResolverErrorWithPluralThrowOnFieldErrorQuery {\n nodes(ids: [\"7\", \"8\"]) {\n __typename\n ...observeFragmentTestResolverErrorWithPluralThrowOnFieldErrorFragment\n id\n }\n}\n\nfragment UserAlwaysThrowsResolver on User {\n __typename\n}\n\nfragment observeFragmentTestResolverErrorWithPluralThrowOnFieldErrorFragment on User {\n ...UserAlwaysThrowsResolver\n}\n" + } +}; +})(); + +if (__DEV__) { + (node/*: any*/).hash = "412492582875c8c7b44e67794ed55763"; +} + +module.exports = ((node/*: any*/)/*: Query< + observeFragmentTestResolverErrorWithPluralThrowOnFieldErrorQuery$variables, + observeFragmentTestResolverErrorWithPluralThrowOnFieldErrorQuery$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestResolverErrorWithThrowOnFieldErrorFragment.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestResolverErrorWithThrowOnFieldErrorFragment.graphql.js index 271fbbebb0a77..399bfaf8365fb 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestResolverErrorWithThrowOnFieldErrorFragment.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestResolverErrorWithThrowOnFieldErrorFragment.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<8077cbe9adba41b9ff6804b8994d70bc>> + * @generated SignedSource<<60afda4fdaac41660aada81a1cb1b26f>> * @flow * @lightSyntaxTransform * @nogrep @@ -59,7 +59,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "always_throws", - "resolverModule": require('./../resolvers/UserAlwaysThrowsResolver').always_throws, + "resolverModule": require('../resolvers/UserAlwaysThrowsResolver').always_throws, "path": "always_throws" } ], diff --git a/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestToResolverSuspenseFragment.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestToResolverSuspenseFragment.graphql.js index 5ae20f76f1442..55e80c10f346e 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestToResolverSuspenseFragment.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/observeFragmentTestToResolverSuspenseFragment.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<8229cec6e11b78d82481c095a5c41114>> + * @generated SignedSource<<6972b1a399382269a5fecc1a666eb5d4>> * @flow * @lightSyntaxTransform * @nogrep @@ -54,7 +54,7 @@ var node/*: ReaderFragment*/ = { "fragment": null, "kind": "RelayLiveResolver", "name": "counter_suspends_when_odd", - "resolverModule": require('./../resolvers/CounterSuspendsWhenOddOnUser').counter_suspends_when_odd, + "resolverModule": require('../resolvers/CounterSuspendsWhenOddOnUser').counter_suspends_when_odd, "path": "counter_suspends_when_odd" } ] diff --git a/packages/relay-runtime/store/__tests__/__generated__/observeQueryTestClientEdgeToServerQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/observeQueryTestClientEdgeToServerQuery.graphql.js new file mode 100644 index 0000000000000..00d099c9a6e12 --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/observeQueryTestClientEdgeToServerQuery.graphql.js @@ -0,0 +1,166 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ConcreteRequest, Query } from 'relay-runtime'; +import type { DataID } from "relay-runtime"; +import type { UserClientEdgeResolver$key } from "./../resolvers/__generated__/UserClientEdgeResolver.graphql"; +import {client_edge as userClientEdgeResolverType} from "../resolvers/UserClientEdgeResolver.js"; +import type { TestResolverContextType } from "../../../mutations/__tests__/TestResolverContextType"; +// Type assertion validating that `userClientEdgeResolverType` resolver is correctly implemented. +// A type error here indicates that the type signature of the resolver module is incorrect. +(userClientEdgeResolverType: ( + rootKey: UserClientEdgeResolver$key, + args: void, + context: TestResolverContextType, +) => ?{| + +id: DataID, +|}); +export type observeQueryTestClientEdgeToServerQuery$variables = {||}; +export type observeQueryTestClientEdgeToServerQuery$data = {| + +me: ?{| + +client_edge: ?{| + +name: ?string, + |}, + |}, +|}; +export type observeQueryTestClientEdgeToServerQuery = {| + response: observeQueryTestClientEdgeToServerQuery$data, + variables: observeQueryTestClientEdgeToServerQuery$variables, +|}; +*/ + +var node/*: ConcreteRequest*/ = (function(){ +var v0 = [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + } +]; +return { + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": { + "hasClientEdges": true + }, + "name": "observeQueryTestClientEdgeToServerQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "me", + "plural": false, + "selections": [ + { + "kind": "ClientEdgeToServerObject", + "operation": require('./ClientEdgeQuery_observeQueryTestClientEdgeToServerQuery_me__client_edge.graphql'), + "backingField": { + "alias": null, + "args": null, + "fragment": { + "args": null, + "kind": "FragmentSpread", + "name": "UserClientEdgeResolver" + }, + "kind": "RelayResolver", + "name": "client_edge", + "resolverModule": require('../resolvers/UserClientEdgeResolver').client_edge, + "path": "me.client_edge" + }, + "linkedField": { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "client_edge", + "plural": false, + "selections": (v0/*: any*/), + "storageKey": null + } + } + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [], + "kind": "Operation", + "name": "observeQueryTestClientEdgeToServerQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "me", + "plural": false, + "selections": [ + { + "name": "client_edge", + "args": null, + "fragment": { + "kind": "InlineFragment", + "selections": (v0/*: any*/), + "type": "User", + "abstractKey": null + }, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": false + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "4e94c015341087851229eec13f7213b1", + "id": null, + "metadata": {}, + "name": "observeQueryTestClientEdgeToServerQuery", + "operationKind": "query", + "text": "query observeQueryTestClientEdgeToServerQuery {\n me {\n ...UserClientEdgeResolver\n id\n }\n}\n\nfragment UserClientEdgeResolver on User {\n name\n}\n" + } +}; +})(); + +if (__DEV__) { + (node/*: any*/).hash = "ffa93f1454a0796bf5a92612348c1069"; +} + +module.exports = ((node/*: any*/)/*: Query< + observeQueryTestClientEdgeToServerQuery$variables, + observeQueryTestClientEdgeToServerQuery$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/observeQueryTestMissingDataQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/observeQueryTestMissingDataQuery.graphql.js new file mode 100644 index 0000000000000..87e2905af4489 --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/observeQueryTestMissingDataQuery.graphql.js @@ -0,0 +1,109 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<33f91a63eb0724562bcf16677a24057a>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ConcreteRequest, Query } from 'relay-runtime'; +export type observeQueryTestMissingDataQuery$variables = {||}; +export type observeQueryTestMissingDataQuery$data = {| + +me: ?{| + +name: ?string, + |}, +|}; +export type observeQueryTestMissingDataQuery = {| + response: observeQueryTestMissingDataQuery$data, + variables: observeQueryTestMissingDataQuery$variables, +|}; +*/ + +var node/*: ConcreteRequest*/ = (function(){ +var v0 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null +}; +return { + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "observeQueryTestMissingDataQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "me", + "plural": false, + "selections": [ + (v0/*: any*/) + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [], + "kind": "Operation", + "name": "observeQueryTestMissingDataQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "me", + "plural": false, + "selections": [ + (v0/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "42404fb6150a1449eab3884b37c73364", + "id": null, + "metadata": {}, + "name": "observeQueryTestMissingDataQuery", + "operationKind": "query", + "text": "query observeQueryTestMissingDataQuery {\n me {\n name\n id\n }\n}\n" + } +}; +})(); + +if (__DEV__) { + (node/*: any*/).hash = "fce2f88e91a34d55e4623cc1fe05fd8d"; +} + +module.exports = ((node/*: any*/)/*: Query< + observeQueryTestMissingDataQuery$variables, + observeQueryTestMissingDataQuery$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/observeQueryTestMissingDataThrowOnFieldErrorQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/observeQueryTestMissingDataThrowOnFieldErrorQuery.graphql.js new file mode 100644 index 0000000000000..68dece5bca1aa --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/observeQueryTestMissingDataThrowOnFieldErrorQuery.graphql.js @@ -0,0 +1,111 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<63b805928cad74aec1262eb1dfaf9298>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ConcreteRequest, Query } from 'relay-runtime'; +export type observeQueryTestMissingDataThrowOnFieldErrorQuery$variables = {||}; +export type observeQueryTestMissingDataThrowOnFieldErrorQuery$data = {| + +me: ?{| + +name: ?string, + |}, +|}; +export type observeQueryTestMissingDataThrowOnFieldErrorQuery = {| + response: observeQueryTestMissingDataThrowOnFieldErrorQuery$data, + variables: observeQueryTestMissingDataThrowOnFieldErrorQuery$variables, +|}; +*/ + +var node/*: ConcreteRequest*/ = (function(){ +var v0 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null +}; +return { + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": { + "throwOnFieldError": true + }, + "name": "observeQueryTestMissingDataThrowOnFieldErrorQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "me", + "plural": false, + "selections": [ + (v0/*: any*/) + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [], + "kind": "Operation", + "name": "observeQueryTestMissingDataThrowOnFieldErrorQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "me", + "plural": false, + "selections": [ + (v0/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "822ec917528a39a1cb8331c99debf942", + "id": null, + "metadata": {}, + "name": "observeQueryTestMissingDataThrowOnFieldErrorQuery", + "operationKind": "query", + "text": "query observeQueryTestMissingDataThrowOnFieldErrorQuery {\n me {\n name\n id\n }\n}\n" + } +}; +})(); + +if (__DEV__) { + (node/*: any*/).hash = "2c67ee13885f703685a003f37f84b58e"; +} + +module.exports = ((node/*: any*/)/*: Query< + observeQueryTestMissingDataThrowOnFieldErrorQuery$variables, + observeQueryTestMissingDataThrowOnFieldErrorQuery$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/observeQueryTestMissingDataUnrelatedQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/observeQueryTestMissingDataUnrelatedQuery.graphql.js new file mode 100644 index 0000000000000..e010cd2655e67 --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/observeQueryTestMissingDataUnrelatedQuery.graphql.js @@ -0,0 +1,109 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<4f49edc6b8b70bea52e470ea3bf72b8f>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ConcreteRequest, Query } from 'relay-runtime'; +export type observeQueryTestMissingDataUnrelatedQuery$variables = {||}; +export type observeQueryTestMissingDataUnrelatedQuery$data = {| + +me: ?{| + +__typename: "User", + |}, +|}; +export type observeQueryTestMissingDataUnrelatedQuery = {| + response: observeQueryTestMissingDataUnrelatedQuery$data, + variables: observeQueryTestMissingDataUnrelatedQuery$variables, +|}; +*/ + +var node/*: ConcreteRequest*/ = (function(){ +var v0 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null +}; +return { + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "observeQueryTestMissingDataUnrelatedQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "me", + "plural": false, + "selections": [ + (v0/*: any*/) + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [], + "kind": "Operation", + "name": "observeQueryTestMissingDataUnrelatedQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "me", + "plural": false, + "selections": [ + (v0/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "68872f518ba2e0b2eaa2d19f91b90428", + "id": null, + "metadata": {}, + "name": "observeQueryTestMissingDataUnrelatedQuery", + "operationKind": "query", + "text": "query observeQueryTestMissingDataUnrelatedQuery {\n me {\n __typename\n id\n }\n}\n" + } +}; +})(); + +if (__DEV__) { + (node/*: any*/).hash = "7ea7dad2b70a3cbeb40022b3f5970f4f"; +} + +module.exports = ((node/*: any*/)/*: Query< + observeQueryTestMissingDataUnrelatedQuery$variables, + observeQueryTestMissingDataUnrelatedQuery$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/observeQueryTestMissingDataUnrelatedThrowOnFieldErrorQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/observeQueryTestMissingDataUnrelatedThrowOnFieldErrorQuery.graphql.js new file mode 100644 index 0000000000000..d7c08d0e9e070 --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/observeQueryTestMissingDataUnrelatedThrowOnFieldErrorQuery.graphql.js @@ -0,0 +1,109 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<8dffc4533d89b9f876ceee7657d2d467>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ConcreteRequest, Query } from 'relay-runtime'; +export type observeQueryTestMissingDataUnrelatedThrowOnFieldErrorQuery$variables = {||}; +export type observeQueryTestMissingDataUnrelatedThrowOnFieldErrorQuery$data = {| + +me: ?{| + +__typename: "User", + |}, +|}; +export type observeQueryTestMissingDataUnrelatedThrowOnFieldErrorQuery = {| + response: observeQueryTestMissingDataUnrelatedThrowOnFieldErrorQuery$data, + variables: observeQueryTestMissingDataUnrelatedThrowOnFieldErrorQuery$variables, +|}; +*/ + +var node/*: ConcreteRequest*/ = (function(){ +var v0 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null +}; +return { + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "observeQueryTestMissingDataUnrelatedThrowOnFieldErrorQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "me", + "plural": false, + "selections": [ + (v0/*: any*/) + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [], + "kind": "Operation", + "name": "observeQueryTestMissingDataUnrelatedThrowOnFieldErrorQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "me", + "plural": false, + "selections": [ + (v0/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "675526f1d29570a05a0db2e4922ae976", + "id": null, + "metadata": {}, + "name": "observeQueryTestMissingDataUnrelatedThrowOnFieldErrorQuery", + "operationKind": "query", + "text": "query observeQueryTestMissingDataUnrelatedThrowOnFieldErrorQuery {\n me {\n __typename\n id\n }\n}\n" + } +}; +})(); + +if (__DEV__) { + (node/*: any*/).hash = "f2f774d0e2e76182f3eb83256b8185ba"; +} + +module.exports = ((node/*: any*/)/*: Query< + observeQueryTestMissingDataUnrelatedThrowOnFieldErrorQuery$variables, + observeQueryTestMissingDataUnrelatedThrowOnFieldErrorQuery$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/observeQueryTestMissingRequiredQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/observeQueryTestMissingRequiredQuery.graphql.js new file mode 100644 index 0000000000000..ffd8087b2f833 --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/observeQueryTestMissingRequiredQuery.graphql.js @@ -0,0 +1,113 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ConcreteRequest, Query } from 'relay-runtime'; +export type observeQueryTestMissingRequiredQuery$variables = {||}; +export type observeQueryTestMissingRequiredQuery$data = {| + +me: ?{| + +name: string, + |}, +|}; +export type observeQueryTestMissingRequiredQuery = {| + response: observeQueryTestMissingRequiredQuery$data, + variables: observeQueryTestMissingRequiredQuery$variables, +|}; +*/ + +var node/*: ConcreteRequest*/ = (function(){ +var v0 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null +}; +return { + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "observeQueryTestMissingRequiredQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "me", + "plural": false, + "selections": [ + { + "kind": "RequiredField", + "field": (v0/*: any*/), + "action": "THROW" + } + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [], + "kind": "Operation", + "name": "observeQueryTestMissingRequiredQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "me", + "plural": false, + "selections": [ + (v0/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "707724bd3beaff7ad90917bad010d845", + "id": null, + "metadata": {}, + "name": "observeQueryTestMissingRequiredQuery", + "operationKind": "query", + "text": "query observeQueryTestMissingRequiredQuery {\n me {\n name\n id\n }\n}\n" + } +}; +})(); + +if (__DEV__) { + (node/*: any*/).hash = "b548b7931d8b55a04a57e7b1c1797f4c"; +} + +module.exports = ((node/*: any*/)/*: Query< + observeQueryTestMissingRequiredQuery$variables, + observeQueryTestMissingRequiredQuery$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/observeQueryTestNetworkErrorQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/observeQueryTestNetworkErrorQuery.graphql.js new file mode 100644 index 0000000000000..de85b3b26e742 --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/observeQueryTestNetworkErrorQuery.graphql.js @@ -0,0 +1,109 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<09d0a1c478d35136e63afa2bc2653ad8>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ConcreteRequest, Query } from 'relay-runtime'; +export type observeQueryTestNetworkErrorQuery$variables = {||}; +export type observeQueryTestNetworkErrorQuery$data = {| + +me: ?{| + +name: ?string, + |}, +|}; +export type observeQueryTestNetworkErrorQuery = {| + response: observeQueryTestNetworkErrorQuery$data, + variables: observeQueryTestNetworkErrorQuery$variables, +|}; +*/ + +var node/*: ConcreteRequest*/ = (function(){ +var v0 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null +}; +return { + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "observeQueryTestNetworkErrorQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "me", + "plural": false, + "selections": [ + (v0/*: any*/) + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [], + "kind": "Operation", + "name": "observeQueryTestNetworkErrorQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "me", + "plural": false, + "selections": [ + (v0/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "fce18c65a650f046d25eda46a99e460e", + "id": null, + "metadata": {}, + "name": "observeQueryTestNetworkErrorQuery", + "operationKind": "query", + "text": "query observeQueryTestNetworkErrorQuery {\n me {\n name\n id\n }\n}\n" + } +}; +})(); + +if (__DEV__) { + (node/*: any*/).hash = "c4ad31bfcc15055941d25760eea785bf"; +} + +module.exports = ((node/*: any*/)/*: Query< + observeQueryTestNetworkErrorQuery$variables, + observeQueryTestNetworkErrorQuery$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/observeQueryTestResolverErrorWithThrowOnFieldErrorQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/observeQueryTestResolverErrorWithThrowOnFieldErrorQuery.graphql.js new file mode 100644 index 0000000000000..709be22e4c0e2 --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/observeQueryTestResolverErrorWithThrowOnFieldErrorQuery.graphql.js @@ -0,0 +1,144 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ConcreteRequest, Query } from 'relay-runtime'; +import type { UserAlwaysThrowsResolver$key } from "./../resolvers/__generated__/UserAlwaysThrowsResolver.graphql"; +import {always_throws as userAlwaysThrowsResolverType} from "../resolvers/UserAlwaysThrowsResolver.js"; +import type { TestResolverContextType } from "../../../mutations/__tests__/TestResolverContextType"; +// Type assertion validating that `userAlwaysThrowsResolverType` resolver is correctly implemented. +// A type error here indicates that the type signature of the resolver module is incorrect. +(userAlwaysThrowsResolverType: ( + rootKey: UserAlwaysThrowsResolver$key, + args: void, + context: TestResolverContextType, +) => ?string); +export type observeQueryTestResolverErrorWithThrowOnFieldErrorQuery$variables = {||}; +export type observeQueryTestResolverErrorWithThrowOnFieldErrorQuery$data = {| + +me: ?{| + +always_throws: ?string, + |}, +|}; +export type observeQueryTestResolverErrorWithThrowOnFieldErrorQuery = {| + response: observeQueryTestResolverErrorWithThrowOnFieldErrorQuery$data, + variables: observeQueryTestResolverErrorWithThrowOnFieldErrorQuery$variables, +|}; +*/ + +var node/*: ConcreteRequest*/ = { + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": { + "throwOnFieldError": true + }, + "name": "observeQueryTestResolverErrorWithThrowOnFieldErrorQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "me", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "fragment": { + "args": null, + "kind": "FragmentSpread", + "name": "UserAlwaysThrowsResolver" + }, + "kind": "RelayResolver", + "name": "always_throws", + "resolverModule": require('../resolvers/UserAlwaysThrowsResolver').always_throws, + "path": "me.always_throws" + } + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [], + "kind": "Operation", + "name": "observeQueryTestResolverErrorWithThrowOnFieldErrorQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "me", + "plural": false, + "selections": [ + { + "name": "always_throws", + "args": null, + "fragment": { + "kind": "InlineFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null + } + ], + "type": "User", + "abstractKey": null + }, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": true + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "d7bef3f05a4033e1460dbe3276e7926b", + "id": null, + "metadata": {}, + "name": "observeQueryTestResolverErrorWithThrowOnFieldErrorQuery", + "operationKind": "query", + "text": "query observeQueryTestResolverErrorWithThrowOnFieldErrorQuery {\n me {\n ...UserAlwaysThrowsResolver\n id\n }\n}\n\nfragment UserAlwaysThrowsResolver on User {\n __typename\n}\n" + } +}; + +if (__DEV__) { + (node/*: any*/).hash = "37e387539b481973fc13b94cc329ce2a"; +} + +module.exports = ((node/*: any*/)/*: Query< + observeQueryTestResolverErrorWithThrowOnFieldErrorQuery$variables, + observeQueryTestResolverErrorWithThrowOnFieldErrorQuery$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/observeQueryTestThrowOnFieldErrorQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/observeQueryTestThrowOnFieldErrorQuery.graphql.js new file mode 100644 index 0000000000000..83f5f33c10fa3 --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/observeQueryTestThrowOnFieldErrorQuery.graphql.js @@ -0,0 +1,111 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<4def5f761d617b4eba0fe481b0871928>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ConcreteRequest, Query } from 'relay-runtime'; +export type observeQueryTestThrowOnFieldErrorQuery$variables = {||}; +export type observeQueryTestThrowOnFieldErrorQuery$data = {| + +me: ?{| + +name: ?string, + |}, +|}; +export type observeQueryTestThrowOnFieldErrorQuery = {| + response: observeQueryTestThrowOnFieldErrorQuery$data, + variables: observeQueryTestThrowOnFieldErrorQuery$variables, +|}; +*/ + +var node/*: ConcreteRequest*/ = (function(){ +var v0 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null +}; +return { + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": { + "throwOnFieldError": true + }, + "name": "observeQueryTestThrowOnFieldErrorQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "me", + "plural": false, + "selections": [ + (v0/*: any*/) + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [], + "kind": "Operation", + "name": "observeQueryTestThrowOnFieldErrorQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "me", + "plural": false, + "selections": [ + (v0/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "2bed926674a95b10fc3581d217d73493", + "id": null, + "metadata": {}, + "name": "observeQueryTestThrowOnFieldErrorQuery", + "operationKind": "query", + "text": "query observeQueryTestThrowOnFieldErrorQuery {\n me {\n name\n id\n }\n}\n" + } +}; +})(); + +if (__DEV__) { + (node/*: any*/).hash = "4e57fcb1ba72674139f2450c6bfda49a"; +} + +module.exports = ((node/*: any*/)/*: Query< + observeQueryTestThrowOnFieldErrorQuery$variables, + observeQueryTestThrowOnFieldErrorQuery$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/observeQueryTestToPromiseChainQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/observeQueryTestToPromiseChainQuery.graphql.js new file mode 100644 index 0000000000000..2133c14bca95a --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/observeQueryTestToPromiseChainQuery.graphql.js @@ -0,0 +1,115 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ConcreteRequest, Query } from 'relay-runtime'; +import type { observeQueryTestToPromiseFragment$fragmentType } from "./observeQueryTestToPromiseFragment.graphql"; +export type observeQueryTestToPromiseChainQuery$variables = {||}; +export type observeQueryTestToPromiseChainQuery$data = {| + +me: {| + +$fragmentSpreads: observeQueryTestToPromiseFragment$fragmentType, + |}, +|}; +export type observeQueryTestToPromiseChainQuery = {| + response: observeQueryTestToPromiseChainQuery$data, + variables: observeQueryTestToPromiseChainQuery$variables, +|}; +*/ + +var node/*: ConcreteRequest*/ = { + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "observeQueryTestToPromiseChainQuery", + "selections": [ + { + "kind": "RequiredField", + "field": { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "me", + "plural": false, + "selections": [ + { + "args": null, + "kind": "FragmentSpread", + "name": "observeQueryTestToPromiseFragment" + } + ], + "storageKey": null + }, + "action": "THROW" + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [], + "kind": "Operation", + "name": "observeQueryTestToPromiseChainQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "me", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "10a4ce6c650ba3747f231ed24cb8311c", + "id": null, + "metadata": {}, + "name": "observeQueryTestToPromiseChainQuery", + "operationKind": "query", + "text": "query observeQueryTestToPromiseChainQuery {\n me {\n ...observeQueryTestToPromiseFragment\n id\n }\n}\n\nfragment observeQueryTestToPromiseFragment on User {\n name\n}\n" + } +}; + +if (__DEV__) { + (node/*: any*/).hash = "d090c63a0d3e49ee45ef4907368db51e"; +} + +module.exports = ((node/*: any*/)/*: Query< + observeQueryTestToPromiseChainQuery$variables, + observeQueryTestToPromiseChainQuery$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/observeQueryTestToPromiseFragment.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/observeQueryTestToPromiseFragment.graphql.js new file mode 100644 index 0000000000000..c2d0ffefd6560 --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/observeQueryTestToPromiseFragment.graphql.js @@ -0,0 +1,59 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<866486fad21b6f203e5d569bbfa5816a>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { Fragment, ReaderFragment } from 'relay-runtime'; +import type { FragmentType } from "relay-runtime"; +declare export opaque type observeQueryTestToPromiseFragment$fragmentType: FragmentType; +export type observeQueryTestToPromiseFragment$data = {| + +name: ?string, + +$fragmentType: observeQueryTestToPromiseFragment$fragmentType, +|}; +export type observeQueryTestToPromiseFragment$key = { + +$data?: observeQueryTestToPromiseFragment$data, + +$fragmentSpreads: observeQueryTestToPromiseFragment$fragmentType, + ... +}; +*/ + +var node/*: ReaderFragment*/ = { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "observeQueryTestToPromiseFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + } + ], + "type": "User", + "abstractKey": null +}; + +if (__DEV__) { + (node/*: any*/).hash = "9d69f728a12d61512c227f5014a447cd"; +} + +module.exports = ((node/*: any*/)/*: Fragment< + observeQueryTestToPromiseFragment$fragmentType, + observeQueryTestToPromiseFragment$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/observeQueryTestToPromiseQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/observeQueryTestToPromiseQuery.graphql.js new file mode 100644 index 0000000000000..6bdda999f7ec0 --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/observeQueryTestToPromiseQuery.graphql.js @@ -0,0 +1,109 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ConcreteRequest, Query } from 'relay-runtime'; +export type observeQueryTestToPromiseQuery$variables = {||}; +export type observeQueryTestToPromiseQuery$data = {| + +me: ?{| + +name: ?string, + |}, +|}; +export type observeQueryTestToPromiseQuery = {| + response: observeQueryTestToPromiseQuery$data, + variables: observeQueryTestToPromiseQuery$variables, +|}; +*/ + +var node/*: ConcreteRequest*/ = (function(){ +var v0 = { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null +}; +return { + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "observeQueryTestToPromiseQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "me", + "plural": false, + "selections": [ + (v0/*: any*/) + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [], + "kind": "Operation", + "name": "observeQueryTestToPromiseQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "me", + "plural": false, + "selections": [ + (v0/*: any*/), + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "044d32caa80c462880354d2031312400", + "id": null, + "metadata": {}, + "name": "observeQueryTestToPromiseQuery", + "operationKind": "query", + "text": "query observeQueryTestToPromiseQuery {\n me {\n name\n id\n }\n}\n" + } +}; +})(); + +if (__DEV__) { + (node/*: any*/).hash = "9ef8801e54038e17ccb9d41fe22ee69d"; +} + +module.exports = ((node/*: any*/)/*: Query< + observeQueryTestToPromiseQuery$variables, + observeQueryTestToPromiseQuery$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/observeQueryTestToResolverSuspenseQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/observeQueryTestToResolverSuspenseQuery.graphql.js new file mode 100644 index 0000000000000..78fb61ce0f63d --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/observeQueryTestToResolverSuspenseQuery.graphql.js @@ -0,0 +1,134 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ConcreteRequest, Query } from 'relay-runtime'; +import type { LiveState } from "relay-runtime"; +import {counter_suspends_when_odd as userCounterSuspendsWhenOddResolverType} from "../resolvers/CounterSuspendsWhenOddOnUser.js"; +import type { TestResolverContextType } from "../../../mutations/__tests__/TestResolverContextType"; +// Type assertion validating that `userCounterSuspendsWhenOddResolverType` resolver is correctly implemented. +// A type error here indicates that the type signature of the resolver module is incorrect. +(userCounterSuspendsWhenOddResolverType: ( + args: void, + context: TestResolverContextType, +) => LiveState); +export type observeQueryTestToResolverSuspenseQuery$variables = {||}; +export type observeQueryTestToResolverSuspenseQuery$data = {| + +me: ?{| + +counter_suspends_when_odd: ?number, + |}, +|}; +export type observeQueryTestToResolverSuspenseQuery = {| + response: observeQueryTestToResolverSuspenseQuery$data, + variables: observeQueryTestToResolverSuspenseQuery$variables, +|}; +*/ + +var node/*: ConcreteRequest*/ = { + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "observeQueryTestToResolverSuspenseQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "me", + "plural": false, + "selections": [ + { + "kind": "ClientExtension", + "selections": [ + { + "alias": null, + "args": null, + "fragment": null, + "kind": "RelayLiveResolver", + "name": "counter_suspends_when_odd", + "resolverModule": require('../resolvers/CounterSuspendsWhenOddOnUser').counter_suspends_when_odd, + "path": "me.counter_suspends_when_odd" + } + ] + } + ], + "storageKey": null + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [], + "kind": "Operation", + "name": "observeQueryTestToResolverSuspenseQuery", + "selections": [ + { + "alias": null, + "args": null, + "concreteType": "User", + "kind": "LinkedField", + "name": "me", + "plural": false, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + }, + { + "kind": "ClientExtension", + "selections": [ + { + "name": "counter_suspends_when_odd", + "args": null, + "fragment": null, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": true + } + ] + } + ], + "storageKey": null + } + ] + }, + "params": { + "cacheID": "35367c8e2d47eb1f011a9ec78dbbcd71", + "id": null, + "metadata": {}, + "name": "observeQueryTestToResolverSuspenseQuery", + "operationKind": "query", + "text": "query observeQueryTestToResolverSuspenseQuery {\n me {\n id\n }\n}\n" + } +}; + +if (__DEV__) { + (node/*: any*/).hash = "c12131b8b3d0e8ec0de28cd1a839c2b8"; +} + +module.exports = ((node/*: any*/)/*: Query< + observeQueryTestToResolverSuspenseQuery$variables, + observeQueryTestToResolverSuspenseQuery$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/readInlineDataTestUserQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/readInlineDataTestUserQuery.graphql.js index 88fc2474f71a5..b47f26d73cfed 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/readInlineDataTestUserQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/readInlineDataTestUserQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<10034eb057a199176c4362dd541b97c3>> + * @generated SignedSource<<6a37ab03e5df65024c27b4da8cfd151d>> * @flow * @lightSyntaxTransform * @nogrep @@ -148,7 +148,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "64aaa0bdd963532aa7d5e61221722bca"; + (node/*: any*/).hash = "27f84c47b8d1f1f6f15e5a5869ff2687"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/store/__tests__/__generated__/waitForFragmentDataTestOkPluralFragment.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/waitForFragmentDataTestOkPluralFragment.graphql.js new file mode 100644 index 0000000000000..ecd0f3164f631 --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/waitForFragmentDataTestOkPluralFragment.graphql.js @@ -0,0 +1,61 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<2ed8051023394e48547a26c640b8e13c>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { Fragment, ReaderFragment } from 'relay-runtime'; +import type { FragmentType } from "relay-runtime"; +declare export opaque type waitForFragmentDataTestOkPluralFragment$fragmentType: FragmentType; +export type waitForFragmentDataTestOkPluralFragment$data = $ReadOnlyArray<{| + +name: ?string, + +$fragmentType: waitForFragmentDataTestOkPluralFragment$fragmentType, +|}>; +export type waitForFragmentDataTestOkPluralFragment$key = $ReadOnlyArray<{ + +$data?: waitForFragmentDataTestOkPluralFragment$data, + +$fragmentSpreads: waitForFragmentDataTestOkPluralFragment$fragmentType, + ... +}>; +*/ + +var node/*: ReaderFragment*/ = { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": { + "plural": true + }, + "name": "waitForFragmentDataTestOkPluralFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + } + ], + "type": "User", + "abstractKey": null +}; + +if (__DEV__) { + (node/*: any*/).hash = "27b1b1a834949ad358eaf6d1396d3f9d"; +} + +module.exports = ((node/*: any*/)/*: Fragment< + waitForFragmentDataTestOkPluralFragment$fragmentType, + waitForFragmentDataTestOkPluralFragment$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/waitForFragmentDataTestOkPluralQuery.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/waitForFragmentDataTestOkPluralQuery.graphql.js new file mode 100644 index 0000000000000..c1f1dc35eef47 --- /dev/null +++ b/packages/relay-runtime/store/__tests__/__generated__/waitForFragmentDataTestOkPluralQuery.graphql.js @@ -0,0 +1,137 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<5eca2a10e99142f6f4e5d99b02421ad1>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ConcreteRequest, Query } from 'relay-runtime'; +import type { waitForFragmentDataTestOkPluralFragment$fragmentType } from "./waitForFragmentDataTestOkPluralFragment.graphql"; +export type waitForFragmentDataTestOkPluralQuery$variables = {||}; +export type waitForFragmentDataTestOkPluralQuery$data = {| + +nodes: ?$ReadOnlyArray, +|}; +export type waitForFragmentDataTestOkPluralQuery = {| + response: waitForFragmentDataTestOkPluralQuery$data, + variables: waitForFragmentDataTestOkPluralQuery$variables, +|}; +*/ + +var node/*: ConcreteRequest*/ = (function(){ +var v0 = [ + { + "kind": "Literal", + "name": "ids", + "value": [ + "1", + "2" + ] + } +]; +return { + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "waitForFragmentDataTestOkPluralQuery", + "selections": [ + { + "alias": null, + "args": (v0/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "nodes", + "plural": true, + "selections": [ + { + "args": null, + "kind": "FragmentSpread", + "name": "waitForFragmentDataTestOkPluralFragment" + } + ], + "storageKey": "nodes(ids:[\"1\",\"2\"])" + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [], + "kind": "Operation", + "name": "waitForFragmentDataTestOkPluralQuery", + "selections": [ + { + "alias": null, + "args": (v0/*: any*/), + "concreteType": null, + "kind": "LinkedField", + "name": "nodes", + "plural": true, + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__typename", + "storageKey": null + }, + { + "kind": "InlineFragment", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "name", + "storageKey": null + } + ], + "type": "User", + "abstractKey": null + }, + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "id", + "storageKey": null + } + ], + "storageKey": "nodes(ids:[\"1\",\"2\"])" + } + ] + }, + "params": { + "cacheID": "91c66e0b45b06f9cc7a66477f8156418", + "id": null, + "metadata": {}, + "name": "waitForFragmentDataTestOkPluralQuery", + "operationKind": "query", + "text": "query waitForFragmentDataTestOkPluralQuery {\n nodes(ids: [\"1\", \"2\"]) {\n __typename\n ...waitForFragmentDataTestOkPluralFragment\n id\n }\n}\n\nfragment waitForFragmentDataTestOkPluralFragment on User {\n name\n}\n" + } +}; +})(); + +if (__DEV__) { + (node/*: any*/).hash = "139b32cebe816906461147bcb6b1db45"; +} + +module.exports = ((node/*: any*/)/*: Query< + waitForFragmentDataTestOkPluralQuery$variables, + waitForFragmentDataTestOkPluralQuery$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/__generated__/waitForFragmentDataTestResolverErrorWithThrowOnFieldErrorFragment.graphql.js b/packages/relay-runtime/store/__tests__/__generated__/waitForFragmentDataTestResolverErrorWithThrowOnFieldErrorFragment.graphql.js index 9c78e0478b039..54c373e2878f3 100644 --- a/packages/relay-runtime/store/__tests__/__generated__/waitForFragmentDataTestResolverErrorWithThrowOnFieldErrorFragment.graphql.js +++ b/packages/relay-runtime/store/__tests__/__generated__/waitForFragmentDataTestResolverErrorWithThrowOnFieldErrorFragment.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<83a0b4251531d569ff3d1bbe768926e9>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -59,7 +59,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "always_throws", - "resolverModule": require('./../resolvers/UserAlwaysThrowsResolver').always_throws, + "resolverModule": require('../resolvers/UserAlwaysThrowsResolver').always_throws, "path": "always_throws" } ], diff --git a/packages/relay-runtime/store/__tests__/observeFragment-test.js b/packages/relay-runtime/store/__tests__/observeFragment-test.js index 484a74009c32c..0bbfe7084a23a 100644 --- a/packages/relay-runtime/store/__tests__/observeFragment-test.js +++ b/packages/relay-runtime/store/__tests__/observeFragment-test.js @@ -145,6 +145,35 @@ test('Missing required data', async () => { }); }); +test('Keep loading on network error', async () => { + const query = graphql` + query observeFragmentTestNetworkErrorQuery { + ...observeFragmentTestNetworkErrorFragment + } + `; + + const fragment = graphql` + fragment observeFragmentTestNetworkErrorFragment on Query { + me { + name + } + } + `; + + const environment = createMockEnvironment(); + const variables = {}; + const operation = createOperationDescriptor(query, variables); + fetchQuery(environment, query, variables).subscribe({}); + const {data} = environment.lookup(operation.fragment); + // $FlowFixMe Data is untyped + const observable = observeFragment(environment, fragment, data); + withObservableValues(observable, results => { + expect(results).toEqual([{state: 'loading'}]); + environment.mock.reject(operation, new Error('Network error')); + expect(results).toEqual([{state: 'loading'}]); + }); +}); + test('Field error with @throwOnFieldError', async () => { const query = graphql` query observeFragmentTestThrowOnFieldErrorQuery { @@ -230,7 +259,7 @@ test('Resolver error with @throwOnFieldError', async () => { expect(results).toEqual([ { error: new Error( - "Relay: Resolver error at path 'always_throws' in 'observeFragmentTestResolverErrorWithThrowOnFieldErrorFragment'.", + "Relay: Resolver error at path 'always_throws' in 'observeFragmentTestResolverErrorWithThrowOnFieldErrorFragment'. Message: I always throw. What did you expect?", ), state: 'error', }, @@ -337,6 +366,238 @@ test('read deferred fragment', async () => { }); }); +test('observes a plural fragment', async () => { + const query = graphql` + query observeFragmentTestPluralQuery { + nodes(ids: ["1", "2"]) { + ...observeFragmentTestPluralFragment + } + } + `; + + const fragment = graphql` + fragment observeFragmentTestPluralFragment on User @relay(plural: true) { + name + } + `; + + const environment = createMockEnvironment({ + store: new LiveResolverStore(new RelayRecordSource()), + }); + const variables = {}; + const operation = createOperationDescriptor(query, variables); + environment.commitPayload(operation, { + nodes: [ + {id: '1', __typename: 'User', name: 'Alice'}, + {id: '2', __typename: 'User', name: 'Bob'}, + ], + }); + const {data} = environment.lookup(operation.fragment); + // $FlowFixMe Data is untyped + const observable = observeFragment(environment, fragment, data.nodes); + const result = await observable.toPromise(); + expect(result).toEqual({ + state: 'ok', + value: [{name: 'Alice'}, {name: 'Bob'}], + }); +}); + +test('Missing required data on plural fragment', async () => { + const query = graphql` + query observeFragmentTestMissingRequiredPluralQuery { + nodes(ids: ["1", "2"]) { + ...observeFragmentTestMissingRequiredPluralFragment + } + } + `; + + const fragment = graphql` + fragment observeFragmentTestMissingRequiredPluralFragment on User + @relay(plural: true) { + name @required(action: THROW) + } + `; + + const environment = createMockEnvironment(); + const variables = {}; + const operation = createOperationDescriptor(query, variables); + environment.commitPayload(operation, { + nodes: [ + // Name is null despite being required + {id: '1', __typename: 'User', name: null}, + {id: '2', __typename: 'User', name: 'Bob'}, + ], + }); + + const {data} = environment.lookup(operation.fragment); + // $FlowFixMe Data is untyped + const observable = observeFragment(environment, fragment, data.nodes); + withObservableValues(observable, results => { + expect(results).toEqual([ + { + error: new Error( + "Relay: Missing @required value at path 'name' in 'observeFragmentTestMissingRequiredPluralFragment'.", + ), + state: 'error', + }, + ]); + }); +}); + +test('Field error with @relay(plural: true) @throwOnFieldError', async () => { + const query = graphql` + query observeFragmentTestPluralThrowOnFieldErrorQuery { + nodes(ids: ["1", "2"]) { + ...observeFragmentTestPluralThrowOnFieldErrorFragment + } + } + `; + + const fragment = graphql` + fragment observeFragmentTestPluralThrowOnFieldErrorFragment on User + @relay(plural: true) + @throwOnFieldError { + name + } + `; + + let dataSource: Sink; + const fetch = ( + _query: RequestParameters, + _variables: Variables, + _cacheConfig: CacheConfig, + ) => { + // $FlowFixMe[missing-local-annot] Error found while enabling LTI on this file + return RelayObservable.create(sink => { + dataSource = sink; + }); + }; + + const environment = createMockEnvironment({ + network: RelayNetwork.create(fetch), + }); + const variables = {}; + const operation = createOperationDescriptor(query, variables); + + environment.execute({operation}).subscribe({}); + invariant(dataSource != null, 'Expected data source to be set'); + dataSource.next({ + data: { + nodes: [ + {id: '1', __typename: 'User', name: null}, + {id: '2', __typename: 'User', name: 'Bob'}, + ], + }, + errors: [{message: 'error', path: ['nodes', 0, 'name']}], + }); + + const {data} = environment.lookup(operation.fragment); + // $FlowFixMe Data is untyped + const observable = observeFragment(environment, fragment, data.nodes); + withObservableValues(observable, results => { + expect(results).toEqual([ + { + error: new Error( + 'Relay: Unexpected response payload - check server logs for details.', + ), + state: 'error', + }, + ]); + }); +}); + +test('Resolver error with @relay(plural: true) @throwOnFieldError', async () => { + const query = graphql` + query observeFragmentTestResolverErrorWithPluralThrowOnFieldErrorQuery { + nodes(ids: ["7", "8"]) { + ...observeFragmentTestResolverErrorWithPluralThrowOnFieldErrorFragment + } + } + `; + + const fragment = graphql` + fragment observeFragmentTestResolverErrorWithPluralThrowOnFieldErrorFragment on User + @relay(plural: true) + @throwOnFieldError { + always_throws + } + `; + + const environment = createMockEnvironment({ + store: new LiveResolverStore(new RelayRecordSource()), + }); + const variables = {}; + const operation = createOperationDescriptor(query, variables); + environment.commitPayload(operation, { + nodes: [ + {id: '7', __typename: 'User'}, + {id: '8', __typename: 'User'}, + ], + }); + const {data} = environment.lookup(operation.fragment); + // $FlowFixMe Data is untyped + const observable = observeFragment(environment, fragment, data.nodes); + withObservableValues(observable, results => { + expect(results).toEqual([ + { + error: new Error( + "Relay: Resolver error at path 'always_throws' in 'observeFragmentTestResolverErrorWithPluralThrowOnFieldErrorFragment'. Message: I always throw. What did you expect?", + ), + state: 'error', + }, + ]); + }); +}); + +test('Store update across list items notifies multiple times', async () => { + const query = graphql` + query observeFragmentTestListUpdateQuery { + nodes(ids: ["1", "2"]) { + ...observeFragmentTestListUpdateFragment + } + } + `; + + const fragment = graphql` + fragment observeFragmentTestListUpdateFragment on User + @relay(plural: true) { + name + } + `; + + const environment = createMockEnvironment({ + store: new LiveResolverStore(new RelayRecordSource()), + }); + const variables = {}; + const operation = createOperationDescriptor(query, variables); + environment.commitPayload(operation, { + nodes: [ + {id: '1', __typename: 'User', name: 'Alice'}, + {id: '2', __typename: 'User', name: 'Bob'}, + ], + }); + const {data} = environment.lookup(operation.fragment); + // $FlowFixMe Data is untyped + const observable = observeFragment(environment, fragment, data.nodes); + withObservableValues(observable, results => { + expect(results).toEqual([ + {state: 'ok', value: [{name: 'Alice'}, {name: 'Bob'}]}, + ]); + + environment.commitPayload(operation, { + nodes: [ + {id: '1', __typename: 'User', name: 'Alice updated'}, + {id: '2', __typename: 'User', name: 'Bob updated'}, + ], + }); + expect(results).toEqual([ + {state: 'ok', value: [{name: 'Alice'}, {name: 'Bob'}]}, + {state: 'ok', value: [{name: 'Alice updated'}, {name: 'Bob'}]}, + {state: 'ok', value: [{name: 'Alice updated'}, {name: 'Bob updated'}]}, + ]); + }); +}); + test('data goes missing due to unrelated query response', async () => { const query = graphql` query observeFragmentTestMissingDataQuery { diff --git a/packages/relay-runtime/store/__tests__/observeQuery-test.js b/packages/relay-runtime/store/__tests__/observeQuery-test.js new file mode 100644 index 0000000000000..53947ede77882 --- /dev/null +++ b/packages/relay-runtime/store/__tests__/observeQuery-test.js @@ -0,0 +1,394 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + * @oncall relay + */ + +import type {GraphQLResponse} from '../../network/RelayNetworkTypes'; +import type {Sink} from '../../network/RelayObservable'; +import type {RequestParameters} from 'relay-runtime/util/RelayConcreteNode'; +import type { + CacheConfig, + Variables, +} from 'relay-runtime/util/RelayRuntimeTypes'; + +const RelayNetwork = require('../../network/RelayNetwork'); +const RelayObservable = require('../../network/RelayObservable'); +const fetchQuery = require('../../query/fetchQuery'); +const {graphql} = require('../../query/GraphQLTag'); +const LiveResolverStore = require('../live-resolvers/LiveResolverStore'); +const {observeFragment} = require('../observeFragmentExperimental'); +const {observeQuery} = require('../observeQueryExperimental'); +const { + createOperationDescriptor, +} = require('../RelayModernOperationDescriptor'); +const RelayRecordSource = require('../RelayRecordSource'); +const {GLOBAL_STORE} = require('./resolvers/ExampleExternalStateStore'); +const invariant = require('invariant'); +const {createMockEnvironment} = require('relay-test-utils-internal'); + +afterEach(() => { + GLOBAL_STORE.reset(); +}); + +test('toPromise state ok', async () => { + const query = graphql` + query observeQueryTestToPromiseQuery { + me { + name + } + } + `; + + const environment = createMockEnvironment({ + store: new LiveResolverStore(new RelayRecordSource()), + }); + const variables = {}; + const operation = createOperationDescriptor(query, variables); + environment.commitPayload(operation, { + me: {id: '7', __typename: 'User', name: 'Elizabeth'}, + }); + const observable = observeQuery(environment, query, variables); + const result = await observable.toPromise(); + expect(result).toEqual({state: 'ok', value: {me: {name: 'Elizabeth'}}}); +}); + +test('toPromise state ok chain with observeFragment', async () => { + const query = graphql` + query observeQueryTestToPromiseChainQuery { + me @required(action: THROW) { + ...observeQueryTestToPromiseFragment + } + } + `; + + const fragment = graphql` + fragment observeQueryTestToPromiseFragment on User { + name + } + `; + + const environment = createMockEnvironment({ + store: new LiveResolverStore(new RelayRecordSource()), + }); + const variables = {}; + const operation = createOperationDescriptor(query, variables); + environment.commitPayload(operation, { + me: {id: '7', __typename: 'User', name: 'Elizabeth'}, + }); + const observable = observeQuery(environment, query, variables); + const result = await observable.toPromise(); + if (result == null || result.state !== 'ok') { + throw new Error('Expected state to be ok'); + } + const fragmentObservable = observeFragment( + environment, + fragment, + result.value.me, + ); + + const fragmentResult = await fragmentObservable.toPromise(); + + expect(fragmentResult).toEqual({state: 'ok', value: {name: 'Elizabeth'}}); +}); + +test('resolver suspense suspends', async () => { + const query = graphql` + query observeQueryTestToResolverSuspenseQuery { + me { + counter_suspends_when_odd + } + } + `; + + const environment = createMockEnvironment({ + store: new LiveResolverStore(new RelayRecordSource()), + }); + const variables = {}; + const operation = createOperationDescriptor(query, variables); + environment.commitPayload(operation, { + me: {id: '7', __typename: 'User', name: 'Elizabeth'}, + }); + // $FlowFixMe Data is untyped + const observable = observeQuery(environment, query, variables); + withObservableValues(observable, results => { + GLOBAL_STORE.dispatch({type: 'INCREMENT'}); + GLOBAL_STORE.dispatch({type: 'INCREMENT'}); + GLOBAL_STORE.dispatch({type: 'INCREMENT'}); + expect(results).toEqual([ + {state: 'ok', value: {me: {counter_suspends_when_odd: 0}}}, + {state: 'loading'}, + {state: 'ok', value: {me: {counter_suspends_when_odd: 2}}}, + {state: 'loading'}, + ]); + }); +}); + +test('Missing required data', async () => { + const query = graphql` + query observeQueryTestMissingRequiredQuery { + me { + name @required(action: THROW) + } + } + `; + + const environment = createMockEnvironment(); + const variables = {}; + const operation = createOperationDescriptor(query, variables); + environment.commitPayload(operation, { + me: { + id: '7', + __typename: 'User', + // Name is null despite being required + name: null, + }, + }); + const observable = observeQuery(environment, query, variables); + withObservableValues(observable, results => { + expect(results).toEqual([ + { + error: new Error( + "Relay: Missing @required value at path 'me.name' in 'observeQueryTestMissingRequiredQuery'.", + ), + state: 'error', + }, + ]); + }); +}); + +test('Keep loading on network error', async () => { + const query = graphql` + query observeQueryTestNetworkErrorQuery { + me { + name + } + } + `; + + const environment = createMockEnvironment(); + const variables = {}; + const operation = createOperationDescriptor(query, variables); + fetchQuery(environment, query, variables).subscribe({}); + const observable = observeQuery(environment, query, variables); + withObservableValues(observable, results => { + expect(results).toEqual([{state: 'loading'}]); + environment.mock.reject(operation, new Error('Network error')); + expect(results).toEqual([{state: 'loading'}]); + }); +}); + +test('Field error with @throwOnFieldError', async () => { + const query = graphql` + query observeQueryTestThrowOnFieldErrorQuery @throwOnFieldError { + me { + name + } + } + `; + + let dataSource: Sink; + const fetch = ( + _query: RequestParameters, + _variables: Variables, + _cacheConfig: CacheConfig, + ) => { + // $FlowFixMe[missing-local-annot] Error found while enabling LTI on this file + return RelayObservable.create(sink => { + dataSource = sink; + }); + }; + + const environment = createMockEnvironment({ + network: RelayNetwork.create(fetch), + }); + const variables = {}; + const operation = createOperationDescriptor(query, variables); + + environment.execute({operation}).subscribe({}); + invariant(dataSource != null, 'Expected data source to be set'); + dataSource.next({ + data: {me: {id: '1', __typename: 'User', name: null}}, + errors: [{message: 'error', path: ['me', 'name']}], + }); + + const observable = observeQuery(environment, query, variables); + withObservableValues(observable, results => { + expect(results).toEqual([ + { + error: new Error( + 'Relay: Unexpected response payload - check server logs for details.', + ), + state: 'error', + }, + ]); + }); +}); + +test('Resolver error with @throwOnFieldError', async () => { + const query = graphql` + query observeQueryTestResolverErrorWithThrowOnFieldErrorQuery + @throwOnFieldError { + me { + always_throws + } + } + `; + + const environment = createMockEnvironment({ + store: new LiveResolverStore(new RelayRecordSource()), + }); + const variables = {}; + const operation = createOperationDescriptor(query, variables); + environment.commitPayload(operation, {me: {id: '7', __typename: 'User'}}); + // $FlowFixMe Data is untyped + const observable = observeQuery(environment, query, variables); + withObservableValues(observable, results => { + expect(results).toEqual([ + { + error: new Error( + "Relay: Resolver error at path 'me.always_throws' in 'observeQueryTestResolverErrorWithThrowOnFieldErrorQuery'. Message: I always throw. What did you expect?", + ), + state: 'error', + }, + ]); + }); +}); + +test('Resolver with client edge to server object', async () => { + const query = graphql` + query observeQueryTestClientEdgeToServerQuery { + me { + client_edge @waterfall { + name + } + } + } + `; + const environment = createMockEnvironment({ + store: new LiveResolverStore(new RelayRecordSource()), + }); + const variables = {}; + const operation = createOperationDescriptor(query, variables); + environment.commitPayload(operation, {me: {id: '7', __typename: 'User'}}); + let result; + try { + // $FlowFixMe Data is untyped + const observable = observeQuery(environment, query, variables); + // Today we never get to this, but once client edges are supported, we will + withObservableValues(observable, results => { + expect(results).toEqual([{state: 'error'}]); + }); + } catch (e) { + result = e; + } + // Until we support client edges, we throw an error + expect(result?.message).toEqual("Client edges aren't supported yet."); +}); + +test('data goes missing due to unrelated query response', async () => { + const query = graphql` + query observeQueryTestMissingDataQuery { + me { + name + } + } + `; + + const unrelatedQuery = graphql` + query observeQueryTestMissingDataUnrelatedQuery { + me { + # Does not fetch name + __typename + } + } + `; + + const environment = createMockEnvironment(); + const variables = {}; + const operation = createOperationDescriptor(query, variables); + environment.commitPayload(operation, { + me: {id: '7', __typename: 'User', name: 'Elizabeth'}, + }); + // $FlowFixMe Data is untyped + const observable = observeQuery(environment, query, variables); + + // Now an unrelated query comes in and changes the Query.me relationship to a + // new user, but does not fetch `name` for that user. Now we are missing data + // for the initial fragment, but there is no request in flight to fetch it. + const unrelatedOperation = createOperationDescriptor(unrelatedQuery, {}); + withObservableValues(observable, results => { + environment.commitPayload(unrelatedOperation, { + // Note: This is a _different_ user than last time + me: {id: '99', __typename: 'User'}, + }); + expect(results).toEqual([ + {state: 'ok', value: {me: {name: 'Elizabeth'}}}, + {state: 'ok', value: {me: {name: undefined}}}, + ]); + }); +}); + +test('data goes missing due to unrelated query response (@throwOnFieldErrro)', async () => { + const query = graphql` + query observeQueryTestMissingDataThrowOnFieldErrorQuery @throwOnFieldError { + me { + name + } + } + `; + + const unrelatedQuery = graphql` + query observeQueryTestMissingDataUnrelatedThrowOnFieldErrorQuery { + me { + # Does not fetch name + __typename + } + } + `; + + const environment = createMockEnvironment(); + const variables = {}; + const operation = createOperationDescriptor(query, variables); + environment.commitPayload(operation, { + me: {id: '7', __typename: 'User', name: 'Elizabeth'}, + }); + // $FlowFixMe Data is untyped + const observable = observeQuery(environment, query, variables); + + // Now an unrelated query comes in and changes the Query.me relationship to a + // new user, but does not fetch `name` for that user. Now we are missing data + // for the initial fragment, but there is no request in flight to fetch it. + const unrelatedOperation = createOperationDescriptor(unrelatedQuery, {}); + withObservableValues(observable, results => { + environment.commitPayload(unrelatedOperation, { + // Note: This is a _different_ user than last time + me: {id: '99', __typename: 'User'}, + }); + expect(results).toEqual([ + {state: 'ok', value: {me: {name: 'Elizabeth'}}}, + {state: 'error', error: expect.anything()}, + ]); + }); +}); + +// Helper function to test that a given Observable emits the expected values. +// The callback is called with an array of the values emitted by the Observable. +// The array is mutated as values are emitted, so the callback can always check +// the current state of the array. +function withObservableValues( + observable: RelayObservable, + callback: (values: Array) => void, +) { + const values = []; + const subscription = observable.subscribe({ + next: value => values.push(value), + }); + callback(values); + subscription.unsubscribe(); +} diff --git a/packages/relay-runtime/store/__tests__/readInlineData-test.js b/packages/relay-runtime/store/__tests__/readInlineData-test.js index f331e736420b5..40339bb3cc08b 100644 --- a/packages/relay-runtime/store/__tests__/readInlineData-test.js +++ b/packages/relay-runtime/store/__tests__/readInlineData-test.js @@ -28,7 +28,7 @@ disallowWarnings(); const UserQuery = graphql` query readInlineDataTestUserQuery($id: ID!) { node(id: $id) { - ...readInlineDataTestUserFragment # @arguments(cond: true) + ...readInlineDataTestUserFragment @dangerously_unaliased_fixme # @arguments(cond: true) } # with_name: node(id: $id) { # id diff --git a/packages/relay-runtime/store/__tests__/relayReaderTestExecTimeResolversFalseProvider.js b/packages/relay-runtime/store/__tests__/relayReaderTestExecTimeResolversFalseProvider.js new file mode 100644 index 0000000000000..9cd2d666e3a28 --- /dev/null +++ b/packages/relay-runtime/store/__tests__/relayReaderTestExecTimeResolversFalseProvider.js @@ -0,0 +1,18 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + * @oncall relay + */ + +'use strict'; + +module.exports = { + get(): boolean { + return false; + }, +}; diff --git a/packages/relay-runtime/store/__tests__/relayReaderTestExecTimeResolversTrueProvider.js b/packages/relay-runtime/store/__tests__/relayReaderTestExecTimeResolversTrueProvider.js new file mode 100644 index 0000000000000..ae80b363d857a --- /dev/null +++ b/packages/relay-runtime/store/__tests__/relayReaderTestExecTimeResolversTrueProvider.js @@ -0,0 +1,18 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + * @oncall relay + */ + +'use strict'; + +module.exports = { + get(): boolean { + return true; + }, +}; diff --git a/packages/relay-runtime/store/__tests__/resolvers/AstrologicalSignUtils.js b/packages/relay-runtime/store/__tests__/resolvers/AstrologicalSignUtils.js index cc440a9e7e7af..0ba068ad7b22a 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/AstrologicalSignUtils.js +++ b/packages/relay-runtime/store/__tests__/resolvers/AstrologicalSignUtils.js @@ -68,7 +68,7 @@ const HOUSE_ORDER = [ function findSign(month: number, day: number): AstrologicalSignID { const days = [21, 20, 21, 21, 22, 22, 23, 24, 24, 24, 23, 22]; - const signs = [ + const signs: AstrologicalSignID[] = [ 'Aquarius', 'Pisces', 'Aries', diff --git a/packages/relay-runtime/store/__tests__/resolvers/LiveCounterContextResolver.js b/packages/relay-runtime/store/__tests__/resolvers/LiveCounterContextResolver.js index 4730b15d34408..820559df92615 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/LiveCounterContextResolver.js +++ b/packages/relay-runtime/store/__tests__/resolvers/LiveCounterContextResolver.js @@ -44,6 +44,79 @@ function counter_context( }; } +/** + * @RelayResolver BaseCounter + * @weak + */ +export type BaseCounter = { + count: number, +}; + +/** + * @RelayResolver Query.base_counter_context: BaseCounter + * @live + * + * A Relay Resolver that returns an object implementing the External State + * Resolver interface. + */ +function base_counter_context( + _args: void, + context: TestResolverContextType, +): LiveState { + let value = 0; + + return { + read() { + return { + count: value, + }; + }, + subscribe(cb): () => void { + const subscription = context.counter.subscribe({ + next: v => { + value = v; + cb(); + }, + }); + + return () => subscription.unsubscribe(); + }, + }; +} + +/** + * @RelayResolver BaseCounter.count_plus_one: Int + * @live + * + * A Relay Resolver that returns an object implementing the External State + * Resolver interface. + */ +function count_plus_one( + _parent: mixed, + _args: void, + context: TestResolverContextType, +): LiveState { + let value = 0; + + return { + read() { + return value; + }, + subscribe(cb): () => void { + const subscription = context.counter.subscribe({ + next: v => { + value = v + 1; + cb(); + }, + }); + + return () => subscription.unsubscribe(); + }, + }; +} + module.exports = { counter_context, + base_counter_context, + count_plus_one, }; diff --git a/packages/relay-runtime/store/__tests__/resolvers/LiveResolvers-test.js b/packages/relay-runtime/store/__tests__/resolvers/LiveResolvers-test.js index 3c1c610efadaa..821fbf4f0d5b0 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/LiveResolvers-test.js +++ b/packages/relay-runtime/store/__tests__/resolvers/LiveResolvers-test.js @@ -256,7 +256,7 @@ test('Errors thrown during _initial_ read() are caught as resolver errors', () = }); const snapshot = environment.lookup(operation.fragment); - expect(snapshot.errorResponseFields).toEqual([ + expect(snapshot.fieldErrors).toEqual([ { kind: 'relay_resolver.error', error: Error('What?'), @@ -299,7 +299,7 @@ test('Errors thrown during read() _after update_ are caught as resolver errors', environment.subscribe(snapshot, handler); // Confirm there are no initial errors - expect(snapshot.errorResponseFields).toEqual(null); + expect(snapshot.fieldErrors).toEqual(null); const data: $FlowExpectedError = snapshot.data; expect(data.counter_throws_when_odd).toBe(0); @@ -310,7 +310,7 @@ test('Errors thrown during read() _after update_ are caught as resolver errors', const nextSnapshot = handler.mock.calls[0][0]; - expect(nextSnapshot.errorResponseFields).toEqual([ + expect(nextSnapshot.fieldErrors).toEqual([ { kind: 'relay_resolver.error', error: Error('What?'), @@ -331,7 +331,7 @@ test('Errors thrown during read() _after update_ are caught as resolver errors', const finalSnapshot = handler.mock.calls[0][0]; // Confirm there are no initial errors - expect(finalSnapshot.errorResponseFields).toEqual(null); + expect(finalSnapshot.fieldErrors).toEqual(null); const finalData: $FlowExpectedError = finalSnapshot.data; expect(finalData.counter_throws_when_odd).toBe(2); }); diff --git a/packages/relay-runtime/store/__tests__/resolvers/Resolver-test.js b/packages/relay-runtime/store/__tests__/resolvers/Resolver-test.js index 93aa49b1af3b1..dbcf654400ce1 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/Resolver-test.js +++ b/packages/relay-runtime/store/__tests__/resolvers/Resolver-test.js @@ -125,8 +125,8 @@ describe('Relay Resolver', () => { environment.commitPayload(operation, {}); - const {data, errorResponseFields} = environment.lookup(operation.fragment); - expect(errorResponseFields).toBe(null); + const {data, fieldErrors} = environment.lookup(operation.fragment); + expect(fieldErrors).toBe(null); // $FlowFixMe[incompatible-use] Lookup is untyped expect(data.hello_optional_world).toEqual('Hello, Default!'); diff --git a/packages/relay-runtime/store/__tests__/resolvers/ResolverGC-test.js b/packages/relay-runtime/store/__tests__/resolvers/ResolverGC-test.js index d77946a896124..5ea579b266efb 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/ResolverGC-test.js +++ b/packages/relay-runtime/store/__tests__/resolvers/ResolverGC-test.js @@ -10,6 +10,7 @@ */ 'use strict'; + import type {GraphQLResponse} from '../../../network/RelayNetworkTypes'; import type {ConcreteRequest} from '../../../util/RelayConcreteNode'; import type { @@ -40,6 +41,9 @@ const { } = require('relay-runtime/store/RelayModernOperationDescriptor'); const RelayModernStore = require('relay-runtime/store/RelayModernStore.js'); const RelayRecordSource = require('relay-runtime/store/RelayRecordSource'); +const { + RELAY_READ_TIME_RESOLVER_KEY_PREFIX, +} = require('relay-runtime/store/RelayStoreUtils'); const { disallowConsoleErrors, disallowWarnings, @@ -70,7 +74,7 @@ test('Live Resolver without fragment', async () => { expect(snapshot.data).toEqual({counter_no_fragment: 0}); expect(recordIdsInStore).toEqual([ 'client:root', - 'client:root:counter_no_fragment', + `client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}counter_no_fragment`, ]); }, afterRetainedGC: (snapshot, recordIdsInStore) => { @@ -78,7 +82,7 @@ test('Live Resolver without fragment', async () => { expect(snapshot.data).toEqual({counter_no_fragment: 0}); expect(recordIdsInStore).toEqual([ 'client:root', - 'client:root:counter_no_fragment', + `client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}counter_no_fragment`, ]); }, afterFreedGC: recordIdsInStore => { @@ -89,7 +93,7 @@ test('Live Resolver without fragment', async () => { expect(snapshot.data).toEqual({counter_no_fragment: 0}); expect(recordIdsInStore).toEqual([ 'client:root', - 'client:root:counter_no_fragment', + `client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}counter_no_fragment`, ]); }, }); @@ -114,7 +118,7 @@ test('Live Resolver _with_ root fragment', async () => { expect(recordIdsInStore).toEqual([ 'client:root', '1', - 'client:root:counter', + `client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}counter`, ]); }, afterRetainedGC: (snapshot, recordIdsInStore) => { @@ -123,7 +127,7 @@ test('Live Resolver _with_ root fragment', async () => { expect(recordIdsInStore).toEqual([ 'client:root', '1', - 'client:root:counter', + `client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}counter`, ]); }, afterFreedGC: recordIdsInStore => { @@ -133,7 +137,10 @@ test('Live Resolver _with_ root fragment', async () => { expect(counterResolver.callCount - initialCallCount).toBe(2); // Note that we _can't_ recreate the Resolver value because it's root fragment has been GGed. expect(snapshot.data).toEqual({counter: undefined}); - expect(recordIdsInStore).toEqual(['client:root', 'client:root:counter']); + expect(recordIdsInStore).toEqual([ + 'client:root', + `client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}counter`, + ]); }, }); }); @@ -155,8 +162,8 @@ test('Regular resolver with fragment reads live resovler with fragment', async ( expect(recordIdsInStore).toEqual([ 'client:root', '1', - 'client:root:counter', - 'client:root:counter_plus_one', + `client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}counter`, + `client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}counter_plus_one`, ]); }, afterRetainedGC: (snapshot, recordIdsInStore) => { @@ -164,8 +171,8 @@ test('Regular resolver with fragment reads live resovler with fragment', async ( expect(recordIdsInStore).toEqual([ 'client:root', '1', - 'client:root:counter', - 'client:root:counter_plus_one', + `client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}counter`, + `client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}counter_plus_one`, ]); }, afterFreedGC: recordIdsInStore => { @@ -173,7 +180,7 @@ test('Regular resolver with fragment reads live resovler with fragment', async ( }, afterLookupAfterFreedGC: (snapshot, recordIdsInStore) => { expect(snapshot.data).toEqual({counter_plus_one: null}); - expect(snapshot.errorResponseFields).toEqual([ + expect(snapshot.fieldErrors).toEqual([ { fieldPath: 'me.', kind: 'missing_expected_data.log', @@ -188,8 +195,8 @@ test('Regular resolver with fragment reads live resovler with fragment', async ( ]); expect(recordIdsInStore).toEqual([ 'client:root', - 'client:root:counter', - 'client:root:counter_plus_one', + `client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}counter`, + `client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}counter_plus_one`, ]); }, }); @@ -214,7 +221,7 @@ test('Non-live Resolver with fragment', async () => { expect(recordIdsInStore).toEqual([ 'client:root', '1', - 'client:1:greeting', + `client:1:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}greeting`, ]); }, afterRetainedGC: (snapshot, recordIdsInStore) => { @@ -222,7 +229,7 @@ test('Non-live Resolver with fragment', async () => { expect(recordIdsInStore).toEqual([ 'client:root', '1', - 'client:1:greeting', + `client:1:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}greeting`, ]); }, afterFreedGC: recordIdsInStore => { @@ -252,14 +259,14 @@ test('Non-live Resolver with no fragment and static arguments', async () => { expect(snapshot.data).toEqual({hello: 'Hello, Planet!'}); expect(recordIdsInStore).toEqual([ 'client:root', - 'client:root:hello(world:"Planet")', + `client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}hello(world:"Planet")`, ]); }, afterRetainedGC: (snapshot, recordIdsInStore) => { expect(snapshot.data).toEqual({hello: 'Hello, Planet!'}); expect(recordIdsInStore).toEqual([ 'client:root', - 'client:root:hello(world:"Planet")', + `client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}hello(world:"Planet")`, ]); }, afterFreedGC: recordIdsInStore => { @@ -269,7 +276,7 @@ test('Non-live Resolver with no fragment and static arguments', async () => { expect(snapshot.data).toEqual({hello: 'Hello, Planet!'}); expect(recordIdsInStore).toEqual([ 'client:root', - 'client:root:hello(world:"Planet")', + `client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}hello(world:"Planet")`, ]); }, }); @@ -291,14 +298,14 @@ test('Non-live Resolver with no fragment and dynamic arguments', async () => { expect(snapshot.data).toEqual({hello: 'Hello, Planet!'}); expect(recordIdsInStore).toEqual([ 'client:root', - 'client:root:hello(world:"Planet")', + `client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}hello(world:"Planet")`, ]); }, afterRetainedGC: (snapshot, recordIdsInStore) => { expect(snapshot.data).toEqual({hello: 'Hello, Planet!'}); expect(recordIdsInStore).toEqual([ 'client:root', - 'client:root:hello(world:"Planet")', + `client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}hello(world:"Planet")`, ]); }, afterFreedGC: recordIdsInStore => { @@ -309,7 +316,7 @@ test('Non-live Resolver with no fragment and dynamic arguments', async () => { expect(snapshot.data).toEqual({hello: 'Hello, Planet!'}); expect(recordIdsInStore).toEqual([ 'client:root', - 'client:root:hello(world:"Planet")', + `client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}hello(world:"Planet")`, ]); }, }); @@ -366,7 +373,7 @@ test('Resolver reading a client-edge to a server type', async () => { expect(recordIdsInStore).toEqual([ 'client:root', '1', - 'client:1:client_edge', + `client:1:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}client_edge`, '1337', '1234', ]); @@ -385,7 +392,7 @@ test('Resolver reading a client-edge to a server type', async () => { expect(recordIdsInStore).toEqual([ 'client:root', '1', - 'client:1:client_edge', + `client:1:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}client_edge`, '1337', '1234', ]); @@ -457,9 +464,9 @@ test('Resolver reading a client-edge to a server type (recursive)', async () => expect(recordIdsInStore).toEqual([ 'client:root', '1', - 'client:1:client_edge', + `client:1:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}client_edge`, '1337', - 'client:1337:another_client_edge', + `client:1337:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}another_client_edge`, '1338', ]); }, @@ -477,9 +484,9 @@ test('Resolver reading a client-edge to a server type (recursive)', async () => expect(recordIdsInStore).toEqual([ 'client:root', '1', - 'client:1:client_edge', + `client:1:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}client_edge`, '1337', - 'client:1337:another_client_edge', + `client:1337:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}another_client_edge`, '1338', ]); }, @@ -527,11 +534,11 @@ test('Resolver reading a client-edge to a client type', async () => { expect(recordIdsInStore).toEqual([ 'client:root', '1', - 'client:1:birthdate', - 'client:1:astrological_sign', - 'client:AstrologicalSign:Pisces', - 'client:AstrologicalSign:Pisces:self', - 'client:AstrologicalSign:Pisces:name', + `client:1:birthdate`, + `client:1:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}astrological_sign`, + `client:AstrologicalSign:Pisces`, + `client:AstrologicalSign:Pisces:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}self`, + `client:AstrologicalSign:Pisces:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}name`, ]); }, afterRetainedGC: (snapshot, recordIdsInStore) => { @@ -542,10 +549,10 @@ test('Resolver reading a client-edge to a client type', async () => { 'client:root', '1', 'client:1:birthdate', - 'client:1:astrological_sign', + `client:1:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}astrological_sign`, 'client:AstrologicalSign:Pisces', - 'client:AstrologicalSign:Pisces:self', - 'client:AstrologicalSign:Pisces:name', + `client:AstrologicalSign:Pisces:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}self`, + `client:AstrologicalSign:Pisces:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}name`, ]); }, @@ -593,11 +600,11 @@ test('Resolver reading a client-edge to a client type (resolver marked dirty)', expect(recordIdsInStore).toEqual([ 'client:root', '1', - 'client:1:birthdate', - 'client:1:astrological_sign', - 'client:AstrologicalSign:Pisces', - 'client:AstrologicalSign:Pisces:self', - 'client:AstrologicalSign:Pisces:name', + `client:1:birthdate`, + `client:1:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}astrological_sign`, + `client:AstrologicalSign:Pisces`, + `client:AstrologicalSign:Pisces:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}self`, + `client:AstrologicalSign:Pisces:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}name`, ]); /* Here we update the user to invalidate the astrological_sign resolver */ @@ -615,10 +622,10 @@ test('Resolver reading a client-edge to a client type (resolver marked dirty)', 'client:root', '1', 'client:1:birthdate', - 'client:1:astrological_sign', + `client:1:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}astrological_sign`, 'client:AstrologicalSign:Pisces', - 'client:AstrologicalSign:Pisces:self', - 'client:AstrologicalSign:Pisces:name', + `client:AstrologicalSign:Pisces:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}self`, + `client:AstrologicalSign:Pisces:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}name`, ]); }, @@ -658,7 +665,7 @@ test('Resolver reading a client-edge to a client type (suspended)', async () => expect(recordIdsInStore).toEqual([ 'client:root', '1', - 'client:root:virgo_suspends_when_counter_is_odd', + `client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}virgo_suspends_when_counter_is_odd`, // We don't have any of the Virgo records because they were not created. ]); }, @@ -667,7 +674,7 @@ test('Resolver reading a client-edge to a client type (suspended)', async () => expect(recordIdsInStore).toEqual([ 'client:root', '1', - 'client:root:virgo_suspends_when_counter_is_odd', + `client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}virgo_suspends_when_counter_is_odd`, // We don't have any of the Virgo records because they were not created. ]); }, @@ -704,7 +711,7 @@ test('Resolver reading a plural client-edge to a client type', async () => { expect(recordIdsInStore).toEqual([ 'client:root', '1', - 'client:root:all_astrological_signs', + `client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}all_astrological_signs`, ...HOUSE_ORDER.map(name => `client:AstrologicalSign:${name}`), ]); }, @@ -717,7 +724,7 @@ test('Resolver reading a plural client-edge to a client type', async () => { expect(recordIdsInStore).toEqual([ 'client:root', '1', - 'client:root:all_astrological_signs', + `client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}all_astrological_signs`, ...HOUSE_ORDER.map(name => `client:AstrologicalSign:${name}`), ]); }, @@ -728,7 +735,7 @@ test('Resolver reading a plural client-edge to a client type', async () => { afterLookupAfterFreedGC: (snapshot, recordIdsInStore) => { // Note that we _can't_ recreate the Resolver value because it's root fragment has been GGed. expect(snapshot.data).toEqual({all_astrological_signs: null}); - expect(snapshot.errorResponseFields).toEqual([ + expect(snapshot.fieldErrors).toEqual([ { fieldPath: 'me.', kind: 'missing_expected_data.log', @@ -737,7 +744,7 @@ test('Resolver reading a plural client-edge to a client type', async () => { ]); expect(recordIdsInStore).toEqual([ 'client:root', - 'client:root:all_astrological_signs', + `client:root:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}all_astrological_signs`, ]); }, }); @@ -779,15 +786,15 @@ test('Resolver reading a client-edge to a client type (recursive)', async () => expect(recordIdsInStore).toEqual([ 'client:root', '1', - 'client:1:birthdate', - 'client:1:astrological_sign', - 'client:AstrologicalSign:Pisces', - 'client:AstrologicalSign:Pisces:self', - 'client:AstrologicalSign:Pisces:name', - 'client:AstrologicalSign:Pisces:opposite', - 'client:AstrologicalSign:Virgo', - 'client:AstrologicalSign:Virgo:self', - 'client:AstrologicalSign:Virgo:name', + `client:1:birthdate`, + `client:1:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}astrological_sign`, + `client:AstrologicalSign:Pisces`, + `client:AstrologicalSign:Pisces:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}self`, + `client:AstrologicalSign:Pisces:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}name`, + `client:AstrologicalSign:Pisces:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}opposite`, + `client:AstrologicalSign:Virgo`, + `client:AstrologicalSign:Virgo:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}self`, + `client:AstrologicalSign:Virgo:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}name`, ]); }, afterRetainedGC: (snapshot, recordIdsInStore) => { @@ -799,14 +806,14 @@ test('Resolver reading a client-edge to a client type (recursive)', async () => 'client:root', '1', 'client:1:birthdate', - 'client:1:astrological_sign', + `client:1:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}astrological_sign`, 'client:AstrologicalSign:Pisces', - 'client:AstrologicalSign:Pisces:self', - 'client:AstrologicalSign:Pisces:name', - 'client:AstrologicalSign:Pisces:opposite', + `client:AstrologicalSign:Pisces:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}self`, + `client:AstrologicalSign:Pisces:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}name`, + `client:AstrologicalSign:Pisces:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}opposite`, 'client:AstrologicalSign:Virgo', - 'client:AstrologicalSign:Virgo:self', - 'client:AstrologicalSign:Virgo:name', + `client:AstrologicalSign:Virgo:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}self`, + `client:AstrologicalSign:Virgo:${RELAY_READ_TIME_RESOLVER_KEY_PREFIX}name`, ]); }, afterFreedGC: recordIdsInStore => { @@ -820,6 +827,85 @@ test('Resolver reading a client-edge to a client type (recursive)', async () => }); }); +test.each([0, 1, 5])( + 'Live Resolver cleanup when %i references retained', + async numRetainedReferences => { + const unsubscribeMock = jest.fn(); + const subscribeSpy = jest + .spyOn(GLOBAL_STORE, 'subscribe') + .mockImplementation(() => { + return unsubscribeMock; + }); + + // Reset the store before each test run + resetStore(); + + const source = RelayRecordSource.create(); + + const store = new RelayModernStore(source, { + gcReleaseBufferSize: 0, + }); + + const environment = new RelayModernEnvironment({ + network: RelayNetwork.create((request, variables) => { + return Promise.resolve({data: {}}); + }), + store, + }); + + // The operation that uses the live resolver + const operation = createOperationDescriptor( + graphql` + query ResolverGCTestNoRetainedQueriesQuery { + counter_no_fragment + } + `, + {}, + ); + + // Execute the query to populate the store + await environment.execute({operation}).toPromise(); + + // Lookup the data to trigger evaluation of the resolver + const snapshot = environment.lookup(operation.fragment); + + // Ensure the live resolver has been called + expect(subscribeSpy).toHaveBeenCalledTimes(1); + expect(snapshot.data).toEqual({counter_no_fragment: 0}); + + // Retain the operation if numRetainedReferences > 0 + const retains = []; + for (let i = 0; i < numRetainedReferences; i++) { + retains.push(environment.retain(operation)); + } + + // Run GC + store.__gc(); + + if (numRetainedReferences > 0) { + // The data is still retained, so cleanup should not have happened + expect(unsubscribeMock).not.toHaveBeenCalled(); + } else { + // The data is not retained, cleanup should have happened + expect(unsubscribeMock).toHaveBeenCalledTimes(1); + } + + // Dispose of the retains + for (const retain of retains) { + retain.dispose(); + } + + // Run GC again to ensure cleanup happens after disposing retains + store.__gc(); + + // Now, cleanup should have happened if it didn't before + expect(unsubscribeMock).toHaveBeenCalledTimes(1); + + // Cleanup the spy + subscribeSpy.mockRestore(); + }, +); + type TestProps = { query: ConcreteRequest, variables: VariablesOf, diff --git a/packages/relay-runtime/store/__tests__/resolvers/TodoModel.js b/packages/relay-runtime/store/__tests__/resolvers/TodoModel.js index d022ad573c3bb..ee454ec18f3bd 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/TodoModel.js +++ b/packages/relay-runtime/store/__tests__/resolvers/TodoModel.js @@ -11,6 +11,7 @@ 'use strict'; +import type {TestResolverContextType} from '../../../mutations/__tests__/TestResolverContextType'; import type {TodoModelCapitalizedID$key} from './__generated__/TodoModelCapitalizedID.graphql'; import type {TodoModelCapitalizedIDLegacy$key} from './__generated__/TodoModelCapitalizedIDLegacy.graphql'; import type {TodoDescription} from './TodoDescription'; @@ -50,6 +51,17 @@ function description(model: TodoModelType): ?string { return model?.description; } +/** + * @RelayResolver TodoModel.another_value_from_context: String + */ +function another_value_from_context( + model: TodoModelType, + _: mixed, + context: TestResolverContextType, +): ?string { + return context?.greeting.myHello; +} + /** * @RelayResolver TodoModel.capitalized_id: String * @rootFragment TodoModelCapitalizedID @@ -180,6 +192,7 @@ module.exports = { todo_model_null, TodoModel, description, + another_value_from_context, fancy_description, fancy_description_null, fancy_description_suspends, diff --git a/packages/relay-runtime/store/__tests__/resolvers/UserAgeResolvers.js b/packages/relay-runtime/store/__tests__/resolvers/UserAgeResolvers.js new file mode 100644 index 0000000000000..745b23aa7608d --- /dev/null +++ b/packages/relay-runtime/store/__tests__/resolvers/UserAgeResolvers.js @@ -0,0 +1,17 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +/** + * @RelayResolver User.age: Int + */ +function age(_: mixed, context: { age: number }): number { + return context.age; +} + +module.exports = { + age, +}; diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/AstrologicalSignHouseResolver.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/AstrologicalSignHouseResolver.graphql.js index d5c127e28fc5d..c172e00209a79 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/AstrologicalSignHouseResolver.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/AstrologicalSignHouseResolver.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<31c0cc7d99ab6224a2913622408c661f>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -57,7 +57,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "self", - "resolverModule": require('./../AstrologicalSignSelfResolver').self, + "resolverModule": require('../AstrologicalSignSelfResolver').self, "path": "self" } ], diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/AstrologicalSignNameResolver.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/AstrologicalSignNameResolver.graphql.js index 0306e262cf869..b2ecd61e763de 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/AstrologicalSignNameResolver.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/AstrologicalSignNameResolver.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<654459e353102459ce6b54f39e099d4a>> + * @generated SignedSource<<4fadd9f926486d3b17d9d8c0b0131f26>> * @flow * @lightSyntaxTransform * @nogrep @@ -57,7 +57,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "self", - "resolverModule": require('./../AstrologicalSignSelfResolver').self, + "resolverModule": require('../AstrologicalSignSelfResolver').self, "path": "self" } ], diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/AstrologicalSignOppositeResolver.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/AstrologicalSignOppositeResolver.graphql.js index 9ee3f7c49de04..1f27bee7005be 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/AstrologicalSignOppositeResolver.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/AstrologicalSignOppositeResolver.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<54efd952607f53a3eb31f42a75d53b4d>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -57,7 +57,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "self", - "resolverModule": require('./../AstrologicalSignSelfResolver').self, + "resolverModule": require('../AstrologicalSignSelfResolver').self, "path": "self" } ], diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/BaseCounter____relay_model_instance.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/BaseCounter____relay_model_instance.graphql.js new file mode 100644 index 0000000000000..cc8482d44955e --- /dev/null +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/BaseCounter____relay_model_instance.graphql.js @@ -0,0 +1,61 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<7a18458e8e74a97968fd3346963dee05>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { Fragment, ReaderFragment } from 'relay-runtime'; +import type { BaseCounter } from "../LiveCounterContextResolver.js"; +import type { FragmentType } from "relay-runtime"; +declare export opaque type BaseCounter____relay_model_instance$fragmentType: FragmentType; +export type BaseCounter____relay_model_instance$data = {| + +__relay_model_instance: BaseCounter, + +$fragmentType: BaseCounter____relay_model_instance$fragmentType, +|}; +export type BaseCounter____relay_model_instance$key = { + +$data?: BaseCounter____relay_model_instance$data, + +$fragmentSpreads: BaseCounter____relay_model_instance$fragmentType, + ... +}; +*/ + +var node/*: ReaderFragment*/ = { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "BaseCounter____relay_model_instance", + "selections": [ + { + "kind": "ClientExtension", + "selections": [ + { + "alias": null, + "args": null, + "kind": "ScalarField", + "name": "__relay_model_instance", + "storageKey": null + } + ] + } + ], + "type": "BaseCounter", + "abstractKey": null +}; + +module.exports = ((node/*: any*/)/*: Fragment< + BaseCounter____relay_model_instance$fragmentType, + BaseCounter____relay_model_instance$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/Cat____relay_model_instance.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/Cat____relay_model_instance.graphql.js index 0b466c9a9a1f9..86f1d48b13ab4 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/Cat____relay_model_instance.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/Cat____relay_model_instance.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<98df57edbbcd1fc2e7c6e6e10f01867b>> * @flow * @lightSyntaxTransform * @nogrep @@ -57,7 +57,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "__relay_model_instance", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./Cat__id.graphql'), require('./../CatResolvers').Cat, 'id', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./Cat__id.graphql'), require('../CatResolvers').Cat, 'id', true), "path": "__relay_model_instance" } ], diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/ClientUser____relay_model_instance.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/ClientUser____relay_model_instance.graphql.js index 565af05c73873..eb912310b71d7 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/ClientUser____relay_model_instance.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/ClientUser____relay_model_instance.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<59f822d5e484816fa51c4a8f271a2667>> + * @generated SignedSource<<998f0e30a31dea1cee6c27f9fdee2997>> * @flow * @lightSyntaxTransform * @nogrep @@ -57,7 +57,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "__relay_model_instance", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./ClientUser__id.graphql'), require('./../Client3DClientUserResolvers').ClientUser, 'id', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./ClientUser__id.graphql'), require('../Client3DClientUserResolvers').ClientUser, 'id', true), "path": "__relay_model_instance" } ], diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/CounterPlusOneResolver.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/CounterPlusOneResolver.graphql.js index b1ea5644a1868..ee5b309dd2fab 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/CounterPlusOneResolver.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/CounterPlusOneResolver.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<0841992868c0ef9b9b3488eac7b92ccd>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -59,7 +59,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayLiveResolver", "name": "counter", - "resolverModule": require('./../LiveCounterResolver').counter, + "resolverModule": require('../LiveCounterResolver').counter, "path": "counter" }, "action": "THROW" diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/Fish____relay_model_instance.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/Fish____relay_model_instance.graphql.js index 3d18c3c5edb57..f75b40a1b06b5 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/Fish____relay_model_instance.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/Fish____relay_model_instance.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<25c3c2089e62abda4b66d6be0947e878>> + * @generated SignedSource<<881df56cf9fd645fc89ef6629b8c9180>> * @flow * @lightSyntaxTransform * @nogrep @@ -57,7 +57,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "__relay_model_instance", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./Fish__id.graphql'), require('./../FishResolvers').Fish, 'id', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./Fish__id.graphql'), require('../FishResolvers').Fish, 'id', true), "path": "__relay_model_instance" } ], diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/HelloWorldResolverWithProvidedVariable.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/HelloWorldResolverWithProvidedVariable.graphql.js index 9e1481a93b9c5..27f296b6fd76d 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/HelloWorldResolverWithProvidedVariable.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/HelloWorldResolverWithProvidedVariable.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<4f3705ae49dac8dc45ab3d386293a4ae>> * @flow * @lightSyntaxTransform * @nogrep @@ -67,7 +67,7 @@ var node/*: ReaderFragment*/ = { "fragment": null, "kind": "RelayResolver", "name": "hello", - "resolverModule": require('./../HelloWorldResolver').hello, + "resolverModule": require('../HelloWorldResolver').hello, "path": "hello" } ] diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/LiveExternalGreetingFragment.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/LiveExternalGreetingFragment.graphql.js index f7923c1385158..9682c1fe49023 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/LiveExternalGreetingFragment.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/LiveExternalGreetingFragment.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<52969e8d13280a8efa0446b45423d7ee>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -60,7 +60,7 @@ var node/*: ReaderFragment*/ = { "fragment": null, "kind": "RelayLiveResolver", "name": "live_user_suspends_when_odd", - "resolverModule": require('./../LiveUserSuspendsWhenOdd').live_user_suspends_when_odd, + "resolverModule": require('../LiveUserSuspendsWhenOdd').live_user_suspends_when_odd, "path": "user" }, "linkedField": { diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/LiveResolversTestBatchingQuery.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/LiveResolversTestBatchingQuery.graphql.js index 55304a946c047..e18ea3a9b21f1 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/LiveResolversTestBatchingQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/LiveResolversTestBatchingQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<124f32101834ca1f9f1d61368fc90268>> + * @generated SignedSource<<63e044290b65a580283f699c8952f0fb>> * @flow * @lightSyntaxTransform * @nogrep @@ -71,7 +71,7 @@ return { "fragment": null, "kind": "RelayLiveResolver", "name": "counter_no_fragment", - "resolverModule": require('./../LiveCounterNoFragment').counter_no_fragment, + "resolverModule": require('../LiveCounterNoFragment').counter_no_fragment, "path": "counter_no_fragment" }, { @@ -80,7 +80,7 @@ return { "fragment": null, "kind": "RelayLiveResolver", "name": "counter_no_fragment_with_arg", - "resolverModule": require('./../LiveCounterNoFragmentWithArg').counter_no_fragment_with_arg, + "resolverModule": require('../LiveCounterNoFragmentWithArg').counter_no_fragment_with_arg, "path": "counter_no_fragment_with_arg" } ] diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/LiveResolversTestHandlesErrorOnReadQuery.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/LiveResolversTestHandlesErrorOnReadQuery.graphql.js index c8614aa4e69d0..a927642d7eb89 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/LiveResolversTestHandlesErrorOnReadQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/LiveResolversTestHandlesErrorOnReadQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<95765d7cb8801f24c37507d759200aea>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -53,7 +53,7 @@ var node/*: ClientRequest*/ = { "fragment": null, "kind": "RelayLiveResolver", "name": "counter_throws_when_odd", - "resolverModule": require('./../QueryLiveResolverThrowsOnRead').counter_throws_when_odd, + "resolverModule": require('../QueryLiveResolverThrowsOnRead').counter_throws_when_odd, "path": "counter_throws_when_odd" } ] diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/LiveResolversTestHandlesErrorOnUpdateQuery.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/LiveResolversTestHandlesErrorOnUpdateQuery.graphql.js index ae1d527174e31..6653211c05e74 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/LiveResolversTestHandlesErrorOnUpdateQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/LiveResolversTestHandlesErrorOnUpdateQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<7f9b7a0be2a23656e341ed8f7229f1b5>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -53,7 +53,7 @@ var node/*: ClientRequest*/ = { "fragment": null, "kind": "RelayLiveResolver", "name": "counter_throws_when_odd", - "resolverModule": require('./../QueryLiveResolverThrowsOnRead').counter_throws_when_odd, + "resolverModule": require('../QueryLiveResolverThrowsOnRead').counter_throws_when_odd, "path": "counter_throws_when_odd" } ] diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/LiveResolversTestOptimisticUpdatesQuery.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/LiveResolversTestOptimisticUpdatesQuery.graphql.js index 6c15dd5338fb5..41a226ed35305 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/LiveResolversTestOptimisticUpdatesQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/LiveResolversTestOptimisticUpdatesQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<0b9f1e1f6018bce3e3ea6ecc24f148a1>> + * @generated SignedSource<<99535ee92c29f2b0029d1a1c2db47ea4>> * @flow * @lightSyntaxTransform * @nogrep @@ -65,7 +65,7 @@ var node/*: ConcreteRequest*/ = { }, "kind": "RelayResolver", "name": "greeting", - "resolverModule": require('./../UserGreetingResolver').greeting, + "resolverModule": require('../UserGreetingResolver').greeting, "path": "me.greeting" } ], diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/LiveResolversTestUnsubscribesWhenSuspendsQuery.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/LiveResolversTestUnsubscribesWhenSuspendsQuery.graphql.js index 7985baf0415a3..3b79230a3efc0 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/LiveResolversTestUnsubscribesWhenSuspendsQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/LiveResolversTestUnsubscribesWhenSuspendsQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<9fa3d676b0a5167cb3ce46e406259bd3>> + * @generated SignedSource<<5069bedfe0dd09be51e14237d383a2aa>> * @flow * @lightSyntaxTransform * @nogrep @@ -78,7 +78,7 @@ return { "fragment": null, "kind": "RelayLiveResolver", "name": "live_user_suspends_when_odd", - "resolverModule": require('./../LiveUserSuspendsWhenOdd').live_user_suspends_when_odd, + "resolverModule": require('../LiveUserSuspendsWhenOdd').live_user_suspends_when_odd, "path": "user" }, "linkedField": { @@ -110,7 +110,7 @@ return { }, "kind": "RelayLiveResolver", "name": "live_external_greeting", - "resolverModule": require('./../LiveExternalGreeting').live_external_greeting, + "resolverModule": require('../LiveExternalGreeting').live_external_greeting, "path": "greeting" } ], diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/OuterResolver.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/OuterResolver.graphql.js index e3eeae846ad85..80a15a28bbcd7 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/OuterResolver.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/OuterResolver.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<2435268cc8e896f52a9e951d3fc5b95f>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -57,7 +57,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayLiveResolver", "name": "inner", - "resolverModule": require('./../InnerResolver').inner, + "resolverModule": require('../InnerResolver').inner, "path": "inner" } ], diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/RefetchableClientEdgeQuery_ResolverGCTestResolverClientEdgeToServerRecursiveQuery_me__client_edge.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/RefetchableClientEdgeQuery_ResolverGCTestResolverClientEdgeToServerRecursiveQuery_me__client_edge.graphql.js index 6589926c7dcee..2dd6aeab9d439 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/RefetchableClientEdgeQuery_ResolverGCTestResolverClientEdgeToServerRecursiveQuery_me__client_edge.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/RefetchableClientEdgeQuery_ResolverGCTestResolverClientEdgeToServerRecursiveQuery_me__client_edge.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -98,7 +98,7 @@ return { }, "kind": "RelayResolver", "name": "another_client_edge", - "resolverModule": require('./../UserAnotherClientEdgeResolver').another_client_edge, + "resolverModule": require('../UserAnotherClientEdgeResolver').another_client_edge, "path": "another_client_edge" }, "linkedField": { diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestLiveWithRootFragmentQuery.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestLiveWithRootFragmentQuery.graphql.js index 39c9edf9ce19f..e5f4089d917e0 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestLiveWithRootFragmentQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestLiveWithRootFragmentQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<82fd959394b822fe29ac1003c8c6587d>> + * @generated SignedSource<<2e4f7bb7765b2aa5412110074d4299b6>> * @flow * @lightSyntaxTransform * @nogrep @@ -56,7 +56,7 @@ var node/*: ConcreteRequest*/ = { }, "kind": "RelayLiveResolver", "name": "counter", - "resolverModule": require('./../LiveCounterResolver').counter, + "resolverModule": require('../LiveCounterResolver').counter, "path": "counter" } ], diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestNoFragmentDynamicArgsQuery.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestNoFragmentDynamicArgsQuery.graphql.js index dbacc295e1c98..c4e7d60398ea8 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestNoFragmentDynamicArgsQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestNoFragmentDynamicArgsQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<65407fd3e9a25762831384ff01c6d07a>> * @flow * @lightSyntaxTransform * @nogrep @@ -71,7 +71,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "hello", - "resolverModule": require('./../HelloWorldResolver').hello, + "resolverModule": require('../HelloWorldResolver').hello, "path": "hello" } ] diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestNoFragmentStaticArgsQuery.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestNoFragmentStaticArgsQuery.graphql.js index 5cc83293a1cc3..e21cd22bac0a0 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestNoFragmentStaticArgsQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestNoFragmentStaticArgsQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<9d6873325f05214bcecfbd4ca83c858f>> * @flow * @lightSyntaxTransform * @nogrep @@ -62,7 +62,7 @@ return { "fragment": null, "kind": "RelayResolver", "name": "hello", - "resolverModule": require('./../HelloWorldResolver').hello, + "resolverModule": require('../HelloWorldResolver').hello, "path": "hello" } ] diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestNoRetainedQueriesQuery.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestNoRetainedQueriesQuery.graphql.js new file mode 100644 index 0000000000000..2f364a7749f83 --- /dev/null +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestNoRetainedQueriesQuery.graphql.js @@ -0,0 +1,103 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @oncall relay + * + * @generated SignedSource<<99f5ad9271fda6ef2e2726413f476a62>> + * @flow + * @lightSyntaxTransform + * @nogrep + */ + +/* eslint-disable */ + +'use strict'; + +/*:: +import type { ClientRequest, ClientQuery } from 'relay-runtime'; +import type { LiveState } from "relay-runtime"; +import {counter_no_fragment as queryCounterNoFragmentResolverType} from "../LiveCounterNoFragment.js"; +import type { TestResolverContextType } from "../../../../mutations/__tests__/TestResolverContextType"; +// Type assertion validating that `queryCounterNoFragmentResolverType` resolver is correctly implemented. +// A type error here indicates that the type signature of the resolver module is incorrect. +(queryCounterNoFragmentResolverType: ( + args: void, + context: TestResolverContextType, +) => LiveState); +export type ResolverGCTestNoRetainedQueriesQuery$variables = {||}; +export type ResolverGCTestNoRetainedQueriesQuery$data = {| + +counter_no_fragment: ?number, +|}; +export type ResolverGCTestNoRetainedQueriesQuery = {| + response: ResolverGCTestNoRetainedQueriesQuery$data, + variables: ResolverGCTestNoRetainedQueriesQuery$variables, +|}; +*/ + +var node/*: ClientRequest*/ = { + "fragment": { + "argumentDefinitions": [], + "kind": "Fragment", + "metadata": null, + "name": "ResolverGCTestNoRetainedQueriesQuery", + "selections": [ + { + "kind": "ClientExtension", + "selections": [ + { + "alias": null, + "args": null, + "fragment": null, + "kind": "RelayLiveResolver", + "name": "counter_no_fragment", + "resolverModule": require('../LiveCounterNoFragment').counter_no_fragment, + "path": "counter_no_fragment" + } + ] + } + ], + "type": "Query", + "abstractKey": null + }, + "kind": "Request", + "operation": { + "argumentDefinitions": [], + "kind": "Operation", + "name": "ResolverGCTestNoRetainedQueriesQuery", + "selections": [ + { + "kind": "ClientExtension", + "selections": [ + { + "name": "counter_no_fragment", + "args": null, + "fragment": null, + "kind": "RelayResolver", + "storageKey": null, + "isOutputType": true + } + ] + } + ] + }, + "params": { + "cacheID": "a5905620d7505500bf0cb987c540bc24", + "id": null, + "metadata": {}, + "name": "ResolverGCTestNoRetainedQueriesQuery", + "operationKind": "query", + "text": null + } +}; + +if (__DEV__) { + (node/*: any*/).hash = "c081f9d7220711c53528ae28f136ca80"; +} + +module.exports = ((node/*: any*/)/*: ClientQuery< + ResolverGCTestNoRetainedQueriesQuery$variables, + ResolverGCTestNoRetainedQueriesQuery$data, +>*/); diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestNonLiveWithFragmentQuery.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestNonLiveWithFragmentQuery.graphql.js index 664e367f311d8..1dbd6cfde4b01 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestNonLiveWithFragmentQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestNonLiveWithFragmentQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<0ffbc672af1e238c985fdbbc2c1aae15>> + * @generated SignedSource<<254582f28198d4bbe66d50aa37920232>> * @flow * @lightSyntaxTransform * @nogrep @@ -65,7 +65,7 @@ var node/*: ConcreteRequest*/ = { }, "kind": "RelayResolver", "name": "greeting", - "resolverModule": require('./../UserGreetingResolver').greeting, + "resolverModule": require('../UserGreetingResolver').greeting, "path": "me.greeting" } ], diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestRegularReadsLiveQuery.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestRegularReadsLiveQuery.graphql.js index 7f193b732bdcb..4f1e2f32e84f3 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestRegularReadsLiveQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestRegularReadsLiveQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<6f8710960ff2e2281f4143e91ee93f66>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -55,7 +55,7 @@ var node/*: ConcreteRequest*/ = { }, "kind": "RelayResolver", "name": "counter_plus_one", - "resolverModule": require('./../CounterPlusOneResolver').counter_plus_one, + "resolverModule": require('../CounterPlusOneResolver').counter_plus_one, "path": "counter_plus_one" } ], diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestResolverClientEdgeToClientDirtyQuery.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestResolverClientEdgeToClientDirtyQuery.graphql.js index 0f95e49254bef..ede973562552d 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestResolverClientEdgeToClientDirtyQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestResolverClientEdgeToClientDirtyQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<9f64372a6da65518e08738d9995a0961>> * @flow * @lightSyntaxTransform * @nogrep @@ -93,7 +93,7 @@ return { }, "kind": "RelayResolver", "name": "astrological_sign", - "resolverModule": require('./../UserAstrologicalSignResolver').astrological_sign, + "resolverModule": require('../UserAstrologicalSignResolver').astrological_sign, "path": "me.astrological_sign" }, "linkedField": { @@ -114,7 +114,7 @@ return { }, "kind": "RelayResolver", "name": "name", - "resolverModule": require('./../AstrologicalSignNameResolver').name, + "resolverModule": require('../AstrologicalSignNameResolver').name, "path": "me.astrological_sign.name" } ], diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestResolverClientEdgeToClientQuery.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestResolverClientEdgeToClientQuery.graphql.js index 75902519eb964..9310d83e044f2 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestResolverClientEdgeToClientQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestResolverClientEdgeToClientQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -93,7 +93,7 @@ return { }, "kind": "RelayResolver", "name": "astrological_sign", - "resolverModule": require('./../UserAstrologicalSignResolver').astrological_sign, + "resolverModule": require('../UserAstrologicalSignResolver').astrological_sign, "path": "me.astrological_sign" }, "linkedField": { @@ -114,7 +114,7 @@ return { }, "kind": "RelayResolver", "name": "name", - "resolverModule": require('./../AstrologicalSignNameResolver').name, + "resolverModule": require('../AstrologicalSignNameResolver').name, "path": "me.astrological_sign.name" } ], diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestResolverClientEdgeToClientRecursiveQuery.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestResolverClientEdgeToClientRecursiveQuery.graphql.js index 80461dacd80da..212a8d1e2482e 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestResolverClientEdgeToClientRecursiveQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestResolverClientEdgeToClientRecursiveQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -142,7 +142,7 @@ return { }, "kind": "RelayResolver", "name": "astrological_sign", - "resolverModule": require('./../UserAstrologicalSignResolver').astrological_sign, + "resolverModule": require('../UserAstrologicalSignResolver').astrological_sign, "path": "me.astrological_sign" }, "linkedField": { @@ -159,7 +159,7 @@ return { "fragment": (v0/*: any*/), "kind": "RelayResolver", "name": "name", - "resolverModule": require('./../AstrologicalSignNameResolver').name, + "resolverModule": require('../AstrologicalSignNameResolver').name, "path": "me.astrological_sign.name" }, { @@ -176,7 +176,7 @@ return { }, "kind": "RelayResolver", "name": "opposite", - "resolverModule": require('./../AstrologicalSignOppositeResolver').opposite, + "resolverModule": require('../AstrologicalSignOppositeResolver').opposite, "path": "me.astrological_sign.opposite" }, "linkedField": { @@ -193,7 +193,7 @@ return { "fragment": (v0/*: any*/), "kind": "RelayResolver", "name": "name", - "resolverModule": require('./../AstrologicalSignNameResolver').name, + "resolverModule": require('../AstrologicalSignNameResolver').name, "path": "me.astrological_sign.opposite.name" } ], diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestResolverClientEdgeToClientSuspendedQuery.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestResolverClientEdgeToClientSuspendedQuery.graphql.js index 8b7009f594f80..5efc7ea28fe2e 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestResolverClientEdgeToClientSuspendedQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestResolverClientEdgeToClientSuspendedQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<34a8e8cbb6424b8147b062cd86c47e21>> + * @generated SignedSource<<721f227a2dacdaad832c941fa734ffd7>> * @flow * @lightSyntaxTransform * @nogrep @@ -87,7 +87,7 @@ return { "fragment": null, "kind": "RelayLiveResolver", "name": "virgo_suspends_when_counter_is_odd", - "resolverModule": require('./../QueryVirgoLiveSuspendsWhenOddResolver').virgo_suspends_when_counter_is_odd, + "resolverModule": require('../QueryVirgoLiveSuspendsWhenOddResolver').virgo_suspends_when_counter_is_odd, "path": "virgo_suspends_when_counter_is_odd" }, "linkedField": { @@ -108,7 +108,7 @@ return { }, "kind": "RelayResolver", "name": "name", - "resolverModule": require('./../AstrologicalSignNameResolver').name, + "resolverModule": require('../AstrologicalSignNameResolver').name, "path": "virgo_suspends_when_counter_is_odd.name" } ], diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestResolverClientEdgeToPluralClientQuery.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestResolverClientEdgeToPluralClientQuery.graphql.js index 730b939c140ac..b232cc012e551 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestResolverClientEdgeToPluralClientQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestResolverClientEdgeToPluralClientQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<8449eea2ea4af32a0baf88ba1ea33cf8>> * @flow * @lightSyntaxTransform * @nogrep @@ -81,7 +81,7 @@ return { }, "kind": "RelayResolver", "name": "all_astrological_signs", - "resolverModule": require('./../QueryAllAstrologicalSignsResolver').all_astrological_signs, + "resolverModule": require('../QueryAllAstrologicalSignsResolver').all_astrological_signs, "path": "all_astrological_signs" }, "linkedField": { diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestResolverClientEdgeToServerQuery.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestResolverClientEdgeToServerQuery.graphql.js index 5b0385d26354f..37646a33eaa6f 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestResolverClientEdgeToServerQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestResolverClientEdgeToServerQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<7c4e9a5496de08cdafa104c7aca75950>> * @flow * @lightSyntaxTransform * @nogrep @@ -97,7 +97,7 @@ return { }, "kind": "RelayResolver", "name": "client_edge", - "resolverModule": require('./../UserClientEdgeResolver').client_edge, + "resolverModule": require('../UserClientEdgeResolver').client_edge, "path": "me.client_edge" }, "linkedField": { diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestResolverClientEdgeToServerRecursiveQuery.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestResolverClientEdgeToServerRecursiveQuery.graphql.js index df9c7fd9641f9..4aaa1fdfd1067 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestResolverClientEdgeToServerRecursiveQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestResolverClientEdgeToServerRecursiveQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<0f7b5fe04971b2c5d2633bdc0f25f6bb>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -108,7 +108,7 @@ return { }, "kind": "RelayResolver", "name": "client_edge", - "resolverModule": require('./../UserClientEdgeResolver').client_edge, + "resolverModule": require('../UserClientEdgeResolver').client_edge, "path": "me.client_edge" }, "linkedField": { @@ -134,7 +134,7 @@ return { }, "kind": "RelayResolver", "name": "another_client_edge", - "resolverModule": require('./../UserAnotherClientEdgeResolver').another_client_edge, + "resolverModule": require('../UserAnotherClientEdgeResolver').another_client_edge, "path": "me.client_edge.another_client_edge" }, "linkedField": { diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestWithoutFragmentQuery.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestWithoutFragmentQuery.graphql.js index b590abebe4aac..bc8c804b0919b 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestWithoutFragmentQuery.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverGCTestWithoutFragmentQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<26811535a27d0c22a86eefa867c29039>> + * @generated SignedSource<<8f4a4e255a6a0611ccc1a4c1a0282d8a>> * @flow * @lightSyntaxTransform * @nogrep @@ -53,7 +53,7 @@ var node/*: ClientRequest*/ = { "fragment": null, "kind": "RelayLiveResolver", "name": "counter_no_fragment", - "resolverModule": require('./../LiveCounterNoFragment').counter_no_fragment, + "resolverModule": require('../LiveCounterNoFragment').counter_no_fragment, "path": "counter_no_fragment" } ] diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverTest1Query.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverTest1Query.graphql.js index cdc6508e42f8e..6a3f4c401dbde 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverTest1Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverTest1Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<42cd4a5f26fcd1aa24617e626f0eabb5>> * @flow * @lightSyntaxTransform * @nogrep @@ -65,7 +65,7 @@ var node/*: ConcreteRequest*/ = { }, "kind": "RelayResolver", "name": "greeting", - "resolverModule": require('./../UserGreetingResolver').greeting, + "resolverModule": require('../UserGreetingResolver').greeting, "path": "me.greeting" } ], diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverTest2Fragment.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverTest2Fragment.graphql.js index e99cb02c87d94..63a5506a9118b 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverTest2Fragment.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverTest2Fragment.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<4d664bb0bbe193af9fdbd0bf4a505348>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -71,7 +71,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "greeting", - "resolverModule": require('./../UserGreetingResolver').greeting, + "resolverModule": require('../UserGreetingResolver').greeting, "path": "greeting" }, { diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverTest3Query.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverTest3Query.graphql.js index bbe2b86976b4a..0bbfd19ba6140 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverTest3Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverTest3Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<28417c47408d6c7804396960e9b53492>> + * @generated SignedSource<<6ab72fa477b974eaa74862e0957d7894>> * @flow * @lightSyntaxTransform * @nogrep @@ -75,7 +75,7 @@ var node/*: ConcreteRequest*/ = { }, "kind": "RelayResolver", "name": "client_edge", - "resolverModule": require('./../UserClientEdgeResolver').client_edge, + "resolverModule": require('../UserClientEdgeResolver').client_edge, "path": "me.client_edge" }, "linkedField": { diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverTest4Query.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverTest4Query.graphql.js index 2ecd083560324..f8206408caa17 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverTest4Query.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/ResolverTest4Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<9b0f8e436138c2f095ab39aa19525b77>> + * @generated SignedSource<<482e1bf9f224d1e562feb8d57dfd9158>> * @flow * @lightSyntaxTransform * @nogrep @@ -54,7 +54,7 @@ var node/*: ClientRequest*/ = { "fragment": null, "kind": "RelayResolver", "name": "hello_optional_world", - "resolverModule": require('./../HelloWorldOptionalResolver').hello_optional_world, + "resolverModule": require('../HelloWorldOptionalResolver').hello_optional_world, "path": "hello_optional_world" } ] diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/SpecialUser____relay_model_instance.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/SpecialUser____relay_model_instance.graphql.js index e68bf647a058e..43f74703f8516 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/SpecialUser____relay_model_instance.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/SpecialUser____relay_model_instance.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<29854a2bd721fdfae953526de2bbd232>> + * @generated SignedSource<<63ac02b54ca04df2b445774e1c967950>> * @flow * @lightSyntaxTransform * @nogrep @@ -57,7 +57,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "__relay_model_instance", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./SpecialUser__id.graphql'), require('./../Client3DSpecialUserResolvers').SpecialUser, 'id', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./SpecialUser__id.graphql'), require('../Client3DSpecialUserResolvers').SpecialUser, 'id', true), "path": "__relay_model_instance" } ], diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/TodoBlockedByResolverFragment.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/TodoBlockedByResolverFragment.graphql.js index 4845b838e35e1..5bf278e221329 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/TodoBlockedByResolverFragment.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/TodoBlockedByResolverFragment.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<934cf011eec7768dfb7a4f6c7987bd5b>> + * @generated SignedSource<<4fcb3d1cccf346f111ef051dc63904d5>> * @flow * @lightSyntaxTransform * @nogrep @@ -57,7 +57,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayLiveResolver", "name": "self", - "resolverModule": require('./../TodoSelfResolver').self, + "resolverModule": require('../TodoSelfResolver').self, "path": "self" } ], diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/TodoCompleteResolverFragment.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/TodoCompleteResolverFragment.graphql.js index f7f6f39dfd9c6..990a4d3c73ad9 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/TodoCompleteResolverFragment.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/TodoCompleteResolverFragment.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<4cefb5ab340dde75594ef4026ba9842e>> + * @generated SignedSource<<15e2562dfebf2861c59e06444940854d>> * @flow * @lightSyntaxTransform * @nogrep @@ -57,7 +57,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayLiveResolver", "name": "self", - "resolverModule": require('./../TodoSelfResolver').self, + "resolverModule": require('../TodoSelfResolver').self, "path": "self" } ], diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/TodoDescription_text_style.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/TodoDescription_text_style.graphql.js index 68c3726d37c5a..e0102228e7595 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/TodoDescription_text_style.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/TodoDescription_text_style.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<05d1f2c5d37810b412a5fda333e95500>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -59,7 +59,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "color", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./TodoDescription____relay_model_instance.graphql'), require('./../TodoDescription').color, '__relay_model_instance', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./TodoDescription____relay_model_instance.graphql'), require('../TodoDescription').color, '__relay_model_instance', true), "path": "color" }, "action": "THROW" diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/TodoModel____relay_model_instance.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/TodoModel____relay_model_instance.graphql.js index 32545610060e2..22ff7da9a13df 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/TodoModel____relay_model_instance.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/TodoModel____relay_model_instance.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<04a4d792231248214f4c73842911dcbc>> + * @generated SignedSource<<2cb83012c5cb177ed4233336f7dc2528>> * @flow * @lightSyntaxTransform * @nogrep @@ -57,7 +57,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayLiveResolver", "name": "__relay_model_instance", - "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./TodoModel__id.graphql'), require('./../TodoModel').TodoModel, 'id', true), + "resolverModule": require('relay-runtime/experimental').resolverDataInjector(require('./TodoModel__id.graphql'), require('../TodoModel').TodoModel, 'id', true), "path": "__relay_model_instance" } ], diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/TodoTextResolverFragment.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/TodoTextResolverFragment.graphql.js index f945f0f4bcc77..3980da27a942d 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/TodoTextResolverFragment.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/TodoTextResolverFragment.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<5871e43d2979bff41b95b2cf662e24c0>> + * @generated SignedSource<<3198a6150452f2ae812525e7166ee021>> * @flow * @lightSyntaxTransform * @nogrep @@ -57,7 +57,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayLiveResolver", "name": "self", - "resolverModule": require('./../TodoSelfResolver').self, + "resolverModule": require('../TodoSelfResolver').self, "path": "self" } ], diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/UserAlwaysThrowsResolver.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/UserAlwaysThrowsResolver.graphql.js index fb62796ae5f57..44c530d8f4e21 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/UserAlwaysThrowsResolver.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/UserAlwaysThrowsResolver.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<65000cc0a4b69bdffd1ff81454a9ea5e>> + * @generated SignedSource<<46c82d1a963162cda03157ec67d447fa>> * @flow * @lightSyntaxTransform * @nogrep @@ -23,11 +23,6 @@ declare export opaque type UserAlwaysThrowsResolver$fragmentType: FragmentType; export type UserAlwaysThrowsResolver$data = {| +__typename: "User", +$fragmentType: UserAlwaysThrowsResolver$fragmentType, -|} | {| - // This will never be '%other', but we need some - // value in case none of the concrete values match. - +__typename: "%other", - +$fragmentType: UserAlwaysThrowsResolver$fragmentType, |}; export type UserAlwaysThrowsResolver$key = { +$data?: UserAlwaysThrowsResolver$data, diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/UserAlwaysThrowsTransitivelyResolver.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/UserAlwaysThrowsTransitivelyResolver.graphql.js index ed1f281afae35..f2b2a0e4ee867 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/UserAlwaysThrowsTransitivelyResolver.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/UserAlwaysThrowsTransitivelyResolver.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<8208626e87f520ef9ec13f8b00139491>> * @flow * @lightSyntaxTransform * @nogrep @@ -57,7 +57,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "always_throws", - "resolverModule": require('./../UserAlwaysThrowsResolver').always_throws, + "resolverModule": require('../UserAlwaysThrowsResolver').always_throws, "path": "always_throws" } ], diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/UserAnotherClientEdgeResolver.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/UserAnotherClientEdgeResolver.graphql.js index 5460b0f18008e..b3defaa980d19 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/UserAnotherClientEdgeResolver.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/UserAnotherClientEdgeResolver.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<5d8624f4c97bb474b71fe652707728bc>> + * @generated SignedSource<<9b561042e59b39b66fc334d8324c0d70>> * @flow * @lightSyntaxTransform * @nogrep @@ -23,11 +23,6 @@ declare export opaque type UserAnotherClientEdgeResolver$fragmentType: FragmentT export type UserAnotherClientEdgeResolver$data = {| +__typename: "User", +$fragmentType: UserAnotherClientEdgeResolver$fragmentType, -|} | {| - // This will never be '%other', but we need some - // value in case none of the concrete values match. - +__typename: "%other", - +$fragmentType: UserAnotherClientEdgeResolver$fragmentType, |}; export type UserAnotherClientEdgeResolver$key = { +$data?: UserAnotherClientEdgeResolver$data, diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/UserBestFriendShoutedGreetingResolver.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/UserBestFriendShoutedGreetingResolver.graphql.js index 9d2119552771d..2da0ac9b1f946 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/UserBestFriendShoutedGreetingResolver.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/UserBestFriendShoutedGreetingResolver.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<6620c21fdc16d53f3650b3330946b6ef>> * @flow * @lightSyntaxTransform * @nogrep @@ -101,7 +101,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "greeting", - "resolverModule": require('./../UserGreetingResolver').greeting, + "resolverModule": require('../UserGreetingResolver').greeting, "path": "friends.edges.node.greeting" } ], diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/UserConstantDependentResolver.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/UserConstantDependentResolver.graphql.js index 168fcf6a40be9..d008d287d8f3e 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/UserConstantDependentResolver.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/UserConstantDependentResolver.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<88cbf6b2de4c792fa3dd5dd0dd50006e>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -57,7 +57,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "constant", - "resolverModule": require('./../UserConstantResolver').constant, + "resolverModule": require('../UserConstantResolver').constant, "path": "constant" } ], diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/UserNameAndCounterSuspendsWhenOdd.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/UserNameAndCounterSuspendsWhenOdd.graphql.js index 40c3717e2fb10..83fcd6678f992 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/UserNameAndCounterSuspendsWhenOdd.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/UserNameAndCounterSuspendsWhenOdd.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<315f5bbba9915a69e600b39621763422>> + * @generated SignedSource<<8a82aca988d4e53a1cdbe4abdb1c09d2>> * @flow * @lightSyntaxTransform * @nogrep @@ -78,7 +78,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayLiveResolver", "name": "counter_suspends_when_odd", - "resolverModule": require('./../CounterSuspendsWhenOdd').counter_suspends_when_odd, + "resolverModule": require('../CounterSuspendsWhenOdd').counter_suspends_when_odd, "path": "counter_suspends_when_odd" } ], diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/UserProfilePictureUriSuspendsWhenTheCounterIsOdd.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/UserProfilePictureUriSuspendsWhenTheCounterIsOdd.graphql.js index 00ec2ab87562e..5e8f63d4da6b2 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/UserProfilePictureUriSuspendsWhenTheCounterIsOdd.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/UserProfilePictureUriSuspendsWhenTheCounterIsOdd.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<8d6fa83f6d19fa90b55190ef1168c80a>> + * @generated SignedSource<<6142d9f97247a1b158ca74abfe2c0fb9>> * @flow * @lightSyntaxTransform * @nogrep @@ -75,7 +75,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "greeting", - "resolverModule": require('./../UserGreetingResolver').greeting, + "resolverModule": require('../UserGreetingResolver').greeting, "path": "greeting" }, { @@ -94,7 +94,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "user_profile_picture_uri_with_scale", - "resolverModule": require('./../UserProfilePictureResolver').user_profile_picture_uri_with_scale, + "resolverModule": require('../UserProfilePictureResolver').user_profile_picture_uri_with_scale, "path": "uri" } ], diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/UserReadsClientEdgeResolver.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/UserReadsClientEdgeResolver.graphql.js index e7f92eb1e29e6..cf89b04602c3d 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/UserReadsClientEdgeResolver.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/UserReadsClientEdgeResolver.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<37c707178ed8e72b465de22e28e3be29>> + * @generated SignedSource<<86cdc5e5257c9ebf2d6e0d08c6c8daf6>> * @flow * @lightSyntaxTransform * @nogrep @@ -66,7 +66,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "client_edge", - "resolverModule": require('./../UserClientEdgeResolver').client_edge, + "resolverModule": require('../UserClientEdgeResolver').client_edge, "path": "client_edge" }, "linkedField": { diff --git a/packages/relay-runtime/store/__tests__/resolvers/__generated__/UserShoutedGreetingResolver.graphql.js b/packages/relay-runtime/store/__tests__/resolvers/__generated__/UserShoutedGreetingResolver.graphql.js index f1634bf708ee2..329a976de75fd 100644 --- a/packages/relay-runtime/store/__tests__/resolvers/__generated__/UserShoutedGreetingResolver.graphql.js +++ b/packages/relay-runtime/store/__tests__/resolvers/__generated__/UserShoutedGreetingResolver.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<39f40b26989a53cc0d499eb5eced94d8>> + * @generated SignedSource<<52aaca3e357241a0800ed6756c6788d4>> * @flow * @lightSyntaxTransform * @nogrep @@ -57,7 +57,7 @@ var node/*: ReaderFragment*/ = { }, "kind": "RelayResolver", "name": "greeting", - "resolverModule": require('./../UserGreetingResolver').greeting, + "resolverModule": require('../UserGreetingResolver').greeting, "path": "greeting" } ], diff --git a/packages/relay-runtime/store/__tests__/waitForFragmentData-test.js b/packages/relay-runtime/store/__tests__/waitForFragmentData-test.js index ec64390dd1a92..456725cd381d4 100644 --- a/packages/relay-runtime/store/__tests__/waitForFragmentData-test.js +++ b/packages/relay-runtime/store/__tests__/waitForFragmentData-test.js @@ -52,6 +52,39 @@ test('data ok', async () => { expect(result).toEqual({name: 'Elizabeth'}); }); +test('data ok with plural fragment', async () => { + const query = graphql` + query waitForFragmentDataTestOkPluralQuery { + nodes(ids: ["1", "2"]) { + ...waitForFragmentDataTestOkPluralFragment + } + } + `; + + const fragment = graphql` + fragment waitForFragmentDataTestOkPluralFragment on User + @relay(plural: true) { + name + } + `; + + const environment = createMockEnvironment({ + store: new LiveResolverStore(new RelayRecordSource()), + }); + const variables = {}; + const operation = createOperationDescriptor(query, variables); + environment.commitPayload(operation, { + nodes: [ + {id: '1', __typename: 'User', name: 'Alice'}, + {id: '2', __typename: 'User', name: 'Bob'}, + ], + }); + const {data} = environment.lookup(operation.fragment); + // $FlowFixMe - data is untyped + const result = await waitForFragmentData(environment, fragment, data.nodes); + expect(result).toEqual([{name: 'Alice'}, {name: 'Bob'}]); +}); + test('Promise rejects with @throwOnFieldError', async () => { const query = graphql` query waitForFragmentDataTestThrowOnFieldErrorQuery { @@ -83,7 +116,7 @@ test('Promise rejects with @throwOnFieldError', async () => { result = e; } expect(result?.message).toEqual( - "Relay: Resolver error at path 'always_throws' in 'waitForFragmentDataTestResolverErrorWithThrowOnFieldErrorFragment'.", + "Relay: Resolver error at path 'always_throws' in 'waitForFragmentDataTestResolverErrorWithThrowOnFieldErrorFragment'. Message: I always throw. What did you expect?", ); }); diff --git a/packages/relay-runtime/store/createRelayContext.js b/packages/relay-runtime/store/createRelayContext.js index 87074907d1a10..82ac5551936c4 100644 --- a/packages/relay-runtime/store/createRelayContext.js +++ b/packages/relay-runtime/store/createRelayContext.js @@ -12,6 +12,7 @@ 'use strict'; import type {RelayContext} from './RelayStoreTypes.js'; +import type {Context} from 'react'; import typeof {createContext} from 'react'; const invariant = require('invariant'); @@ -24,10 +25,10 @@ type React = $ReadOnly<{ ... }>; -let relayContext: ?React$Context; +let relayContext: ?Context; let firstReact: ?React; -function createRelayContext(react: React): React$Context { +function createRelayContext(react: React): Context { if (!relayContext) { relayContext = react.createContext(null); if (__DEV__) { diff --git a/packages/relay-runtime/store/createRelayLoggingContext.js b/packages/relay-runtime/store/createRelayLoggingContext.js new file mode 100644 index 0000000000000..73be01ef00b16 --- /dev/null +++ b/packages/relay-runtime/store/createRelayLoggingContext.js @@ -0,0 +1,46 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + * @oncall relay + */ + +'use strict'; + +import type {Context} from 'react'; +import typeof {createContext} from 'react'; + +const invariant = require('invariant'); + +// Ideally, we'd just import the type of the react module, but this causes Flow +// problems. +type React = $ReadOnly<{ + createContext: createContext, + version: string, + ... +}>; + +let relayLoggingContext: ?Context; +let firstReact: ?React; + +function createRelayLoggingContext(react: React): Context { + if (!relayLoggingContext) { + relayLoggingContext = react.createContext(null); + if (__DEV__) { + relayLoggingContext.displayName = 'RelayLoggingContext'; + } + firstReact = react; + } + invariant( + react === firstReact, + '[createRelayLoggingContext]: You are passing a different instance of React', + react.version, + ); + return relayLoggingContext; +} + +module.exports = createRelayLoggingContext; diff --git a/packages/relay-runtime/store/defaultGetDataID.js b/packages/relay-runtime/store/defaultGetDataID.js index 5ba0df0096405..5e944a179bde7 100644 --- a/packages/relay-runtime/store/defaultGetDataID.js +++ b/packages/relay-runtime/store/defaultGetDataID.js @@ -14,7 +14,7 @@ const {VIEWER_ID, VIEWER_TYPE} = require('./ViewerPattern'); function defaultGetDataID( - fieldValue: {[string]: mixed}, + fieldValue: {+[string]: mixed}, typeName: string, ): mixed { if (typeName === VIEWER_TYPE) { diff --git a/packages/relay-runtime/store/generateTypenamePrefixedDataID.js b/packages/relay-runtime/store/generateTypenamePrefixedDataID.js new file mode 100644 index 0000000000000..ddf4df9b80a19 --- /dev/null +++ b/packages/relay-runtime/store/generateTypenamePrefixedDataID.js @@ -0,0 +1,25 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + * @oncall relay + */ + +'use strict'; + +import type {DataID} from 'relay-runtime/util/RelayRuntimeTypes'; + +const TYPENAME_PREFIX = '__type:'; + +function generateTypenamePrefixedDataID( + typeName: string, + dataID: DataID, +): DataID { + return `${TYPENAME_PREFIX}${typeName}:${dataID}`; +} + +module.exports = {generateTypenamePrefixedDataID}; diff --git a/packages/relay-runtime/store/live-resolvers/LiveResolverCache.js b/packages/relay-runtime/store/live-resolvers/LiveResolverCache.js index c36b546f6f3cc..c9d32b26a17d3 100644 --- a/packages/relay-runtime/store/live-resolvers/LiveResolverCache.js +++ b/packages/relay-runtime/store/live-resolvers/LiveResolverCache.js @@ -48,6 +48,7 @@ const { RELAY_RESOLVER_OUTPUT_TYPE_RECORD_IDS, RELAY_RESOLVER_SNAPSHOT_KEY, RELAY_RESOLVER_VALUE_KEY, + getReadTimeResolverStorageKey, getStorageKey, } = require('../RelayStoreUtils'); const getOutputTypeRecordIDs = require('./getOutputTypeRecordIDs'); @@ -129,7 +130,7 @@ class LiveResolverCache implements ResolverCache { // resolvers on this parent record. const record = expectRecord(recordSource, recordID); - const storageKey = getStorageKey(field, variables); + const storageKey = getReadTimeResolverStorageKey(field, variables); let linkedID = RelayModernRecord.getLinkedRecordID(record, storageKey); let linkedRecord = linkedID == null ? null : recordSource.get(linkedID); @@ -753,7 +754,8 @@ class LiveResolverCache implements ResolverCache { outputTypeDataID, variables, ); - + // LiveResolverCache is only used by read time resolvers, so this flag should be hardcoded as false. + const useExecTimeResolvers = false; const normalizationOptions = this._store.__getNormalizationOptions(fieldPath); // The resulted `source` is the normalized version of the @@ -761,6 +763,7 @@ class LiveResolverCache implements ResolverCache { // All records in the `source` should have IDs that // is "prefix-ed" with the parent resolver record `ID` // and they don't expect to have a "strong" identifier. + return normalize( source, selector, @@ -770,6 +773,8 @@ class LiveResolverCache implements ResolverCache { // $FlowFixMe[incompatible-variance] value, normalizationOptions, + undefined, + useExecTimeResolvers, ).source; } // For weak models we have a simpler case. We simply need to update a diff --git a/packages/relay-runtime/store/live-resolvers/resolverDataInjector.js b/packages/relay-runtime/store/live-resolvers/resolverDataInjector.js index 09521911a7df0..072913311489c 100644 --- a/packages/relay-runtime/store/live-resolvers/resolverDataInjector.js +++ b/packages/relay-runtime/store/live-resolvers/resolverDataInjector.js @@ -12,12 +12,12 @@ 'use strict'; import type {Fragment} from '../../util/RelayRuntimeTypes'; -import type {FragmentType} from '../RelayStoreTypes'; +import type {FragmentType, ResolverContext} from '../RelayStoreTypes'; const {readFragment} = require('../ResolverFragments'); const invariant = require('invariant'); -type ResolverFn = ($FlowFixMe, ?$FlowFixMe) => mixed; +type ResolverFn = ($FlowFixMe, ?$FlowFixMe, ResolverContext) => mixed; /** * @@ -40,7 +40,11 @@ function resolverDataInjector( isRequiredField?: boolean, ): (fragmentKey: TFragmentType, args: mixed) => mixed { const resolverFn: ResolverFn = _resolverFn; - return (fragmentKey: TFragmentType, args: mixed): mixed => { + return ( + fragmentKey: TFragmentType, + args: mixed, + resolverContext: ResolverContext, + ): mixed => { const data = readFragment(fragment, fragmentKey); if (fieldName != null) { if (data == null) { @@ -52,7 +56,7 @@ function resolverDataInjector( fragment.name, ); } else { - return resolverFn(null, args); + return resolverFn(null, args, resolverContext); // TODO: This statement does not seem to be covered by a test? } } @@ -70,7 +74,7 @@ function resolverDataInjector( } // $FlowFixMe[invalid-computed-prop] - return resolverFn(data[fieldName], args); + return resolverFn(data[fieldName], args, resolverContext); } else { // If both `data` and `fieldName` is available, we expect the // `fieldName` field in the `data` object. @@ -83,7 +87,7 @@ function resolverDataInjector( } } else { // By default we will pass the full set of the fragment data to the resolver - return resolverFn(data, args); + return resolverFn(null, args, resolverContext); // TODO: This statement does not seem to be covered by a test? } }; } diff --git a/packages/relay-runtime/store/normalizeResponse.js b/packages/relay-runtime/store/normalizeResponse.js index 4f89a47278797..0b6b736803f7f 100644 --- a/packages/relay-runtime/store/normalizeResponse.js +++ b/packages/relay-runtime/store/normalizeResponse.js @@ -27,6 +27,7 @@ function normalizeResponse( selector: NormalizationSelector, typeName: string, options: NormalizationOptions, + useExecTimeResolvers: boolean, ): RelayResponsePayload { const {data, errors} = response; const source = RelayRecordSource.create(); @@ -38,6 +39,7 @@ function normalizeResponse( data, options, errors, + useExecTimeResolvers, ); return { ...relayPayload, diff --git a/packages/relay-runtime/store/observeFragmentExperimental.js b/packages/relay-runtime/store/observeFragmentExperimental.js index e84a00855c944..68ed41452f237 100644 --- a/packages/relay-runtime/store/observeFragmentExperimental.js +++ b/packages/relay-runtime/store/observeFragmentExperimental.js @@ -9,7 +9,7 @@ * @oncall relay */ -import type {RequestDescriptor} from './RelayStoreTypes'; +import type {PluralReaderSelector, RequestDescriptor} from './RelayStoreTypes'; import type { Fragment, FragmentType, @@ -20,8 +20,8 @@ import type { } from 'relay-runtime'; const Observable = require('../network/RelayObservable'); +const {getObservableForActiveRequest} = require('../query/fetchQueryInternal'); const {getFragment} = require('../query/GraphQLTag'); -const getPendingOperationsForFragment = require('../util/getPendingOperationsForFragment'); const { handlePotentialSnapshotErrors, } = require('../util/handlePotentialSnapshotErrors'); @@ -47,8 +47,7 @@ export type HasSpread = { /** * EXPERIMENTAL: This API is experimental and does not yet support all Relay - * features. Notably, it does not correectly handle plural fragments or some - * features of Relay Resolvers. + * features. Notably, it does not correctly handle some features of Relay Resolvers. * * Given a fragment and a fragment reference, returns a promise that resolves * once the fragment data is available, or rejects if the fragment has an error. @@ -63,7 +62,9 @@ export type HasSpread = { async function waitForFragmentData( environment: IEnvironment, fragment: Fragment, - fragmentRef: HasSpread, + fragmentRef: + | HasSpread + | $ReadOnlyArray>, ): Promise { let subscription: ?Subscription; @@ -94,13 +95,14 @@ async function waitForFragmentData( declare function observeFragment( environment: IEnvironment, fragment: Fragment, - fragmentRef: HasSpread, + fragmentRef: + | HasSpread + | $ReadOnlyArray>, ): Observable>; /** * EXPERIMENTAL: This API is experimental and does not yet support all Relay - * features. Notably, it does not correectly handle plural fragments or some - * features of Relay Resolvers. + * features. Notably, it does not correctly handle some features of Relay Resolvers. * * Given a fragment and a fragment reference, returns an observable that emits * the state of the fragment over time. The observable will emit the following @@ -114,7 +116,7 @@ function observeFragment( environment: IEnvironment, fragment: Fragment, fragmentRef: mixed, -): Observable> { +): mixed { const fragmentNode = getFragment(fragment); const fragmentSelector = getSelector(fragmentNode, fragmentRef); invariant( @@ -124,24 +126,19 @@ function observeFragment( invariant(fragmentSelector != null, 'Expected a selector, got null.'); switch (fragmentSelector.kind) { case 'SingularReaderSelector': - return observeSelector(environment, fragment, fragmentSelector); + return observeSingularSelector(environment, fragment, fragmentSelector); case 'PluralReaderSelector': { - // TODO: We could use something like this RXJS's combineLatest to create - // an observable for each selector and merge them. - // https://github.com/ReactiveX/rxjs/blob/master/packages/rxjs/src/internal/observable/combineLatest.ts - // - // Note that this problem is a bit tricky because Relay currently only - // lets you subscribe at a singular fragment granularity. This makes it - // hard to batch updates such that when a store update causes multiple - // fragments to change, we can only publish a single update to the - // fragment owner. - invariant(false, 'Plural fragments are not supported'); + return observePluralSelector( + environment, + (fragment: $FlowFixMe), + fragmentSelector, + ); } } invariant(false, 'Unsupported fragment selector kind'); } -function observeSelector( +function observeSingularSelector( environment: IEnvironment, fragmentNode: Fragment, fragmentSelector: SingularReaderSelector, @@ -173,6 +170,49 @@ function observeSelector( }); } +function observePluralSelector< + TFragmentType: FragmentType, + TData: Array, +>( + environment: IEnvironment, + fragmentNode: Fragment, + fragmentSelector: PluralReaderSelector, +): Observable> { + const snapshots = fragmentSelector.selectors.map(selector => + environment.lookup(selector), + ); + + return Observable.create(sink => { + // This array is mutable since each subscription updates the array in place. + const states = snapshots.map((snapshot, index) => + snapshotToFragmentState( + environment, + fragmentNode, + fragmentSelector.selectors[index].owner, + snapshot, + ), + ); + + sink.next((mergeFragmentStates(states): $FlowFixMe)); + + const subscriptions = snapshots.map((snapshot, index) => + environment.subscribe(snapshot, latestSnapshot => { + states[index] = snapshotToFragmentState( + environment, + fragmentNode, + fragmentSelector.selectors[index].owner, + latestSnapshot, + ); + // This doesn't batch updates, so it will notify the subscriber multiple times + // if a store update impacting multiple items in the list is published. + sink.next((mergeFragmentStates(states): $FlowFixMe)); + }), + ); + + return () => subscriptions.forEach(subscription => subscription.dispose()); + }); +} + function snapshotToFragmentState( environment: IEnvironment, fragmentNode: Fragment, @@ -201,18 +241,18 @@ function snapshotToFragmentState( } if (snapshot.isMissingData) { - const pendingOperations = getPendingOperationsForFragment( - environment, - fragmentNode, - owner, - ); - if (pendingOperations != null) { + if ( + getObservableForActiveRequest(environment, owner) != null || + environment + .getOperationTracker() + .getPendingOperationsAffectingOwner(owner) != null + ) { return {state: 'loading'}; } } try { - handlePotentialSnapshotErrors(environment, snapshot.errorResponseFields); + handlePotentialSnapshotErrors(environment, snapshot.fieldErrors); } catch (error) { return {error, state: 'error'}; } @@ -229,6 +269,20 @@ function snapshotToFragmentState( return {state: 'ok', value: (snapshot.data: $FlowFixMe)}; } +function mergeFragmentStates( + states: $ReadOnlyArray>, +): FragmentState> { + const value = []; + for (const state of states) { + if (state.state === 'ok') { + value.push(state.value); + } else { + return state; + } + } + return {state: 'ok', value}; +} + module.exports = { observeFragment, waitForFragmentData, diff --git a/packages/relay-runtime/store/observeQueryExperimental.js b/packages/relay-runtime/store/observeQueryExperimental.js new file mode 100644 index 0000000000000..74e1a630b9ef0 --- /dev/null +++ b/packages/relay-runtime/store/observeQueryExperimental.js @@ -0,0 +1,61 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @flow strict-local + * @format + * @oncall relay + */ + +'use strict'; + +import type RelayObservable from '../network/RelayObservable'; +import type {FragmentState} from './observeFragmentExperimental'; +import type {OperationDescriptor} from './RelayStoreTypes'; +import type {Fragment, IEnvironment, Query, Variables} from 'relay-runtime'; + +const {observeFragment} = require('./observeFragmentExperimental'); +const {createOperationDescriptor} = require('./RelayModernOperationDescriptor'); + +/** + * This function returns an observable that can be used to subscribe to the data + * contained in a query. It does not return the full response shape, but rather + * the contents of the query body minus any fragment spreads. If you wish to + * read the contents of a fragment spread into this query you may pass the + * object into which the fragment was spread to `observeFragment`. + * + * NOTE: `observeQuery` assumes that you have already fetched and retained the + * query via some other means, such as `fetchQuery`. + * + * This feature is still experimental and does not properly handle some resolver + * features such as client-to-server edges. + */ +function observeQuery( + environment: IEnvironment, + gqlQuery: Query, + variables: TVariables, +): RelayObservable> { + const operation: OperationDescriptor = createOperationDescriptor( + gqlQuery, + variables, + ); + + const rootFragmentRef: $FlowFixMe = { + __id: operation.fragment.dataID, + __fragments: { + [operation.fragment.node.name]: operation.request.variables, + }, + __fragmentOwner: operation.request, + }; + + const fragmentNode: Fragment<$FlowFixMe, TData> = (operation.request.node + .fragment: $FlowFixMe); + + return observeFragment(environment, fragmentNode, rootFragmentRef); +} + +module.exports = { + observeQuery, +}; diff --git a/packages/relay-runtime/store/waitForFragmentExperimental.js b/packages/relay-runtime/store/waitForFragmentExperimental.js index 53ba061e85ec5..e1ad597178d86 100644 --- a/packages/relay-runtime/store/waitForFragmentExperimental.js +++ b/packages/relay-runtime/store/waitForFragmentExperimental.js @@ -21,8 +21,7 @@ const {observeFragment} = require('./observeFragmentExperimental'); /** * EXPERIMENTAL: This API is experimental and does not yet support all Relay - * features. Notably, it does not correectly handle plural fragments or some - * features of Relay Resolvers. + * features. Notably, it does not correctly handle some features of Relay Resolvers. * * Given a fragment and a fragment reference, returns a promise that resolves * once the fragment data is available, or rejects if the fragment has an error. @@ -37,7 +36,9 @@ const {observeFragment} = require('./observeFragmentExperimental'); async function waitForFragmentData( environment: IEnvironment, fragment: Fragment, - fragmentRef: HasSpread, + fragmentRef: + | HasSpread + | $ReadOnlyArray>, ): Promise { let subscription: ?Subscription; diff --git a/packages/relay-runtime/subscription/__tests__/requestSubscription-test.js b/packages/relay-runtime/subscription/__tests__/requestSubscription-test.js index 3aed57af6c441..bb30f3b0b7384 100644 --- a/packages/relay-runtime/subscription/__tests__/requestSubscription-test.js +++ b/packages/relay-runtime/subscription/__tests__/requestSubscription-test.js @@ -10,6 +10,7 @@ */ 'use strict'; +import type {DeclarativeMutationConfig} from '../../mutations/RelayDeclarativeMutationConfig'; import type {GraphQLResponse} from '../../network/RelayNetworkTypes'; import type {RecordSourceSelectorProxy} from '../../store/RelayStoreTypes'; import type {RequestParameters} from '../../util/RelayConcreteNode'; @@ -109,7 +110,7 @@ describe('requestSubscription-test', () => { } `; - const configs = [ + const configs: DeclarativeMutationConfig[] = [ { type: 'RANGE_ADD', connectionName: 'comments', @@ -194,7 +195,7 @@ describe('requestSubscription-test', () => { }); describe('requestSubscription() cacheConfig', () => { - let cacheMetadata: ?{[key: string]: mixed}; + let cacheMetadata: ?{+[key: string]: mixed}; let environment; let CommentCreateSubscription; const feedbackId = 'foo'; diff --git a/packages/relay-runtime/subscription/requestSubscription.js b/packages/relay-runtime/subscription/requestSubscription.js index 32b49948dcbfe..1ba8d31ad54f2 100644 --- a/packages/relay-runtime/subscription/requestSubscription.js +++ b/packages/relay-runtime/subscription/requestSubscription.js @@ -41,16 +41,17 @@ export type SubscriptionParameters = { * Updated Flow type that makes use of typed graphql tagged literals with * type information. */ -export type GraphQLSubscriptionConfig = { - configs?: Array, - cacheConfig?: CacheConfig, - subscription: GraphQLSubscription, - variables: TVariables, - onCompleted?: ?() => void, - onError?: ?(error: Error) => void, - onNext?: ?(response: ?TData) => void, - updater?: ?SelectorStoreUpdater, -}; +export type GraphQLSubscriptionConfig = + $ReadOnly<{ + configs?: Array, + cacheConfig?: CacheConfig, + subscription: GraphQLSubscription, + variables: TVariables, + onCompleted?: ?() => void, + onError?: ?(error: Error) => void, + onNext?: ?(response: ?TData) => void, + updater?: ?SelectorStoreUpdater, + }>; function requestSubscription( environment: IEnvironment, diff --git a/packages/relay-runtime/util/NormalizationNode.js b/packages/relay-runtime/util/NormalizationNode.js index de2efe45742e1..d2a818d9b1da1 100644 --- a/packages/relay-runtime/util/NormalizationNode.js +++ b/packages/relay-runtime/util/NormalizationNode.js @@ -12,7 +12,7 @@ 'use strict'; import type {ResolverFunction, ResolverModule} from './ReaderNode'; -import type {ConcreteRequest} from './RelayConcreteNode'; +import type {ConcreteRequest, ProvidedVariableType} from './RelayConcreteNode'; import type {JSResourceReference} from 'JSResourceReference'; /** @@ -28,6 +28,7 @@ export type NormalizationOperation = { +[string]: $ReadOnlyArray, }, +use_exec_time_resolvers?: boolean, + +exec_time_resolvers_enabled_provider?: ProvidedVariableType, }; export type NormalizationHandle = diff --git a/packages/relay-runtime/util/RelayConcreteNode.js b/packages/relay-runtime/util/RelayConcreteNode.js index bf50490cbef93..0a8c591b54b61 100644 --- a/packages/relay-runtime/util/RelayConcreteNode.js +++ b/packages/relay-runtime/util/RelayConcreteNode.js @@ -39,6 +39,8 @@ export type NormalizationRootNode = | ConcreteRequest | NormalizationSplitOperation; +export type ProvidedVariableType = {get(): mixed}; + export type ProvidedVariablesType = {+[key: string]: {get(): mixed}}; /** diff --git a/packages/relay-runtime/util/RelayError.js b/packages/relay-runtime/util/RelayError.js index d97f4e8266c26..3f82e4450afad 100644 --- a/packages/relay-runtime/util/RelayError.js +++ b/packages/relay-runtime/util/RelayError.js @@ -25,6 +25,7 @@ function createError( String(messageParams[index++]), ); const err = new Error(message); + // $FlowFixMe[unsafe-object-assign] const error = Object.assign((err: any), { name, messageFormat, diff --git a/packages/relay-runtime/util/RelayFeatureFlags.js b/packages/relay-runtime/util/RelayFeatureFlags.js index 91d5ff2672d62..3c078fbdf6216 100644 --- a/packages/relay-runtime/util/RelayFeatureFlags.js +++ b/packages/relay-runtime/util/RelayFeatureFlags.js @@ -26,6 +26,7 @@ export type FeatureFlags = { STRING_INTERN_LEVEL: number, LOG_MISSING_RECORDS_IN_PROD: boolean, ENABLE_RELAY_OPERATION_TRACKER_SUSPENSE: boolean, + ENABLE_UI_CONTEXT_ON_RELAY_LOGGER: boolean, // Some GraphQL servers are noncompliant with the GraphQL specification and // return an empty list instead of null when there is a field error on a list. @@ -58,6 +59,27 @@ export type FeatureFlags = { // Adds a prefix to the storage key of read time resolvers. This is used to // disambiguate the same resolver being used at both read time and exec time. ENABLE_READ_TIME_RESOLVER_STORAGE_KEY_PREFIX: boolean, + + // Enable the fix for usePaginationFragment stucking in loading state + ENABLE_USE_PAGINATION_IS_LOADING_FIX: boolean, + + // Enable logging an ID collision in the Relay store + ENABLE_STORE_ID_COLLISION_LOGGING: boolean, + + // Throw on nested store updates + DISALLOW_NESTED_UPDATES: boolean, + + // Enable prefixing of DataID in the store with __typename + ENABLE_TYPENAME_PREFIXED_DATA_ID: boolean, + + // Relay previously had a bug where it would fail to check for missing client + // edge to server data in fragments nested within client edge Relay Resolver + // fields. This feature flag fixes the behavior but comes with a perf cost. + // This flag is here to allow us to gradually rollout the fix and track the perf + // impact. + // + // See https://github.com/facebook/relay/issues/4882 + CHECK_ALL_FRAGMENTS_FOR_MISSING_CLIENT_EDGES: boolean, }; const RelayFeatureFlags: FeatureFlags = { @@ -72,6 +94,7 @@ const RelayFeatureFlags: FeatureFlags = { MAX_DATA_ID_LENGTH: null, STRING_INTERN_LEVEL: 0, LOG_MISSING_RECORDS_IN_PROD: false, + ENABLE_STORE_ID_COLLISION_LOGGING: false, ENABLE_NONCOMPLIANT_ERROR_HANDLING_ON_LISTS: false, ENABLE_LOOSE_SUBSCRIPTION_ATTRIBUTION: false, ENABLE_OPERATION_TRACKER_OPTIMISTIC_UPDATES: false, @@ -81,6 +104,11 @@ const RelayFeatureFlags: FeatureFlags = { ENABLE_CYLE_DETECTION_IN_VARIABLES: false, ENABLE_ACTIVITY_COMPATIBILITY: false, ENABLE_READ_TIME_RESOLVER_STORAGE_KEY_PREFIX: true, + ENABLE_USE_PAGINATION_IS_LOADING_FIX: false, + DISALLOW_NESTED_UPDATES: false, + ENABLE_TYPENAME_PREFIXED_DATA_ID: false, + ENABLE_UI_CONTEXT_ON_RELAY_LOGGER: false, + CHECK_ALL_FRAGMENTS_FOR_MISSING_CLIENT_EDGES: false, }; module.exports = RelayFeatureFlags; diff --git a/packages/relay-runtime/util/RelayRuntimeTypes.js b/packages/relay-runtime/util/RelayRuntimeTypes.js index c7179cfae539e..5083a57926ced 100644 --- a/packages/relay-runtime/util/RelayRuntimeTypes.js +++ b/packages/relay-runtime/util/RelayRuntimeTypes.js @@ -53,20 +53,23 @@ export type VariablesOf = T['variables']; * state of any configured response cache. * - `poll`: causes a query to live update by polling at the specified interval * in milliseconds. (This value will be passed to setTimeout.) - * - `liveConfigId`: causes a query to live update by calling GraphQLLiveQuery, - * it represents a configuration of gateway when doing live query - * - `onSubscribe`: Not in use. + * - `liveConfigId`: Makes a query live by sending through RTI stack. + * - `onSubscribe`: Callback to be called when a live query stream is started. + * - `onPause`: Callback to be called when a live query stream is paused, e.g. due to a network disconnection. + * - `onResume`: Callback to be called when a live query stream is resumed, e.g. from a network disconnection. * - `metadata`: user-supplied metadata. * - `transactionId`: a user-supplied value, intended for use as a unique id for * a given instance of executing an operation. */ export type CacheConfig = { - force?: ?boolean, - poll?: ?number, - liveConfigId?: ?string, - onSubscribe?: () => void, - metadata?: {[key: string]: mixed, ...}, - transactionId?: ?string, + +force?: ?boolean, + +poll?: ?number, + +liveConfigId?: ?string, + +onSubscribe?: () => void, + +onResume?: (pauseTimeMs: number) => void, + +onPause?: (mqttConnectionIsOk: boolean, internetIsOk: boolean) => void, + +metadata?: {+[key: string]: mixed, ...}, + +transactionId?: ?string, }; export type FetchQueryFetchPolicy = 'store-or-network' | 'network-only'; diff --git a/packages/relay-runtime/util/__tests__/__generated__/getFragmentIdentifierTest1UserQuery.graphql.js b/packages/relay-runtime/util/__tests__/__generated__/getFragmentIdentifierTest1UserQuery.graphql.js index 636bc49292310..ebdee63c602e4 100644 --- a/packages/relay-runtime/util/__tests__/__generated__/getFragmentIdentifierTest1UserQuery.graphql.js +++ b/packages/relay-runtime/util/__tests__/__generated__/getFragmentIdentifierTest1UserQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<1354180d9df58b0704c83b17a43fb95f>> * @flow * @lightSyntaxTransform * @nogrep @@ -171,7 +171,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "a6ebfafd3adccdfde2e43998af09c190"; + (node/*: any*/).hash = "18064784693e9788fe52f1bbae385ee0"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/util/__tests__/__generated__/getFragmentIdentifierTest1UserQueryWithArgsQuery.graphql.js b/packages/relay-runtime/util/__tests__/__generated__/getFragmentIdentifierTest1UserQueryWithArgsQuery.graphql.js index 52a02d6a72122..7b8c876201750 100644 --- a/packages/relay-runtime/util/__tests__/__generated__/getFragmentIdentifierTest1UserQueryWithArgsQuery.graphql.js +++ b/packages/relay-runtime/util/__tests__/__generated__/getFragmentIdentifierTest1UserQueryWithArgsQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<13674f5f22576daad7c1bf3df650b42c>> * @flow * @lightSyntaxTransform * @nogrep @@ -177,7 +177,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "2c6734517dd2de88716f70f4c2a3b59e"; + (node/*: any*/).hash = "608b70474f7b066bac350843e1dd58c6"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/util/__tests__/__generated__/getFragmentIdentifierTestUserQuery.graphql.js b/packages/relay-runtime/util/__tests__/__generated__/getFragmentIdentifierTestUserQuery.graphql.js index 04a0c9142c5ad..eb813fc962ad8 100644 --- a/packages/relay-runtime/util/__tests__/__generated__/getFragmentIdentifierTestUserQuery.graphql.js +++ b/packages/relay-runtime/util/__tests__/__generated__/getFragmentIdentifierTestUserQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<6d05a0a55ccd1745dfdaa1439e03cd61>> + * @generated SignedSource<<2474e6b61cea3140e05b74f7be798603>> * @flow * @lightSyntaxTransform * @nogrep @@ -171,7 +171,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "0109159e3a59848d6da59a0b19147157"; + (node/*: any*/).hash = "3e1de67e9c815581b0b170f37d56a100"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/util/__tests__/__generated__/getFragmentIdentifierTestUserQueryWithArgsQuery.graphql.js b/packages/relay-runtime/util/__tests__/__generated__/getFragmentIdentifierTestUserQueryWithArgsQuery.graphql.js index afa5171093a84..b9202ac38428e 100644 --- a/packages/relay-runtime/util/__tests__/__generated__/getFragmentIdentifierTestUserQueryWithArgsQuery.graphql.js +++ b/packages/relay-runtime/util/__tests__/__generated__/getFragmentIdentifierTestUserQueryWithArgsQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<313d4d202ed7f617972df191239b7378>> + * @generated SignedSource<<8cd3dc9de6099bff154fa4911de1a39c>> * @flow * @lightSyntaxTransform * @nogrep @@ -177,7 +177,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "146115816909f480fcbd194b3816899f"; + (node/*: any*/).hash = "ce1c117df7e611604494cc0036258270"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/util/__tests__/__generated__/withProvidedVariablesTest1Query.graphql.js b/packages/relay-runtime/util/__tests__/__generated__/withProvidedVariablesTest1Query.graphql.js index ab2803ec94c86..d6ebeccf4dcdc 100644 --- a/packages/relay-runtime/util/__tests__/__generated__/withProvidedVariablesTest1Query.graphql.js +++ b/packages/relay-runtime/util/__tests__/__generated__/withProvidedVariablesTest1Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<6703998458a25903621ee13227354a96>> + * @generated SignedSource<<18c8b08a242350766dc3e35b00e88bee>> * @flow * @lightSyntaxTransform * @nogrep @@ -30,7 +30,7 @@ export type withProvidedVariablesTest1Query = {| variables: withProvidedVariablesTest1Query$variables, |}; ({ - "__relay_internal__pv__provideNumberOfFriendsrelayprovider": require('./../provideNumberOfFriends.relayprovider') + "__relay_internal__pv__provideNumberOfFriendsrelayprovider": require('../provideNumberOfFriends.relayprovider') }: {| +__relay_internal__pv__provideNumberOfFriendsrelayprovider: {| +get: () => number, @@ -151,14 +151,14 @@ return { "operationKind": "query", "text": "query withProvidedVariablesTest1Query(\n $__relay_internal__pv__provideNumberOfFriendsrelayprovider: Int!\n) {\n node(id: 4) {\n __typename\n ...withProvidedVariablesTest1Fragment\n id\n }\n}\n\nfragment withProvidedVariablesTest1Fragment on User {\n friends(first: $__relay_internal__pv__provideNumberOfFriendsrelayprovider) {\n count\n }\n}\n", "providedVariables": { - "__relay_internal__pv__provideNumberOfFriendsrelayprovider": require('./../provideNumberOfFriends.relayprovider') + "__relay_internal__pv__provideNumberOfFriendsrelayprovider": require('../provideNumberOfFriends.relayprovider') } } }; })(); if (__DEV__) { - (node/*: any*/).hash = "a613af5829765dd939e03fd6ff1141ff"; + (node/*: any*/).hash = "c5f46e63be71ffd76d40c58b53dc2c3a"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/util/__tests__/__generated__/withProvidedVariablesTest2Query.graphql.js b/packages/relay-runtime/util/__tests__/__generated__/withProvidedVariablesTest2Query.graphql.js index 7b51233673ed9..b811c64d092d0 100644 --- a/packages/relay-runtime/util/__tests__/__generated__/withProvidedVariablesTest2Query.graphql.js +++ b/packages/relay-runtime/util/__tests__/__generated__/withProvidedVariablesTest2Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<28b3eba1bf961281a612ddc50ead4b1f>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -32,7 +32,7 @@ export type withProvidedVariablesTest2Query = {| variables: withProvidedVariablesTest2Query$variables, |}; ({ - "__relay_internal__pv__provideNumberOfFriendsrelayprovider": require('./../provideNumberOfFriends.relayprovider') + "__relay_internal__pv__provideNumberOfFriendsrelayprovider": require('../provideNumberOfFriends.relayprovider') }: {| +__relay_internal__pv__provideNumberOfFriendsrelayprovider: {| +get: () => number, @@ -174,14 +174,14 @@ return { "operationKind": "query", "text": "query withProvidedVariablesTest2Query(\n $includeFriendsCount: Boolean!\n $__relay_internal__pv__provideNumberOfFriendsrelayprovider: Int!\n) {\n node(id: 4) {\n __typename\n ...withProvidedVariablesTest2Fragment_47ZY3u\n id\n }\n}\n\nfragment withProvidedVariablesTest2Fragment_47ZY3u on User {\n friends(first: $__relay_internal__pv__provideNumberOfFriendsrelayprovider) {\n count @include(if: $includeFriendsCount)\n }\n}\n", "providedVariables": { - "__relay_internal__pv__provideNumberOfFriendsrelayprovider": require('./../provideNumberOfFriends.relayprovider') + "__relay_internal__pv__provideNumberOfFriendsrelayprovider": require('../provideNumberOfFriends.relayprovider') } } }; })(); if (__DEV__) { - (node/*: any*/).hash = "e9e93574d0f69b01f4abac7b2738cece"; + (node/*: any*/).hash = "672d458b4f030a73c4aeb0146485c476"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/util/__tests__/__generated__/withProvidedVariablesTest3Query.graphql.js b/packages/relay-runtime/util/__tests__/__generated__/withProvidedVariablesTest3Query.graphql.js index eb7961d1bd5cc..057fe912a2239 100644 --- a/packages/relay-runtime/util/__tests__/__generated__/withProvidedVariablesTest3Query.graphql.js +++ b/packages/relay-runtime/util/__tests__/__generated__/withProvidedVariablesTest3Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<5815185ccc4d1a85d36ed17b4f10ec0d>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -30,8 +30,8 @@ export type withProvidedVariablesTest3Query = {| variables: withProvidedVariablesTest3Query$variables, |}; ({ - "__relay_internal__pv__provideNumberOfFriendsrelayprovider": require('./../provideNumberOfFriends.relayprovider'), - "__relay_internal__pv__provideIncludeUserNamesrelayprovider": require('./../provideIncludeUserNames.relayprovider') + "__relay_internal__pv__provideNumberOfFriendsrelayprovider": require('../provideNumberOfFriends.relayprovider'), + "__relay_internal__pv__provideIncludeUserNamesrelayprovider": require('../provideIncludeUserNames.relayprovider') }: {| +__relay_internal__pv__provideIncludeUserNamesrelayprovider: {| +get: () => boolean, @@ -174,15 +174,15 @@ return { "operationKind": "query", "text": "query withProvidedVariablesTest3Query(\n $__relay_internal__pv__provideNumberOfFriendsrelayprovider: Int!\n $__relay_internal__pv__provideIncludeUserNamesrelayprovider: Boolean!\n) {\n node(id: 4) {\n __typename\n ...withProvidedVariablesTest3Fragment\n id\n }\n}\n\nfragment withProvidedVariablesTest3Fragment on User {\n name @include(if: $__relay_internal__pv__provideIncludeUserNamesrelayprovider)\n friends(first: $__relay_internal__pv__provideNumberOfFriendsrelayprovider) {\n count\n }\n}\n", "providedVariables": { - "__relay_internal__pv__provideNumberOfFriendsrelayprovider": require('./../provideNumberOfFriends.relayprovider'), - "__relay_internal__pv__provideIncludeUserNamesrelayprovider": require('./../provideIncludeUserNames.relayprovider') + "__relay_internal__pv__provideNumberOfFriendsrelayprovider": require('../provideNumberOfFriends.relayprovider'), + "__relay_internal__pv__provideIncludeUserNamesrelayprovider": require('../provideIncludeUserNames.relayprovider') } } }; })(); if (__DEV__) { - (node/*: any*/).hash = "a0300c848560c03d4f2d7662ab5d27d3"; + (node/*: any*/).hash = "e9f1d13b64d707716ef131a7ec5c3ca0"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/util/__tests__/__generated__/withProvidedVariablesTest4Query.graphql.js b/packages/relay-runtime/util/__tests__/__generated__/withProvidedVariablesTest4Query.graphql.js index 6dd528e418ba3..ba55d7d1fd913 100644 --- a/packages/relay-runtime/util/__tests__/__generated__/withProvidedVariablesTest4Query.graphql.js +++ b/packages/relay-runtime/util/__tests__/__generated__/withProvidedVariablesTest4Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<4ea47aea3efd370c3a8321792c54990d>> * @flow * @lightSyntaxTransform * @nogrep @@ -31,8 +31,8 @@ export type withProvidedVariablesTest4Query = {| variables: withProvidedVariablesTest4Query$variables, |}; ({ - "__relay_internal__pv__provideNumberOfFriendsrelayprovider": require('./../provideNumberOfFriends.relayprovider'), - "__relay_internal__pv__provideIncludeUserNamesrelayprovider": require('./../provideIncludeUserNames.relayprovider') + "__relay_internal__pv__provideNumberOfFriendsrelayprovider": require('../provideNumberOfFriends.relayprovider'), + "__relay_internal__pv__provideIncludeUserNamesrelayprovider": require('../provideIncludeUserNames.relayprovider') }: {| +__relay_internal__pv__provideIncludeUserNamesrelayprovider: {| +get: () => boolean, @@ -206,15 +206,15 @@ return { "operationKind": "query", "text": "query withProvidedVariablesTest4Query(\n $__relay_internal__pv__provideNumberOfFriendsrelayprovider: Int!\n $__relay_internal__pv__provideIncludeUserNamesrelayprovider: Boolean!\n) {\n node(id: 4) {\n __typename\n ...withProvidedVariablesTest4Fragment1\n ...withProvidedVariablesTest4Fragment2\n id\n }\n}\n\nfragment withProvidedVariablesTest4Fragment1 on User {\n friends(first: $__relay_internal__pv__provideNumberOfFriendsrelayprovider) {\n count\n edges {\n node {\n name @include(if: $__relay_internal__pv__provideIncludeUserNamesrelayprovider)\n id\n }\n }\n }\n}\n\nfragment withProvidedVariablesTest4Fragment2 on User {\n name @include(if: $__relay_internal__pv__provideIncludeUserNamesrelayprovider)\n}\n", "providedVariables": { - "__relay_internal__pv__provideNumberOfFriendsrelayprovider": require('./../provideNumberOfFriends.relayprovider'), - "__relay_internal__pv__provideIncludeUserNamesrelayprovider": require('./../provideIncludeUserNames.relayprovider') + "__relay_internal__pv__provideNumberOfFriendsrelayprovider": require('../provideNumberOfFriends.relayprovider'), + "__relay_internal__pv__provideIncludeUserNamesrelayprovider": require('../provideIncludeUserNames.relayprovider') } } }; })(); if (__DEV__) { - (node/*: any*/).hash = "08096779ec00305df9771824e002669c"; + (node/*: any*/).hash = "b5809a8936443db3f6696119fe66e3ed"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/util/__tests__/__generated__/withProvidedVariablesTest5Query.graphql.js b/packages/relay-runtime/util/__tests__/__generated__/withProvidedVariablesTest5Query.graphql.js index d2216f8272afa..07110bd769b62 100644 --- a/packages/relay-runtime/util/__tests__/__generated__/withProvidedVariablesTest5Query.graphql.js +++ b/packages/relay-runtime/util/__tests__/__generated__/withProvidedVariablesTest5Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<9f59b2fcd357e043b5d5fa94238f3fe2>> + * @generated SignedSource<<34bd9ffc955c782f7e5871d6d90ed2a0>> * @flow * @lightSyntaxTransform * @nogrep @@ -30,8 +30,8 @@ export type withProvidedVariablesTest5Query = {| variables: withProvidedVariablesTest5Query$variables, |}; ({ - "__relay_internal__pv__provideRandomNumber_invalid1relayprovider": require('./../provideRandomNumber_invalid1.relayprovider'), - "__relay_internal__pv__provideRandomNumber_invalid2relayprovider": require('./../provideRandomNumber_invalid2.relayprovider') + "__relay_internal__pv__provideRandomNumber_invalid1relayprovider": require('../provideRandomNumber_invalid1.relayprovider'), + "__relay_internal__pv__provideRandomNumber_invalid2relayprovider": require('../provideRandomNumber_invalid2.relayprovider') }: {| +__relay_internal__pv__provideRandomNumber_invalid1relayprovider: {| +get: () => number, @@ -177,15 +177,15 @@ return { "operationKind": "query", "text": "query withProvidedVariablesTest5Query(\n $__relay_internal__pv__provideRandomNumber_invalid1relayprovider: Float!\n $__relay_internal__pv__provideRandomNumber_invalid2relayprovider: Float!\n) {\n node(id: 4) {\n __typename\n ...withProvidedVariablesTest5Fragment\n id\n }\n}\n\nfragment withProvidedVariablesTest5Fragment on User {\n profile_picture(scale: $__relay_internal__pv__provideRandomNumber_invalid1relayprovider) {\n uri\n }\n other_picture: profile_picture(scale: $__relay_internal__pv__provideRandomNumber_invalid2relayprovider) {\n uri\n }\n}\n", "providedVariables": { - "__relay_internal__pv__provideRandomNumber_invalid1relayprovider": require('./../provideRandomNumber_invalid1.relayprovider'), - "__relay_internal__pv__provideRandomNumber_invalid2relayprovider": require('./../provideRandomNumber_invalid2.relayprovider') + "__relay_internal__pv__provideRandomNumber_invalid1relayprovider": require('../provideRandomNumber_invalid1.relayprovider'), + "__relay_internal__pv__provideRandomNumber_invalid2relayprovider": require('../provideRandomNumber_invalid2.relayprovider') } } }; })(); if (__DEV__) { - (node/*: any*/).hash = "b9da4aca44074152f72d5208416421af"; + (node/*: any*/).hash = "fea93f1b453b7ed30e1b62dc0b32bc4e"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/util/__tests__/__generated__/withProvidedVariablesTest6Query.graphql.js b/packages/relay-runtime/util/__tests__/__generated__/withProvidedVariablesTest6Query.graphql.js index 6b3d98efd465a..bd2ca92f4e1e4 100644 --- a/packages/relay-runtime/util/__tests__/__generated__/withProvidedVariablesTest6Query.graphql.js +++ b/packages/relay-runtime/util/__tests__/__generated__/withProvidedVariablesTest6Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<33a7c304a92c212c1292fd71495c8cb8>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -30,7 +30,7 @@ export type withProvidedVariablesTest6Query = {| variables: withProvidedVariablesTest6Query$variables, |}; ({ - "__relay_internal__pv__provideRandomNumber_invalid1relayprovider": require('./../provideRandomNumber_invalid1.relayprovider') + "__relay_internal__pv__provideRandomNumber_invalid1relayprovider": require('../provideRandomNumber_invalid1.relayprovider') }: {| +__relay_internal__pv__provideRandomNumber_invalid1relayprovider: {| +get: () => number, @@ -151,14 +151,14 @@ return { "operationKind": "query", "text": "query withProvidedVariablesTest6Query(\n $__relay_internal__pv__provideRandomNumber_invalid1relayprovider: Float!\n) {\n node(id: 4) {\n __typename\n ...withProvidedVariablesTest6Fragment\n id\n }\n}\n\nfragment withProvidedVariablesTest6Fragment on User {\n profile_picture(scale: $__relay_internal__pv__provideRandomNumber_invalid1relayprovider) {\n uri\n }\n}\n", "providedVariables": { - "__relay_internal__pv__provideRandomNumber_invalid1relayprovider": require('./../provideRandomNumber_invalid1.relayprovider') + "__relay_internal__pv__provideRandomNumber_invalid1relayprovider": require('../provideRandomNumber_invalid1.relayprovider') } } }; })(); if (__DEV__) { - (node/*: any*/).hash = "8c7f3de6f49184530628833ac9970eef"; + (node/*: any*/).hash = "4f2c4062537ffda1cddd4cb6b75b6bfa"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-runtime/util/__tests__/getFragmentIdentifier-test.js b/packages/relay-runtime/util/__tests__/getFragmentIdentifier-test.js index 669f13b53b49d..25e3c7d8ef4d9 100644 --- a/packages/relay-runtime/util/__tests__/getFragmentIdentifier-test.js +++ b/packages/relay-runtime/util/__tests__/getFragmentIdentifier-test.js @@ -51,7 +51,7 @@ describe('getFragmentIdentifier', () => { gqlSingularQuery = graphql` query getFragmentIdentifierTestUserQuery($id: ID!, $scale: Float!) { node(id: $id) { - ...getFragmentIdentifierTestUserFragment + ...getFragmentIdentifierTestUserFragment @dangerously_unaliased_fixme } } `; @@ -62,6 +62,7 @@ describe('getFragmentIdentifier', () => { ) { node(id: $id) { ...getFragmentIdentifierTestUserFragmentWithArgs + @dangerously_unaliased_fixme @arguments(scaleLocal: $scale) } } @@ -229,7 +230,7 @@ describe('getFragmentIdentifier Optimized', () => { gqlSingularQuery = graphql` query getFragmentIdentifierTest1UserQuery($id: ID!, $scale: Float!) { node(id: $id) { - ...getFragmentIdentifierTest1UserFragment + ...getFragmentIdentifierTest1UserFragment @dangerously_unaliased_fixme } } `; @@ -241,6 +242,7 @@ describe('getFragmentIdentifier Optimized', () => { ) { node(id: $id) { ...getFragmentIdentifierTest1UserFragmentWithArgs + @dangerously_unaliased_fixme @arguments(scaleLocal: $scale) } } diff --git a/packages/relay-runtime/util/__tests__/getPaginationVariables-test.js b/packages/relay-runtime/util/__tests__/getPaginationVariables-test.js index 3e21c5227a658..a124c75038677 100644 --- a/packages/relay-runtime/util/__tests__/getPaginationVariables-test.js +++ b/packages/relay-runtime/util/__tests__/getPaginationVariables-test.js @@ -11,10 +11,12 @@ 'use strict'; +import type {Direction} from '../getPaginationVariables'; + const getPaginationVariables = require('../getPaginationVariables'); describe('getPaginationVariables', () => { - let direction; + let direction: Direction; describe('forward', () => { beforeEach(() => { diff --git a/packages/relay-runtime/util/__tests__/handlePotentialSnapshotErrors-test.js b/packages/relay-runtime/util/__tests__/handlePotentialSnapshotErrors-test.js index 60a6b51e6549a..8d22843c1277c 100644 --- a/packages/relay-runtime/util/__tests__/handlePotentialSnapshotErrors-test.js +++ b/packages/relay-runtime/util/__tests__/handlePotentialSnapshotErrors-test.js @@ -11,6 +11,7 @@ 'use strict'; +import {RelayFeatureFlags} from '../..'; import {handlePotentialSnapshotErrors} from '../handlePotentialSnapshotErrors'; import {createMockEnvironment} from 'relay-test-utils-internal'; @@ -21,6 +22,7 @@ describe('handlePotentialSnapshotErrors', () => { beforeEach(() => { relayFieldLogger = jest.fn(); environment = createMockEnvironment({relayFieldLogger}); + RelayFeatureFlags.ENABLE_UI_CONTEXT_ON_RELAY_LOGGER = false; }); it('should not throw in default case', () => { @@ -38,6 +40,7 @@ describe('handlePotentialSnapshotErrors', () => { owner: 'testOwner', fieldPath: 'testPath', handled: false, + uiContext: undefined, }, ]); }).toThrowError( @@ -47,23 +50,21 @@ describe('handlePotentialSnapshotErrors', () => { it('throws required even when missingData exists in errors array', () => { expect(() => { - handlePotentialSnapshotErrors( - environment, - - [ - { - kind: 'missing_required_field.throw', - owner: 'testOwner', - fieldPath: 'testPath', - handled: false, - }, - { - kind: 'missing_expected_data.log', - owner: 'RelayModernStoreSubscriptionsTest1Fragment', - fieldPath: '', - }, - ], - ); + handlePotentialSnapshotErrors(environment, [ + { + kind: 'missing_required_field.throw', + owner: 'testOwner', + fieldPath: 'testPath', + handled: false, + uiContext: undefined, + }, + { + kind: 'missing_expected_data.log', + owner: 'RelayModernStoreSubscriptionsTest1Fragment', + fieldPath: '', + uiContext: undefined, + }, + ]); }).toThrowError( /^Relay: Missing @required value at path 'testPath' in 'testOwner'./, ); @@ -77,11 +78,13 @@ describe('handlePotentialSnapshotErrors', () => { owner: 'testOwner', fieldPath: 'testPath', handled: false, + uiContext: undefined, }, { kind: 'missing_expected_data.log', owner: 'RelayModernStoreSubscriptionsTest1Fragment', fieldPath: '', + uiContext: undefined, }, { kind: 'relay_field_payload.error', @@ -94,6 +97,7 @@ describe('handlePotentialSnapshotErrors', () => { }, shouldThrow: false, handled: false, + uiContext: undefined, }, ]); }).toThrowError( @@ -107,6 +111,7 @@ describe('handlePotentialSnapshotErrors', () => { kind: 'missing_required_field.log', owner: 'testOwner', fieldPath: 'testPath', + uiContext: undefined, }, ]); @@ -128,6 +133,7 @@ describe('handlePotentialSnapshotErrors', () => { owner: '', fieldPath: '', handled: false, + uiContext: undefined, }, ]); }).toThrowError(/^Relay: Missing expected data at path '' in ''./); @@ -148,6 +154,7 @@ describe('handlePotentialSnapshotErrors', () => { kind: 'missing_expected_data.log', owner: '', fieldPath: '', + uiContext: undefined, }, ]); }).not.toThrow(); @@ -167,6 +174,7 @@ describe('handlePotentialSnapshotErrors', () => { kind: 'missing_expected_data.log', owner: '', fieldPath: '', + uiContext: undefined, }, ]); }).not.toThrow(); @@ -188,6 +196,7 @@ describe('handlePotentialSnapshotErrors', () => { kind: 'missing_expected_data.log', owner: '', fieldPath: '', + uiContext: undefined, }, ]); }).not.toThrow(); @@ -207,6 +216,7 @@ describe('handlePotentialSnapshotErrors', () => { }, shouldThrow: false, handled: false, + uiContext: undefined, }, ]); }).not.toThrow(); @@ -226,6 +236,92 @@ describe('handlePotentialSnapshotErrors', () => { }); }); + it("only logs but doesn't throw when explicit error handling disabled - including a set uiContext when flag is on", () => { + RelayFeatureFlags.ENABLE_UI_CONTEXT_ON_RELAY_LOGGER = true; + expect(() => { + handlePotentialSnapshotErrors( + environment, + [ + { + kind: 'relay_field_payload.error', + owner: 'testOwner', + fieldPath: 'testPath', + error: { + message: 'testMessage', + path: ['testPath'], + severity: 'CRITICAL', + }, + shouldThrow: false, + handled: false, + // flag is on so this will be populated in the logger + uiContext: undefined, + }, + ], + {arbitraryUIContextData: 'arbitraryUIContextDataValue'}, + ); + }).not.toThrow(); + + expect(relayFieldLogger).toHaveBeenCalledTimes(1); + expect(relayFieldLogger).toHaveBeenCalledWith({ + error: { + message: 'testMessage', + path: ['testPath'], + severity: 'CRITICAL', + }, + fieldPath: 'testPath', + kind: 'relay_field_payload.error', + owner: 'testOwner', + shouldThrow: false, + handled: false, + // flag was true so uiContext is populated + uiContext: {arbitraryUIContextData: 'arbitraryUIContextDataValue'}, + }); + }); + + it("only logs but doesn't throw when explicit error handling disabled - without a set uiContext when flag is off", () => { + RelayFeatureFlags.ENABLE_UI_CONTEXT_ON_RELAY_LOGGER = false; + expect(() => { + handlePotentialSnapshotErrors( + environment, + [ + { + kind: 'relay_field_payload.error', + owner: 'testOwner', + fieldPath: 'testPath', + error: { + message: 'testMessage', + path: ['testPath'], + severity: 'CRITICAL', + }, + shouldThrow: false, + handled: false, + // would have been overwritten with {arbitraryUIContextData: 'arbitraryUIContextDataValue'} + // had the flag been true + uiContext: undefined, + }, + ], + // when the flag is off, this will never have a value because the hook is never called + undefined, + ); + }).not.toThrow(); + + expect(relayFieldLogger).toHaveBeenCalledTimes(1); + expect(relayFieldLogger).toHaveBeenCalledWith({ + error: { + message: 'testMessage', + path: ['testPath'], + severity: 'CRITICAL', + }, + fieldPath: 'testPath', + kind: 'relay_field_payload.error', + owner: 'testOwner', + shouldThrow: false, + handled: false, + // flag was false so this is undefined + uiContext: undefined, + }); + }); + it("only logs twice but doesn't throw when explicit error handling disabled - and missing data", () => { expect(() => { handlePotentialSnapshotErrors(environment, [ @@ -240,11 +336,13 @@ describe('handlePotentialSnapshotErrors', () => { }, shouldThrow: false, handled: false, + uiContext: undefined, }, { kind: 'missing_expected_data.log', owner: '', fieldPath: '', + uiContext: undefined, }, ]); }).not.toThrow(); @@ -282,6 +380,7 @@ describe('handlePotentialSnapshotErrors', () => { }, shouldThrow: false, handled: false, + uiContext: undefined, }, ]); @@ -315,11 +414,13 @@ describe('handlePotentialSnapshotErrors', () => { }, shouldThrow: true, handled: false, + uiContext: undefined, }, { kind: 'missing_expected_data.log', owner: 'RelayModernStoreSubscriptionsTest1Fragment', fieldPath: '', + uiContext: undefined, }, ]); }).toThrowError( @@ -360,6 +461,7 @@ describe('handlePotentialSnapshotErrors', () => { error: Error('testError'), shouldThrow: false, handled: false, + uiContext: undefined, }, ]); @@ -384,10 +486,11 @@ describe('handlePotentialSnapshotErrors', () => { error: Error('testError'), shouldThrow: true, handled: false, + uiContext: undefined, }, ]); }).toThrowError( - /^Relay: Resolver error at path 'testPath' in 'testOwner'/, + "Relay: Resolver error at path 'testPath' in 'testOwner'. Message: testError", ); expect(relayFieldLogger).toHaveBeenCalledTimes(1); diff --git a/packages/relay-runtime/util/__tests__/withProvidedVariables-test.js b/packages/relay-runtime/util/__tests__/withProvidedVariables-test.js index 7d32e9160bbae..cbc0f93525e59 100644 --- a/packages/relay-runtime/util/__tests__/withProvidedVariables-test.js +++ b/packages/relay-runtime/util/__tests__/withProvidedVariables-test.js @@ -26,7 +26,7 @@ describe('withProvidedVariables', () => { const userQuery = graphql` query withProvidedVariablesTest1Query { node(id: 4) { - ...withProvidedVariablesTest1Fragment + ...withProvidedVariablesTest1Fragment @dangerously_unaliased_fixme } } `; @@ -62,6 +62,7 @@ describe('withProvidedVariables', () => { query withProvidedVariablesTest2Query($includeFriendsCount: Boolean!) { node(id: 4) { ...withProvidedVariablesTest2Fragment + @dangerously_unaliased_fixme @arguments(includeFriendsCount_: $includeFriendsCount) } } @@ -99,7 +100,7 @@ describe('withProvidedVariables', () => { const userQuery = graphql` query withProvidedVariablesTest3Query { node(id: 4) { - ...withProvidedVariablesTest3Fragment + ...withProvidedVariablesTest3Fragment @dangerously_unaliased_fixme } } `; @@ -142,8 +143,8 @@ describe('withProvidedVariables', () => { const userQuery = graphql` query withProvidedVariablesTest4Query { node(id: 4) { - ...withProvidedVariablesTest4Fragment1 - ...withProvidedVariablesTest4Fragment2 + ...withProvidedVariablesTest4Fragment1 @dangerously_unaliased_fixme + ...withProvidedVariablesTest4Fragment2 @dangerously_unaliased_fixme } } `; @@ -203,7 +204,7 @@ describe('withProvidedVariables', () => { const userQuery = graphql` query withProvidedVariablesTest5Query { node(id: 4) { - ...withProvidedVariablesTest5Fragment + ...withProvidedVariablesTest5Fragment @dangerously_unaliased_fixme } } `; @@ -269,7 +270,7 @@ describe('withProvidedVariables', () => { const userQuery = graphql` query withProvidedVariablesTest6Query { node(id: 4) { - ...withProvidedVariablesTest6Fragment + ...withProvidedVariablesTest6Fragment @dangerously_unaliased_fixme } } `; diff --git a/packages/relay-runtime/util/getPaginationVariables.js b/packages/relay-runtime/util/getPaginationVariables.js index a38fb729463dd..7a5247bd9225e 100644 --- a/packages/relay-runtime/util/getPaginationVariables.js +++ b/packages/relay-runtime/util/getPaginationVariables.js @@ -56,6 +56,7 @@ function getPaginationVariables( ...baseVariables, ...extraVariables, [backwardMetadata.cursor]: cursor, + // $FlowFixMe[incompatible-type] [backwardMetadata.count]: count, }; if (forwardMetadata && forwardMetadata.cursor) { @@ -92,6 +93,7 @@ function getPaginationVariables( ...baseVariables, ...extraVariables, [forwardMetadata.cursor]: cursor, + // $FlowFixMe[incompatible-type] [forwardMetadata.count]: count, }; if (backwardMetadata && backwardMetadata.cursor) { diff --git a/packages/relay-runtime/util/handlePotentialSnapshotErrors.js b/packages/relay-runtime/util/handlePotentialSnapshotErrors.js index 8e975827feec3..d5dba580f7d17 100644 --- a/packages/relay-runtime/util/handlePotentialSnapshotErrors.js +++ b/packages/relay-runtime/util/handlePotentialSnapshotErrors.js @@ -12,8 +12,8 @@ 'use strict'; import type { - ErrorResponseField, - ErrorResponseFields, + FieldError, + FieldErrors, IEnvironment, } from '../store/RelayStoreTypes'; @@ -21,21 +21,32 @@ const invariant = require('invariant'); function handleFieldErrors( environment: IEnvironment, - errorResponseFields: ErrorResponseFields, + fieldErrors: FieldErrors, + loggingContext: mixed | void, ) { - for (const fieldError of errorResponseFields) { + for (const fieldError of fieldErrors) { // First we log all events. Note that the logger may opt to throw its own // error here if it wants to throw an error that is better integrated into // site's error handling infrastructure. - environment.relayFieldLogger(fieldError); + + // Awkward. We don't want to attach the ui context in RelayReader where we + // create the event, but it means we need to add it here instead of just + // passing the event through. + + environment.relayFieldLogger({ + // the uiContext on fieldError undefined *always*, + ...fieldError, + // and this is where we assign loggingContext to uiContext to populate it + uiContext: loggingContext, + }); } - for (const fieldError of errorResponseFields) { + for (const fieldError of fieldErrors) { if (eventShouldThrow(fieldError)) { switch (fieldError.kind) { case 'relay_resolver.error': throw new Error( - `Relay: Resolver error at path '${fieldError.fieldPath}' in '${fieldError.owner}'.`, + `Relay: Resolver error at path '${fieldError.fieldPath}' in '${fieldError.owner}'. Message: ${fieldError.error.message}`, ); case 'relay_field_payload.error': throw new Error( @@ -63,7 +74,7 @@ function handleFieldErrors( } } -function eventShouldThrow(event: ErrorResponseField): boolean { +function eventShouldThrow(event: FieldError): boolean { switch (event.kind) { case 'relay_resolver.error': case 'relay_field_payload.error': @@ -82,14 +93,15 @@ function eventShouldThrow(event: ErrorResponseField): boolean { function handlePotentialSnapshotErrors( environment: IEnvironment, - errorResponseFields: ?ErrorResponseFields, + fieldErrors: ?FieldErrors, + loggingContext: mixed | void, ) { /** * Inside handleFieldErrors, we check for throwOnFieldError - but this fn logs the error anyway by default * which is why this still should run in any case there's errors. */ - if (errorResponseFields != null) { - handleFieldErrors(environment, errorResponseFields); + if (fieldErrors != null) { + handleFieldErrors(environment, fieldErrors, loggingContext); } } diff --git a/packages/relay-runtime/util/registerEnvironmentWithDevTools.js b/packages/relay-runtime/util/registerEnvironmentWithDevTools.js index e9a782191b154..18adf2c3ad25a 100644 --- a/packages/relay-runtime/util/registerEnvironmentWithDevTools.js +++ b/packages/relay-runtime/util/registerEnvironmentWithDevTools.js @@ -19,8 +19,10 @@ function registerEnvironmentWithDevTools(environment: IEnvironment): void { const _global = typeof global !== 'undefined' ? global - : typeof window !== 'undefined' - ? window + : // $FlowFixMe[cannot-resolve-name] + typeof window !== 'undefined' + ? // $FlowFixMe[cannot-resolve-name] + window : undefined; // $FlowFixMe[incompatible-use] D61394600 diff --git a/packages/relay-runtime/util/withProvidedVariables.js b/packages/relay-runtime/util/withProvidedVariables.js index dc813400d676c..fdf2a3c993dfd 100644 --- a/packages/relay-runtime/util/withProvidedVariables.js +++ b/packages/relay-runtime/util/withProvidedVariables.js @@ -30,6 +30,7 @@ function withProvidedVariables( ): Variables { if (providedVariables != null) { const operationVariables: {[string]: mixed} = {}; + // $FlowFixMe[unsafe-object-assign] Object.assign(operationVariables, userSuppliedVariables); Object.keys(providedVariables).forEach((varName: string) => { const providerFunction = providedVariables[varName].get; diff --git a/packages/relay-runtime/util/withStartAndDuration.js b/packages/relay-runtime/util/withStartAndDuration.js index 07dff3435095c..a3aa8fe8911c9 100644 --- a/packages/relay-runtime/util/withStartAndDuration.js +++ b/packages/relay-runtime/util/withStartAndDuration.js @@ -12,11 +12,14 @@ 'use strict'; const isPerformanceNowAvailable = + // $FlowFixMe[cannot-resolve-name] typeof window !== 'undefined' && + // $FlowFixMe[cannot-resolve-name] typeof window?.performance?.now === 'function'; function currentTimestamp(): number { if (isPerformanceNowAvailable) { + // $FlowFixMe[cannot-resolve-name] return window.performance.now(); } return Date.now(); diff --git a/packages/relay-test-utils-internal/__tests__/getOutputForFixture-test.js b/packages/relay-test-utils-internal/__tests__/getOutputForFixture-test.js index 2b2182f0fe0c2..207084d5433c9 100644 --- a/packages/relay-test-utils-internal/__tests__/getOutputForFixture-test.js +++ b/packages/relay-test-utils-internal/__tests__/getOutputForFixture-test.js @@ -14,34 +14,34 @@ const getOutputForFixture = require('../getOutputForFixture'); describe('getOutputForFixture', () => { - it('should throw when there is #expected-to-throw but operation succeeded', () => { + it('should throw when there is #expected-to-throw but operation succeeded', async () => { const RESULT_STRING = 'SUCCESS_STRING'; - expect(() => { - getOutputForFixture( + await expect(async () => { + await getOutputForFixture( '# expected-to-throw\n query{}', () => RESULT_STRING, 'test.graphql', ); - }).toThrow( + }).rejects.toThrow( `Expected test file 'test.graphql' to throw, but it passed:\n${RESULT_STRING}`, ); }); - it('should throw when there is no #expected-to-throw but operation failed', () => { - expect(() => { - getOutputForFixture( + it('should throw when there is no #expected-to-throw but operation failed', async () => { + await expect(async () => { + await getOutputForFixture( 'fragment tnemgarf{}', () => { throw new Error('my error'); }, 'test.graphql', ); - }).toThrow('my error'); + }).rejects.toThrow('my error'); }); - it('should pass when there is #expected-to-throw and operation failed', () => { + it('should pass when there is #expected-to-throw and operation failed', async () => { const RESULT_STRING = 'ERROR_STRING'; - const output = getOutputForFixture( + const output = await getOutputForFixture( '# expected-to-throw\n query{}', () => { throw new Error(RESULT_STRING); @@ -51,9 +51,9 @@ describe('getOutputForFixture', () => { expect(output).toEqual(`THROWN EXCEPTION:\n\nError: ${RESULT_STRING}`); }); - it('should pass when there is no expected-to-throw and operation succeeded', () => { + it('should pass when there is no expected-to-throw and operation succeeded', async () => { const RESULT_STRING = 'SUCCESS_STRING'; - const output = getOutputForFixture( + const output = await getOutputForFixture( 'fragment tnemgarf{}', () => RESULT_STRING, 'test.graphql', diff --git a/packages/relay-test-utils-internal/describeWithFeatureFlags.js b/packages/relay-test-utils-internal/describeWithFeatureFlags.js index fd41453bcb671..f01ea5d907d75 100644 --- a/packages/relay-test-utils-internal/describeWithFeatureFlags.js +++ b/packages/relay-test-utils-internal/describeWithFeatureFlags.js @@ -56,10 +56,12 @@ function describeWithFeatureFlags( beforeEach(() => { const {RelayFeatureFlags} = require('relay-runtime'); originalFlags = {...RelayFeatureFlags}; + // $FlowFixMe[unsafe-object-assign] Object.assign(RelayFeatureFlags, flags); }); afterEach(() => { const {RelayFeatureFlags} = require('relay-runtime'); // re-import in case of jest module resets + // $FlowFixMe[unsafe-object-assign] Object.assign(RelayFeatureFlags, originalFlags); }); body(); diff --git a/packages/relay-test-utils-internal/generateTestsFromFixtures.js b/packages/relay-test-utils-internal/generateTestsFromFixtures.js index 53bdc9186fe6e..d341f6372677b 100644 --- a/packages/relay-test-utils-internal/generateTestsFromFixtures.js +++ b/packages/relay-test-utils-internal/generateTestsFromFixtures.js @@ -40,7 +40,7 @@ expect.addSnapshotSerializer({ */ function generateTestsFromFixtures( fixturesPath: string, - operation: (input: string) => string, + operation: (input: string) => Promise, ): void { let fixtures = fs.readdirSync(fixturesPath); @@ -56,9 +56,9 @@ function generateTestsFromFixtures( ); fixtures = onlyFixtures; } - test.each(fixtures)('matches expected output: %s', file => { + test.each(fixtures)('matches expected output: %s', async file => { const input = fs.readFileSync(path.join(fixturesPath, file), 'utf8'); - const output = getOutputForFixture(input, operation, file); + const output = await getOutputForFixture(input, operation, file); expect({ [FIXTURE_TAG]: true, input, diff --git a/packages/relay-test-utils-internal/getOutputForFixture.js b/packages/relay-test-utils-internal/getOutputForFixture.js index 8b80f20a76489..ae0948b90d776 100644 --- a/packages/relay-test-utils-internal/getOutputForFixture.js +++ b/packages/relay-test-utils-internal/getOutputForFixture.js @@ -11,17 +11,17 @@ 'use strict'; -function getOutputForFixture( +async function getOutputForFixture( input: string, - operation: (input: string) => string, + operation: (input: string) => Promise | string, file: string, -): string { +): Promise { const shouldThrow = /^# *expected-to-throw/.test(input) || /\.error\.\w+$/.test(file); if (shouldThrow) { let result; try { - result = operation(input); + result = await operation(input); } catch (e) { return `THROWN EXCEPTION:\n\n${e.toString()}`; } diff --git a/packages/relay-test-utils-internal/index.js b/packages/relay-test-utils-internal/index.js index d48664138fbf2..e9d198c8511c5 100644 --- a/packages/relay-test-utils-internal/index.js +++ b/packages/relay-test-utils-internal/index.js @@ -49,6 +49,7 @@ const {createMockEnvironment, unwrapContainer} = require('relay-test-utils'); function cannotReadPropertyOfUndefined__DEPRECATED( propertyName: string, ): string { + // $FlowFixMe[cannot-resolve-name] const matches = process.version.match(/^v(\d+)\./); const majorVersion = matches == null ? null : parseInt(matches[1], 10); if (majorVersion == null || majorVersion < 16) { diff --git a/packages/relay-test-utils-internal/package.json b/packages/relay-test-utils-internal/package.json index 30c4bfd9940d0..a0f12e29d4be6 100644 --- a/packages/relay-test-utils-internal/package.json +++ b/packages/relay-test-utils-internal/package.json @@ -1,7 +1,7 @@ { "name": "relay-test-utils-internal", "description": "Internal utilities for testing Relay.", - "version": "18.2.0", + "version": "20.1.1", "keywords": [ "graphql", "relay" @@ -17,7 +17,7 @@ "dependencies": { "@babel/runtime": "^7.25.0", "fbjs": "^3.0.2", - "relay-runtime": "18.2.0" + "relay-runtime": "20.1.1" }, "directories": { "": "./" diff --git a/packages/relay-test-utils-internal/schema-extensions/ClientAccount.graphql b/packages/relay-test-utils-internal/schema-extensions/ClientAccount.graphql new file mode 100644 index 0000000000000..be946eea0dda5 --- /dev/null +++ b/packages/relay-test-utils-internal/schema-extensions/ClientAccount.graphql @@ -0,0 +1,3 @@ +type ClientAccount { + id: ID +} diff --git a/packages/relay-test-utils-internal/testschema.graphql b/packages/relay-test-utils-internal/testschema.graphql index 6eb3c5484ade7..fecbe642096e1 100644 --- a/packages/relay-test-utils-internal/testschema.graphql +++ b/packages/relay-test-utils-internal/testschema.graphql @@ -967,7 +967,7 @@ type PlainUserRenderer { } union UserNameRenderer = - PlainUserNameRenderer + | PlainUserNameRenderer | MarkdownUserNameRenderer | CustomNameRenderer diff --git a/packages/relay-test-utils/RelayMockPayloadGenerator.js b/packages/relay-test-utils/RelayMockPayloadGenerator.js index 938c273fbf113..5401987dac198 100644 --- a/packages/relay-test-utils/RelayMockPayloadGenerator.js +++ b/packages/relay-test-utils/RelayMockPayloadGenerator.js @@ -71,7 +71,7 @@ type Traversable = { +args: ?{[string]: mixed, ...}, }; type MockData = {[string]: mixed, ...}; -type MockResolverContext = { +export type MockResolverContext = { +parentType: ?string, +name: ?string, +alias: ?string, @@ -82,7 +82,7 @@ type MockResolver = ( context: MockResolverContext, generateId: () => number, ) => mixed; -export type MockResolvers = {[typeName: string]: MockResolver, ...}; +export type MockResolvers = {+[typeName: string]: MockResolver, ...}; type SelectionMetadata = { [selectionPath: string]: { @@ -536,7 +536,9 @@ class RelayMockPayloadGenerator { mockData = { ...mockData, [TYPENAME_KEY]: typeName, + // $FlowFixMe[invalid-computed-prop] [getModuleOperationKey(documentName)]: operation.name, + // $FlowFixMe[invalid-computed-prop] [getModuleComponentKey(documentName)]: defaultValues.__module_component, ...this._traverseSelections( diff --git a/packages/relay-test-utils/RelayModernMockEnvironment.js b/packages/relay-test-utils/RelayModernMockEnvironment.js index eda5f493a6af9..e81e6183654ab 100644 --- a/packages/relay-test-utils/RelayModernMockEnvironment.js +++ b/packages/relay-test-utils/RelayModernMockEnvironment.js @@ -259,6 +259,11 @@ function createMockEnvironment( pendingRequests = pendingRequests.filter( pending => !areEqual(pending, nextRequest), ); + const currentOperation = pendingOperations.find( + op => + areEqual(op.request.node.params, request) && + areEqual(op.request.variables, variables), + ); pendingOperations = pendingOperations.filter( op => op !== currentOperation, ); diff --git a/packages/relay-test-utils/RelayResolverTestUtils.js b/packages/relay-test-utils/RelayResolverTestUtils.js index b6b084f19a78b..37b2976ad7ff0 100644 --- a/packages/relay-test-utils/RelayResolverTestUtils.js +++ b/packages/relay-test-utils/RelayResolverTestUtils.js @@ -34,11 +34,11 @@ const { * expect(actual).toEqual(expectedValue) * ``` **/ -function testResolver( - resolver: ({$data: D, $fragmentRefs: any, $fragmentSpreads: any}) => Ret, +function testResolver( + resolver: ({$data?: D, $fragmentRefs: any, $fragmentSpreads: any}) => Ret, // indexed_access is not yet enabled for this code base. Once it is, this can // become: `Key['$data']` - fragmentData: $Diff, + fragmentData: NoInfer, '$fragmentType'>>, ): Ret { const readFragment = ResolverFragments.readFragment; // $FlowFixMe: a test utility, so... YOLO!! diff --git a/packages/relay-test-utils/__tests__/RelayMockEnvironment-test.js b/packages/relay-test-utils/__tests__/RelayMockEnvironment-test.js index 8619e7d056968..ba53bac149d00 100644 --- a/packages/relay-test-utils/__tests__/RelayMockEnvironment-test.js +++ b/packages/relay-test-utils/__tests__/RelayMockEnvironment-test.js @@ -157,7 +157,9 @@ describe('when generating multiple payloads for deferred data', () => { node(id: $id) { id ... on User { - ...RelayMockEnvironmentTestWithDeferFragment_user @defer + ...RelayMockEnvironmentTestWithDeferFragment_user + @dangerously_unaliased_fixme + @defer } } } diff --git a/packages/relay-test-utils/__tests__/RelayMockEnvironmentWithComponents-test.js b/packages/relay-test-utils/__tests__/RelayMockEnvironmentWithComponents-test.js index ebe7a9c9314da..d25dc0158a19c 100644 --- a/packages/relay-test-utils/__tests__/RelayMockEnvironmentWithComponents-test.js +++ b/packages/relay-test-utils/__tests__/RelayMockEnvironmentWithComponents-test.js @@ -65,8 +65,10 @@ describe('ReactRelayTestMocker with Containers', () => { if (props) { return `My id ${props.user.id} and name is ${props.user.name}`; } else if (error) { + // $FlowFixMe[incompatible-type] return
{error.message}
; } + // $FlowFixMe[incompatible-type] return
Loading...
; }} /> @@ -176,6 +178,7 @@ describe('ReactRelayTestMocker with Containers', () => { id name ...RelayMockEnvironmentWithComponentsTestProminentSolutionFragment + @dangerously_unaliased_fixme } } `; @@ -194,6 +197,7 @@ describe('ReactRelayTestMocker with Containers', () => { // to get flow to accept this typing. (props: $FlowFixMe) => { return ( + // $FlowFixMe[incompatible-type] { id name ...RelayMockEnvironmentWithComponentsTestRobustAwesomenessFragment + @dangerously_unaliased_fixme } } `; @@ -309,6 +314,7 @@ describe('ReactRelayTestMocker with Containers', () => { const [isLoading, setIsLoading] = useState(props.relay.isLoading()); return ( <> + {/* $FlowFixMe[incompatible-type] */}
    {props.user.friends.edges.map(({node, cursor}) => { return ( @@ -319,7 +325,11 @@ describe('ReactRelayTestMocker with Containers', () => { ); })}
- {isLoading &&
Loading more...
} + { + // $FlowFixMe[incompatible-type] + isLoading &&
Loading more...
+ } + {/* $FlowFixMe[incompatible-type] */}
); } else if (error) { + // $FlowFixMe[incompatible-type] return
{error.message}
; } + // $FlowFixMe[incompatible-type] return
Loading...
; }} /> @@ -546,6 +558,7 @@ describe('ReactRelayTestMocker with Containers', () => { ) @relay_test_operation { node(id: $id) { ...RelayMockEnvironmentWithComponentsTestUsefulAwesomenessFragment + @dangerously_unaliased_fixme } } `; @@ -561,9 +574,14 @@ describe('ReactRelayTestMocker with Containers', () => { const [isLoading, setIsLoading] = useState(false); return ( <> + {/* $FlowFixMe[incompatible-type] */}
{props.page.name}
Websites: {props.page.websites}
- {isLoading &&
Refetching...
} + { + // $FlowFixMe[incompatible-type] + isLoading &&
Refetching...
+ } + {/* $FlowFixMe[incompatible-type] */} ); @@ -805,8 +828,10 @@ describe('ReactRelayTestMocker with Containers', () => { ); } else if (error) { + // $FlowFixMe[incompatible-type] return
{error.message}
; } + // $FlowFixMe[incompatible-type] return
Loading...
; }} /> @@ -944,10 +969,13 @@ describe('ReactRelayTestMocker with Containers', () => { variables={{}} render={({error, props}) => { if (props) { + // $FlowFixMe[incompatible-type] return
{props.viewer.actor.name}
; } else if (error) { + // $FlowFixMe[incompatible-type] return
{error.message}
; } + // $FlowFixMe[incompatible-type] return
Loading...
; }} /> @@ -997,6 +1025,7 @@ describe('ReactRelayTestMocker with Containers', () => { ) { feedback: node(id: $id) { ...RelayMockEnvironmentWithComponentsTestImpactfulAwesomenessFragment + @dangerously_unaliased_fixme } } `; @@ -1041,10 +1070,12 @@ describe('ReactRelayTestMocker with Containers', () => { return (
{props.feedback.message.text} + {/* $FlowFixMe[incompatible-type] Error found when typing DOM + * intrinsics */} { /> ); } else if (error) { + // $FlowFixMe[incompatible-type] return
{error.message}
; } + // $FlowFixMe[incompatible-type] return
Loading...
; }} /> @@ -1184,6 +1217,7 @@ describe('ReactRelayTestMocker with Containers', () => { variables={{userId: 'my-user-id'}} render={({error, props}) => { if (props) { + // $FlowFixMe[incompatible-type] return
{props.user.name}
; } else if (error) { return
{error.message}
; @@ -1197,6 +1231,7 @@ describe('ReactRelayTestMocker with Containers', () => { variables={{pageId: 'my-page-id'}} render={({error, props}) => { if (props) { + // $FlowFixMe[incompatible-type] return
{props.page.name}
; } else if (error) { return
{error.message}
; @@ -1290,8 +1325,10 @@ describe('ReactRelayTestMocker with Containers', () => { variables={{userId: 'my-user-id'}} render={({error, props}) => { if (props) { + // $FlowFixMe[incompatible-type] return
{props.user.name}
; } else if (error) { + // $FlowFixMe[incompatible-type] return
{error.message}
; } return
Loading...
; diff --git a/packages/relay-test-utils/__tests__/RelayMockPayloadGenerator-test.js b/packages/relay-test-utils/__tests__/RelayMockPayloadGenerator-test.js index de489e8c50ef7..197d1083f6867 100644 --- a/packages/relay-test-utils/__tests__/RelayMockPayloadGenerator-test.js +++ b/packages/relay-test-utils/__tests__/RelayMockPayloadGenerator-test.js @@ -53,7 +53,7 @@ test('generate mock for simple fragment', () => { testGeneratedData(graphql` query RelayMockPayloadGeneratorTest1Query { node(id: "my-id") { - ...RelayMockPayloadGeneratorTestFragment + ...RelayMockPayloadGeneratorTestFragment @dangerously_unaliased_fixme } } `); @@ -120,7 +120,7 @@ test('generate mock with inline fragment', () => { testGeneratedData(graphql` query RelayMockPayloadGeneratorTest3Query($condition: Boolean!) { node(id: "my-id") { - ...RelayMockPayloadGeneratorTest2Fragment + ...RelayMockPayloadGeneratorTest2Fragment @dangerously_unaliased_fixme } } `); @@ -168,7 +168,7 @@ test('generate mock with condition (and other complications)', () => { $hideAuthorUsername: Boolean! ) { node(id: "my-id") { - ...RelayMockPayloadGeneratorTest3Fragment + ...RelayMockPayloadGeneratorTest3Fragment @dangerously_unaliased_fixme } } `); @@ -196,11 +196,12 @@ test('generate mock with connection', () => { node { id ...RelayMockPayloadGeneratorTest4Fragment + @dangerously_unaliased_fixme @skip(if: $skipUserInConnection) } } } - ...RelayMockPayloadGeneratorTest4Fragment + ...RelayMockPayloadGeneratorTest4Fragment @dangerously_unaliased_fixme } } } @@ -211,7 +212,7 @@ test('generate mock with connection', () => { $skipUserInConnection: Boolean! ) { node(id: "my-id") { - ...RelayMockPayloadGeneratorTest5Fragment + ...RelayMockPayloadGeneratorTest5Fragment @dangerously_unaliased_fixme } } `); @@ -232,7 +233,7 @@ test('generate basic mock data', () => { graphql` query RelayMockPayloadGeneratorTest6Query { node(id: "my-id") { - ...RelayMockPayloadGeneratorTest6Fragment + ...RelayMockPayloadGeneratorTest6Fragment @dangerously_unaliased_fixme } } `, @@ -254,7 +255,7 @@ test('generate mock using custom mock functions', () => { graphql` query RelayMockPayloadGeneratorTest7Query { node(id: "my-id") { - ...RelayMockPayloadGeneratorTest7Fragment + ...RelayMockPayloadGeneratorTest7Fragment @dangerously_unaliased_fixme } } `, @@ -288,7 +289,7 @@ test('generate mock using custom mock functions for object type', () => { graphql` query RelayMockPayloadGeneratorTest8Query { node(id: "my-id") { - ...RelayMockPayloadGeneratorTest8Fragment + ...RelayMockPayloadGeneratorTest8Fragment @dangerously_unaliased_fixme } } `, @@ -317,7 +318,7 @@ test('generate mock for objects without concrete type', () => { graphql` query RelayMockPayloadGeneratorTest9Query { node(id: "my-id") { - ...RelayMockPayloadGeneratorTest9Fragment + ...RelayMockPayloadGeneratorTest9Fragment @dangerously_unaliased_fixme } } `, @@ -356,6 +357,7 @@ test('generate mock using custom mock functions for object type (multiple object query RelayMockPayloadGeneratorTest10Query { node(id: "my-id") { ...RelayMockPayloadGeneratorTest10Fragment + @dangerously_unaliased_fixme } } `, @@ -465,6 +467,7 @@ test('generate mock with manual mock for objects', () => { query RelayMockPayloadGeneratorTest12Query { node(id: "my-id") { ...RelayMockPayloadGeneratorTest12Fragment + @dangerously_unaliased_fixme } } `, @@ -547,6 +550,7 @@ test('generate mock and verify arguments in the context', () => { ) { node(id: "my-id") { ...RelayMockPayloadGeneratorTest14Fragment + @dangerously_unaliased_fixme } } `, @@ -583,7 +587,9 @@ test('generate mock for fragment with @argumentsDefinition', () => { graphql` query RelayMockPayloadGeneratorTest15Query($scale: Float = 1.0) { node(id: "my-id") { - ...RelayMockPayloadGeneratorTest15Fragment @arguments(withName: true) + ...RelayMockPayloadGeneratorTest15Fragment + @dangerously_unaliased_fixme + @arguments(withName: true) } } `, @@ -649,7 +655,7 @@ test('generate mock for multiple fragment spreads', () => { id } myActor: actor { - ...RelayMockPayloadGeneratorTest17Fragment + ...RelayMockPayloadGeneratorTest17Fragment @dangerously_unaliased_fixme } ...RelayMockPayloadGeneratorTest18Fragment ...RelayMockPayloadGeneratorTest19Fragment @@ -658,7 +664,7 @@ test('generate mock for multiple fragment spreads', () => { testGeneratedData(graphql` query RelayMockPayloadGeneratorTest17Query { node(id: "my-id") { - ...RelayMockPayloadGeneratorTest20Fragment + ...RelayMockPayloadGeneratorTest20Fragment @dangerously_unaliased_fixme } } `); @@ -744,7 +750,9 @@ test('generate mock for with directives and handlers', () => { $RELAY_INCREMENTAL_DELIVERY: Boolean = false ) { node(id: "my-id") { - ...RelayMockPayloadGeneratorTest22Fragment @arguments(condition: true) + ...RelayMockPayloadGeneratorTest22Fragment + @dangerously_unaliased_fixme + @arguments(condition: true) } } `); @@ -783,7 +791,7 @@ test('should return `null` for selection if that is specified in default values' id } myActor: actor { - ...RelayMockPayloadGeneratorTest24Fragment + ...RelayMockPayloadGeneratorTest24Fragment @dangerously_unaliased_fixme } ...RelayMockPayloadGeneratorTest25Fragment } @@ -793,6 +801,7 @@ test('should return `null` for selection if that is specified in default values' query RelayMockPayloadGeneratorTest19Query { node(id: "my-id") { ...RelayMockPayloadGeneratorTest27Fragment + @dangerously_unaliased_fixme } } `, @@ -840,6 +849,7 @@ describe('with @relay_test_operation', () => { query RelayMockPayloadGeneratorTest21Query @relay_test_operation { node(id: "my-id") { ...RelayMockPayloadGeneratorTest28Fragment + @dangerously_unaliased_fixme } } `); @@ -952,7 +962,9 @@ describe('with @relay_test_operation', () => { query RelayMockPayloadGeneratorTest26Query @relay_test_operation { node(id: "my-id") { ...RelayMockPayloadGeneratorTest29Fragment + @dangerously_unaliased_fixme ...RelayMockPayloadGeneratorTest30Fragment + @dangerously_unaliased_fixme } } `, @@ -1461,6 +1473,7 @@ describe('with @relay_test_operation', () => { query RelayMockPayloadGeneratorTest44Query @relay_test_operation { node(id: "my-id") { ...RelayMockPayloadGeneratorTestNameRendererFragment + @dangerously_unaliased_fixme } } `, @@ -1508,6 +1521,7 @@ describe('with @relay_test_operation', () => { query RelayMockPayloadGeneratorTest45Query @relay_test_operation { node(id: "my-id") { ...RelayMockPayloadGeneratorTest31Fragment + @dangerously_unaliased_fixme } } `, @@ -1555,6 +1569,7 @@ describe('with @relay_test_operation', () => { query RelayMockPayloadGeneratorTest46Query @relay_test_operation { node(id: "my-id") { ...RelayMockPayloadGeneratorTest32Fragment + @dangerously_unaliased_fixme } } `, @@ -1593,6 +1608,7 @@ describe('with @relay_test_operation', () => { query RelayMockPayloadGeneratorTest47Query @relay_test_operation { node(id: "my-id") { ...RelayMockPayloadGeneratorTest33Fragment + @dangerously_unaliased_fixme } } `, @@ -1633,6 +1649,7 @@ describe('with @relay_test_operation', () => { query RelayMockPayloadGeneratorTest48Query @relay_test_operation { node(id: "my-id") { ...RelayMockPayloadGeneratorTest34Fragment + @dangerously_unaliased_fixme } } `, @@ -1658,7 +1675,9 @@ describe('with @relay_test_operation', () => { id client_name client_code - ...RelayMockPayloadGeneratorTest43SubFragment @defer + ...RelayMockPayloadGeneratorTest43SubFragment + @dangerously_unaliased_fixme + @defer } } } @@ -1709,7 +1728,9 @@ test('Query with @no_inline fragment spread with literal argument', () => { const query = graphql` query RelayMockPayloadGeneratorTest58Query { node(id: "4") { - ...RelayMockPayloadGeneratorTest_fragment59 @arguments(cond: true) + ...RelayMockPayloadGeneratorTest_fragment59 + @dangerously_unaliased_fixme + @arguments(cond: true) } } `; @@ -1730,7 +1751,9 @@ test('Query with @no_inline fragment spread with variable argument', () => { const query = graphql` query RelayMockPayloadGeneratorTest60Query($cond: Boolean!) { node(id: "4") { - ...RelayMockPayloadGeneratorTest_fragment61 @arguments(cond: $cond) + ...RelayMockPayloadGeneratorTest_fragment61 + @dangerously_unaliased_fixme + @arguments(cond: $cond) } } `; @@ -1782,7 +1805,9 @@ test('generate mock for deferred fragments', () => { query RelayMockPayloadGeneratorTest61Query { node(id: "my-id") { id - ...RelayMockPayloadGeneratorTest61Fragment @defer + ...RelayMockPayloadGeneratorTest61Fragment + @dangerously_unaliased_fixme + @defer } } `, @@ -1808,7 +1833,9 @@ test('generate mock for deferred fragments with if condition true', () => { query RelayMockPayloadGeneratorTest62Query { node(id: "my-id") { id - ...RelayMockPayloadGeneratorTest62Fragment @defer(if: true) + ...RelayMockPayloadGeneratorTest62Fragment + @dangerously_unaliased_fixme + @defer(if: true) } } `, @@ -1828,7 +1855,9 @@ test('generate mock for deferred fragments with if condition false', () => { query RelayMockPayloadGeneratorTest63Query { node(id: "my-id") { id - ...RelayMockPayloadGeneratorTest63Fragment @defer(if: false) + ...RelayMockPayloadGeneratorTest63Fragment + @dangerously_unaliased_fixme + @defer(if: false) } } `, @@ -1953,6 +1982,7 @@ test('should generate data for @match with PlainUserNameRenderer_name and use de query RelayMockPayloadGeneratorTest67Query @relay_test_operation { node(id: "my-id") { ...RelayMockPayloadGeneratorTest67Fragment + @dangerously_unaliased_fixme } } `, diff --git a/packages/relay-test-utils/__tests__/__generated__/RelayMockEnvironmentTestWithDeferQuery.graphql.js b/packages/relay-test-utils/__tests__/__generated__/RelayMockEnvironmentTestWithDeferQuery.graphql.js index 0f847d2674021..0f400b411ecf4 100644 --- a/packages/relay-test-utils/__tests__/__generated__/RelayMockEnvironmentTestWithDeferQuery.graphql.js +++ b/packages/relay-test-utils/__tests__/__generated__/RelayMockEnvironmentTestWithDeferQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<6d6f8ed199277b8878602e183463f5f0>> + * @generated SignedSource<<578edc34d1829740f7c241a4e900b1c2>> * @flow * @lightSyntaxTransform * @nogrep @@ -156,7 +156,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "b81417cb4d8ea298c71a3c957694cc4e"; + (node/*: any*/).hash = "5d610ca536e5dc2be8f6c17b55cd8a75"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-test-utils/__tests__/__generated__/RelayMockEnvironmentWithComponentsTestImpossibleAwesomenessQuery.graphql.js b/packages/relay-test-utils/__tests__/__generated__/RelayMockEnvironmentWithComponentsTestImpossibleAwesomenessQuery.graphql.js index 8c8a09ddec595..cc9489d4fd91f 100644 --- a/packages/relay-test-utils/__tests__/__generated__/RelayMockEnvironmentWithComponentsTestImpossibleAwesomenessQuery.graphql.js +++ b/packages/relay-test-utils/__tests__/__generated__/RelayMockEnvironmentWithComponentsTestImpossibleAwesomenessQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<3d283e64d2b3986fbb09b2d27670c643>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -170,7 +170,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "7c1e9456116ebdaa3af258c9693f19db"; + (node/*: any*/).hash = "f76438756eb9c84ef378afca68405ae2"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-test-utils/__tests__/__generated__/RelayMockEnvironmentWithComponentsTestImpressiveResultQuery.graphql.js b/packages/relay-test-utils/__tests__/__generated__/RelayMockEnvironmentWithComponentsTestImpressiveResultQuery.graphql.js index 6609acc349412..5a2a27215c04e 100644 --- a/packages/relay-test-utils/__tests__/__generated__/RelayMockEnvironmentWithComponentsTestImpressiveResultQuery.graphql.js +++ b/packages/relay-test-utils/__tests__/__generated__/RelayMockEnvironmentWithComponentsTestImpressiveResultQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<7e8c24ee0e0cc82734e07f5550d0012f>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -174,7 +174,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "eec1fb2dd0c1d0773d8745926c5fdd09"; + (node/*: any*/).hash = "aef8567276a76a051e8b0e2db5c71189"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-test-utils/__tests__/__generated__/RelayMockEnvironmentWithComponentsTestNoticeableSuccessQuery.graphql.js b/packages/relay-test-utils/__tests__/__generated__/RelayMockEnvironmentWithComponentsTestNoticeableSuccessQuery.graphql.js index 7001b73b4dc7d..550bd49ef5fef 100644 --- a/packages/relay-test-utils/__tests__/__generated__/RelayMockEnvironmentWithComponentsTestNoticeableSuccessQuery.graphql.js +++ b/packages/relay-test-utils/__tests__/__generated__/RelayMockEnvironmentWithComponentsTestNoticeableSuccessQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<8a93c1e2893fa131124d32efae9c66d3>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -266,7 +266,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "4ec459264098ec6a6d713fd7cd3c81c3"; + (node/*: any*/).hash = "130f72e8a3b1d89a616098a3f830c21a"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-test-utils/__tests__/__generated__/RelayMockEnvironmentWithComponentsTestRemarkableImpactQuery.graphql.js b/packages/relay-test-utils/__tests__/__generated__/RelayMockEnvironmentWithComponentsTestRemarkableImpactQuery.graphql.js index cdf86f26ce394..6b72bc8ed27c7 100644 --- a/packages/relay-test-utils/__tests__/__generated__/RelayMockEnvironmentWithComponentsTestRemarkableImpactQuery.graphql.js +++ b/packages/relay-test-utils/__tests__/__generated__/RelayMockEnvironmentWithComponentsTestRemarkableImpactQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -152,7 +152,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "9eedc32df7974b18aa138a63ffd62be9"; + (node/*: any*/).hash = "fe9ff25ab50d511448e63a6dfd620458"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-test-utils/__tests__/__generated__/RelayMockEnvironmentWithComponentsTestWorldClassAwesomenessQuery.graphql.js b/packages/relay-test-utils/__tests__/__generated__/RelayMockEnvironmentWithComponentsTestWorldClassAwesomenessQuery.graphql.js index edca425372d5a..d5f4e63bf883b 100644 --- a/packages/relay-test-utils/__tests__/__generated__/RelayMockEnvironmentWithComponentsTestWorldClassAwesomenessQuery.graphql.js +++ b/packages/relay-test-utils/__tests__/__generated__/RelayMockEnvironmentWithComponentsTestWorldClassAwesomenessQuery.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<7723c8c6a32f33c22170c7c1dfb2a72d>> + * @generated SignedSource<<6bb3b7f104b48651aa046a1bc9bbc504>> * @flow * @lightSyntaxTransform * @nogrep @@ -152,7 +152,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "ac58d11ae357d8162d5c45b77ca2c4a1"; + (node/*: any*/).hash = "27fc5cfac1bab81b1d82d08bc4e16d65"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest10Query.graphql.js b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest10Query.graphql.js index 397b31f6e4908..f2877c7169f74 100644 --- a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest10Query.graphql.js +++ b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest10Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<4f9afa61a8f9bf39e830eba4bd362483>> + * @generated SignedSource<<407724e220e3a338154b21076c86b23a>> * @flow * @lightSyntaxTransform * @nogrep @@ -187,7 +187,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "1c2a05dc735392e60f599f20acecbd4f"; + (node/*: any*/).hash = "4f914d9a228ed65faea4092d886c2184"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest12Query.graphql.js b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest12Query.graphql.js index 5653009fc8e09..9e7480e608e8e 100644 --- a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest12Query.graphql.js +++ b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest12Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<728a5cc924c2d86104460e09b75982d3>> * @flow * @lightSyntaxTransform * @nogrep @@ -263,7 +263,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "a1220185ee0ceb6d25b4cd775cdd7e36"; + (node/*: any*/).hash = "5ba52fe2450ad45a2106f09d013ef267"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest14Query.graphql.js b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest14Query.graphql.js index c5b3d0d0aa769..5455143845c63 100644 --- a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest14Query.graphql.js +++ b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest14Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<7b068a3083f3ff3cef42293a83b62e92>> * @flow * @lightSyntaxTransform * @nogrep @@ -185,7 +185,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "4ef820e776fe2defd0a8ebd1a54c7fe1"; + (node/*: any*/).hash = "e964764fb520672744832fda6e6ed892"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest15Query.graphql.js b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest15Query.graphql.js index b8f1db9184b41..f0e01013b6be1 100644 --- a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest15Query.graphql.js +++ b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest15Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<274456d39f60a8073996617f28ee51db>> * @flow * @lightSyntaxTransform * @nogrep @@ -178,7 +178,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "bc6005fc100a31baef4ea752b4292268"; + (node/*: any*/).hash = "f4ed0565a2e70ee82b67036101c26acd"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest17Query.graphql.js b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest17Query.graphql.js index 1a08a638d62d1..fc3907675abb2 100644 --- a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest17Query.graphql.js +++ b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest17Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<03f23067c363cb4e1ea511495f7455cf>> + * @generated SignedSource<<17361854e373c12d33a96056beaa4b09>> * @flow * @lightSyntaxTransform * @nogrep @@ -212,7 +212,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "0b3d641ae5319ba8c18ea9bdc28f596a"; + (node/*: any*/).hash = "79bdb355184ef7ba8e17b34fc383b66b"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest18Query.graphql.js b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest18Query.graphql.js index 1500d7947abd5..bc6a6236373d2 100644 --- a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest18Query.graphql.js +++ b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest18Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<95b89298664f302bc36f812dcddbaed8>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -499,7 +499,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "b3b221c6cd5d517678c590d0bf6a22ee"; + (node/*: any*/).hash = "b66819c8c8d971ae83ff691449db5624"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest19Query.graphql.js b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest19Query.graphql.js index a3346602e6d6e..2d96d2ecafaec 100644 --- a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest19Query.graphql.js +++ b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest19Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<24f25c892acd9d6003b763988716ae0d>> + * @generated SignedSource<<4dffe1ec42d010d729a03db3d0694699>> * @flow * @lightSyntaxTransform * @nogrep @@ -213,7 +213,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "4eec29a05a7f95a10a9aeb1c5c575e65"; + (node/*: any*/).hash = "1c18a51722104d40fce836ccd865a96b"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest1Query.graphql.js b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest1Query.graphql.js index be2fa438b3d49..d4cf50c8c1017 100644 --- a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest1Query.graphql.js +++ b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest1Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<932eeb1d1e045e6abe4fcb1b5a8d7285>> + * @generated SignedSource<<962a482ff8b12e8fc6717c65227e869e>> * @flow * @lightSyntaxTransform * @nogrep @@ -157,7 +157,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "84c1a5848aae26089f4aaaaa74c56543"; + (node/*: any*/).hash = "da022d676ffde7b720c5358afc35d31d"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest20Fragment.graphql.js b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest20Fragment.graphql.js index 54bfcecdaf6eb..bc5d9e531a1e1 100644 --- a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest20Fragment.graphql.js +++ b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest20Fragment.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<9b05cab55f41e37e5b2914ffb204791d>> + * @generated SignedSource<<501bd0a3f240f2792d8cd8cdabbd09cd>> * @flow * @lightSyntaxTransform * @nogrep @@ -125,7 +125,7 @@ var node/*: ReaderFragment*/ = { }; if (__DEV__) { - (node/*: any*/).hash = "d436f500bb695c067652f4f926493f86"; + (node/*: any*/).hash = "2e12e553476613cfd4087dc1f1407096"; } module.exports = ((node/*: any*/)/*: Fragment< diff --git a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest21Query.graphql.js b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest21Query.graphql.js index 990ed01804cf4..f99d4489e1fef 100644 --- a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest21Query.graphql.js +++ b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest21Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<2d5ba923b4ef6396fabffae045977d46>> + * @generated SignedSource<<7239a172fcd66a4c2ab01781399ce4ae>> * @flow * @lightSyntaxTransform * @nogrep @@ -200,7 +200,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "bbea6ffc62a6e03cff58249ad8368c27"; + (node/*: any*/).hash = "c67efae098def8e3b4cf2c6f23d48f3a"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest26Query.graphql.js b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest26Query.graphql.js index e380f550247e3..af14c8e449e42 100644 --- a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest26Query.graphql.js +++ b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest26Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<4e6124886cfbdebfdea90fc343214632>> * @flow * @lightSyntaxTransform * @nogrep @@ -174,7 +174,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "7a9885b7c8d5c02b3497ea60e2d3097c"; + (node/*: any*/).hash = "e3566d23bc9b945b0bd9131af72fdde9"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest27Fragment.graphql.js b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest27Fragment.graphql.js index 3cad323b528f3..9f97923a84057 100644 --- a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest27Fragment.graphql.js +++ b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest27Fragment.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<1fa4bebad4f9d5ab009cd65d212dd706>> * @flow * @lightSyntaxTransform * @nogrep @@ -119,7 +119,7 @@ var node/*: ReaderFragment*/ = { }; if (__DEV__) { - (node/*: any*/).hash = "07719ddfc2591587bf8141cba4f35d9f"; + (node/*: any*/).hash = "f516f909f2ceff438767128d376606f5"; } module.exports = ((node/*: any*/)/*: Fragment< diff --git a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest3Query.graphql.js b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest3Query.graphql.js index 63068e17757be..0d49ce44e4227 100644 --- a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest3Query.graphql.js +++ b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest3Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<80a8d05249f02714c9c45fd287aef9a5>> + * @generated SignedSource<<4eb8b9913613cd5ea956a374b0121f19>> * @flow * @lightSyntaxTransform * @nogrep @@ -234,7 +234,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "02749249be24669737cad13a3b9b3559"; + (node/*: any*/).hash = "516e96dc8b1e7ac336c80a4bca81bb10"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest43Query.graphql.js b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest43Query.graphql.js index 4196b68a1bb49..0abc56b5896ab 100644 --- a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest43Query.graphql.js +++ b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest43Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<9e37c62cf243100bed6d598f2b5cdd88>> + * @generated SignedSource<<5e98366f4551b8da3ab100b7b0c1e2e8>> * @flow * @lightSyntaxTransform * @nogrep @@ -205,7 +205,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "09c33ddebc7c4cfa00dd333b78b76f7d"; + (node/*: any*/).hash = "4d4487329ce65328b9d7bd21046280b1"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest44Query.graphql.js b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest44Query.graphql.js index 64fb8345692c7..9d7bb98b4f9f0 100644 --- a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest44Query.graphql.js +++ b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest44Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<826350ac79eada202187caf7b48b06c6>> * @flow * @lightSyntaxTransform * @nogrep @@ -204,7 +204,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "bad22ec9565e842bc6f16eb946a3a2d5"; + (node/*: any*/).hash = "e5927e3fbc3652488bf5407a4f7ae31c"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest45Query.graphql.js b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest45Query.graphql.js index 9527acc769714..4c50acbf89418 100644 --- a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest45Query.graphql.js +++ b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest45Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<6f05906ea903dfc7ef5634d397123f57>> + * @generated SignedSource<<99abaa28ce7cc37a5011fb390531c7f2>> * @flow * @lightSyntaxTransform * @nogrep @@ -226,7 +226,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "c45f56ca143cdb6c8633da434a4e229d"; + (node/*: any*/).hash = "36fe172073efce867387e06cfd5c9696"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest46Query.graphql.js b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest46Query.graphql.js index 4768ecf70f609..e770cb062bd0f 100644 --- a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest46Query.graphql.js +++ b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest46Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<50ffc09355fbc07ab26d9a062474b9da>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -226,7 +226,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "9be5402320aa98b21b5d86334183b77f"; + (node/*: any*/).hash = "3a294329388e7e030970e11891ed7b00"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest47Query.graphql.js b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest47Query.graphql.js index 6f8afd11d11c0..1781b8dfe3b6d 100644 --- a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest47Query.graphql.js +++ b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest47Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<51126c9cec4db52fcb13ee8a3fedb219>> * @flow * @lightSyntaxTransform * @nogrep @@ -204,7 +204,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "4b48e2f43a325d78c6749a5475dbed06"; + (node/*: any*/).hash = "405204aa6f6931208ebc8d03a94890ff"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest48Query.graphql.js b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest48Query.graphql.js index 50475205d71b3..d2a895c8eb5b2 100644 --- a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest48Query.graphql.js +++ b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest48Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<92219263b1d097629de734f2ee3ed049>> + * @generated SignedSource<<5e74236453420efb21ac09a9eeda9d14>> * @flow * @lightSyntaxTransform * @nogrep @@ -204,7 +204,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "420a4f67dcb61f38361f4e5565b8497a"; + (node/*: any*/).hash = "04ba39056e3b88849b3adaf124aa25e5"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest4Query.graphql.js b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest4Query.graphql.js index 02ec8acf75b4d..72881635b6bf5 100644 --- a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest4Query.graphql.js +++ b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest4Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<91ec76861dbf3c0ee3530f4e22b885f0>> * @flow * @lightSyntaxTransform * @nogrep @@ -350,7 +350,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "9c1756d1dc05632f216b5817ca47e88a"; + (node/*: any*/).hash = "80bdf2a384f649fcfde653ff34e3a8cc"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest57Query.graphql.js b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest57Query.graphql.js index 4f8e6b229f4f1..31b32563b1270 100644 --- a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest57Query.graphql.js +++ b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest57Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<47f76140deeacf45ee24386e211a2d9b>> * @flow * @lightSyntaxTransform * @nogrep @@ -65,7 +65,7 @@ var node/*: ConcreteRequest*/ = { }, "kind": "RelayResolver", "name": "name_passthrough", - "resolverModule": require('./../../../relay-runtime/store/__tests__/resolvers/UserNamePassthroughResolver').name_passthrough, + "resolverModule": require('../../../relay-runtime/store/__tests__/resolvers/UserNamePassthroughResolver').name_passthrough, "path": "me.name_passthrough" } ], diff --git a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest58Query.graphql.js b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest58Query.graphql.js index e6dcfce994bd7..5a1b992f51d48 100644 --- a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest58Query.graphql.js +++ b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest58Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<5be38ced0f7b5fc865350942614026f0>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -128,7 +128,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "7207fa27437b4334d2950de9e7043322"; + (node/*: any*/).hash = "c0467d664d629e5180ecfc619505654e"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest5Fragment.graphql.js b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest5Fragment.graphql.js index 330782f43c76a..feafce11692c0 100644 --- a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest5Fragment.graphql.js +++ b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest5Fragment.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<4a846836a0164c2584cea5b24ad65537>> * @flow * @lightSyntaxTransform * @nogrep @@ -220,7 +220,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "0caeda7ac9d7c75061766462726bf298"; + (node/*: any*/).hash = "f00962ad9a873757b70472af4c68956a"; } module.exports = ((node/*: any*/)/*: Fragment< diff --git a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest5Query.graphql.js b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest5Query.graphql.js index 3583b7460e4d3..483690c4ec21f 100644 --- a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest5Query.graphql.js +++ b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest5Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<02094ce579d1f395bd7a2f0317a86cb1>> + * @generated SignedSource<<8adc0eedf5a7d2e77ee761d04f44be1c>> * @flow * @lightSyntaxTransform * @nogrep @@ -286,7 +286,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "23c84e020a1563dfe94d3c227d810700"; + (node/*: any*/).hash = "2654e0737422134909d621561162ec9c"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest60Query.graphql.js b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest60Query.graphql.js index cf94aeca80f08..04046c9cb5164 100644 --- a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest60Query.graphql.js +++ b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest60Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<0e9a44239d593d8d98a33d680af09bf0>> * @flow * @lightSyntaxTransform * @nogrep @@ -137,7 +137,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "3deee5972aa6d5f988a035cc01194d24"; + (node/*: any*/).hash = "418a20e9e7e8dd61af4c63c47b8a88c3"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest61Query.graphql.js b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest61Query.graphql.js index 16298de8d3430..98681a8e5bdb2 100644 --- a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest61Query.graphql.js +++ b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest61Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<8bd31d1467a7b1b340295fa768c2a6ce>> * @flow * @lightSyntaxTransform * @nogrep @@ -185,7 +185,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "2f6426e08b7bf1bc520a2e62d89805ff"; + (node/*: any*/).hash = "c227dfa395367e6e586d0105e47e1a7d"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest62Query.graphql.js b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest62Query.graphql.js index 925596d761aa5..9ddcbe5df8be3 100644 --- a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest62Query.graphql.js +++ b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest62Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<2a20a6b2373df9fd85348e0e9aa406c5>> + * @generated SignedSource<<3a82a415193676b0bbe6bf89b2294108>> * @flow * @lightSyntaxTransform * @nogrep @@ -140,7 +140,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "b01069bf80c36d8dae8d719a55af39a7"; + (node/*: any*/).hash = "a022e454cd8aa6d39c0ed51d0621a5a3"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest63Query.graphql.js b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest63Query.graphql.js index 80a91becddc5c..da0cfa8734710 100644 --- a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest63Query.graphql.js +++ b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest63Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<5aff17f172699964d5438d70948bd4f7>> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -128,7 +128,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "5407f8130e4f883d6d9f133941b5f0dd"; + (node/*: any*/).hash = "eb1568209e8f70eb6e179622fddb10e3"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest67Query.graphql.js b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest67Query.graphql.js index d3e35f35fa3aa..4de709e02666f 100644 --- a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest67Query.graphql.js +++ b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest67Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<6e21ab76283f7b2a77fccae86d5f2aa2>> * @flow * @lightSyntaxTransform * @nogrep @@ -204,7 +204,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "09729a7c122a5fedbea96ed60b70ee3b"; + (node/*: any*/).hash = "ee32413bf007d34e02585799a2630ae6"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest6Query.graphql.js b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest6Query.graphql.js index b5270eae93502..ad66f8d84a7d6 100644 --- a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest6Query.graphql.js +++ b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest6Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<> * @flow * @lightSyntaxTransform * @nogrep @@ -140,7 +140,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "216d5f7d912a96c513bc0fff4679f94a"; + (node/*: any*/).hash = "ff342d024fff5a4b1dcee5543c6aa2d4"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest7Query.graphql.js b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest7Query.graphql.js index 88614e6224883..d55a0ca25477b 100644 --- a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest7Query.graphql.js +++ b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest7Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<8e53102e1703f928b49c51525c87d1fa>> * @flow * @lightSyntaxTransform * @nogrep @@ -143,7 +143,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "a3c95c433bbd4ee9485da633208ee3c6"; + (node/*: any*/).hash = "0cc06f93ea6f93b459881b5fc3fef6c9"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest8Query.graphql.js b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest8Query.graphql.js index 2cb665c14359b..30406851c11d6 100644 --- a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest8Query.graphql.js +++ b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest8Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<3e7a21a4842c8f1b476d76d170457a57>> + * @generated SignedSource<<8e578ff50728ff5a213348a114e4c14d>> * @flow * @lightSyntaxTransform * @nogrep @@ -165,7 +165,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "dcc5f3c62fd805745834727a37388a3f"; + (node/*: any*/).hash = "961c9b3eb7887b23c338ca99b5445314"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest9Query.graphql.js b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest9Query.graphql.js index aa7162a94a35a..97bfa33a751ca 100644 --- a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest9Query.graphql.js +++ b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest9Query.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<<35b120ca4a7acdb4f09a65ffc9de8839>> + * @generated SignedSource<<1e5b0822d3c927d459abc9cb5a4e8219>> * @flow * @lightSyntaxTransform * @nogrep @@ -140,7 +140,7 @@ return { })(); if (__DEV__) { - (node/*: any*/).hash = "866407595a79b48f9ec7557156be487a"; + (node/*: any*/).hash = "1ee38c0038e45d0a5c0dfe00b20d5106"; } module.exports = ((node/*: any*/)/*: Query< diff --git a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest_fragment59$normalization.graphql.js b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest_fragment59$normalization.graphql.js index c7b2a6453db73..28fe8a13f328c 100644 --- a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest_fragment59$normalization.graphql.js +++ b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest_fragment59$normalization.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<4cdda03c548724371f4b5575c7ac4495>> * @flow * @lightSyntaxTransform * @nogrep @@ -21,7 +21,7 @@ import type { NormalizationSplitOperation } from 'relay-runtime'; export type RelayMockPayloadGeneratorTest_fragment59$normalization = {| +id: string, - +name: ?string, + +name?: ?string, |}; */ diff --git a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest_fragment61$normalization.graphql.js b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest_fragment61$normalization.graphql.js index f74056539c116..01bf107fe0bc0 100644 --- a/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest_fragment61$normalization.graphql.js +++ b/packages/relay-test-utils/__tests__/__generated__/RelayMockPayloadGeneratorTest_fragment61$normalization.graphql.js @@ -6,7 +6,7 @@ * * @oncall relay * - * @generated SignedSource<> + * @generated SignedSource<<10d99aad572cf708781d6a2425fc893e>> * @flow * @lightSyntaxTransform * @nogrep @@ -21,7 +21,7 @@ import type { NormalizationSplitOperation } from 'relay-runtime'; export type RelayMockPayloadGeneratorTest_fragment61$normalization = {| +id: string, - +name: ?string, + +name?: ?string, |}; */ diff --git a/packages/relay-test-utils/package.json b/packages/relay-test-utils/package.json index b0f5738c3dc49..a4f7238891ea1 100644 --- a/packages/relay-test-utils/package.json +++ b/packages/relay-test-utils/package.json @@ -1,7 +1,7 @@ { "name": "relay-test-utils", "description": "Utilities for testing Relay applications.", - "version": "18.2.0", + "version": "20.1.1", "keywords": [ "graphql", "relay" @@ -18,7 +18,7 @@ "@babel/runtime": "^7.25.0", "fbjs": "^3.0.2", "invariant": "^2.2.4", - "relay-runtime": "18.2.0" + "relay-runtime": "20.1.1" }, "directories": { "": "./" diff --git a/packages/relay-test-utils/unwrapContainer.js b/packages/relay-test-utils/unwrapContainer.js index be54b1a7898ea..676341df56d0f 100644 --- a/packages/relay-test-utils/unwrapContainer.js +++ b/packages/relay-test-utils/unwrapContainer.js @@ -23,11 +23,11 @@ const invariant = require('invariant'); /** * Returns original component class wrapped by e.g. createFragmentContainer */ -function unwrapContainer( - ComponentClass: React.ComponentType< - $RelayProps, - >, -): React.ComponentType { +function unwrapContainer( + ComponentClass: component( + ...$RelayProps + ), +): component(...Props) { // $FlowExpectedError const unwrapped = ComponentClass.__ComponentClass; invariant( diff --git a/scripts/check-git-status.sh b/scripts/check-git-status.sh index 63887f402f0ef..8ed70939c2cd0 100755 --- a/scripts/check-git-status.sh +++ b/scripts/check-git-status.sh @@ -11,6 +11,6 @@ if [ -z "$(git status --porcelain)" ]; then exit 0 else echo "Detected changes in the working directory:" - git --no-pager diff --stat + git --no-pager diff HEAD exit 1 fi diff --git a/scripts/config.tests.json b/scripts/config.tests.json index a42343e00d2cd..6a013e9bc9ed5 100644 --- a/scripts/config.tests.json +++ b/scripts/config.tests.json @@ -34,6 +34,10 @@ "mode": "Custom", "statement": "() => require('./.<$module>')" }, + "operationModuleProvider": { + "mode": "Custom", + "statement": "() => require('<$module>')" + }, "surface": "resolvers" }, "schemaConfig": { @@ -62,13 +66,17 @@ "relay_resolver_enable_interface_output_type": { "kind": "enabled" }, - "enable_exec_time_resolvers_directive": true + "enable_exec_time_resolvers_directive": true, + "allow_output_type_resolvers": { + "kind": "enabled" + } }, "language": "flow", "resolverContextType": { "name": "TestResolverContextType", "path": "packages/relay-runtime/mutations/__tests__/TestResolverContextType" - } + }, + "eagerEsModules": false } }, "isDevVariableName": "__DEV__" diff --git a/scripts/jest/environment.js b/scripts/jest/environment.js index c7d2d8c5ecf0b..69ef077ff0dd0 100644 --- a/scripts/jest/environment.js +++ b/scripts/jest/environment.js @@ -18,3 +18,14 @@ global.__DEV__ = true; require('@babel/runtime/regenerator'); process.env.RTL_SKIP_AUTO_CLEANUP = true; + +/** + * Prettier v3 uses import (cjs/mjs) file formats that jest-runtime does not + * support. To work around this we need to bypass the jest module system by + * using the orginal node `require` function. + */ +jest.mock('prettier', () => { + // $FlowExpectedError[underconstrained-implicit-instantiation] + const module = jest.requireActual('module'); + return module.prototype.require(require.resolve('prettier')); +}); diff --git a/scripts/jest/preprocessor.js b/scripts/jest/preprocessor.js index e7c7a8e6d797f..d809c2707b515 100644 --- a/scripts/jest/preprocessor.js +++ b/scripts/jest/preprocessor.js @@ -20,7 +20,7 @@ const babelOptions = getBabelOptions({ env: 'test', autoImport: false, plugins: [ - './dist/babel-plugin-relay', + ['./dist/babel-plugin-relay', {eagerEsModules: false}], '@babel/plugin-transform-flow-strip-types', '@babel/plugin-transform-runtime', '@babel/plugin-proposal-nullish-coalescing-operator', diff --git a/scripts/release-notes.js b/scripts/release-notes.js index faf25baa389af..d9f15781cdd84 100644 --- a/scripts/release-notes.js +++ b/scripts/release-notes.js @@ -99,12 +99,16 @@ function getData() { .toString() .split('\n'); const commits = listOfCommits.split('\n').map((commitMessage, index) => { + const diffMatch = body[index].match(/D\d+/); + const diff = diffMatch != null && diffMatch[0]; const [hash, date, name, _email] = commitMessage.split('|'); return { - hash: hash.slice(0, 6), + hash: hash.slice(0, 7), + fullHash: hash, summary: summary[index], message: body[index], author: name, + diff, date, }; }); diff --git a/scripts/release-notes/App.js b/scripts/release-notes/App.js index 4ffceef39c61c..b9efbd1077c39 100644 --- a/scripts/release-notes/App.js +++ b/scripts/release-notes/App.js @@ -45,11 +45,27 @@ function CommitCard({ date, selectedCategory, onCategoryChange, + fullHash, + diff, }) { return (
-

{summary}

-

{author}

+

+ + {summary} + +

+

+ {author} + {diff && ( + <> + {' '} + + {diff} + + + )} +

onCategoryChange(category)} />
); @@ -91,6 +107,8 @@ function App({commits, lastRelease}) { summary={commit.summary} author={commit.author} date={commit.date} + fullHash={commit.fullHash} + diff={commit.diff} selectedCategory={selectedCategories[commit.hash]} onCategoryChange={category => { setSelectedCategories({ @@ -206,13 +224,16 @@ function CommitList({commits}) { return (
    {commits.map(commit => { + const summary = commit.summary.replace(/^- /, ''); // Avoid commit messages that render as indented list items. + const link = `${REPO_URL}/commit/${commit.fullHash}`; return (
  • - {'- '}[ - - {commit.hash} + {' - '} + {capitalize(summary)} by {commit.author} ([ + + commit - ]: {capitalize(commit.summary)} by {commit.author} + ]({link}))
  • ); })} diff --git a/vscode-extension/.eslintrc.js b/vscode-extension/.eslintrc.js index ebaa6dc4f159c..9ca934c89a864 100644 --- a/vscode-extension/.eslintrc.js +++ b/vscode-extension/.eslintrc.js @@ -38,6 +38,7 @@ module.exports = { {functions: false}, ], 'class-methods-use-this': 'off', + 'max-classes-per-file': 'off', }, }, ], diff --git a/vscode-extension/README.md b/vscode-extension/README.md index aac7ec0874f7b..fe2d9a44369a7 100644 --- a/vscode-extension/README.md +++ b/vscode-extension/README.md @@ -46,7 +46,7 @@ Specify the output level of the Relay language server. The available options are - verbose - debug -#### `relay.pathToBinary` (default: `null`) +#### `relay.pathToRelay` (default: `null`) A path to the Relay binary relative to the root of your project. If this is not specified, we will try to find one in your `node_modules` folder. diff --git a/vscode-extension/package.json b/vscode-extension/package.json index 2ac799944a954..fa546c18ff44d 100644 --- a/vscode-extension/package.json +++ b/vscode-extension/package.json @@ -1,7 +1,7 @@ { "name": "relay", "displayName": "Relay GraphQL", - "version": "2.5.0", + "version": "2.5.1", "description": "Relay-powered IDE experience", "repository": { "type": "git", diff --git a/vscode-extension/src/extension.ts b/vscode-extension/src/extension.ts index a2d9bfed93182..f3b5c12bbda1f 100644 --- a/vscode-extension/src/extension.ts +++ b/vscode-extension/src/extension.ts @@ -8,7 +8,7 @@ import path = require('path'); import {ExtensionContext, window, workspace} from 'vscode'; import {registerCommands} from './commands/register'; -import {registerProviders} from './providers/register'; +import {registerProviders, registerNoopProviders} from './providers/register'; import {createAndStartCompiler} from './compiler'; import {getConfig} from './config'; @@ -87,6 +87,10 @@ export async function activate(extensionContext: ExtensionContext) { ].join(' '), ); } + } else { + // We still need to register a handler for `relay://` otherwise non-Relay + // projects will get a warning at the top of their `package.json` files. + registerNoopProviders(extensionContext); } } diff --git a/vscode-extension/src/providers/register.ts b/vscode-extension/src/providers/register.ts index 7444cf291e41e..e41823194cfc6 100644 --- a/vscode-extension/src/providers/register.ts +++ b/vscode-extension/src/providers/register.ts @@ -5,9 +5,12 @@ * LICENSE file in the root directory of this source tree. */ -import {workspace} from 'vscode'; +import {workspace, ExtensionContext} from 'vscode'; import {RelayExtensionContext} from '../context'; -import {RelayTextDocumentContentProvider} from './textDocumentContentProvider'; +import { + RelayTextDocumentContentProvider, + NoopTextDocumentContentProvider, +} from './textDocumentContentProvider'; export function registerProviders(context: RelayExtensionContext) { context.extensionContext.subscriptions.push( @@ -17,3 +20,12 @@ export function registerProviders(context: RelayExtensionContext) { ), ); } + +export function registerNoopProviders(extensionContext: ExtensionContext) { + extensionContext.subscriptions.push( + workspace.registerTextDocumentContentProvider( + RelayTextDocumentContentProvider.scheme, + new NoopTextDocumentContentProvider(), + ), + ); +} diff --git a/vscode-extension/src/providers/textDocumentContentProvider.ts b/vscode-extension/src/providers/textDocumentContentProvider.ts index c9befb1163b58..753850db93b1f 100644 --- a/vscode-extension/src/providers/textDocumentContentProvider.ts +++ b/vscode-extension/src/providers/textDocumentContentProvider.ts @@ -46,7 +46,7 @@ export class RelayTextDocumentContentProvider if (!this.cachedJsonSchema) { // We return an empty JSON schema instead of undefined to prevent - // an error being shown in the user's IDE. + // a warning being shown in the user's IDE. return '{}'; } @@ -76,3 +76,19 @@ export class RelayTextDocumentContentProvider } } } + +export class NoopTextDocumentContentProvider + implements TextDocumentContentProvider +{ + provideTextDocumentContent(uri: Uri): ProviderResult { + if ( + uri.authority === PACKAGE_JSON_RELAY_CONFIG_SCHEMA_PATH || + uri.authority === RELAY_CONFIG_SCHEMA_PATH + ) { + // We return an empty JSON schema instead of undefined to prevent + // a warning being shown in the user's IDE. + return '{}'; + } + return undefined; + } +} diff --git a/vscode-extension/src/utils/findRelayBinary.ts b/vscode-extension/src/utils/findRelayBinary.ts index 4f704766e12de..0ce6d3d721885 100644 --- a/vscode-extension/src/utils/findRelayBinary.ts +++ b/vscode-extension/src/utils/findRelayBinary.ts @@ -185,7 +185,7 @@ export async function findRelayBinaryWithWarnings( if (config.pathToRelay) { outputChannel.appendLine( - "You've manually specified 'relay.pathToBinary'. We cannot confirm this version of the Relay Compiler is supported by this version of the extension. I hope you know what you're doing.", + "You've manually specified 'relay.pathToRelay'. We cannot confirm this version of the Relay Compiler is supported by this version of the extension. I hope you know what you're doing.", ); return {path: config.pathToRelay}; diff --git a/website/blog/2021-03-09-introducing-relay-hooks.md b/website/blog/2021-03-09-introducing-relay-hooks.md index f3d8fee6996cd..27d1e24875d7c 100644 --- a/website/blog/2021-03-09-introducing-relay-hooks.md +++ b/website/blog/2021-03-09-introducing-relay-hooks.md @@ -141,11 +141,11 @@ Although Relay Hooks uses Suspense for some of its APIs, *support, general guida Nonetheless, we released Relay Hooks now because we know these APIs are on the right trajectory for supporting upcoming releases of React. Even though parts of Relay’s Suspense implementation may still change, the Relay Hooks APIs themselves are stable; they have been widely adopted internally, and have been in use in production for over a year. -See Suspense Compatibility and Loading States with Suspense for deeper treatments of this topic. +See [Suspense Compatibility](https://relay.dev/docs/v13.0.0/migration-and-compatibility/suspense-compatibility/) and Loading States with Suspense for deeper treatments of this topic. ### Where to go from here -Please check out the getting started guide, the migration guide and the guided tour. +Please check out the getting started guide, the [migration guide](https://relay.dev/docs/v13.0.0/migration-and-compatibility/) and the guided tour. ### Thanks diff --git a/website/blog/2023-03-30-relay-15.mdx b/website/blog/2023-03-30-relay-15.mdx index 8073afbdbd10c..763f4f122bf19 100644 --- a/website/blog/2023-03-30-relay-15.mdx +++ b/website/blog/2023-03-30-relay-15.mdx @@ -62,7 +62,7 @@ Typesafe updaters now support missing field handlers. Previously, if you selecte In this release, we add support for missing field handlers in typesafe updaters, meaning that if a missing field handler is set up for node (as in [this example](https://relay.dev/docs/next/guided-tour/reusing-cached-data/filling-in-missing-data/#internaldocs-banner)), you will be able to update the user's name with this missing field handler. -In order to support this, the signature of [missing field handlers](https://relay.dev/docs/guided-tour/reusing-cached-data/filling-in-missing-data) has been changed. The `record` argument to the handler used to recieve a `Record` type (which is an untyped grab-bag of data). It now receives a `ReadOnlyRecordProxy`. Furthermore, the field argument of type `NormalizationLinkedField` is now `CommonLinkedField`, which is a type containing the properties found in both `ReaderLinkedField` and `NormalizationLinkedField`. +In order to support this, the signature of [missing field handlers](https://relay.dev/docs/guided-tour/reusing-cached-data/filling-in-missing-data) has been changed. The `record` argument to the handler used to receive a `Record` type (which is an untyped grab-bag of data). It now receives a `ReadOnlyRecordProxy`. Furthermore, the field argument of type `NormalizationLinkedField` is now `CommonLinkedField`, which is a type containing the properties found in both `ReaderLinkedField` and `NormalizationLinkedField`. ### Flow type improvements diff --git a/website/blog/2025-04-02-relay-19.mdx b/website/blog/2025-04-02-relay-19.mdx new file mode 100644 index 0000000000000..e89bd0b391fbd --- /dev/null +++ b/website/blog/2025-04-02-relay-19.mdx @@ -0,0 +1,165 @@ +--- +title: Relay v19.0 +author: The Relay Team +hide_table_of_contents: false +--- + +# Version 19.0.0 Release Notes + +Relay 19.0.0 includes many documentation improvements, bug fixes, and improved capabilities. + +## `@alias` required on conditional fragments + +To improve type safety, the [`@alias` directive](https://relay.dev/docs/next/guides/alias-directive/) is now required on all fragments that are only conditionally fetched either due to `@skip`/`@include` or fragment type conditions which only conditionally match. You can opt out of this validation on a per-fragment basis with the `@dangerously_unaliased_fixme` directive. + +To enable incremental migration we [include a codemod](https://relay.dev/docs/next/guides/codemods/#mark-dangerous-conditional-fragment-spreads) which will automatically add the `@dangerously_unaliased_fixme` in all required places: + +```bash +npx relay-compiler codemod mark-dangerous-conditional-fragment-spreads +``` + +You can also opt out of this validation entirely via compiler config feature flag: + +```json filename="relay.config.json" +{ + // ... + "featureFlags": { + "enforce_fragment_alias_where_ambiguous": { + "kind": "disabled" + } + } +} +``` + +## Improved Docs + +We've merged ~30 commits to clean up and improve our docs since the last release: + +* Added new pages: + * [Quick Start](https://relay.dev/docs/next/getting-started/quick-start/) - A single page guide to get Relay up and running locally + * [Production Setup](https://relay.dev/docs/next/getting-started/production/) - A list of best practices for setting your Relay application up for production use cases + * [Relay Babel Plugin](https://relay.dev/docs/next/getting-started/babel-plugin/) - Information about Relay's Babel plugin and how to install it in your app. + * [Relay Environment](https://relay.dev/docs/next/api-reference/relay-runtime/relay-environment/) - API docs for the core Relay environment + * [Runtime Configuration](https://relay.dev/docs/next/api-reference/runtime-config/) - Documentation covering runtime feature flags and more +* Removed outdated or orphaned doc pages +* Streamlined and fixed issues in existing docs + +## React 19 Compatible + +React 19 is now a valid peer dependency of Relay (#4944) by Krzysztof Karol ([commit](https://github.com/facebook/relay/commit/aeb26c3ac05f)) + +## Breaking Changes + +- Relay now defaults to generating ES module imports in its generated js files. You can add `"eagerEsModules": false` in your `relay.config.json` to opt back into the old behavior. ([commit](https://github.com/facebook/relay/commit/bd321a231397)) +- Relay's NPM modules nolonger include a pre-bundled module. (#4935) by Iha Shin ([commit](https://github.com/facebook/relay/commit/cc4cf767dd44)) + +## Improvements +- Avoid duplication in config JSON Schema by Jordan Eldredge ([commit](https://github.com/facebook/relay/commit/1e4b1646c334)) +- Go to definition in the LSP can now navigate to the correct column (#4969) by Jordan Eldredge ([commit](https://github.com/facebook/relay/commit/4cab6f1a4d4e)) +- Make store an optional argument when constructing a Relay Environment by Jordan Eldredge ([commit](https://github.com/facebook/relay/commit/288aebdbd77d)) +- Include mixed loggerContext in handled snapshot errors by Itamar Kestenbaum ([commit](https://github.com/facebook/relay/commit/d800611ad70d)) +- Add id collision logging in RelayResponseNormalizer with typename metadata by Monica Tang ([commit](https://github.com/facebook/relay/commit/419353ce8b50)) +- Mark useLazyLoadQuery options as ReadOnly by Marco Wang ([commit](https://github.com/facebook/relay/commit/7730d71ec4a0)) +- Add onPause in cacheConfig for Relay subscriptions by Aria Fallah ([commit](https://github.com/facebook/relay/commit/76b801a7946f)) +- Add typename metadata to id collision log event by Monica Tang ([commit](https://github.com/facebook/relay/commit/027572b82f89)) +- Allow @dangerously_unaliased_fixme on updatable fragment spreads by Jordan Eldredge ([commit](https://github.com/facebook/relay/commit/837d4ab00f54)) +- Add a feature flag to throw on nested updates in dev by Tianyu Yao ([commit](https://github.com/facebook/relay/commit/0ea5dfbbd836)) +- Do not emit union type for __typename selection on non-abstract type (#4923) by tobias-tengler ([commit](https://github.com/facebook/relay/commit/d9967e8fc676)) +- Reenable warning if fetchQuery is called in render (using new unsatable React APIs) by Jordan Eldredge ([commit](https://github.com/facebook/relay/commit/9f7fb21578c0)) +- Log ID collisions in production by Monica Tang ([commit](https://github.com/facebook/relay/commit/65b9b9a6124a)) +- Add log events by Monica Tang ([commit](https://github.com/facebook/relay/commit/c640e2ef97c5)) +- Pass operation availability to the network layer for loadQuery by Tianyu Yao ([commit](https://github.com/facebook/relay/commit/ab3b117680c5)) +- Add compiler validation to error on resolver that returns plural server type by Jordan Eldredge ([commit](https://github.com/facebook/relay/commit/687e9964123f)) +- Enable by default recreating loadMore optimization by Andrei Marchenko ([commit](https://github.com/facebook/relay/commit/a6f66ebe468b)) +- Allow @dangerously_unaliased_fixme in updatable fragments by Jordan Eldredge ([commit](https://github.com/facebook/relay/commit/3a16e220a2ff)) +- Error on empty selections after fragment argument transform (#4908) by tobias-tengler ([commit](https://github.com/facebook/relay/commit/56fbda68b171)) +- Add error message to Resolver error by Itamar Kestenbaum ([commit](https://github.com/facebook/relay/commit/134aea416143)) +- Fix codemod crashing on aliases in inline fragments by Gordy French ([commit](https://github.com/facebook/relay/commit/002f9ed05aed)) +- Allow required alias codemod to be applied to a rollout range by Jordan Eldredge ([commit](https://github.com/facebook/relay/commit/afca975cd55b)) +- Add onResume in cacheConfig for Relay subscriptions by Xiangxin Sun ([commit](https://github.com/facebook/relay/commit/40b579fe4088)) +- Shorten relay read time resolver key prefix by Tianyu Yao ([commit](https://github.com/facebook/relay/commit/a5d6b23af4f0)) +- Arguments in prefetch pagination variables bug fix by Lynn Yu ([commit](https://github.com/facebook/relay/commit/b6291f97cb98)) +- handle local variables vs global variables in prefetch pagination by Lynn Yu ([commit](https://github.com/facebook/relay/commit/59ae29292204)) +- Manual rebase of: Enable __token field based on compiler schema config #4347 (#4889) by Jordan Eldredge ([commit](https://github.com/facebook/relay/commit/9b90142ae813)) +- Fix conditional sub-selections in raw response type (#4774) by tobias-tengler ([commit](https://github.com/facebook/relay/commit/38ae4692b3a3)) +- Add execute.unsubsribe logging by Tianyu Yao ([commit](https://github.com/facebook/relay/commit/45984275bf72)) +- Enhance `schemaExtensions` to Support Both Files and Directories (#4859) by Sverre Johansen ([commit](https://github.com/facebook/relay/commit/be8f5f77e113)) +- Add plural fragment support to `observeFragment()` (#4862) by Iha Shin ([commit](https://github.com/facebook/relay/commit/9ce48174b60a)) +- Handle CRLF when parsing docblocks (#4865) by Sverre Johansen ([commit](https://github.com/facebook/relay/commit/79f0355e28ec)) +- Validate that client schema extensions within @throwOnFieldError have @catch by Jordan Eldredge ([commit](https://github.com/facebook/relay/commit/572a90f626a7)) +- Add @catch to client schema extension fields within @throwOnFieldError by Jordan Eldredge ([commit](https://github.com/facebook/relay/commit/61b028d932f4)) +- Make Result type fields readonly by Jordan Eldredge ([commit](https://github.com/facebook/relay/commit/6186a7da2f50)) +- Limit WalkDir to the root directories referenced by the configuration (#4850) by Sverre Johansen ([commit](https://github.com/facebook/relay/commit/9503d7be26aa)) +- Simplify dependencies for loadMore function by Andrei Marchenko ([commit](https://github.com/facebook/relay/commit/8c29557d54d2)) +- Propegate empty arrays into the store when handling errors on noncompliant lists by Ryan Holdren ([commit](https://github.com/facebook/relay/commit/6f76a2af2f88)) + +## Bug fixes +- Fix nullable refetchedFragmentRef in checkSameIDAfterRefetch (#4945) by Krzysztof Karol ([commit](https://github.com/facebook/relay/commit/677c1e3dc44e)) +- Add missing `readFragment` export in relay-runtime (#4931) by Jay Jaeho Lee ([commit](https://github.com/facebook/relay/commit/51845382b662)) +- Fix bug with caching incomplete used variables for fragment cycles by Jordan Eldredge ([commit](https://github.com/facebook/relay/commit/3b732fc0ac88)) +- Fix `observeFragment` triggering unhandled rejections on network error (#4885) by Iha Shin ([commit](https://github.com/facebook/relay/commit/10073c88f8ec)) +- Checked for missed updates in effect create phase by Jack Pope ([commit](https://github.com/facebook/relay/commit/616508125a8e)) +- Avoid writing to stdout in LSP by Jordan Eldredge ([commit](https://github.com/facebook/relay/commit/51fac5d3c9a6)) +- Run requried transform on IR before validating @required on semantic non null fields in LSP by Jordan Eldredge ([commit](https://github.com/facebook/relay/commit/911e0831f77f)) +- Fix: Don't report @required on field that can be null due to @required bubbling as unnessesary by Jordan Eldredge ([commit](https://github.com/facebook/relay/commit/f11501758ab7)) +- Unbreak LiveState unsub when references.size === 0 (#4832) by Tom Aylott ([commit](https://github.com/facebook/relay/commit/1e60e4716a5e)) +- Remove PointerAddress from generate_typename by Gordy French ([commit](https://github.com/facebook/relay/commit/d0da5251e8cb)) +- Context not properly provided through data injector and subscriptions (#4846) by Mark Polak ([commit](https://github.com/facebook/relay/commit/2211ac6c529b)) +- Fix rare client_extension instability by Gordy French ([commit](https://github.com/facebook/relay/commit/28be5c299075)) +- Enforce TTL-based GC when release buffer is full by Monica Tang ([commit](https://github.com/facebook/relay/commit/29b7a1e08c66)) +- Fix usePagination stuck in isLoading by Tianyu Yao ([commit](https://github.com/facebook/relay/commit/e9e556cf9bf3)) + + +## Documentation Improvements +- Update language to reference hooks not containers (#4964) by Jordan Eldredge ([commit](https://github.com/facebook/relay/commit/5893f3a06f4b)) +- Move the "Organizing Operations" tutorial page into the guides by Evan Yeung ([commit](https://github.com/facebook/relay/commit/91ec3275ae13)) +- Fix code examples in the connections tutorial page by Evan Yeung ([commit](https://github.com/facebook/relay/commit/155865207912)) +- Migrate most blockquotes in our docs to admonitions by Evan Yeung ([commit](https://github.com/facebook/relay/commit/07e662edd398)) +- Update VSCode docs/errors to use pathToRelay (#4965) by Jordan Eldredge ([commit](https://github.com/facebook/relay/commit/91c22be60cb5)) +- Iteration on new quick start guide based on feedback (#4966) by Jordan Eldredge ([commit](https://github.com/facebook/relay/commit/c7078a15e13f)) +- Quick Start: Add note about watchman by Jordan Eldredge ([commit](https://github.com/facebook/relay/commit/66550c3399a0)) +- Remove "TBD" sections from the docs by Evan Yeung ([commit](https://github.com/facebook/relay/commit/0470769e8235)) +- Rework onboarding flow by Jordan Eldredge ([commit](https://github.com/facebook/relay/commit/2784dfc48f68)) +- Remove migration and compatability pages by Jordan Eldredge ([commit](https://github.com/facebook/relay/commit/479fde84fcc4)) +- Make footer logo smaller by Monica Tang ([commit](https://github.com/facebook/relay/commit/010b8e1e75b8)) +- Delete some pages by Monica Tang ([commit](https://github.com/facebook/relay/commit/ce0b8c3396aa)) +- Remove orphaned page "workflow" by Jordan Eldredge ([commit](https://github.com/facebook/relay/commit/7bf190f14fd9)) +- Delete empty docs pages by Monica Tang ([commit](https://github.com/facebook/relay/commit/bb7bde0e3d1f)) +- Fix typos and improve clarity in the tutorial by Evan Yeung ([commit](https://github.com/facebook/relay/commit/c13dbb18f180)) +- Delete legacy API docs (with broken links) by Monica Tang ([commit](https://github.com/facebook/relay/commit/a178610d4c06)) +- Fix subscription call signature in observeFragment docs by Jordan Eldredge ([commit](https://github.com/facebook/relay/commit/6d10effc2d56)) +- Fix broken markdown in OssOnly blocks by Itamar Kestenbaum ([commit](https://github.com/facebook/relay/commit/caf95e9700b0)) +- Fix broken markdown in FbInternalOnly blocks by Itamar Kestenbaum ([commit](https://github.com/facebook/relay/commit/bcd5c070fbce)) +- Document configuring relay runtime globally (#4906) by Jordan Eldredge ([commit](https://github.com/facebook/relay/commit/d51fae0970ed)) +- Update docs on usePaginationFragement & transitions in Relay tutorial. (#4842) by Daniel Stocks ([commit](https://github.com/facebook/relay/commit/3bec307e636e)) +- fix mistake in refetching-queries-with-different-data by Lynn Yu ([commit](https://github.com/facebook/relay/commit/61c7de97d28b)) +- Add documentation for RecordSourceProxy by Lynn Yu ([commit](https://github.com/facebook/relay/commit/3151894ad717)) +- Remove dead links from Relay users page by Evan Yeung ([commit](https://github.com/facebook/relay/commit/d91fa3fadd45)) +- Add documentation for Relay performance logger by Tianyu Yao ([commit](https://github.com/facebook/relay/commit/d64b0e9e4bc0)) +- Add docs for @gqlField resolvers by Evan Yeung ([commit](https://github.com/facebook/relay/commit/29fb05b5a3bd)) +- Document resolvers returning abstract types by Jordan Eldredge ([commit](https://github.com/facebook/relay/commit/620e7a63f23f)) +- Start to fix `Updating Data` section of relay docs by Lynn Yu ([commit](https://github.com/facebook/relay/commit/e946bb41a2c7)) +- make it clear that 3D `@module` will only work if each fragment is on a different concrete type by Lynn Yu ([commit](https://github.com/facebook/relay/commit/87432667c110)) +- Fix typo in documentation by Allan Spreys ([commit](https://github.com/facebook/relay/commit/a8a075079ae3)) + +## Experimental Changes +- Fix relay reader module import bug by Lynn Yu ([commit](https://github.com/facebook/relay/commit/9eb1ad09a5d0)) +- Load component module earlier into record store 1/2 by Lynn Yu ([commit](https://github.com/facebook/relay/commit/9d1fe445d97d)) +- Generate data driven dependencies for static resources used by exec resolver normalization artifacts by Lynn Yu ([commit](https://github.com/facebook/relay/commit/9ba58ebc2b81)) +- add ability to configure operationModuleProvider path separately from componentModuleProvider by Lynn Yu ([commit](https://github.com/facebook/relay/commit/543918ee73ee)) +- Check for .read_time_resolvers in module_metadata before adding exec time directive in split_module_import by Lynn Yu ([commit](https://github.com/facebook/relay/commit/2fd469ae95d7)) +- add exec time resolvers directive to split module import by Lynn Yu ([commit](https://github.com/facebook/relay/commit/07ef8d231aa5)) +- Generate exec time ASTs in rootFragment $normalization files by Tianyu Yao ([commit](https://github.com/facebook/relay/commit/aa6f91aeca86)) +- Compiler client 3D changes to support exec time resolvers by Lynn Yu ([commit](https://github.com/facebook/relay/commit/52f1e24926a3)) +- Create feature flag for typename prefixing of ids by Monica Tang ([commit](https://github.com/facebook/relay/commit/ae9ed10010ba)) +- support client 3d on concrete objects by Lynn Yu ([commit](https://github.com/facebook/relay/commit/78e1499ba1a9)) +- Add test cases for @defer behavior in read time resolvers by Tianyu Yao ([commit](https://github.com/facebook/relay/commit/0cd69573cf21)) +- Modify _readClientSideDirectiveField in RelayReader to work for exec time resolvers by Lynn Yu ([commit](https://github.com/facebook/relay/commit/6b38703d0fce)) +- Allow @match on client edges by Tianyu Yao ([commit](https://github.com/facebook/relay/commit/ee924ca4a526)) +- Add enabledProvider argument inside @exec_time_resolvers directive (runtime + artifacts) by Lynn Yu ([commit](https://github.com/facebook/relay/commit/e29e1f947be8)) +- Modify useLazyLoadClientQuery to take a flag for exec time resolvers on or off by Lynn Yu ([commit](https://github.com/facebook/relay/commit/322d60a4cc31)) +- amend compiler changes to handle exec_time_resolvers ONLY case differently by Lynn Yu ([commit](https://github.com/facebook/relay/commit/2e41a4518fa2)) +- COMPILER changes add enabled argument inside @exec_time_resolvers directive by Lynn Yu ([commit](https://github.com/facebook/relay/commit/8c32ec4bab1b)) +- Parse the @gqlField docblock for the description and deprecated tag by Evan Yeung ([commit](https://github.com/facebook/relay/commit/89872b1e3f00)) +- Add logic to find property lookup resolver docblocks by Evan Yeung ([commit](https://github.com/facebook/relay/commit/1c02417a669c)) +- Pipe information through compiler for simplified resolver generation for property lookup resolvers by Evan Yeung ([commit](https://github.com/facebook/relay/commit/24951b9b6511)) diff --git a/website/blog/2025-06-13-relay-20.mdx b/website/blog/2025-06-13-relay-20.mdx new file mode 100644 index 0000000000000..707894073ac9f --- /dev/null +++ b/website/blog/2025-06-13-relay-20.mdx @@ -0,0 +1,66 @@ +--- +title: Relay v20.0 +author: The Relay Team +hide_table_of_contents: false +--- + +# Version 20.0.0 Release Notes + +## Announcement: ESLint Plugin v2.0.0 Released +Relay's ESLint plugin, [eslint-plugin-relay](https://github.com/relayjs/eslint-plugin-relay), was recently updated to v2.0.0. This release includes a number of compatibility updates and removes a couple of deprecated rules. For more info, see the [eslint-plugin-relay changelog](https://github.com/relayjs/eslint-plugin-relay/blob/main/CHANGELOG.md). + +## Generated Documentation +This release includes a brand new page covering the [Relay compiler config](https://relay.dev/docs/next/getting-started/compiler-config/). This has largely been undocumented so far and now includes autogenerated documentation! We also added tooling to autogenerate API docs from source code. The documentation for [`useRelayEnvironment`](https://relay.dev/docs/next/api-reference/use-relay-environment/) and [`useLazyLoadQuery`](https://relay.dev/docs/next/api-reference/use-lazy-load-query/) are examples of the new autogenerated tooling. + +## Breaking Changes +- Deprecate returning non-model weak types from resolvers. If you were using client side resolvers with the `@outputType` directive, these must be migrated to be strong or weak objects. You can continue to use the `@outputType` directive by enabling the `allow_output_type_resolvers` feature flag in the compiler config. (#5004) by Jordan Eldredge ([commit](https://github.com/facebook/relay/commit/8fbc0b9cc87f)) + +## Bug fixes +- Fix operation cleanup in RelayModernMockEnvironment by Martin Booth ([commit](https://github.com/facebook/relay/commit/239f6d9351b1)) +- Fix nested @defer + 3D when server doesn't support streaming by Tianyu Yao ([commit](https://github.com/facebook/relay/commit/00594e1b7141)) +- Fix nested @defer when server doesn't support streaming by Tianyu Yao ([commit](https://github.com/facebook/relay/commit/b801db036d13)) +- Fix incremental bug where resolver artifacts were not cleaned up by Jordan Eldredge ([commit](https://github.com/facebook/relay/commit/b270988c0849)) +- Fetch missing client edge server queries discovered in nested fragments (#4992) by Jordan Eldredge ([commit](https://github.com/facebook/relay/commit/611f6d9cfc99)) +- Pass client edge context to resolver root fragments by Jordan Eldredge ([commit](https://github.com/facebook/relay/commit/4177f3e27b26)) +- Fix variable name for imported model resolvers when using ES module imports (#4984) by Jordan Eldredge ([commit](https://github.com/facebook/relay/commit/1073517d9842)) +- Fix excluding generated dir under xplat_react by Tianyu Yao ([commit](https://github.com/facebook/relay/commit/9cbd126f113e)) + +## Improvements +- Add more time loggings in try_saved_state by Tianyu Yao ([commit](https://github.com/facebook/relay/commit/cba7007c7aaf)) +- Log saved state info query time in relay compiler by Lynn Yu ([commit](https://github.com/facebook/relay/commit/60b906b921a4)) +- Annotate read time resolver promises by Tianyu Yao ([commit](https://github.com/facebook/relay/commit/5ec796e48509)) +- Add VSCode tasks for common commands (#5003) by Jordan Eldredge ([commit](https://github.com/facebook/relay/commit/6ab766a3e684)) +- Surface prefetchExpiryInHours from the entrypoint by Alice Liu ([commit](https://github.com/facebook/relay/commit/ca7ddea7ed4b)) +- Regression test for client edge server data discovered missing in resolver root fragment (#4994) by Jordan Eldredge ([commit](https://github.com/facebook/relay/commit/48c81ad22bbf)) +- Run client edge tests for both versions of useFragment by Jordan Eldredge ([commit](https://github.com/facebook/relay/commit/16cd20078c84)) +- Update babel transform to default to default to ES modules to match compiler behavior (#4982) by Jordan Eldredge ([commit](https://github.com/facebook/relay/commit/281d76a0b0b4)) +- Stop unnecessary runtime transforms by Lynn Yu ([commit](https://github.com/facebook/relay/commit/1eca4a5720ee)) +- Support enquoted field alias by Arseniy Panfilov ([commit](https://github.com/facebook/relay/commit/1182ca64e0a0)) +- Parser option to allow literal string aliases by Arseniy Panfilov ([commit](https://github.com/facebook/relay/commit/fb9f4d7fcb10)) + +## Documentation Improvements +- Update graphql.md readability (#5018) by Josh Maloon ([commit](https://github.com/facebook/relay/commit/9facd47af415)) +- Add argument definitions to usePaginationFragment docs (#5015) by DrillableBit ([commit](https://github.com/facebook/relay/commit/2b85d3740f40)) +- Remove docs on comet_routing_prefetch by Nithik Balachandran ([commit](https://github.com/facebook/relay/commit/5cb92f41b9fc)) +- Fix typo in description of the @throwOnFieldError directive by Marius Schulz ([commit](https://github.com/facebook/relay/commit/abedb68a3ad1)) +- A couple grammar fixes (#5008) by Roman A ([commit](https://github.com/facebook/relay/commit/8c30f80fe6a2)) +- Replace out of date compiler readme with link to up-to-date docs page (#5005) by Jordan Eldredge ([commit](https://github.com/facebook/relay/commit/2c773876c7e8)) +- Handle args and returns, convert useLazyLoadQuery by Tianyu Yao ([commit](https://github.com/facebook/relay/commit/c3b70bc3a787)) +- Parse some docblock syntax and gen markdown for `useRelayEnvironment` by Tianyu Yao ([commit](https://github.com/facebook/relay/commit/9b7f643c006b)) +- Pass compiler config json schema as a prop (#4997) by Jordan Eldredge ([commit](https://github.com/facebook/relay/commit/9f242bb00f54)) +- Add docs page describing the Relay lint rules by Jordan Eldredge ([commit](https://github.com/facebook/relay/commit/7eca724d0aea)) +- Prevent json schema css for h2 from leaking into the rest of the site by Jordan Eldredge ([commit](https://github.com/facebook/relay/commit/35229211f119)) +- Compiler Docs: Improve separators in JSON arrays and objects (#4990) by Jordan Eldredge ([commit](https://github.com/facebook/relay/commit/e59619e23998)) +- Make comment into doc comment in config struct (#4989) by Jordan Eldredge ([commit](https://github.com/facebook/relay/commit/0b10aa591cf1)) +- Fill in missing documentation in compiler config by Jordan Eldredge ([commit](https://github.com/facebook/relay/commit/70b27dfdfa16)) +- Add page documenting the compiler config (#4985) by Jordan Eldredge ([commit](https://github.com/facebook/relay/commit/50fcf8726308)) +- Add missing generic type parameter for mutation tutorial example (#4981) by Joey Yu ([commit](https://github.com/facebook/relay/commit/f25a9d19aa91)) +- Fixups to quickstart (#4983) by Jordan Eldredge ([commit](https://github.com/facebook/relay/commit/3de18da10cdf)) +- Update quick start guide to align with new version of Relay by Jordan Eldredge ([commit](https://github.com/facebook/relay/commit/19c33bbb4d02)) + +# Experimental Changes +- Do not mark a query as inactive when waiting for resolver or server payload by Tianyu Yao ([commit](https://github.com/facebook/relay/commit/dce1a2eaa7ec)) +- In a exec time query, mark query as completed if all initial payloads are received by Tianyu Yao ([commit](https://github.com/facebook/relay/commit/ba09a88925c0)) +- Fix operation executor for exec time by Tianyu Yao ([commit](https://github.com/facebook/relay/commit/4136a8cd71d9)) +- Expose a loadClientQuery helper by Tianyu Yao ([commit](https://github.com/facebook/relay/commit/21926a531f4f)) +- Support normalized responses in OperationExecutor by Tianyu Yao ([commit](https://github.com/facebook/relay/commit/f3a9afa895ba)) diff --git a/website/docs/api-reference/graphql/graphql-directives.md b/website/docs/api-reference/graphql/graphql-directives.md index a8dfbacc760d7..eb7f1a824beed 100644 --- a/website/docs/api-reference/graphql/graphql-directives.md +++ b/website/docs/api-reference/graphql/graphql-directives.md @@ -23,10 +23,14 @@ appropriate runtime artifacts. These directives only appear in your application code and are removed from requests sent to your GraphQL server. + **Note:** The Relay compiler will maintain any directives supported by your server (such as `@include` or `@skip`) so they remain part of the request to the GraphQL server and won't alter generated runtime artifacts. + + **Note:** The Relay compiler will maintain any directives supported by your server (such as `@include` or `@skip`) so they remain part of the request to the GraphQL server and won't alter generated runtime artifacts. Additional directives are documented [here](https://www.internalfb.com/intern/wiki/GraphQL/APIs_and_References/Directives/#graphql-standard). + ## `@arguments` @@ -184,8 +188,8 @@ graphql` ## `@relay(plural: Boolean)` -When defining a fragment for use with a Fragment container, you can use the -`@relay(plural: true)` directive to indicate that container expects the prop for +When defining a fragment for use with `useFragment`, you can use the +`@relay(plural: true)` directive to indicate that the hook expects the prop for that fragment to be a list of items instead of a single item. A query or parent that spreads a `@relay(plural: true)` fragment should do so within a plural field (ie a field backed by a diff --git a/website/docs/api-reference/hooks/_use-lazy-load-query-extra.md b/website/docs/api-reference/hooks/_use-lazy-load-query-extra.md new file mode 100644 index 0000000000000..3613600d2b6cc --- /dev/null +++ b/website/docs/api-reference/hooks/_use-lazy-load-query-extra.md @@ -0,0 +1,16 @@ +### Behavior + +* It is expected for `useLazyLoadQuery` to have been rendered under a [`RelayEnvironmentProvider`](../relay-environment-provider), in order to access the correct Relay environment, otherwise an error will be thrown. +* Calling `useLazyLoadQuery` will fetch and render the data for this query, and it may [*_suspend_*](../../guided-tour/rendering/loading-states) while the network request is in flight, depending on the specified `fetchPolicy`, and whether cached data is available, or if it needs to send and wait for a network request. If `useLazyLoadQuery` causes the component to suspend, you'll need to make sure that there's a `Suspense` ancestor wrapping this component in order to show the appropriate loading state. + * For more details on Suspense, see our [Loading States with Suspense](../../guided-tour/rendering/loading-states/) guide. +* The component is automatically subscribed to updates to the query data: if the data for this query is updated anywhere in the app, the component will automatically re-render with the latest updated data. +* After a component using `useLazyLoadQuery` has committed, re-rendering/updating the component will not cause the query to be fetched again. + * If the component is re-rendered with *different query variables,* that will cause the query to be fetched again with the new variables, and potentially re-render with different data. + * If the component *unmounts and remounts*, that will cause the current query and variables to be refetched (depending on the `fetchPolicy` and the state of the cache). + +### Differences with `QueryRenderer` + +* `useLazyLoadQuery` no longer takes a Relay environment as a parameter, and thus no longer sets the environment in React Context, like `QueryRenderer` did. Instead, `useLazyLoadQuery` should be used as a descendant of a [`RelayEnvironmentProvider`](../relay-environment-provider), which now sets the Relay environment in Context. Usually, you should render a single `RelayEnvironmentProvider` at the very root of the application, to set a single Relay environment for the whole application. +* `useLazyLoadQuery` will use [Suspense](../../guided-tour/rendering/loading-states) to allow developers to render loading states using Suspense boundaries, and will throw errors if network errors occur, which can be caught and rendered with Error Boundaries. This as opposed to providing error objects or null props to the `QueryRenderer` render function to indicate errors or loading states. +* `useLazyLoadQuery` fully supports fetch policies in order to reuse data that is cached in the Relay store instead of solely relying on the network response cache. +* `useLazyLoadQuery` has better type safety guarantees for the data it returns, which was not possible with QueryRenderer since we couldn't parametrize the type of the data with a renderer api. diff --git a/website/docs/api-reference/hooks/load-query.md b/website/docs/api-reference/hooks/load-query.md index 9501d0a0984b6..5e635910dc492 100644 --- a/website/docs/api-reference/hooks/load-query.md +++ b/website/docs/api-reference/hooks/load-query.md @@ -56,7 +56,7 @@ const queryReference = loadQuery( * `query`: GraphQL query to fetch, specified using a `graphql` template literal, or a preloadable concrete request, which can be acquired by requiring the file `$Parameters.graphql`. Relay will only generate the `$Parameters` file if the query is annotated with `@preloadable`. * `variables`: Object containing the variable values to fetch the query. These variables need to match GraphQL variables declared inside the query. * `options`: *_[Optional]_* options object - * `fetchPolicy`: Determines if cached data should be used, and whether to send a network request based on the cached data that is currently available in the Relay store (for more details, see our [Fetch Policies](../../guided-tour/reusing-cached-data/fetch-policies) and [Garbage Collection](../../guided-tour/reusing-cached-data/availability-of-data) guides): + * `fetchPolicy`: Determines if cached data should be used, and whether to send a network request based on the cached data that is currently available in the Relay store (for more details, see our [Fetch Policies](../../guided-tour/reusing-cached-data/fetch-policies) and [Garbage Collection](../../guided-tour/reusing-cached-data/presence-of-data) guides): * "store-or-network": **(default)** *will* reuse locally cached data and will *only* send a network request if any data for the query is missing. If the query is fully cached, a network request will *not* be made. * "store-and-network": *will* reuse locally cached data and will *always* send a network request, regardless of whether any data was missing from the local cache or not. * "network-only": *will not* reuse locally cached data, and will *always* send a network request to fetch the query, ignoring any data that might be locally cached in Relay. diff --git a/website/docs/api-reference/hooks/use-lazy-load-query.md b/website/docs/api-reference/hooks/use-lazy-load-query.md index 1a80e04b4a9a6..d5e3bfedab2c9 100644 --- a/website/docs/api-reference/hooks/use-lazy-load-query.md +++ b/website/docs/api-reference/hooks/use-lazy-load-query.md @@ -39,39 +39,24 @@ function App() { ### Arguments -* `query`: GraphQL query specified using a `graphql` template literal. +* `gqlQuery`: GraphQL query specified using a `graphql` template literal. * `variables`: Object containing the variable values to fetch the query. These variables need to match GraphQL variables declared inside the query. * `options`: _*[Optional]*_ options object - * `fetchPolicy`: Determines if cached data should be used, and when to send a network request based on the cached data that is currently available in the Relay store (for more details, see our [Fetch Policies](../../guided-tour/reusing-cached-data/fetch-policies) and [Garbage Collection](../../guided-tour/reusing-cached-data/presence-of-data) guides): - * "store-or-network": _*(default)*_ *will* reuse locally cached data and will *only* send a network request if any data for the query is missing. If the query is fully cached, a network request will *not* be made. - * "store-and-network": *will* reuse locally cached data and will *always* send a network request, regardless of whether any data was missing from the local cache or not. - * "network-only": *will* *not* reuse locally cached data, and will *always* send a network request to fetch the query, ignoring any data that might be locally cached in Relay. - * "store-only": *will* *only* reuse locally cached data, and will *never* send a network request to fetch the query. In this case, the responsibility of fetching the query falls to the caller, but this policy could also be used to read and operate on data that is entirely [local](../../guided-tour/updating-data/local-data-updates). - * `fetchKey`: A `fetchKey` can be passed to force a re-evaluation of the current query and variables when the component re-renders, even if the variables didn't change, or even if the component isn't remounted (similarly to how passing a different `key` to a React component will cause it to remount). If the `fetchKey` is different from the one used in the previous render, the current query will be re-evaluated against the store, and it might be refetched depending on the current `fetchPolicy` and the state of the cache. - * `networkCacheConfig`: *_[Optional] _* Default value: `{force: true}`. Object containing cache config options for the *network layer*. Note that the network layer may contain an *additional* query response cache which will reuse network responses for identical queries. If you want to bypass this cache completely (which is the default behavior), pass `{force: true}` as the value for this option. + * `fetchPolicy`: _*[Optional]*_ Determines if cached data should be used, and when to send a network request based on the cached data that is currently available in the Relay store (for more details, see our [Fetch Policies](../../guided-tour/reusing-cached-data/fetch-policies) and [Garbage Collection](../../guided-tour/reusing-cached-data/presence-of-data) guides): + * "store-or-network": _*(default)*_ *will* reuse locally cached data and will *only* send a network request if any data for the query is missing. If the query is fully cached, a network request will *not* be made. + * "store-and-network": *will* reuse locally cached data and will *always* send a network request, regardless of whether any data was missing from the local cache or not. + * "network-only": *will* *not* reuse locally cached data, and will *always* send a network request to fetch the query, ignoring any data that might be locally cached in Relay. + * "store-only": *will* *only* reuse locally cached data, and will *never* send a network request to fetch the query. In this case, the responsibility of fetching the query falls to the caller, but this policy could also be used to read and operate on data that is entirely [local](../../guided-tour/updating-data/local-data-updates). + * `fetchKey`: _*[Optional]*_ A `fetchKey` can be passed to force a re-evaluation of the current query and variables when the component re-renders, even if the variables didn't change, or even if the component isn't remounted (similarly to how passing a different `key` to a React component will cause it to remount). If the `fetchKey` is different from the one used in the previous render, the current query will be re-evaluated against the store, and it might be refetched depending on the current `fetchPolicy` and the state of the cache. + * `networkCacheConfig`: _*[Optional]*_ Default value: `{force: true}`. Object containing cache config options for the *network layer*. Note that the network layer may contain an *additional* query response cache which will reuse network responses for identical queries. If you want to bypass this cache completely (which is the default behavior), pass `{force: true}` as the value for this option. + * `UNSTABLE_renderPolicy`: _*[Optional]*_ Undocumented option. ### Return Value -* `data`: Object that contains data which has been read out from the Relay store; the object matches the shape of specified query. - * The Flow type for data will also match this shape, and contain types derived from the GraphQL Schema. For example, the type of `data` above is: `{| user: ?{| name: ?string |} |}`. - -### Behavior - -* It is expected for `useLazyLoadQuery` to have been rendered under a [`RelayEnvironmentProvider`](../relay-environment-provider), in order to access the correct Relay environment, otherwise an error will be thrown. -* Calling `useLazyLoadQuery` will fetch and render the data for this query, and it may [*_suspend_*](../../guided-tour/rendering/loading-states) while the network request is in flight, depending on the specified `fetchPolicy`, and whether cached data is available, or if it needs to send and wait for a network request. If `useLazyLoadQuery` causes the component to suspend, you'll need to make sure that there's a `Suspense` ancestor wrapping this component in order to show the appropriate loading state. - * For more details on Suspense, see our [Loading States with Suspense](../../guided-tour/rendering/loading-states/) guide. -* The component is automatically subscribed to updates to the query data: if the data for this query is updated anywhere in the app, the component will automatically re-render with the latest updated data. -* After a component using `useLazyLoadQuery` has committed, re-rendering/updating the component will not cause the query to be fetched again. - * If the component is re-rendered with *different query variables,* that will cause the query to be fetched again with the new variables, and potentially re-render with different data. - * If the component *unmounts and remounts*, that will cause the current query and variables to be refetched (depending on the `fetchPolicy` and the state of the cache). - -### Differences with `QueryRenderer` - -* `useLazyLoadQuery` no longer takes a Relay environment as a parameter, and thus no longer sets the environment in React Context, like `QueryRenderer` did. Instead, `useLazyLoadQuery` should be used as a descendant of a [`RelayEnvironmentProvider`](../relay-environment-provider), which now sets the Relay environment in Context. Usually, you should render a single `RelayEnvironmentProvider` at the very root of the application, to set a single Relay environment for the whole application. -* `useLazyLoadQuery` will use [Suspense](../../guided-tour/rendering/loading-states) to allow developers to render loading states using Suspense boundaries, and will throw errors if network errors occur, which can be caught and rendered with Error Boundaries. This as opposed to providing error objects or null props to the `QueryRenderer` render function to indicate errors or loading states. -* `useLazyLoadQuery` fully supports fetch policies in order to reuse data that is cached in the Relay store instead of solely relying on the network response cache. -* `useLazyLoadQuery` has better type safety guarantees for the data it returns, which was not possible with QueryRenderer since we couldn't parametrize the type of the data with a renderer api. - +- `data`: Object that contains data which has been read out from the Relay store; the object matches the shape of specified query. + - The Flow type for data will also match this shape, and contain types derived from the GraphQL Schema. For example, the type of `data` above is: `{| user: ?{| name: ?string |} |}`. +import UseLazyLoadQueryExtra from './_use-lazy-load-query-extra.md'; + diff --git a/website/docs/api-reference/hooks/use-pagination-fragment.md b/website/docs/api-reference/hooks/use-pagination-fragment.md index 19d0f0c4770b1..2eccc563e493e 100644 --- a/website/docs/api-reference/hooks/use-pagination-fragment.md +++ b/website/docs/api-reference/hooks/use-pagination-fragment.md @@ -40,6 +40,10 @@ function FriendsList(props: Props) { } = usePaginationFragment( graphql` fragment FriendsListComponent_user on User + @argumentDefinitions( + count: { type: "Int", defaultValue: 5 } + cursor: { type: "String" } + ) @refetchable(queryName: "FriendsListPaginationQuery") { name friends(first: $count, after: $cursor) diff --git a/website/docs/api-reference/hooks/use-prefetchable-forward-pagination-fragment.md b/website/docs/api-reference/hooks/use-prefetchable-forward-pagination-fragment.md index 296e615d9a6ef..0cb81b1a6afb6 100644 --- a/website/docs/api-reference/hooks/use-prefetchable-forward-pagination-fragment.md +++ b/website/docs/api-reference/hooks/use-prefetchable-forward-pagination-fragment.md @@ -2,7 +2,7 @@ id: use-prefetchable-forward-pagination-fragment title: usePrefetchableForwardPaginationFragment slug: /api-reference/use-prefetchable-forward-pagination-fragment/ -description: API reference for usePrefetchableForwardPaginationFragment_EXPERIMENTAL, an experimental React hook used to paginate a connection and automatically prefetches +description: API reference for usePrefetchableForwardPaginationFragment, an experimental React hook used to paginate a connection and automatically prefetches keywords: - pagination - connection @@ -12,14 +12,14 @@ keywords: import DocsRating from '@site/src/core/DocsRating'; NOTE: This is an experimental API and may be subject to change. -`usePrefetchableForwardPaginationFragment_EXPERIMENTAL` is similar to [`usePaginationFragment`](../use-pagination-fragment). It adds the capability to automatically prefetch a `bufferSize` number of items to fill the buffer without displaying the items. And when `loadNext` is called, it vends from the buffer first to achieve faster pagination. It only supports forward pagination (provides APIs for `loadNext`, `hasNext` and `isLoadingNext`) for now. +`usePrefetchableForwardPaginationFragment` is similar to [`usePaginationFragment`](../use-pagination-fragment). It adds the capability to automatically prefetch a `bufferSize` number of items to fill the buffer without displaying the items. And when `loadNext` is called, it vends from the buffer first to achieve faster pagination. It only supports forward pagination (provides APIs for `loadNext`, `hasNext` and `isLoadingNext`) for now. ```js import type {FriendsList_user$key} from 'FriendsList_user.graphql'; const React = require('React'); -const {graphql, usePrefetchableForwardPaginationFragment_EXPERIMENTAL} = require('react-relay'); +const {graphql, usePrefetchableForwardPaginationFragment} = require('react-relay'); type Props = { user: FriendsList_user$key, @@ -33,7 +33,7 @@ function FriendsList(props: Props) { hasNext, isLoadingNext, refetch, // For refetching connection - } = usePrefetchableForwardPaginationFragment_EXPERIMENTAL( + } = usePrefetchableForwardPaginationFragment( graphql` fragment FriendsListComponent_user on User @refetchable(queryName: "FriendsListPaginationQuery") { diff --git a/website/docs/api-reference/hooks/use-subscription.md b/website/docs/api-reference/hooks/use-subscription.md index 01a4822232488..953cae3eccab1 100644 --- a/website/docs/api-reference/hooks/use-subscription.md +++ b/website/docs/api-reference/hooks/use-subscription.md @@ -41,7 +41,7 @@ function UserComponent({ id }) { ### Arguments -* `config`: a config of type [`GraphQLSubscriptionConfig`](#type-graphqlsubscriptionconfigtsubscriptionpayload) passed to [`requestSubscription`](../request-subscription/) +* `config`: a memoized config of type [`GraphQLSubscriptionConfig`](#type-graphqlsubscriptionconfigtsubscriptionpayload) passed to [`requestSubscription`](../request-subscription/) * `requestSubscriptionFn`: `?(IEnvironment, GraphQLSubscriptionConfig) => Disposable`. An optional function with the same signature as [`requestSubscription`](../request-subscription/), which will be called in its stead. Defaults to `requestSubscription`. diff --git a/website/docs/api-reference/legacy-apis/legacy-apis.md b/website/docs/api-reference/legacy-apis/legacy-apis.md deleted file mode 100644 index 0c06eb626c42b..0000000000000 --- a/website/docs/api-reference/legacy-apis/legacy-apis.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -id: legacy-apis -title: Legacy APIs -slug: /api-reference/legacy-apis/ -description: API reference for legacy APIs -keywords: - - QueryRenderer - - Container ---- - -API references for our previous legacy APIs are available in our previous docs website: - -- [`QueryRenderer`](https://relay.dev/docs/en/v10.1.3/query-renderer) -- [`Fragment Container`](https://relay.dev/docs/en/v10.1.3/fragment-container) -- [`Refetch Container`](https://relay.dev/docs/en/v10.1.3/refetch-container) -- [`Pagination Container`](https://relay.dev/docs/en/v10.1.3/pagination-container) -- [`Mutations`](https://relay.dev/docs/en/v10.1.3/mutations) -- [`Subscriptions`](https://relay.dev/docs/en/v10.1.3/subscriptions) diff --git a/website/docs/api-reference/relay-runtime/commit-mutation.md b/website/docs/api-reference/relay-runtime/commit-mutation.md index 9a08ff5a717f0..3eec1a6093c2d 100644 --- a/website/docs/api-reference/relay-runtime/commit-mutation.md +++ b/website/docs/api-reference/relay-runtime/commit-mutation.md @@ -16,7 +16,7 @@ import Disposable from '../types/Disposable.md'; Imperatively execute a mutation. -See also the [`useMutation`](../use-mutation/) API and [Guide to Updating Data](../../guided-tour/updating-data/). +See also the [`useMutation`](../use-mutation/) API and [Guide to Updating Data](../../guided-tour/updating-data/introduction.md). ```js import type {FeedbackLikeMutation} from 'FeedbackLikeMutation.graphql'; diff --git a/website/docs/api-reference/relay-runtime/fetch-query.md b/website/docs/api-reference/relay-runtime/fetch-query.md index 3f977530ddbad..5ebcc811faa59 100644 --- a/website/docs/api-reference/relay-runtime/fetch-query.md +++ b/website/docs/api-reference/relay-runtime/fetch-query.md @@ -72,14 +72,16 @@ fetchQuery( -> The `next` function may be called multiple times when using Relay's [Incremental Data Delivery](../../guides/incremental-data-delivery/) capabilities to receive multiple payloads from the server. +:::info +The `next` function may be called multiple times when using Relay's [Incremental Data Delivery](../../guides/incremental-data-delivery/) capabilities to receive multiple payloads from the server. +::: ### Behavior * `fetchQuery` will automatically save the fetched data to the in-memory Relay store, and notify any components subscribed to the relevant data. -* `fetchQuery` will **NOT** retain the data for the query, meaning that it is not guaranteed that the data will remain saved in the Relay store at any point after the request completes. If you wish to make sure that the data is retained outside of the scope of the request, you need to call `environment.retain()` directly on the query to ensure it doesn't get deleted. See our section on [Controlling Relay's GC Policy](../../guided-tour/reusing-cached-data/availability-of-data) for more details. +* `fetchQuery` will **NOT** retain the data for the query, meaning that it is not guaranteed that the data will remain saved in the Relay store at any point after the request completes. If you wish to make sure that the data is retained outside of the scope of the request, you need to call `environment.retain()` directly on the query to ensure it doesn't get deleted. See our section on [Controlling Relay's GC Policy](../../guided-tour/reusing-cached-data/presence-of-data) for more details. * `fetchQuery` will automatically de-dupe identical network requests (same query and variables) that are in flight at the same time, and that were initiated with `fetchQuery`. diff --git a/website/docs/api-reference/relay-runtime/observe-fragment.md b/website/docs/api-reference/relay-runtime/observe-fragment.md index abf6b830f481f..e0d913c9efbe4 100644 --- a/website/docs/api-reference/relay-runtime/observe-fragment.md +++ b/website/docs/api-reference/relay-runtime/observe-fragment.md @@ -20,6 +20,10 @@ In some cases it can be useful to define data that you wish to read using a Grap To read a fragment's data just once, see [`waitForFragmentData`](./wait-for-fragment-data.md). +:::caution +When using `observeFragment` with a plural fragment, the current implementation notifies the subscription multiple times if a store update impacting multiple list items gets published. Since the notifications happen synchronously, it is advised to debounce for a tick and only use the last payload for batching. +::: + ### Example ```ts @@ -47,17 +51,19 @@ function MyComponent({ key }) { } `, user, - ).subscribe(result => { - switch(result.kind) { - case "loading": - window.title = "...loading"; - break; - case "error": - window.title = "Oops, we hit an error"; - break; - case "ok": - window.title = `Welcome ${result.value.name}`; - break; + ).subscribe({ + next: (result) => { + switch(result.kind) { + case "loading": + window.title = "...loading"; + break; + case "error": + window.title = "Oops, we hit an error"; + break; + case "ok": + window.title = `Welcome ${result.value.name}`; + break; + } } }); return () => { diff --git a/website/docs/api-reference/relay-runtime/relay-environment.md b/website/docs/api-reference/relay-runtime/relay-environment.md new file mode 100644 index 0000000000000..bb4a2dddd86f7 --- /dev/null +++ b/website/docs/api-reference/relay-runtime/relay-environment.md @@ -0,0 +1,52 @@ +--- +id: relay-environment +title: Relay Environment +slug: /api-reference/relay-runtime/relay-environment +description: Setting up a Relay Environment +--- + +The core of Relay's runtime is the `Environment`. The environment knows how to make requests to your GraphQL server and contains the `Store`, Relay's normalized data cache. Generally your application will construct a single environment which is configured to fetch data from your server, and then expose that environment to all of your components via `RelayEnvironmentProvider`. + +## Creating an Environment + +To create your environment you must provide two key pieces, a [`Network`](../../guides/network-layer.md) and a [`Store`](store.md). + +The `Network` is responsible for making requests to your GraphQL server. The `Store` holds the normalized data cache. + +A minimal implementation of an environment might look like this: + +```ts title="RelayEnvironment.js" +import { Environment, Store, RecordSource, Network, FetchFunction } from "relay-runtime"; + +const HTTP_ENDPOINT = "https://graphql.org/graphql/"; + +const fetchGraphQL: FetchFunction = async (request, variables) => { + const resp = await fetch(HTTP_ENDPOINT, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ query: request.text, variables }), + }); + if (!resp.ok) { + throw new Error("Response failed."); + } + return await resp.json(); +}; + +export const environment = new Environment({ + store: new Store(new RecordSource({})), + network: Network.create(fetchGraphQL), +}); +``` + +## Advanced Configuration + +The Relay environment accepts a number of additional configuration options when it is created. These options are all optional, but can be used to customize the behavior of the environment. + +Notable options include: + +* `log` - A function that will be called with telemetry events. See the types for [`LogEvent`](https://github.com/facebook/relay/blob/0414c9ad0744483e349e07defcb6d70a52cf8b3c/packages/relay-runtime/store/RelayStoreTypes.js#L799) for a full list of events and their fields. +* [`missingFieldHandlers`](../../guided-tour/reusing-cached-data/filling-in-missing-data.md) - A list of handlers that will be called when a field is missing from the store. This can be used to enable fulfilling queries to fields like `Query.node` from cache. +* `getDataID` - A function that will be called to generate a unique ID for a given object. This can be used to customize the way that Relay generates IDs for objects if your server does not implement the [Global Object Identification spec](https://graphql.org/learn/global-object-identification/). +* [`relayFieldLogger`](./field-logger.md) - A function that will be called when Relay encounters a field-level error. + +For a full list of options, inspect the [provided TypeScript types](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/relay-runtime/lib/store/RelayModernEnvironment.d.ts#L26-L43). diff --git a/website/docs/api-reference/relay-runtime/request-subscription.md b/website/docs/api-reference/relay-runtime/request-subscription.md index 407d1b2a18b0f..0c3783803f0b8 100644 --- a/website/docs/api-reference/relay-runtime/request-subscription.md +++ b/website/docs/api-reference/relay-runtime/request-subscription.md @@ -15,8 +15,7 @@ import Disposable from '../types/Disposable.md'; ## `requestSubscription` Imperative API for establishing a GraphQL Subscription. -See also the [`useSubscription`](../use-subscription/) API and the [Guide to Updating Data](../../guided-tour/updating-data/). - +See also the [`useSubscription`](../use-subscription/) API and the [Guide to Updating Data](../../guided-tour/updating-data/introduction.md). ```js import {graphql, requestSubscription} from 'react-relay'; diff --git a/website/docs/api-reference/relay-runtime/runtime-configuration.md b/website/docs/api-reference/relay-runtime/runtime-configuration.md new file mode 100644 index 0000000000000..f510dd03649aa --- /dev/null +++ b/website/docs/api-reference/relay-runtime/runtime-configuration.md @@ -0,0 +1,52 @@ +--- +id: runtime-config +title: Runtime Configuration +slug: /api-reference/runtime-config/ +description: Configuring the Relay Runtime +keywords: + - feature flags + - configuration +--- + +## ConnectionInterface + +If your server's implementation of the Connection Spec differs from the default interface you will need to configure the Relay Runtime to expect the connection type and field names used in your schema. This is done by updating the global ConnectionInterface instance exported by Relay: + +:::note +You will also need to update your [Relay Compiler Config](../../getting-started/compiler-config/#ConnectionInterface) with these same values at the path `schemaConfig.connectionInterface`. +::: + +```ts title="/src/ConfigureRelay.ts" +import { ConnectionInterface } from 'relay-runtime'; + +// Note: This must match the values configured in the Relay compiler config. +ConnectionInterface.inject({ + END_CURSOR: 'end_cursor', + HAS_NEXT_PAGE: 'has_next_page', + HAS_PREV_PAGE: 'has_previous_page', + START_CURSOR: 'start_cursor', + PAGE_INFO: 'page_info', + NODE: 'node', + CURSOR: 'cursor', + EDGES: 'edges', + PAGE_INFO_TYPE: 'PageInfo', +}); +``` + +## Feature Flags + +:::warning +Feature Flags are used for enabling and configuring unstable Relay features, **regular use of Relay should not need to modify runtime feature flags**. They are documented here for purely informational purposes +::: + +Relay has a number of runtime options called "Feature Flags". In general, these are used for enabling experimental features which are not yet stable and thus not yet enabled by default. + +Feature flags in the Relay Runtime are implemented as a global mutable object. To set/configure a feature flag, import that object and mutate it. If you do this in the module scope, the updates will apply before Relay looks at them. + +```ts title="/src/ConfigureRelay.ts" +import { RelayFeatureFlags } from 'relay-runtime'; + +RelayFeatureFlags.ENABLE_SOME_EXPERIMENT = true; +``` + +You can find the full list of feature flags [here](https://github.com/facebook/relay/blob/203d8b10e9144a37466b8a72edbe6add48f64e7d/packages/relay-runtime/util/RelayFeatureFlags.js#L4), but keep in mind that **feature flags may change arbitrarily between versions of Relay**. diff --git a/website/docs/api-reference/relay-runtime/store.md b/website/docs/api-reference/relay-runtime/store.md index c5cae4a3ceaaa..4652fbdfd1b90 100644 --- a/website/docs/api-reference/relay-runtime/store.md +++ b/website/docs/api-reference/relay-runtime/store.md @@ -16,6 +16,7 @@ Table of Contents: - [RecordSourceSelectorProxy](#recordsourceselectorproxy) - [RecordProxy](#recordproxy) +- [RecordSourceProxy](#recordsourceproxy) - [ConnectionHandler](#connectionhandler) ## RecordSourceSelectorProxy @@ -46,7 +47,7 @@ const record = store.create(dataID, 'Todo'); ### `delete(dataID: string): void` -Deletes a record from the store given its `dataID`. +Deletes a record from the store given its `dataID`. For existing edges to the deleted record, `undefined` will be returned in the default case even when the value is typed as non-nullable. When [`@throwOnFieldError`](../../guides/throw-on-field-error-directive/) is present, the missing data will throw an error. #### Example @@ -83,6 +84,8 @@ Usage: ```javascript // Represents root query const root = store.getRoot(); +// Get the viewer linked record +const viewer = root.getLinkedRecord('viewer'); ``` ### `getRootField(fieldName: string): ?RecordProxy` @@ -454,6 +457,147 @@ After invalidating a record, any query that references the invalidated record an environment.check(query) === 'stale' ``` +## RecordSourceProxy + +The `RecordSourceProxy` serves as an interface to mutate record. + +:::danger +`RecordSourceProxy` exposes many low level APIs that are not typesafe. Users should consider using [typesafe updaters](../../guided-tour/updating-data/typesafe-updaters-faq/), [optimistic updates](../../guided-tour/updating-data/graphql-mutations/#optimistic-updates), and [relay resolvers](../../guides/relay-resolvers/introduction/) instead if their use case can be covered by these alternatives. +::: + +```javascript +interface RecordSourceProxy { + create(dataID: DataID, typeName: string): RecordProxy; + delete(dataID: DataID): void; + get(dataID: DataID): ?RecordProxy; + getRoot(): RecordProxy; + invalidateStore(): void; + readUpdatableFragment( + fragment: UpdatableFragment, + fragmentReference: HasUpdatableSpread, + ): UpdatableData; + readUpdatableQuery( + query: UpdatableQuery, + variables: TVariables, + ): UpdatableData; +} +``` + +### `create(dataID: DataID, typeName: string): RecordProxy` + +Creates a new record in the store given a `dataID` and the `typeName` as defined by the GraphQL schema. Returns a [`RecordProxy`](#recordproxy) which serves as an interface to mutate the newly created record. + +#### Example + +```javascript +const record = store.create(dataID, 'Todo'); +``` + +### `delete(dataID: DataID): void` + +Deletes a record from the store given its `dataID`. For existing edges to the deleted record, `undefined` will be returned in the default case even when the value is typed as non-nullable. When [`@throwOnFieldError`](../../guides/throw-on-field-error-directive/) is present, the missing data will throw an error. + +#### Example + +```javascript +store.delete(dataID); +``` + +### `get(dataID: DataID): ?RecordProxy` + +Retrieves a record from the store given its `dataID`. Returns a [`RecordProxy`](#recordproxy) which serves as an interface to read/mutate the record. + +#### Example + +```javascript +const record = store.get(dataID); +``` + +### `getRoot(): RecordProxy` + +Returns the [`RecordProxy`](#recordproxy) representing the root of the GraphQL document. + +#### Example + +Given the GraphQL document: + +```graphql +viewer { + id +} +``` + +Usage: + +```javascript +// Represents root query +const root = store.getRoot(); +// Get the viewer linked record +const viewer = root.getLinkedRecord('viewer'); +``` + +### `invalidateStore(): void;` + +Globally invalidates the Relay store. This will cause any data that was written to the store before invalidation occurred to be considered stale, and will be considered to require refetch the next time a query is checked with `environment.check()` or is fetched with a `store-or-network` [fetch policy](../../guided-tour/reusing-cached-data/fetch-policies/). + +#### Example + +```javascript +store.invalidateStore(); +``` + +After global invalidation, any query that is checked before refetching it will be considered stale: + +```javascript +environment.check(query) === 'stale' +``` + +### `readUpdatableFragment(fragment: UpdatableFragment,fragmentReference: HasUpdatableSpread): UpdatableData;` + +Fetches an updatable fragment from the store. This updatable fragment's fields can then be imperatively modified to update data in the store. + +For more information on updating the store imperatively, see this [section](../../guided-tour/updating-data/imperatively-modifying-store-data/) of the guided tour. + +#### Example +```javascript +const fragment = graphql` + fragment StoryLikeButton_updatable on Story @updatable { + likeCount + doesViewerLike + } +`; +const { + updatableData +} = store.readUpdatableFragment( + fragment, + story +); +updatableData.likeCount = updatableData.likeCount + 1 +``` + +### `readUpdatableQuery(query: UpdatableQuery,variables: TVariables): UpdatableData` + +Reads an updatable query from the store. This updatable query's fields can be imperatively modified to update data in the store. Unlike `readUpdatableFragment`, you do not need to pass in a `fragmentReference` as an input argument. + +For more information on updating the store imperatively, see this [section](../../guided-tour/updating-data/imperatively-modifying-store-data/) of the guided tour. + +#### Example + +```javascript +const {updatableData} = store.readUpdatableQuery( + graphql` + query NameUpdaterUpdateQuery @updatable { + viewer { + name + } + } + `, + {} +); +const viewer = updatableData.viewer; +viewer.name = newName; +``` + ## ConnectionHandler `ConnectionHandler` is a utility module exposed by `relay-runtime` that aids in the manipulation of connections. `ConnectionHandler` exposes the following interface: diff --git a/website/docs/debugging/inconsistent-typename-error.md b/website/docs/debugging/inconsistent-typename-error.md index 6944b91f4c2dd..650aea5b958f8 100644 --- a/website/docs/debugging/inconsistent-typename-error.md +++ b/website/docs/debugging/inconsistent-typename-error.md @@ -20,7 +20,9 @@ The GraphQL server likely violated the globally unique ID requirement by returni If you're seeing an error like: -> `RelayResponseNormalizer: Invalid record '543'. Expected __typename to be consistent, but the record was assigned conflicting types Foo and Bar. The GraphQL server likely violated the globally unique ID requirement by returning the same ID for different objects.` +``` +`RelayResponseNormalizer: Invalid record '543'. Expected __typename to be consistent, but the record was assigned conflicting types Foo and Bar. The GraphQL server likely violated the globally unique ID requirement by returning the same ID for different objects.` +``` the server implementation of one of the types is not spec compliant. We require the `id` field to be globally unique. This is a problem because Relay stores objects in a normalized key-value store and one of the object just overwrote the other. This means your app is broken in some subtle or less subtle way. diff --git a/website/docs/getting-started/babel-plugin.md b/website/docs/getting-started/babel-plugin.md new file mode 100644 index 0000000000000..5daab60584de2 --- /dev/null +++ b/website/docs/getting-started/babel-plugin.md @@ -0,0 +1,31 @@ +--- +id: babel-plugin +title: Relay Babel Plugin +slug: /getting-started/babel-plugin/ +description: Setting up Relay's Babel plugin +keywords: +- babel +--- +# Babel Plugin + +Relay requires a [Babel plugin](https://www.npmjs.com/package/babel-plugin-relay) to convert GraphQL to compiler-generated runtime artifacts. Depending upon what framework/bundler you are using, there may be a framwork-specific plugin you can use: + +- Vite: [vite-pugin-relay](https://github.com/oscartbeaumont/vite-plugin-relay) +- Next.js: [Relay config opton](https://nextjs.org/docs/architecture/nextjs-compiler#relay) +- SWC: [@swc/plugin-relay](https://www.npmjs.com/package/@swc/plugin-relay) + +If not, you can install the Babel plugin manually: + +```sh +yarn add --dev babel-plugin-relay graphql +``` + +Add `"relay"` to the list of plugins in your `.babelrc` file: + +```javascript +{ + "plugins": ["relay"] +} +``` + +Please note that the `"relay"` plugin should run before other plugins or presets to ensure the `graphql` template literals are correctly transformed. See Babel's [documentation on this topic](https://babeljs.io/docs/plugins/#pluginpreset-ordering). diff --git a/website/docs/getting-started/compiler-config.md b/website/docs/getting-started/compiler-config.md new file mode 100644 index 0000000000000..295bb33f2aa8c --- /dev/null +++ b/website/docs/getting-started/compiler-config.md @@ -0,0 +1,25 @@ +--- +id: compiler-config +title: Compiler Configuration +slug: /getting-started/compiler-config/ +description: Schema description of the Relay Compiler configuration +keywords: +- compiler +- configuration +- config +hide_table_of_contents: false +--- +import CompilerConfig from '@site/src/compiler-config/CompilerConfig'; +import schema from '@compilerConfigJsonSchema'; + +## Compiler Config Options + +For information about where the Relay compiler looks for its config file, or a minimal config, see the [Relay Compiler](./compiler.md#Configuration) page. + +If you need more advanced options of the Relay Compiler Config, the exhaustive full schema can be found below. The shape of the Relay Compiler Config is given as `ConfigFile`. Note that while the shapes are documented in pseudo TypeScript, the compiler is parsing them in Rust so some subtle differences may exist. + +:::tip +Install the [Relay VSCode extension](../editor-support.md) to get autocomplete, hover tips, and type checking for the options in your Relay config. +::: + + diff --git a/website/docs/getting-started/compiler.md b/website/docs/getting-started/compiler.md index 8c64afa197141..e4d433721dfcb 100644 --- a/website/docs/getting-started/compiler.md +++ b/website/docs/getting-started/compiler.md @@ -7,166 +7,72 @@ keywords: - compiler --- -import DocsRating from '@site/src/core/DocsRating'; -import {FbInternalOnly, OssOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; -import FbRunningCompiler from '../guides/fb/FbRunningCompiler.md'; -import FbGraphQLSchema from '../guides/fb/FbGraphQLSchema.md'; -import FbImportingGeneratedDefinitions from '../guides/fb/FbImportingGeneratedDefinitions.md'; +Relay depends upon ahead-of-time compilation of GraphQL queries and fragments to generate artifacts that are used at runtime. -## `graphql` +The Relay compiler is a command line tool that reads GraphQL fragments, queries, and mutations in your JavaScript code and generates TypeScript/Flow types and additional JavaScript code that gets included in your code via [Relay's Babel Plugin](./babel-plugin.md). -The `graphql` template tag provided by Relay serves as the mechanism to write queries, fragments, mutations and subscriptions in the [GraphQL](http://graphql.org/learn/) language. For example: +This guide explains configuring and using the Relay Compiler. -```javascript -import {graphql} from 'react-relay'; +## Configuration -graphql` - query MyQuery { - viewer { - id - } - } -`; -``` - -The result of using the `graphql` template tag is a `GraphQLTaggedNode`; a runtime representation of the GraphQL document. - -Note that `graphql` template tags are **never executed at runtime**. Instead, they are compiled ahead of time by the Relay compiler into generated artifacts that live alongside your source code, and which Relay requires to operate at runtime. - - -## Compiler - -Relay uses the Relay Compiler to convert [`graphql`](#graphql) literals into generated files that live alongside your source files. - -A fragment like the following: - -```javascript -graphql` - fragment MyComponent on Type { - field - } -` -``` - -Will cause a generated file to appear in `./__generated__/MyComponent.graphql.js`, -with both runtime artifacts (which help to read and write from the Relay Store) -and [Flow types](https://flow.org/) to help you write type-safe code. - -The Relay Compiler is responsible for generating code as part of a build step which can then be referenced at runtime. By building the query ahead of time, the Relay's runtime is not responsible for generating a query string, and various optimizations can be performed on the query that could be too expensive at runtime (for example, fields that are duplicated in the query can be merged during the build step, to improve efficiency of processing the GraphQL response). - -### GraphQL Schema - - - - +The Relay compiler will look for a Relay config in the following locations. It's up to you to decide which location works best for your project. - +* `relay.config.json` in your project root +* `relay.config.(js/mjs/ts)` in your project root +* A `"relay"` key in your `package.json` -To use the Relay Compiler, you need a `.graphql` [GraphQL Schema](https://graphql.org/learn/schema/) file, describing your GraphQL server's API. Typically these files are local representations of a server source of truth and are not edited directly. For example, we might have a `schema.graphql` like: +The Relay compiler config tells Relay things like where it can find your GraphQL schema and what language your code is written in. A minimal Relay compiler config looks like this: -```graphql -schema { - query: Root -} - -type Root { - dictionary: [Word] -} - -type Word { - id: String! - definition: WordDefinition -} - -type WordDefinition { - text: String - image: String +```json title="relay.config.json" +{ + "src": "./src", + "schema": "./schema.graphql", + "language": "typescript" } ``` - - -### Running the Compiler - - - - - - - -Additionally, you need a directory containing `.js` files that use the `graphql` tag to describe GraphQL queries and fragments. Let's call this `./src`. +:::tip +Install the [Relay VSCode extension](../editor-support.md) to get autocomplete, hover tips, and type checking for the options in your relay config. +::: -Then run `yarn run relay` as set up before. +The compiler config is very powerful, and includes many specialized configuration options. For a full enumeration of the available options see the [Compiler Configuration](./compiler-config.md) page. -This will create a series of `__generated__` directories that are co-located with the corresponding files containing `graphql` tags. -For example, given the two files: +## Running the compiler -- `src/Components/DictionaryComponent.js` +It is generally recommended that you add a `scripts` entry to your `package.json` to make it easy to run the Relay compiler for your project. -```javascript -const DictionaryWordFragment = graphql` - fragment DictionaryComponent_word on Word { - id - definition { - ...DictionaryComponent_definition - } +```json title="package.json" +{ + "scripts": { + // change-line + "relay": "relay-compiler" } -` - -const DictionaryDefinitionFragment = graphql` - fragment DictionaryComponent_definition on WordDefinition { - text - image - } -` +} ``` -- `src/Queries/DictionaryQuery.js` +With this added you can run the Relay compiler like so: -```javascript -const DictionaryQuery = graphql` - query DictionaryQuery { - dictionary { - ...DictionaryComponent_word - } - } -` +```sh +npm run relay ``` -This would produce three generated files, and two `__generated__` directories: - -- `src/Components/__generated__/DictionaryComponent_word.graphql.js` -- `src/Components/__generated__/DictionaryComponent_definition.graphql.js` -- `src/Queries/__generated__/DictionaryQuery.graphql.js` +### Watch mode - +If you have [watchman](https://facebook.github.io/watchman) installed you can pass `--watch` to the Relay compiler to have it continue running and automatically update generated files as you edit your product code: - -### Importing generated definitions - - - - - - - - -Typically you will not need to import your generated definitions. The [Relay Babel plugin](../../getting-started/installation-and-setup#setup-babel-plugin-relay) will then convert the `graphql` literals in your code into `require()` calls for the generated files. - -However the Relay Compiler also automatically generates [Flow](https://flow.org) types as [type comments](https://flow.org/en/docs/types/comments/). For example, you can import the generated Flow types like so: - -```javascript -import type {DictionaryComponent_word} from './__generated__/DictionaryComponent_word.graphql'; +```sh +npm run relay --watch ``` -More rarely, you may need to access a query, mutation, fragment or subscription from multiple files. In these cases, you can also import it directly: +### Codemods -```js -import DictionaryComponent_word from './__generated__/DictionaryComponent_word.graphql'; -``` +The Relay compiler supports some built in codemods. Learn more in the [Codemods Guide](../guides/codemods.md). - +### Help +To learn about the other capabilities of the Relay compiler see it's extensive `--help` output: - +```sh +npm run relay --help +``` diff --git a/website/docs/getting-started/installation-and-setup.md b/website/docs/getting-started/installation-and-setup.md deleted file mode 100644 index 57a685e534981..0000000000000 --- a/website/docs/getting-started/installation-and-setup.md +++ /dev/null @@ -1,150 +0,0 @@ ---- -id: installation-and-setup -title: Installing in a Project -slug: /getting-started/installation-and-setup/ -description: Relay installation and setup guide -keywords: -- installation -- setup -- compiler -- babel-plugin-relay ---- - -import DocsRating from '@site/src/core/DocsRating'; -import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; - -# Installation - -In many situations, the easiest way to install Relay is with the `create-relay-app` package written by Tobias Tengler. Contrary to what the name suggests, this package *installs* Relay on your existing app. - -It currently supports apps built on Next, Vite, and Create React App. If you aren't on one of those platforms, or if it doesn't work for you for some reason, proceed to the manual steps below. - -To run it, make sure you have a clean working directory and run: - -``` -npm create @tobiastengler/relay-app -``` - -(or use `yarn` or `pnpm` instead of `npm` as you prefer). - -When it's done it will print some "Next Steps" for you to follow. - -More details about this script can be found at its [GitHub repository](https://github.com/tobias-tengler/create-relay-app). - -* * * - -# Manual Installation - -Install React and Relay using `yarn` or `npm`: - -```sh -yarn add react react-dom react-relay -``` - -## Set up the compiler - -Relay's ahead-of-time compilation requires the [Relay Compiler](../../guides/compiler/), which you can install via `yarn` or `npm`: - -```sh -yarn add --dev relay-compiler -``` - -This installs the bin script `relay-compiler` in your node_modules folder. It's recommended to run this from a `yarn`/`npm` script by adding a script to your `package.json` file: - -```js -"scripts": { - "relay": "relay-compiler" -} -``` - -## Compiler configuration - -Create the configuration file: - -```javascript -// relay.config.js -module.exports = { - // ... - // Configuration options accepted by the `relay-compiler` command-line tool and `babel-plugin-relay`. - src: "./src", - language: "javascript", // "javascript" | "typescript" | "flow" - schema: "./data/schema.graphql", - excludes: ["**/node_modules/**", "**/__mocks__/**", "**/__generated__/**"], -} -``` - -This configuration also can be specified in `"relay"` section of the `package.json` file. -For more details, and configuration options see: [Relay Compiler Configuration](https://github.com/facebook/relay/tree/main/packages/relay-compiler) - - -## Set up babel-plugin-relay - -Relay requires a Babel plugin to convert GraphQL to runtime artifacts: - -```sh -yarn add --dev babel-plugin-relay graphql -``` - -Add `"relay"` to the list of plugins your `.babelrc` file: - -```javascript -{ - "plugins": [ - "relay" - ] -} -``` - -Please note that the `"relay"` plugin should run before other plugins or -presets to ensure the `graphql` template literals are correctly transformed. See -Babel's [documentation on this topic](https://babeljs.io/docs/plugins/#pluginpreset-ordering). - -Alternatively, instead of using `babel-plugin-relay`, you can use Relay with [babel-plugin-macros](https://github.com/kentcdodds/babel-plugin-macros). After installing `babel-plugin-macros` and adding it to your Babel config: - -```javascript -const graphql = require('babel-plugin-relay/macro'); -``` - -## Running the compiler - -After making edits to your application files, just run the `relay` script to generate new compiled artifacts: - -```sh -yarn run relay -``` - -Alternatively, you can pass the `--watch` option to watch for file changes in your source code and automatically re-generate the compiled artifacts (**Note:** Requires [watchman](https://facebook.github.io/watchman) to be installed): - -```sh -yarn run relay --watch -``` - -For more details, check out our [Relay Compiler docs](../../guides/compiler/). - -## JavaScript environment requirements - -The Relay packages distributed on NPM use the widely-supported ES5 -version of JavaScript to support as many browser environments as possible. - -However, Relay expects modern JavaScript global types (`Map`, `Set`, -`Promise`, `Object.assign`) to be defined. If you support older browsers and -devices which may not yet provide these natively, consider including a global -polyfill in your bundled application, such as [core-js][] or -[@babel/polyfill](https://babeljs.io/docs/usage/polyfill/). - -A polyfilled environment for Relay using [core-js][] to support older browsers -might look like: - -```javascript -require('core-js/es6/map'); -require('core-js/es6/set'); -require('core-js/es6/promise'); -require('core-js/es6/object'); - -require('./myRelayApplication'); -``` - -[core-js]: https://github.com/zloirock/core-js - - - diff --git a/website/docs/getting-started/lint-rules.md b/website/docs/getting-started/lint-rules.md new file mode 100644 index 0000000000000..46b07bc46d9ec --- /dev/null +++ b/website/docs/getting-started/lint-rules.md @@ -0,0 +1,87 @@ +--- +id: lint-rules +title: Relay ESLint Plugin +slug: /getting-started/lint-rules/ +description: ESLint Plugin Relay +keywords: +- eslint +- lint +--- + +One of the unique features enabled by Relay is the ability to statically detect unused GraphQL fields. This can [categorically prevent](https://relay.dev/blog/2023/10/24/how-relay-enables-optimal-data-fetching/) the "append only query" problem that is a common disfunction in many GraphQL clients. + +![Relay ESLint Plugin](../../static/img/docs/no-unused-fields.png) + +This validation, and other helpful checks, are enabled by Relay's ESLint plugin [`eslint-plugin-relay`](https://www.npmjs.com/package/eslint-plugin-relay). **The Relay ESLint plugin is a key part of the Relay developer experience**. + +## Installation + +Assuming you have [ESLint](https://eslint.org/) already installed, you can add the Relay ESLint plugin to your project by running: + +```sh +npm install --save-dev eslint-plugin-relay +``` + +Then update your ESLint configuration to include the plugin: + +```js tile="eslint.config.js" +import relay from 'eslint-plugin-relay'; + +export default [ + // ... other ESlint Config + { + plugins: { relay }, + rules: relay.configs['ts-recommended'].rules, + }, +]; +``` + +## Rule Descriptions + +The following validation rules are included in the Relay ESLint plugin: + +### `relay/unused-fields` +Ensures that every GraphQL field referenced is used within the module that includes it. This helps enable Relay's [optimal data fetching](https://relay.dev/blog/2023/10/24/how-relay-enables-optimal-data-fetching/). + +### `relay/no-future-added-value` +Ensures code does not try to explicitly handle the `"%future added value"` enum variant which Relay inserts as a placeholder to ensure you handle the possibility that new enum variants may be added by the server after your application has been deployed. + +### `relay/graphql-syntax` +Ensures each `graphql` tagged template literal contains syntactically valid GraphQL. This is also validated by the Relay Compiler, but the ESLint plugin can often provide faster feedback. + +### `relay/graphql-naming` +Ensures GraphQL fragments and queries follow Relay's naming conventions. This is also validated by the Relay Compiler, but the ESLint plugin can often provide faster feedback. + +### `relay/function-required-argument` +Ensures that `readInlineData` is always passed an explicit argument even though that argument is allowed to be `undefined` at runtime. + +### `relay/hook-required-argument` +Ensures that Relay hooks are always passed an explicit argument even though that argument is allowed to be `undefined` at runtime. + +### `relay/must-colocate-fragment-spreads` +Ensures that when a fragment spread is added within a module, that module directly imports the module which defines that fragment. This prevents the anti-pattern when one component fetches a fragment that is not used by a direct child component. +**Note**: This rule leans heavily on Meta's globally unique module names. It likely won't work well in other environments. + +## Suppressing rules within graphql tags + +The following rules support suppression within graphql tags: + +- `relay/unused-fields` +- `relay/must-colocate-fragment-spreads` + +Supported rules can be suppressed by adding `# eslint-disable-next-line relay/name-of-rule` to the preceding line: + +```js +graphql` + fragment foo on Page { + # eslint-disable-next-line relay/must-colocate-fragment-spreads + ...unused1 + } +`; +``` + +Note that only the `eslint-disable-next-line` form of suppression works. `eslint-disable-line` doesn't currently work until graphql-js provides support for [parsing Comment nodes](https://github.com/graphql/graphql-js/issues/2241) in their AST. + +## Contributing + +If you wish to contribute to the Relay ESLint plugin, you can find the code on GitHub at [relay/eslint-plugin-relay](https://github.com/relayjs/eslint-plugin-relay/). diff --git a/website/docs/getting-started/prerequisites.md b/website/docs/getting-started/prerequisites.md deleted file mode 100644 index 2c8cdd98a88c6..0000000000000 --- a/website/docs/getting-started/prerequisites.md +++ /dev/null @@ -1,49 +0,0 @@ ---- -id: prerequisites -title: Prerequisites -slug: /getting-started/prerequisites/ -description: Prerequisites for setting up Relay -keywords: -- prerequisites ---- - -import DocsRating from '@site/src/core/DocsRating'; -import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; - - -Before getting started with Relay, bear in mind that we assume that the following infrastructure has already been set up, as well as some level of familiarity with the topics below. - -## JavaScript - -Relay is a framework built in JavaScript, so we assume familiarity with the JavaScript language. - -## React - -Relay is a framework for data management with the primary supported binding for React applications, so we assume that you are already familiar with [React](https://reactjs.org/). - -## GraphQL - -We also assume basic understanding of [GraphQL](http://graphql.org/learn/). In order to start using Relay, you will also need: - -### A GraphQL Schema - -A description of your data model with an associated set of resolve methods that know how to fetch any data your application could ever need. - -GraphQL is designed to support a wide range of data access patterns. In order to understand the structure of an application's data, Relay requires that you follow certain conventions when defining your schema. These are documented in the [GraphQL Server Specification](../../guides/graphql-server-specification). - -- **[graphql-js](https://github.com/graphql/graphql-js)** on [npm](https://www.npmjs.com/package/graphql) - - General-purpose tools for building a GraphQL schema using JavaScript - -- **[graphql-relay-js](https://github.com/graphql/graphql-relay-js)** on [npm](https://www.npmjs.com/package/graphql-relay) - - JavaScript helpers for defining connections between data, and mutations, in a way that smoothly integrates with Relay. - -### A GraphQL Server - -Any server can be taught to load a schema and speak GraphQL. Our [examples](https://github.com/relayjs/relay-examples) use Express. - -- **[express-graphql](https://github.com/graphql/express-graphql)** on [npm](https://www.npmjs.com/package/express-graphql) - - - diff --git a/website/docs/getting-started/production.md b/website/docs/getting-started/production.md new file mode 100644 index 0000000000000..439df66bbd2bc --- /dev/null +++ b/website/docs/getting-started/production.md @@ -0,0 +1,30 @@ +--- +id: production +title: Production Setup +slug: /getting-started/production/ +description: Setting up Relay for production use +keywords: +- production +--- + +Getting the most out of Relay in production requires a few extra steps. This page covers a list of best practices for taking Relay to production. + +## Persisted Queries (Trusted Documents) + +One of GraphQL's key innovations is that it enables clients to define arbitrary queries. While this unlocks a lot of flexibility, it also opens the doors to abuse. Scrapers can use GraphQL to scrape data from your site, and malicious users can use GraphQL to send large requests to your server that cause a denial of service. To prevent this, we recommend using [Persisted Queries](../guides/persisted-queries.md) also know as [Trusted Documents](https://benjie.dev/graphql/trusted-documents). + +With Persisted Queries, the set of queries that the client can send is locked in at build time. This means that scrapers and malicious attackers are limited to sending only the queries used by the client, just like REST. It also has the added benefit that the client code only needs to include an single id for each query rather than the whole query text. + + + +## Relay Lint Rules + +A key principal of Relay is data colocation where each component defines its own data dependencies. It's critical to [How Relay Enables Optimal Data Fetching](https://relay.dev/blog/2023/10/24/how-relay-enables-optimal-data-fetching/). However, this is often unintuitive for developers who are used to fetching data in a single query. Relay's ESLint rules can help enforce this pattern by ensuring no component fetches fields which it does not itself use. + +We recommend using the [`eslint-plugin-relay`](https://github.com/relayjs/eslint-plugin-relay), especially the `relay/unused-fields` rule. + +Learn how to install them: [Relay ESLint Plugin](./lint-rules.md). + +## Running the Relay Compiler in CI + +We recommend committing Relay's generated artifacts to source control along with your application code. This ensures generated types are present without needing an additional build, and allows for inspection of generated artifacts in code review. To ensure the generated artifacts are always in sync with the source code, we recommend running the Relay compiler in CI and ensuring it does not change any generated files. A example bash script which checks for changes can be found [here](https://github.com/facebook/relay/blob/0414c9ad0744483e349e07defcb6d70a52cf8b3c/scripts/check-git-status.sh). diff --git a/website/docs/getting-started/quick-start.md b/website/docs/getting-started/quick-start.md new file mode 100644 index 0000000000000..d486a88959526 --- /dev/null +++ b/website/docs/getting-started/quick-start.md @@ -0,0 +1,207 @@ +--- +id: quick-start +title: Quick Start +slug: /getting-started/quick-start/ +description: Get up an running with Relay +keywords: +- quick +--- +This quick start guide will start with a new React app using Vite and show you how to add Relay to it. + +:::tip +If you'd prefer an automated approach, [`create-relay-app`](https://github.com/tobias-tengler/create-relay-app) by Tobias Tengler will walk you through adding Relay to an existing React app via a series of prompts: `npm create @tobiastengler/relay-app` +::: + +We will be building a simple app which shows Star Wars movies fetched from the [example Star Wars GraphQL API](https://graphql.org/swapi-graphql/) hosted by graphql.org. + +## Scaffold a React App + +We’ll start with a [Vite](https://vite.dev/) React app using TypeScript. + +```bash +npm create vite -- --template react-ts +``` + +You’ll be prompted for a project name. Type: `relay-example` + +## Install Dependencies + +```bash +cd relay-example + +# Runtime dependencies +npm install relay-runtime react-relay +# Dev dependencies +npm install --save-dev babel-plugin-relay graphql relay-compiler +# Types +npm install --save-dev @types/relay-runtime @types/react-relay +``` + +## Configure Vite to use Relay + +Relay uses a [Babel plugin](./babel-plugin.md) to insert code generated by the Relay compiler into your bundle. We can enable the Relay Babel plugin we installed earliery by configuring the React Vite plugin. + +```tsx title="vite.config.ts" +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [ + // change-line + react({ babel: { plugins: ["relay"] } }) + ], +}) +``` + +See [Babel Plugin](./babel-plugin.md) for information about how to configure the Babel plugin for other build systems. + +## Configure the Relay Compiler + +Next we will download the GraphQL schema for the Star Wars GraphQL endpoint. + +```bash +curl -O https://raw.githubusercontent.com/graphql/swapi-graphql/refs/heads/master/schema.graphql +``` + +And define our `relay.config.json` config file which tells the [Relay Compiler](./compiler.md) which schema file we want it to use and other details about our project. + +```json title="relay.config.json" +{ + "src": "./src", + "schema": "./schema.graphql", + "language": "typescript" +} +``` + +See [Relay Compiler](./compiler.md) for more information about configuring and running the Relay compiler. + +## Configure your Relay Environment + +To allow components within our application to fetch GraphQL we configure a Relay Environment to fetch from our test endpoint and add it to React context. + +```tsx title="src/main.tsx" +import { StrictMode, Suspense } from "react"; +import { createRoot } from "react-dom/client"; +import "./index.css"; +import App from "./App.tsx"; +import { RelayEnvironmentProvider } from "react-relay"; +import { Environment, Network, FetchFunction } from "relay-runtime"; + +const HTTP_ENDPOINT = "https://graphql.org/graphql/"; + +const fetchGraphQL: FetchFunction = async (request, variables) => { + const resp = await fetch(HTTP_ENDPOINT, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ query: request.text, variables }), + }); + if (!resp.ok) { + throw new Error("Response failed."); + } + return await resp.json(); +}; + +const environment = new Environment({ + network: Network.create(fetchGraphQL), +}); + +createRoot(document.getElementById("root")!).render( + + + + + + + +); +``` + +See [Relay Environment](../api-reference/relay-runtime/relay-environment.md) for an overview of the Relay Environment and how to configure it. + +:::tip +`` exposes your Environment via [React context](https://react.dev/learn/passing-data-deeply-with-context), so it must wrap your entire application. +::: + +## Define your first Relay component + +Finally we can start defining the data we want to fetch and build our UI. Our app will fetch a list of films and render each one using a `` component. + +```tsx title="src/App.tsx" +import { AppQuery } from "./__generated__/AppQuery.graphql"; +import { graphql, useLazyLoadQuery } from "react-relay"; +import Film from "./Film"; + +export default function App() { + const data = useLazyLoadQuery( + graphql` + query AppQuery { + allFilms { + films { + id + ...Film_item + } + } + } + `, + {} + ); + + const films = data?.allFilms?.films?.filter((film) => film != null); + + return ( +
    +

    Star Wars Films

    + {films?.map((film) => ( + + ))} +
    + ); +} +``` + +## Define your first fragment + +One of Relay's core principles is that each component should define its own data dependencies. So, we define our `` component using a [GraphQL Fragment](https://graphql.org/learn/queries/#fragments). + +```typescript title="src/Film.tsx" +import { graphql, useFragment } from "react-relay"; +import type { Film_item$key } from "./__generated__/Film_item.graphql"; + +export default function FilmListItem(props: { film: Film_item$key; }) { + const film = useFragment( + graphql` + fragment Film_item on Film { + title + director + } + `, + props.film + ); + + return ( +
  • + {film.title}: directed by {film.director} +
  • + ); +} +``` + +## Compile and run your app + +All that’s left is to run the Relay compiler and start your app! + +The Relay compiler generates TypeScript types and combines your queries and fragments into optimized representations. You have to run the Relay compiler each time you modify your GraphQL queries or fragments. + +:::tip +If you have [Watchman](https://facebook.github.io/watchman/) installed you can run `npx relay-compiler --watch` to have the compiler run in watch mode, but you'll need to run `echo "{}" > .watchmanconfig` to create a Watchman root. +::: + +```bash +npx relay-compiler +npm run dev +``` + +You should now be able to open your app in a browser: [http://localhost:5173/](http://localhost:5173/) + +May the force be with you! diff --git a/website/docs/getting-started/step-by-step-guide.md b/website/docs/getting-started/step-by-step-guide.md deleted file mode 100644 index 33b519abb5284..0000000000000 --- a/website/docs/getting-started/step-by-step-guide.md +++ /dev/null @@ -1,314 +0,0 @@ ---- -id: step-by-step-guide -title: Server Setup Example -slug: /getting-started/step-by-step-guide/ -description: Step-by-step guide for setting up Relay -keywords: -- setup ---- - -import DocsRating from '@site/src/core/DocsRating'; -import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; - - -Relay is a framework for managing and declaratively fetching GraphQL data. It allows developers to declare *what* data each component needs via GraphQL, and then aggregate these dependencies and efficiently fetch the data in fewer round trips. In this guide we'll introduce the key concepts for using Relay in a React app one at a time. - -## Step 1: Create React App - -For this example we'll start with a standard install of [Create React App](https://create-react-app.dev). Create React App makes it easy to get a fully configured React app up and running and also supports configuring Relay. To get started, create a new app with: - -```bash -# NPM -npx create-react-app your-app-name - -# Yarn -yarn create react-app your-app-name -``` - -At this point we should be able to change to the app's directory and run it: - -```bash -# NPM -cd your-app-name -npm start - -# Yarn -cd your-app-name -yarn start -``` - -For troubleshooting and more setup options, see the [Create React App documentation](https://create-react-app.dev/docs/getting-started). - -## Step 2: Fetch GraphQL (without Relay) - -If you're exploring using GraphQL with Relay, we highly recommend starting with a basic approach and using as few libraries as possible. GraphQL servers can generally be accessed using plain-old HTTP requests, so we can use the [`fetch` API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) to request some data from our server. For this guide we'll use GitHub's GraphQL API as the server, but if you already have a GraphQL server feel free to use that instead. - -### 2.1: GitHub GraphQL Authentication - -To start we'll need an authentication token for the GitHub API (if you're using your own GraphQL endpoint, you can skip this step): - -* Open [github.com/settings/tokens](https://github.com/settings/tokens). -* Ensure that at least the `repo` scope is selected. -* Generate a token -* Create a file `./your-app-name/.env.local` and add the following contents, replacing `` with your authentication token: - -``` -# your-app-name/.env.local -REACT_APP_GITHUB_AUTH_TOKEN= -``` - -### 2.2: A fetchGraphQL Helper - -Next let's update the home screen of our app to show the name of the Relay repository. We'll start with a common approach to fetching data in React, calling our fetch function after the component mounts (note: later we'll see some limitations of this approach and a better alternative that works with React Concurrent Mode and Suspense). First we'll create a helper function to load data from the server. Again, this example will use the GitHub API, but feel free to replace it with the appropriate URL and authentication mechanism for your own GraphQL server: - -```javascript -// your-app-name/src/fetchGraphQL.js -async function fetchGraphQL(text, variables) { - const REACT_APP_GITHUB_AUTH_TOKEN = process.env.REACT_APP_GITHUB_AUTH_TOKEN; - - // Fetch data from GitHub's GraphQL API: - const response = await fetch('https://api.github.com/graphql', { - method: 'POST', - headers: { - Authorization: `bearer ${REACT_APP_GITHUB_AUTH_TOKEN}`, - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - query: text, - variables, - }), - }); - - // Get the response as JSON - return await response.json(); -} - -export default fetchGraphQL; -``` - -### 2.3: Fetching GraphQL From React - -Now we can use our `fetchGraphQL` function to fetch some data in our app. Open `src/App.js` and edit it as follows: - -```javascript -// your-app-name/src/App.js -import React from 'react'; -import './App.css'; -import fetchGraphQL from './fetchGraphQL'; - -const { useState, useEffect } = React; - -function App() { - // We'll load the name of a repository, initially setting it to null - const [name, setName] = useState(null); - - // When the component mounts we'll fetch a repository name - useEffect(() => { - let isMounted = true; - fetchGraphQL(` - query RepositoryNameQuery { - # feel free to change owner/name here - repository(owner: "facebook" name: "relay") { - name - } - } - `).then(response => { - // Avoid updating state if the component unmounted before the fetch completes - if (!isMounted) { - return; - } - const data = response.data; - setName(data.repository.name); - }).catch(error => { - console.error(error); - }); - - return () => { - isMounted = false; - }; - }, []); - - // Render "Loading" until the query completes - return ( -
    -
    -

    - {name != null ? `Repository: ${name}` : "Loading"} -

    -
    -
    - ); -} - -export default App; -``` - -## Step 3: When To Use Relay - -At this point we can fetch data with GraphQL and render it with React. This is a reasonable solution that can be sufficient for many apps. However, this approach doesn't necessarily scale. As our app grows in size and complexity, or the number of people working on the app grows, a simple approach like this can become limiting. Relay provides a number of features designed to help keep applications fast and reliable even as they grow in size and complexity: collocating data dependencies in components with GraphQL fragments, data consistency, mutations, etc. Check out [Thinking in GraphQL](../../principles-and-architecture/thinking-in-graphql/) and [Thinking in Relay](../../principles-and-architecture/thinking-in-relay/) for an overview of how Relay makes it easier to work with GraphQL. - - -## Step 4: Adding Relay To Our Project - -In this guide we'll demonstrate installing the *experimental* release of Relay Hooks, a new, hooks-based Relay API that supports React Concurrent Mode and Suspense. - -First we'll add the necessary packages. Note that Relay is comprised of three key pieces: a compiler (which is used at build time), a core runtime (that is React agnostic), and a React integration layer. - -```bash -# NPM Users -npm install --save relay-runtime react-relay -npm install --save-dev relay-compiler babel-plugin-relay - -# Yarn Users -yarn add relay-runtime react-relay -yarn add --dev relay-compiler babel-plugin-relay -``` - -### 4.1 Configure Relay Compiler - -Next let's configure Relay compiler. We'll need a copy of the schema as a `.graphql` file. If you're using the GitHub GraphQL API, you can download a copy directly from the Relay example app: - -```bash -cd your-app-name -curl https://raw.githubusercontent.com/relayjs/relay-examples/main/issue-tracker/schema/schema.graphql > schema.graphql -``` -*Note:* On Windows, the `.graphql` file has to be explicitly saved with UTF-8 encoding, not the default UTF-16. See this [issue](https://github.com/prisma-labs/get-graphql-schema/issues/30) for more details. - -If you're using your own API we suggest using the [`get-graphql-schema`](https://www.npmjs.com/package/get-graphql-schema) utility to download your schema into a `.graphql` file. - -Now that we have a schema we can modify `package.json` to run the compiler first whenever we build or start our app: - -```json -// your-app-name/package.json -{ - ... - "scripts": { - ... - "start": "yarn run relay && react-scripts start", - "build": "yarn run relay && react-scripts build", - "relay": "yarn run relay-compiler" - ... - }, - "relay": { - "src": "./src/", - "schema": "./schema.graphql", - "language": "javascript" - } - ... -} -``` - -At this point, you should be able to run the following successfully: - -```bash -cd your-app-name -yarn start -``` - -If it works, your app will open at [localhost:3000](http://localhost:3000). Now when we write GraphQL in our app, Relay will detect it and generate code to represent our queries in `your-app-name/src/__generated__/`. We recommend checking in these generated files to source control. - -### 4.2 Configure Relay Runtime - -Now that the compiler is configured we can set up the runtime - we have to tell Relay how to connect to our GraphQL server. We'll reuse the `fetchGraphQL` utility we built above. Assuming you haven't modified it (or at least that it still takes `text` and `variables` as arguments), we can now define a Relay `Environment`. An `Environment` encapsulates how to talk to our server (a Relay `Network`) with a cache of data retrieved from that server. We'll create a new file, `src/RelayEnvironment.js` and add the following: - -```javascript -// your-app-name/src/RelayEnvironment.js -import {Environment, Network, RecordSource, Store} from 'relay-runtime'; -import fetchGraphQL from './fetchGraphQL'; - -// Relay passes a "params" object with the query name and text. So we define a helper function -// to call our fetchGraphQL utility with params.text. -async function fetchRelay(params, variables) { - console.log(`fetching query ${params.name} with ${JSON.stringify(variables)}`); - return fetchGraphQL(params.text, variables); -} - -// Export a singleton instance of Relay Environment configured with our network function: -export default new Environment({ - network: Network.create(fetchRelay), - store: new Store(new RecordSource()), -}); -``` - -## Step 5: Fetching a Query With Relay - -Now that Relay is installed and configured we can change `App.js` to use it instead. We'll prepare our data as the app starts, and wait for it to be ready in ``. Replace the contents of `src/App.js` with the following: - -```javascript -import React from 'react'; -import './App.css'; -import graphql from 'babel-plugin-relay/macro'; -import { - RelayEnvironmentProvider, - loadQuery, - usePreloadedQuery, -} from 'react-relay/hooks'; -import RelayEnvironment from './RelayEnvironment'; - -const { Suspense } = React; - -// Define a query -const RepositoryNameQuery = graphql` - query AppRepositoryNameQuery { - repository(owner: "facebook", name: "relay") { - name - } - } -`; - -// Immediately load the query as our app starts. For a real app, we'd move this -// into our routing configuration, preloading data as we transition to new routes. -const preloadedQuery = loadQuery(RelayEnvironment, RepositoryNameQuery, { - /* query variables */ -}); - -// Inner component that reads the preloaded query results via `usePreloadedQuery()`. -// This works as follows: -// - If the query has completed, it returns the results of the query. -// - If the query is still pending, it "suspends" (indicates to React that the -// component isn't ready to render yet). This will show the nearest -// fallback. -// - If the query failed, it throws the failure error. For simplicity we aren't -// handling the failure case here. -function App(props) { - const data = usePreloadedQuery(RepositoryNameQuery, props.preloadedQuery); - - return ( -
    -
    -

    {data.repository.name}

    -
    -
    - ); -} - -// The above component needs to know how to access the Relay environment, and we -// need to specify a fallback in case it suspends: -// - tells child components how to talk to the current -// Relay Environment instance -// - specifies a fallback in case a child suspends. -function AppRoot(props) { - return ( - - - - - - ); -} - -export default AppRoot; -``` - -Note that you'll have to restart the app - `yarn start` - so that Relay compiler can see the new query and generate code for it. See the [Relay Compiler setup docs](../installation-and-setup/#set-up-relay-compiler) for how to run Relay Compiler in watch mode, to regenerate code as you modify queries. - -## Step 6: Explore! - -At this point we have an app configured to use Relay. We recommend checking out the following for information and ideas about where to go next: - -* The [Guided Tour](../../guided-tour/) describes how to implement many common use-cases. -* The [API Reference](../../api-reference/use-fragment/) has full details on the Relay Hooks APIs. -* The [Example App](https://github.com/relayjs/relay-examples/tree/main/issue-tracker) is a more sophisticated version of what we've started building here. It includes routing integration and uses React Concurrent Mode and Suspense for smooth transitions between pages. - - - diff --git a/website/docs/glossary/glossary.md b/website/docs/glossary/glossary.md index 1cc950e4cc448..e3383180dcd20 100644 --- a/website/docs/glossary/glossary.md +++ b/website/docs/glossary/glossary.md @@ -63,7 +63,7 @@ Compare with [variables](#variables) and see the [relevant section](../guided-to ## Artifact -Files that are generated by the Relay compiler, typically ending in `.graphql.js`. +Files that are generated by the [Relay compiler](../getting-started/compiler.md), typically ending in `.graphql.js`. @@ -91,7 +91,7 @@ A build-time transformation of the Javascript codebase, which turns calls to graphql`...` ``` -into `require(NAME_OF_GENERATED_ARTIFACT)` calls. +into `require(NAME_OF_GENERATED_ARTIFACT)` calls. See [Babel Plugin](../getting-started/babel-plugin.md). ## Client Schema Extension @@ -135,7 +135,7 @@ In addition, calls to `graphql`...`` are turned into concrete requests at build ## Config -A file or javascript object which controls, among other things, which files are scanned by the Relay [compiler](#compiler) for your project. +A [file or javascript object](../getting-started/compiler-config.md) which controls, among other things, which files are scanned by the Relay [compiler](#compiler) for your project. ## @connection @@ -144,11 +144,15 @@ A directive which declares that a field implements the [connection](#connection) ## Connection + A field implementing the [connection spec](https://relay.dev/graphql/connections.htm). See here for more details on the spec, and the section of the guided tour on rendering list data and pagination. + + A field implementing the [connection spec](https://relay.dev/graphql/connections.htm). See the section of the guided tour on rendering list data and pagination. + See also [`usePaginationFragment`](../api-reference/use-pagination-fragment). @@ -186,7 +190,9 @@ See the [Thinking in Relay guide](../principles-and-architecture/thinking-in-rel A directive which can be added to a fragment spread or inline fragment to avoid blocking on that fragment's data. For more detail refer to GraphQL's [documentation on the @defer directive](https://github.com/graphql/graphql-wg/blob/main/rfcs/DeferStream.md#defer). + See [Incremental Data Delivery](https://www.internalfb.com/intern/staticdocs/relay/docs/guides/incremental-data-delivery/). + ## Definition @@ -420,7 +426,9 @@ A lightweight API for specifying a that a React component should be loaded on de This API is safe to use in entrypoint files. + See [the npm module](https://www.npmjs.com/package/jsresource). + ## Lazy Loading @@ -496,11 +504,15 @@ TODO A mutation is a combination of two things: a mutation on the backend, followed by query against updated data. + See the [guide on mutations](../guided-tour/updating-data/graphql-mutations), and [this article](https://www.internalfb.com/intern/wiki/Graphql-for-hack-developers/mutation-root-fields/) on defining mutations in your hack code. + + See the [guide on mutations](../guided-tour/updating-data/graphql-mutations). + ## Mutation Root Query @@ -636,7 +648,9 @@ Required because of current limitations on dynamically loading components in Rea For Relay to process a file with a GraphQL literal, it must be included in a project. A project specifies the folders to which it applies and the schema against which to evaluate GraphQL literals, and includes other information needed by the Relay compiler. + Projects are defined in a single [config](#config) file, found [here](https://www.internalfb.com/intern/diffusion/WWW/browse/master/scripts/relay/compiler-rs/config.www.json) and [here](https://www.internalfb.com/intern/diffusion/FBS/browse/master/xplat/relay/compiler-rs/config.xplat.json). + ## Profiler @@ -855,6 +869,7 @@ TODO A collection of all of the GraphQL types that are known to Relay, for a given [project](#project). + ## Schema Sync The GraphQL [schema](#schema) is derived from annotations on Hack classes in the www repository. @@ -964,7 +979,7 @@ See also [abstract type refinement](#abstract-type-refinement). A callback passed to `commitMutation`, which provides the application developer with imperative control over the data in the store. -See [the documentation](../guided-tour/updating-data/) and also optimistic updater. +See [the documentation](../guided-tour/updating-data/introduction.md) and also optimistic updater. ## Value diff --git a/website/docs/guided-tour/list-data/advanced-pagination.md b/website/docs/guided-tour/list-data/advanced-pagination.md index 84e7594eb00cb..591e49ed22796 100644 --- a/website/docs/guided-tour/list-data/advanced-pagination.md +++ b/website/docs/guided-tour/list-data/advanced-pagination.md @@ -155,46 +155,3 @@ function FriendsListComponent(props: Props) { * The APIs for both *"forward"* and *"backward"* are exactly the same, they're only named differently. When paginating forward, then the `after` and `first` connection arguments will be used, when paginating backward, the `before` and `last` connection arguments will be used. * Note that the primitives for both *"forward"* and *"backward"* pagination are exposed from a single call to `usePaginationFragment`, so both *"forward"* and *"backward"* pagination can be performed simultaneously in the same component. - - - -## Custom Connection State - -By default, when using `usePaginationFragment` and `@connection`, Relay will *append* new pages of items to the connection when paginating *"forward",* and *prepend* new pages of items when paginating *"backward"*. This means that your component will always render the *full* connection, with *all* of the items that have been accumulated so far via pagination, and/or items that have been added or removed via mutations or subscriptions. - -However, it is possible that you'd need different behavior for how to merge and accumulate pagination results (or other updates to the connection), and/or derive local component state from changes to the connection. Some examples of this might be: - -* Keeping track of different *visible* slices or windows of the connection. -* Visually separating each *page* of items. This requires knowledge of the exact set of items inside each page that has been fetched. -* Displaying different ends of the same connection simultaneously, while keeping track of the "gaps" between them, and being able to merge results when preforming pagination between the gaps. For example, imagine rendering a list of comments where the oldest comments are displayed at the top, then a "gap" that can be interacted with to paginate, and then a section at the bottom which shows the most recent comments that have been added by the user or by real-time subscriptions. - - -To address these more complex use cases, Relay is still working on a solution: - - -> TBD - - - - -## Refreshing connections - -> TBD - - - - -## Prefetching Pages of a Connection - -> TBD - - - - -## Rendering One Page of Items at a Time - -> TBD - - - - diff --git a/website/docs/guided-tour/list-data/introduction.md b/website/docs/guided-tour/list-data/introduction.md new file mode 100644 index 0000000000000..d956e77478544 --- /dev/null +++ b/website/docs/guided-tour/list-data/introduction.md @@ -0,0 +1,17 @@ +--- +id: introduction +title: Introduction +slug: /guided-tour/list-data/introduction +description: Relay guide to fetching data +keywords: +- fetching data +--- + +import DocsRating from '@site/src/core/DocsRating'; +import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; + +After an app has been initially rendered, there are various scenarios in which you might want to fetch and show *new* or *different* data (e.g. change the currently displayed item), or maybe refresh the currently rendered data with the latest version from the server (e.g. refreshing a count), usually as a result of an event or user interaction. + +In this section we'll cover some of the most common scenarios and how to build them with Relay. + + diff --git a/website/docs/guided-tour/list-data/refetching-connections.md b/website/docs/guided-tour/list-data/refetching-connections.md deleted file mode 100644 index b30bbf7dec66f..0000000000000 --- a/website/docs/guided-tour/list-data/refetching-connections.md +++ /dev/null @@ -1,210 +0,0 @@ ---- -id: refetching-connections -title: Refetching Connections (Using and Changing Filters) -slug: /guided-tour/list-data/refetching-connections/ -description: Relay guide to refetching connections -keywords: -- pagination -- refetching -- connection -- useRefetchableFragment ---- - -import DocsRating from '@site/src/core/DocsRating'; -import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; -import FbRefetchingConnectionsUsingUseTransition from './fb/FbRefetchingConnectionsUsingUseTransition.md'; - -Often times when querying for a list of data, you can provide different values in the query which serve as filters that change the result set, or sort it differently. - -Some examples of this are: - -* Building a search typeahead, where the list of results is a list filtered by the search term entered by the user. -* Changing the ordering mode of the list comments currently displayed for a post, which could produce a completely different set of comments from the server. -* Changing the way News Feed is ranked and sorted. - - -Specifically, in GraphQL, connection fields can accept arguments to sort or filter the set of queried results: - -```graphql -fragment UserFragment on User { - name - friends(order_by: DATE_ADDED, search_term: "Alice", first: 10) { - edges { - node { - name - age - } - } - } -} -``` - - -In Relay, we can pass those arguments as usual using GraphQL [variables](../../rendering/variables/) - -```js -type Props = { - userRef: FriendsListComponent_user$key, -}; - -function FriendsListComponent(props: Props) { - const userRef = props.userRef; - - const {data, ...} = usePaginationFragment( - graphql` - fragment FriendsListComponent_user on User { - name - friends( - order_by: $orderBy, - search_term: $searchTerm, - after: $cursor, - first: $count, - ) @connection(key: "FriendsListComponent_user_friends_connection") { - edges { - node { - name - age - } - } - } - } - `, - userRef, - ); - - return (...); -} -``` - - -When paginating, the original values for those filters will be preserved: - -```js -type Props = { - userRef: FriendsListComponent_user$key, -}; - -function FriendsListComponent(props: Props) { - const userRef = props.userRef; - - const {data, loadNext} = usePaginationFragment( - graphql` - fragment FriendsListComponent_user on User { - name - friends(order_by: $orderBy, search_term: $searchTerm) - @connection(key: "FriendsListComponent_user_friends_connection") { - edges { - node { - name - age - } - } - } - } - `, - userRef, - ); - - return ( - <> -

    Friends of {data.name}:

    - {...} - - {/* - Loading the next items will use the original order_by and search_term - values used for the initial query - */ } - - - ); -} -``` -* Note that calling `loadNext` will use the original `order_by` and `search_term` values used for the initial query. During pagination, these value won't (*and shouldn't*) change. - -If we want to refetch the connection with *different* variables, we can use the `refetch` function provided by `usePaginationFragment`, similarly to how we do so when [Refetching Fragments with Different Data](../../refetching/refetching-fragments-with-different-data/): - - - - - - - -```js -/** - * FriendsListComponent.react.js - */ -import type {FriendsListComponent_user$key} from 'FriendsListComponent_user.graphql'; - -const React = require('React'); -const {useState, useEffect} = require('React'); - -const {graphql, usePaginationFragment} = require('react-relay'); - - -type Props = { - searchTerm?: string, - user: FriendsListComponent_user$key, -}; - -function FriendsListComponent(props: Props) { - const searchTerm = props.searchTerm; - const {data, loadNext, refetch} = usePaginationFragment( - graphql` - fragment FriendsListComponent_user on User { - name - friends( - order_by: $orderBy, - search_term: $searchTerm, - after: $cursor, - first: $count, - ) @connection(key: "FriendsListComponent_user_friends_connection") { - edges { - node { - name - age - } - } - } - } - `, - props.user, - ); - - useEffect(() => { - // When the searchTerm provided via props changes, refetch the connection - // with the new searchTerm - refetch({first: 10, search_term: searchTerm}, {fetchPolicy: 'store-or-network'}); - }, [searchTerm]) - - return ( - <> -

    Friends of {data.name}:

    - - {/* When the button is clicked, refetch the connection but sorted differently */} - - - ... - - - ); -} -``` - -Let's distill what's going on here: - -* Calling `refetch` and passing a new set of variables will fetch the fragment again *with the newly provided variables*. The variables you need to provide are a subset of the variables that the generated query expects; the generated query will require an `id`, if the type of the fragment has an `id` field, and any other variables that are transitively referenced in your fragment. - * In our case, we need to pass the count we want to fetch as the `first` variable, and we can pass different values for our filters, like `orderBy` or `searchTerm`. -* This will re-render your component and may cause it to suspend (as explained in [Loading States with Suspense](../../rendering/loading-states/)) if it needs to send and wait for a network request. If `refetch` causes the component to suspend, you'll need to make sure that there's a `Suspense` boundary wrapping this component from above. -* Conceptually, when we call refetch, we're fetching the connection *from scratch*. It other words, we're fetching it again from the *beginning* and *"resetting"* our pagination state. For example, if we fetch the connection with a different `search_term`, our pagination information for the previous `search_term` no longer makes sense, since we're essentially paginating over a new list of items. - -
    - - - - - diff --git a/website/docs/guided-tour/managing-data-outside-react/prefetching-queries.md b/website/docs/guided-tour/managing-data-outside-react/prefetching-queries.md deleted file mode 100644 index 46cb0f1a9a054..0000000000000 --- a/website/docs/guided-tour/managing-data-outside-react/prefetching-queries.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -id: prefetching-queries -title: Prefetching Queries -slug: /guided-tour/accessing-data-without-react/prefetching-queries/ ---- - -import DocsRating from '@site/src/core/DocsRating'; -import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; - - diff --git a/website/docs/guided-tour/managing-data-outside-react/reading-fragments.md b/website/docs/guided-tour/managing-data-outside-react/reading-fragments.md deleted file mode 100644 index 50932e7d0da96..0000000000000 --- a/website/docs/guided-tour/managing-data-outside-react/reading-fragments.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -id: reading-fragments -title: Reading Fragments -slug: /guided-tour/accessing-data-without-react/reading-fragments/ ---- - -import DocsRating from '@site/src/core/DocsRating'; -import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; - - - - diff --git a/website/docs/guided-tour/managing-data-outside-react/reading-queries.md b/website/docs/guided-tour/managing-data-outside-react/reading-queries.md deleted file mode 100644 index ee479755318f3..0000000000000 --- a/website/docs/guided-tour/managing-data-outside-react/reading-queries.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -id: reading-queries -title: Reading Queries -slug: /guided-tour/accessing-data-without-react/reading-queries/ ---- - -import DocsRating from '@site/src/core/DocsRating'; -import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; - - - - diff --git a/website/docs/guided-tour/managing-data-outside-react/subscribing-to-queries.md b/website/docs/guided-tour/managing-data-outside-react/subscribing-to-queries.md deleted file mode 100644 index 24321e5e076ba..0000000000000 --- a/website/docs/guided-tour/managing-data-outside-react/subscribing-to-queries.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -id: subscribing-to-queries -title: Subscribing to Queries -slug: /guided-tour/accessing-data-without-react/subscribing-to-queries/ ---- - -import DocsRating from '@site/src/core/DocsRating'; -import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; - - - - diff --git a/website/docs/guided-tour/refetching/OssAvoidSuspenseNote.md b/website/docs/guided-tour/refetching/OssAvoidSuspenseNote.md deleted file mode 100644 index 27fd6885b1f5d..0000000000000 --- a/website/docs/guided-tour/refetching/OssAvoidSuspenseNote.md +++ /dev/null @@ -1,3 +0,0 @@ -:::note -In future versions of React when concurrent rendering is supported, React will provide an option to support this case and avoid hiding already rendered content with a Suspense fallback when suspending. -::: diff --git a/website/docs/guided-tour/refetching/introduction.md b/website/docs/guided-tour/refetching/introduction.md deleted file mode 100644 index 08c2e1948b5ea..0000000000000 --- a/website/docs/guided-tour/refetching/introduction.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -id: introduction -title: Introduction -slug: /guided-tour/refetching/ -description: Relay guide to refetching -keywords: -- refetching ---- - -import DocsRating from '@site/src/core/DocsRating'; -import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; - -After an app has been initially rendered, there are various scenarios in which you might want to refetch and show *new* or *different* data (e.g. change the currently displayed item), or maybe refresh the currently rendered data with the latest version from the server (e.g. refreshing a count), usually as a result of an event or user interaction. - -In this section we'll cover some of the most common scenarios and how to build them with Relay. - - diff --git a/website/docs/guided-tour/refetching/refetching-fragments-with-different-data.md b/website/docs/guided-tour/refetching/refetching-fragments-with-different-data.md deleted file mode 100644 index 81e954561a270..0000000000000 --- a/website/docs/guided-tour/refetching/refetching-fragments-with-different-data.md +++ /dev/null @@ -1,171 +0,0 @@ ---- -id: refetching-fragments-with-different-data -title: Refetching Fragments with Different Data -slug: /guided-tour/refetching/refetching-fragments-with-different-data/ -description: Relay guide to refetching fragments with different data -keywords: -- refetching -- fragment ---- - -import DocsRating from '@site/src/core/DocsRating'; -import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; -import FbRefetchingFragments from './fb/FbRefetchingFragments.md'; -import FbAvoidSuspenseCaution from './fb/FbAvoidSuspenseCaution.md'; -import OssAvoidSuspenseNote from './OssAvoidSuspenseNote.md'; - -When referring to **"refetching a fragment"**, we mean fetching a *different* version of the data than the one was originally rendered by the fragment. For example, this might be to change a currently selected item, to render a different list of items than the one being shown, or more generally to transition the currently rendered content to show new or different content. - -Conceptually, this means fetching and rendering the currently rendered fragment again, but under a new query with *different variables*; or in other words, rendering the fragment under a new query root. Remember that *fragments can't be fetched by themselves: they need to be part of a query,* so we can't just "fetch" the fragment again by itself. - -## Using `useRefetchableFragment` - -To do so, we can also use the [`useRefetchableFragment`](../../../api-reference/use-refetchable-fragment/) Hook in combination with the `@refetchable` directive, which will automatically generate a query to refetch the fragment under, and which we can fetch using the `refetch` function: - - - - - - - -```js -import type {CommentBody_comment$key} from 'CommentBody_comment.graphql'; - -type Props = { - comment: CommentBody_comment$key, -}; - -function CommentBody(props: Props) { - const [data, refetch] = useRefetchableFragment( - graphql` - fragment CommentBody_comment on Comment - # @refetchable makes it so Relay autogenerates a query for - # fetching this fragment - @refetchable(queryName: "CommentBodyRefetchQuery") { - body(lang: $lang) { - text - } - } - `, - props.comment, - ); - - const refetchTranslation = () => { - // We call refetch with new variables, - // which will refetch the @refetchable query with the - // new variables and update this component with the - // latest fetched data. - refetch({lang: 'SPANISH'}); - }; - - return ( - <> -

    {data.body?.text}

    - - - ); -} -``` - -Let's distill what's happening in this example: - -* `useRefetchableFragment` behaves similarly to [`useFragment`](../../../api-reference/use-fragment/) (see the [Fragments](../../rendering/fragments/) section), but with a few additions: - * It expects a fragment that is annotated with the `@refetchable` directive. Note that `@refetchable` directive can only be added to fragments that are "refetchable", that is, on fragments that are on `Viewer`, on `Query`, on any type that implements `Node` (i.e. a type that has an `id` field), or on a [`@fetchable`](https://fb.workplace.com/groups/graphql.fyi/permalink/1539541276187011/) type. -* It returns a `refetch` function, which is already Flow-typed to expect the query variables that the generated query expects. -* It takes two Flow type parameters: the type of the generated query (in our case `CommentBodyRefetchQuery`), and a second type which can always be inferred, so you only need to pass underscore (`_`). -* We're calling the `refetch` function with 2 main inputs: - * The first argument is the set of variables to fetch the fragment with. In this case, calling `refetch` and passing a new set of variables will fetch the fragment again *with the newly provided variables*. The variables you need to provide are a subset of the variables that the `@refetchable` query expects; the query will require an `id`, if the type of the fragment has an `id` field, and any other variables that are transitively referenced in your fragment. - * In this case we're passing the current comment `id` and a new value for the `translationType` variable to fetch the translated comment body. - * We are not passing a second options argument in this case, which means that we will use the default `fetchPolicy` of `'store-or-network'`, which will skip the network request if the new data for that fragment is already cached (as we covered in [Reusing Cached Data For Render](../../reusing-cached-data/)). -* Calling `refetch` will re-render the component and may cause `useRefetchableFragment` to suspend (as explained in [Loading States with Suspense](../../rendering/loading-states/)). This means that you'll need to make sure that there's a `Suspense` boundary wrapping this component from above in order to show a fallback loading state. - -
    - -:::info -Note that this same behavior also applies to using the `refetch` function from [`usePaginationFragment`](../../../api-reference/use-pagination-fragment). -::: - -### If you need to avoid Suspense - -In some cases, you might want to avoid showing a Suspense fallback, which would hide the already rendered content. For these cases, you can use [`fetchQuery`](../../../api-reference/fetch-query/) instead, and manually keep track of a loading state: - - - - - - - - - -```js -import type {CommentBody_comment$key} from 'CommentBody_comment.graphql'; - -type Props = { - comment: CommentBody_comment$key, -}; - -function CommentBody(props: Props) { - const [data, refetch] = useRefetchableFragment( - graphql` - fragment CommentBody_comment on Comment - # @refetchable makes it so Relay autogenerates a query for - # fetching this fragment - @refetchable(queryName: "CommentBodyRefetchQuery") { - body(lang: $lang) { - text - } - } - `, - props.comment, - ); - - const [isRefetching, setIsRefreshing] = useState(false) - const refetchTranslation = () => { - if (isRefetching) { return; } - setIsRefreshing(true); - - // fetchQuery will fetch the query and write - // the data to the Relay store. This will ensure - // that when we re-render, the data is already - // cached and we don't suspend - fetchQuery(environment, AppQuery, variables) - .subscribe({ - complete: () => { - setIsRefreshing(false); - - // *After* the query has been fetched, we call - // refetch again to re-render with the updated data. - // At this point the data for the query should - // be cached, so we use the 'store-only' - // fetchPolicy to avoid suspending. - refetch({lang: 'SPANISH'}, {fetchPolicy: 'store-only'}); - } - error: () => { - setIsRefreshing(false); - } - }); - }; - - return ( - <> -

    {data.body?.text}

    - - - ); -} -``` - -Let's distill what's going on here: - -* When refetching, we now keep track of our own `isRefetching` loading state, since we are avoiding suspending. We can use this state to render a busy spinner or similar loading UI in our component, *without* hiding the content. -* In the event handler, we first call `fetchQuery`, which will fetch the query and write the data to the local Relay store. When the `fetchQuery` network request completes, we call `refetch` so that we render the updated data, similar to the previous example. -* At this point, when `refetch` is called, the data for the fragment should already be cached in the local Relay store, so we use `fetchPolicy` of `'store-only'` to avoid suspending and only read the already cached data. - - diff --git a/website/docs/guided-tour/refetching/refetching-queries-with-different-data.md b/website/docs/guided-tour/refetching/refetching-queries-with-different-data.md index 6640c5a9c101c..dcc0d05e553f9 100644 --- a/website/docs/guided-tour/refetching/refetching-queries-with-different-data.md +++ b/website/docs/guided-tour/refetching/refetching-queries-with-different-data.md @@ -13,7 +13,6 @@ import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/interna import FbRefetchingQueriesUsingUseQueryLoader from './fb/FbRefetchingQueriesUsingUseQueryLoader.md'; import FbRefetchingQueriesUsingUseLazyLoadQuery from './fb/FbRefetchingQueriesUsingUseLazyLoadQuery.md'; import FbAvoidSuspenseCaution from './fb/FbAvoidSuspenseCaution.md'; -import OssAvoidSuspenseNote from './OssAvoidSuspenseNote.md'; When referring to **"refetching a query"**, we mean fetching the query again for *different* data than was originally rendered by the query. For example, this might be to change a currently selected item, to render a different list of items than the one being shown, or more generally to transition the currently rendered content to show new or different content. @@ -109,10 +108,6 @@ In some cases, you might want to avoid showing a Suspense fallback, which would
    - - - - ```js /** * App.react.js @@ -130,6 +125,7 @@ function App(props: Props) { const refetch = useCallback(() => { if (isRefetching) { return; } setIsRefetching(true); + const variables = { id: 'different-id' }; // fetchQuery will fetch the query and write // the data to the Relay store. This will ensure @@ -146,7 +142,7 @@ function App(props: Props) { // At this point the data for the query should // be cached, so we use the 'store-only' // fetchPolicy to avoid suspending. - loadQuery({id: 'different-id'}, {fetchPolicy: 'store-only'}); + loadQuery(variables, {fetchPolicy: 'store-only'}); }, error: () => { setIsRefetching(false); @@ -271,10 +267,6 @@ In some cases, you might want to avoid showing a Suspense fallback, which would
    - - - - ```js /** * App.react.js @@ -292,6 +284,7 @@ function App(props: Props) { const refetch = useCallback(() => { if (isRefreshing) { return; } setIsRefreshing(true); + const variables = { id: 'different-id' }; // fetchQuery will fetch the query and write // the data to the Relay store. This will ensure @@ -313,7 +306,7 @@ function App(props: Props) { fetchKey: (prev?.options.fetchKey ?? 0) + 1, fetchPolicy: 'store-only', }, - variables: {id: 'different-id'} + variables, })); }, error: () => { diff --git a/website/docs/guided-tour/refetching/refreshing-fragments.md b/website/docs/guided-tour/refetching/refreshing-fragments.md deleted file mode 100644 index 937af7feb3495..0000000000000 --- a/website/docs/guided-tour/refetching/refreshing-fragments.md +++ /dev/null @@ -1,191 +0,0 @@ ---- -id: refreshing-fragments -title: Refreshing Fragments -slug: /guided-tour/refetching/refreshing-fragments/ -description: Relay guide to refreshing fragments -keywords: -- refreshing -- fragment ---- - -import DocsRating from '@site/src/core/DocsRating'; -import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; -import FbRefreshingUsingRealTimeFeatures from './fb/FbRefreshingUsingRealTimeFeatures.md'; -import FbRefreshingFragments from './fb/FbRefreshingFragments.md'; -import FbAvoidSuspenseCaution from './fb/FbAvoidSuspenseCaution.md'; -import OssAvoidSuspenseNote from './OssAvoidSuspenseNote.md'; - -When referring to **"refreshing a fragment"**, we mean fetching the *exact* same data that was originally rendered by the fragment, in order to get the most up-to-date version of that data from the server. - -## Using real-time features - - - - - - -If we want to keep our data up to date with the latest version from the server, the first thing to consider is if it appropriate to use any real-time features, which can make it easier to automatically keep the data up to date without manually refreshing the data periodically. - -One example of this is using [GraphQL Subscriptions](https://relay.dev/docs/guided-tour/updating-data/graphql-subscriptions/), which will require additional configuration on your server and [network layer](https://relay.dev/docs/guided-tour/updating-data/graphql-subscriptions/#configuring-the-network-layer). - - -## Using `useRefetchableFragment` - -In order to manually refresh the data for a fragment, we need a query to refetch the fragment under; remember, *fragments can't be fetched by themselves: they need to be part of a query,* so we can't just "fetch" the fragment again by itself. - -To do so, we can also use the [`useRefetchableFragment`](../../../api-reference/use-refetchable-fragment/) Hook in combination with the `@refetchable` directive, which will automatically generate a query to refetch the fragment under, and which we can fetch using the `refetch` function: - - - - - - - -```js -import type {UserComponent_user$key} from 'UserComponent_user.graphql'; - -type Props = { - user: UserComponent_user$key, -}; - -function UserComponent(props: Props) { - const [data, refetch] = useRefetchableFragment( - graphql` - fragment UserComponent_user on User - # @refetchable makes it so Relay autogenerates a query for - # fetching this fragment - @refetchable(queryName: "UserComponentRefreshQuery") { - id - name - friends { - count - } - } - `, - props.user, - ); - - const refresh = useCallback(() => { - // We call refetch with empty variables: `{}`, - // which will refetch the @refetchable query with the same - // original variables the fragment was fetched with, and update - // this component with the latest fetched data. - // The fetchPolicy ensures we always fetch from the server and skip - // the local data cache. - refetch({}, {fetchPolicy: 'network-only'}) - }), [/* ... */]; - - return ( - <> -

    {data.name}

    -
    Friends count: {data.friends?.count}
    - - - ); -} -``` - -Let's distill what's happening in this example: - -* `useRefetchableFragment` behaves similarly to [`useFragment`](../../../api-reference/use-fragment/) (see the [Fragments](../../rendering/fragments/) section), but with a few additions: - * It expects a fragment that is annotated with the `@refetchable` directive. Note that `@refetchable` directive can only be added to fragments that are "refetchable", that is, on fragments that are on `Viewer`, on `Query`, on any type that implements `Node` (i.e. a type that has an `id` field). -* It returns a `refetch` function, which is already Flow-typed to expect the query variables that the generated query expects -* It takes two Flow type parameters: the type of the generated query (in our case `UserComponentRefreshQuery`), and a second type which can always be inferred, so you only need to pass underscore (`_`). -* We're calling the `refetch` function with 2 main inputs: - * The first argument is the set of variables to fetch the fragment with. In this case, calling `refetch` and passing an empty set of variables will fetch the fragment again *with the exact same variables the fragment was originally fetched with,* which is what we want for a refresh. - * In the second argument we are passing a `fetchPolicy` of `'network-only'` to ensure that we always fetch from the network and skip the local data cache. -* Calling `refetch` will re-render the component and cause `useRefetchableFragment` to suspend (as explained in [Loading States with Suspense](../../rendering/loading-states/)), since a network request will be required due to the `fetchPolicy` we are using. This means that you'll need to make sure that there's a `Suspense` boundary wrapping this component from above in order to show a fallback loading state. - -
    - -:::info -Note that this same behavior also applies to using the `refetch` function from [`usePaginationFragment`](../../../api-reference/use-pagination-fragment). -::: - -### If you need to avoid Suspense - -In some cases, you might want to avoid showing a Suspense fallback, which would hide the already rendered content. For these cases, you can use [`fetchQuery`](../../../api-reference/fetch-query/) instead, and manually keep track of a loading state: - - - - - - - - - -```js -import type {UserComponent_user$key} from 'UserComponent_user.graphql'; - -type Props = { - user: UserComponent_user$key, -}; - -function UserComponent(props: Props) { - const [data, refetch] = useRefetchableFragment( - graphql` - fragment UserComponent_user on User - # @refetchable makes it so Relay autogenerates a query for - # fetching this fragment - @refetchable(queryName: "UserComponentRefreshQuery") { - id - name - friends { - count - } - } - `, - props.user, - ); - - const [isRefreshing, setIsRefreshing] = useState(false); - const refresh = useCallback(() => { - if (isRefreshing) { return; } - setIsRefreshing(true); - - // fetchQuery will fetch the query and write - // the data to the Relay store. This will ensure - // that when we re-render, the data is already - // cached and we don't suspend - fetchQuery(environment, AppQuery, variables) - .subscribe({ - complete: () => { - setIsRefreshing(false); - - // *After* the query has been fetched, we call - // refetch again to re-render with the updated data. - // At this point the data for the query should - // be cached, so we use the 'store-only' - // fetchPolicy to avoid suspending. - refetch({}, {fetchPolicy: 'store-only'}); - }, - error: () => { - setIsRefreshing(false); - } - }); - }, [/* ... */]); - - return ( - <> -

    {data.name}

    -
    Friends count: {data.friends?.count}
    - - - ); -} -``` - -Let's distill what's going on here: - -* When refreshing, we now keep track of our own `isRefreshing` loading state, since we are avoiding suspending. We can use this state to render a busy spinner or similar loading UI in our component, *without* hiding the content. -* In the event handler, we first call `fetchQuery`, which will fetch the query and write the data to the local Relay store. When the `fetchQuery` network request completes, we call `refetch` so that we render the updated data, similar to the previous example. -* At this point, when `refetch` is called, the data for the fragment should already be cached in the local Relay store, so we use `fetchPolicy` of `'store-only'` to avoid suspending and only read the already cached data. - - diff --git a/website/docs/guided-tour/refetching/refreshing-queries.md b/website/docs/guided-tour/refetching/refreshing-queries.md index 41af90583486d..cec6e54164b73 100644 --- a/website/docs/guided-tour/refetching/refreshing-queries.md +++ b/website/docs/guided-tour/refetching/refreshing-queries.md @@ -14,7 +14,6 @@ import FbRefreshingUsingRealTimeFeatures from './fb/FbRefreshingUsingRealTimeFea import FbRefreshingQueriesUsingUseQueryLoader from './fb/FbRefreshingQueriesUsingUseQueryLoader.md'; import FbAvoidSuspenseCaution from './fb/FbAvoidSuspenseCaution.md'; import FbRefreshingQueriesUsingUseLazyLoadQuery from './fb/FbRefreshingQueriesUsingUseLazyLoadQuery.md'; -import OssAvoidSuspenseNote from './OssAvoidSuspenseNote.md'; When referring to **"refreshing a query"**, we mean fetching the *exact* same data that was originally rendered by the query, in order to get the most up-to-date version of that data from the server. @@ -25,9 +24,11 @@ When referring to **"refreshing a query"**, we mean fetching the *exact* same da + If we want to keep our data up to date with the latest version from the server, the first thing to consider is if it appropriate to use any real-time features, which can make it easier to automatically keep the data up to date without manually refreshing the data periodically. One example of this is using [GraphQL Subscriptions](https://relay.dev/docs/guided-tour/updating-data/graphql-subscriptions), which will require additional configuration on your server and [network layer](https://relay.dev/docs/guided-tour/updating-data/graphql-subscriptions/#configuring-the-network-layer). + ## When using `useQueryLoader` / `loadQuery` @@ -124,10 +125,6 @@ In some cases, you might want to avoid showing a Suspense fallback, which would - - - - ```js /** * App.react.js diff --git a/website/docs/guided-tour/rendering/loading-states.md b/website/docs/guided-tour/rendering/loading-states.md index 0c52b41bd5781..fb9ed1bbc7014 100644 --- a/website/docs/guided-tour/rendering/loading-states.md +++ b/website/docs/guided-tour/rendering/loading-states.md @@ -40,18 +40,6 @@ This capability is useful for components to express asynchronous dependencies li - - -:::caution -Note that this **DOES NOT** mean that "Suspense for Data Fetching" is ready for general implementation and adoption yet. **Support, general guidance, and requirements for usage of Suspense for Data Fetching are still not ready**, and the React team is still defining what this guidance will be for upcoming React releases. - -Even though there will be some limitations when Suspense is used in React 17, Relay Hooks are stable and on the trajectory for supporting upcoming releases of React. - -For more information, see our **[Suspense Compatibility](../../../migration-and-compatibility/suspense-compatibility/)** guide. -::: - - - ## Loading fallbacks with Suspense Boundaries When a component is suspended, we need to render a *fallback* in place of the component while we wait for it to become *"ready"*. In order to do so, we use the `Suspense` component provided by React: @@ -252,6 +240,6 @@ Fragments are also integrated with Suspense in order to support rendering of dat -Additionally, our APIs for refetching ([Refreshing and Refetching](../../refetching/)) and for [rendering connections](../../list-data/connections/) are also integrated with Suspense; for these use cases, these APIs will also suspend. +Additionally, our APIs for fetching ([Refreshing and Refetching](../list-data/introduction.md)) and for [rendering connections](../../list-data/connections/) are also integrated with Suspense; for these use cases, these APIs will also suspend. diff --git a/website/docs/guided-tour/reusing-cached-data/availability-of-data.md b/website/docs/guided-tour/reusing-cached-data/availability-of-data.md deleted file mode 100644 index 81ef071f315ea..0000000000000 --- a/website/docs/guided-tour/reusing-cached-data/availability-of-data.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -id: availability-of-data -title: Availability of Data -slug: /guided-tour/reusing-cached-data/availability-of-data/ -description: Relay guide to the availability of data -keywords: -- availability ---- - -import DocsRating from '@site/src/core/DocsRating'; -import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; - -The behavior of the fetch policies described in the [previous section](../fetch-policies/) will depend on the availability of the data in the Relay store at the moment we attempt to evaluate a query. - -There are two factors that determine the availability of data: the [presence of data](../presence-of-data/) and [staleness of data](../staleness-of-data/). - - - diff --git a/website/docs/guided-tour/reusing-cached-data/fetch-policies.md b/website/docs/guided-tour/reusing-cached-data/fetch-policies.md index cf5a31a4c22f6..e44df74e5efa5 100644 --- a/website/docs/guided-tour/reusing-cached-data/fetch-policies.md +++ b/website/docs/guided-tour/reusing-cached-data/fetch-policies.md @@ -37,7 +37,7 @@ function AppTabs() { The provided `fetchPolicy` will determine: * *whether* the query should be fulfilled from the local cache, and -* *whether* a network request should be made to fetch the query from the server, depending on the [availability of the data for that query in the store](../availability-of-data/). +* *whether* a network request should be made to fetch the query from the server, depending on the availability of the data for that query in the store. By default, Relay will try to read the query from the local cache; if any piece of data for that query is [missing](../presence-of-data/) or [stale](../staleness-of-data/), it will fetch the entire query from the network. This default `fetchPolicy` is called "*store-or-network".* @@ -50,7 +50,7 @@ Specifically, `fetchPolicy` can be any of the following options: ** * "store-only": *will* *only* reuse locally cached data, and will *never* send a network request to fetch the query. In this case, the responsibility of fetching the query falls to the caller, but this policy could also be used to read and operate on data that is entirely [local](../../updating-data/local-data-updates/). -Note that the `refetch` function discussed in the [Fetching and Rendering Different Data](../../refetching/) section also takes a `fetchPolicy`. +Note that the `refetch` function discussed in the [Fetching and Rendering Different Data](../list-data/introduction.md) section also takes a `fetchPolicy`. diff --git a/website/docs/guided-tour/updating-data/introduction.md b/website/docs/guided-tour/updating-data/introduction.md index 50e732fc8134f..c2b1afc22c08a 100644 --- a/website/docs/guided-tour/updating-data/introduction.md +++ b/website/docs/guided-tour/updating-data/introduction.md @@ -1,21 +1,22 @@ --- id: introduction title: Introduction -slug: /guided-tour/updating-data/ +slug: /guided-tour/updating-data/introduction description: Relay guide to updating data keywords: - updating - mutation - useMutation - commitMutation +- relay store --- import DocsRating from '@site/src/core/DocsRating'; import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; -In previous sections, the guided tour discussed how to fetch data using GraphQL queries. Though [refetching data](../refetching/) can have the *incidental* effect of modifying data in Relay's local store (if the refetched data has changed), we haven't discussed any ways to *intentionally* modify our locally stored data. +In the [fetching data](../list-data/introduction.md) section, we discuss how to fetch data using GraphQL queries. Though fetching data can have the *incidental* effect of modifying data in Relay's local store (if the fetched data has changed), we haven't discussed any ways to *intentionally* modify our locally stored data. -This section will do just that: it will discuss how to update data on the server and how to update our local data store. +This section will do just that: it will discuss how to update our local data store, and data on the server. :::note The **Relay store** is a cache of GraphQL data, associated with a given Relay environment, that we have encountered during the execution of an application. diff --git a/website/docs/guided-tour/updating-data/typesafe-updaters-faq.md b/website/docs/guided-tour/updating-data/typesafe-updaters-faq.md index 22a2f46ca770f..3bc6a5c393460 100644 --- a/website/docs/guided-tour/updating-data/typesafe-updaters-faq.md +++ b/website/docs/guided-tour/updating-data/typesafe-updaters-faq.md @@ -32,6 +32,8 @@ Is something missing from this Q&A? Are you confused? Would you like help adopti Typesafe updaters is the name given to a project to provide a typesafe and ergonomic alternative to the existing APIs for imperatively updating data in the Relay store. +For example, [`readUpdatableFragment`](../../../api-reference/store/#readupdatablefragmentfragment-updatablefragmenttfragmenttype-tdatafragmentreference-hasupdatablespreadtfragmenttype-updatabledatatdata) and [`readUpdatableQuery`](../../../api-reference/store/#readupdatablequeryquery-updatablequerytvariables-tdatavariables-tvariables-updatabledatatdata) are two typesafe updaters that the store exposes. + ## Why? Relay provides typesafe and ergonomic APIs for fetching and managing data that originates on the server. In addition, Relay provides the ability to define local-only fields in **client schema extensions**. However, the APIs for mutating the data in these fields has hitherto been verbose and not ergonomic, meaning that we could not recommend Relay as a solution for managing local state. @@ -40,16 +42,12 @@ Relay provides typesafe and ergonomic APIs for fetching and managing data that o The pre-existing APIs are verbose and not typesafe. They make it easy to make a variety of mistakes and require that the developer understand a new set of APIs only when writing updaters. -Typesafe updaters is a set of APIs that are typesafe and (hopefully) more ergonomic. They leverage well-known Relay idioms (queries, fragments, type refinement) and use getters and setters instead of requiring that the developer learn about a set of methods that are unused elsewhere. +Typesafe updaters is a set of APIs that are typesafe and more ergonomic. They leverage well-known Relay idioms (queries, fragments, type refinement) and use getters and setters instead of requiring that the developer learn about a set of methods that are unused elsewhere. ## How does a developer use typesafe updaters? With typesafe updaters, a developers writes an updatable query or a fragment that specifies the data to imperatively update. Then, the developer reads out that data from the store, returning a so-called **updatable proxy**. Then, the developer mutates that updatable proxy. Mutating that updatable proxy using setters (e.g. `updatableData.name = "Godzilla"`) results in calls to the old API, but with added type safety. -## Why are these labeled `_EXPERIMENTAL`? - -These are de facto not experimental. We encourage you to use them when writing new code! This suffix will be removed soon. - ## What is an updatable query or fragment? An updatable query or fragment is a query or fragment that has the `@updatable` directive. @@ -77,7 +75,7 @@ You should select that field in both a regular query/fragment **and** in an upda ## Where do I get a `store`? -The classes `RelayRecordSourceSelectorProxy` and `RelayRecordSourceProxy` contain the methods `readUpdatableQuery` and `readUpdatableFragment`. One can acquire an instance of these classes: +The classes `RelayRecordSourceSelectorProxy`, `RecordSourceProxy` and `RelayRecordSourceProxy` contain the methods `readUpdatableQuery` and `readUpdatableFragment`. One can acquire an instance of these classes: * In updaters of mutations and subscriptions * In optimistic updaters of mutations diff --git a/website/docs/guided-tour/list-data/updating-connections.md b/website/docs/guided-tour/updating-data/updating-connections.md similarity index 96% rename from website/docs/guided-tour/list-data/updating-connections.md rename to website/docs/guided-tour/updating-data/updating-connections.md index f0430022586bb..fcc0f73cfe8c1 100644 --- a/website/docs/guided-tour/list-data/updating-connections.md +++ b/website/docs/guided-tour/updating-data/updating-connections.md @@ -15,7 +15,7 @@ import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/interna Usually when you're rendering a connection, you'll also want to be able to add or remove items to/from the connection in response to user actions. -As explained in our [Updating Data](../../updating-data/) section, Relay holds a local in-memory store of normalized GraphQL data, where records are stored by their IDs. When creating mutations, subscriptions, or local data updates with Relay, you must provide an [`updater`](../../updating-data/graphql-mutations/#updater-functions) function, inside which you can access and read records, as well as write and make updates to them. When records are updated, any components affected by the updated data will be notified and re-rendered. +Relay holds a local in-memory store of normalized GraphQL data, where records are stored by their IDs. When creating mutations, subscriptions, or local data updates with Relay, you must provide an [`updater`](../../updating-data/graphql-mutations/#updater-functions) function, inside which you can access and read records, as well as write and make updates to them. When records are updated, any components affected by the updated data will be notified and re-rendered. ## Connection Records @@ -553,11 +553,9 @@ function updater(store: RecordSourceSelectorProxy) { -_Managing connections with many filters:_ +### Managing connections with many filters -As you can see, just adding a few filters to a connection can make the complexity and number of connection records that need to be managed explode. In order to more easily manage this, Relay provides 2 strategies: - -1) Specify exactly *which* filters should be used as connection identifiers. +As you can see, just adding a few filters to a connection can make the complexity and number of connection records that need to be managed explode. In order to more easily manage this, Relay allows you to specify exactly *which* filters should be used as connection identifiers. By default, *all* non-pagination filters will be used as part of the connection identifier. However, when declaring a `@connection`, you can specify the exact set of filters to use for connection identity: @@ -591,13 +589,4 @@ const storyFragment = graphql` * Conceptually, this means that we're specifying which arguments affect the output of the connection from the server, or in other words, which arguments are *actually* *filters*. If one of the connection arguments doesn't actually change the set of items that are returned from the server, or their ordering, then it isn't really a filter on the connection, and we don't need to identify the connection differently when that value changes. In our example, changing the `language` of the comments we request doesn't change the set of comments that are returned by the connection, so it is safe to exclude it from `filters`. * This can also be useful if we know that any of the connection arguments will never change in our app, in which case it would also be safe to exclude from `filters`. - - -2) An easier API alternative to manage multiple connections with multiple filter values is still pending - - -> TBD - - - diff --git a/website/docs/guided-tour/workflow.md b/website/docs/guided-tour/workflow.md deleted file mode 100644 index ce8aa650a3127..0000000000000 --- a/website/docs/guided-tour/workflow.md +++ /dev/null @@ -1,39 +0,0 @@ ---- -id: workflow -title: Workflow -slug: /guided-tour/workflow/ -description: Relay guide to workflow -keywords: -- workflow -- compiler ---- - -import DocsRating from '@site/src/core/DocsRating'; -import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; -import FbWorkflow from './fb/FbWorkflow.md'; - - - - - - - -Before we can get started writing Relay code, we need to make sure to **[setup the Relay Compiler](../../getting-started/installation-and-setup/#set-up-relay-compiler)**. - -The **[Relay Compiler](../../guides/compiler/)** will analyze any `graphql` literals inside your Javascript code, and produce a set of artifacts that are used by Relay at runtime, when the application is running on the browser. - -So whenever we're developing Relay components, for example by writing [Fragments](../rendering/fragments/) or [Queries](../rendering/queries/), we will need to run the Relay Compiler: - -```sh -yarn run relay -``` - -Or we can run it in watch mode, so the artifacts are re-generated as we update our source code: - -```sh -yarn run relay --watch -``` - - - - diff --git a/website/docs/guides/alias-directive.md b/website/docs/guides/alias-directive.md index 3624fa480560b..e56bfe0c41501 100644 --- a/website/docs/guides/alias-directive.md +++ b/website/docs/guides/alias-directive.md @@ -101,9 +101,9 @@ function MyUser({userKey}) { ## Enforced Safety -We've outlined two different ways that fragments can be unsafe in Relay today without `@alias`. To help prevent runtime issues resulting from these unsafe edge cases, Relay will soon require that all conditionally fetched fragments be aliased. +We've outlined two different ways that fragments can be unsafe in Relay today without `@alias`. To help prevent runtime issues resulting from these unsafe edge cases, Relay requires that all conditionally fetched fragments are aliased. -To experiment with this validation in your project today, you can enable the experimental `enforce_fragment_alias_where_ambiguous` compiler feature flag for your project. To enable incremental adoption of this enforcement, Relay exposes a directive `@dangerously_unaliased_fixme` which will suppress these enforcement errors. This will allow you to enable the enforcement for all new spreads without first needing to migrate all existing issues. +To disable this validation in your project, you can disable the `enforce_fragment_alias_where_ambiguous` compiler feature flag for your project. If you need to enable incremental adoption of this enforcement, Relay exposes a directive `@dangerously_unaliased_fixme` which will suppress enforcement errors. This will allow you to enable the enforcement for all new spreads without first needing to migrate all existing issues. The [Relay VSCode extension](../editor-support.md) offers quick fixes to add either `@alias` or `@dangerously_unaliased_fixme` to unsafe fragments, and the [mark-dangerous-conditional-fragment-spreads](../codemods/#mark-dangerous-conditional-fragment-spreads) codemod can be used to apply `@dangerously_unaliased_fixme` across your entire project. diff --git a/website/docs/guides/client-schema-extensions.md b/website/docs/guides/client-schema-extensions.md index a9a0959e0d49c..ef9c7c4272e5c 100644 --- a/website/docs/guides/client-schema-extensions.md +++ b/website/docs/guides/client-schema-extensions.md @@ -29,9 +29,10 @@ The Relay Compiler fully supports client-side extensions of the schema, which al ## Extending the server schema -To extend the server schema, create a new `.graphql` file inside your `--src` directory. -Let's call it `./src/clientSchema.graphql`. -This file needs to be in a folder referenced in the `"schemaExtensions"` of your Relay config. +To extend the server schema, create a new `.graphql` file inside your `--src` +directory. Let's call it `./src/clientSchema.graphql`. This file needs to be +referenced in the `"schemaExtensions"` of your Relay config, either directly or +via its folder. This schema describes what local data can be queried on the client. It can even be used to extend an existing server schema. diff --git a/website/docs/guides/data-driven-dependencies/client-3d.md b/website/docs/guides/data-driven-dependencies/client-3d.md index 5f4d35eae23e9..99bc096f91eb9 100644 --- a/website/docs/guides/data-driven-dependencies/client-3d.md +++ b/website/docs/guides/data-driven-dependencies/client-3d.md @@ -23,7 +23,9 @@ Here is an example of how Client 3D can be used in a React app. -> **NOTE:** For an example diff implementing Client 3D in a www repository, see D62352682. +:::note +For an example diff implementing Client 3D in a www repository, see D62352682. +::: @@ -184,9 +186,15 @@ in this example, these separate fragments are `FOO_FRAGMENT`, `BAR_FRAGMENT`, an to this fragment's data as an argument. 3. Return the final component using Relay's `MatchContainer`, providing the returned query data as a prop. +Notice that in Client 3D, just as in Server 3D, you cannot use `@module` on multiple fragments on the SAME concrete type (but they can be on the same abstract type i.e. a union or an interface). + +So in this example, `Client3DFooComponent_Fragment` is on the concrete type `Client3DFoo`, and `Client3DBarComponent_Fragment` is on the concrete type `Client3DBar`. If `Client3DBarComponent_Fragment` was also on `Client3DFoo`, the relay compiler would report an error. However, all three concrete types implement the same parent interface `IClient3D`, which is fine. + -> **NOTE:** If you are in www, but not in Comet, you should use `RelayFBMatchContainer` instead of `MatchContainer`. +:::tip +If you are in www, but not in Comet, you should use `RelayFBMatchContainer` instead of `MatchContainer`. +::: diff --git a/website/docs/guides/data-driven-dependencies/server-3d.md b/website/docs/guides/data-driven-dependencies/server-3d.md index e0fa8e51ada8f..96c6183601ceb 100644 --- a/website/docs/guides/data-driven-dependencies/server-3d.md +++ b/website/docs/guides/data-driven-dependencies/server-3d.md @@ -18,13 +18,17 @@ import FbSuspensePlaceholder from '../../fb/FbSuspensePlaceholder.md'; -> **NOTE:** throughout this guide, we use `MatchContainer`. If you are in www, but not in Comet, you should use RelayFBMatchContainer. +:::note +Throughout this guide, we use `MatchContainer`. If you are in www, but not in Comet, you should use RelayFBMatchContainer. +::: -> **NOTE:** Server 3D requires configuring your server to support various features! It is unlikely to work in OSS without significant work. Relay does not claim to fully support Server 3D in OSS (yet), but [Client 3D](../client-3d/) is fully supported. +:::note +Server 3D requires configuring your server to support various features! It is unlikely to work in OSS without significant work. Relay does not claim to fully support Server 3D in OSS (yet), but [Client 3D](../client-3d/) is fully supported. +::: @@ -126,7 +130,9 @@ function CommentRenderer(props) { module.exports = CommentRenderer; ``` -> **IMPORTANT:** When using MatchContainer, the component loaded using 3D needs to have the same prop name as the fragment suffix e.g. if your fragment is `Comment_comment`, your prop needs to be called `comment` instead of something like `comment$key` +:::caution +When using MatchContainer, the component loaded using 3D needs to have the same prop name as the fragment suffix e.g. if your fragment is `Comment_comment`, your prop needs to be called `comment` instead of something like `comment$key` +::: ## Advanced 3D with match @@ -276,6 +282,10 @@ public function commentContentRenderer(Traversable $supported): Awaitabl Your Relay fragment can now use `@match` to specify that for the `comment_content_renderer` field, we expect dependencies to be decided by the data. In this example, if the `comment_content_renderer` field is of type `CommentMarkdownRenderer`, we load the `CommentMarkdownRenderer.react` component and use the `CommentMarkdownRenderer_comment` fragment to load its data. Similar for the plaintext variant. +:::caution +The inline fragments annotated with `@module` on the same parent 3D fragment must be on distinct concrete types. If they are on the same concrete type, the relay compiler will report an error. So in the example below, `CommentMarkdownRenderer_comment` must be on a different concrete type than `CommentPlaintextRenderer_comment` (for example, the former could be on a `MarkdownComment` type, and the latter on a `PlaintextComment` type. Both could implement a parent interface `Comment`). +::: + On the Relay side you'd write: ```graphql @@ -340,7 +350,9 @@ function CommentRenderer(props) { module.exports = CommentRenderer; ``` -> **IMPORTANT:** When using MatchContainer, the component loaded using 3D needs to have the same prop name as the fragment suffix e.g. if your fragment is `Comment_comment`, your prop needs to be called `comment` instead of something like `comment$key` +:::caution +When using MatchContainer, the component loaded using 3D needs to have the same prop name as the fragment suffix e.g. if your fragment is `Comment_comment`, your prop needs to be called `comment` instead of something like `comment$key` +::: ## Multiple 3D Selections Per Fragment @@ -468,7 +480,9 @@ The typical usage of data-driven dependencies is to dynamically load modules tha -> In www, outside of Comet, you should use `RelayFBModuleResource.read()` instead of `ModuleResource.read()`. +:::tip +In www, outside of Comet, you should use `RelayFBModuleResource.read()` instead of `ModuleResource.read()`. +::: @@ -526,7 +540,9 @@ You can also use `@module` directly to load a non-React module for a field if it -> In www, outside of Comet, you should use `RelayFBModuleResource.read()` instead of `ModuleResource.read()`. +:::tip +In www, outside of Comet, you should use `RelayFBModuleResource.read()` instead of `ModuleResource.read()`. +::: diff --git a/website/docs/guides/network-layer.md b/website/docs/guides/network-layer.md index 37e7076fa9552..c798f21f542bf 100644 --- a/website/docs/guides/network-layer.md +++ b/website/docs/guides/network-layer.md @@ -9,15 +9,8 @@ keywords: --- import DocsRating from '@site/src/core/DocsRating'; -import useBaseUrl from '@docusaurus/useBaseUrl'; - - -> In most cases, the network layer is setup for you. You should not need to worry about this step unless you are setting up a new environment. - - - -In order to know how to access your GraphQL server, Relay requires developers to provide an object implementing the `INetwork` interface when creating an instance of a Relay Environment. The environment uses this network layer to execute queries, mutations, and (if your server supports them) subscriptions. This allows developers to use whatever transport (HTTP, WebSockets, etc) and authentication is most appropriate for their application, decoupling the environment from the particulars of each application's network configuration. +In order to know how to access your GraphQL server, Relay requires developers to provide an object implementing the `INetwork` interface when creating an instance of a [Relay Environment](../api-reference/relay-runtime/relay-environment.md). The environment uses this network layer to execute queries, mutations, and (if your server supports them) subscriptions. This allows developers to use whatever transport (HTTP, WebSockets, etc) and authentication is most appropriate for their application, decoupling the environment from the particulars of each application's network configuration. Currently the easiest way to create a network layer is via a helper from the `relay-runtime` package: @@ -65,10 +58,12 @@ const environment = new Environment({ export default environment; ``` -Note that this is a basic example to help you get started. This example could be extended with additional features such as request/response caching (enabled e.g. when `cacheConfig.force` is false) and uploading form data for mutations (the `uploadables` parameter). +:::warning +This is just a basic example to help you get started. Features like `@stream`, `@defer` or Persisted Queries will require additional logic be added to your `fetchQuery` function. +::: -## Caching +## Advanced Network Layer -The Relay store will cache data from queries that are currently retained. See the section on [reusing cached data](../../guided-tour/reusing-cached-data/) of the guided tour. +The network layer provides a place for developers to customize the data fetching behaviors. For example, adding network level caching, telemetry or support for uploading form data for mutations. diff --git a/website/docs/guides/persisted-queries.md b/website/docs/guides/persisted-queries.md index 3d7ff66adfcad..be253ff600837 100644 --- a/website/docs/guides/persisted-queries.md +++ b/website/docs/guides/persisted-queries.md @@ -12,7 +12,9 @@ import {FbInternalOnly, OssOnly} from 'docusaurus-plugin-internaldocs-fb/interna -> Persistence is handled by the `relay` command for you. You likely do not need to worry about the contents of this guide. +:::tip +At Meta, persistence is handled by the `relay` command for you. You likely do not need to worry about the contents of this guide. +::: diff --git a/website/docs/guides/relay-resolvers/defining-fields.md b/website/docs/guides/relay-resolvers/defining-fields.md index 58d6d66e55954..a384138e1f102 100644 --- a/website/docs/guides/relay-resolvers/defining-fields.md +++ b/website/docs/guides/relay-resolvers/defining-fields.md @@ -110,3 +110,42 @@ This is just a simple resolver that reads from the model type and returns a scal * [Field Arguments](./field-arguments.md) * [Live Fields](./live-fields.md) * [Derived Fields](./derived-fields.md) + + + +## Simplified Syntax for Property Lookups + +If you have a ["weak" type](./defining-types.md#defining-a-weak-type), you can easily define a simple resolver that just returns a property from the underlying model. For example, take a resolver being defined on the `UserModel` that looks like: +```tsx +/** + * @RelayResolver + */ +export function name(user: UserModel): string { + return user.name; +} +``` + +When defining the weak type, this resolver can by automatically generated by using a docblock with `@gqlField` over the field you want to expose. +```tsx +/** + * @RelayResolver + */ +export type UserModel = { + /** + * @gqlField + */ + name: string, +} +``` + +You can optionally include a description or `@deprecated` tag in the docblock +```tsx +/** + * @gqlField + * @deprecated Do not use this field anymore + * + * This is a description. Include more information + * about your field here. + */ +``` + diff --git a/website/docs/guides/relay-resolvers/defining-types.md b/website/docs/guides/relay-resolvers/defining-types.md index 128e3183739a3..66471ade373ca 100644 --- a/website/docs/guides/relay-resolvers/defining-types.md +++ b/website/docs/guides/relay-resolvers/defining-types.md @@ -125,7 +125,9 @@ interface IUser { You could define two (or more) concrete resolver types that implement the IUser interface by adding annotations in the docblock (the same applies for unions). + Note, support for abstract types is not available for relay resolvers in Flow syntax (yet). + `. The codemod doesn't support all cases, so you might need to modify some files manually after it runs. + diff --git a/website/docs/guides/relay-resolvers/return-types.md b/website/docs/guides/relay-resolvers/return-types.md index 9f4e1bb070504..d3a48b89b9907 100644 --- a/website/docs/guides/relay-resolvers/return-types.md +++ b/website/docs/guides/relay-resolvers/return-types.md @@ -109,6 +109,38 @@ function Post() { } ``` +## Abstract Types + +Resolvers may return some permutations of "abstract" types (GraphQL unions and interfaces). To use this feature simply use the abstract type's name in the docblock field description and include the typename in the object returned from your resolver. For "strong" types, that will look like: `{id: DataID, __typename: string}`. For "weak" types that will look like: `{__relay_model_instance: T, __typename: string}`. + +```tsx +import {DataID} from 'relay-runtime'; + +type AnimalTypenames = "Cat" | "Dog"; +/** + * @RelayResolver User.pet: Animal + */ +export function pet(user: User): {id: DataID, __typename: AnimalTypenames } { + return {id: "5", __typename: "Dog" } +} +``` + +:::tip +Relay will generate type assertions to ensure your resolver function returns the expected type. However, not all combinations are supported. For example, Relay does not yet support the following permutations of abstract types: Unions including weak types, abstract types which mix strong add weak types, and abstract types which include server-backed types. +::: + +While abstract types themselves cannot be defined using Resolver syntax today, you may define interfaces and unions, as well as their members, using [Client Schema Extensions](../client-schema-extensions.md). For example: + +```graphql title="client-schema.graphql" +interface Animal { + legs: Int +} + +extend type Cat implements Animal { + __do_not_use: String # Placeholder because GraphQL does not allow empty field sets. +} +``` + ## JavaScript Values There are rare cases where you want to return an arbitrary JavaScript value from your Resolver schema, one which cannot not have a corresponding GraphQL type. As an escape hatch Relay supports a custom return type `RelayResolverValue` that allows you to return any JavaScript value from your resolver. **JavaScript values returned from resolvers should be immutable.** diff --git a/website/docs/guides/required-directive.md b/website/docs/guides/required-directive.md index 470d7a5db2fd1..65debdfac2b06 100644 --- a/website/docs/guides/required-directive.md +++ b/website/docs/guides/required-directive.md @@ -180,7 +180,9 @@ fragment MyFrag on Actor { In this situation Relay will generate a union type like: `{__typename: 'User', name: string} | {__typename: '%ignore this%}`. Now you can check the `__typename` field to narrow your object's type down to one that has a non-nullable `name`. + Example diff showing the adoption of this strategy: D24370183 + ### Why not implement this at the schema/server level? @@ -197,6 +199,7 @@ Basically every value returned by Relay is nullable. This is intentional since w _Extracted from [this comment thread](https://fb.workplace.com/groups/cometeng/permalink/937671436726844/?comment_id=937681186725869)._ _Further discussion in [this comment thread](https://fb.workplace.com/groups/cometeng/permalink/937671436726844/?comment_id=938335873327067)._ + ### Can `(action: NONE)` be the default? diff --git a/website/docs/guides/testing-relay-components.md b/website/docs/guides/testing-relay-components.md index ea4de6f2cc36e..a4a4fe8fc73e2 100644 --- a/website/docs/guides/testing-relay-components.md +++ b/website/docs/guides/testing-relay-components.md @@ -57,7 +57,9 @@ With the `MockPayloadGenerator` and `@relay_test_operation`, we want to get rid **[React Testing Library](https://testing-library.com/react)** is a set of helpers that let you test React components without relying on their implementation details. This approach makes refactoring a breeze and also nudges you towards best practices for accessibility. Although it doesn't provide a way to "shallowly" render a component without its children, a test runner like Jest lets you do this by [mocking](https://reactjs.org/docs/testing-recipes.html#mocking-modules). + Note: The [`ReactTestRenderer`](https://www.npmjs.com/package/react-test-renderer) library has been deprecated since React v18. `ReactTestRenderer` may still be referenced internally as an alternative to React Testing Library. However, when possible, we recommend using React Testing Library (or `@testing-library/react-native`) to test your React applications with Relay. + ## RelayMockEnvironment API Overview @@ -537,7 +539,9 @@ test('it should send mutation', () => { ### Subscription -> The `useSubscription` API is an improvement over calling `requestSubscription` directly. +:::tip +The `useSubscription` API is an improvement over calling `requestSubscription` directly. +::: We can test subscriptions similarly to how we test mutations. diff --git a/website/docs/home.md b/website/docs/home.md index 724253511e34b..9038b9efe33c4 100644 --- a/website/docs/home.md +++ b/website/docs/home.md @@ -16,50 +16,17 @@ keywords: import DocsRating from '@site/src/core/DocsRating'; import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; -Relay is a data management library for React that lets you fetch and update data with GraphQL. It embodies years of learning to give you **outstanding performance by default** while keeping your code **stable and maintainable**. +Relay is a powerful [GraphQL](https://graphql.org/) client for [React](https://react.dev/). It embodies years of learning to give you **outstanding performance by default** while keeping your code **scalable and maintainable**. Relay brings the composability of React components to data fetching. Each component declares its own data needs, and Relay combines them into efficient pre-loadable queries. Every aspect of its design is to make the natural way of writing components also the most performant. ## Features -* Declarative data: Just declare what data each component needs and Relay will handle the loading states. -* Co-location and composability: Each component declares its own data needs; Relay combines them into efficient queries. When you re-use a component on a different screen, your queries are automatically updated. -* Pre-fetching: Relay analyses your code so you can start fetching queries before your code even downloads or runs. -* UI patterns: Relay implements loading states, pagination, refetching, optimistic updates, rollbacks, and other common UI behaviors that are tricky to get right. -* Consistent updates: Relay maintains a normalized data store, so components that observe the same data stay in sync even if they reach it by different queries. -* Streaming and deferred data: Declaratively defer parts of your query and Relay will progressively re-render your UI as the data streams in. -* Great developer experience: Relay provides autocompletion and go-to-definition for your GraphQL schema. -* Type safety: Relay generates type definitions so that mistakes are caught statically, not at runtime. -* Manage local data: Use the same API for server data and local client state. -* Hyper-optimized runtime: Relay is relentlessly optimized. Its JIT-friendly runtime processes incoming data faster by statically determining what payloads to expect. - -## Stack - -Relay works on the Web and on React Native — it is used extensively at Meta in both environments. It is framework-agnostic and works with Next, React Router, Create React App, etc. It works with both TypeScript and Flow. - -Relay is completely tied to GraphQL, so if you cannot use GraphQL then it's not the right choice for you. - -Relay has a UI-agnostic layer that fetches and manages data, and a React-specific layer that handles loading states, pagination, and other UI paradigms. It is mainly supported when used with React, although you can access your Relay data outside of React if you need to. The React-specific parts of Relay are based on Suspense, so there are some limitations if you're stuck on an older version of React. - -## Where to Go from Here - - - -
    -Start with the tutorial — it will take you step-by-step through building a Relay app. -
    - - -- An overview of the **[prerequisites](./getting-started/prerequisites/)** for using Relay, and an **[installation and setup guide](./getting-started/installation-and-setup/)**. -- The **[API reference](./api-reference/relay-environment-provider/)**, for a reference of our APIs including a detailed overview of their inputs and outputs. - -
    - - - -- Start with the **[tutorial](./tutorial/intro/)** — it will take you step-by-step through building a Relay app. -- The **[API reference](./api-reference/relay-environment-provider/)**, for a reference of our APIs including a detailed overview of their inputs and outputs. - - - - +* **Declarative**: Just declare what data each component needs and Relay will handle generating [optimal queries](https://relay.dev/blog/2023/10/24/how-relay-enables-optimal-data-fetching/) for each surface. +* **Composable**: Components act like building bricks that can click into place anywhere in your app without needing manually update queries. +* **Pre-fetchable**: Relay's generated queries allow you to start fetching data for your surface before your code even downloads or runs. +* **Built-in UI patterns**: Relay implements loading states, pagination, refetching, optimistic updates, rollbacks, and other common UI behaviors that are tricky to get right. +* **Consistent state**: Relay maintains a normalized data store, so components that observe the same data stay in sync even if they reach it by different queries. +* **Type safe**: Relay generates TypeScript types for each GraphQL snippet so that errors are caught statically, not at runtime. +* **Streaming/deferred data**: Declaratively defer parts of your query and Relay will progressively re-render your UI as the data streams in. +* **Developer experience**: Relay's [editor support](./editor-support.md) provides autocompletion and go-to-definition for your GraphQL schema. diff --git a/website/docs/migration-and-compatibility/relay-hooks-and-legacy-container-apis.md b/website/docs/migration-and-compatibility/relay-hooks-and-legacy-container-apis.md deleted file mode 100644 index fc9d1dc248aea..0000000000000 --- a/website/docs/migration-and-compatibility/relay-hooks-and-legacy-container-apis.md +++ /dev/null @@ -1,564 +0,0 @@ ---- -id: relay-hooks-and-legacy-container-apis -title: Relay Hooks and Legacy Container APIs -slug: /migration-and-compatibility/relay-hooks-and-legacy-container-apis/ -description: Relay guide to compatibility between hooks and containers -keywords: -- migration -- compatibility -- container -- QueryRenderer -- FragmentContainer -- RefetchContainer -- PaginationContainer ---- - -import DocsRating from '@site/src/core/DocsRating'; - -## Compatibility between Relay Hooks and Containers - -Relay Hooks are fully compatible with Relay's [container-based APIs](../../api-reference/legacy-apis/), meaning that containers can render components that use Hooks, and vice-versa. - -This means that you can adopt Relay Hooks incrementally, either by using them exclusively for new code, or by migrating specific parts of your app, without affecting the rest of your existing application. - - -## Migrating existing container-based code - -As we've mentioned, migrating existing code to Relay Hooks is ***not*** required, and **container-based code will continue to work**. - -However, in this section we will go over common migration patterns you can follow if you do choose to migrate container-based code to Relay Hooks. - - -### `QueryRenderer` → `useLazyLoadQuery` - -Converting from a `QueryRenderer` to the [`useLazyLoadQuery`](../../api-reference/use-lazy-load-query/) Hook is the most straightforward conversion, and will have a similar behavior of fetching the specified query *during render.* - -To convert a `QueryRenderer` to `useLazyLoadQuery`, you need to take the following steps: - -1. Render a [`RelayEnvironmentProvider`](../../api-reference/relay-environment-provider/) where the QueryRenderer was, or above it. Usually, we recommend rendering the `RelayEnvironmentProvider` at the very root of your app: - -```js - - - -``` - - -2. Convert the `QueryRenderer` into `useLazyLoadQuery`: - -**Before:** - -```js -import * as React from 'React'; -import {graphql, QueryRenderer} from 'react-relay'; - -export default function Home() { - return ( - { - if (error) { - return ; - } - if (!props) { - return ; - } - return

    {props.user?.name}

    - }} - /> - ); -} -``` - - -**After:** -Fetch and render the query: - -```js -import * as React from 'React'; -import {graphql, useLazyLoadQuery} from 'react-relay'; - -export default function Home() { - const data = useLazyLoadQuery( - graphql` - query HomeQuery($id: ID!) { - user(id: $id) { - name - } - } - `, - {id: 4}, - ); - - return

    {data.user?.name}

    ; -} -``` - -[Loading states](../../guided-tour/rendering/loading-states/) and [error states](../../guided-tour/rendering/error-states/) are handled by Suspense and Error Boundaries: - -```js - - }> - - - -``` - - - -### `QueryRenderer` → `useQueryLoader` + `usePreloadedQuery` - -Unlike `useLazyLoadQuery`, using [`useQueryLoader`](../../api-reference/use-query-loader/) in combination with [`usePreloadedQuery`](../../api-reference/use-preloaded-query/) will start fetching the data *ahead* of render, following the "render-as-you-fetch" pattern. This means that the data fetch will start sooner, and potentially speed up the time it takes to show content to users. - -To make best use of this pattern, query loading is usually integrated at the router level, or other parts of your UI infra. To see a full example, see our [`issue-tracker`](https://github.com/relayjs/relay-examples/blob/main/issue-tracker/src/routes.js) example app. - - -To convert a `QueryRenderer` to `useQueryLoader`, you need to take the following steps: - -1. Render a [`RelayEnvironmentProvider`](../../api-reference/relay-environment-provider/) where the QueryRenderer was, or above it. Usually, we recommend rendering the `RelayEnvironmentProvider` at the very root of your app: - -```js - - - -``` - -2. Convert the `QueryRenderer` into `useQueryLoader` + `usePreloadedQuery`: - -**Before:** - -```js -import * as React from 'React'; -import {graphql, QueryRenderer} from 'react-relay'; - -export default function UserPopover() { - return ( - { - if (error) { - return ; - } - if (!props) { - return ; - } - return

    {props.user?.name}

    - }} - /> - ); -} -``` - - -**After:** -Render the preloaded query: - -```js -import * as React from 'React'; -import {graphql, usePreloadedQuery} from 'react-relay'; - -export default function UserPopover(props) { - const data = usePreloadedQuery( - graphql` - query UserPopoverQuery($id: ID!) { - user(id: $id) { - name - } - } - `, - props.queryRef, - ); - - return

    {data.user?.name}

    ; -} -``` - - -Load the query with `loadQuery` from `useQueryLoader`. This part of the code would usually be integrated in your routing, or other parts of your UI infra: - -```js -import * as React from 'React'; -import {useQueryLoader} from 'react-relay'; - -// Import the query defined in the UserPopover component -import UserPopoverQuery from '__generated__/UserPopoverQuery.graphql'; - -// This is *NOT* a real-world example, only used -// to illustrate usage. - -export default function UserPopoverButton(props) { - const [queryRef, loadQuery] = useQueryLoader(UserPopoverQuery) - - const handleClick = useCallback(() => { - // Load the query in the event handler, onClick - loadQuery({id: props.userID}) - }, [loadQuery, props.userID]); - - return ( - <> - - - ); -} - -export default createRefetchContainer( - CommentBody, - { - user: graphql` - fragment CommentBody_comment on Comment { - body(lang: $lang) { - text - } - } - `, - }, - - // This option is no longer required, the refetch query - // will automatically be generated by Relay using the @refetchable - // directive. - graphql` - query AppQuery($id: ID!, lang: Lang) { - node(id: $id) { - ...CommentBody_comment - } - } - `, -); -``` - -**After:** - -```js -import * as React from 'React'; -import {graphql, useRefetchableFragment} from 'react-relay'; - -export default function CommentBody(props: Props) { - const [data, refetch] = useRefetchableFragment( - graphql` - fragment CommentBody_comment on Comment - @refetchable(queryName: "CommentBodyRefetchQuery") { - body(lang: $lang) { - text - } - } - `, - props.comment, - ); - - const handleClick = useCallback(() => { - refetch({lang: 'SPANISH'}); - }, [refetch]); - - return ( - <> -

    {data.body?.text}

    - - - ); -} -``` - - - -### Pagination Container → `usePaginationFragment` - -The pagination API for [`usePaginationFragment`](../../api-reference/use-pagination-fragment/) has been greatly simplified and reduced compared to the former PaginationContainer. Migration will require mapping inputs into the new API. - -**Before:** - -```js -import * as React from 'React'; -import {graphql, createPaginationContainer} from 'react-relay'; - -class UserContainerComponent extends React.Component { - render(): React.Node { - const isLoading = this.props.relay.isLoading() || this.state.loading; - const hasMore = this.props.relay.hasMore(); - - return ( - <> - - - - ); - } - - loadMore() { - if ( - !this.props.relay.hasMore() || - this.props.relay.isLoading() || - this.state.loading - ) { - return; - } - - this.setState({loading: true}); - - this.props.relay.loadMore(5, () => this.setState({loading: false})); - } -} - -export default createPaginationContainer( - UserContainerComponent, - { - user: graphql` - fragment UserContainerComponent_user on User - @argumentDefinitions(count: {type: "Int!"}, cursor: {type: "ID"}) - @refetchable(queryName: "UserComponentRefetchQuery") { - friends(first: $count, after: $cursor) - @connection(key: "UserComponent_user_friends") { - edges { - node { - name - } - } - } - } - `, - }, - { - // This option is no longer necessary, usePaginationFragment supports - // bi-directional pagination out of the box. - direction: 'forward', - - // This option is no longer required, and will be automatically - // determined by usePaginationFragment - getConnectionFromProps(props: Props) { - return props.user?.friends; - }, - - // This option is no longer required, and will be automatically - // determined by usePaginationFragment - getFragmentVariables(vars, count) { - return {...vars, count}; - }, - - // This option is no longer required, and will be automatically - // determined by usePaginationFragment - getVariables(props: Props, {count, cursor}) { - return { - cursor, - count, - }; - }, - - // This option is no longer required, the pagination query - // will automatically be generated by Relay using the @refetchable - // directive. - query: graphql` - query UserContainerComponentQuery { - viewer { - actor { - ... on User { - ...UserContainerComponent_user @arguments(count: 10) - } - } - } - } - `, - }, -); -``` - - -**After:** - -```js -import * as React from 'React'; -import {graphql, usePaginationFragment} from 'react-relay'; - -export default function UserComponent(props: Props) { - const {data, loadNext, hasNext, isLoadingNext} = usePaginationFragment( - graphql` - fragment UserComponent_user on User - @refetchable(queryName: "UserComponentRefetchQuery") { - friends(first: $count, after: $after) - @connection(key: "UserComponent_user_friends") { - edges { - node { - name - } - } - } - } - `, - props.user, - ); - - const handleClick = useCallback(() => { - loadNext(5) - }, [loadNext]) - - return ( - <> - - - - ); -} -``` - - - - -* * * - -### QueryRenderer → useEntryPointLoader + EntryPointContainer - -TODO - - - -### commitMutation → useMutation - -TODO - - -### requestSubscription → useSubscription - -TODO - - diff --git a/website/docs/migration-and-compatibility/suspense-compatibility.md b/website/docs/migration-and-compatibility/suspense-compatibility.md deleted file mode 100644 index 75930e4f02b52..0000000000000 --- a/website/docs/migration-and-compatibility/suspense-compatibility.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -id: suspense-compatibility -title: Suspense Compatibility -slug: /migration-and-compatibility/suspense-compatibility/ -description: Relay guide to suspense compatibility -keywords: -- suspense -- container ---- - -import DocsRating from '@site/src/core/DocsRating'; - -## What about Suspense? - -Relay Hooks uses React Suspense for [specifying loading states](../../guided-tour/rendering/loading-states/), so you might be wondering: Why is that the case if Suspense for Data Fetching is still not supported? Does this mean that Suspense for Data Fetching is officially supported now in React 17? - -## Is Suspense for Data Fetching ready yet? - -The short answer is: **NO**. - -**Support, general guidance, and requirements for usage of Suspense for Data Fetching are still not ready**, and the React team is still defining what this guidance will be for upcoming React releases. - -With that said, even though there are still things to figure out before Suspense for Data Fetching can be broadly implemented and adopted, we released Relay Hooks on React 17 for a few reasons: - -* Relay was a very early adopter of Suspense, and collaborated with React on the research of Suspense for Data Fetching. It was one of the first testing grounds for using Suspense in production, and helped inform some of its design decisions. As such, there are still parts of our Suspense *implementation* that reflect those early learnings (which aren't yet fully documented) and which aren't quite where we want them to be. Although we know there are still likely changes to be made in the implementation, and that there will be some limitations when Suspense is used in React 17, we know Relay Hooks are on the right trajectory for upcoming releases of React, and those changes can be streamlined and allow us to release Relay Hooks a bit earlier. -* The Relay Hooks APIs represent the APIs we want to deliver long-term for Relay and which we believe are an improvement over our previous APIs. Even though their underlying implementation is still changing and will likely change more as the Suspense for Data Fetching guidance is documented and finalized by the React team, the Relay Hooks APIs themselves are stable. They have been widely adopted internally at Facebook, and have been in use in production for over a year, so we are confident that they work. We want to allow the community to start adopting them, and be able to get external feedback from the community as well. - - -## What does it mean for me if I start using Relay Hooks in React 17? - -What this means for users adopting Relay Hooks is: - -* There will be some limitations when using Suspense in React 17, which we've documented in [our docs](../../guided-tour/refetching/refetching-queries-with-different-data/#if-you-need-to-avoid-suspense). Specifically, the current release includes a subset of features that work with both synchronous rendering and concurrent rendering. In order to fully support Suspense for Data Fetching, we also need features such as concurrently rendering suspended trees, and transitioning to new trees when data is refetched. The APIs we've currently released will allow us to support concurrent rendering with the same APIs in future versions of React. -* When a future version of React is released that fully supports concurrent rendering and Suspense for Data Fetching, Relay will also make a new major release alongside the React release. That release will likely include breaking changes that we will document for the upgrade. - - diff --git a/website/docs/migration-and-compatibility/upgrading-to-relay-hooks.md b/website/docs/migration-and-compatibility/upgrading-to-relay-hooks.md deleted file mode 100644 index f9619a27907bb..0000000000000 --- a/website/docs/migration-and-compatibility/upgrading-to-relay-hooks.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -id: upgrading-to-relay-hooks -title: Upgrading to Relay Hooks -slug: /migration-and-compatibility/ -description: Relay guide to upgrading to Relay hooks -keywords: -- upgrade -- hooks ---- - -[Relay Hooks](/blog/2021/03/09/introducing-relay-hooks) is a set of new Hooks-based APIs for using Relay with React that improves upon the existing container-based APIs. - -In this we will cover how to start using Relay Hooks, what you need to know about compatibility, and how to migrate existing container-based code to Hooks if you choose to do so. However, note that migrating existing code to Relay Hooks is ***not*** required, and **container-based code will continue to work**. - -## Accessing Relay Hooks - -Make sure the latest versions of React and Relay are installed, and that you’ve followed additional setup in our [Installation & Setup](../getting-started/installation-and-setup/) guide: - -``` -yarn add react react-dom react-relay -``` - -Then, you can import Relay Hooks from the **`react-relay`** module, or if you only want to include Relay Hooks in your bundle, you can import them from **`react-relay/hooks`**: - -```js -import {graphql, useFragment} from 'react-relay'; // or 'react-relay/hooks' - -// ... -``` - -## Next Steps - -Check out the following guides in this section: -* [Suspense Compatibility](./suspense-compatibility/) -* [Relay Hooks and Legacy Container APIs](./relay-hooks-and-legacy-container-apis/) - - -For more documentation on the APIs themselves, check out our [API Reference](../api-reference/relay-environment-provider) or our [Guided Tour](../guided-tour/). diff --git a/website/docs/tutorial/connections-pagination.md b/website/docs/tutorial/connections-pagination.md index 487a0bd869617..3f7959f17864b 100644 --- a/website/docs/tutorial/connections-pagination.md +++ b/website/docs/tutorial/connections-pagination.md @@ -150,7 +150,7 @@ function StoryCommentsSection({story}) { Here we see that `StoryCommentsSection` is selecting the first three comments for each story using the Connection schema convention: the `comments` field accepts the page size as an argument, and for each comment there is an `edge` and within that a `node` containing the actual comment data — we’re spreading in `CommentFragment` here to retrieve the data needed to show an individual comment with the `Comment` component. It also uses the `pageInfo` field of the connection to decide whether to show a “Load More” button. -Our task then is to make the “Load More” button actually load an additional page of comments. Relay handles the gritty details for us, but we do have to supply a few steps to set it up. +Our task then is to make the “Load More” button actually load an additional page of comments. Relay handles the nitty-gritty details for us, but we do have to supply a few steps to set it up. ### Augmenting the Fragment @@ -243,21 +243,18 @@ const onLoadMore = () => loadNext(3); Run `npm run relay`. Now the Load More button should cause another three comments to be loaded. -### Improving the Loading Experience with useTransition +### Improving the Loading Experience As it stands, there’s no user feedback when you click the “Load More” button until the new comments have finished loading and then appear. Every user action should result in immediate feedback, so let’s show a spinner while the new data is loading — but without hiding the existing UI. -To do that, we need to wrap our call to `loadNext` inside a React transition. Here’s the change’s we need to make: +To do that, we need can use the `isLoadingNext` boolean value returned from `usePaginationFragment`. Here are the changes we need to make: ``` +import SmallSpinner from "./SmallSpinner"; + function StoryCommentsSection({story}) { // change-line - const [isPending, startTransition] = useTransition(); - const {data, loadNext} = usePaginationFragment(StoryCommentsSectionFragment, story); - // change - const onLoadMore = () => startTransition(() => { - loadNext(3); - }); + const {data, loadNext, isLoadingNext} = usePaginationFragment(StoryCommentsSectionFragment, story); // end-change return ( <> @@ -268,18 +265,16 @@ function StoryCommentsSection({story}) { )} // change-line - {isPending && } + {isLoadingNext && } ); } ``` -Every user action with results that aren’t immediate should be wrapped in a React transition. This allows React to prioritize different updates: for example, if when the data becomes available and React is rendering the new comments, the user clicks on another tab to navigate to a different page, React can interrupt rendering the comments in order to render the new page that the user wanted. - * * * ## Infinite Scrolling Newsfeed Stories @@ -339,8 +334,11 @@ function Newsfeed() { const storyEdges = data.viewer.newsfeedStories.edges; return ( <> + // change-line {storyEdges.map(storyEdge => + // change-line + // change-line )} ); @@ -356,22 +354,36 @@ To get this to work, we just need to separate out the contents `NewsfeedQuery` i ``` const NewsfeedQuery = graphql` query NewsfeedQuery { + // change-line ...NewsfeedContentsFragment } `; +// change-line const NewsfeedContentsFragment = graphql` + // change-line fragment NewsfeedContentsFragment on Query { + // change-line viewer { + // change-line newsfeedStories { + // change-line edges { + // change-line node { + // change-line id + // change-line ...StoryFragment + // change-line } + // change-line } + // change-line } + // change-line } + // change-line } `; ``` @@ -405,14 +417,21 @@ You should end up with something like this: ``` const NewsfeedContentsFragment = graphql` fragment NewsfeedContentsFragment on Query + // change-line @argumentDefinitions ( + // change-line cursor: { type: "String" } + // change-line count: { type: "Int", defaultValue: 3 } + // change-line ) + // change-line @refetchable(queryName: "NewsfeedContentsRefetchQuery") { viewer { + // change-line newsfeedStories(after: $cursor, first: $count) + // change-line @connection(key: "NewsfeedContentsFragment_newsfeedStories") { edges { @@ -432,13 +451,22 @@ const NewsfeedContentsFragment = graphql` Now we need to modify the `Newsfeed` component to call `usePaginationFragment:` ``` +import type { NewsfeedContentsRefetchQuery as NewsfeedContentsRefetchQueryType } from "./__generated__/NewsfeedContentsRefetchQuery.graphql"; +import { NewsfeedContentsFragment$key } from "./__generated__/NewsfeedContentsFragment.graphql"; + function Newsfeed() { const queryData = useLazyLoadQuery( NewsfeedQuery, {}, ); // change-line - const {data, loadNext} = usePaginationFragment(NewsfeedContentsFragment, queryData); + const {data, loadNext} = usePaginationFragment< + // change-line + NewsfeedContentsRefetchQueryType, + // change-line + NewsfeedContentsFragment$key + // change-line + >(NewsfeedContentsFragment, queryData); const storyEdges = data.viewer.newsfeedStories.edges; return (
    @@ -469,7 +497,10 @@ function Newsfeed() { hasNext, // change-line isLoadingNext, - } = usePaginationFragment(NewsfeedContentsFragment, queryData); + } = usePaginationFragment< + NewsfeedContentsRefetchQueryType, + NewsfeedContentsFragment$key + >(NewsfeedContentsFragment, queryData); // change function onEndReached() { loadNext(1); diff --git a/website/docs/tutorial/fragments-1.md b/website/docs/tutorial/fragments-1.md index 1a8e558ae20d8..9e7ca87f9fa1f 100644 --- a/website/docs/tutorial/fragments-1.md +++ b/website/docs/tutorial/fragments-1.md @@ -187,7 +187,7 @@ As we'll see in later examples, you can spread multiple fragments into the same To complete the fragmentization, we also need to change the type definition for `Props` so that TypeScript knows this component expects to receive a fragment key instead of the raw data. -Recall that when you spread a fragment into a query (or another fragment), the part of the query result corresponding to where you spread the fragment becomes a *fragment key* for that fragment. This is the object that you pass to a component in its props in order to give it a specific place in the graph to read the fragment from. +Recall that when you spread a fragment into a query (or another fragment), the part of the query result corresponding to where the fragment is spread becomes a *fragment key* for that fragment. This fragment key is the object that you pass to a component to tell it where in the graph to read the fragment from. To make this type-safe, Relay generates a type that represents the fragment key for that specific fragment — this way, if you try to use a component without spreading its fragment into your query, you won’t be able to provide a fragment key that satisfies the type system. Here are the changes we need to make: @@ -426,7 +426,7 @@ Besides these, GraphQL servers can specify additional directives as part of thei -### Step 2 +*** Now the different fragments using `Image` can pass in the appropriate size for each image: diff --git a/website/docs/tutorial/graphql.md b/website/docs/tutorial/graphql.md index 6c737b540c229..305606640fc08 100644 --- a/website/docs/tutorial/graphql.md +++ b/website/docs/tutorial/graphql.md @@ -1,6 +1,6 @@ # GraphQL and Relay -This section is an overview to situate Relay in relation to GraphQL, React, and the other parts of the stack. Don’t worry about understanding every detail, just try to get the gist and the proceed to the next section to start working with code. Much more specifics will be explained as we go through working examples throughout the tutorial. +This section is an overview to situate Relay in relation to GraphQL, React, and the other parts of the stack. Don’t worry about understanding every detail, just try to get the gist and then proceed to the next section to start working with code. Much more specifics will be explained as we go through working examples throughout the tutorial. import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; @@ -165,7 +165,7 @@ Besides the compiler, Relay has runtime code that manages the fetching and proce The advantage of having a centralized Store is that it lets you keep your data consistent when it’s updated. For instance, if your UI has a way for somebody to edit their name, then you can make that update in a single place and every component that displays that person’s name will see the new information, even if they’re on different screens and therefore used different queries to initially retrieve the data. This is because Relay *normalizes* the data as it comes in, meaning that it merges all the data it sees for a single graph node into one place, so it doesn’t have multiple copies of the same node. -Indeed, Relay doesn’t just query data, it provides for the entire lifecycle of querying and updating, including support for optimistic updates and rollbacks. You can paginate, refresh data — all of the basic operations you’ll need to create a UI. Whenever data in the Store is updated, Relay efficiently re-renders just those components that are displaying that particular data. +Indeed, Relay doesn’t just query data, it provides functions to manage the entire lifecycle of querying and updating, including support for optimistic updates and rollbacks. You can paginate, refresh data — all of the basic operations you’ll need to create a UI. Whenever data in the Store is updated, Relay efficiently re-renders just those components that are displaying that particular data. ## Summary diff --git a/website/docs/tutorial/interfaces-polymorphism.md b/website/docs/tutorial/interfaces-polymorphism.md index 5cdf44b7b3e73..86a98b5db00d8 100644 --- a/website/docs/tutorial/interfaces-polymorphism.md +++ b/website/docs/tutorial/interfaces-polymorphism.md @@ -33,7 +33,7 @@ interface Actor { } ``` -Not coincidentally this is exactly the information that we’re displaying here. There are two types in the schema that *implement* Actor, meaning that they include all the fields defined in Actor and declare as such: +Not coincidentally this is exactly the information that we’re displaying here. There are two types in the schema that *implement* Actor, meaning that they include all the fields defined in Actor. They are declared as such: ``` type Person implements Actor { diff --git a/website/docs/tutorial/intro.md b/website/docs/tutorial/intro.md index 633956e930565..0d6f46975d71a 100644 --- a/website/docs/tutorial/intro.md +++ b/website/docs/tutorial/intro.md @@ -2,10 +2,10 @@ This tutorial will get you started with the most important and frequently-used features of Relay. To do that, we’ll build a simple app that displays a newsfeed. We will cover: -* How to fetch data using Queries. -* How to make components self-contained by breaking Queries into Fragments. -* How to paginate through data with Connections. -* How to update data on the server with Mutations and Updaters. +* How to fetch data using [Queries](./queries-1.md). +* How to make components self-contained by breaking [Queries](./queries-1.md) into [Fragments](./fragments-1.md). +* How to paginate through data with [Connections](./connections-pagination.md). +* How to update data on the server with [Mutations and Updaters](./mutations-updates.md). This tutorial assumes a fair familiarity with React. If you’re still new to React, we suggest going through the [React tutorial](https://reactjs.org/tutorial/) and working with React until you’re comfortable with creating components, passing props, and using the basic hooks such as `useState`. The tutorial is based on the Web, but Relay also works great with React Native. @@ -15,7 +15,7 @@ This tutorial is built with TypeScript, so [very basic knowledge of TypeScript]( **IMPORTANT**: The tutorial is meant to be gone through in order, as the exercises build on each other. You’ll be making incremental changes to an example app, so later section won’t make sense if you haven’t done the earlier sections. ::: -* * * +## Setup To get started, run the following commands: @@ -44,11 +44,13 @@ Now that these processes are running, you should be able to open [http://localho We start from a webpage that shows a single Newsfeed story rendered with React, but the data for that story is just placeholder data hard-coded into the React components. In the rest of this tutorial, we’ll make the app functional by having it fetch data from the server, paginate over multiple stories, and update the data by commenting and liking. +## Folder Structure + The files that make up the example app are laid out in this way: * `src/components` — the front-end app components that we’ll be modifying and working with. Some of the important components are: * `App.tsx` — the top-level component - * `Newsfeed.tsx` — a component that will run a query to fetch newsfeed stories and display a scrolling list of stories. At the beginning of the tutorial, this component uses hard-coded placeholder data — we’ll modify it to fetch data via GraphQL and Relay. + * `Newsfeed.tsx` — a component that will run a query to fetch newsfeed stories and display a scrolling list of stories * `Story.tsx` — a component that shows a single newsfeed story. * `server` — a very basic GraphQL server that serves up example data * `server/schema.graphql` — the GraphQL schema: it specifies what information can be queried from the server via GraphQL. diff --git a/website/docs/tutorial/mutations-updates.md b/website/docs/tutorial/mutations-updates.md index 240956ced8523..296d2cf733806 100644 --- a/website/docs/tutorial/mutations-updates.md +++ b/website/docs/tutorial/mutations-updates.md @@ -359,7 +359,7 @@ function StoryLikeButton({story}) { const { // color1 updatableData - } = store.readUpdatableFragment( + } = store.readUpdatableFragment( // color2 fragment, // color3 @@ -384,7 +384,11 @@ function StoryLikeButton({story}) { doesViewerLike } `; - const {updatableData} = store.readUpdatableFragment(fragment, story); + const { updatableData } = + store.readUpdatableFragment( + fragment, + story + ); // change const alreadyLikes = updatableData.doesViewerLike; updatableData.doesViewerLike = !alreadyLikes; diff --git a/website/docs/tutorial/organizing-mutations-queries-and-subscriptions.md b/website/docs/tutorial/organizing-mutations-queries-and-subscriptions.md index 17bdb50cc8be3..b45ec56156432 100644 --- a/website/docs/tutorial/organizing-mutations-queries-and-subscriptions.md +++ b/website/docs/tutorial/organizing-mutations-queries-and-subscriptions.md @@ -1,24 +1,24 @@ -# Organizing Mutations, Queries, and Subscriptions +# Organizing GraphQL Operations -Relay Operations (Mutations, Queries, and Subscriptions) have strict naming requirements. The operation name must begin with the module name, and end with the GraphQL operation type. The name also must be globally unique. +In Relay, GraphQL operations (Mutations, Queries, and Subscriptions) have strict naming requirements. The operation name must begin with the module name, and end with the GraphQL operation type. The name also must be globally unique. -> Side note: This naming scheme originates from trying to enforce the uniqueness constraint. At Meta, Haste (a dependency management system for static resources) enforces that all module names are unique to derive sensible globally unique Relay names. Coupling the module name and Relay name also makes it easier to locate a fragment/query/mutation if you know that name. This makes sense within Meta, and may be less sensible in an OSS setting. +:::note +The "module/operation type" naming scheme originates from trying to enforce the uniqueness constraint. At Meta, Haste (a dependency management system for static resources) enforces that all module names are unique so using the module name in the operation name encourages globally unique operation names. Coupling the module name and operation name also makes it easier to locate a fragment/query/mutation if you know that name. +::: For example: -1. A Mutation in the file `MyComponent.js` must be named with the scheme `MyComponent[MyDescriptiveNameHere]Mutation`. +1. A Mutation in the file `MyComponent.js` must be named with the scheme `MyComponent*Mutation`. 2. A Query in the file `MyComponent.react.js` must be named with the scheme `MyComponent*Query`. A NewsFeed component may have mutations/queries that shouldn't logically start with `NewsFeed`, but Relay requires this _if they are defined in that file_. -### Recommended Structure For Mutations and Subscriptions +### Recommended Organization For Mutations and Subscriptions Put Mutations in their own hook module so the name is closer to _what the mutation does_ rather than _which component invokes it_. If the module name is correctly descriptive, it is fine to declare it in the same file. If you are adding a Mutation for `Post`, like adding a comment to a post, you may create a new file titled `useAddPostComment.js`. Your mutation (in this file) will then be named `useAddPostCommentMutation`, which is a perfectly descriptive name. -You may consider putting all of these hooks in a dedicated `hooks` directory. - -### Recommended Structure for Queries +### Recommended Organization for Queries and Fragments Root components should have a single query that is tightly coupled to a component, since it describes that component's data dependencies. Queries and fragments should co-locate with their data-use code. diff --git a/website/docs/tutorial/queries-1.md b/website/docs/tutorial/queries-1.md index 7038ea0592cff..94c9033cf7c32 100644 --- a/website/docs/tutorial/queries-1.md +++ b/website/docs/tutorial/queries-1.md @@ -80,10 +80,10 @@ This illustrates the part of the graph that this query is asking for: ![Parts of the GraphQL query](/img/docs/tutorial/query-breakdown.png) Now that we’ve defined the query, we need to do two things. -1. Run the Relay compiler so that it knows about the new Graphql query. [npm run relay.] +1. Run the Relay compiler so that it knows about the new Graphql query. (`npm run relay`) 2. Modify our React component to fetch it and to use the data returned by the server. -If you open package.json you will find the script `relay` is hooked up to run the Relay compiler. This is what `npm run relay` does. Once the compiler successfully updates/generated the new compiled query you will be able to find it in the __generated__ folder under src/components/ as NewsfeedQuery.graphql.ts. This project comes with precomputed fragments, so unless you do this step, you will not get the desired results. +If you open `package.json` you will find the script `relay` is hooked up to run the Relay compiler. This is what `npm run relay` does. Once the compiler successfully updates/generated the new compiled query you will be able to find it in the `__generated__` folder under `src/components/` as `NewsfeedQuery.graphql.ts`. Next, turn back to the `Newsfeed` component and start by deleting the placeholder data. Then, replace it with this: diff --git a/website/docs/tutorial/queries-2.md b/website/docs/tutorial/queries-2.md index 9680e82f42960..1a614b6c13464 100644 --- a/website/docs/tutorial/queries-2.md +++ b/website/docs/tutorial/queries-2.md @@ -2,8 +2,8 @@ We’ve seen how fragments let us specify data requirements in each component, yet at runtime perform only a single query for an entire screen. Here we’ll look at a situation where we *want* a second query on the same screen. This will also let us explore some more features of GraphQL queries. -* We’ll build a **hovercard** that shows more details about the poster of a story when you hover over their name. -* The hovercard will use a second query to fetch **additional information** that’s only needed if the user hovers. +* We’ll build a hovercard that shows more details about the poster of a story when you hover over their name. +* The hovercard will use a second query to fetch additional information that’s only needed if the user hovers. * We’ll use **query variables** to tell the server which person we’d like more details about. * We’ll see how to improve performance with **preloaded queries**. @@ -108,9 +108,9 @@ const PosterDetailsHovercardContentsQuery = graphql` `; ``` -The node field is a top-level field defined in our schema that lets us fetch any graph node given its unique ID. It takes the ID as an argument, which is currently hard-coded. In this exercise, we’ll be replacing this hard-coded ID with a variable supplied by our UI state. +

    The node field is a top-level field defined in our schema that lets us fetch any graph node given its unique ID. It takes the ID as an argument, which is currently hard-coded. In this exercise, we’ll be replacing this hard-coded ID with a variable supplied by our UI state.

    -The funny-looking `... on Actor` is a *type refinement*. We’ll look at these in more detail in the next section and can ignore it for now. In brief, since we could supply any ID at all to the `node` field, there’s no way to know statically what *type* of node we’d be selecting. The type refinement specifies what type we expect, allowing us to use fields from the `Actor` type +The funny-looking `... on Actor` is a *type refinement*. We’ll look at these in more detail in the next section and can ignore it for now. In brief, since we could supply any ID at all to the `node` field, there’s no way to know statically what *type* of node we’d be selecting. The type refinement specifies what type we expect, allowing us to use fields from the `Actor` type. Within that, we simply spread a fragment that contains the fields we want to show — about which more later. For now, here are the steps to take to replace this hard-coded ID with the ID of the poster we’re hovering over: @@ -298,15 +298,15 @@ export default function PosterDetailsHovercardContents({ } ``` -### Step 2: export the query for access from the parent component - -We’ll be modifying the parent component, `PosterByline`, to have it initiate the `PosterDetailsHovercardContentsQuery` query. It needs a reference to that query, so we need to export it: - +:::note +We’ll be modifying the parent component, `PosterByline`, to have it initiate the `PosterDetailsHovercardContentsQuery` query. Since it needs a reference the query, the query needs to be exported: ``` export const PosterDetailsHovercardContentsQuery = graphql`... ``` +::: + -### Step 3: Call useQueryLoader in the parent component +### Step 2 — Call useQueryLoader in the parent component Now that `PosterDetailsHovercardContents` expects a query ref, we need to create that query ref and pass it down from the parent component, which is `PosterByline`. We create the query ref using a hook called `useQueryLoader`. This hook also returns a function that we call in our event handler to trigger the query fetch. @@ -339,7 +339,7 @@ The `useQueryLoader` hook returns two things we need: * The query ref is an opaque piece of information that `usePreloadedQuery` will use to retrieve the result of the query. * `loadHovercardQuery` is a function that will initiate the request. -### Step 4: Fetch the query in the event handler +### Step 3 — Fetch the query in the event handler Finally, we need to call `loadHovercardQuery` in an event handler that happens when the card is shown. Luckily the `Hovercard` component has a `onBeginHover` event that we can use: diff --git a/website/docs/tutorial/refetchable-fragments.md b/website/docs/tutorial/refetchable-fragments.md index d907d620c5674..18d6f46c059ca 100644 --- a/website/docs/tutorial/refetchable-fragments.md +++ b/website/docs/tutorial/refetchable-fragments.md @@ -336,7 +336,9 @@ import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/interna Besides fragments on types that implement `Node`, you can also refetch fragments that are on `Viewer` (since the viewer is assumed to be stable throughout a session) and that are at the top level of a query (since there’s no field above them that could change identity). + Meta only: Ents marked with GraphQLFetchable can also be refetched. + diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index 5b21c208dc9d9..cc9d4f746f024 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -126,12 +126,6 @@ module.exports = { pinned: true, description: 'Used at autoguru.com.au, and affiliates', }, - { - caption: 'Foton', - image: '/img/logos/foton.png', - infoLink: 'https://fotontech.io', - pinned: false, - }, { caption: 'M1 Finance', image: '/img/logos/m1finance.png', @@ -156,12 +150,6 @@ module.exports = { infoLink: 'http://www.habilelabs.io/', pinned: false, }, - { - caption: 'Quanto', - image: '/img/logos/quanto.png', - infoLink: 'https://www.contaquanto.com.br/', - pinned: false, - }, { caption: 'Butterfly Network', image: '/img/logos/butterfly.png', @@ -198,6 +186,18 @@ module.exports = { infoLink: 'https://www.rea-app.fr/', pinned: false, }, + { + caption: 'Steep Wellness', + image: '/img/logos/steep.png', + infoLink: 'https://steepapp.com', + pinned: false, + }, + { + caption: 'GigSmart', + image: '/img/logos/gigsmart.png', + infoLink: 'https://gigsmart.com', + pinned: false, + }, ], }, onBrokenLinks: 'throw', @@ -245,6 +245,7 @@ module.exports = { './src/css/prism.css', './src/css/customTheme.css', './src/css/custom.css', + './src/css/jsonSchema.css', ], }, gtag: { @@ -257,6 +258,7 @@ module.exports = { ], ], plugins: [ + require.resolve('./plugins/webpack-alias'), [ '@docusaurus/plugin-client-redirects', { @@ -280,11 +282,15 @@ module.exports = { ], }, { - to: '/docs/getting-started/step-by-step-guide/', + // Update on next versioned docs release + // to: '/docs/getting-started/quick-start', + to: '/docs/', from: ['/docs/en/quick-start-guide', '/docs/quick-start-guide'], }, { - to: '/docs/getting-started/step-by-step-guide/', + // Update on next versioned docs release + // to: '/docs/getting-started/quick-start', + to: '/docs/', from: [ '/docs/en/experimental/step-by-step', '/docs/experimental/step-by-step', diff --git a/website/plugins/webpack-alias.js b/website/plugins/webpack-alias.js new file mode 100644 index 0000000000000..18ab6711a3b61 --- /dev/null +++ b/website/plugins/webpack-alias.js @@ -0,0 +1,50 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @oncall relay + */ + +'use strict'; + +const fs = require('fs'); +const path = require('path'); + +module.exports = function (context, options) { + return { + name: 'custom-webpack-alias', + configureWebpack() { + // The relative location of the JSON schema file differs between GitHub + // and internal Meta builds, so we need a place to handle that + // conditionality. + + const CANDIDATE_PATHS = [ + // Used in OSS builds + '../../compiler/crates/relay-compiler/relay-compiler-config-schema.json', + // Used in internal Meta builds + '../../../../../../../../../fbcode/relay/oss/crates/relay-compiler/relay-compiler-config-schema.json', + ].map(p => path.resolve(__dirname, p)); + + const jsonSchemaPath = CANDIDATE_PATHS.find(p => fs.existsSync(p)); + + if (!jsonSchemaPath) { + throw new Error( + `Could not find JSON schema file for compiler config. Looked in: ${CANDIDATE_PATHS.join( + ', ', + )}`, + ); + } + + return { + resolve: { + alias: { + '@compilerConfigJsonSchema': jsonSchemaPath, + }, + }, + }; + }, + }; +}; diff --git a/website/sidebars.js b/website/sidebars.js index f39bb6ffe2fb0..afa50651d930f 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -19,6 +19,7 @@ const {fbContent} = require('docusaurus-plugin-internaldocs-fb/internal'); const Guides = { 'Fetching Data': [ + 'guided-tour/list-data/introduction', ...fbContent({ internal: [ { @@ -64,7 +65,6 @@ const Guides = { ], }, 'guides/fb/image-prefetching', - 'guides/fb/comet-route-prefetching', 'guides/fb/web-query-preloading', 'guides/fb/production-graphql-endpoint-in-sandboxes', 'guides/fb/react-flight', @@ -83,7 +83,9 @@ const Guides = { 'guides/semantic-nullability', ], 'Updating Data': [ - 'guided-tour/list-data/updating-connections', + 'guided-tour/updating-data/introduction', + 'guided-tour/updating-data/graphql-mutations', + 'guided-tour/updating-data/updating-connections', 'guided-tour/updating-data/imperatively-modifying-store-data', 'guided-tour/updating-data/imperatively-modifying-linked-fields', 'guided-tour/updating-data/typesafe-updaters-faq', @@ -102,11 +104,6 @@ const Guides = { 'guided-tour/reusing-cached-data/rendering-partially-cached-data', 'guided-tour/reusing-cached-data/filling-in-missing-data', 'guided-tour/managing-data-outside-react/retaining-queries', - // These were already commented out - // 'guided-tour/managing-data-outside-react/prefetching-queries', - // 'guided-tour/managing-data-outside-react/subscribing-to-queries', - // 'guided-tour/managing-data-outside-react/reading-queries', - // 'guided-tour/managing-data-outside-react/reading-fragments', ], 'Client Side Data': [ { @@ -156,11 +153,14 @@ module.exports = { docs: [ 'home', { - Installation: [ - 'getting-started/prerequisites', - 'getting-started/installation-and-setup', - 'editor-support', + 'Get Started': [ + 'getting-started/quick-start', + 'getting-started/babel-plugin', 'getting-started/compiler', + 'getting-started/compiler-config', + 'getting-started/lint-rules', + 'editor-support', + 'getting-started/production', ], Tutorial: [ 'tutorial/intro', @@ -209,6 +209,7 @@ module.exports = { 'api-reference/entrypoint-apis/entrypoint-container', ], 'Relay Runtime': [ + 'api-reference/relay-runtime/relay-environment', 'api-reference/relay-runtime/fetch-query', 'api-reference/relay-runtime/store', 'api-reference/relay-runtime/field-logger', @@ -216,6 +217,7 @@ module.exports = { 'api-reference/relay-runtime/request-subscription', 'api-reference/relay-runtime/observe-fragment', 'api-reference/relay-runtime/wait-for-fragment-data', + 'api-reference/relay-runtime/runtime-config', ], }, { @@ -225,7 +227,6 @@ module.exports = { ], }, 'api-reference/graphql/graphql-directives', - 'api-reference/legacy-apis/legacy-apis', ], 'Testing and Debugging': [ 'guides/testing-relay-components', @@ -235,6 +236,7 @@ module.exports = { 'debugging/fb/debugging-and-troubleshooting', 'debugging/relay-devtools', 'debugging/fb/network-logger', + 'debugging/fb/performance-logger', 'debugging/inconsistent-typename-error', 'debugging/declarative-mutation-directives', 'debugging/fb/debugging-suspense', @@ -252,11 +254,6 @@ module.exports = { }), 'error-reference/unknown-field', ], - 'Migration and Compatibility': [ - 'migration-and-compatibility/upgrading-to-relay-hooks', - 'migration-and-compatibility/suspense-compatibility', - 'migration-and-compatibility/relay-hooks-and-legacy-container-apis', - ], 'Principles and Architecture': [ 'principles-and-architecture/thinking-in-graphql', 'principles-and-architecture/thinking-in-relay', diff --git a/website/src/compiler-config/CompilerConfig.js b/website/src/compiler-config/CompilerConfig.js new file mode 100644 index 0000000000000..e67d5e462ea50 --- /dev/null +++ b/website/src/compiler-config/CompilerConfig.js @@ -0,0 +1,488 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +import React from 'react'; +import {useMemo} from 'react'; + +/** + * Generate documentation for the Relay Compiler Config Schema dynamically from + * the JSON schema, which is itself generated from our Rust structs/enums. + * + * The output is formatted to resemble the TypeScript type definitions since we + * expect Relay users are intuitively familiar with TypeScript. + * + * This component will get executed during build time, so it should be written + * defensively. It's okay for us to only handle a subset of the JSON schema as long + * as we explicitly throw on any unhandled cases. If no config options are added + * which exercise these unhandled cases, the build will fail and we can add support for + * them at that point. + */ + +export default function CompilerConfig({schema, definitions}) { + const indirections = useIndirections(schema); + return ( + + ); +} + +function Schema({schema, indirections, definitions}) { + const defs = definitions ?? schema.$defs; + if (defs == null) { + throw new Error( + 'Expected schema to have $defs or have non-standard definitions explicitly passed in.', + ); + } + return ( +
    + + {defs != null && + Object.entries(defs).map(([name, definition], index) => { + return ( + + ); + })} +
    + ); +} + +function SchemaDefinition({definition, name, indirections}) { + if (indirections.getInlineDef(definition)) { + // If this type is rendered inline, we don't need to render its definition. + return null; + } + switch (definition.type) { + case 'object': + return ( + + ); + case 'array': + throw new Error('Expected array type to be handled as indirection'); + case 'string': + if (definition.enum) { + return ; + } + throw new Error('Expected string type to be handled as indirection'); + default: + if (definition.$ref) { + throw new Error('Expected $ref type to be handled as indirection'); + } + if (definition.oneOf && definition.oneOf.length > 0) { + return ( + + ); + } + if (definition.anyOf && definition.anyOf.length > 0) { + return ( + + ); + } + if (definition.allOf) { + throw new Error('Expected allOf to be handled as indirection'); + } + throw new Error('Unhandled definition type'); + } +} + +function SchemaEnumDefinition({definition, name}) { + return ( + + + type {name} {' ='} + + {definition.enum.map((item, index) => { + return ( +
    + {item.description && ( +
    {' // ' + item.description}
    + )} + {'|'}{' '} + + {'"'} + {item} + {'"'} + +
    + ); + })} +
    + ); +} + +function SchemaOneOfDefinition({definition, name, indirections}) { + return ( + + + type {name} {' ='} + + {definition.oneOf.map((item, index) => { + return ( +
    + {item.description && ( +
    {' // ' + item.description}
    + )} + {'|'} +
    + ); + })} +
    + ); +} +function SchemaAnyOfDefinition({definition, name, indirections}) { + return ( + + + type {name} {' ='} + + {definition.anyOf.map((item, index) => { + return ( +
    + {item.description && ( +
    {' // ' + item.description}
    + )} + {'|'} +
    + ); + })} +
    + ); +} + +function SchemaObjectDefinition({definition, name, indirections}) { + return ( + + + type {name} {' = {'} + +
    + {Object.entries(definition.properties).map( + ([name, property], index) => { + return ( + + ); + }, + )} +
    + {'}'} +
    + ); +} + +function SchemaProperty({property, name, required, indirections}) { + return ( +
    + {property.description && ( +
    {' // ' + property.description}
    + )} +
    + + {name} + {required ? '' : '?'} + + : + +
    +
    + ); +} + +function Default({prop}) { + if (prop.default) { + return ( + + {' = '} + + + ); + } + return null; +} + +function RawJson({json}) { + if (json === null) { + return 'null'; + } + switch (typeof json) { + case 'string': + case 'number': + case 'boolean': + return JSON.stringify(json); + case 'object': + if (Array.isArray(json)) { + return ( + + [ + + {json.map((item, index) => ( + + ))} + + ] + + ); + } + return ( + + {'{'} + {Object.entries(json).map(([key, value], index) => ( +
    + {key}: + {', '} +
    + ))} + {'}'} +
    + ); + default: + throw new Error('Unexpected JSON type'); + } +} + +function TypeRef({prop, indirections}) { + return ( + + + + ); +} + +function T({prop, indirections}) { + if (typeof prop === 'boolean') { + return String(prop); + } + if (prop.$ref) { + const value = indirections.resolveRef(prop.$ref); + const inlineDef = indirections.getInlineDef(value); + if (inlineDef) { + return ; + } + const typeName = prop.$ref.split('/').pop(); + if (typeName != null) { + return {typeName}; + } + throw new Error('Expected $ref to be a valid type name'); + } + if (prop.type === 'array' && prop.items) { + return ( + <> + + {'[]'} + + ); + } + if (Array.isArray(prop.type)) { + return prop.type.join(' | '); + } + + if (prop.enum) { + return ( + + {prop.enum.map((item, i) => ( + + {'"'} + {item} + {'"'} + + ))} + + ); + } + + if (prop.allOf) { + if (prop.allOf.length !== 1) { + throw new Error('allOf should only have one item'); + } + return ; + } + if (prop.anyOf) { + return ( + + {prop.anyOf.map((item, i) => ( + + ))} + + ); + } + + if (Array.isArray(prop)) { + return ( + + {prop.map((item, i) => ( + + {item} + + ))} + + ); + } + + switch (prop.type) { + case 'string': + case 'number': + case 'boolean': + case 'null': + if (prop.const != null) { + return JSON.stringify(prop.const); + } + return prop.type; + case 'integer': + // TODO: Clarify if this needs to be a unint8 + return 'number'; + case 'object': + if (prop.additionalProperties) { + return ( + + {'{ [key: string]: '} + + {' }'} + + ); + } + return ; + default: + return prop.type ?? 'any'; + } +} + +function InlineObject({prop, indirections}) { + return ( + + {'{'} + {Object.entries(prop.properties).map(([name, property], index) => { + return ( +
    + {property.description && ( +
    {' // ' + property.description}
    + )} + {name} + : + +
    + ); + })} + {'}'} +
    + ); +} + +function Definition({name, description, children}) { + return ( +
    +

    + {splitPascalCase(name)} +

    + {description &&

    {description}

    } +
    {children}
    +
    + ); +} + +function Join({children, separator}) { + const filtered = children.filter(Boolean); + return ( + <> + {filtered.map((child, idx) => ( + + {child} + {idx < filtered.length - 1 && separator} + + ))} + + ); +} + +function splitPascalCase(str) { + return str.replace(/([A-Z]+)/g, ' $1').trim(); +} + +// Some types have a name but are really just simple wrappers around other types +// or shapes. First we collect up these types and build a map redirecting them to +// their real type. +function useIndirections(data) { + return useMemo(() => new IndirectionResolver(data), [data]); +} + +class IndirectionResolver { + constructor(schema) { + this._schema = schema; + } + + /** + * Some types are trivial wrappers which are not worth showing as their own + * types. Here we encode a heuristic for types which we think are not worth + * showing as their own types, and instead we can use either the type they + * wrap, or the structure of the type directly. + */ + getInlineDef(value) { + if (value.type === 'string' || value.type === 'array') { + return value; + } + if (Array.isArray(value.type)) { + return value.type; + } + if (value.allOf) { + if (value.allOf.length !== 1) { + throw new Error( + 'Expected allOf to only be used as a wrapper type wrapping a single type.', + ); + } + return value.allOf[0]; + } + if (value.$ref) { + return this.getInlineDef(this.resolveRef(value.$ref)); + } + return null; + } + + resolveRef(ref) { + const path = ref.split('/'); + let node = null; + for (const segment of path) { + if (segment === '#') { + node = this._schema; + } else { + node = node[segment]; + } + if (!node) { + throw new Error(`Reference ${ref} not found in schema.`); + } + } + return node; + } +} diff --git a/website/src/css/custom.css b/website/src/css/custom.css index 255e2dcdaad9c..85ffd260d1fc4 100644 --- a/website/src/css/custom.css +++ b/website/src/css/custom.css @@ -11,6 +11,12 @@ code { border: none; } +:root { + --ifm-blockquote-padding-vertical: 12px; + --ifm-blockquote-border-left-width: 0px; + --ifm-code-font-size: 16px; +} + html[data-theme="light"] { --main-bg-color: var(--ifm-color-primary); --light-bg-color: #f9f6ef; @@ -21,10 +27,9 @@ html[data-theme="light"] { --ifm-menu-color-background-active: transparent; --ifm-menu-color-background-hover: transparent; --pre-background-color: var(--light-bg-color); - --blockquote-background-color: #e9e3e3; --ifm-blockquote-color: #3a434d; + --blockquote-background-color: #f0f0f0; --ifm-navbar-link-color: white; - --ifm-code-font-size: 16px; --ifm-alert-color: black; } @@ -38,9 +43,8 @@ html[data-theme="dark"] { --dark-bg-color: black; --pre-background-color: #1A1A1A; --pre-border-color: #222; - --blockquote-background-color: #262626; --ifm-blockquote-color: var(--ifm-font-color-base); - --ifm-code-font-size: 16px; + --blockquote-background-color: #434343; } /* Navbar */ @@ -145,6 +149,11 @@ html[data-theme="light"] .homePage .container { background-color: var(--light-bg-color); } +.footer__logo { + width: 80px; + height: auto; +} + .homePage .footer { background-color: var(--light-bg-color); } diff --git a/website/src/css/jsonSchema.css b/website/src/css/jsonSchema.css new file mode 100644 index 0000000000000..0d5182f6b66a3 --- /dev/null +++ b/website/src/css/jsonSchema.css @@ -0,0 +1,26 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + */ + +.json-schema { font-family: monospace; font-size: 13px; max-width: 750px; } +.json-schema h1, .json-schema h2 { font-size: 1.5em; border-bottom: 2px solid #ccc; margin-top: 2em; } +.json-schema details { margin-left: 1em; } +.json-schema summary { font-weight: bold; cursor: pointer; font-size: 13px;} +.json-schema .property { margin-left: 2em; } +.json-schema .inline-property { margin-left: 2em; } +.json-schema .type { color: #0074d9; } +.json-schema code { background: #eee; padding: 0 0.2em; border-radius: 3px; } +.json-schema .description { color: var(--ifm-color-emphasis-600); font-size: 0.9em; margin-top: 1em; } +.json-schema .default { color: var(--ifm-color-emphasis-600); } +/** + * Needed to ensure anchor links don't get hidden under the top banner. + * Copied from .anchorWithStickyNavbar_node_modules-\@docusaurus-theme-classic-lib-theme-Heading-styles-module + */ +.json-schema .definition-title { + scroll-margin-top: calc(var(--ifm-navbar-height) + 0.5rem); +} diff --git a/website/src/pages/index.js b/website/src/pages/index.js index b73ec519e7a95..dea9936b67d91 100755 --- a/website/src/pages/index.js +++ b/website/src/pages/index.js @@ -86,7 +86,6 @@ const HomeSplash = () => { const Index = () => { const {siteConfig} = useDocusaurusContext(); const {withBaseUrl} = useBaseUrlUtils(); - const {isDarkTheme} = useThemeConfig(); const showcase = siteConfig.customFields.users .filter(user => { @@ -94,7 +93,7 @@ const Index = () => { }) .map((user, i) => { return ( - +
    {showcase}
    -

    Are you using this project?

    - - Add your project -
diff --git a/website/static/img/docs/no-unused-fields.png b/website/static/img/docs/no-unused-fields.png new file mode 100644 index 0000000000000..b00f52206dabf Binary files /dev/null and b/website/static/img/docs/no-unused-fields.png differ diff --git a/website/static/img/logos/foton.png b/website/static/img/logos/foton.png deleted file mode 100644 index 2eca0cec32bca..0000000000000 Binary files a/website/static/img/logos/foton.png and /dev/null differ diff --git a/website/static/img/logos/gigsmart.png b/website/static/img/logos/gigsmart.png new file mode 100644 index 0000000000000..88f70ee9dd8d4 Binary files /dev/null and b/website/static/img/logos/gigsmart.png differ diff --git a/website/static/img/logos/quanto.png b/website/static/img/logos/quanto.png deleted file mode 100644 index b58b1b69f5a84..0000000000000 Binary files a/website/static/img/logos/quanto.png and /dev/null differ diff --git a/website/static/img/logos/steep.png b/website/static/img/logos/steep.png new file mode 100644 index 0000000000000..ca4662023366b Binary files /dev/null and b/website/static/img/logos/steep.png differ diff --git a/website/versioned_docs/version-v13.0.0/glossary/glossary.md b/website/versioned_docs/version-v13.0.0/glossary/glossary.md index c0d23ed4ec388..726f76204f0e7 100644 --- a/website/versioned_docs/version-v13.0.0/glossary/glossary.md +++ b/website/versioned_docs/version-v13.0.0/glossary/glossary.md @@ -133,11 +133,15 @@ A directive which declares that a field implements the [connection](#connection) ## Connection + A field implementing the connection spec. See here for more details on the spec, and the section of the guided tour on rendering list data and pagination. + + A field implementing the connection spec. See the section of the guided tour on rendering list data and pagination. + See also [`usePaginationFragment`](../api-reference/use-pagination-fragment). @@ -357,7 +361,9 @@ A lightweight API for specifying a that a React component should be loaded on de This API is safe to use in entrypoint files. + See [the npm module](https://www.npmjs.com/package/jsresource). + ## Lazy Loading @@ -419,11 +425,15 @@ TODO A mutation is a combination of two things: a mutation on the backend, followed by query against updated data. + See the [guide on mutations](../guided-tour/updating-data/graphql-mutations), and [this article](https://www.internalfb.com/intern/wiki/Graphql-for-hack-developers/mutation-root-fields/) on defining mutations in your hack code. + + See the [guide on mutations](../guided-tour/updating-data/graphql-mutations). + ## Mutation Root Query @@ -539,7 +549,9 @@ Required because of current limitations on dynamically loading components in Rea For Relay to process a file with a GraphQL literal, it must be included in a project. A project specifies the folders to which it applies and the schema against which to evaluate GraphQL literals, and includes other information needed by the Relay compiler. + Projects are defined in a single [config](#config) file, found [here](https://www.internalfb.com/intern/diffusion/WWW/browse/master/scripts/relay/compiler-rs/config.www.json) and [here](https://www.internalfb.com/intern/diffusion/FBS/browse/master/xplat/relay/compiler-rs/config.xplat.json). + ## Profiler @@ -735,6 +747,7 @@ TODO A collection of all of the GraphQL types that are known to Relay, for a given [project](#project). + ## Schema Sync The GraphQL [schema](#schema) is derived from annotations on Hack classes in the www repository. diff --git a/website/versioned_docs/version-v13.0.0/guided-tour/refetching/refreshing-fragments.md b/website/versioned_docs/version-v13.0.0/guided-tour/refetching/refreshing-fragments.md index 796428949ec7e..edf2e9b83c25d 100644 --- a/website/versioned_docs/version-v13.0.0/guided-tour/refetching/refreshing-fragments.md +++ b/website/versioned_docs/version-v13.0.0/guided-tour/refetching/refreshing-fragments.md @@ -24,9 +24,11 @@ When referring to **"refreshing a fragment"**, we mean fetching the *exact* same + If we want to keep our data up to date with the latest version from the server, the first thing to consider is if it appropriate to use any real-time features, which can make it easier to automatically keep the data up to date without manually refreshing the data periodically. One example of this is using [GraphQL Subscriptions](https://relay.dev/docs/guided-tour/updating-data/graphql-subscriptions/), which will require additional configuration on your server and [network layer](https://relay.dev/docs/guided-tour/updating-data/graphql-subscriptions/#configuring-the-network-layer). + ## Using `useRefetchableFragment` diff --git a/website/versioned_docs/version-v13.0.0/guided-tour/refetching/refreshing-queries.md b/website/versioned_docs/version-v13.0.0/guided-tour/refetching/refreshing-queries.md index cacb088a1c464..6cc8431a76f00 100644 --- a/website/versioned_docs/version-v13.0.0/guided-tour/refetching/refreshing-queries.md +++ b/website/versioned_docs/version-v13.0.0/guided-tour/refetching/refreshing-queries.md @@ -25,9 +25,11 @@ When referring to **"refreshing a query"**, we mean fetching the *exact* same da + If we want to keep our data up to date with the latest version from the server, the first thing to consider is if it appropriate to use any real-time features, which can make it easier to automatically keep the data up to date without manually refreshing the data periodically. One example of this is using [GraphQL Subscriptions](https://relay.dev/docs/guided-tour/updating-data/graphql-subscriptions), which will require additional configuration on your server and [network layer](https://relay.dev/docs/guided-tour/updating-data/graphql-subscriptions/#configuring-the-network-layer). + ## When using `useQueryLoader` / `loadQuery` diff --git a/website/versioned_docs/version-v13.0.0/guides/required-directive.md b/website/versioned_docs/version-v13.0.0/guides/required-directive.md index b0799f710414c..1486fc02e7de7 100644 --- a/website/versioned_docs/version-v13.0.0/guides/required-directive.md +++ b/website/versioned_docs/version-v13.0.0/guides/required-directive.md @@ -119,7 +119,9 @@ fragment MyFrag on Actor { In this situation Relay will generate a union type like: `{__typename: 'User', name: string} | {__typename: '%ignore this%}`. Now you can check the `__typename` field to narrow your object's type down to one that has a non-nullable `name`. + Example diff showing the adoption of this strategy: D24370183 + ### Why not implement this at the schema/server level? diff --git a/website/versioned_docs/version-v14.0.0/glossary/glossary.md b/website/versioned_docs/version-v14.0.0/glossary/glossary.md index 37859796c6091..e8d066e4db8f6 100644 --- a/website/versioned_docs/version-v14.0.0/glossary/glossary.md +++ b/website/versioned_docs/version-v14.0.0/glossary/glossary.md @@ -144,11 +144,15 @@ A directive which declares that a field implements the [connection](#connection) ## Connection + A field implementing the connection spec. See here for more details on the spec, and the section of the guided tour on rendering list data and pagination. + + A field implementing the connection spec. See the section of the guided tour on rendering list data and pagination. + See also [`usePaginationFragment`](../api-reference/use-pagination-fragment). @@ -186,7 +190,9 @@ See the [Thinking in Relay guide](../principles-and-architecture/thinking-in-rel A directive which can be added to a fragment spread or inline fragment to avoid blocking on that fragment's data. + See the [documentation](https://www.internalfb.com/intern/wiki/Relay/Web/incremental-data-delivery-defer-stream/#defer). + ## Definition @@ -420,7 +426,9 @@ A lightweight API for specifying a that a React component should be loaded on de This API is safe to use in entrypoint files. + See [the npm module](https://www.npmjs.com/package/jsresource). + ## Lazy Loading @@ -482,11 +490,15 @@ TODO A mutation is a combination of two things: a mutation on the backend, followed by query against updated data. + See the [guide on mutations](../guided-tour/updating-data/graphql-mutations), and [this article](https://www.internalfb.com/intern/wiki/Graphql-for-hack-developers/mutation-root-fields/) on defining mutations in your hack code. + + See the [guide on mutations](../guided-tour/updating-data/graphql-mutations). + ## Mutation Root Query @@ -622,7 +634,9 @@ Required because of current limitations on dynamically loading components in Rea For Relay to process a file with a GraphQL literal, it must be included in a project. A project specifies the folders to which it applies and the schema against which to evaluate GraphQL literals, and includes other information needed by the Relay compiler. + Projects are defined in a single [config](#config) file, found [here](https://www.internalfb.com/intern/diffusion/WWW/browse/master/scripts/relay/compiler-rs/config.www.json) and [here](https://www.internalfb.com/intern/diffusion/FBS/browse/master/xplat/relay/compiler-rs/config.xplat.json). + ## Profiler @@ -779,7 +793,7 @@ An object associating a [concrete request](#concrete-request) and [variables](#v ## Resolver -An overloaded term, mostly referring to virtual fields, but also occassionally referring to other things. +An overloaded term, mostly referring to virtual fields, but also occasionally referring to other things. ### When describing a field @@ -842,6 +856,7 @@ TODO A collection of all of the GraphQL types that are known to Relay, for a given [project](#project). + ## Schema Sync The GraphQL [schema](#schema) is derived from annotations on Hack classes in the www repository. diff --git a/website/versioned_docs/version-v14.0.0/guided-tour/refetching/refreshing-fragments.md b/website/versioned_docs/version-v14.0.0/guided-tour/refetching/refreshing-fragments.md index 796428949ec7e..edf2e9b83c25d 100644 --- a/website/versioned_docs/version-v14.0.0/guided-tour/refetching/refreshing-fragments.md +++ b/website/versioned_docs/version-v14.0.0/guided-tour/refetching/refreshing-fragments.md @@ -24,9 +24,11 @@ When referring to **"refreshing a fragment"**, we mean fetching the *exact* same + If we want to keep our data up to date with the latest version from the server, the first thing to consider is if it appropriate to use any real-time features, which can make it easier to automatically keep the data up to date without manually refreshing the data periodically. One example of this is using [GraphQL Subscriptions](https://relay.dev/docs/guided-tour/updating-data/graphql-subscriptions/), which will require additional configuration on your server and [network layer](https://relay.dev/docs/guided-tour/updating-data/graphql-subscriptions/#configuring-the-network-layer). + ## Using `useRefetchableFragment` diff --git a/website/versioned_docs/version-v14.0.0/guided-tour/refetching/refreshing-queries.md b/website/versioned_docs/version-v14.0.0/guided-tour/refetching/refreshing-queries.md index 02fe58709fa1c..2f67260ab8014 100644 --- a/website/versioned_docs/version-v14.0.0/guided-tour/refetching/refreshing-queries.md +++ b/website/versioned_docs/version-v14.0.0/guided-tour/refetching/refreshing-queries.md @@ -25,9 +25,11 @@ When referring to **"refreshing a query"**, we mean fetching the *exact* same da + If we want to keep our data up to date with the latest version from the server, the first thing to consider is if it appropriate to use any real-time features, which can make it easier to automatically keep the data up to date without manually refreshing the data periodically. One example of this is using [GraphQL Subscriptions](https://relay.dev/docs/guided-tour/updating-data/graphql-subscriptions), which will require additional configuration on your server and [network layer](https://relay.dev/docs/guided-tour/updating-data/graphql-subscriptions/#configuring-the-network-layer). + ## When using `useQueryLoader` / `loadQuery` diff --git a/website/versioned_docs/version-v14.0.0/guided-tour/updating-data/graphql-subscriptions.md b/website/versioned_docs/version-v14.0.0/guided-tour/updating-data/graphql-subscriptions.md index a8699767025ce..548a6890c716c 100644 --- a/website/versioned_docs/version-v14.0.0/guided-tour/updating-data/graphql-subscriptions.md +++ b/website/versioned_docs/version-v14.0.0/guided-tour/updating-data/graphql-subscriptions.md @@ -175,7 +175,7 @@ Spreading fragments is generally preferable to refetching the data in response t In addition to writing updated data to the Relay store, we may want to execute a callback whenever a subscription payload is received. We may want to execute a callback if an error is received or if an error is received or if the server ends the subscription. The `GraphQLSubscriptionConfig` can include the following fields to handle such cases: * `onNext`, a callback that is executed when a subscription payload is received. It is passed the subscription response (stopping at fragment spread boundaries). -* `onError`, a callback that is executed when the subscription errors. It is passed the error that occured. +* `onError`, a callback that is executed when the subscription errors. It is passed the error that occurred. * `onCompleted`, a callback that is executed when the server ends the subscription. ## Declarative mutation directives diff --git a/website/versioned_docs/version-v14.0.0/guides/required-directive.md b/website/versioned_docs/version-v14.0.0/guides/required-directive.md index b0799f710414c..f21bd1ddd9e11 100644 --- a/website/versioned_docs/version-v14.0.0/guides/required-directive.md +++ b/website/versioned_docs/version-v14.0.0/guides/required-directive.md @@ -119,7 +119,9 @@ fragment MyFrag on Actor { In this situation Relay will generate a union type like: `{__typename: 'User', name: string} | {__typename: '%ignore this%}`. Now you can check the `__typename` field to narrow your object's type down to one that has a non-nullable `name`. + Example diff showing the adoption of this strategy: D24370183 + ### Why not implement this at the schema/server level? @@ -136,6 +138,7 @@ Basically every value returned by Relay is nullable. This is intentional since w _Extracted from [this comment thread](https://fb.workplace.com/groups/cometeng/permalink/937671436726844/?comment_id=937681186725869)._ _Further discussion in [this comment thread](https://fb.workplace.com/groups/cometeng/permalink/937671436726844/?comment_id=938335873327067)._ + ### Can `(action: NONE)` be the default? diff --git a/website/versioned_docs/version-v14.0.0/tutorial/refetchable-fragments.md b/website/versioned_docs/version-v14.0.0/tutorial/refetchable-fragments.md index 9732f90c881ed..2b4317ef3d3a7 100644 --- a/website/versioned_docs/version-v14.0.0/tutorial/refetchable-fragments.md +++ b/website/versioned_docs/version-v14.0.0/tutorial/refetchable-fragments.md @@ -336,7 +336,9 @@ import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/interna Besides fragments on types that implement `Node`, you can also refetch fragments that are on `Viewer` (since the viewer is assumed to be stable throughout a session) and that are at the top level of a query (since there’s no field above them that could change identity). + Meta only: Ents marked with [`GraphQLFetchable](https://fb.workplace.com/groups/graphql.fyi/permalink/1539541276187011/)` can also be refetched.]] + diff --git a/website/versioned_docs/version-v15.0.0/api-reference/graphql/graphql-directives.md b/website/versioned_docs/version-v15.0.0/api-reference/graphql/graphql-directives.md index 4d5dd12edb272..a685ef12e5aa0 100644 --- a/website/versioned_docs/version-v15.0.0/api-reference/graphql/graphql-directives.md +++ b/website/versioned_docs/version-v15.0.0/api-reference/graphql/graphql-directives.md @@ -20,10 +20,14 @@ import {FbInternalOnly, OssOnly} from 'docusaurus-plugin-internaldocs-fb/interna Relay uses directives to add additional information to GraphQL documents, which are used by the [Relay compiler](../../guides/compiler/) to generate the appropriate runtime artifacts. These directives only appear in your application code and are removed from requests sent to your GraphQL server. + **Note:** The Relay compiler will maintain any directives supported by your server (such as `@include` or `@skip`) so they remain part of the request to the GraphQL server and won't alter generated runtime artifacts. + + **Note:** The Relay compiler will maintain any directives supported by your server (such as `@include` or `@skip`) so they remain part of the request to the GraphQL server and won't alter generated runtime artifacts. Additional directives are documented [here](https://www.internalfb.com/intern/wiki/GraphQL/APIs_and_References/Directives/#graphql-standard). + ## `@arguments` diff --git a/website/versioned_docs/version-v15.0.0/glossary/glossary.md b/website/versioned_docs/version-v15.0.0/glossary/glossary.md index 07c3b7816cd26..78f75e55a192e 100644 --- a/website/versioned_docs/version-v15.0.0/glossary/glossary.md +++ b/website/versioned_docs/version-v15.0.0/glossary/glossary.md @@ -144,11 +144,14 @@ A directive which declares that a field implements the [connection](#connection) ## Connection + A field implementing the connection spec. See here for more details on the spec, and the section of the guided tour on rendering list data and pagination. + -A field implementing the connection spec. See the section of the guided tour on rendering list data and pagination. + +(\S+) field implementing the connection spec. See the section of the guided tour on rendering list data and pagination. See also [`usePaginationFragment`](../api-reference/use-pagination-fragment). @@ -186,7 +189,9 @@ See the [Thinking in Relay guide](../principles-and-architecture/thinking-in-rel A directive which can be added to a fragment spread or inline fragment to avoid blocking on that fragment's data. + See the [documentation](https://www.internalfb.com/intern/wiki/Relay/Web/incremental-data-delivery-defer-stream/#defer). + ## Definition @@ -420,7 +425,8 @@ A lightweight API for specifying a that a React component should be loaded on de This API is safe to use in entrypoint files. -See [the npm module](https://www.npmjs.com/package/jsresource). + +(\S+) [the npm module](https://www.npmjs.com/package/jsresource). ## Lazy Loading @@ -482,11 +488,14 @@ TODO A mutation is a combination of two things: a mutation on the backend, followed by query against updated data. + See the [guide on mutations](../guided-tour/updating-data/graphql-mutations), and [this article](https://www.internalfb.com/intern/wiki/Graphql-for-hack-developers/mutation-root-fields/) on defining mutations in your hack code. + -See the [guide on mutations](../guided-tour/updating-data/graphql-mutations). + +(\S+) the [guide on mutations](../guided-tour/updating-data/graphql-mutations). ## Mutation Root Query @@ -622,7 +631,9 @@ Required because of current limitations on dynamically loading components in Rea For Relay to process a file with a GraphQL literal, it must be included in a project. A project specifies the folders to which it applies and the schema against which to evaluate GraphQL literals, and includes other information needed by the Relay compiler. + Projects are defined in a single [config](#config) file, found [here](https://www.internalfb.com/intern/diffusion/WWW/browse/master/scripts/relay/compiler-rs/config.www.json) and [here](https://www.internalfb.com/intern/diffusion/FBS/browse/master/xplat/relay/compiler-rs/config.xplat.json). + ## Profiler @@ -779,7 +790,7 @@ An object associating a [concrete request](#concrete-request) and [variables](#v ## Resolver -An overloaded term, mostly referring to virtual fields, but also occassionally referring to other things. +An overloaded term, mostly referring to virtual fields, but also occasionally referring to other things. ### When describing a field @@ -842,6 +853,7 @@ TODO A collection of all of the GraphQL types that are known to Relay, for a given [project](#project). + ## Schema Sync The GraphQL [schema](#schema) is derived from annotations on Hack classes in the www repository. diff --git a/website/versioned_docs/version-v15.0.0/guided-tour/refetching/refreshing-fragments.md b/website/versioned_docs/version-v15.0.0/guided-tour/refetching/refreshing-fragments.md index 937af7feb3495..8b200ca599ad4 100644 --- a/website/versioned_docs/version-v15.0.0/guided-tour/refetching/refreshing-fragments.md +++ b/website/versioned_docs/version-v15.0.0/guided-tour/refetching/refreshing-fragments.md @@ -24,9 +24,11 @@ When referring to **"refreshing a fragment"**, we mean fetching the *exact* same + If we want to keep our data up to date with the latest version from the server, the first thing to consider is if it appropriate to use any real-time features, which can make it easier to automatically keep the data up to date without manually refreshing the data periodically. One example of this is using [GraphQL Subscriptions](https://relay.dev/docs/guided-tour/updating-data/graphql-subscriptions/), which will require additional configuration on your server and [network layer](https://relay.dev/docs/guided-tour/updating-data/graphql-subscriptions/#configuring-the-network-layer). + ## Using `useRefetchableFragment` diff --git a/website/versioned_docs/version-v15.0.0/guided-tour/refetching/refreshing-queries.md b/website/versioned_docs/version-v15.0.0/guided-tour/refetching/refreshing-queries.md index 41af90583486d..98bb479c046df 100644 --- a/website/versioned_docs/version-v15.0.0/guided-tour/refetching/refreshing-queries.md +++ b/website/versioned_docs/version-v15.0.0/guided-tour/refetching/refreshing-queries.md @@ -25,9 +25,11 @@ When referring to **"refreshing a query"**, we mean fetching the *exact* same da + If we want to keep our data up to date with the latest version from the server, the first thing to consider is if it appropriate to use any real-time features, which can make it easier to automatically keep the data up to date without manually refreshing the data periodically. One example of this is using [GraphQL Subscriptions](https://relay.dev/docs/guided-tour/updating-data/graphql-subscriptions), which will require additional configuration on your server and [network layer](https://relay.dev/docs/guided-tour/updating-data/graphql-subscriptions/#configuring-the-network-layer). + ## When using `useQueryLoader` / `loadQuery` diff --git a/website/versioned_docs/version-v15.0.0/guided-tour/updating-data/graphql-subscriptions.md b/website/versioned_docs/version-v15.0.0/guided-tour/updating-data/graphql-subscriptions.md index 8e36ffc74c9ff..61541e242c501 100644 --- a/website/versioned_docs/version-v15.0.0/guided-tour/updating-data/graphql-subscriptions.md +++ b/website/versioned_docs/version-v15.0.0/guided-tour/updating-data/graphql-subscriptions.md @@ -175,7 +175,7 @@ Spreading fragments is generally preferable to refetching the data in response t In addition to writing updated data to the Relay store, we may want to execute a callback whenever a subscription payload is received. We may want to execute a callback if an error is received or if an error is received or if the server ends the subscription. The `GraphQLSubscriptionConfig` can include the following fields to handle such cases: * `onNext`, a callback that is executed when a subscription payload is received. It is passed the subscription response (stopping at fragment spread boundaries). -* `onError`, a callback that is executed when the subscription errors. It is passed the error that occured. +* `onError`, a callback that is executed when the subscription errors. It is passed the error that occurred. * `onCompleted`, a callback that is executed when the server ends the subscription. ## Declarative mutation directives diff --git a/website/versioned_docs/version-v15.0.0/guides/relay-resolvers.md b/website/versioned_docs/version-v15.0.0/guides/relay-resolvers.md index 8e44a3f197893..f8a5b7610f15c 100644 --- a/website/versioned_docs/version-v15.0.0/guides/relay-resolvers.md +++ b/website/versioned_docs/version-v15.0.0/guides/relay-resolvers.md @@ -98,9 +98,11 @@ In order for Relay to be able to call a Relay Resolver, it must conform to a set Unlike server resolvers, Relay Resolvers may return any JavaScript value. This includes classes, functions and arrays. However, we generally encourage having Relay Resolvers return scalar values and only returning more complex JavaScript values (like functions) as an escape hatch. + ## Lint Rule In many cases, the contents of the docblock can be derived from the javascript implementation. In those cases, the [`relay-resolvers`](https://www.internalfb.com/eslint/relay-resolvers) ESLint rule rule will offer auto-fixes to derive the docblock from the implementation and ensure that the two remain in sync. The lint rule also enforces a naming convention for resolver function and modules names. + ## How They Work diff --git a/website/versioned_docs/version-v15.0.0/guides/required-directive.md b/website/versioned_docs/version-v15.0.0/guides/required-directive.md index f6e373f343759..a697a6b961e7e 100644 --- a/website/versioned_docs/version-v15.0.0/guides/required-directive.md +++ b/website/versioned_docs/version-v15.0.0/guides/required-directive.md @@ -188,7 +188,9 @@ fragment MyFrag on Actor { In this situation Relay will generate a union type like: `{__typename: 'User', name: string} | {__typename: '%ignore this%}`. Now you can check the `__typename` field to narrow your object's type down to one that has a non-nullable `name`. + Example diff showing the adoption of this strategy: D24370183 + ### Why not implement this at the schema/server level? diff --git a/website/versioned_docs/version-v15.0.0/tutorial/refetchable-fragments.md b/website/versioned_docs/version-v15.0.0/tutorial/refetchable-fragments.md index c94b7744bb33e..88e81a5903f3b 100644 --- a/website/versioned_docs/version-v15.0.0/tutorial/refetchable-fragments.md +++ b/website/versioned_docs/version-v15.0.0/tutorial/refetchable-fragments.md @@ -336,7 +336,9 @@ import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/interna Besides fragments on types that implement `Node`, you can also refetch fragments that are on `Viewer` (since the viewer is assumed to be stable throughout a session) and that are at the top level of a query (since there’s no field above them that could change identity). + Meta only: Ents marked with GraphQLFetchable can also be refetched. + diff --git a/website/versioned_docs/version-v16.0.0/api-reference/graphql/graphql-directives.md b/website/versioned_docs/version-v16.0.0/api-reference/graphql/graphql-directives.md index 47dde7dd53a60..4bd480700306c 100644 --- a/website/versioned_docs/version-v16.0.0/api-reference/graphql/graphql-directives.md +++ b/website/versioned_docs/version-v16.0.0/api-reference/graphql/graphql-directives.md @@ -20,10 +20,14 @@ import {FbInternalOnly, OssOnly} from 'docusaurus-plugin-internaldocs-fb/interna Relay uses directives to add additional information to GraphQL documents, which are used by the [Relay compiler](../../guides/compiler/) to generate the appropriate runtime artifacts. These directives only appear in your application code and are removed from requests sent to your GraphQL server. + **Note:** The Relay compiler will maintain any directives supported by your server (such as `@include` or `@skip`) so they remain part of the request to the GraphQL server and won't alter generated runtime artifacts. + + **Note:** The Relay compiler will maintain any directives supported by your server (such as `@include` or `@skip`) so they remain part of the request to the GraphQL server and won't alter generated runtime artifacts. Additional directives are documented [here](https://www.internalfb.com/intern/wiki/GraphQL/APIs_and_References/Directives/#graphql-standard). + ## `@arguments` diff --git a/website/versioned_docs/version-v16.0.0/glossary/glossary.md b/website/versioned_docs/version-v16.0.0/glossary/glossary.md index 6be2b09a09d5a..2d8ad83a6e5ec 100644 --- a/website/versioned_docs/version-v16.0.0/glossary/glossary.md +++ b/website/versioned_docs/version-v16.0.0/glossary/glossary.md @@ -144,10 +144,13 @@ A directive which declares that a field implements the [connection](#connection) ## Connection + A field implementing the connection spec. See here for more details on the spec, and the section of the guided tour on rendering list data and pagination. + + A field implementing the connection spec. See the section of the guided tour on rendering list data and pagination. @@ -186,7 +189,9 @@ See the [Thinking in Relay guide](../principles-and-architecture/thinking-in-rel A directive which can be added to a fragment spread or inline fragment to avoid blocking on that fragment's data. + See the [documentation](https://www.internalfb.com/intern/wiki/Relay/Web/incremental-data-delivery-defer-stream/#defer). + ## Definition @@ -420,6 +425,7 @@ A lightweight API for specifying a that a React component should be loaded on de This API is safe to use in entrypoint files. + See [the npm module](https://www.npmjs.com/package/jsresource). @@ -482,10 +488,13 @@ TODO A mutation is a combination of two things: a mutation on the backend, followed by query against updated data. + See the [guide on mutations](../guided-tour/updating-data/graphql-mutations), and [this article](https://www.internalfb.com/intern/wiki/Graphql-for-hack-developers/mutation-root-fields/) on defining mutations in your hack code. + + See the [guide on mutations](../guided-tour/updating-data/graphql-mutations). @@ -622,7 +631,9 @@ Required because of current limitations on dynamically loading components in Rea For Relay to process a file with a GraphQL literal, it must be included in a project. A project specifies the folders to which it applies and the schema against which to evaluate GraphQL literals, and includes other information needed by the Relay compiler. + Projects are defined in a single [config](#config) file, found [here](https://www.internalfb.com/intern/diffusion/WWW/browse/master/scripts/relay/compiler-rs/config.www.json) and [here](https://www.internalfb.com/intern/diffusion/FBS/browse/master/xplat/relay/compiler-rs/config.xplat.json). + ## Profiler @@ -778,7 +789,7 @@ An object associating a [concrete request](#concrete-request) and [variables](#v ## Resolver -An overloaded term, mostly referring to virtual fields, but also occassionally referring to other things. +An overloaded term, mostly referring to virtual fields, but also occasionally referring to other things. ### When describing a field @@ -841,6 +852,7 @@ TODO A collection of all of the GraphQL types that are known to Relay, for a given [project](#project). + ## Schema Sync The GraphQL [schema](#schema) is derived from annotations on Hack classes in the www repository. diff --git a/website/versioned_docs/version-v16.0.0/guided-tour/refetching/refreshing-fragments.md b/website/versioned_docs/version-v16.0.0/guided-tour/refetching/refreshing-fragments.md index 937af7feb3495..8b200ca599ad4 100644 --- a/website/versioned_docs/version-v16.0.0/guided-tour/refetching/refreshing-fragments.md +++ b/website/versioned_docs/version-v16.0.0/guided-tour/refetching/refreshing-fragments.md @@ -24,9 +24,11 @@ When referring to **"refreshing a fragment"**, we mean fetching the *exact* same + If we want to keep our data up to date with the latest version from the server, the first thing to consider is if it appropriate to use any real-time features, which can make it easier to automatically keep the data up to date without manually refreshing the data periodically. One example of this is using [GraphQL Subscriptions](https://relay.dev/docs/guided-tour/updating-data/graphql-subscriptions/), which will require additional configuration on your server and [network layer](https://relay.dev/docs/guided-tour/updating-data/graphql-subscriptions/#configuring-the-network-layer). + ## Using `useRefetchableFragment` diff --git a/website/versioned_docs/version-v16.0.0/guided-tour/refetching/refreshing-queries.md b/website/versioned_docs/version-v16.0.0/guided-tour/refetching/refreshing-queries.md index 41af90583486d..98bb479c046df 100644 --- a/website/versioned_docs/version-v16.0.0/guided-tour/refetching/refreshing-queries.md +++ b/website/versioned_docs/version-v16.0.0/guided-tour/refetching/refreshing-queries.md @@ -25,9 +25,11 @@ When referring to **"refreshing a query"**, we mean fetching the *exact* same da + If we want to keep our data up to date with the latest version from the server, the first thing to consider is if it appropriate to use any real-time features, which can make it easier to automatically keep the data up to date without manually refreshing the data periodically. One example of this is using [GraphQL Subscriptions](https://relay.dev/docs/guided-tour/updating-data/graphql-subscriptions), which will require additional configuration on your server and [network layer](https://relay.dev/docs/guided-tour/updating-data/graphql-subscriptions/#configuring-the-network-layer). + ## When using `useQueryLoader` / `loadQuery` diff --git a/website/versioned_docs/version-v16.0.0/guided-tour/updating-data/graphql-subscriptions.md b/website/versioned_docs/version-v16.0.0/guided-tour/updating-data/graphql-subscriptions.md index 8e36ffc74c9ff..61541e242c501 100644 --- a/website/versioned_docs/version-v16.0.0/guided-tour/updating-data/graphql-subscriptions.md +++ b/website/versioned_docs/version-v16.0.0/guided-tour/updating-data/graphql-subscriptions.md @@ -175,7 +175,7 @@ Spreading fragments is generally preferable to refetching the data in response t In addition to writing updated data to the Relay store, we may want to execute a callback whenever a subscription payload is received. We may want to execute a callback if an error is received or if an error is received or if the server ends the subscription. The `GraphQLSubscriptionConfig` can include the following fields to handle such cases: * `onNext`, a callback that is executed when a subscription payload is received. It is passed the subscription response (stopping at fragment spread boundaries). -* `onError`, a callback that is executed when the subscription errors. It is passed the error that occured. +* `onError`, a callback that is executed when the subscription errors. It is passed the error that occurred. * `onCompleted`, a callback that is executed when the server ends the subscription. ## Declarative mutation directives diff --git a/website/versioned_docs/version-v16.0.0/guides/compiler.md b/website/versioned_docs/version-v16.0.0/guides/compiler.md index 2f127a23aa20d..1f57e31b92d22 100644 --- a/website/versioned_docs/version-v16.0.0/guides/compiler.md +++ b/website/versioned_docs/version-v16.0.0/guides/compiler.md @@ -146,6 +146,7 @@ This would produce three generated files, and two `__generated__` directories: ### Importing generated definitions + diff --git a/website/versioned_docs/version-v16.0.0/guides/required-directive.md b/website/versioned_docs/version-v16.0.0/guides/required-directive.md index ce6e02a190277..d1cb1ee91b593 100644 --- a/website/versioned_docs/version-v16.0.0/guides/required-directive.md +++ b/website/versioned_docs/version-v16.0.0/guides/required-directive.md @@ -188,7 +188,9 @@ fragment MyFrag on Actor { In this situation Relay will generate a union type like: `{__typename: 'User', name: string} | {__typename: '%ignore this%}`. Now you can check the `__typename` field to narrow your object's type down to one that has a non-nullable `name`. + Example diff showing the adoption of this strategy: D24370183 + ### Why not implement this at the schema/server level? diff --git a/website/versioned_docs/version-v16.0.0/tutorial/refetchable-fragments.md b/website/versioned_docs/version-v16.0.0/tutorial/refetchable-fragments.md index cf39d0b2776fb..3f4b82a05befc 100644 --- a/website/versioned_docs/version-v16.0.0/tutorial/refetchable-fragments.md +++ b/website/versioned_docs/version-v16.0.0/tutorial/refetchable-fragments.md @@ -336,7 +336,9 @@ import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/interna Besides fragments on types that implement `Node`, you can also refetch fragments that are on `Viewer` (since the viewer is assumed to be stable throughout a session) and that are at the top level of a query (since there’s no field above them that could change identity). + Meta only: Ents marked with GraphQLFetchable can also be refetched. + diff --git a/website/versioned_docs/version-v17.0.0/api-reference/graphql/graphql-directives.md b/website/versioned_docs/version-v17.0.0/api-reference/graphql/graphql-directives.md index 3450d789224ca..bc8a019bb836c 100644 --- a/website/versioned_docs/version-v17.0.0/api-reference/graphql/graphql-directives.md +++ b/website/versioned_docs/version-v17.0.0/api-reference/graphql/graphql-directives.md @@ -20,10 +20,14 @@ import {FbInternalOnly, OssOnly} from 'docusaurus-plugin-internaldocs-fb/interna Relay uses directives to add additional information to GraphQL documents, which are used by the [Relay compiler](../../guides/compiler/) to generate the appropriate runtime artifacts. These directives only appear in your application code and are removed from requests sent to your GraphQL server. + **Note:** The Relay compiler will maintain any directives supported by your server (such as `@include` or `@skip`) so they remain part of the request to the GraphQL server and won't alter generated runtime artifacts. + + **Note:** The Relay compiler will maintain any directives supported by your server (such as `@include` or `@skip`) so they remain part of the request to the GraphQL server and won't alter generated runtime artifacts. Additional directives are documented [here](https://www.internalfb.com/intern/wiki/GraphQL/APIs_and_References/Directives/#graphql-standard). + ## `@arguments` diff --git a/website/versioned_docs/version-v17.0.0/glossary/glossary.md b/website/versioned_docs/version-v17.0.0/glossary/glossary.md index dfed9d5f295de..8355f19a45a80 100644 --- a/website/versioned_docs/version-v17.0.0/glossary/glossary.md +++ b/website/versioned_docs/version-v17.0.0/glossary/glossary.md @@ -144,11 +144,15 @@ A directive which declares that a field implements the [connection](#connection) ## Connection + A field implementing the [connection spec](https://relay.dev/graphql/connections.htm). See here for more details on the spec, and the section of the guided tour on rendering list data and pagination. + + A field implementing the [connection spec](https://relay.dev/graphql/connections.htm). See the section of the guided tour on rendering list data and pagination. + See also [`usePaginationFragment`](../api-reference/use-pagination-fragment). @@ -186,7 +190,9 @@ See the [Thinking in Relay guide](../principles-and-architecture/thinking-in-rel A directive which can be added to a fragment spread or inline fragment to avoid blocking on that fragment's data. For more detail refer to GraphQL's [documentation on the @defer directive](https://github.com/graphql/graphql-wg/blob/main/rfcs/DeferStream.md#defer). + See [Incremental Data Delivery](https://www.internalfb.com/intern/staticdocs/relay/docs/guides/incremental-data-delivery/). + ## Definition @@ -420,6 +426,7 @@ A lightweight API for specifying a that a React component should be loaded on de This API is safe to use in entrypoint files. + See [the npm module](https://www.npmjs.com/package/jsresource). @@ -496,10 +503,13 @@ TODO A mutation is a combination of two things: a mutation on the backend, followed by query against updated data. + See the [guide on mutations](../guided-tour/updating-data/graphql-mutations), and [this article](https://www.internalfb.com/intern/wiki/Graphql-for-hack-developers/mutation-root-fields/) on defining mutations in your hack code. + + See the [guide on mutations](../guided-tour/updating-data/graphql-mutations). @@ -636,7 +646,9 @@ Required because of current limitations on dynamically loading components in Rea For Relay to process a file with a GraphQL literal, it must be included in a project. A project specifies the folders to which it applies and the schema against which to evaluate GraphQL literals, and includes other information needed by the Relay compiler. + Projects are defined in a single [config](#config) file, found [here](https://www.internalfb.com/intern/diffusion/WWW/browse/master/scripts/relay/compiler-rs/config.www.json) and [here](https://www.internalfb.com/intern/diffusion/FBS/browse/master/xplat/relay/compiler-rs/config.xplat.json). + ## Profiler @@ -792,7 +804,7 @@ An object associating a [concrete request](#concrete-request) and [variables](#v ## Resolver -An overloaded term, mostly referring to virtual fields, but also occassionally referring to other things. +An overloaded term, mostly referring to virtual fields, but also occasionally referring to other things. ### When describing a field @@ -855,6 +867,7 @@ TODO A collection of all of the GraphQL types that are known to Relay, for a given [project](#project). + ## Schema Sync The GraphQL [schema](#schema) is derived from annotations on Hack classes in the www repository. diff --git a/website/versioned_docs/version-v17.0.0/guided-tour/refetching/refreshing-fragments.md b/website/versioned_docs/version-v17.0.0/guided-tour/refetching/refreshing-fragments.md index 937af7feb3495..8b200ca599ad4 100644 --- a/website/versioned_docs/version-v17.0.0/guided-tour/refetching/refreshing-fragments.md +++ b/website/versioned_docs/version-v17.0.0/guided-tour/refetching/refreshing-fragments.md @@ -24,9 +24,11 @@ When referring to **"refreshing a fragment"**, we mean fetching the *exact* same + If we want to keep our data up to date with the latest version from the server, the first thing to consider is if it appropriate to use any real-time features, which can make it easier to automatically keep the data up to date without manually refreshing the data periodically. One example of this is using [GraphQL Subscriptions](https://relay.dev/docs/guided-tour/updating-data/graphql-subscriptions/), which will require additional configuration on your server and [network layer](https://relay.dev/docs/guided-tour/updating-data/graphql-subscriptions/#configuring-the-network-layer). + ## Using `useRefetchableFragment` diff --git a/website/versioned_docs/version-v17.0.0/guided-tour/updating-data/graphql-subscriptions.md b/website/versioned_docs/version-v17.0.0/guided-tour/updating-data/graphql-subscriptions.md index 8e36ffc74c9ff..61541e242c501 100644 --- a/website/versioned_docs/version-v17.0.0/guided-tour/updating-data/graphql-subscriptions.md +++ b/website/versioned_docs/version-v17.0.0/guided-tour/updating-data/graphql-subscriptions.md @@ -175,7 +175,7 @@ Spreading fragments is generally preferable to refetching the data in response t In addition to writing updated data to the Relay store, we may want to execute a callback whenever a subscription payload is received. We may want to execute a callback if an error is received or if an error is received or if the server ends the subscription. The `GraphQLSubscriptionConfig` can include the following fields to handle such cases: * `onNext`, a callback that is executed when a subscription payload is received. It is passed the subscription response (stopping at fragment spread boundaries). -* `onError`, a callback that is executed when the subscription errors. It is passed the error that occured. +* `onError`, a callback that is executed when the subscription errors. It is passed the error that occurred. * `onCompleted`, a callback that is executed when the server ends the subscription. ## Declarative mutation directives diff --git a/website/versioned_docs/version-v17.0.0/guides/required-directive.md b/website/versioned_docs/version-v17.0.0/guides/required-directive.md index ce6e02a190277..d1cb1ee91b593 100644 --- a/website/versioned_docs/version-v17.0.0/guides/required-directive.md +++ b/website/versioned_docs/version-v17.0.0/guides/required-directive.md @@ -188,7 +188,9 @@ fragment MyFrag on Actor { In this situation Relay will generate a union type like: `{__typename: 'User', name: string} | {__typename: '%ignore this%}`. Now you can check the `__typename` field to narrow your object's type down to one that has a non-nullable `name`. + Example diff showing the adoption of this strategy: D24370183 + ### Why not implement this at the schema/server level? diff --git a/website/versioned_docs/version-v17.0.0/guides/testing-relay-components.md b/website/versioned_docs/version-v17.0.0/guides/testing-relay-components.md index ea4de6f2cc36e..88f444adfa076 100644 --- a/website/versioned_docs/version-v17.0.0/guides/testing-relay-components.md +++ b/website/versioned_docs/version-v17.0.0/guides/testing-relay-components.md @@ -57,7 +57,9 @@ With the `MockPayloadGenerator` and `@relay_test_operation`, we want to get rid **[React Testing Library](https://testing-library.com/react)** is a set of helpers that let you test React components without relying on their implementation details. This approach makes refactoring a breeze and also nudges you towards best practices for accessibility. Although it doesn't provide a way to "shallowly" render a component without its children, a test runner like Jest lets you do this by [mocking](https://reactjs.org/docs/testing-recipes.html#mocking-modules). + Note: The [`ReactTestRenderer`](https://www.npmjs.com/package/react-test-renderer) library has been deprecated since React v18. `ReactTestRenderer` may still be referenced internally as an alternative to React Testing Library. However, when possible, we recommend using React Testing Library (or `@testing-library/react-native`) to test your React applications with Relay. + ## RelayMockEnvironment API Overview diff --git a/website/versioned_docs/version-v17.0.0/tutorial/refetchable-fragments.md b/website/versioned_docs/version-v17.0.0/tutorial/refetchable-fragments.md index d907d620c5674..18d6f46c059ca 100644 --- a/website/versioned_docs/version-v17.0.0/tutorial/refetchable-fragments.md +++ b/website/versioned_docs/version-v17.0.0/tutorial/refetchable-fragments.md @@ -336,7 +336,9 @@ import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/interna Besides fragments on types that implement `Node`, you can also refetch fragments that are on `Viewer` (since the viewer is assumed to be stable throughout a session) and that are at the top level of a query (since there’s no field above them that could change identity). + Meta only: Ents marked with GraphQLFetchable can also be refetched. + diff --git a/website/versioned_docs/version-v18.0.0/api-reference/graphql/graphql-directives.md b/website/versioned_docs/version-v18.0.0/api-reference/graphql/graphql-directives.md index a6cb86c92d6f5..ad4056acfae70 100644 --- a/website/versioned_docs/version-v18.0.0/api-reference/graphql/graphql-directives.md +++ b/website/versioned_docs/version-v18.0.0/api-reference/graphql/graphql-directives.md @@ -23,10 +23,14 @@ appropriate runtime artifacts. These directives only appear in your application code and are removed from requests sent to your GraphQL server. + **Note:** The Relay compiler will maintain any directives supported by your server (such as `@include` or `@skip`) so they remain part of the request to the GraphQL server and won't alter generated runtime artifacts. + + **Note:** The Relay compiler will maintain any directives supported by your server (such as `@include` or `@skip`) so they remain part of the request to the GraphQL server and won't alter generated runtime artifacts. Additional directives are documented [here](https://www.internalfb.com/intern/wiki/GraphQL/APIs_and_References/Directives/#graphql-standard). + ## `@arguments` diff --git a/website/versioned_docs/version-v18.0.0/api-reference/legacy-apis/legacy-apis.md b/website/versioned_docs/version-v18.0.0/api-reference/legacy-apis/legacy-apis.md deleted file mode 100644 index 0c06eb626c42b..0000000000000 --- a/website/versioned_docs/version-v18.0.0/api-reference/legacy-apis/legacy-apis.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -id: legacy-apis -title: Legacy APIs -slug: /api-reference/legacy-apis/ -description: API reference for legacy APIs -keywords: - - QueryRenderer - - Container ---- - -API references for our previous legacy APIs are available in our previous docs website: - -- [`QueryRenderer`](https://relay.dev/docs/en/v10.1.3/query-renderer) -- [`Fragment Container`](https://relay.dev/docs/en/v10.1.3/fragment-container) -- [`Refetch Container`](https://relay.dev/docs/en/v10.1.3/refetch-container) -- [`Pagination Container`](https://relay.dev/docs/en/v10.1.3/pagination-container) -- [`Mutations`](https://relay.dev/docs/en/v10.1.3/mutations) -- [`Subscriptions`](https://relay.dev/docs/en/v10.1.3/subscriptions) diff --git a/website/versioned_docs/version-v18.0.0/api-reference/relay-runtime/store.md b/website/versioned_docs/version-v18.0.0/api-reference/relay-runtime/store.md index c5cae4a3ceaaa..e224bc9e420be 100644 --- a/website/versioned_docs/version-v18.0.0/api-reference/relay-runtime/store.md +++ b/website/versioned_docs/version-v18.0.0/api-reference/relay-runtime/store.md @@ -16,6 +16,7 @@ Table of Contents: - [RecordSourceSelectorProxy](#recordsourceselectorproxy) - [RecordProxy](#recordproxy) +- [RecordSourceProxy](#recordsourceproxy) - [ConnectionHandler](#connectionhandler) ## RecordSourceSelectorProxy @@ -46,7 +47,7 @@ const record = store.create(dataID, 'Todo'); ### `delete(dataID: string): void` -Deletes a record from the store given its `dataID`. +Deletes a record from the store given its `dataID`. For existing edges to the deleted record, `undefined` will be returned in the default case even when the value is typed as non-nullable. When [`@throwOnFieldError`](../../guides/throw-on-field-error-directive/) is present, the missing data will throw an error. #### Example @@ -453,6 +454,144 @@ After invalidating a record, any query that references the invalidated record an ```javascript environment.check(query) === 'stale' ``` +## RecordSourceProxy + +The `RecordSourceProxy` serves as an interface to mutate record. + +> **NOTE:** `RecordSourceProxy` exposes many low level APIs that are not typesafe. Users should consider using [typesafe updaters](../../guided-tour/updating-data/typesafe-updaters-faq/), [optimistic updates](../../guided-tour/updating-data/graphql-mutations/#optimistic-updates), and [relay resolvers](../../guides/relay-resolvers/introduction/) instead if their use case can be covered by these alternatives. + +```javascript +interface RecordSourceProxy { + create(dataID: DataID, typeName: string): RecordProxy; + delete(dataID: DataID): void; + get(dataID: DataID): ?RecordProxy; + getRoot(): RecordProxy; + invalidateStore(): void; + readUpdatableFragment( + fragment: UpdatableFragment, + fragmentReference: HasUpdatableSpread, + ): UpdatableData; + readUpdatableQuery( + query: UpdatableQuery, + variables: TVariables, + ): UpdatableData; +} +``` + +### `create(dataID: DataID, typeName: string): RecordProxy` + +Creates a new record in the store given a `dataID` and the `typeName` as defined by the GraphQL schema. Returns a [`RecordProxy`](#recordproxy) which serves as an interface to mutate the newly created record. + +#### Example + +```javascript +const record = store.create(dataID, 'Todo'); +``` + +### `delete(dataID: DataID): void` + +Deletes a record from the store given its `dataID`. For existing edges to the deleted record, `undefined` will be returned in the default case even when the value is typed as non-nullable. When [`@throwOnFieldError`](../../guides/throw-on-field-error-directive/) is present, the missing data will throw an error. + +#### Example + +```javascript +store.delete(dataID); +``` + +### `get(dataID: DataID): ?RecordProxy` + +Retrieves a record from the store given its `dataID`. Returns a [`RecordProxy`](#recordproxy) which serves as an interface to read/mutate the record. + +#### Example + +```javascript +const record = store.get(dataID); +``` + +### `getRoot(): RecordProxy` + +Returns the [`RecordProxy`](#recordproxy) representing the root of the GraphQL document. + +#### Example + +Given the GraphQL document: + +```graphql +viewer { + id +} +``` + +Usage: + +```javascript +// Represents root query +const root = store.getRoot(); +// Get the viewer linked record +const viewer = root.getLinkedRecord('viewer'); +``` + +### `invalidateStore(): void;` + +Globally invalidates the Relay store. This will cause any data that was written to the store before invalidation occurred to be considered stale, and will be considered to require refetch the next time a query is checked with `environment.check()` or is fetched with a `store-or-network` [fetch policy](../../guided-tour/reusing-cached-data/fetch-policies/). + +#### Example + +```javascript +store.invalidateStore(); +``` + +After global invalidation, any query that is checked before refetching it will be considered stale: + +```javascript +environment.check(query) === 'stale' +``` + +### `readUpdatableFragment(fragment: UpdatableFragment,fragmentReference: HasUpdatableSpread): UpdatableData;` + +Fetches an updatable fragment from the store. This updatable fragment's fields can then be imperatively modified to update data in the store. + +For more information on updating the store imperatively, see this [section](../../guided-tour/updating-data/imperatively-modifying-store-data/) of the guided tour. + +#### Example +```javascript +const fragment = graphql` + fragment StoryLikeButton_updatable on Story @updatable { + likeCount + doesViewerLike + } +`; +const { + updatableData +} = store.readUpdatableFragment( + fragment, + story +); +updatableData.likeCount = updatableData.likeCount + 1 +``` + +### `readUpdatableQuery(query: UpdatableQuery,variables: TVariables): UpdatableData` + +Reads an updatable query from the store. This updatable query's fields can be imperatively modified to update data in the store. Unlike `readUpdatableFragment`, you do not need to pass in a `fragmentReference` as an input argument. + +For more information on updating the store imperatively, see this [section](../../guided-tour/updating-data/imperatively-modifying-store-data/) of the guided tour. + +#### Example + +```javascript +const {updatableData} = store.readUpdatableQuery( + graphql` + query NameUpdaterUpdateQuery @updatable { + viewer { + name + } + } + `, + {} +); +const viewer = updatableData.viewer; +viewer.name = newName; +``` ## ConnectionHandler diff --git a/website/versioned_docs/version-v18.0.0/glossary/glossary.md b/website/versioned_docs/version-v18.0.0/glossary/glossary.md index d3c089cde7dd6..a6b2974f4d819 100644 --- a/website/versioned_docs/version-v18.0.0/glossary/glossary.md +++ b/website/versioned_docs/version-v18.0.0/glossary/glossary.md @@ -144,10 +144,13 @@ A directive which declares that a field implements the [connection](#connection) ## Connection + A field implementing the [connection spec](https://relay.dev/graphql/connections.htm). See here for more details on the spec, and the section of the guided tour on rendering list data and pagination. + + A field implementing the [connection spec](https://relay.dev/graphql/connections.htm). See the section of the guided tour on rendering list data and pagination. @@ -186,7 +189,9 @@ See the [Thinking in Relay guide](../principles-and-architecture/thinking-in-rel A directive which can be added to a fragment spread or inline fragment to avoid blocking on that fragment's data. For more detail refer to GraphQL's [documentation on the @defer directive](https://github.com/graphql/graphql-wg/blob/main/rfcs/DeferStream.md#defer). + See [Incremental Data Delivery](https://www.internalfb.com/intern/staticdocs/relay/docs/guides/incremental-data-delivery/). + ## Definition @@ -420,6 +425,7 @@ A lightweight API for specifying a that a React component should be loaded on de This API is safe to use in entrypoint files. + See [the npm module](https://www.npmjs.com/package/jsresource). @@ -496,10 +502,13 @@ TODO A mutation is a combination of two things: a mutation on the backend, followed by query against updated data. + See the [guide on mutations](../guided-tour/updating-data/graphql-mutations), and [this article](https://www.internalfb.com/intern/wiki/Graphql-for-hack-developers/mutation-root-fields/) on defining mutations in your hack code. + + See the [guide on mutations](../guided-tour/updating-data/graphql-mutations). @@ -636,7 +645,9 @@ Required because of current limitations on dynamically loading components in Rea For Relay to process a file with a GraphQL literal, it must be included in a project. A project specifies the folders to which it applies and the schema against which to evaluate GraphQL literals, and includes other information needed by the Relay compiler. + Projects are defined in a single [config](#config) file, found [here](https://www.internalfb.com/intern/diffusion/WWW/browse/master/scripts/relay/compiler-rs/config.www.json) and [here](https://www.internalfb.com/intern/diffusion/FBS/browse/master/xplat/relay/compiler-rs/config.xplat.json). + ## Profiler @@ -855,6 +866,7 @@ TODO A collection of all of the GraphQL types that are known to Relay, for a given [project](#project). + ## Schema Sync The GraphQL [schema](#schema) is derived from annotations on Hack classes in the www repository. diff --git a/website/versioned_docs/version-v18.0.0/guided-tour/refetching/refetching-queries-with-different-data.md b/website/versioned_docs/version-v18.0.0/guided-tour/refetching/refetching-queries-with-different-data.md index 6640c5a9c101c..af6a18239fabc 100644 --- a/website/versioned_docs/version-v18.0.0/guided-tour/refetching/refetching-queries-with-different-data.md +++ b/website/versioned_docs/version-v18.0.0/guided-tour/refetching/refetching-queries-with-different-data.md @@ -130,6 +130,7 @@ function App(props: Props) { const refetch = useCallback(() => { if (isRefetching) { return; } setIsRefetching(true); + const variables = { id: 'different-id' }; // fetchQuery will fetch the query and write // the data to the Relay store. This will ensure @@ -146,7 +147,7 @@ function App(props: Props) { // At this point the data for the query should // be cached, so we use the 'store-only' // fetchPolicy to avoid suspending. - loadQuery({id: 'different-id'}, {fetchPolicy: 'store-only'}); + loadQuery(variables, {fetchPolicy: 'store-only'}); }, error: () => { setIsRefetching(false); @@ -292,6 +293,7 @@ function App(props: Props) { const refetch = useCallback(() => { if (isRefreshing) { return; } setIsRefreshing(true); + const variables = { id: 'different-id' }; // fetchQuery will fetch the query and write // the data to the Relay store. This will ensure @@ -313,7 +315,7 @@ function App(props: Props) { fetchKey: (prev?.options.fetchKey ?? 0) + 1, fetchPolicy: 'store-only', }, - variables: {id: 'different-id'} + variables, })); }, error: () => { diff --git a/website/versioned_docs/version-v18.0.0/guided-tour/refetching/refreshing-fragments.md b/website/versioned_docs/version-v18.0.0/guided-tour/refetching/refreshing-fragments.md index 937af7feb3495..8b200ca599ad4 100644 --- a/website/versioned_docs/version-v18.0.0/guided-tour/refetching/refreshing-fragments.md +++ b/website/versioned_docs/version-v18.0.0/guided-tour/refetching/refreshing-fragments.md @@ -24,9 +24,11 @@ When referring to **"refreshing a fragment"**, we mean fetching the *exact* same + If we want to keep our data up to date with the latest version from the server, the first thing to consider is if it appropriate to use any real-time features, which can make it easier to automatically keep the data up to date without manually refreshing the data periodically. One example of this is using [GraphQL Subscriptions](https://relay.dev/docs/guided-tour/updating-data/graphql-subscriptions/), which will require additional configuration on your server and [network layer](https://relay.dev/docs/guided-tour/updating-data/graphql-subscriptions/#configuring-the-network-layer). + ## Using `useRefetchableFragment` diff --git a/website/versioned_docs/version-v18.0.0/guided-tour/refetching/refreshing-queries.md b/website/versioned_docs/version-v18.0.0/guided-tour/refetching/refreshing-queries.md index 41af90583486d..7e683fe30ba7f 100644 --- a/website/versioned_docs/version-v18.0.0/guided-tour/refetching/refreshing-queries.md +++ b/website/versioned_docs/version-v18.0.0/guided-tour/refetching/refreshing-queries.md @@ -25,6 +25,7 @@ When referring to **"refreshing a query"**, we mean fetching the *exact* same da + If we want to keep our data up to date with the latest version from the server, the first thing to consider is if it appropriate to use any real-time features, which can make it easier to automatically keep the data up to date without manually refreshing the data periodically. One example of this is using [GraphQL Subscriptions](https://relay.dev/docs/guided-tour/updating-data/graphql-subscriptions), which will require additional configuration on your server and [network layer](https://relay.dev/docs/guided-tour/updating-data/graphql-subscriptions/#configuring-the-network-layer). diff --git a/website/versioned_docs/version-v18.0.0/guided-tour/updating-data/typesafe-updaters-faq.md b/website/versioned_docs/version-v18.0.0/guided-tour/updating-data/typesafe-updaters-faq.md index 22a2f46ca770f..3bc6a5c393460 100644 --- a/website/versioned_docs/version-v18.0.0/guided-tour/updating-data/typesafe-updaters-faq.md +++ b/website/versioned_docs/version-v18.0.0/guided-tour/updating-data/typesafe-updaters-faq.md @@ -32,6 +32,8 @@ Is something missing from this Q&A? Are you confused? Would you like help adopti Typesafe updaters is the name given to a project to provide a typesafe and ergonomic alternative to the existing APIs for imperatively updating data in the Relay store. +For example, [`readUpdatableFragment`](../../../api-reference/store/#readupdatablefragmentfragment-updatablefragmenttfragmenttype-tdatafragmentreference-hasupdatablespreadtfragmenttype-updatabledatatdata) and [`readUpdatableQuery`](../../../api-reference/store/#readupdatablequeryquery-updatablequerytvariables-tdatavariables-tvariables-updatabledatatdata) are two typesafe updaters that the store exposes. + ## Why? Relay provides typesafe and ergonomic APIs for fetching and managing data that originates on the server. In addition, Relay provides the ability to define local-only fields in **client schema extensions**. However, the APIs for mutating the data in these fields has hitherto been verbose and not ergonomic, meaning that we could not recommend Relay as a solution for managing local state. @@ -40,16 +42,12 @@ Relay provides typesafe and ergonomic APIs for fetching and managing data that o The pre-existing APIs are verbose and not typesafe. They make it easy to make a variety of mistakes and require that the developer understand a new set of APIs only when writing updaters. -Typesafe updaters is a set of APIs that are typesafe and (hopefully) more ergonomic. They leverage well-known Relay idioms (queries, fragments, type refinement) and use getters and setters instead of requiring that the developer learn about a set of methods that are unused elsewhere. +Typesafe updaters is a set of APIs that are typesafe and more ergonomic. They leverage well-known Relay idioms (queries, fragments, type refinement) and use getters and setters instead of requiring that the developer learn about a set of methods that are unused elsewhere. ## How does a developer use typesafe updaters? With typesafe updaters, a developers writes an updatable query or a fragment that specifies the data to imperatively update. Then, the developer reads out that data from the store, returning a so-called **updatable proxy**. Then, the developer mutates that updatable proxy. Mutating that updatable proxy using setters (e.g. `updatableData.name = "Godzilla"`) results in calls to the old API, but with added type safety. -## Why are these labeled `_EXPERIMENTAL`? - -These are de facto not experimental. We encourage you to use them when writing new code! This suffix will be removed soon. - ## What is an updatable query or fragment? An updatable query or fragment is a query or fragment that has the `@updatable` directive. @@ -77,7 +75,7 @@ You should select that field in both a regular query/fragment **and** in an upda ## Where do I get a `store`? -The classes `RelayRecordSourceSelectorProxy` and `RelayRecordSourceProxy` contain the methods `readUpdatableQuery` and `readUpdatableFragment`. One can acquire an instance of these classes: +The classes `RelayRecordSourceSelectorProxy`, `RecordSourceProxy` and `RelayRecordSourceProxy` contain the methods `readUpdatableQuery` and `readUpdatableFragment`. One can acquire an instance of these classes: * In updaters of mutations and subscriptions * In optimistic updaters of mutations diff --git a/website/versioned_docs/version-v18.0.0/guides/relay-resolvers/defining-types.md b/website/versioned_docs/version-v18.0.0/guides/relay-resolvers/defining-types.md index 128e3183739a3..66471ade373ca 100644 --- a/website/versioned_docs/version-v18.0.0/guides/relay-resolvers/defining-types.md +++ b/website/versioned_docs/version-v18.0.0/guides/relay-resolvers/defining-types.md @@ -125,7 +125,9 @@ interface IUser { You could define two (or more) concrete resolver types that implement the IUser interface by adding annotations in the docblock (the same applies for unions). + Note, support for abstract types is not available for relay resolvers in Flow syntax (yet). + + Example diff showing the adoption of this strategy: D24370183 + ### Why not implement this at the schema/server level? diff --git a/website/versioned_docs/version-v18.0.0/guides/testing-relay-components.md b/website/versioned_docs/version-v18.0.0/guides/testing-relay-components.md index ea4de6f2cc36e..88f444adfa076 100644 --- a/website/versioned_docs/version-v18.0.0/guides/testing-relay-components.md +++ b/website/versioned_docs/version-v18.0.0/guides/testing-relay-components.md @@ -57,7 +57,9 @@ With the `MockPayloadGenerator` and `@relay_test_operation`, we want to get rid **[React Testing Library](https://testing-library.com/react)** is a set of helpers that let you test React components without relying on their implementation details. This approach makes refactoring a breeze and also nudges you towards best practices for accessibility. Although it doesn't provide a way to "shallowly" render a component without its children, a test runner like Jest lets you do this by [mocking](https://reactjs.org/docs/testing-recipes.html#mocking-modules). + Note: The [`ReactTestRenderer`](https://www.npmjs.com/package/react-test-renderer) library has been deprecated since React v18. `ReactTestRenderer` may still be referenced internally as an alternative to React Testing Library. However, when possible, we recommend using React Testing Library (or `@testing-library/react-native`) to test your React applications with Relay. + ## RelayMockEnvironment API Overview diff --git a/website/versioned_docs/version-v18.0.0/migration-and-compatibility/relay-hooks-and-legacy-container-apis.md b/website/versioned_docs/version-v18.0.0/migration-and-compatibility/relay-hooks-and-legacy-container-apis.md index fc9d1dc248aea..a8627c0b15333 100644 --- a/website/versioned_docs/version-v18.0.0/migration-and-compatibility/relay-hooks-and-legacy-container-apis.md +++ b/website/versioned_docs/version-v18.0.0/migration-and-compatibility/relay-hooks-and-legacy-container-apis.md @@ -17,7 +17,7 @@ import DocsRating from '@site/src/core/DocsRating'; ## Compatibility between Relay Hooks and Containers -Relay Hooks are fully compatible with Relay's [container-based APIs](../../api-reference/legacy-apis/), meaning that containers can render components that use Hooks, and vice-versa. +Relay Hooks are fully compatible with Relay's container-based APIs, meaning that containers can render components that use Hooks, and vice-versa. This means that you can adopt Relay Hooks incrementally, either by using them exclusively for new code, or by migrating specific parts of your app, without affecting the rest of your existing application. diff --git a/website/versioned_docs/version-v18.0.0/tutorial/refetchable-fragments.md b/website/versioned_docs/version-v18.0.0/tutorial/refetchable-fragments.md index d907d620c5674..18d6f46c059ca 100644 --- a/website/versioned_docs/version-v18.0.0/tutorial/refetchable-fragments.md +++ b/website/versioned_docs/version-v18.0.0/tutorial/refetchable-fragments.md @@ -336,7 +336,9 @@ import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/interna Besides fragments on types that implement `Node`, you can also refetch fragments that are on `Viewer` (since the viewer is assumed to be stable throughout a session) and that are at the top level of a query (since there’s no field above them that could change identity). + Meta only: Ents marked with GraphQLFetchable can also be refetched. + diff --git a/website/versioned_docs/version-v19.0.0/api-reference/entrypoint-apis/entrypoint-container.md b/website/versioned_docs/version-v19.0.0/api-reference/entrypoint-apis/entrypoint-container.md new file mode 100644 index 0000000000000..77c2a3e44a451 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/api-reference/entrypoint-apis/entrypoint-container.md @@ -0,0 +1,38 @@ +--- +id: entrypoint-container +title: EntryPointContainer +slug: /api-reference/entrypoint-container/ +description: API reference for EntryPointContainer, a React component used to render the root component of an entrypoint +keywords: + - entrypoint + - container + - root +--- + +import DocsRating from '@site/src/core/DocsRating'; +import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; + +## `EntryPointContainer` + + + +For more information, see the [Defining EntryPoints](../../guides/entrypoints/using-entrypoints/#defining-entrypoints) and [Consuming EntryPoints](../../guides/entrypoints/using-entrypoints/#-entrypoints) guides. + + + +```js +function EntryPointContainer({ + entryPointReference, + props, +}: { + +entryPointReference: PreloadedEntryPoint, + +props: TRuntimeProps, +}): ReactElement +``` + +A React component that renders a preloaded EntryPoint. + +* `entryPointReference`: the value returned from a call to `loadEntryPoint` or acquired from the `useEntryPointLoader` hook. +* `props`: additional runtime props that will be passed to the `Component` + + diff --git a/website/versioned_docs/version-v19.0.0/api-reference/entrypoint-apis/load-entrypoint.md b/website/versioned_docs/version-v19.0.0/api-reference/entrypoint-apis/load-entrypoint.md new file mode 100644 index 0000000000000..66c819c8b7d0e --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/api-reference/entrypoint-apis/load-entrypoint.md @@ -0,0 +1,77 @@ +--- +id: load-entrypoint +title: loadEntryPoint +slug: /api-reference/load-entrypoint/ +description: API reference for loadEntryPoint, which imperatively loads an entrypoint and data for its queries +keywords: + - entrypoint + - preload + - render-as-you-fetch +--- + +import DocsRating from '@site/src/core/DocsRating'; +import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; + +## `loadEntryPoint` + +This function is designed to be used with `EntryPointContainer` to implement the "render-as-you-fetch" pattern. + +EntryPoint references returned from `loadEntryPoint` will leak data to the Relay store (if they have associated queries) unless `.dispose()` is called on them once they are no longer referenced. As such, prefer using `useEntryPointLoader` when possible, which ensures that EntryPoint references are correctly disposed for you. See the [`useEntryPointLoader`](../use-entrypoint-loader) docs for a more complete example. + + + +For more information, see the [Loading EntryPoints](../../guides/entrypoints/using-entrypoints/#loading-entrypoints) guide. + + + +```js +const EntryPoint = require('MyComponent.entrypoint.js'); + +const {loadQuery} = require('react-relay'); + +// Generally, your component should access the environment from the React context, +// and pass that environment to this function. +const getEntrypointReference = environment => loadEntryPoint( + { getEnvironment: () => environment }, + EntryPoint, + {id: '4'}, +); + +// later: pass entryPointReference to EntryPointContainer +// Note that EntryPoint references should have .dispose() called on them, +// which is missing in this example. +``` + +### Arguments + +* `environmentProvider`: A provider for a Relay Environment instance on which to execute the request. If you're starting this request somewhere within a React component, you probably want to use the environment you obtain from using [`useRelayEnvironment`](../use-relay-environment/). +* `EntryPoint`: EntryPoint to load. +* `entryPointParams`: Parameters that will be passed to the EntryPoint's `getPreloadProps` method. + +### Flow Type Parameters + +* `TEntryPointParams`: Type parameter corresponding to the type of the first parameter of the `getPreloadProps` method of the EntryPoint. +* `TPreloadedQueries`: the type of the `queries` parameter to the EntryPoint component. +* `TPreloadedEntryPoints`: the type of the `entrypoints` parameter passed to the EntryPoint component. +* `TRuntimeProps`: the type of the `props` prop passed to `EntryPointContainer`. This object is passed down to the EntryPoint component, also as `props`. +* `TExtraProps`: if an EntryPoint's `getPreloadProps` method returns an object with an `extraProps` property, those extra props will be passed to the EntryPoint component as `extraProps`. +* `TEntryPointComponent`: the type of the EntryPoint. +* `TEntryPoint`: the type of the EntryPoint. + +### Return Value + +An EntryPoint reference with the following properties: + +* `dispose`: a method that will release any query references loaded by this EntryPoint (including indirectly, by way of other EntryPoints) from being retained by the store. This can cause the data referenced by these query reference to be garbage collected. + +The exact format of the return value is *unstable and highly likely to change*. We strongly recommend not using any other properties of the return value, as such code would be highly likely to break when upgrading to future versions of Relay. Instead, pass the result of `loadEntryPoint()` to `EntryPointContainer`. + +### Behavior + +* When `loadEntryPoint()` is called, each of an EntryPoint's associated queries (if it has any) will load their query data and query AST. Once both the query AST and the data are available, the data will be written to the store. This differs from the behavior of `prepareEntryPoint_DEPRECATED`, which would only write the data from an associated query to the store when that query was rendered with `usePreloadedQuery`. +* The EntryPoint reference's associated query references will be retained by the Relay store, preventing it the data from being garbage collected. Once you call `.dispose()` on the EntryPoint reference, the data from the associated queries is liable to be garbage collected. +* `loadEntryPoint` may throw an error if it is called during React's render phase. + + + + diff --git a/website/versioned_docs/version-v19.0.0/api-reference/entrypoint-apis/use-entrypoint-loader.md b/website/versioned_docs/version-v19.0.0/api-reference/entrypoint-apis/use-entrypoint-loader.md new file mode 100644 index 0000000000000..79c9b7452d54d --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/api-reference/entrypoint-apis/use-entrypoint-loader.md @@ -0,0 +1,99 @@ +--- +id: use-entrypoint-loader +title: useEntryPointLoader +slug: /api-reference/use-entrypoint-loader/ +description: API reference for useEntryPointLoader, a React hook used to load entrypoints in response to user events +keywords: + - render-as-you-fetch + - entrypoint + - preload +--- + +import DocsRating from '@site/src/core/DocsRating'; +import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; + +## `useEntryPointLoader` + +Hook used to make it easy to safely work with EntryPoints, while avoiding data leaking into the Relay store. It will keep an EntryPoint reference in state, and dispose of it when it is no longer accessible via state. + + + +For more information, see the [Loading EntryPoints](https://www.internalfb.com/intern/wiki/Relay/Guides/entry-points/#loading-entrypoints) guide. + + + +```js +const {useEntryPointLoader, EntryPointContainer} = require('react-relay'); + +const ComponentEntryPoint = require('Component.entrypoint'); + +function EntryPointRevealer(): React.MixedElement { + const environmentProvider = useMyEnvironmentProvider(); + const [ + entryPointReference, + loadEntryPoint, + disposeEntryPoint, + ] = useEntryPointLoader(environmentProvider, ComponentEntryPoint); + + return ( + <> + { + entryPointReference == null && ( + + ) + } + { + entryPointReference != null && ( + <> + + + + + + ) + } + + ); +} +``` + +### Arguments + +* `environmentProvider`: an object with a `getEnvironment` method that returns a Relay environment. +* `EntryPoint`: the EntryPoint, usually acquired by importing a `.entrypoint.js` file. + +### Flow Type Parameters + +* `TEntryPointParams`: the type of the first argument to the `getPreloadProps` method of the EntryPoint. +* `TPreloadedQueries`: the type of the `queries` prop passed to the EntryPoint component. +* `TPreloadedEntryPoints`: the type of the `entryPoints` prop passed to the EntryPoint component. +* `TRuntimeProps`: the type of the `props` prop passed to `EntryPointContainer`. This object is passed down to the EntryPoint component, also as `props`. +* `TExtraProps`: if an EntryPoint's `getPreloadProps` method returns an object with an `extraProps` property, those extra props will be passed to the EntryPoint component as `extraProps` and have type `TExtraProps`. +* `TEntryPointComponent`: the type of the EntryPoint component. +* `TEntryPoint`: the type of the EntryPoint. + +### Return value + +A tuple containing the following values: + +* `entryPointReference`: the EntryPoint reference, or `null`. +* `loadEntryPoint`: a callback that, when executed, will load an EntryPoint, which will be accessible as `entryPointReference`. If a previous EntryPoint was loaded, it will dispose of it. It may throw an error if called during React's render phase. + * Parameters + * `params: TEntryPointParams`: the params passed to the EntryPoint's `getPreloadProps` method. +* `disposeEntryPoint`: a callback that, when executed, will set `entryPointReference` to `null` and call `.dispose()` on it. It has type `() => void`. It should not be called during React's render phase. + +### Behavior + +* When the `loadEntryPoint` callback is called, each of an EntryPoint's associated queries (if it has any) will load their query data and query AST. Once both the query AST and the data are available, the data will be written to the store. This differs from the behavior of `prepareEntryPoint_DEPRECATED`, which would only write the data from an associated query to the store when that query was rendered with `usePreloadedQuery`. +* The EntryPoint reference's associated query references will be retained by the Relay store, preventing it the data from being garbage collected. Once you call `.dispose()` on the EntryPoint reference, the data from the associated queries is liable to be garbage collected. +* The `loadEntryPoint` callback may throw an error if it is called during React's render phase. + + + diff --git a/website/versioned_docs/version-v19.0.0/api-reference/graphql/graphql-directives.md b/website/versioned_docs/version-v19.0.0/api-reference/graphql/graphql-directives.md new file mode 100644 index 0000000000000..eb7f1a824beed --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/api-reference/graphql/graphql-directives.md @@ -0,0 +1,378 @@ +--- +id: graphql-directives +title: GraphQL Directives +slug: /api-reference/graphql-and-directives/ +description: API Reference for GraphQL directives +keywords: + - GraphQL + - Directive + - arguments + - argumentDefinitions + - connection + - relay + - inline + - provider +--- + +import DocsRating from '@site/src/core/DocsRating'; import {FbInternalOnly, +OssOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; + +Relay uses directives to add additional information to GraphQL documents, which +are used by the [Relay compiler](../../guides/compiler/) to generate the +appropriate runtime artifacts. These directives only appear in your application +code and are removed from requests sent to your GraphQL server. + + + +**Note:** The Relay compiler will maintain any directives supported by your server (such as `@include` or `@skip`) so they remain part of the request to the GraphQL server and won't alter generated runtime artifacts. + + + + +**Note:** The Relay compiler will maintain any directives supported by your server (such as `@include` or `@skip`) so they remain part of the request to the GraphQL server and won't alter generated runtime artifacts. Additional directives are documented [here](https://www.internalfb.com/intern/wiki/GraphQL/APIs_and_References/Directives/#graphql-standard). + + + +## `@arguments` + +`@arguments` is a directive used to pass arguments to a fragment that was +defined using [`@argumentDefinitions`](#argumentdefinitions). For example: + +```graphql +query TodoListQuery($userID: ID) { + ...TodoList_list @arguments(count: $count, userID: $userID) # Pass arguments here +} +``` + +## `@argumentDefinitions` + +`@argumentDefinitions` is a directive used to specify arguments taken by a +fragment. For example: + +```graphql +fragment TodoList_list on TodoList +@argumentDefinitions( + count: {type: "Int", defaultValue: 10} # Optional argument + userID: {type: "ID"} # Required argument +) { + title + todoItems(userID: $userID, first: $count) { + # Use fragment arguments here as variables + ...TodoItem_item + } +} +``` + +### Provided Variables + +A provided variable is a special fragment variable whose value is supplied by a +specified provider function at runtime. This simplifies supplying device +attributes, user experiment flags, and other runtime constants to graphql +fragments. + +To add a provided variable: + +- add an argument with `provider: "[JSModule].relayprovider"` to + `@argumentDefinitions` +- ensure that `[JSModule].relayprovider.js` exists and exports a `get()` + function + - `get` should return the same value on every call for a given run. + +```graphql +fragment TodoItem_item on TodoList +@argumentDefinitions( + include_timestamp: { + type: "Boolean!" + provider: "Todo_ShouldIncludeTimestamp.relayprovider" + } +) { + timestamp @include(if: $include_timestamp) + text +} +``` + +```javascript +// Todo_ShouldIncludeTimestamp.relayprovider.js +export default { + get(): boolean { + // must always return true or false for a given run + return check('todo_should_include_timestamp'); + }, +}; +``` + +Notes: + + + +- Even though fragments declare provided variables in `argumentDefinitions`, + their parent cannot pass provided variables through `@arguments`. +- An argument definition cannot specify both a provider and a defaultValue. +- If the modified fragment is included in operations that use hack preloaders + (`@preloadable(hackPreloader: true)`), you will need to manually add provided + variables when calling `RelayPreloader::gen`. + - Hack's typechecker will fail with + `The field __relay_internal__pv__[JsModule] is missing.` + - We strongly encourage switching to + [Entrypoints](../../guides/entrypoints/using-entrypoints/) if possible. +- _Unstable / subject to change_ + - Relay transforms provided variables to operation root variables and renames + them to `__relay_internal__pv__[JsModule]`. + - Only relevant if you are debugging a query that uses provided variables. + + + + + +- Even though fragments declare provided variables in `argumentDefinitions`, + their parent cannot pass provided variables through `@arguments`. +- An argument definition cannot specify both a provider and a defaultValue. +- _Unstable / subject to change_ + - Relay transforms provided variables to operation root variables and renames + them to `__relay_internal__pv__[JsModule]`. + - Only relevant if you are debugging a query that uses provided variables. + + + +## `@catch` + +`@catch` is a directive you can add to fields, fragments, queries, mutations, +and aliased inline fragments in your Relay queries to declare how field-level +errors are handled in runtime. + +See also [the @catch guide](../../guides/catch-directive/). + +## `@connection(key: String!, filters: [String])` + +With `usePaginationFragment`, Relay expects connection fields to be annotated +with a `@connection` directive. For more detailed information and an example, +check out the +[docs on `usePaginationFragment`](../../guided-tour/list-data/rendering-connections). + +## `@refetchable(queryName: String!, directives: [String], preferFetchable: Boolean)` + +With `useRefetchableFragment` and `usePaginationFragment`, Relay expects a +`@refetchable` directive. The `@refetchable` directive can only be added to +fragments that are "refetchable", that is, on fragments that are declared on +`Viewer` or `Query` types, or on a type that implements `Node` (i.e. a type that +has an id). The `@refetchable` directive will autogenerate a query with the +specified `queryName`. This will also generate Flow types for the query, +available to import from the generated file: `.graphql.js`. For more +detailed information and examples, check out the docs on +[`useRefetchableFragment`](../use-refetchable-fragment/) or +[`usePaginationFragment`](../use-pagination-fragment/). + +Optionally, you can pass in a list of directives to add to the autogenerated +query. For example, this can be used to add the `@relay_test_operation` +directive for [testing](../../guides/testing-relay-components): + +[Optional] `preferFetchable: Boolean` + +This argument tells the Relay compiler to prefer generating +`fetch_MyType(): MyType` queries for types that implement the `Node` interface. +This is useful for schemas that have adopted the `@strong` and `@fetchable` +server annotations for types. You can directly fetch concrete objects without +needing to refine `Node` interface to a specific type. + +```javascript +graphql` + fragment FriendsListComponent_user on User + @refetchable( + queryName: "FriendsListFetchQuery" + directives: ["@relay_test_operation"] + ) { + ... + } +`; +``` + +## `@relay(plural: Boolean)` + +When defining a fragment for use with `useFragment`, you can use the +`@relay(plural: true)` directive to indicate that the hook expects the prop for +that fragment to be a list of items instead of a single item. A query or parent +that spreads a `@relay(plural: true)` fragment should do so within a plural +field (ie a field backed by a +[GraphQL list](http://graphql.org/learn/schema/#lists-and-non-null). For +example: + +```javascript +// Plural fragment definition +graphql` + fragment TodoItems_items on TodoItem @relay(plural: true) { + id + text + } +`; + +// Plural fragment usage: note the parent type is a list of items (`TodoItem[]`) +fragment TodoApp_app on App { + items { + // parent type is a list here + ...TodoItem_items + } +} +``` + +## `@required` + +`@required` is a directive you can add to fields in your Relay queries to +declare how null values should be handled at runtime. + +See also [the @required guide](../../guides/required-directive/). + +## `@throwOnFieldError` + +`@throwOnFieldError` is a directive you can add to your Relay queries and fragments to have Relay throw if any field errors are encountered when reading the query or fragment. Adding the directive will allow Relay to generate non-null types for any fields marked as `@semanticNonNull` in the schema. + +See also [the @throwOnFieldError guide](../../guides/throw-on-field-error-directive/). + +**Read more about Relay's experimental support for +[Semantic Nullability](../../guides/semantic-nullability.md).** + +## `@semanticNonNull` + +The `@semanticNonNull` directive can be added to fields in your schema to +indicate that the field is non-nullable in the semantic sense, but that the +client should still be prepared to handle errors. + +**Read more about Relay's experimental support for +[Semantic Nullability](../../guides/semantic-nullability.md).** + +## `@alias` + +`@alias` is a directive that allows you to give a fragment spread or inline +fragment an alias, similar to a field alias. This is useful when you want to +conditionally include a fragment and check if it was fetched, or otherwise group +data together. + +For fragment spreads, the alias will default to the fragment name. For inline +fragments, the alias will default to the type name. If you wish to supply your +own name, or you have an inline fragment without any type condition, you can +specify the alias using the `as` argument. + +```graphql +fragment MyFragment on User { + ... on User @alias(as: "myGreatAlias") { + name + } +} +``` + +See also [the @alias guide](../../guides/alias-directive/). + +## `@inline` + +The hooks APIs that Relay exposes allow you to read data from the store only +during the render phase. In order to read data from outside of the render phase +(or from outside of React), Relay exposes the `@inline` directive. The data from +a fragment annotated with `@inline` can be read using `readInlineData`. + +In the example below, the function `processItemData` is called from a React +component. It requires an item object with a specific set of fields. All React +components that use this function should spread the `processItemData_item` +fragment to ensure all of the correct item data is loaded for this function. + +```javascript +import {graphql, readInlineData} from 'react-relay'; + +// non-React function called from React +function processItemData(itemRef) { + const item = readInlineData( + graphql` + fragment processItemData_item on Item @inline { + title + price + creator { + name + } + } + `, + itemRef, + ); + sendToThirdPartyApi({ + title: item.title, + price: item.price, + creatorName: item.creator.name, + }); +} +``` + +```javascript +export default function MyComponent({item}) { + function handleClick() { + processItemData(item); + } + + const data = useFragment( + graphql` + fragment MyComponent_item on Item { + ...processItemData_item + title + } + `, + item, + ); + + return ; +} +``` + +## `@relay(mask: Boolean)` + +It is not recommended to use `@relay(mask: false)`. Please instead consider +using the `@inline` fragment. + +`@relay(mask: false)` can be used to prevent data masking; when including a +fragment and annotating it with `@relay(mask: false)`, its data will be +available directly to the parent instead of being masked for a different +container. + +Applied to a fragment definition, `@relay(mask: false)` changes the generated +Flow types to be better usable when the fragment is included with the same +directive. The Flow types will no longer be exact objects and no longer contain +internal marker fields. + +This may be helpful to reduce redundant fragments when dealing with nested or +recursive data within a single Component. + +Keep in mind that it is typically considered an **anti-pattern** to create a +single fragment shared across many containers. Abusing this directive could +result in over-fetching in your application. + +In the example below, the `user` prop will include the data for `id` and `name` +fields wherever `...Component_internUser` is included, instead of Relay's normal +behavior to mask those fields: + +```javascript +graphql` + fragment Component_internUser on InternUser @relay(mask: false) { + id + name + } +`; +``` + +## `@waterfall` + +With [Relay Resolvers](../../guides/relay-resolvers/introduction.md) it's +possible to create client-defined edges in the graph which point to server +types. When reading these edge fields, Relay is forced to lazily fetch the +server data for the edge. This will force Relay to make a second request to the +server to fetch the data for the edge. + +To highlight this tradeoff both in the editor and during code review, the Relay +compiler expects all reads of these fields to be annotated as `@waterfall`. + +```graphql +fragment EditPost on DraftPost { + author @waterfall { + name + } +} +``` + +See the [Return Type](../../guides/relay-resolvers/return-types.md#server-types) +portion of the Relay Resolvers guide for more information. + + diff --git a/website/versioned_docs/version-v19.0.0/api-reference/hooks/load-query.md b/website/versioned_docs/version-v19.0.0/api-reference/hooks/load-query.md new file mode 100644 index 0000000000000..5e635910dc492 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/api-reference/hooks/load-query.md @@ -0,0 +1,84 @@ +--- +id: load-query +title: loadQuery +slug: /api-reference/load-query/ +description: API reference for loadQuery, which imperatively fetches data for a query, retains that query and returns a query reference +keywords: + - preload + - fetch + - query + - render-as-you-fetch + - retain + - query reference +--- + +import DocsRating from '@site/src/core/DocsRating'; +import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; + +## `loadQuery` + +This function is designed to be used with the `usePreloadedQuery()` hook to implement the "render-as-you-fetch". + +Query references returned from `loadQuery` will leak data into the Relay store if `.dispose()` is not called on them once they are no longer referenced. As such, prefer calling `useQueryLoader` when possible, which ensures that query references are disposed for you. + +See the [`usePreloadedQuery`](../use-preloaded-query) docs for a more complete example. + +```js +const MyEnvironment = require('MyEnvironment'); +const {loadQuery} = require('react-relay'); + +const query = graphql` + query AppQuery($id: ID!) { + user(id: $id) { + name + } + } +`; + +// Note: you should generally not call loadQuery at the top level. +// Instead, it should be called in response to an event (such a route navigation, +// click, etc.). +const queryReference = loadQuery( + MyEnvironment, + query, + {id: '4'}, + {fetchPolicy: 'store-or-network'}, +); + +// later: pass queryReference to usePreloadedQuery() +// Note that query reference should have .dispose() called on them, +// which is missing in this example. +``` + +### Arguments + +* `environment`: A Relay Environment instance on which to execute the request. If you're starting this request somewhere within a React component, you probably want to use the environment you obtain from using [`useRelayEnvironment`](#userelayenvironment). +* `query`: GraphQL query to fetch, specified using a `graphql` template literal, or a preloadable concrete request, which can be acquired by requiring the file `$Parameters.graphql`. Relay will only generate the `$Parameters` file if the query is annotated with `@preloadable`. +* `variables`: Object containing the variable values to fetch the query. These variables need to match GraphQL variables declared inside the query. +* `options`: *_[Optional]_* options object + * `fetchPolicy`: Determines if cached data should be used, and whether to send a network request based on the cached data that is currently available in the Relay store (for more details, see our [Fetch Policies](../../guided-tour/reusing-cached-data/fetch-policies) and [Garbage Collection](../../guided-tour/reusing-cached-data/presence-of-data) guides): + * "store-or-network": **(default)** *will* reuse locally cached data and will *only* send a network request if any data for the query is missing. If the query is fully cached, a network request will *not* be made. + * "store-and-network": *will* reuse locally cached data and will *always* send a network request, regardless of whether any data was missing from the local cache or not. + * "network-only": *will not* reuse locally cached data, and will *always* send a network request to fetch the query, ignoring any data that might be locally cached in Relay. + * `networkCacheConfig`: *_[Optional]_* Default value: `{force: true}`. Object containing cache config options for the *network layer*. Note that the network layer may contain an *additional* query response cache which will reuse network responses for identical queries. If you want to bypass this cache completely (which is the default behavior), pass `{force: true}` as the value for this option. +* `environmentProviderOptions`: *[Optional]* options object + * Options passed to an `environmentProvider` used in `prepareSurfaceEntryPoint.js`. + +### Return Value + +A query reference with the following properties: + +* `dispose`: a method that will release the query reference from being retained by the store. This can cause the data referenced by the query reference to be garbage collected. + +The exact format of the return value is *unstable and highly likely to change*. We strongly recommend not using any other properties of the return value, as such code would be highly likely to break when upgrading to future versions of Relay. Instead, pass the result of `loadQuery()` to `usePreloadedQuery()`. + +### Behavior + +* `loadQuery()` will fetch data if passed a query, or data and the query if passed a preloadable concrete request. Once both the query and data are available, the data from the query will be written to the store. This differs from the behavior of `preloadQuery_DEPRECATED`, which would only write data to the store if the query was passed to `usePreloadedQuery`. +* the query reference returned from `loadQuery` will be retained by the Relay store, preventing the data from being garbage collected. Once you call `.dispose()` on the query reference, it can be garbage collected. +* `loadQuery()` will throw an error if it is called during React's render phase. + + + + + diff --git a/website/versioned_docs/version-v19.0.0/api-reference/hooks/relay-environment-provider.md b/website/versioned_docs/version-v19.0.0/api-reference/hooks/relay-environment-provider.md new file mode 100644 index 0000000000000..882e457cace88 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/api-reference/hooks/relay-environment-provider.md @@ -0,0 +1,78 @@ +--- +id: relay-environment-provider +title: RelayEnvironmentProvider +slug: /api-reference/relay-environment-provider/ +description: API reference for RelayEnvironmentProvider, which sets a Relay environment in React context +keywords: + - environment + - context +--- + +import DocsRating from '@site/src/core/DocsRating'; + +## `RelayEnvironmentProvider` + +This component is used to set a Relay environment in React Context. Usually, a *single* instance of this component should be rendered at the very root of the application, in order to set the Relay environment for the whole application: + +```js +const React = require('React'); +const { + Store, + RecordSource, + Environment, + Network, + Observable, +} = require("relay-runtime"); + +const {RelayEnvironmentProvider} = require('react-relay'); + +/** + * Custom fetch function to handle GraphQL requests for a Relay environment. + * + * This function is responsible for sending GraphQL requests over the network and returning + * the response data. It can be customized to integrate with different network libraries or + * to add authentication headers as needed. + * + * @param {RequestParameters} params - The GraphQL request parameters to send to the server. + * @param {Variables} variables - Variables used in the GraphQL query. + */ +function fetchFunction(params, variables) { + const response = fetch("http://my-graphql/api", { + method: "POST", + headers: [["Content-Type", "application/json"]], + body: JSON.stringify({ + query: params.text, + variables, + }), + }); + + return Observable.from(response.then((data) => data.json())); +}; + +/** + * Creates a new Relay environment instance for managing (fetching, storing) GraphQL data. + */ +function createEnvironment() { + const network = Network.create(fetchFunction); + const store = new Store(new RecordSource()); + return new Environment({ store, network }); +} + +const environment = createEnvironment(); + +function Root() { + return ( + + + + ); +} + +module.exports = Root; +``` + +### Props + +* `environment`: The Relay environment to set in React Context. Any Relay Hooks (like [`useLazyLoadQuery`](../use-lazy-load-query) or [`useFragment`](../use-fragment)) used in descendants of this provider component will use the Relay environment specified here + + diff --git a/website/versioned_docs/version-v19.0.0/api-reference/hooks/use-client-query.md b/website/versioned_docs/version-v19.0.0/api-reference/hooks/use-client-query.md new file mode 100644 index 0000000000000..8e1f3a1019674 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/api-reference/hooks/use-client-query.md @@ -0,0 +1,65 @@ +--- +id: use-client-query +title: useClientQuery +slug: /api-reference/use-client-query/ +description: API reference for useClientQuery, a React hook used to render client only queries +keywords: + - query + - read + - client-query +--- + +import DocsRating from '@site/src/core/DocsRating'; + +`useClientQuery` hook is used to render queries that read _only_ client fields. + +The Relay Compiler fully supports [client-side extensions](../../guides/client-schema-extensions/) of the schema, which allows you to define local fields and types. + +```graphql +# example client extension of the `Query` type +extend type Query { + client_field: String +} +``` + +These client-only fields are not sent to the server, and should be updated +using APIs for local updates, for example `commitPayload`. + +```js +const React = require('React'); + +const {graphql, useClientQuery} = require('react-relay'); + +function ClientQueryComponent() { + const data = useClientQuery( + graphql` + query ClientQueryComponentQuery { + client_field + } + `, + {}, // variables + ); + + return ( +
{data.client_field}
+ ); +} +``` + + +### Arguments + +* `query`: GraphQL query specified using a `graphql` template literal. +* `variables`: Object containing the variable values to fetch the query. These variables need to match GraphQL variables declared inside the query. + +### Return Value + +* `data`: Object that contains data which has been read out from the Relay store; the object matches the shape of specified query. + * The Flow type for data will also match this shape, and contain types derived from the GraphQL Schema. For example, the type of `data` above is: `{| user: ?{| name: ?string |} |}`. + +### Behavior + +* This hooks works as [`useLazyLoadQuery`](../use-lazy-load-query) with `fetchPolicy: store-only`, it does not send the network request. + + + diff --git a/website/versioned_docs/version-v19.0.0/api-reference/hooks/use-fragment.md b/website/versioned_docs/version-v19.0.0/api-reference/hooks/use-fragment.md new file mode 100644 index 0000000000000..da33250e03959 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/api-reference/hooks/use-fragment.md @@ -0,0 +1,69 @@ +--- +id: use-fragment +title: useFragment +slug: /api-reference/use-fragment/ +description: API reference for useFragment, a React hook used to read fragment data from the Relay store using a fragment reference +keywords: + - fragment + - read + - fragment reference +--- + +import DocsRating from '@site/src/core/DocsRating'; + +## `useFragment` + +```js +import type {UserComponent_user$key} from 'UserComponent_user.graphql'; + +const React = require('React'); + +const {graphql, useFragment} = require('react-relay'); + +type Props = { + user: UserComponent_user$key, +}; + +function UserComponent(props: Props) { + const data = useFragment( + graphql` + fragment UserComponent_user on User { + name + profile_picture(scale: 2) { + uri + } + } + `, + props.user, + ); + + return ( + <> +

{data.name}

+
+ +
+ + ); +} +``` + +### Arguments + +* `fragment`: GraphQL fragment specified using a `graphql` template literal. +* `fragmentReference`: The *fragment reference* is an opaque Relay object that Relay uses to read the data for the fragment from the store; more specifically, it contains information about which particular object instance the data should be read from. + * The type of the fragment reference can be imported from the generated Flow types, from the file `.graphql.js`, and can be used to declare the type of your `Props`. The name of the fragment reference type will be: `$key`. We use our [lint rule](https://github.com/relayjs/eslint-plugin-relay) to enforce that the type of the fragment reference prop is correctly declared. + +### Return Value + +* `data`: Object that contains data which has been read out from the Relay store; the object matches the shape of specified fragment. + * The Flow type for data will also match this shape, and contain types derived from the GraphQL Schema. For example, the type of `data` above is: `{ name: ?string, profile_picture: ?{ uri: ?string } }`. + +### Behavior + +* The component is automatically subscribed to updates to the fragment data: if the data for this particular `User` is updated anywhere in the app (e.g. via fetching new data, or mutating existing data), the component will automatically re-render with the latest updated data. +* The component will suspend if any data for that specific fragment is missing, and the data is currently being fetched by a parent query. + * For more details on Suspense, see our [Loading States with Suspense](../../guided-tour/rendering/loading-states) guide. + + + diff --git a/website/versioned_docs/version-v19.0.0/api-reference/hooks/use-lazy-load-query.md b/website/versioned_docs/version-v19.0.0/api-reference/hooks/use-lazy-load-query.md new file mode 100644 index 0000000000000..1a80e04b4a9a6 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/api-reference/hooks/use-lazy-load-query.md @@ -0,0 +1,77 @@ +--- +id: use-lazy-load-query +title: useLazyLoadQuery +slug: /api-reference/use-lazy-load-query/ +description: API reference for useLazyLoadQuery, a React hook used to lazily fetch query data when a component renders +keywords: + - lazy fetching + - query + - fetch +--- + +import DocsRating from '@site/src/core/DocsRating'; + +## `useLazyLoadQuery` + +Hook used to fetch a GraphQL query during render. This hook can trigger multiple nested or waterfalling round trips if used without caution, and waits until render to start a data fetch (when it can usually start a lot sooner than render), thereby degrading performance. Instead, prefer [`usePreloadedQuery`](../use-preloaded-query). + +```js +const React = require('React'); + +const {graphql, useLazyLoadQuery} = require('react-relay'); + +function App() { + const data = useLazyLoadQuery( + graphql` + query AppQuery($id: ID!) { + user(id: $id) { + name + } + } + `, + {id: 4}, + {fetchPolicy: 'store-or-network'}, + ); + + return

{data.user?.name}

; +} +``` + +### Arguments + +* `query`: GraphQL query specified using a `graphql` template literal. +* `variables`: Object containing the variable values to fetch the query. These variables need to match GraphQL variables declared inside the query. +* `options`: _*[Optional]*_ options object + * `fetchPolicy`: Determines if cached data should be used, and when to send a network request based on the cached data that is currently available in the Relay store (for more details, see our [Fetch Policies](../../guided-tour/reusing-cached-data/fetch-policies) and [Garbage Collection](../../guided-tour/reusing-cached-data/presence-of-data) guides): + * "store-or-network": _*(default)*_ *will* reuse locally cached data and will *only* send a network request if any data for the query is missing. If the query is fully cached, a network request will *not* be made. + * "store-and-network": *will* reuse locally cached data and will *always* send a network request, regardless of whether any data was missing from the local cache or not. + * "network-only": *will* *not* reuse locally cached data, and will *always* send a network request to fetch the query, ignoring any data that might be locally cached in Relay. + * "store-only": *will* *only* reuse locally cached data, and will *never* send a network request to fetch the query. In this case, the responsibility of fetching the query falls to the caller, but this policy could also be used to read and operate on data that is entirely [local](../../guided-tour/updating-data/local-data-updates). + * `fetchKey`: A `fetchKey` can be passed to force a re-evaluation of the current query and variables when the component re-renders, even if the variables didn't change, or even if the component isn't remounted (similarly to how passing a different `key` to a React component will cause it to remount). If the `fetchKey` is different from the one used in the previous render, the current query will be re-evaluated against the store, and it might be refetched depending on the current `fetchPolicy` and the state of the cache. + * `networkCacheConfig`: *_[Optional] _* Default value: `{force: true}`. Object containing cache config options for the *network layer*. Note that the network layer may contain an *additional* query response cache which will reuse network responses for identical queries. If you want to bypass this cache completely (which is the default behavior), pass `{force: true}` as the value for this option. + +### Return Value + +* `data`: Object that contains data which has been read out from the Relay store; the object matches the shape of specified query. + * The Flow type for data will also match this shape, and contain types derived from the GraphQL Schema. For example, the type of `data` above is: `{| user: ?{| name: ?string |} |}`. + +### Behavior + +* It is expected for `useLazyLoadQuery` to have been rendered under a [`RelayEnvironmentProvider`](../relay-environment-provider), in order to access the correct Relay environment, otherwise an error will be thrown. +* Calling `useLazyLoadQuery` will fetch and render the data for this query, and it may [*_suspend_*](../../guided-tour/rendering/loading-states) while the network request is in flight, depending on the specified `fetchPolicy`, and whether cached data is available, or if it needs to send and wait for a network request. If `useLazyLoadQuery` causes the component to suspend, you'll need to make sure that there's a `Suspense` ancestor wrapping this component in order to show the appropriate loading state. + * For more details on Suspense, see our [Loading States with Suspense](../../guided-tour/rendering/loading-states/) guide. +* The component is automatically subscribed to updates to the query data: if the data for this query is updated anywhere in the app, the component will automatically re-render with the latest updated data. +* After a component using `useLazyLoadQuery` has committed, re-rendering/updating the component will not cause the query to be fetched again. + * If the component is re-rendered with *different query variables,* that will cause the query to be fetched again with the new variables, and potentially re-render with different data. + * If the component *unmounts and remounts*, that will cause the current query and variables to be refetched (depending on the `fetchPolicy` and the state of the cache). + +### Differences with `QueryRenderer` + +* `useLazyLoadQuery` no longer takes a Relay environment as a parameter, and thus no longer sets the environment in React Context, like `QueryRenderer` did. Instead, `useLazyLoadQuery` should be used as a descendant of a [`RelayEnvironmentProvider`](../relay-environment-provider), which now sets the Relay environment in Context. Usually, you should render a single `RelayEnvironmentProvider` at the very root of the application, to set a single Relay environment for the whole application. +* `useLazyLoadQuery` will use [Suspense](../../guided-tour/rendering/loading-states) to allow developers to render loading states using Suspense boundaries, and will throw errors if network errors occur, which can be caught and rendered with Error Boundaries. This as opposed to providing error objects or null props to the `QueryRenderer` render function to indicate errors or loading states. +* `useLazyLoadQuery` fully supports fetch policies in order to reuse data that is cached in the Relay store instead of solely relying on the network response cache. +* `useLazyLoadQuery` has better type safety guarantees for the data it returns, which was not possible with QueryRenderer since we couldn't parametrize the type of the data with a renderer api. + + + + diff --git a/website/versioned_docs/version-v19.0.0/api-reference/hooks/use-mutation.md b/website/versioned_docs/version-v19.0.0/api-reference/hooks/use-mutation.md new file mode 100644 index 0000000000000..ac87cd519cfff --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/api-reference/hooks/use-mutation.md @@ -0,0 +1,94 @@ +--- +id: use-mutation +title: useMutation +slug: /api-reference/use-mutation/ +description: API reference for useMutation, a React hook used to execute a GraphQL mutation +keywords: + - mutation +--- + +import DocsRating from '@site/src/core/DocsRating'; +import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; +import FbUseMutationParameter from './fb/FbUseMutationParameter.md'; + +## `useMutation` + +Hook used to execute a mutation in a React component. + +```js +import type {FeedbackLikeMutation} from 'FeedbackLikeMutation.graphql'; +const React = require('React'); + +const {graphql, useMutation} = require('react-relay'); + +function LikeButton() { + const [commit, isInFlight] = useMutation(graphql` + mutation FeedbackLikeMutation($input: FeedbackLikeData!) { + feedback_like(data: $input) { + feedback { + id + viewer_does_like + like_count + } + } + } + `); + + if (isInFlight) { + return ; + } + + return ( + + + ); +} + +module.exports = FriendsList; +``` + +### Arguments + +* `fragment`: GraphQL fragment specified using a `graphql` template literal. + * This fragment must have an `@connection` directive on a connection field, otherwise using it will throw an error. + * This fragment must have a `@refetchable` directive, otherwise using it will throw an error. The `@refetchable` directive can only be added to fragments that are "refetchable", that is, on fragments that are declared on `Viewer` or `Query` types, or on a type that implements `Node` (i.e. a type that has an `id`). + * Note that you *do not* need to manually specify a pagination query yourself. The `@refetchable` directive will autogenerate a query with the specified `queryName`. This will also generate Flow types for the query, available to import from the generated file: `.graphql.js`. +* `fragmentReference`: The *fragment reference* is an opaque Relay object that Relay uses to read the data for the fragment from the store; more specifically, it contains information about which particular object instance the data should be read from. + * The type of the fragment reference can be imported from the generated Flow types, from the file `.graphql.js`, and can be used to declare the type of your `Props`. The name of the fragment reference type will be: `$key`. We use our [lint rule](https://github.com/relayjs/eslint-plugin-relay) to enforce that the type of the fragment reference prop is correctly declared. + +### Return Value + + + + + + + +Object containing the following properties: + +* `data`: Object that contains data which has been read out from the Relay store; the object matches the shape of specified fragment. + * The Flow type for data will also match this shape, and contain types derived from the GraphQL Schema. +* `isLoadingNext`: Boolean value which indicates if a pagination request for the *next* items in the connection is currently in flight, including any incremental data payloads. +* `isLoadingPrevious`: Boolean value which indicates if a pagination request for the *previous* items in the connection is currently in flight, including any incremental data payloads. +* `hasNext`: Boolean value which indicates if the end of the connection has been reached in the "forward" direction. It will be true if there are more items to query for available in that direction, or false otherwise. +* `hasPrevious`: Boolean value which indicates if the end of the connection has been reached in the "backward" direction. It will be true if there are more items to query for available in that direction, or false otherwise. +* `loadNext`: Function used to fetch more items in the connection in the "forward" direction. + * Arguments: + * `count`*:* Number that indicates how many items to query for in the pagination request. + * `options`: *_[Optional]_* options object + * `onComplete`: Function that will be called whenever the refetch request has completed, including any incremental data payloads. If an error occurs during the request, `onComplete` will be called with an `Error` object as the first parameter. + * Return Value: + * `disposable`: Object containing a `dispose` function. Calling `disposable.dispose()` will cancel the pagination request. + * Behavior: + * Calling `loadNext` *will not* cause the component to suspend. Instead, the `isLoadingNext` value will be set to true while the request is in flight, and the new items from the pagination request will be added to the connection, causing the component to re-render. + * Pagination requests initiated from calling `loadNext` will *always* use the same variables that were originally used to fetch the connection, *except* pagination variables (which need to change in order to perform pagination); changing variables other than the pagination variables during pagination doesn't make sense, since that'd mean we'd be querying for a different connection. +* `loadPrevious`: Function used to fetch more items in the connection in the "backward" direction. + * Arguments: + * `count`*:* Number that indicates how many items to query for in the pagination request. + * `options`: *_[Optional]_* options object + * `onComplete`: Function that will be called whenever the refetch request has completed, including any incremental data payloads. If an error occurs during the request, `onComplete` will be called with an `Error` object as the first parameter. + * Return Value: + * `disposable`: Object containing a `dispose` function. Calling `disposable.dispose()` will cancel the pagination request. + * Behavior: + * Calling `loadPrevious` *will not* cause the component to suspend. Instead, the `isLoadingPrevious` value will be set to true while the request is in flight, and the new items from the pagination request will be added to the connection, causing the component to re-render. + * Pagination requests initiated from calling `loadPrevious` will *always* use the same variables that were originally used to fetch the connection, *except* pagination variables (which need to change in order to perform pagination); changing variables other than the pagination variables during pagination doesn't make sense, since that'd mean we'd be querying for a different connection. +* `refetch`: Function used to refetch the connection fragment with a potentially new set of variables. + * Arguments: + * `variables`: Object containing the new set of variable values to be used to fetch the `@refetchable` query. + * These variables need to match GraphQL variables referenced inside the fragment. + * However, only the variables that are intended to change for the refetch request need to be specified; any variables referenced by the fragment that are omitted from this input will fall back to using the value specified in the original parent query. So for example, to refetch the fragment with the exact same variables as it was originally fetched, you can call `refetch({})`. + * Similarly, passing an `id` value for the `$id` variable is _*optional*_, unless the fragment wants to be refetched with a different `id`. When refetching a `@refetchable` fragment, Relay will already know the id of the rendered object. + * `options`: *_[Optional]_* options object + * `fetchPolicy`: Determines if cached data should be used, and when to send a network request based on cached data that is available. See the [Fetch Policies](../../guided-tour/reusing-cached-data/fetch-policies/) section for full specification. + * `onComplete`: Function that will be called whenever the refetch request has completed, including any incremental data payloads. + * Return value: + * `disposable`: Object containing a `dispose` function. Calling `disposable.dispose()` will cancel the refetch request. + * Behavior: + * Calling `refetch` with a new set of variables will fetch the fragment again *with the newly provided variables*. Note that the variables you need to provide are only the ones referenced inside the fragment. In this example, it means fetching the translated body of the currently rendered Comment, by passing a new value to the `lang` variable. + * Calling `refetch` will re-render your component and may cause it to *[suspend](../../guided-tour/rendering/loading-states)*, depending on the specified `fetchPolicy` and whether cached data is available or if it needs to send and wait for a network request. If refetch causes the component to suspend, you'll need to make sure that there's a `Suspense` boundary wrapping this component. + * For more details on Suspense, see our [Loading States with Suspense](../../guided-tour/rendering/loading-states/) guide. + + + +### Behavior + +* The component is automatically subscribed to updates to the fragment data: if the data for this particular `User` is updated anywhere in the app (e.g. via fetching new data, or mutating existing data), the component will automatically re-render with the latest updated data. +* The component will suspend if any data for that specific fragment is missing, and the data is currently being fetched by a parent query. + * For more details on Suspense, see our [Loading States with Suspense](../../guided-tour/rendering/loading-states/) guide. +* Note that pagination (`loadNext` or `loadPrevious`), *will not* cause the component to suspend. + +### Differences with `PaginationContainer` + +* A pagination query no longer needs to be specified in this api, since it will be automatically generated by Relay by using a `@refetchable` fragment. +* This api supports simultaneous bi-directional pagination out of the box. +* This api no longer requires passing a `getVariables` or `getFragmentVariables` configuration functions, like the `PaginationContainer` does. + * This implies that pagination no longer has a between `variables` and `fragmentVariables`, which were previously vaguely defined concepts. Pagination requests will always use the same variables that were originally used to fetch the connection, *except* pagination variables (which need to change in order to perform pagination); changing variables other than the pagination variables during pagination doesn't make sense, since that'd mean we'd be querying for a different connection. +* This api no longer takes additional configuration like `direction` or `getConnectionFromProps` function (like Pagination Container does). These values will be automatically determined by Relay. +* Refetching no longer has a distinction between `variables` and `fragmentVariables`, which were previously vaguely defined concepts. Refetching will always correctly refetch and render the fragment with the variables you provide (any variables omitted in the input will fallback to using the original values in the parent query). +* Refetching will unequivocally update the component, which was not always true when calling `refetchConnection` from `PaginationContainer` (it would depend on what you were querying for in the refetch query and if your fragment was defined on the right object type). + + + diff --git a/website/versioned_docs/version-v19.0.0/api-reference/hooks/use-prefetchable-forward-pagination-fragment.md b/website/versioned_docs/version-v19.0.0/api-reference/hooks/use-prefetchable-forward-pagination-fragment.md new file mode 100644 index 0000000000000..0cb81b1a6afb6 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/api-reference/hooks/use-prefetchable-forward-pagination-fragment.md @@ -0,0 +1,134 @@ +--- +id: use-prefetchable-forward-pagination-fragment +title: usePrefetchableForwardPaginationFragment +slug: /api-reference/use-prefetchable-forward-pagination-fragment/ +description: API reference for usePrefetchableForwardPaginationFragment, an experimental React hook used to paginate a connection and automatically prefetches +keywords: + - pagination + - connection + - prefetching +--- + +import DocsRating from '@site/src/core/DocsRating'; + +NOTE: This is an experimental API and may be subject to change. +`usePrefetchableForwardPaginationFragment` is similar to [`usePaginationFragment`](../use-pagination-fragment). It adds the capability to automatically prefetch a `bufferSize` number of items to fill the buffer without displaying the items. And when `loadNext` is called, it vends from the buffer first to achieve faster pagination. It only supports forward pagination (provides APIs for `loadNext`, `hasNext` and `isLoadingNext`) for now. + +```js +import type {FriendsList_user$key} from 'FriendsList_user.graphql'; + +const React = require('React'); + +const {graphql, usePrefetchableForwardPaginationFragment} = require('react-relay'); + +type Props = { + user: FriendsList_user$key, +}; + +function FriendsList(props: Props) { + const { + data, + edges, // NOTE: It is required to use `edges` to access the items + loadNext, + hasNext, + isLoadingNext, + refetch, // For refetching connection + } = usePrefetchableForwardPaginationFragment( + graphql` + fragment FriendsListComponent_user on User + @refetchable(queryName: "FriendsListPaginationQuery") { + name + friends(first: $count, after: $cursor) + @connection(key: "FriendsList_user_friends", prefetchable_pagination: true) { + edges { + node { + name + age + } + } + } + } + `, + props.user, + ); + + return ( + <> +

Friends of {data.name}:

+ + edge.node)}> + {node => { + return ( +
+ {node.name} - {node.age} +
+ ); + }} +
+ + + ); +} + +module.exports = FriendsList; +``` + +### Arguments + +* `fragment`: GraphQL fragment specified using a `graphql` template literal. + * This fragment must have an `@connection` directive on a connection field, and set `prefetchable_pagination` to `true`, otherwise using it will throw an error. + * This fragment must have a `@refetchable` directive, otherwise using it will throw an error. The `@refetchable` directive can only be added to fragments that are "refetchable", that is, on fragments that are declared on `Viewer` or `Query` types, or on a type that implements `Node` (i.e. a type that has an `id`). + * Note that you *do not* need to manually specify a pagination query yourself. The `@refetchable` directive will autogenerate a query with the specified `queryName`. This will also generate Flow types for the query, available to import from the generated file: `.graphql.js`. +* `fragmentReference`: The *fragment reference* is an opaque Relay object that Relay uses to read the data for the fragment from the store; more specifically, it contains information about which particular object instance the data should be read from. + * The type of the fragment reference can be imported from the generated Flow types, from the file `.graphql.js`, and can be used to declare the type of your `Props`. The name of the fragment reference type will be: `$key`. We use our [lint rule](https://github.com/relayjs/eslint-plugin-relay) to enforce that the type of the fragment reference prop is correctly declared. +* `bufferSize`: The size of the buffer. The component will always try to prefetch to fill the buffer. +* `initialSize`: *_[Optional]_* argument to define the initial number of items to display. If it is unset, the component shows all available items on the initial render or on refetch. +* `prefetchingLoadMoreOptions`: *_[Optional]_* fetching arguments to provide to the automatic prefetch. + * `options`: *_[Optional]_* options object + * `onComplete`: Function that will be called whenever the request has completed, including any incremental data payloads. If an error occurs during the request, `onComplete` will be called with an `Error` object as the first parameter. +* `minimumFetchSize`: Optional argument to define the minimum number of items to fetch in any requests. + +### Return Value + +Object containing the following properties: + +* `edges`: The edges to use. This provides a filtered list of edges in the connection that excludes the buffer. Do not use the connection edges from `data` to render otherwise the hook will not work correctly. +* `data`: Object that contains data which has been read out from the Relay store; the object matches the shape of specified fragment. + * The Flow type for data will also match this shape, and contain types derived from the GraphQL Schema. +* `isLoadingNext`: Boolean value which indicates if a pagination request for the *next* items in the connection is currently in flight, including any incremental data payloads. The value stays `false` if the hook is automatically prefetching to fill the buffer, and the code hasn't asked for more items. +* `hasNext`: Boolean value which indicates if the end of the connection has been reached in the "forward" direction. It will be true if there are more items to query for available in that direction, or there are more items in the buffer, or false otherwise. +* `loadNext`: Function used to fetch more items in the connection in the "forward" direction. + * Arguments: + * `count`*:* Number that indicates how many items to show more from buffer or fetch. + * `options`: *_[Optional]_* options object + * `onComplete`: Function that will be called whenever the request has completed, including any incremental data payloads. If an error occurs during the request, `onComplete` will be called with an `Error` object as the first parameter. + * Return Value: void + * Behavior: + * Calling `loadNext` *will not* cause the component to suspend. The component first try to fill the request using items prefetched in the buffer, if there isn't enough item, it sends a request. The `isLoadingNext` value will be set to true while the request is in flight. + * Pagination requests initiated from calling `loadNext` will *always* use the same variables that were originally used to fetch the connection, *except* pagination variables (which need to change in order to perform pagination); changing variables other than the pagination variables during pagination doesn't make sense, since that'd mean we'd be querying for a different connection. + +* `refetch`: Function used to refetch the connection fragment with a potentially new set of variables. + * Arguments: + * `variables`: Object containing the new set of variable values to be used to fetch the `@refetchable` query. + * These variables need to match GraphQL variables referenced inside the fragment. + * However, only the variables that are intended to change for the refetch request need to be specified; any variables referenced by the fragment that are omitted from this input will fall back to using the value specified in the original parent query. So for example, to refetch the fragment with the exact same variables as it was originally fetched, you can call `refetch({})`. + * Similarly, passing an `id` value for the `$id` variable is _*optional*_, unless the fragment wants to be refetched with a different `id`. When refetching a `@refetchable` fragment, Relay will already know the id of the rendered object. + * `options`: *_[Optional]_* options object + * `fetchPolicy`: Determines if cached data should be used, and when to send a network request based on cached data that is available. See the [Fetch Policies](../../guided-tour/reusing-cached-data/fetch-policies/) section for full specification. + * `onComplete`: Function that will be called whenever the refetch request has completed, including any incremental data payloads. + * Return value: + * `disposable`: Object containing a `dispose` function. Calling `disposable.dispose()` will cancel the refetch request. + * Behavior: + * Calling `refetch` with a new set of variables will fetch the fragment again *with the newly provided variables*. Note that the variables you need to provide are only the ones referenced inside the fragment. In this example, it means fetching the translated body of the currently rendered Comment, by passing a new value to the `lang` variable. + * Calling `refetch` will re-render your component and may cause it to *[suspend](../../guided-tour/rendering/loading-states)*, depending on the specified `fetchPolicy` and whether cached data is available or if it needs to send and wait for a network request. If refetch causes the component to suspend, you'll need to make sure that there's a `Suspense` boundary wrapping this component. + * For more details on Suspense, see our [Loading States with Suspense](../../guided-tour/rendering/loading-states/) guide. + +### Behavior + +* The component automatically fetches for more items to fill the buffer in `useEffect`. +* The component is automatically subscribed to updates to the fragment data: if the data for this particular `User` is updated anywhere in the app (e.g. via fetching new data, or mutating existing data), the component will automatically re-render with the latest updated data. +* The component will suspend if any data for that specific fragment is missing, and the data is currently being fetched by a parent query. + * For more details on Suspense, see our [Loading States with Suspense](../../guided-tour/rendering/loading-states/) guide. +* Note that pagination (`loadNext`), *will not* cause the component to suspend. + + diff --git a/website/versioned_docs/version-v19.0.0/api-reference/hooks/use-preloaded-query.md b/website/versioned_docs/version-v19.0.0/api-reference/hooks/use-preloaded-query.md new file mode 100644 index 0000000000000..57c0abcdd6780 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/api-reference/hooks/use-preloaded-query.md @@ -0,0 +1,84 @@ +--- +id: use-preloaded-query +title: usePreloadedQuery +slug: /api-reference/use-preloaded-query/ +description: API reference for usePreloadedQuery, a React hook used to read query data from the Relay store using a query reference +keywords: + - read + - query + - query reference +--- + +import DocsRating from '@site/src/core/DocsRating'; + +## `usePreloadedQuery` + +Hook used to access data fetched by an earlier call to [`loadQuery`](../load-query) or with the help of [`useQueryLoader`](../use-query-loader). This implements the "render-as-you-fetch" pattern: + +* Call the `loadQuery` callback returned from `useQueryLoader`. This will store a query reference in React state. + * You can also call the imported `loadQuery` directly, which returns a query reference. In that case, store the item in state or in a React ref, and call `dispose()` on the value when you are no longer using it. +* Then, in your render method, consume the query reference with `usePreloadedQuery()`. This call will suspend if the query is still pending, throw an error if it failed, and otherwise return the query results. +* This pattern is encouraged over `useLazyLoadQuery()` as it can allow fetching data earlier while not blocking rendering. + +For more information, see the [Rendering Queries](../../guided-tour/rendering/queries) guide. + +```js + +import type {AppQueryType} from 'AppQueryType.graphql'; + +const React = require('React'); + +const {graphql, useQueryLoader, usePreloadedQuery} = require('react-relay'); + +const AppQuery = graphql` + query AppQuery($id: ID!) { + user(id: $id) { + name + } + } +`; + +type Props = { + initialQueryRef: PreloadedQuery, +}; + +function NameLoader(props) { + const [queryReference, loadQuery] = useQueryLoader( + AppQuery, + props.initialQueryRef, /* e.g. provided by router */ + ); + + return (<> + + + {queryReference != null + ? + : null + } + + ); +} + +function NameDisplay({ queryReference }) { + const data = usePreloadedQuery(AppQuery, queryReference); + + return

{data.user?.name}

; +} +``` + +### Arguments + +* `query`: GraphQL query specified using a `graphql` template literal. +* `preloadedQueryReference`: A `PreloadedQuery` query reference, which can be acquired from [`useQueryLoader`](../use-query-loader) or by calling [`loadQuery()`](../load-query) . + +### Return Value + +* `data`: Object that contains data which has been read out from the Relay store; the object matches the shape of specified query. + * The Flow type for data will also match this shape, and contain types derived from the GraphQL Schema. For example, the type of `data` above is: `{ user: ?{ name: ?string } }`. + + diff --git a/website/versioned_docs/version-v19.0.0/api-reference/hooks/use-query-loader.md b/website/versioned_docs/version-v19.0.0/api-reference/hooks/use-query-loader.md new file mode 100644 index 0000000000000..908bbc356b871 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/api-reference/hooks/use-query-loader.md @@ -0,0 +1,95 @@ +--- +id: use-query-loader +title: useQueryLoader +slug: /api-reference/use-query-loader/ +description: API reference for useQueryLoader, a React hook used to imperatively fetch data for a query in response to a user event +keywords: + - query + - fetch + - preload + - render-as-you-fetch +--- + +import DocsRating from '@site/src/core/DocsRating'; + +## `useQueryLoader` + +Hook used to make it easy to safely load and retain queries. It will keep a query reference stored in state, and dispose of it when the component is disposed or it is no longer accessible via state. + +This hook is designed to be used with [`usePreloadedQuery`](../use-preloaded-query) to implement the "render-as-you-fetch" pattern. For more information, see the [Fetching Queries for Render](../../guided-tour/rendering/queries/) guide. + +```js +import type {PreloadedQuery} from 'react-relay'; + +const {useQueryLoader, usePreloadedQuery} = require('react-relay'); + +const AppQuery = graphql` + query AppQuery($id: ID!) { + user(id: $id) { + name + } + } +`; + +function QueryFetcherExample() { + const [ + queryReference, + loadQuery, + disposeQuery, + ] = useQueryLoader( + AppQuery, + ); + + if (queryReference == null) { + return ( + + ); + } + + return ( + <> + + + + + + ); +} + +function NameDisplay({ queryReference }) { + const data = usePreloadedQuery(AppQuery, queryReference); + + return

{data.user?.name}

; +} +``` + +### Arguments + +* `query`: GraphQL query specified using a `graphql` template literal. +* `initialQueryRef`: _*[Optional]*_ An initial `PreloadedQuery` to be used as the initial value of the `queryReference` stored in state and returned by `useQueryLoader`. + +### Return value + +A tuple containing the following values: + +* `queryReference`: the query reference, or `null`. +* `loadQuery`: a callback that, when executed, will load a query, which will be accessible as `queryReference`. If a previous query was loaded, it will dispose of it. It should not be called during React's render phase. + * Parameters + * `variables`: the variables with which the query is loaded. + * `options`: `LoadQueryOptions`. An optional options object, containing the following keys: + * `fetchPolicy`: _*[Optional]*_ Determines if cached data should be used, and when to send a network request based on the cached data that is currently available in the Relay store (for more details, see our [Fetch Policies](../../guided-tour/reusing-cached-data/fetch-policies) and [Garbage Collection](../../guided-tour/reusing-cached-data/presence-of-data) guides): + * "store-or-network": _*(default)*_ *will* reuse locally cached data and will *only* send a network request if any data for the query is missing. If the query is fully cached, a network request will *not* be made. + * "store-and-network": *will* reuse locally cached data and will *always* send a network request, regardless of whether any data was missing from the local cache or not. + * "network-only": *will* *not* reuse locally cached data, and will *always* send a network request to fetch the query, ignoring any data that might be locally cached in Relay. + * `networkCacheConfig`: *_[Optional]_* Default value: `{force: true}`. Object containing cache config options for the *network layer*. Note that the network layer may contain an *additional* query response cache which will reuse network responses for identical queries. If you want to bypass this cache completely (which is the default behavior), pass `{force: true}` as the value for this option. +* `disposeQuery`: a callback that, when executed, will set `queryReference` to `null` and call `.dispose()` on it. It has type `() => void`. It should not be called during React's render phase. + +### Behavior + +* The `loadQuery` callback will fetch data if passed a query, or data and the query if passed a preloadable concrete request. Once both the query and data are available, the data from the query will be written to the store. This differs from the behavior of `preloadQuery_DEPRECATED`, which would only write data to the store if the query was passed to `usePreloadedQuery`. +* This query reference will be retained by the Relay store, preventing the data from being garbage collected. Once `.dispose()` is called on the query reference, the data is liable to be garbage collected. +* The `loadQuery` callback should not be called during React's render phase. + + diff --git a/website/versioned_docs/version-v19.0.0/api-reference/hooks/use-refetchable-fragment.md b/website/versioned_docs/version-v19.0.0/api-reference/hooks/use-refetchable-fragment.md new file mode 100644 index 0000000000000..8b195431e7e93 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/api-reference/hooks/use-refetchable-fragment.md @@ -0,0 +1,122 @@ +--- +id: use-refetchable-fragment +title: useRefetchableFragment +slug: /api-reference/use-refetchable-fragment/ +description: API reference for useRefetchableFragment, a React hook used to refetch fragment data +keywords: + - refetch + - fragment +--- + +import DocsRating from '@site/src/core/DocsRating'; +import {FbInternalOnly, OssOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; +import FbUseRefetchableFragmentApiReferenceCodeExample from './fb/FbUseRefetchableFragmentApiReferenceCodeExample.md'; +import FbUseRefetchableFragmentReturnValue from './fb/FbUseRefetchableFragmentReturnValue.md'; + +## `useRefetchableFragment` + +You can use `useRefetchableFragment` when you want to fetch and re-render a fragment with different data: + + + + + + + +```js +import type {CommentBody_comment$key} from 'CommentBody_comment.graphql'; + +const React = require('React'); + +const {graphql, useRefetchableFragment} = require('react-relay'); + + +type Props = { + comment: CommentBody_comment$key, +}; + +function CommentBody(props: Props) { + const [data, refetch] = useRefetchableFragment( + graphql` + fragment CommentBody_comment on Comment + @refetchable(queryName: "CommentBodyRefetchQuery") { + body(lang: $lang) { + text + } + } + `, + props.comment, + ); + + return ( + <> +

{data.body?.text}

+ + + ); +} + +module.exports = CommentBody; +``` + +
+ +### Arguments + +* `fragment`: GraphQL fragment specified using a `graphql` template literal. This fragment must have a `@refetchable` directive, otherwise using it will throw an error. The `@refetchable` directive can only be added to fragments that are "refetchable", that is, on fragments that are declared on `Viewer` or `Query` types, or on a type that implements `Node` (i.e. a type that has an `id` and has the capability to be queryed by its `id` field. See [graphql server specification section](../../guides/graphql-server-specification) for more details). + * Note that you *do not* need to manually specify a refetch query yourself. The `@refetchable` directive will autogenerate a query with the specified `queryName`. This will also generate Flow types for the query, available to import from the generated file: `.graphql.js`. +* `fragmentReference`: The *fragment reference* is an opaque Relay object that Relay uses to read the data for the fragment from the store; more specifically, it contains information about which particular object instance the data should be read from. + * The type of the fragment reference can be imported from the generated Flow types, from the file `.graphql.js`, and can be used to declare the type of your `Props`. The name of the fragment reference type will be: `$key`. We use our [lint rule](https://github.com/relayjs/eslint-plugin-relay) to enforce that the type of the fragment reference prop is correctly declared. + +### Return Value + + + + + + + +Tuple containing the following values + +* [0] `data`: Object that contains data which has been read out from the Relay store; the object matches the shape of specified fragment. + * The Flow type for data will also match this shape, and contain types derived from the GraphQL Schema. +* [1] `refetch`: Function used to refetch the fragment with a potentially new set of variables. + * Arguments: + * `variables`: Object containing the new set of variable values to be used to fetch the `@refetchable` query. + * These variables need to match GraphQL variables referenced inside the fragment. + * If the fragment key passed to `useRefetchableFragment` is optional then all non-optional variables must be passed including, potentially, the object's ID since Relay may not have any existing variables to reuse. + * If the fragment key is non-optional, only the variables that are intended to change for the refetch request need to be specified; any variables referenced by the fragment that are omitted from this input will fall back to using the value specified in the original parent query. So for example, to refetch the fragment with the exact same variables as it was originally fetched, you can call `refetch({})`. + * Similarly, if the fragment key is non-optional, passing an `id` value for the `$id` variable is _*optional*_, unless the fragment wants to be refetched with a different `id`. When refetching a non-nullable `@refetchable` fragment, Relay will already know the id of the rendered object. + * `options`: *_[Optional]_* options object + * `fetchPolicy`: Determines if cached data should be used, and when to send a network request based on cached data that is available. See the [Fetch Policies](../../guided-tour/reusing-cached-data/fetch-policies/) section for full specification. + * `onComplete`: Function that will be called whenever the refetch request has completed, including any incremental data payloads. + * Return value: + * `disposable`: Object containing a `dispose` function. Calling `disposable.dispose()` will cancel the refetch request. + * Behavior: + * Calling `refetch` with a new set of variables will fetch the fragment again *with the newly provided variables*. Note that the variables you need to provide are only the ones referenced inside the fragment. In this example, it means fetching the translated body of the currently rendered Comment, by passing a new value to the `lang` variable. + * Calling `refetch` will re-render your component and may cause it to _*[suspend](../../guided-tour/rendering/loading-states)*_, depending on the specified `fetchPolicy` and whether cached data is available or if it needs to send and wait for a network request. If refetch causes the component to suspend, you'll need to make sure that there's a `Suspense` boundary wrapping this component. + * For more details on Suspense, see our [Loading States with Suspense](../../guided-tour/rendering/loading-states/) guide. + + + +### Behavior + +* The component is automatically subscribed to updates to the fragment data: if the data for this particular `User` is updated anywhere in the app (e.g. via fetching new data, or mutating existing data), the component will automatically re-render with the latest updated data. +* The component will suspend if any data for that specific fragment is missing, and the data is currently being fetched by a parent query. + * For more details on Suspense, see our [Loading States with Suspense](../../guided-tour/rendering/loading-states/) guide. + +### Differences with `RefetchContainer` + +* A refetch query no longer needs to be specified in this api, since it will be automatically generated by Relay by using a `@refetchable` fragment. +* Refetching no longer has a distinction between `refetchVariables` and `renderVariables`, which were previously vaguely defined concepts. Refetching will always correctly refetch and render the fragment with the variables you provide (any variables omitted in the input will fallback to using the original values from the parent query). +* Refetching will unequivocally update the component, which was not always true when calling refetch from `RefetchContainer` (it would depend on what you were querying for in the refetch query and if your fragment was defined on the right object type). + + + + diff --git a/website/versioned_docs/version-v19.0.0/api-reference/hooks/use-relay-environment.md b/website/versioned_docs/version-v19.0.0/api-reference/hooks/use-relay-environment.md new file mode 100644 index 0000000000000..098764d7ed419 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/api-reference/hooks/use-relay-environment.md @@ -0,0 +1,37 @@ +--- +id: use-relay-environment +title: useRelayEnvironment +slug: /api-reference/use-relay-environment/ +description: API reference for useRelayEnvironment, a React hook used to access the Relay environment from context +keywords: + - environment + - context +--- + +import DocsRating from '@site/src/core/DocsRating'; + +## `useRelayEnvironment` + +Hook used to access a Relay environment that was set by a [`RelayEnvironmentProvider`](../relay-environment-provider): + +```js +const React = require('React'); + +const {useRelayEnvironment} = require('react-relay'); + +function MyComponent() { + const environment = useRelayEnvironment(); + + const handler = useCallback(() => { + // For example, can be used to pass the environment to functions + // that require a Relay environment. + commitMutation(environment, ...); + }, [environment]) + + return (...); +} + +module.exports = MyComponent; +``` + + diff --git a/website/versioned_docs/version-v19.0.0/api-reference/hooks/use-subscription.md b/website/versioned_docs/version-v19.0.0/api-reference/hooks/use-subscription.md new file mode 100644 index 0000000000000..01a4822232488 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/api-reference/hooks/use-subscription.md @@ -0,0 +1,66 @@ +--- +id: use-subscription +title: useSubscription +slug: /api-reference/use-subscription/ +description: API reference for useSubscription, a React hook used to subscribe and unsubscribe from a subscription +keywords: + - subscription +--- + +import DocsRating from '@site/src/core/DocsRating'; +import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; +import GraphQLSubscriptionConfig from '../types/GraphQLSubscriptionConfig.md'; + +## `useSubscription` + +Hook used to subscribe and unsubscribe to a subscription. + +```js +import {graphql, useSubscription} from 'react-relay'; +import {useMemo} from 'react'; + +const subscription = graphql` + subscription UserDataSubscription($input: InputData!) { + # ... + } +`; + +function UserComponent({ id }) { + // IMPORTANT: your config should be memoized. + // Otherwise, useSubscription will re-render too frequently. + const config = useMemo(() => ({ + variables: {id}, + subscription, + }), [id, subscription]); + + useSubscription(config); + + return (/* ... */); +} +``` + +### Arguments + +* `config`: a config of type [`GraphQLSubscriptionConfig`](#type-graphqlsubscriptionconfigtsubscriptionpayload) passed to [`requestSubscription`](../request-subscription/) +* `requestSubscriptionFn`: `?(IEnvironment, GraphQLSubscriptionConfig) => Disposable`. An optional function with the same signature as [`requestSubscription`](../request-subscription/), which will be called in its stead. Defaults to `requestSubscription`. + + + +### Behavior + +* This is only a thin wrapper around the `requestSubscription` API. It will: + * Subscribe when the component is mounted with the given config + * Unsubscribe when the component is unmounted + * Unsubscribe and resubscribe with new values if the environment, config or `requestSubscriptionFn` changes. +* If you have the need to do something more complicated, such as imperatively requesting a subscription, please use the [`requestSubscription`](../request-subscription/) API directly. +* See the [GraphQL Subscriptions Guide](../../guided-tour/updating-data/graphql-subscriptions/) for a more detailed explanation of how to work with subscriptions. + + + +:::note +`useSubscription` doesn't automatically add `client_subscription_id`. You may need to provide an arbitrary `client_subscription_id` to `config.variables.input` +::: + + + + diff --git a/website/versioned_docs/version-v19.0.0/api-reference/relay-resolvers/docblock-format.md b/website/versioned_docs/version-v19.0.0/api-reference/relay-resolvers/docblock-format.md new file mode 100644 index 0000000000000..e48eb2653606a --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/api-reference/relay-resolvers/docblock-format.md @@ -0,0 +1,321 @@ +--- +id: docblock-format +title: 'Docblock Format' +slug: /api-reference/relay-resolvers/docblock-format/ +description: Docblock format for Relay Resolvers +--- +import {FbInternalOnly, fbContent} from 'docusaurus-plugin-internaldocs-fb/internal'; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +Relay Resolvers allow you to define additional types and fields in your GraphQL schema that are backed by client-side data. To achieve this, the Relay compiler looks for special `@RelayResolver` docblocks in your code. These docblocks define the types and fields in your schema and also tell Relay where to find the resolver functions that implement them. + +For an overview of Relay Resolvers and how to think about them, see the [Relay Resolvers](../../guides/relay-resolvers/introduction.md) guide. This page documents the different docblock tags that the Relay compiler looks for, and how to use them. + +:::note The Relay compiler only looks at docblocks which include the +`@RelayResolver` tag. Any other docblocks will be ignored. +::: + +## `@RelayResolver TypeName` + +The `@RelayResolver` tag followed by a single name defines a new GraphQL type in your schema. By default it is expected to be followed by an exported function whose name matches the type name. The function should accept an ID as its sole argument and return the JavaScript model/object which is the backing data for the type. See [`@weak`](#weak) for an alternative way to define the backing data for a type. + + + + + ```tsx + /** + * @RelayResolver User + */ + export function User(id): UserModel { + return UserModel.getById(id); + } + ``` + + + + ```tsx + /** + * @RelayResolver + */ + export function User(id): UserModel { + return UserModel.getById(id); + } + ``` + + + + + +See the [Defining Types](../../guides/relay-resolvers/defining-types.md) guide for more information. + +## `@RelayResolver TypeName.fieldName: FieldTypeName` + +If the typename in a `@RelayResolver` tag is followed by a dot and then a field definition, it defines a new field on the type. The portion following the `.` is expected to follow GraphQL's +[schema definition language](https://spec.graphql.org/June2018/#FieldDefinition). + +Field definitions are expected to be followed by an exported function whose name matches the field name. The function should accept the model/object returned by the type resolver as its sole argument and return the value of the field. + + + + + ```tsx + /** + * @RelayResolver User.name: String + */ + export function name(user: UserModel): string { + return user.name; + } + ``` + + + + ```tsx + /** + * @RelayResolver + */ + export function name(user: UserModel): string { + return user.name; + } + ``` + + + +See the [Defining Fields](../../guides/relay-resolvers/defining-fields.md) guide for more information. + +## `@rootFragment` + +Relay Resolvers may also be used to model data that is derived from other data in the graph. These fields will be automatically recomputed by Relay when the data they depend on changes. + +To define a derived field, use the `@rootFragment` tag on an existing field +definition, and follow it with the name of a fragment that defines the data that the field depends on. The resolver function for the field will be passed a fragment key which can be used to read the fragment data using `readFragment()`. + + + + + ```tsx + import {readFragment} from 'relay-runtime'; + + /** + * @RelayResolver User.fullName: String + * @rootFragment UserFullNameFragment + */ + export function fullName(key: UserFullNameFragment$key): string { + const user = readFragment( + graphql` + fragment UserFullNameFragment on User { + firstName + lastName + } + `, + key, + ); + return `${user.firstName} ${user.lastName}`; + } + ``` + + + + ```tsx + import {readFragment} from 'relay-runtime'; + + /** + * @RelayResolver + */ + export function fullName(key: UserFullNameFragment$key): string { + const user = readFragment( + graphql` + fragment UserFullNameFragment on User { + firstName + lastName + } + `, + key, + ); + return `${user.firstName} ${user.lastName}`; + } + ``` + + + +See [Derived Fields](../../guides/relay-resolvers/derived-fields.md) for more information. + +## `@live` + +When modeling client state that can change over time, a resolver function which returns a single value is not sufficient. To accommodate this, Relay Resolvers allow you to define a field that returns a stream of values over time. This is done by adding the `@live` tag to a _field or type definition_. + +`@live` resolvers must return an object with the shape of a `LiveStateValue` to allow Relay to read the current value and subscribe to changes. + + + + + ```tsx + import type {LiveState} from 'relay-runtime'; + + /** + * @RelayResolver Query.counter: Int + * @live + */ + export function counter(): LiveState { + return { + read: () => store.getState().counter, + subscribe: cb => { + return store.subscribe(cb); + }, + }; + } + ``` + + + + ```tsx + import type {LiveState} from 'relay-runtime'; + + /** + * @RelayResolver + */ + export function counter(): LiveState { + return { + read: () => store.getState().counter, + subscribe: cb => { + return store.subscribe(cb); + }, + }; + } + ``` + + + + +See the [Live Fields](../../guides/relay-resolvers/live-fields.md) guide for +more information. + +## `@weak` + +By default, Relay Resolvers expect the backing data for a type to be returned by a resolver function. However, in some cases objects of a given type may not have identifiers. In this case, you can use the `@RelayResolver TypeName` syntax described above followed by the tag `@weak` to define a "weak" type. + +Weak type declarations are expected to be followed by an exported type +definition whose name matches the type name. + + + + + ```tsx + /** + * @RelayResolver ProfilePicture + * @weak + */ + export type ProfilePicture = { + url: string; + width: number; + height: number; + }; + ``` + + + + ```tsx + /** + * @RelayResolver + */ + export type ProfilePicture = { + url: string; + width: number; + height: number; + }; + ``` + + + +See the [Weak Types](../../guides/relay-resolvers/defining-types.md#Defining a “weak” type) guide for more information including how to define an edge to a weak type. + +## `@deprecated` + +Just like the GraphQL schema definition language, Relay Resolvers support the `@deprecated` tag to mark a field as deprecated. The tag may be followed by a string which will be used as the deprecation reason. Deprecated fields will +receive special treatment in the editor if you are using the +[Relay VSCode extension](../../editor-support.md). + +```tsx +/** + * @RelayResolver User.name: String + * @deprecated Use `fullName` instead. + */ +export function name(user: UserModel): string { + return user.name; +} +``` + +See the [Deprecated](../../guides/relay-resolvers/deprecated.md) guide for more information. + +## Descriptions + +Any free text in the docblock (text not following a tag) will be used as the description for the type or field. This description will be surfaced in the editor if you are using the [Relay VSCode extension](../../editor-support.md). + +```tsx +/** + * @RelayResolver User.name: String + * + * What's in a name? That which we call a rose by any other name would smell + * just as sweet. + */ +export function name(user: UserModel): string { + return user.name; +} +``` + +See the [Descriptions](../../guides/relay-resolvers/descriptions.md) guide for more information. diff --git a/website/versioned_docs/version-v19.0.0/api-reference/relay-resolvers/runtime-functions.md b/website/versioned_docs/version-v19.0.0/api-reference/relay-resolvers/runtime-functions.md new file mode 100644 index 0000000000000..cd526eaaeb450 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/api-reference/relay-resolvers/runtime-functions.md @@ -0,0 +1,94 @@ +--- +id: runtime-functions +title: "Runtime Functions" +slug: /api-reference/relay-resolvers/runtime-functions/ +description: Runtime functions associated with Relay Resolvers +--- + +This page documents the runtime functions associated with Relay Resolvers. For an overview of Relay Resolvers and how to think about them, see the [Relay Resolvers](../../guides/relay-resolvers/introduction.md) guide. + +## RelayModernStore + + +RelayModernStore exposes `batchLiveStateUpdates()`. See [Live Fields](../../guides/relay-resolvers/live-fields.md#batching) for more details of how to use this method. + +## `readFragment()` + +Derived resolver fields model data that is derived from other data in the graph. To read the data that a derived field depends on, they must use the `readFragment()` function which is exported from `relay-runtime`. This function accepts a GraphQL fragment and a fragment key, and returns the data for the fragment. + +:::warning +`readFragment()` may only be used in Relay Resolvers. It will throw an error if used in any other context. +::: + +```tsx +import {readFragment} from "relay-runtime"; + +/** + * @RelayResolver User.fullName: String + * @rootFragment UserFullNameFragment + */ +export function fullName(key: UserFullNameFragment$key): string { + const user = readFragment(graphql` + fragment UserFullNameFragment on User { + firstName + lastName + } + `, key); + return `${user.firstName} ${user.lastName}`; +} +``` + +Note that Relay will ensure your field resolver is recomputed any time data in that fragment changes. + +See the [Derived Fields](../../guides/relay-resolvers/derived-fields.md) guide for more information. + +## `suspenseSentinel()` + +Live resolvers model client state that can change over time. If at some point during that field's lifecycle, the data being read is in a pending state, for example if the data is being fetched from an API, the resolver may return the `suspenseSentinel()` to indicate that the data is not yet available. + +Relay expects that when the data is available, the `LiveStateValue` will notify Relay by calling the subscribe callback. + +```tsx +import {suspenseSentinel} from 'relay-runtime'; + +/** + * @RelayResolver Query.myIp: String + * @live + */ +export function myIp(): LiveState { + return { + read: () => { + const state = store.getState(); + const ipLoadObject = state.ip; + if (ipLoadObject.status === "LOADING") { + return suspenseSentinel(); + } + return state.ip; + }, + subscribe: (callback) => { + return store.subscribe(callback); + }, + }; +} +``` + +See the [Live Fields](../../guides/relay-resolvers/live-fields.md) guide for more information. + +## `useClientQuery()` + +If a query contains only client fields, it may not currently be used with hooks like `usePreloadedQuery` and `useLazyLoadQuery` since both of those hooks assume they will need to issue a network request. If you attempt to use these APIs in Flow you will get a type error. + +Instead, for client-only queries, you can use the `useClientQuery` hook: + +```tsx +import {useClientQuery} from 'react-relay'; + +export function MyComponent() { + const data = useClientQuery(graphql` + query MyQuery { + myIp + } + `); + return
{data.myIp}
; +} +``` diff --git a/website/versioned_docs/version-v19.0.0/api-reference/relay-runtime/commit-mutation.md b/website/versioned_docs/version-v19.0.0/api-reference/relay-runtime/commit-mutation.md new file mode 100644 index 0000000000000..3eec1a6093c2d --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/api-reference/relay-runtime/commit-mutation.md @@ -0,0 +1,65 @@ +--- +id: commit-mutation +title: commitMutation +slug: /api-reference/commit-mutation/ +description: API reference for commitMutation, which imperatively executes a mutation +keywords: + - mutation +--- + +import DocsRating from '@site/src/core/DocsRating'; +import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; +import MutationConfig from '../types/MutationConfig.md'; +import Disposable from '../types/Disposable.md'; + +## `commitMutation` + +Imperatively execute a mutation. + +See also the [`useMutation`](../use-mutation/) API and [Guide to Updating Data](../../guided-tour/updating-data/introduction.md). + +```js +import type {FeedbackLikeMutation} from 'FeedbackLikeMutation.graphql'; +const React = require('React'); + +const {graphql, commitMutation} = require('react-relay'); + +function likeFeedback(environment: IEnvironment): Disposable { + return commitMutation(environment, { + mutation: graphql` + mutation FeedbackLikeMutation($input: FeedbackLikeData!) { + feedback_like(data: $input) { + feedback { + id + viewer_does_like + like_count + } + } + } + `, + variables: { + input: { + id: '123', + }, + }, + }); +} +``` + +### Arguments + +* `environment`: `IEnvironment`. A Relay environment. +* `config`: [`MutationConfig`](#type-mutationconfigtmutationconfig-mutationparameters). + + + + +### Return Value + +* A [`Disposable`](#interface-disposable) which: + * If called while before the request completes, will cancel revert any optimistic updates and prevent the `onComplete` and `onError` callbacks from being executed. It will not necessarily cancel any network request. Will cause the `onUnsubscribe` callback to be called. + * If called after the initial request completes, will do nothing. + + + + diff --git a/website/versioned_docs/version-v19.0.0/api-reference/relay-runtime/fetch-query.md b/website/versioned_docs/version-v19.0.0/api-reference/relay-runtime/fetch-query.md new file mode 100644 index 0000000000000..5ebcc811faa59 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/api-reference/relay-runtime/fetch-query.md @@ -0,0 +1,113 @@ +--- +id: fetch-query +title: fetchQuery +slug: /api-reference/fetch-query/ +description: API reference for fetchQuery, which imperatively fetches data for a query and returns an observable +keywords: + - observable + - query + - fetch +--- + +import DocsRating from '@site/src/core/DocsRating'; +import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; + +## `fetchQuery` + +If you want to fetch a query outside of React, you can use the `fetchQuery` function from `react-relay`: + +```js +// You should prefer passing an environment that was returned from useRelayEnvironment() +const MyEnvironment = require('MyEnvironment'); +const {fetchQuery} = require('react-relay'); + +fetchQuery( + environment, + graphql` + query AppQuery($id: ID!) { + user(id: $id) { + name + } + } + `, + {id: 4}, +) +.subscribe({ + start: () => {...}, + complete: () => {...}, + error: (error) => {...}, + next: (data) => {...} +}); +``` + +### Arguments + +* `environment`: A Relay Environment instance to execute the request on. If you're starting this request somewhere within a React component, you probably want to use the environment you obtain from using [`useRelayEnvironment`](../use-relay-environment/). +* `query`: GraphQL query to fetch, specified using a `graphql` template literal. +* `variables`: Object containing the variable values to fetch the query. These variables need to match GraphQL variables declared inside the query. +* `options`: *_[Optional]_* options object + * `networkCacheConfig`: *_[Optional]_ *Object containing cache config options + * `force`: Boolean value. If true, will bypass the network response cache. Defaults to true. + +### Flow Type Parameters + +* `TQuery`: Type parameter that should correspond to the Flow type for the specified query. This type is available to import from the the auto-generated file: `.graphql.js`. It will ensure that the type of the data provided by the observable matches the shape of the query, and enforces that the `variables` passed as input to `fetchQuery` match the type of the variables expected by the query. + +### Return Value + +* `observable`: Returns an observable instance. To start the request, `subscribe` or `toPromise` must be called on the observable. Exposes the following methods: + * `subscribe`: Function that can be called to subscribe to the observable for the network request. Keep in mind that this subscribes you only to the fetching of the query, not to any subsequent changes to the data within the Relay Store. + * Arguments: + * `observer`: Object that specifies observer functions for different events occurring on the network request observable. May specify the following event handlers as keys in the observer object: + * `start`: Function that will be called when the network requests starts. It will receive a single `subscription` argument, which represents the subscription on the network observable. + * `complete`: Function that will be called if and when the network request completes successfully. + * `next`: Function that will be called every time a payload is received from the network. It will receive a single `data` argument, which represents a snapshot of the query data read from the Relay store at the moment a payload was received from the server. + * `error`: Function that will be called if an error occurs during the network request. It will receive a single `error` argument, containing the error that occurred. + * `unsubscribe`: Function that will be called whenever the subscription is unsubscribed. It will receive a single `subscription` argument, which represents the subscription on the network observable. + * Return Value: + * `subscription`: Object representing a subscription to the observable. Calling `subscription.unsubscribe()` will cancel the network request. + * `toPromise`: + * Return Value: + * `promise`: Returns a promise that will resolve when the first network response is received from the server. If the request fails, the promise will reject. Cannot be cancelled. + + + +:::info +The `next` function may be called multiple times when using Relay's [Incremental Data Delivery](../../guides/incremental-data-delivery/) capabilities to receive multiple payloads from the server. +::: + + + +### Behavior + +* `fetchQuery` will automatically save the fetched data to the in-memory Relay store, and notify any components subscribed to the relevant data. +* `fetchQuery` will **NOT** retain the data for the query, meaning that it is not guaranteed that the data will remain saved in the Relay store at any point after the request completes. If you wish to make sure that the data is retained outside of the scope of the request, you need to call `environment.retain()` directly on the query to ensure it doesn't get deleted. See our section on [Controlling Relay's GC Policy](../../guided-tour/reusing-cached-data/presence-of-data) for more details. +* `fetchQuery` will automatically de-dupe identical network requests (same query and variables) that are in flight at the same time, and that were initiated with `fetchQuery`. + + +### Behavior with `.toPromise()` + +If desired, you can convert the request into a Promise using `**.toPromise()**`. Note that toPromise will start the query and return a Promise that will resolve when the *first* piece of data returns from the server and *cancel further processing*. That means any deferred or 3D data in the query may not be processed. **We generally recommend against using toPromise() for this reason.** + +```js +const {fetchQuery} = require('react-relay'); + +fetchQuery( + environment, + graphql` + query AppQuery($id: ID!) { + user(id: $id) { + name + } + } + `, + {id: 4}, +) +.toPromise() // NOTE: don't use, this can cause data to be missing! +.then(data => {...}) +.catch(error => {...}; +``` + +* `toPromise` Returns a promise that will resolve when the first network response is received from the server. If the request fails, the promise will reject. Cannot be cancelled. + + diff --git a/website/versioned_docs/version-v19.0.0/api-reference/relay-runtime/field-logger.md b/website/versioned_docs/version-v19.0.0/api-reference/relay-runtime/field-logger.md new file mode 100644 index 0000000000000..87f0f9d68e284 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/api-reference/relay-runtime/field-logger.md @@ -0,0 +1,170 @@ +--- +id: field-logger +title: relayFieldLogger +slug: /api-reference/field-logger/ +description: API reference for the `relayFieldLogger` Environment config option +keywords: + - environment + - logging + - error +--- + +Relay includes a number of features that allow for granular handling of field errors: + +- [`@required`](../../guides/required-directive.md) provides declarative handling of field nullability +- [`@catch`](../../guides/catch-directive.md) allows you to explicitly handle field errors +- [`@throwOnFieldError`](../../guides/throw-on-field-error-directive.md) lets you treat field errors as exceptions +- [Relay Resolvers](../../guides/relay-resolvers/introduction.md) coerce thrown exceptions to null, matching the GraphQL spec + +In each of these cases, field errors are handled by Relay. However, it can still be important to track that these errors are occurring in your app, and monitor or resolve them. To enable this, the Relay Environment can be configured with a `relayFieldLogger`. This logger is a function which is called with events each time Relay handles a field-level error. + +Providing a field logger looks something like this: +```ts +import {Environment} from "relay-runtime"; + +const environment = new Environment({ + relayFieldLogger: (event) => { + switch(event.kind) { + case "missing_expected_data.log": + // ... + break; + // ... handle other events + } + }, + network: // ... + store: // ... +}); +``` + +## Event Types + +The Field Logger currently can receive the following events: + +## Missing Expected Data Log + +Data which Relay expected to be in the store (because it was requested by the parent query/mutation/subscription) was missing. This can happen due to graph relationship changes observed by other queries/mutations, or imperative updates that don't provide all needed data. + +See [Graph Relationship Changes](../../debugging/why-null/#graph-relationship-change). + +In this case Relay will render with the referenced field as `undefined`. + +:::note +This may break with the type contract of Relay's generated types. +::: + +To turn this into a hard error for a given fragment/query, you can use [`@throwOnFieldError`](../../guides/throw-on-field-error-directive/) + +```ts +export type MissingExpectedDataLogEvent = { + +kind: 'missing_expected_data.log', + +owner: string, + +fieldPath: string, +}; +``` + +## Missing Expected Data Throw + +Data which Relay expected to be in the store (because it was requested by the parent query/mutation/subscription) was missing. This can happen due to graph relationship changes observed by other queries/mutations, or imperative updates that don't provide all needed data. + +See [Graph Relationship Changes](../../debugging/why-null/#graph-relationship-change). + + +This event is called `.throw` because the missing data was encountered in a + query/fragment/mutation with [`@throwOnFieldError`](../../guides/throw-on-field-error-directive/) + +Relay will throw immediately after logging this event. If you wish to +customize the error being thrown, you may throw your own error. + +:::note +Only throw on this event if `handled` is false. Errors that have been handled by a `@catch` directive or by making a resolver null will have `handled: true` and should not trigger a throw. +::: + +```ts +export type MissingExpectedDataThrowEvent = { + +kind: 'missing_expected_data.throw', + +owner: string, + +fieldPath: string, + +handled: boolean, +}; +``` + +The `fieldPath` property is a `.` separated string containing the path from the query/mutation/fragment root to the field which was missing. Note that there are some cases where we are missing data that is not strictly a field. In those cases the path will end with one of these segments: + +1. `path.to.` An edge points to an ID that is not present in the Relay store +2. `path.to.` A virtual field inserted by the Relay compiler to detect if a type condition holds is missing from the Relay store + +## Missing Required Field Log + +A field was marked as [@required(action: LOG)](../../guides/required-directive.md#action) but was null or missing in the store. + +```ts +export type MissingRequiredFieldLogEvent = { + +kind: 'missing_required_field.log', + +owner: string, + +fieldPath: string, +}; +``` + +## Missing Required Field Throw + +A field was marked as [@required(action: THROW)](../../guides/required-directive.md#action) but was null or missing in the* store. + +Relay will throw immediately after logging this event. If you wish to customize the error being thrown, you may throw your own error. + +:::note +Only throw on this event if `handled` is false. Errors that have been +handled by a `@catch` directive or by making a resolver null will have +`handled: true` and should not trigger a throw. +::: + +```ts +export type MissingRequiredFieldThrowEvent = { + +kind: 'missing_required_field.throw', + +owner: string, + +fieldPath: string, + +handled: boolean, +}; +``` + +## Relay Resolver Error + + +A [Relay Resolver](../../guides/relay-resolvers/introduction.md) that is currently being read threw a JavaScript error when it was last evaluated. By default, the value has been coerced to null and passed to the product code. + +If [`@throwOnFieldError`](../../guides/throw-on-field-error-directive.md) was used on the parent query/fragment/mutation, you will also receive a runtime exception when the field is read. + +:::note +Only throw on this event if `handled` is false. Errors that have been handled by a `@catch` directive or by making a resolver null will have `handled: true` and should not trigger a throw. + +```ts +export type RelayResolverErrorEvent = { + +kind: 'relay_resolver.error', + +owner: string, + +fieldPath: string, + +error: Error, + +shouldThrow: boolean, + +handled: boolean, +}; +``` + +## GraphQL Payload Field Error + + +A field being read by Relay was marked as being in an error state by the [GraphQL response](https://spec.graphql.org/October2021/#sec-Errors.Field-errors) + +If the field's parent query/fragment/mutation was annotated with [`@throwOnFieldError`](../../guides/throw-on-field-error-directive.md) and no [`@catch`](../../guides/catch-directive.md) directive was used to catch the error, Relay will throw an error immediately after logging this event. + +:::note +Only throw on this event if `handled` is false. Errors that have been handled by a `@catch` directive or by making a resolver null will have `handled: true` and should not trigger a throw. +::: + +```ts +export type RelayFieldPayloadErrorEvent = { + +kind: 'relay_field_payload.error', + +owner: string, + +fieldPath: string, + +error: TRelayFieldError, + +shouldThrow: boolean, + +handled: boolean, +}; +``` diff --git a/website/versioned_docs/version-v19.0.0/api-reference/relay-runtime/observe-fragment.md b/website/versioned_docs/version-v19.0.0/api-reference/relay-runtime/observe-fragment.md new file mode 100644 index 0000000000000..e0d913c9efbe4 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/api-reference/relay-runtime/observe-fragment.md @@ -0,0 +1,92 @@ +--- +id: observe-fragment +title: observeFragment +slug: api-reference/observe-fragment +description: Read the value of a fragment and observe it's state and value over time +keywords: + - observable + - fragment +--- + +import DocsRating from '@site/src/core/DocsRating'; + +:::warning +`observeFragment` is still an experimental API. It currently has some limitations and may evolve slightly during this phase. +::: + +## `observeFragment` + +In some cases it can be useful to define data that you wish to read using a GraphQL fragment, but then consume it outside of React render function. `observeFragment` allows you to consume the state and value of a fragment as it changes over time. This includes loading and error states as well as changes to the data as it gets updated by local updates, mutations or updates to Relay's normalized store from other queries. + +To read a fragment's data just once, see [`waitForFragmentData`](./wait-for-fragment-data.md). + +:::caution +When using `observeFragment` with a plural fragment, the current implementation notifies the subscription multiple times if a store update impacting multiple list items gets published. Since the notifications happen synchronously, it is advised to debounce for a tick and only use the last payload for batching. +::: + +### Example + +```ts +import {observeFragment} from "relay-runtime/experimental"; +import { useEffect } from "react"; +import { useFragment } from "react-relay"; +import { graphql } from "relay-runtime"; + +function MyComponent({ key }) { + const user = useFragment( + graphql` + fragment UserFragment on User { + ...TitleFragment + } + `, + key, + ); + + // Update the title as the user's name changes without triggering rerenders. + useEffect(() => { + const subscription = observeFragment( + graphql` + fragment TitleFragment on User { + name + } + `, + user, + ).subscribe({ + next: (result) => { + switch(result.kind) { + case "loading": + window.title = "...loading"; + break; + case "error": + window.title = "Oops, we hit an error"; + break; + case "ok": + window.title = `Welcome ${result.value.name}`; + break; + } + } + }); + return () => { + subscription.unsubscribe(); + }; + }, [user]); + + return
Check out the document title!
; +} +``` + +### Arguments + +* `environment`: `IEnvironment`. A Relay environment. +* `fragment`: GraphQL fragment specified using a `graphql` template literal. +* `fragmentReference`: The *fragment reference* is an opaque Relay object that Relay uses to read the data for the fragment from the store; more specifically, it contains information about which particular object instance the data should be read from. + * The type of the fragment reference can be imported from the generated Flow types, from the file `.graphql.js`, and can be used to declare the type of your `Props`. The name of the fragment reference type will be: `$key`. We use our [lint rule](https://github.com/relayjs/eslint-plugin-relay) to enforce that the type of the fragment reference prop is correctly declared. + +### Return Value + +* An [`Observable`](../../glossary/glossary.md#observable) which returns a discriminated union modeling the three possible states in which a fragment's data might be: + * `{state: 'ok', value: T}` - When data is avalaible the state is `'ok'`. `T` is the data defined in the fragment. + * `{state: 'error': error: Error}` - When the fragment is in an error state either due to network level errors, [`@throwOnFieldError`](../../guides/throw-on-field-error-directive.md) or [`@required(action: THROW)`](../../guides/required-directive.md) field errors. + * `{state: 'loading'}` - When the parent request, or current `@defer` payload is still in flight, or a [`@live` Relay Resolver](../../guides/relay-resolvers/live-fields.md) being read is in a suspended state. + + diff --git a/website/versioned_docs/version-v19.0.0/api-reference/relay-runtime/relay-environment.md b/website/versioned_docs/version-v19.0.0/api-reference/relay-runtime/relay-environment.md new file mode 100644 index 0000000000000..bb4a2dddd86f7 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/api-reference/relay-runtime/relay-environment.md @@ -0,0 +1,52 @@ +--- +id: relay-environment +title: Relay Environment +slug: /api-reference/relay-runtime/relay-environment +description: Setting up a Relay Environment +--- + +The core of Relay's runtime is the `Environment`. The environment knows how to make requests to your GraphQL server and contains the `Store`, Relay's normalized data cache. Generally your application will construct a single environment which is configured to fetch data from your server, and then expose that environment to all of your components via `RelayEnvironmentProvider`. + +## Creating an Environment + +To create your environment you must provide two key pieces, a [`Network`](../../guides/network-layer.md) and a [`Store`](store.md). + +The `Network` is responsible for making requests to your GraphQL server. The `Store` holds the normalized data cache. + +A minimal implementation of an environment might look like this: + +```ts title="RelayEnvironment.js" +import { Environment, Store, RecordSource, Network, FetchFunction } from "relay-runtime"; + +const HTTP_ENDPOINT = "https://graphql.org/graphql/"; + +const fetchGraphQL: FetchFunction = async (request, variables) => { + const resp = await fetch(HTTP_ENDPOINT, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ query: request.text, variables }), + }); + if (!resp.ok) { + throw new Error("Response failed."); + } + return await resp.json(); +}; + +export const environment = new Environment({ + store: new Store(new RecordSource({})), + network: Network.create(fetchGraphQL), +}); +``` + +## Advanced Configuration + +The Relay environment accepts a number of additional configuration options when it is created. These options are all optional, but can be used to customize the behavior of the environment. + +Notable options include: + +* `log` - A function that will be called with telemetry events. See the types for [`LogEvent`](https://github.com/facebook/relay/blob/0414c9ad0744483e349e07defcb6d70a52cf8b3c/packages/relay-runtime/store/RelayStoreTypes.js#L799) for a full list of events and their fields. +* [`missingFieldHandlers`](../../guided-tour/reusing-cached-data/filling-in-missing-data.md) - A list of handlers that will be called when a field is missing from the store. This can be used to enable fulfilling queries to fields like `Query.node` from cache. +* `getDataID` - A function that will be called to generate a unique ID for a given object. This can be used to customize the way that Relay generates IDs for objects if your server does not implement the [Global Object Identification spec](https://graphql.org/learn/global-object-identification/). +* [`relayFieldLogger`](./field-logger.md) - A function that will be called when Relay encounters a field-level error. + +For a full list of options, inspect the [provided TypeScript types](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/relay-runtime/lib/store/RelayModernEnvironment.d.ts#L26-L43). diff --git a/website/versioned_docs/version-v19.0.0/api-reference/relay-runtime/request-subscription.md b/website/versioned_docs/version-v19.0.0/api-reference/relay-runtime/request-subscription.md new file mode 100644 index 0000000000000..0c3783803f0b8 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/api-reference/relay-runtime/request-subscription.md @@ -0,0 +1,54 @@ +--- +id: request-subscription +title: requestSubscription +slug: /api-reference/request-subscription/ +description: API reference for requestSubscription, which imperatively establishes a GraphQL subscription +keywords: + - subscription +--- + +import DocsRating from '@site/src/core/DocsRating'; +import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; +import GraphQLSubscriptionConfig from '../types/GraphQLSubscriptionConfig.md'; +import Disposable from '../types/Disposable.md'; + +## `requestSubscription` + +Imperative API for establishing a GraphQL Subscription. +See also the [`useSubscription`](../use-subscription/) API and the [Guide to Updating Data](../../guided-tour/updating-data/introduction.md). +```js +import {graphql, requestSubscription} from 'react-relay'; + +const subscription = graphql` + subscription UserDataSubscription($input: InputData!) { + # ... + } +`; + +function createSubscription(environment: IEnvironment): Disposable { + return requestSubscription(environment, { + subscription, + variables: {input: {userId: '4'}}, + }); +} +``` + +### Arguments + +* `environment`: A Relay Environment +* `config`: `GraphQLSubscriptionConfig` + + + +### Return Type + +* A [`Disposable`](#interface-disposable) that clears the subscription. + + + +### Behavior + +* Imperatively establish a subscription. +* See the [GraphQL Subscriptions Guide](../../guided-tour/updating-data/graphql-subscriptions/) for a more detailed explanation of how to work with subscriptions. + + diff --git a/website/versioned_docs/version-v19.0.0/api-reference/relay-runtime/runtime-configuration.md b/website/versioned_docs/version-v19.0.0/api-reference/relay-runtime/runtime-configuration.md new file mode 100644 index 0000000000000..8b7e6fb2d0f21 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/api-reference/relay-runtime/runtime-configuration.md @@ -0,0 +1,52 @@ +--- +id: runtime-config +title: Runtime Configuration +slug: /api-reference/runtime-config/ +description: Configuring the Relay Runtime +keywords: + - feature flags + - configuration +--- + +## ConnectionInterface + +If your server's implementation of the Connection Spec differs from the default interface you will need to configure the Relay Runtime to expect the connection type and field names used in your schema. This is done by updating the global ConnectionInterface instance exported by Relay: + +:::note +You will also need to update your Relay Compiler Config with these same values. +::: + +```ts title="/src/ConfigureRelay.ts" +import { ConnectionInterface } from 'relay-runtime'; + +// Note: This must match the values configured in the Relay compiler config. +ConnectionInterface.inject({ + END_CURSOR: 'end_cursor', + HAS_NEXT_PAGE: 'has_next_page', + HAS_PREV_PAGE: 'has_previous_page', + START_CURSOR: 'start_cursor', + PAGE_INFO: 'page_info', + NODE: 'node', + CURSOR: 'cursor', + EDGES: 'edges', + PAGE_INFO_TYPE: 'PageInfo', +}); +``` + +## Feature Flags + +:::warning +Feature Flags are used for enabling and configuring unstable Relay features, **regular use of Relay should not need to modify runtime feature flags**. They are documented here for purely informational purposes +::: + +Relay has a number of runtime options called "Feature Flags". In general, these are used for enabling experimental features which are not yet stable and thus not yet enabled by default. + +Feature flags in the Relay Runtime are implemented as a global mutable object. To set/configure a feature flag, import that object and mutate it. If you do this in the module scope, the updates will apply before Relay looks at them. + +```ts title="/src/ConfigureRelay.ts" +import { RelayFeatureFlags } from 'relay-runtime'; + +RelayFeatureFlags.ENABLE_SOME_EXPERIMENT = true; +``` + +You can find the full list of feature flags [here](https://github.com/facebook/relay/blob/203d8b10e9144a37466b8a72edbe6add48f64e7d/packages/relay-runtime/util/RelayFeatureFlags.js#L4), but keep in mind that **feature flags may change arbitrarily between versions of Relay**. diff --git a/website/versioned_docs/version-v19.0.0/api-reference/relay-runtime/store.md b/website/versioned_docs/version-v19.0.0/api-reference/relay-runtime/store.md new file mode 100644 index 0000000000000..4652fbdfd1b90 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/api-reference/relay-runtime/store.md @@ -0,0 +1,734 @@ +--- +id: store +title: Store +slug: /api-reference/store/ +description: API reference for the Relay store +keywords: + - store +--- + +import DocsRating from '@site/src/core/DocsRating'; +import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; + +The Relay Store can be used to programmatically update client-side data inside [`updater` functions](../../guided-tour/updating-data/graphql-mutations/). The following is a reference of the Relay Store interface. + +Table of Contents: + +- [RecordSourceSelectorProxy](#recordsourceselectorproxy) +- [RecordProxy](#recordproxy) +- [RecordSourceProxy](#recordsourceproxy) +- [ConnectionHandler](#connectionhandler) + +## RecordSourceSelectorProxy + +The `RecordSourceSelectorProxy` is the type of the `store` that [`updater` functions](../../guided-tour/updating-data/graphql-mutations/) receive as an argument. The following is the `RecordSourceSelectorProxy` interface: + +```javascript +interface RecordSourceSelectorProxy { + create(dataID: string, typeName: string): RecordProxy; + delete(dataID: string): void; + get(dataID: string): ?RecordProxy; + getRoot(): RecordProxy; + getRootField(fieldName: string): ?RecordProxy; + getPluralRootField(fieldName: string): ?Array; + invalidateStore(): void; +} +``` + +### `create(dataID: string, typeName: string): RecordProxy` + +Creates a new record in the store given a `dataID` and the `typeName` as defined by the GraphQL schema. Returns a [`RecordProxy`](#recordproxy) which serves as an interface to mutate the newly created record. + +#### Example + +```javascript +const record = store.create(dataID, 'Todo'); +``` + +### `delete(dataID: string): void` + +Deletes a record from the store given its `dataID`. For existing edges to the deleted record, `undefined` will be returned in the default case even when the value is typed as non-nullable. When [`@throwOnFieldError`](../../guides/throw-on-field-error-directive/) is present, the missing data will throw an error. + +#### Example + +```javascript +store.delete(dataID); +``` + +### `get(dataID: string): ?RecordProxy` + +Retrieves a record from the store given its `dataID`. Returns a [`RecordProxy`](#recordproxy) which serves as an interface to mutate the record. + +#### Example + +```javascript +const record = store.get(dataID); +``` + +### `getRoot(): RecordProxy` + +Returns the [`RecordProxy`](#recordproxy) representing the root of the GraphQL document. + +#### Example + +Given the GraphQL document: + +```graphql +viewer { + id +} +``` + +Usage: + +```javascript +// Represents root query +const root = store.getRoot(); +// Get the viewer linked record +const viewer = root.getLinkedRecord('viewer'); +``` + +### `getRootField(fieldName: string): ?RecordProxy` + +Retrieves a root field from the store given the `fieldName`, as defined by the GraphQL document. Returns a [`RecordProxy`](#recordproxy) which serves as an interface to mutate the record. + +#### Example + +Given the GraphQL document: + +```graphql +viewer { + id +} +``` + +Usage: + +```javascript +const viewer = store.getRootField('viewer'); +``` + +### `getPluralRootField(fieldName: string): ?Array` + +Retrieves a root field that represents a collection from the store given the `fieldName`, as defined by the GraphQL document. Returns an array of [`RecordProxies`](#recordproxy). + +#### Example + +Given the GraphQL document: + +```graphql +nodes(first: 10) { + # ... +} +``` + +Usage: + +```javascript +const nodes = store.getPluralRootField('nodes'); +``` + +### `invalidateStore(): void` + +Globally invalidates the Relay store. This will cause any data that was written to the store before invalidation occurred to be considered stale, and will be considered to require refetch the next time a query is checked with `environment.check()`. + +#### Example + +```javascript +store.invalidateStore(); +``` + +After global invalidation, any query that is checked before refetching it will be considered stale: + +```javascript +environment.check(query) === 'stale' +``` + +## RecordProxy + +The `RecordProxy` serves as an interface to mutate records: + +```javascript +interface RecordProxy { + copyFieldsFrom(sourceRecord: RecordProxy): void; + getDataID(): string; + getLinkedRecord(name: string, arguments?: ?Object): ?RecordProxy; + getLinkedRecords(name: string, arguments?: ?Object): ?Array; + getOrCreateLinkedRecord( + name: string, + typeName: string, + arguments?: ?Object, + ): RecordProxy; + getType(): string; + getValue(name: string, arguments?: ?Object): mixed; + setLinkedRecord( + record: RecordProxy, + name: string, + arguments?: ?Object, + ): RecordProxy; + setLinkedRecords( + records: Array, + name: string, + arguments?: ?Object, + ): RecordProxy; + setValue(value: mixed, name: string, arguments?: ?Object): RecordProxy; + invalidateRecord(): void; +} +``` + +### `getDataID(): string` + +Returns the `dataID` of the current record. + +#### Example + +```javascript +const id = record.getDataID(); +``` + +### `getType(): string` + +Gets the type of the current record, as defined by the GraphQL schema. + +#### Example + +```javascript +const type = user.getType(); // User +``` + +### `getValue(name: string, arguments?: ?Object): mixed` + +Gets the value of a field in the current record given the field name. + +#### Example + +Given the GraphQL document: + +```graphql +viewer { + id + name +} +``` + +Usage: + +```javascript +const name = viewer.getValue('name'); +``` + +Optionally, if the field takes arguments, you can pass a bag of `variables`. + +#### Example + +Given the GraphQL document: + +```graphql +viewer { + id + name(arg: $arg) +} +``` + +Usage: + +```javascript +const name = viewer.getValue('name', {arg: 'value'}); +``` + +### `getLinkedRecord(name: string, arguments?: ?Object): ?RecordProxy` + +Retrieves a record associated with the current record given the field name, as defined by the GraphQL document. Returns a `RecordProxy`. + +#### Example + +Given the GraphQL document: + +```graphql +rootField { + viewer { + id + name + } +} +``` + +Usage: + +```javascript +const rootField = store.getRootField('rootField'); +const viewer = rootField.getLinkedRecord('viewer'); +``` + +Optionally, if the linked record takes arguments, you can pass a bag of `variables` as well. + +#### Example + +Given the GraphQL document: + +```graphql +rootField { + viewer(arg: $arg) { + id + } +} +``` + +Usage: + +```javascript +const rootField = store.getRootField('rootField'); +const viewer = rootField.getLinkedRecord('viewer', {arg: 'value'}); +``` + +### `getLinkedRecords(name: string, arguments?: ?Object): ?Array` + +Retrieves the set of records associated with the current record given the field name, as defined by the GraphQL document. Returns an array of `RecordProxies`. + +#### Example + +Given the GraphQL document: + +```graphql +rootField { + nodes { + # ... + } +} +``` + +Usage: + +```javascript +const rootField = store.getRootField('rootField'); +const nodes = rootField.getLinkedRecords('nodes'); +``` + +Optionally, if the linked record takes arguments, you can pass a bag of `variables` as well. + +#### Example + +Given the GraphQL document: + +```graphql +rootField { + nodes(first: $count) { + # ... + } +} +``` + +Usage: + +```javascript +const rootField = store.getRootField('rootField'); +const nodes = rootField.getLinkedRecords('nodes', {count: 10}); +``` + +### `getOrCreateLinkedRecord(name: string, typeName: string, arguments?: ?Object)` + +Retrieves a record associated with the current record given the field name, as defined by the GraphQL document. If the linked record does not exist, it will be created given the type name. Returns a `RecordProxy`. + +#### Example + +Given the GraphQL document: + +```graphql +rootField { + viewer { + id + } +} +``` + +Usage: + +```javascript +const rootField = store.getRootField('rootField'); +const newViewer = rootField.getOrCreateLinkedRecord('viewer', 'User'); // Will create if it doesn't exist +``` + +Optionally, if the linked record takes arguments, you can pass a bag of `variables` as well. + +### `setValue(value: mixed, name: string, arguments?: ?Object): RecordProxy` + +Mutates the current record by setting a new value on the specified field. Returns the mutated record. + +Given the GraphQL document: + +```graphql +viewer { + id + name +} +``` + +Usage: + +```javascript +viewer.setValue('New Name', 'name'); +``` + +Optionally, if the field takes arguments, you can pass a bag of `variables`. + +```javascript +viewer.setValue('New Name', 'name', {arg: 'value'}); +``` + +### `copyFieldsFrom(sourceRecord: RecordProxy): void` + +Mutates the current record by copying the fields over from the passed in record `sourceRecord`. + +#### Example + +```javascript +const record = store.get(id1); +const otherRecord = store.get(id2); +record.copyFieldsFrom(otherRecord); // Mutates `record` +``` + +### `setLinkedRecord(record: RecordProxy, name: string, arguments?: ?Object)` + +Mutates the current record by setting a new linked record on the given field name. + +#### Example + +Given the GraphQL document: + +```graphql +rootField { + viewer { + id + } +} +``` + +Usage: + +```javascript +const rootField = store.getRootField('rootField'); +const newViewer = store.create(/* ... */); +rootField.setLinkedRecord(newViewer, 'viewer'); +``` + +Optionally, if the linked record takes arguments, you can pass a bag of `variables` as well. + +### `setLinkedRecords(records: Array, name: string, variables?: ?Object)` + +Mutates the current record by setting a new set of linked records on the given field name. + +#### Example + +Given the GraphQL document: + +```graphql +rootField { + nodes { + # ... + } +} +``` + +Usage: + +```javascript +const rootField = store.getRootField('rootField'); +const newNode = store.create(/* ... */); +const newNodes = [...rootField.getLinkedRecords('nodes'), newNode]; +rootField.setLinkedRecords(newNodes, 'nodes'); +``` + +Optionally, if the linked record takes arguments, you can pass a bag of `variables` as well. + +### `invalidateRecord(): void` + +Invalidates the record. This will cause any query that references this record to be considered stale until the next time it is refetched, and will be considered to require a refetch the next time such a query is checked with `environment.check()`. + +#### Example + +```javascript +const record = store.get('4'); +record.invalidateRecord(); +``` + +After invalidating a record, any query that references the invalidated record and that is checked before refetching it will be considered stale: + +```javascript +environment.check(query) === 'stale' +``` + +## RecordSourceProxy + +The `RecordSourceProxy` serves as an interface to mutate record. + +:::danger +`RecordSourceProxy` exposes many low level APIs that are not typesafe. Users should consider using [typesafe updaters](../../guided-tour/updating-data/typesafe-updaters-faq/), [optimistic updates](../../guided-tour/updating-data/graphql-mutations/#optimistic-updates), and [relay resolvers](../../guides/relay-resolvers/introduction/) instead if their use case can be covered by these alternatives. +::: + +```javascript +interface RecordSourceProxy { + create(dataID: DataID, typeName: string): RecordProxy; + delete(dataID: DataID): void; + get(dataID: DataID): ?RecordProxy; + getRoot(): RecordProxy; + invalidateStore(): void; + readUpdatableFragment( + fragment: UpdatableFragment, + fragmentReference: HasUpdatableSpread, + ): UpdatableData; + readUpdatableQuery( + query: UpdatableQuery, + variables: TVariables, + ): UpdatableData; +} +``` + +### `create(dataID: DataID, typeName: string): RecordProxy` + +Creates a new record in the store given a `dataID` and the `typeName` as defined by the GraphQL schema. Returns a [`RecordProxy`](#recordproxy) which serves as an interface to mutate the newly created record. + +#### Example + +```javascript +const record = store.create(dataID, 'Todo'); +``` + +### `delete(dataID: DataID): void` + +Deletes a record from the store given its `dataID`. For existing edges to the deleted record, `undefined` will be returned in the default case even when the value is typed as non-nullable. When [`@throwOnFieldError`](../../guides/throw-on-field-error-directive/) is present, the missing data will throw an error. + +#### Example + +```javascript +store.delete(dataID); +``` + +### `get(dataID: DataID): ?RecordProxy` + +Retrieves a record from the store given its `dataID`. Returns a [`RecordProxy`](#recordproxy) which serves as an interface to read/mutate the record. + +#### Example + +```javascript +const record = store.get(dataID); +``` + +### `getRoot(): RecordProxy` + +Returns the [`RecordProxy`](#recordproxy) representing the root of the GraphQL document. + +#### Example + +Given the GraphQL document: + +```graphql +viewer { + id +} +``` + +Usage: + +```javascript +// Represents root query +const root = store.getRoot(); +// Get the viewer linked record +const viewer = root.getLinkedRecord('viewer'); +``` + +### `invalidateStore(): void;` + +Globally invalidates the Relay store. This will cause any data that was written to the store before invalidation occurred to be considered stale, and will be considered to require refetch the next time a query is checked with `environment.check()` or is fetched with a `store-or-network` [fetch policy](../../guided-tour/reusing-cached-data/fetch-policies/). + +#### Example + +```javascript +store.invalidateStore(); +``` + +After global invalidation, any query that is checked before refetching it will be considered stale: + +```javascript +environment.check(query) === 'stale' +``` + +### `readUpdatableFragment(fragment: UpdatableFragment,fragmentReference: HasUpdatableSpread): UpdatableData;` + +Fetches an updatable fragment from the store. This updatable fragment's fields can then be imperatively modified to update data in the store. + +For more information on updating the store imperatively, see this [section](../../guided-tour/updating-data/imperatively-modifying-store-data/) of the guided tour. + +#### Example +```javascript +const fragment = graphql` + fragment StoryLikeButton_updatable on Story @updatable { + likeCount + doesViewerLike + } +`; +const { + updatableData +} = store.readUpdatableFragment( + fragment, + story +); +updatableData.likeCount = updatableData.likeCount + 1 +``` + +### `readUpdatableQuery(query: UpdatableQuery,variables: TVariables): UpdatableData` + +Reads an updatable query from the store. This updatable query's fields can be imperatively modified to update data in the store. Unlike `readUpdatableFragment`, you do not need to pass in a `fragmentReference` as an input argument. + +For more information on updating the store imperatively, see this [section](../../guided-tour/updating-data/imperatively-modifying-store-data/) of the guided tour. + +#### Example + +```javascript +const {updatableData} = store.readUpdatableQuery( + graphql` + query NameUpdaterUpdateQuery @updatable { + viewer { + name + } + } + `, + {} +); +const viewer = updatableData.viewer; +viewer.name = newName; +``` + +## ConnectionHandler + +`ConnectionHandler` is a utility module exposed by `relay-runtime` that aids in the manipulation of connections. `ConnectionHandler` exposes the following interface: + +```javascript +interface ConnectionHandler { + getConnection( + record: RecordProxy, + key: string, + filters?: ?Object, + ): ?RecordProxy, + createEdge( + store: RecordSourceProxy, + connection: RecordProxy, + node: RecordProxy, + edgeType: string, + ): RecordProxy, + insertEdgeBefore( + connection: RecordProxy, + newEdge: RecordProxy, + cursor?: ?string, + ): void, + insertEdgeAfter( + connection: RecordProxy, + newEdge: RecordProxy, + cursor?: ?string, + ): void, + deleteNode(connection: RecordProxy, nodeID: string): void +} +``` + +### `getConnection(record: RecordProxy, key: string, filters?: ?Object)` + +Given a record and a connection key, and optionally a set of filters, `getConnection` retrieves a [`RecordProxy`](#recordproxy) that represents a connection that was annotated with a `@connection` directive. + +First, let's take a look at a plain connection: + +```graphql +fragment FriendsFragment on User { + friends(first: 10) { + edges { + node { + id + } + } + } +} +``` + +Accessing a plain connection field like this is the same as other regular fields: + +```javascript +// The `friends` connection record can be accessed with: +const user = store.get(userID); +const friends = user && user.getLinkedRecord('friends'); + +// Access fields on the connection: +const edges = friends && friends.getLinkedRecords('edges'); +``` + +When using [usePaginationFragment](../use-pagination-fragment/), we usually annotate the actual connection field with `@connection` to tell Relay which part needs to be paginated: + +```graphql +fragment FriendsFragment on User { + friends(first: 10, orderby: "firstname") @connection( + key: "FriendsFragment_friends", + ) { + edges { + node { + id + } + } + } +} +``` + +For connections like the above, `ConnectionHandler` helps us find the record: + +```javascript +import {ConnectionHandler} from 'relay-runtime'; + +// The `friends` connection record can be accessed with: +const user = store.get(userID); +const friends = ConnectionHandler.getConnection( + user, // parent record + 'FriendsFragment_friends', // connection key + {orderby: 'firstname'} // 'filters' that is used to identify the connection +); +// Access fields on the connection: +const edges = friends.getLinkedRecords('edges'); +``` + +### Edge creation and insertion + +#### `createEdge(store: RecordSourceProxy, connection: RecordProxy, node: RecordProxy, edgeType: string)` + +Creates an edge given a [`store`](#recordsourceselectorproxy), a connection, the edge node, and the edge type. + +#### `insertEdgeBefore(connection: RecordProxy, newEdge: RecordProxy, cursor?: ?string)` + +Given a connection, inserts the edge at the beginning of the connection, or before the specified `cursor`. + +#### `insertEdgeAfter(connection: RecordProxy, newEdge: RecordProxy, cursor?: ?string)` + +Given a connection, inserts the edge at the end of the connection, or after the specified `cursor`. + +#### Example + +```javascript +const user = store.get(userID); +const friends = ConnectionHandler.getConnection(user, 'FriendsFragment_friends'); +const newFriend = store.get(newFriendId); +const edge = ConnectionHandler.createEdge(store, friends, newFriend, 'UserEdge'); + +// No cursor provided, append the edge at the end. +ConnectionHandler.insertEdgeAfter(friends, edge); + +// No cursor provided, insert the edge at the front: +ConnectionHandler.insertEdgeBefore(friends, edge); +``` + +### `deleteNode(connection: RecordProxy, nodeID: string): void` + +Given a connection, deletes any edges whose node id matches the given id. + +#### Example + +```javascript +const user = store.get(userID); +const friends = ConnectionHandler.getConnection(user, 'FriendsFragment_friends'); +ConnectionHandler.deleteNode(friends, idToDelete); +``` + + diff --git a/website/versioned_docs/version-v19.0.0/api-reference/relay-runtime/wait-for-fragment-data.md b/website/versioned_docs/version-v19.0.0/api-reference/relay-runtime/wait-for-fragment-data.md new file mode 100644 index 0000000000000..4b1ab32a6ca71 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/api-reference/relay-runtime/wait-for-fragment-data.md @@ -0,0 +1,89 @@ +--- +id: wait-for-fragment-data +title: waitForFragmentData +slug: /api-reference/wait-for-fragment-data/ +description: Read the value of a fragment as a promise +keywords: + - promise + - fragment +--- + +import DocsRating from '@site/src/core/DocsRating'; + +:::warning +`waitForFragmentData` is still an experimental API. It currently has some limitations and may evolve slightly during this phase. +::: + +## `waitForFragmentData` + +In some cases it can be useful to define data that you wish to read using a GraphQL fragment, but then consume it just once outside of React render function. `waitForFragmentData` allows you to wait for the data of a fragment to be avalaible, + +To read a fragment's data as it changes over time, see [`observeFragment`](./observe-fragment.md). + +### Example: Deferring data used in an event handler + +One use case for `waitForFragmentData` is to defer fetching data that is needed inside an event handler, but is not needed to render the initial view. + +```tsx +import { useCallback } from "react"; +import { useFragment } from "react-relay"; +import { graphql } from "relay-runtime"; +import { waitForFragmentData } from "relay-runtime/experimental"; + +function MyComponent({ key }) { + const user = useFragment( + graphql` + fragment UserFragment on User { + name + # Page load can complete before this data has streamed in from the server. + ...EventHandlerFragment @defer + } + `, + key, + ); + + const onClick = useCallback(async () => { + // Once the user clicks, we may need to wait for the data to finish loading. + const userData = await waitForFragmentData( + graphql` + fragment EventHandlerFragment on User { + age + } + `, + user, + ); + + if (userData.age < 10) { + alert("Hello kiddo!"); + } else if (userData.age < 18) { + alert("Hello young person!"); + } else { + alert("Hello adult person!"); + } + }, [user]); + + return ( +
+ My name is {user.name} + +
+ ); +} +``` + +### Arguments + +* `environment`: `IEnvironment`. A Relay environment. +* `fragment`: GraphQL fragment specified using a `graphql` template literal. +* `fragmentReference`: The *fragment reference* is an opaque Relay object that Relay uses to read the data for the fragment from the store; more specifically, it contains information about which particular object instance the data should be read from. + * The type of the fragment reference can be imported from the generated Flow types, from the file `.graphql.js`, and can be used to declare the type of your `Props`. The name of the fragment reference type will be: `$key`. We use our [lint rule](https://github.com/relayjs/eslint-plugin-relay) to enforce that the type of the fragment reference prop is correctly declared. + +### Return Value + +* A `Promise` where `T` is the data defined in the fragment. + +The Promise will wait for all network data to become avaliable as well as any [`@live` Relay Resolver](../../guides/relay-resolvers/live-fields.md) to be in a non-suspended state before it resolves. + +In the case of a network error, or a field-level error due to [`@throwOnFieldError`](../../guides/throw-on-field-error-directive.md) or [`@required(action: THROW)`](../../guides/required-directive.md), the Promise will reject with an error. + + diff --git a/website/versioned_docs/version-v19.0.0/api-reference/types/CacheConfig.md b/website/versioned_docs/version-v19.0.0/api-reference/types/CacheConfig.md new file mode 100644 index 0000000000000..e7690fbdf7259 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/api-reference/types/CacheConfig.md @@ -0,0 +1,8 @@ +#### Type `CacheConfig` + +* An object with the following fields: + * `force`: *_[Optional]_* A boolean. If true, causes a query to be issued unconditionally, regardless of the state of any configured response cache. + * `poll`: *_[Optional]_* A number. Causes a query to live-update by polling at the specified interval, in milliseconds. (This value will be passed to `setTimeout`). + * `liveConfigId`: *_[Optional]_* A string. Causes a query to live-update by calling GraphQLLiveQuery; it represents a configuration of gateway when doing live query. + * `metadata`: *_[Optional]_* An object. User-supplied metadata. + * `transactionId`: *_[Optional]_* A string. A user-supplied value, intended for use as a unique id for a given instance of executing an operation. diff --git a/website/versioned_docs/version-v19.0.0/api-reference/types/Disposable.md b/website/versioned_docs/version-v19.0.0/api-reference/types/Disposable.md new file mode 100644 index 0000000000000..76dcc19d99490 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/api-reference/types/Disposable.md @@ -0,0 +1,4 @@ +#### Interface `Disposable` + +* An object with the following key: + * `dispose`: `() => void`. Disposes of the resource. diff --git a/website/versioned_docs/version-v19.0.0/api-reference/types/GraphQLSubscriptionConfig.md b/website/versioned_docs/version-v19.0.0/api-reference/types/GraphQLSubscriptionConfig.md new file mode 100644 index 0000000000000..901d88b94322c --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/api-reference/types/GraphQLSubscriptionConfig.md @@ -0,0 +1,17 @@ +import SelectorStoreUpdater from './SelectorStoreUpdater.md'; +import CacheConfig from './CacheConfig.md'; + +#### Type `GraphQLSubscriptionConfig` + +* An object with the following fields: + * `cacheConfig`: *_[Optional]_* [`CacheConfig`](#type-cacheconfig) + * `subscription`: `GraphQLTaggedNode`. A GraphQL subscription specified using a `graphql` template literal + * `variables`: The variables to pass to the subscription + * `onCompleted`: *_[Optional]_* `() => void`. An optional callback that is executed when the subscription is established + * `onError`: *_[Optional]_* `(Error) => {}`. An optional callback that is executed when an error occurs + * `onNext`: *_[Optional]_* `(TSubscriptionPayload) => {}`. An optional callback that is executed when new data is received + * `updater`: *_[Optional]_* [`SelectorStoreUpdater`](#type-selectorstoreupdater). + + + + diff --git a/website/versioned_docs/version-v19.0.0/api-reference/types/MutationConfig.md b/website/versioned_docs/version-v19.0.0/api-reference/types/MutationConfig.md new file mode 100644 index 0000000000000..a0628ccc9f5f1 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/api-reference/types/MutationConfig.md @@ -0,0 +1,31 @@ +import CacheConfig from './CacheConfig.md'; +import SelectorStoreUpdater from './SelectorStoreUpdater.md'; +import UploadableMap from './UploadableMap.md'; + +#### Type `MutationConfig` + +* An object with the following fields: + * `cacheConfig`: *_[Optional]_* [`CacheConfig`](#type-cacheconfig) + * `mutation`: `GraphQLTaggedNode`. A mutation specified using a GraphQL literal + * `onError`: *_[Optional]_* `(Error) => void`. An optional callback executed if the mutation results in an error. + * `onCompleted`: *_[Optional]_* `($ElementType) => void`. An optional callback that is executed when the mutation completes. + * The value passed to `onCompleted` is the the mutation fragment, as read out from the store, **after** updaters and declarative mutation directives are applied. This means that data from within unmasked fragments will not be read, and records that were deleted (e.g. by `@deleteRecord`) may also be null. + * `onUnsubscribe`: *_[Optional]_* `() => void`. An optional callback that is executed when the mutation is unsubscribed, which occurs when the returned `Disposable` is disposed. + * `optimisticResponse`: *_[Optional]_* An object whose type matches the raw response type of the mutation. Make sure you decorate your mutation with `@raw_response_type` if you are using this field. + * `optimisticUpdater`: *_[Optional]_* [`SelectorStoreUpdater`](#type-selectorstoreupdater). A callback that is executed when `commitMutation` is called, after the `optimisticResponse` has been normalized into the store. + * `updater`: *_[Optional]_* [`SelectorStoreUpdater`](#type-selectorstoreupdater). A callback that is executed when a payload is received, after the payload has been written into the store. + * `uploadables`: *_[Optional]_* [`UploadableMap`](#type-uploadablemap). An optional uploadable map. + * `variables`: `$ElementType`. The variables to pass to the mutation. + + + + + + + +#### Type `MutationParameters` + +* An object with the following fields: + * `response`: An object + * `variables`: An object + * `rawResponse`: An optional object diff --git a/website/versioned_docs/version-v19.0.0/api-reference/types/SelectorStoreUpdater.md b/website/versioned_docs/version-v19.0.0/api-reference/types/SelectorStoreUpdater.md new file mode 100644 index 0000000000000..02f93a2aebd71 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/api-reference/types/SelectorStoreUpdater.md @@ -0,0 +1,6 @@ +import useBaseUrl from '@docusaurus/useBaseUrl'; + +#### Type `SelectorStoreUpdater` + +* A function with signature `(store: RecordSourceSelectorProxy, data) => void` +* This interface allows you to *imperatively* write and read data directly to and from the Relay store. This means that you have full control over how to update the store in response to the subscription payload: you can *create entirely new records*, or *update or delete existing ones*. The full API for reading and writing to the Relay store is available here. diff --git a/website/versioned_docs/version-v19.0.0/api-reference/types/UploadableMap.md b/website/versioned_docs/version-v19.0.0/api-reference/types/UploadableMap.md new file mode 100644 index 0000000000000..0050b91169e39 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/api-reference/types/UploadableMap.md @@ -0,0 +1,3 @@ +#### Type `UploadableMap` + +* An object whose values are [`File`](https://developer.mozilla.org/en-US/docs/Web/API/File) or [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob). diff --git a/website/versioned_docs/version-v19.0.0/community/learning-resources.md b/website/versioned_docs/version-v19.0.0/community/learning-resources.md new file mode 100644 index 0000000000000..52cc51dd8c630 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/community/learning-resources.md @@ -0,0 +1,64 @@ +--- +id: learning-resources +title: Community Learning Resources +slug: /community-learning-resources/ +--- + +import useBaseUrl from '@docusaurus/useBaseUrl'; +import DocsRating from '@site/src/core/DocsRating'; +import {FbInternalOnly, OssOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; + +## Guides and articles: + +- [How to use @argumentsDefinitions to define local variables to your fragments](https://medium.com/entria/relay-modern-argumentdefinitions-d53769dbb95d) (by Entria) +- [Deep Dive of Updater Relay Store function. How to update your store properly after a mutation or subscription](https://medium.com/entria/wrangling-the-client-store-with-the-relay-modern-updater-function-5c32149a71ac) (by Entria) +- [Optimistic Update: how to update your UI before server responds](https://medium.com/entria/relay-modern-optimistic-update-a09ba22d83c9) (by Entria) +- [Relay Network Deep Dive - how to incrementally improve your network layer to manage complex data fetching requirements](https://medium.com/entria/relay-modern-network-deep-dive-ec187629dfd3) (by Entria) +- [Relay Modern with TypeScript - how to configure Relay Modern to make it with TypeScript](https://medium.com/@sibelius/relay-modern-migration-to-typescript-c26ab0ee749c) (by @sibelius) +- [Collection of random thoughts and discoveries around Relay](https://mrtnzlml.com/docs/relay) + + + +## Relay example projects + +These projects serve as an example of how to use Relay in real world applications. Some of them are even with educational videos. + +- [github.com/relayjs/relay-examples](https://github.com/relayjs/relay-examples) +- [github.com/adeira/relay-example](https://github.com/adeira/relay-example) +- [github.com/juffalow/react-relay-example](https://github.com/juffalow/react-relay-example) + +## Learn basics + +Here, you will find articles written by Relay community. They are touching basic topic which are necessary for your daily work. + +- [What is a fragment? Basic explanation of what is a fragment and what it is used for](https://medium.com/@sibelius/relay-modern-what-is-a-fragment-c70f164c2469) (by @sibelius) +- [Relay anti-patterns. What you should avoid doing when using Relay concepts](https://medium.com/entria/relay-apollo-anti-pattern-d9f4dea47738) (by Entria) +- [Insights of how Relay Modern has improved a lot since Relay Classic](https://medium.com/entria/relay-is-just-getting-better-54112ffc1a9e) (by Entria) +- [How to use @argumentsDefinitions to define local variables to your fragments](https://medium.com/entria/relay-modern-argumentdefinitions-d53769dbb95d) (by Entria) +- [How to paginate using a Refetch Container. You can use a refetch container to paginate as well, just use renderVariables correctly](https://medium.com/entria/relay-modern-pagination-using-refetch-container-editing-a07c6b33ae4e) (by Entria) + +## About Relay Store + +- [How Relay Modern stores your data](https://medium.com/@sibelius/relay-modern-the-relay-store-8984cd148798) (by @sibelius) +- [Deep Dive of Updater Relay Store function. How to update your store properly after a mutation or subscription](https://medium.com/entria/wrangling-the-client-store-with-the-relay-modern-updater-function-5c32149a71ac) (by Entria) +- [Optimistic Update: how to update your UI before server responds](https://medium.com/entria/relay-modern-optimistic-update-a09ba22d83c9) (by Entria) +- [Local State Management, part 1 - how to create a controlled input using Relay](https://babangsund.com/relay_local_state_management/) (by @babangsund) +- [Local State Management, part 2 - how to manage global state and localStorage persistence on the client, using Relay](https://babangsund.com/relay_local_state_management_2/) (by @babangsund) +- [Local State Management, part 3 - using LocalQueryRenderer and local state to manage nested fragments](https://babangsund.com/relay_local_state_management_3/) (by @babangsund) + +## Network Layer + +- [Relay Network Deep Dive - how to incrementally improve your network layer to manage complex data fetching requirements](https://medium.com/entria/relay-modern-network-deep-dive-ec187629dfd3) (by Entria) + +## Relay Configuration + +- [Relay Modern with TypeScript - how to configure Relay Modern to make it with TypeScript](https://medium.com/@sibelius/relay-modern-migration-to-typescript-c26ab0ee749c) (by @sibelius) + +## Miscellaneous + +- [Relay Modern Learning Blog Posts Thread on Twitter](https://twitter.com/sseraphini/status/1078595758801203202) +- [Collection of random thoughts and discoveries around Relay](https://mrtnzlml.com/docs/relay) + + + + diff --git a/website/versioned_docs/version-v19.0.0/debugging/declarative-mutation-directives.md b/website/versioned_docs/version-v19.0.0/debugging/declarative-mutation-directives.md new file mode 100644 index 0000000000000..cfe156a170312 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/debugging/declarative-mutation-directives.md @@ -0,0 +1,34 @@ +--- +id: declarative-mutation-directives +title: Debugging Declarative Mutation Directives +slug: /debugging/declarative-mutation-directives/ +description: Debugging declarative mutation directives +keywords: +- debugging +- troubleshooting +- declarative mutation directive +- deleteRecord +- handlerProvider +- appendEdge +- prependEdge +- appendNode +- prependNode +--- + +import FbEnvHandlerExample from './fb/FbEnvHandlerExample.md'; + +If you see an error similar to: + +``` +RelayFBHandlerProvider: No handler defined for `deleteRecord`. [Caught in: An uncaught error was thrown inside `RelayObservable`.] +``` + +or + +``` +RelayModernEnvironment: Expected a handler to be provided for handle `deleteRecord`. +``` + +This probably means that you are using a Relay environment to which a `handlerProvider` is passed. However, the handler provider does not know how to accept the handles `"deleteRecord"`, `"appendEdge"` or `"prependEdge"`. If this is the case, you should return `MutationHandlers.DeleteRecordHandler`, `MutationHandlers.AppendEdgeHandler`, or `MutationHandlers.PrependEdgeHandler` respectively (these can be imported from `relay-runtime`). + + diff --git a/website/versioned_docs/version-v19.0.0/debugging/disallowed-id-types-error.md b/website/versioned_docs/version-v19.0.0/debugging/disallowed-id-types-error.md new file mode 100644 index 0000000000000..a3febedc1c864 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/debugging/disallowed-id-types-error.md @@ -0,0 +1,43 @@ +--- +id: disallowed-id-types-error +title: Disallowed Types for `id` Fields +slug: /debugging/disallowed-id-types-error +description: Disallowed types for `id` fields +keywords: +- debugging +- troubleshooting +- disallowed +- id +- Object Identification +--- + +import DocsRating from '@site/src/core/DocsRating'; + +If you see an error from the compiler that reads something like: + +``` +Disallowed type `String` of field `id` on parent type `Foo` cannot be used by Relay to identify entities +``` + +This means that your GraphQL schema defines an object with a field named `id` that doesn't have a valid type. Valid types for this field are `ID` or `ID!` unless configured otherwise. While there may be a valid reason in your application to have that field defined that way, the Relay compiler and runtime will mishandle that field and cause refresh or data updating issues. + +## Disallowing `id` fields without type `ID` + +Recall that Relay uses [Object Identification](../../guides/graphql-server-specification/#object-identification) to know which GraphQL objects to refetch. In the usual case, those GraphQL objects implement the [`Node` interface](https://graphql.org/learn/global-object-identification/#node-interface) and thus come with an `id` field with type `ID`. However, there are types in your GraphQL model that may not require unique identification. For that reason, Relay (by default) does not restrict object definitions, allowing `id` fields with non-`ID` types (e.g. `String`) to be defined. + +This poses a bit of difficulty to both Relay's compiler and runtime. In short, the runtime and compiler only properly handle `id` fields as defined by the `Node` interface. Any selections made with non-`Node` `id` fields will likely exhibit undesirable and unintended Relay behavior on your components (e.g. components not re-rendering on re-fetched data). + +### The significance of the `ID` type + +[Global Object Identification in GraphQL](https://graphql.org/learn/global-object-identification/)) is what underlies Relay's Object Identification. The `id` field supplied by the `Node` interface is specified to be a unique identifier that can be used for storage or caching. + +## Fix: Define your `id` fields as `ID` + +To ensure Relay correctly manages objects fetched to your application, here are two recommended courses of action: + +* Ensure all fields named `id` are typed with `ID` +* Rename any fields named `id` (with a type that isn't `ID`) to a different name (e.g. `special_purpose_id`) + +If your application truly requires that `id` field's definition to remain as-is and you are aware of the problems that may arise, you can add your GraphQL type and the type of its `id` field to the allowlist in `nonNodeIdFields` of the [Relay Compiler's Configuration](https://github.com/facebook/relay/tree/main/packages/relay-compiler). Note that this will only bypass the error for that combination of object type and `id` field type. + + diff --git a/website/versioned_docs/version-v19.0.0/debugging/inconsistent-typename-error.md b/website/versioned_docs/version-v19.0.0/debugging/inconsistent-typename-error.md new file mode 100644 index 0000000000000..650aea5b958f8 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/debugging/inconsistent-typename-error.md @@ -0,0 +1,47 @@ +--- +id: inconsistent-typename-error +title: Inconsistent Typename Error +slug: /debugging/inconsistent-typename-error/ +description: Debugging inconsistent typename errors in Relay +keywords: +- debugging +- troubleshooting +- inconsistent typename +- RelayResponseNormalizer +- globally unique id +--- + +import DocsRating from '@site/src/core/DocsRating'; +import {FbInternalOnly, OssOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; + +## Inconsistent `__typename` error + +The GraphQL server likely violated the globally unique ID requirement by returning the same ID for different objects. + +If you're seeing an error like: + +``` +`RelayResponseNormalizer: Invalid record '543'. Expected __typename to be consistent, but the record was assigned conflicting types Foo and Bar. The GraphQL server likely violated the globally unique ID requirement by returning the same ID for different objects.` +``` + +the server implementation of one of the types is not spec compliant. We require the `id` field to be globally unique. This is a problem because Relay stores objects in a normalized key-value store and one of the object just overwrote the other. This means your app is broken in some subtle or less subtle way. + +## Common cause + +The most common reason for this error is that 2 objects backed by an ID are using the plain ID as the id field, such as a `User` and `MessagingParticipant`. + +Less common reasons could be using array indices or auto-increment IDs from some database that might not be unique to this type. + +## Fix: Make your type spec compliant + +The best way to fix this is to make your type spec compliant. For the case of 2 different types backed by the same ID, a common solution is to prefix the ID of the less widely used type with a unique string and then base64 encode the result. This can be accomplished fairly easily by implementing a `NodeTokenResolver` using the helper trait `NodeTokenResolverWithPrefix`. When the `NodeTokenResolver` is registered is allows you to load your type using `node(id: $yourID)` GraphQL call and your type can return an encoded ID. + + + +### Example + +See [D17996531](https://www.internalfb.com/diff/D17996531) for an example on how to fix this. It created a `FusionPlatformComponentsCategoryNodeResolver` added the trait `TGraphQLNodeMixin` to the conflicting class so that it generates the base64 encoded ID. + + + + diff --git a/website/versioned_docs/version-v19.0.0/debugging/relay-devtools.md b/website/versioned_docs/version-v19.0.0/debugging/relay-devtools.md new file mode 100644 index 0000000000000..14df1c12863ac --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/debugging/relay-devtools.md @@ -0,0 +1,73 @@ +--- +id: relay-devtools +title: Relay DevTools +slug: /debugging/relay-devtools/ +description: Debugging guide for the Relay DevTools +keywords: +- debugging +- troubleshooting +- extension +- devtools +- browser +--- + +import DocsRating from '@site/src/core/DocsRating'; +import {FbInternalOnly, OssOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; + +## Installation + + + +### Internal version (preferred) + +The internal version of devtools has the latest updates and the process of installation will be much faster. + +1. Before downloading the new Devtools, make sure you've deleted all older versions of the extension. +2. Join [Relay Support](https://fb.workplace.com/groups/relay.support) group and you will automatically be added to the `cpe_relay_devtools_extension` gatekeeper. +3. Wait 20-30 minutes, and it should be downloaded on your Chrome browser +4. Or run `sudo soloctl -i` on your machine to get the extension immediately + +### Internal Version for Edgium users + +1. On `C:\Users\\AppData\Local\Google\Chrome\User Data\\Extensions` search for files manifest.json with Relay Developer Tools on it +2. Get the path to this folder e.g `...\Extensions\\\` +3. On edge://extensions/ click load upacked (you might need to Allow extensions for other stores). + +### External version (use at your own risk) + +The external version of Dev Tools does not always contain the latest updates, it may stop working, potentially being restricted to be used or removed by corp device admin (via MDM). +We recommend using the internal installation, but if you prefer, you may download the extension from the [Chrome store](https://chrome.google.com/webstore/detail/relay-developer-tools/ncedobpgnmkhcmnnkcimnobpfepidadl). + + + + + +Follow this link and install it from the [chrome store](https://chrome.google.com/webstore/detail/relay-developer-tools/ncedobpgnmkhcmnnkcimnobpfepidadl). + + + +--- + +## How to Navigate the Relay DevTools Extension + +You should have a new tab called Relay in your Chrome DevTools. In this tab, you will be able to select between 2 panels: the **network panel** and the **store panel**. + +### Network Panel + +The network panel allows users to view individual requests in an active environment. Users can scroll through these requests, search for the requests and view the details of each request. The details of each request includes the status, the variables, and the responses for the request. + +### Store Panel + +The store panel allows users to view individual records from the store data in an active environment. Users can scroll through these records, search for the records, and view the details of each request. Users can also copy the the store data in JSON format to the clipboard. The details of each record includes the ID, the typename, and all of the data in the record. If one of the fields in the data is a reference to another record, users can click on the reference, which will take them to that record. + +--- + +## Multiple Environments + +As you switch through the store and network panels, you'll notice that there is also a dropdown menu on the left side of the developer tools. This dropdown allows users to switch between environments. The requests in the network tab and the store data are grouped by environment, so users can easily shuffle between active environments. + +## Give Feedback + +Look in the top-right corner of the extension panel! + + diff --git a/website/versioned_docs/version-v19.0.0/debugging/why-null.md b/website/versioned_docs/version-v19.0.0/debugging/why-null.md new file mode 100644 index 0000000000000..80d7eee3e764f --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/debugging/why-null.md @@ -0,0 +1,116 @@ +--- +id: why-null +title: "Why Is My Field Null?" +slug: /debugging/why-null/ +description: Get help figuring out why a given field is unexpectedly null. +keywords: +- "null" +- missing +- optional +- nullthrows +--- + +import {FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; +import DocsRating from '@site/src/core/DocsRating'; + +There are a number of reasons that a field read by Relay can be null and some of them are obscure or unintuitive. When debugging an unexpectedly null value, it can be helpful to understand both the common cases and edge cases that can cause a field to be read as null. This document enumerates the cases that can lead to null or missing values with tips for figuring determining which case you are in. + +## Server Returned Null + +The simplest reason a field might be null is that the server explicitly returned null. This can happen in two cases: + +1. The server’s field resolver returned an explicit null +2. The field resolver throws. In this case GraphQL will return null for that field. *This is true even if the server resolver’s return type is non-nullable.* The one exceptions is fields annotated as non-null. In that case server should *never* return null. If an exception is encountered the entire parent object will be nulled out. + + + +:::note +At Meta, non-nullable fields are implemented using [`KillsParentOnException`](https://www.internalfb.com/intern/wiki/Graphql-for-hack-developers/fields/return-type/#non-nullable-fields). +::: + + + +**🕵️‍♀️ How to tell:** Inspect the server’s response using Relay Dev tools, or in your browser’s dev tools’s network tab, to see if the field is null. + +## Graph Relationship Change + +If a different query/mutation/subscription observes a relationship change in the graph, you may end up trying to read fields off of an object which your query never fetched. + +Imagine you have a query that reads your best friend’s name: + +```graphql +query MyQuery { + me { + best_friend { + # id: 1 + name + } + } +} +``` + +After you get your query response, *who* your best friend is *changes* on the server. Then a *different* query/mutation/subscription fetches a different set of fields off of `best_friend`. + +```graphql +query OtherQuery { + me { + best_friend { + # new id: 2 + # Note: name is not fetched here + age + } + } +} +``` + +Because the Relay store is normalized, we will update the `me` record to indicate that `best_friend` linked field now points to the user with ID 2, and the only information we know about that user is their age. + +This will trigger a rerender of `MyQuery`. However, when we try to read the `name` field off of the user with ID 2, we won’t find it, since the only thing we know about the user with ID 2 is their `age`. Note that a relationship “change” in this case, could also mean a relationship that is new. For example, if you start with no best friend but a subsequent response returns *some* best friend, but does not fetch all fields your component needs. + +**Note**: In theory, Relay *could* refetch your query when this state is encountered, but some queries are not safe to re-issue arbitrarily, and more generally, UI state changing in a way that’s not tied to a direct user action can lead to confusion. For this reason, we have chosen not to perform refetches in this scenario. + +**🕵️‍♀️ How to tell:** You can place a breakpoint/`console.log` at the finale return statement of `readWithIdentifier` in `FragmentResource` ([code pointer](https://github.com/facebook/relay/blob/2b9876fcbf0845cd23728d4d720712525ff424c4/packages/react-relay/relay-hooks/FragmentResource.js#L475). This is the point in Relay at which we know that we are missing data, but there is not query in flight to get it. + +## Inconsistent Server Response + +This is a **rare edge case**, *but* if the server does not correctly implement the [field stability](https://graphql.org/learn/global-object-identification/#field-stability) semantics of the id field, it’s possible that a field could be present in one part of the response, but *explicitly null* in another. + +```graphql +{ + me { + id: 1 + name: "Alice" + } + me_elsewhere_in_the_graph { + id: 1 # Note this is the same as the `me` field above... + name: null + } +} +``` + +In this case, Relay first learns that user 1’s `name` is Alice, but later in the query finds that user 1’s `name` has now `null`. Because Relay stores data in a normalized store, user 1 can only have one value for `name` and Relay will end in a state where user 1’s `name` is `null`. + +**🕵️‍♀️ How to tell:** Relay is smart enough to detect when this has happened, and will [log an error to the console](https://github.com/facebook/relay/blob/2b9876fcbf0845cd23728d4d720712525ff424c4/packages/relay-runtime/store/RelayResponseNormalizer.js#L505) in dev that looks like: “RelayResponseNormalizer: Invalid record. The record contains two instances of the same id: 1 with conflicting field, name and its values: Alice and null.". Additionally, you can manually inspect the query response. + +Note that if the unstable field is a linked field (edge to another object), this type of bug can cause a Graph Relationship Change (described above) to occur *within a single response*. For example, if a user with the same `id` appears in two places in the response, but their `best_friend` is different in those two locations. + +**🕵️‍♀️ How to tell:** Relay is also smart enough to detect this case, and will show a [similar console warning](https://github.com/facebook/relay/blob/2b9876fcbf0845cd23728d4d720712525ff424c4/packages/relay-runtime/store/RelayResponseNormalizer.js#L844) in dev. + + + +:::note +Because these errors exist in the codebase and can cause noisy console output, these warnings are [disabled](https://www.internalfb.com/code/www/[5b26a6bd37e8]/html/shared/core/WarningFilter.js?lines=559) in dev mode. +::: + + + + +## Client-side Deletion or Incomplete Update + +Imperative store updates, or optimistic updates could have deleted the record or field. If an imperative store update, or optimistic update, writes a new record to the store, it may not supply a value for a field which you expected to be able to read. This is a fundamental problem, since an updater cannot statically know all the data that might be accessed off of a new object. + +**🕵️‍♀️ How to tell:** Due to React and Relay’s batching, it’s not always possible to associate a component update with the store update that triggered it. Here, your best bet is to set a breakpoint in your component for when your value is null, and then use the Relay Dev Tools to look at the last few updates. + +This can happen due to a newly created object which did not supply a specific field or, as mentioned above, an update which causes a new or changed relationship in the graph. In this case, use the “How do tell” tip from that section. + + diff --git a/website/versioned_docs/version-v19.0.0/editor-support.md b/website/versioned_docs/version-v19.0.0/editor-support.md new file mode 100644 index 0000000000000..93b0400685a89 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/editor-support.md @@ -0,0 +1,55 @@ +--- +id: editor-support +title: Editor Support +slug: /editor-support/ +keywords: +- LSP +- Language Server Protocol +- VS Code +- VSCode +--- + +import useBaseUrl from '@docusaurus/useBaseUrl'; + +*TL;DR: We have a [VS Code Extension](https://marketplace.visualstudio.com/items?itemName=meta.relay)* + +--- + +The Relay compiler has a rich understanding of the GraphQL embedded in your code. We want to use that understanding to improve the developer experience of writing apps with Relay. So, starting in [v14.0.0](https://github.com/facebook/relay/releases/tag/v14.0.0), the new Rust Relay compiler can provide language features directly in your code editor. This means: + +#### Relay compiler errors surface as red squiggles directly in your editor + + + +#### Autocomplete throughout your GraphQL tagged template literals + + + +#### Hover to see type information and documentation about Relay-specific features + + + +#### `@deprecated` fields are rendered using ~~strikethrough~~ + + + +#### Click-to-definition for fragments, fields and types + + + +#### Quick fix suggestions for common errors + + + +## Language Server + +The editor support is implemented using the [Language Server Protocol](https://microsoft.github.io/language-server-protocol/) which means it can be used by a variety of editors, but in tandem with this release, [Terence Bezman](https://twitter.com/b_ez_man) from [Coinbase](https://www.coinbase.com/) has contributed an official VS Code extension. + +[**Find it here!**](https://marketplace.visualstudio.com/items?itemName=meta.relay) + +## Why Have a Relay-Specific Editor Extension? + +The GraphQL foundation has an official language server and [VS Code extension](https://marketplace.visualstudio.com/items?itemName=GraphQL.vscode-graphql) which provides editor support for GraphQL generically. This can provide a good baseline experience, but for Relay users, getting this information directly from the Relay compiler offers a number of benefits: + +* Relay compiler errors can surface directly in the editor as “problems”, often with suggested quick fixes +* Hover information is aware Relay-specific features and directives and can link out to relevant documentation diff --git a/website/versioned_docs/version-v19.0.0/error-reference/unknown-field.md b/website/versioned_docs/version-v19.0.0/error-reference/unknown-field.md new file mode 100644 index 0000000000000..e8e7099bf5f23 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/error-reference/unknown-field.md @@ -0,0 +1,36 @@ +--- +id: unknown-field +title: "Why Is My Field Not Found?" +slug: /error-reference/unknown-field/ +description: Get help figuring out why a given field is not found. +keywords: +- no such field on type +- missing +- field +- type +- compilation +- error +--- + +import {FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; +import DocsRating from '@site/src/core/DocsRating'; + +## [ERROR] Error in the project `some_project`: ✖︎ The type `Some_Type` has no field `some_unknown_field`. + +### Field name typo + +In case of a missing field in a type, the Relay compiler tries to find and suggest a field replacement. For example: ```Error in the project `some_project`: ✖︎ The type `UserInfo` has no field `mail`. Did you mean `email`?``` + + + +### The field exists in another schema + +Relay Compiler uses schemas in order to resolve types and their fields. The type's schema is determined by the file path and the mapping from file path to schema, which is configured in the "schema" or "schemaDir" properties of your Relay compiler config. If you expect this field to exist, make sure you're using the right schema. + +:::note +At meta there are various project config files that are listed [here](https://www.internalfb.com/intern/wiki/Relay-team/Rust_compiler_resources/#where-are-the-project-co). +::: + + + + diff --git a/website/versioned_docs/version-v19.0.0/getting-started/babel-plugin.md b/website/versioned_docs/version-v19.0.0/getting-started/babel-plugin.md new file mode 100644 index 0000000000000..5daab60584de2 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/getting-started/babel-plugin.md @@ -0,0 +1,31 @@ +--- +id: babel-plugin +title: Relay Babel Plugin +slug: /getting-started/babel-plugin/ +description: Setting up Relay's Babel plugin +keywords: +- babel +--- +# Babel Plugin + +Relay requires a [Babel plugin](https://www.npmjs.com/package/babel-plugin-relay) to convert GraphQL to compiler-generated runtime artifacts. Depending upon what framework/bundler you are using, there may be a framwork-specific plugin you can use: + +- Vite: [vite-pugin-relay](https://github.com/oscartbeaumont/vite-plugin-relay) +- Next.js: [Relay config opton](https://nextjs.org/docs/architecture/nextjs-compiler#relay) +- SWC: [@swc/plugin-relay](https://www.npmjs.com/package/@swc/plugin-relay) + +If not, you can install the Babel plugin manually: + +```sh +yarn add --dev babel-plugin-relay graphql +``` + +Add `"relay"` to the list of plugins in your `.babelrc` file: + +```javascript +{ + "plugins": ["relay"] +} +``` + +Please note that the `"relay"` plugin should run before other plugins or presets to ensure the `graphql` template literals are correctly transformed. See Babel's [documentation on this topic](https://babeljs.io/docs/plugins/#pluginpreset-ordering). diff --git a/website/versioned_docs/version-v19.0.0/getting-started/compiler.md b/website/versioned_docs/version-v19.0.0/getting-started/compiler.md new file mode 100644 index 0000000000000..45db925f5bf23 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/getting-started/compiler.md @@ -0,0 +1,77 @@ +--- +id: compiler +title: Relay Compiler +slug: /guides/compiler/ +description: Relay guide to the compiler +keywords: +- compiler +--- + +Relay depends upon ahead-of-time compilation of GraphQL queries and fragments to generate artifacts that are used at runtime. + +The Relay compiler is a command line tool that reads GraphQL fragments, queries, and mutations in your JavaScript code and generates TypeScript/Flow types and additional JavaScript code that gets included in your code via [Relay's Babel Plugin](./babel-plugin.md). + +This guide explains configuring and using the Relay Compiler. + +## Configuration + +The Relay compiler will look for a Relay config in the following locations. It's up to you to decide which location works best for your project. + +* `relay.config.json` in your project root +* `relay.config.(js/mjs/ts)` in your project root +* A `"relay"` key in your `package.json` + +The Relay compiler config tells Relay things like where it can find your GraphQL schema and what language your code is writen in. A minimal Relay compiler config looks like this: + +```json title="relay.config.json" +{ + "src": "./src", + "schema": "./schema.graphql", + "language": "typescript" +} +``` + +The compiler config is very powerful, and includes many specialized configuration options. These are not yet exhaustively documented. In the mean time see [relay-compiler-config-schema.json](https://github.com/facebook/relay/blob/main/compiler/crates/relay-compiler/relay-compiler-config-schema.json). + +:::tip +Install the [Relay VSCode extension](../editor-support.md) to get autocomplete, hover tips, and type checking for the options in your relay config. +::: + +## Running the compiler + +It is generally recommended that you add a `scripts` entry to your `package.json` to make it easy to run the Relay compiler for your project. + +```json title="package.json" +{ + "scripts": { + // change-line + "relay": "relay-compiler" + } +} +``` + +With this added you can run the Relay compiler like so: + +```sh +npm run relay +``` + +### Watch mode + +If you have [watchman](https://facebook.github.io/watchman) installed you can pass `--watch` to the Relay compiler to have it continue running and automatically update generated files as you edit your product code: + +```sh +npm run relay --watch +``` + +### Codemods + +The Relay compiler supprts some built in codemods. Learn more in the [Codemods Guide](../guides/codemods.md). + +### Help + +To learn about the other capabilities of the Relay compiler see it's extensive `--help` output: + +```sh +npm run relay --help +``` diff --git a/website/versioned_docs/version-v19.0.0/getting-started/production.md b/website/versioned_docs/version-v19.0.0/getting-started/production.md new file mode 100644 index 0000000000000..c7a0c6fa36d38 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/getting-started/production.md @@ -0,0 +1,28 @@ +--- +id: production +title: Production Setup +slug: /getting-started/production/ +description: Setting up Relay for production use +keywords: +- production +--- + +Getting the most out of Relay in production requires a few extra steps. This page covers a list of best practices for taking Relay to production. + +## Persisted Queries (Trusted Documents) + +One of GraphQL's key innovations is that it enables clients to define arbitrary queries. While this unlocks a lot of flexibility, it also opens the doors to abuse. Scrapers can use GraphQL to scrape data from your site, and malicious users can use GraphQL to send large requests to your server that cause a denial of service. To prevent this, we recommend using [Persisted Queries](../guides/persisted-queries.md) also know as [Trusted Documents](https://benjie.dev/graphql/trusted-documents). + +With Persisted Queries, the set of queries that the client can send is locked in at build time. This means that scrapers and malicious attackers are limited to sending only the queries used by the client, just like REST. It also has the added benefit that the client code only needs to include an single id for each query rather than the whole query text. + + + +## Relay Lint Rules + +A key principal of Relay is data colocation where each component defines its own data dependencies. It's critical to [How Relay Enables Optimal Data Fetching](https://relay.dev/blog/2023/10/24/how-relay-enables-optimal-data-fetching/). However, this is often unintuitive for developers who are used to fetching data in a single query. Relay's ESLint rules can help enforce this pattern by ensuring no component fetches fields which it does not itself use. + +We recommend using the [`eslint-plugin-relay`](https://github.com/relayjs/eslint-plugin-relay), especially the `relay/unused-fields` rule. + +## Running the Relay Compiler in CI + +We recommend committing Relay's generated artifacts to source control along with your application code. This ensures generated types are present without needing an additional build, and allows for inspection of generated artifacts in code review. To ensure the generated artifacts are always in sync with the source code, we recommend running the Relay compiler in CI and ensuring it does not change any generated files. A example bash script which checks for changes can be found [here](https://github.com/facebook/relay/blob/0414c9ad0744483e349e07defcb6d70a52cf8b3c/scripts/check-git-status.sh). diff --git a/website/versioned_docs/version-v19.0.0/getting-started/quick-start.md b/website/versioned_docs/version-v19.0.0/getting-started/quick-start.md new file mode 100644 index 0000000000000..584afaf028f87 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/getting-started/quick-start.md @@ -0,0 +1,208 @@ +--- +id: quick-start +title: Quick Start +slug: /getting-started/quick-start/ +description: Get up an running with Relay +keywords: +- quick +--- +This quick start guide will start with a new React app using Vite and show you how to add Relay to it. + +:::tip +If you'd prefer an automated approach, [`create-relay-app`](https://github.com/tobias-tengler/create-relay-app) by Tobias Tengler will walk you through adding Relay to an existing React app via a series of prompts: `npm create @tobiastengler/relay-app` +::: + +We will be building a simple app which shows Star Wars movies fetched from the [example Star Wars GraphQL API](https://graphql.org/swapi-graphql/) hosted by graphql.org. + +## Scaffold a React App + +We’ll start with a [Vite](https://vite.dev/) React app using TypeScript. + +```bash +npm create vite -- --template react-ts +``` + +You’ll be prompted for a project name. Type: `relay-example` + +## Install Dependencies + +```bash +cd relay-example + +# Runtime dependencies +npm install relay-runtime react-relay +# Dev dependencies +npm install --save-dev babel-plugin-relay graphql relay-compiler +# Types +npm install --save-dev @types/relay-runtime @types/react-relay +``` + +## Configure Vite to use Relay + +Relay uses a [Babel plugin](./babel-plugin.md) to insert code generated by the Relay compiler into your bundle. We can enable the Relay Babel plugin we installed earliery by configuring the React Vite plugin. + +```tsx title="vite.config.ts" +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [ + // change-line + react({ babel: { plugins: ["relay"] } }) + ], +}) +``` + +See [Babel Plugin](./babel-plugin.md) for information about how to configure the Babel plugin for other build systems. + +## Configure the Relay Compiler + +Next we will download the GraphQL schema for the Star Wars GraphQL endpoint. + +```bash +curl -O https://raw.githubusercontent.com/graphql/swapi-graphql/refs/heads/master/schema.graphql +``` + +And define our `relay.config.json` config file which tells the [Relay Compiler](./compiler.md) which schema file we want it to use and other details about our project. + +```json title="relay.config.json" +{ + "src": "./src", + "schema": "./schema.graphql", + "language": "typescript", + "eagerEsModules": true +} +``` + +See [Relay Compiler](./compiler.md) for more information about configuring and running the Relay compiler. + +## Configure your Relay Environment + +To allow components within our application to fetch GraphQL we configure a Relay Environment to fetch from our test endpoint and add it to React context. + +```tsx title="src/main.tsx" +import { StrictMode, Suspense } from "react"; +import { createRoot } from "react-dom/client"; +import "./index.css"; +import App from "./App.tsx"; +import { RelayEnvironmentProvider } from "react-relay"; +import { Environment, Network, FetchFunction } from "relay-runtime"; + +const HTTP_ENDPOINT = "https://graphql.org/graphql/"; + +const fetchGraphQL: FetchFunction = async (request, variables) => { + const resp = await fetch(HTTP_ENDPOINT, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ query: request.text, variables }), + }); + if (!resp.ok) { + throw new Error("Response failed."); + } + return await resp.json(); +}; + +const environment = new Environment({ + network: Network.create(fetchGraphQL), +}); + +createRoot(document.getElementById("root")!).render( + + + + + + + +); +``` + +See [Relay Environment](../api-reference/relay-runtime/relay-environment.md) for an overview of the Relay Environment and how to configure it. + +:::tip +`` exposes your Environment via [React context](https://react.dev/learn/passing-data-deeply-with-context), so it must wrap your entire application. +::: + +## Define your first Relay component + +Finally we can start defining the data we want to fetch and build our UI. Our app will fetch a list of films and render each one using a `` component. + +```tsx title="src/App.tsx" +import { AppQuery } from "./__generated__/AppQuery.graphql"; +import { graphql, useLazyLoadQuery } from "react-relay"; +import Film from "./Film"; + +export default function App() { + const data = useLazyLoadQuery( + graphql` + query AppQuery { + allFilms { + films { + id + ...Film_item + } + } + } + `, + {} + ); + + const films = data?.allFilms?.films?.filter((film) => film != null); + + return ( +
+

Star Wars Films

+ {films?.map((film) => ( + + ))} +
+ ); +} +``` + +## Define your first fragment + +One of Relay's core principles is that each component should define its own data dependencies. So, we define our `` component using a [GraphQL Fragment](https://graphql.org/learn/queries/#fragments). + +```typescript title="src/Film.tsx" +import { graphql, useFragment } from "react-relay"; +import type { Film_item$key } from "./__generated__/Film_item.graphql"; + +export default function FilmListItem(props: { film: Film_item$key; }) { + const film = useFragment( + graphql` + fragment Film_item on Film { + title + director + } + `, + props.film + ); + + return ( +
  • + {film.title}: directed by {film.director} +
  • + ); +} +``` + +## Compile and run your app + +All that’s left is to run the Relay compiler and start your app! + +The Relay compiler generates TypeScript types and combines your queries and fragments into optimized representations. You have to run the Relay compiler each time you modify your GraphQL queries or fragments. + +:::tip +If you have [Watchman](https://facebook.github.io/watchman/) installed you can run `npx relay-compiler --watch` to have the compiler run in watch mode, but you'll need to run `echo "{}" > .watchmanconfig` to create a Watchman root. +::: + +```bash +npx relay-compiler +npm run dev +``` + +You should now be able to open your app in a browser: [http://localhost:5173/](http://localhost:5173/) + +May the force be with you! diff --git a/website/versioned_docs/version-v19.0.0/glossary/glossary.md b/website/versioned_docs/version-v19.0.0/glossary/glossary.md new file mode 100644 index 0000000000000..0cf006628ee8e --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/glossary/glossary.md @@ -0,0 +1,996 @@ +--- +id: glossary +title: Glossary +slug: /glossary/ +description: Relay terms glossary +keywords: +- glossary +--- + +import DocsRating from '@site/src/core/DocsRating'; +import {FbInternalOnly, OssOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; + +## 3D + +Data-Driven Dependencies. Facebook's way of including the code to render a particular component if and only if it will actually be rendered. Canonical use cases are + +* **Fields that are typically null**, and which are only rendered when not null. +* **Unions**. For example, the core news feed item has many different variants, each of which is a separate React component. Which one we render depends on the data (i.e. is "data-driven"). On a given feed, it is likely that most variants will not be rendered, and need not be downloaded. +* **Component can have different rendering strategies, depending on the data.** + + + +See the [@match](#match) directive, [@module](#module) directive and [the 3D guide](../guides/data-driven-dependencies/server-3d). + + + + + +See the [@match](#match) directive and the [@module](#module) directive. + + + +## Abstract Type + +GraphQL unions and interfaces are abstract types. See [interface](#interface-graphql). + +## Abstract Type Refinement + +See [type refinement](#type-refinement). If type refinement is a way of conditionally including fields if a type implements a particular concrete type (such as `... on User { name }`), abstract type refinement refers to conditionally including fields if a type implements a particular abstract type (i.e. interface). So, `... on Actor { field }`. + +## @arguments + +A [directive](#directive) that modifies a [fragment spread](#fragment-spread) and is used to pass arguments (defined with [`@argumentDefinitions`](#argumentdefinitions)) to that fragment. + +```graphql +...Story_story @arguments(storyId: "1234") +``` + +## @argumentDefinitions + +A directive that modifies a fragment definition and defines the names of the local arguments that the fragment can take, as well as their type. + +```graphql +fragment Store_story on Story + @argumentDefinitions(storyId: {type: "ID!"}) { + # etc +} +``` + +If a variable is used in a fragment but not included in an `@argumentDefinitions` directive, Relay will require that the fragment is only spread in queries which declare these variables, or in fragments which ultimately are spread in such a query. + +Compare with [variables](#variables) and see the [relevant section](../guided-tour/rendering/variables) in the guided tour. + +## Artifact + +Files that are generated by the Relay compiler, typically ending in `.graphql.js`. + + + +See [a guide to Relay artifacts](https://www.internalfb.com/intern/wiki/Relay-team/generated-artifacts/). + + + +## AST + +Abstract Syntax Tree. In Relay, there are two types of ASTs, [normalization](#normalization-ast) and [reader](#reader-ast) ASTs. + +The default export of a `.graphql.js` file is an AST. + +The Relay compiler parses and transforms GraphQL literals, and generates Relay ASTs (see [artifact](#artifact)). Doing this work at compile time allows the Relay runtime to be faster. + +## Availability + +The concept of availability refers to whether there is enough non-stale, non-invalidated data in the store to fulfill a particular request immediately, or whether a request to server needs to be made in order to fulfill that request. + +## Babel Transform + +A build-time transformation of the Javascript codebase, which turns calls to + +```javascript +graphql`...` +``` + +into `require(NAME_OF_GENERATED_ARTIFACT)` calls. + +## Client Schema Extension + +[The GraphQL spec](https://spec.graphql.org/June2018/#sec-Schema-Extension) allow you to define new types, new fields on types, new directives, etc. locally. + +Relay supports adding types and fields in client schema extension files. Developers use this feature to add fields that contain purely local state that is associated with items on the graph. For example, an `is_selected` field on a `User`. + +## CacheConfig + +A value used to control how a query's response may be cached. Ultimately passed to `environment.execute`. + +## Check + +One of the core functions of the store. Given an operation, determines whether the store has all of the data necessary to render that operation. Calls `DataChecker.check`, which synchronously starts with the root node associated with the operation and walks the data in the store. + +In practice, exposed as a method on `environment`. + +In conjunction with the fetch policy, used by `loadQuery` (and other methods) to determine whether it is necessary to make a network request call to fulfill a query. + +## Commit + +After receiving a network response, the payload is committed, or written to the store. + +Commit is also the verb used to describe initiating a mutation and writing its data to the store. + +## Compiler + +The piece of code which scans your Javascript files for `graphql` tagged nodes and generates the appropriate files (`.graphql.js` files, `$Parameters.js` files, etc.) + +The generated output from the compiler is committed and checked into the repository. + +## Concrete Request + +An Abstract Syntax Tree representing a query, subscription or mutation. + +The default export of a `.graphql.js` file corresponding to a query, subscription or mutation. + +In addition, calls to `graphql`...`` are turned into concrete requests at build time via the Relay Babel transform. + +**See the important safety notes at Preloadable Concrete Request.** + +## Config + +A file or javascript object which controls, among other things, which files are scanned by the Relay [compiler](#compiler) for your project. + +## @connection + +A directive which declares that a field implements the [connection](#connection) spec. + +## Connection + + + +A field implementing the [connection spec](https://relay.dev/graphql/connections.htm). See here for more details on the spec, and the section of the guided tour on rendering list data and pagination. + + + + + +A field implementing the [connection spec](https://relay.dev/graphql/connections.htm). See the section of the guided tour on rendering list data and pagination. + + + +See also [`usePaginationFragment`](../api-reference/use-pagination-fragment). + +## Container + +A term for a higher order component that provided a child component with the data from queries and fragments. Associated with Relay Modern. + +You should use the Relay hooks API when possible. + +## Data Checker + +A class exposing a single method, `check`, which synchronously starts with the root node associated with the operation and walks the data in the store. It determines whether the data in the store suffices to fulfill a given operation. + +Called by `store.check`. + +## DataID + +The globally-unique identifier of a record. Can be generated on the client with [missing field handlers](#missing-field-handler). Usually corresponds to an Ent's ID (if available), but guaranteed to equal the value of the `__id` field. + +[`updater`](#updater) and [`optimisticUpdater`](#optimisticupdater) functions are passed instances of [`RelaySourceSelectorProxy`](#recordproxy). Calling `.get(id)` with the DataID on a `RelaySourceSelectorProxy` will look up that item in the store, and return a proxy of it. + +## Data Masking + +Refers to the idea that a component should not be able to access any data it does declare in its fragment or query, even inadvertently. This prevents accidental coupling of data between components, and means that every component can be refactored in isolation. It negates the risk that removing a field in a child component will accidentally break a different component, allowing components to *move fast, with stable infrastructure*. + +Also refers to the practice of hiding the data of child components from their parents, in keeping with the idea. + +In Relay, a query declared like `query FooQuery { viewer { ...subcomponent_``viewer_name } }` will not be able to access the data declared by `subcomponent_viewer_name` without access to the `ReaderFragment` representing the `subcomponent_viewer_name` fragment. + +See the [Thinking in Relay guide](../principles-and-architecture/thinking-in-relay#data-masking). + +## @defer + +A directive which can be added to a fragment spread or inline fragment to avoid blocking on that fragment's data. For more detail refer to GraphQL's [documentation on the @defer directive](https://github.com/graphql/graphql-wg/blob/main/rfcs/DeferStream.md#defer). + + + +See [Incremental Data Delivery](https://www.internalfb.com/intern/staticdocs/relay/docs/guides/incremental-data-delivery/). + + + +## Definition + +In the compiler, a definition refers to the text within a GraphQL literal where an operation or fragment was defined. + +## Descriptor + +Can refer to an `OperationDescriptor` or `RequestDescriptor`. Descriptors are types used internally to the Relay codebase, and generally, refer to an object containing the minimum amount of information needed to uniquely identify an operation or request, such as (for a `RequestIdentifier`), a node, identifier and variables. + +## DevTools + +An awesome Chrome extension for debugging Relay network requests, the Relay store and Relay events. Helpful for answering questions like "Why am I not seeing the data I expect to see?" "Why did this component suspend?" etc. + +See the [documentation](https://relay.dev/docs/debugging/relay-devtools/). + +## Document + +In the compiler, a Document refers to a GraphQL literal that contains one or more operation or fragment [definitions](#definition). Relay requires that GraphQL literals in JavaScript files contain a single definition. + +## Directive + +A special instruction, starting with `@` and contained in a `graphql` literal or graphql file, which provides special instructions to the Relay compiler or to the server. Examples include `@defer`, `@stream` and `@match`. + +## Disposable + +Any object which contains a `.dispose` method which takes no parameters and provides no return value. Many objects in Relay (such query references and entrypoint references) and the return value of many methods (such as calls to `.subscribe` or `.retain`) are disposables. + +## Entrypoint + +A lightweight object containing information on the components which need to be loaded (as in the form of calls to `JSResource`) and which queries need to be loaded (in the form of preloadable concrete requests) before a particular route, popover or other piece of conditionally loaded UI can be rendered. + +All queries which are required for the initial rendering of a piece of UI should be included in that UI's entrypoint. + +Entrypoints can contain queries and other entrypoints. + +See also [preloadable concrete request](#preloadable-concrete-request) and [JSResource](#jsresource). + +## Environment + +An object bringing together many other Relay objects, most importantly a store and a network. Also, includes a publish queue, operation loader, scheduler and [missing fields handlers](#missing-field-handler). + +Set using a `RelayEnvironmentProvider` and passed down through React context. + +All non-internal Relay hooks require being called within a Relay environment context. + +## Execute + +Executing a query, mutation or subscription (collectively, an operation) roughly means "create a lazy observable that, when subscribed to, will make a network request fulfilling the operation and write the returned data to the store." + +A variety of `execute` methods are exposed on the Relay environment. + +## Fetch Policy + +A string that determines in what circumstances to make a network request in which circumstances to fulfill the query using data in the store, if available. Either `network-only`, `store-and-network`, `store-or-network` or `store-only`. (Some methods do not accept all fetch policies.) + +## Field + +Basically, anything you can select using a query, mutation, subscription or fragment. For example, `viewer`, `comment_create(input: $CommentCreateData)` and `name` are all fields. + +The GraphQL schema comprises many fields. + +## Fragment + +Fragment is an overloaded term, and has at least two distinct meanings in Relay. + +### Fragments as a GraphQL concept + +The fundamental reusable unit of GraphQL. Unlike queries, subscriptions and mutations, fragments cannot be queried on their own and must be embedded within a request. + +Fragments can be spread into queries, mutations, subscriptions and other fragments. + +Fragments can be standalone (as in `fragment Component_user on User { name }`) or inline, as in the `... on User { name }` in `query MyQuery { node(id: $id) { ... on User { name } } }`. + +Fragments are always defined on a particular type (`User` in the example), which defines what fields can be selected within it. + +### Fragments within Relay + +Within Relay, a fragment refers to the fields that are read out for a given fragment/operation. The term is also used colloquially to refer to reader ASTs. So, e.g. the following query and fragment might have identical reader ASTs: + +```graphql +query Foo { + actor { name } +} +``` + +``` +fragment Bar on Query { + actor { name } +} +``` + +## Fragment Identifier + +A string, providing enough information to provide the data for a particular fragment. For example: + +`1234{"scale":2}/Story_story/{"scale":2}/"4567"` + +This identifies by its persist ID (`1234`), followed by the variables it accepts, followed by the `Story_story` fragment (which does not have a persist id) and the variables it uses, followed by the Data ID (likely, the `id` field) of whatever Story happened to be referenced. + +## Fragment Reference + +A parameter passed to `useFragment`. Obtained by accessing the value onto which a fragment was spread in another [query](#query), fragment, subscription or mutation. For example, + +```javascript +const queryData = usePreloadedQuery( + graphql`query ComponentQuery { viewer { account_user { ...Component_name } } }`, + {}, +); + +// queryData.viewer is the FragmentReference +// Though this would usually happen in another file, you can +// extract the value of Component_name as follows: +const fragmentData = useFragment( + graphql`fragment Component_name on User { name }`, + queryData?.viewer?.account_user, +); +``` + +Just like a query reference and a graphql tagged literal describing a query (i.e. a concrete request) can be used to access the data from a query, a fragment reference and a graphql tagged literal describing a fragment (i.e. a reader fragment) can be used to access the data referenced from a fragment. + +## Fragment Resource + +An internal class supporting lazily loaded queries. Exposes two important methods: + +* `read`, which is meant to be called during a component's render phase. It will attempt to fulfill a query from the store (by calling `environment.lookup`) and suspend if the data is not available. It furthermore writes the results from the attempted read (whether a promise, error or result) to an internal cache, and updates that cached value when the promise resolves or rejects. +* `subscribe`, which is called during the commit phase, and establishes subscriptions to the Relay store. + +If the component which calls `.read` successfully loads a query, but suspends on a subsequent hook before committing, the data from that query can be garbage collected before the component ultimately renders. Thus, components which rely on `FragmentResource` are at risk of rendering null data. + +Compare to [query resource](#query-resource). + +## Fragment Spec Resolver + +TODO + +## Fragment Spread + +A fragment spread is how one fragment is contained in a query, subscription, mutation or other fragment. In the following example, `...Component_name` is a fragment spread: + +```graphql +query ComponentQuery { + viewer { + account_user { + ...Component_name + } + } +} +``` + +In order for a fragment to be spread in a particular location, the types must match. For example, if `Component_name` was defined as follows: `fragment Component_name on User { name }`, this spread would be valid, as `viewer.account_user` has type `User`. + +## Garbage Collection + +Relay can periodically garbage collect data from queries which are no longer being retained. + +See more information in the [guided tour](https://relay.dev/docs/guided-tour/reusing-cached-data/presence-of-data/#garbage-collection-in-relay). + +## GraphQLTaggedNode + +This is the type of the call to + +```js +graphql`...` +``` + +It is the union of ReaderFragment, ReaderInlineDataFragment, ConcreteRequest, and ConcreteUpdatableQuery. + + + +Note that Flow can be configured to understand that the type of a GraphQL literal is the type of the default export of the generated `.graphql.js` file. + + + + + +Note that Flow is configured to understand that the type of a GraphQL literal is the type of the default export of the generated `.graphql.js` file. + + + +## Handler + +TODO + +## ID + +Relay treats ids specially. In particular, it does the following two things: + +* The compiler automatically adds a selection of the `id` field on every type where the `id` field has type `ID` or `ID!`. +* When [normalizing](#normalization) data, if an object has an `id` property, that field is used as its ID in the store. + +There are types in the schema where the `id` field does not have type `ID` or `ID!` (e.g. has the type `string` or `number`). If a user selects this field themselves, this field is used as an id. This is unexpected and incorrect behavior. + +## @include + +A directive that is added to fields, inline fragments and fragment spreads, and allows for conditional inclusion. It is the opposite of the [`@skip`](#skip) directive. + +In the compiler, the `@include`/`@skip` directives are treated specially, and produce `Condition` nodes. + +## @inline + +A directive that applies to fragments which enables developers to pass masked data to functions that are executed outside of the React render phase. + +Normally, data is read out using `useFragment`. However, this function can only be called during the render phase. If store data is needed in a outside of the render phase, a developer has several options: + +* read that data during the render phase, and pass it to the function/have the function close over that data. (See also [#relay]) +* pass a reference to an `@inline` fragment, which can then be accessed (outside of the render phase) using the `readInlineData` function. + +This directive causes them to be read out when the parent fragment is read out, and unmasked by the call to `readInlineData`. + +## Interface (GraphQL) + +An *Interface* is an abstract type that includes a certain set of fields that a type must include to implement the interface. + +You can spread an fragment on an interface onto a concrete type (for example `query MyQuery { viewer { account_user { ...on Actor { can_viewer_message } } }`) or a fragment on a concrete type onto an interface (for example `query MyQuery { node(id: 4) { ... on User { name } } }`). You are no longer allowed to spread a fragment on an interface onto an interface. + +See also abstract type refinement. + +## Invalidation + +In certain cases, it is easy to determine the outcome of a mutation. For example, if you "like" a Feedback, the like count will increment and `viewer_did_like` will be set to true. However, in other cases, such as when you are blocking another user, the full impact on the data in your store is hard to determine. + +For situations like these, Relay allows you to invalidate a record (or the whole store), which will cause the data to be re-fetched the next time it is rendered. + +See the [section in the guide](https://relay.dev/docs/guided-tour/reusing-cached-data/staleness-of-data/). + +## JSResource + +A lightweight API for specifying a that a React component should be loaded on demand, instead of being bundled with the first require (as would be the case if you imported or required it directly.) + +This API is safe to use in entrypoint files. + + + +See [the npm module](https://www.npmjs.com/package/jsresource). + + + +## Lazy Loading + +A query or entry point is lazy loaded if the request for the data occurs at render time. + +Lazy loaded queries and entry points have performance downsides, are vulnerable to being over- and under-fetched, and can result in components being rendered with null data. They should be avoided. + +## Linked Record + +A linked record is a record that is directly accessible from another record. For example, in the query `query MyQuery { viewer { account_user { active_instant_game { id } } } }`, `active_instant_game` (which has the type `Application` is a linked record of `account_user`. + +A linked record cannot be queried by itself, but must be queried by selecting subfields on it. + +Compare to [value](#value). + +## Literal + +A GraphQL literal is a call to + +```javascript +graphql`...` +``` + +in your code. These are pre-processed, and replaced at build time with a [GraphQLTaggedNode](#graphqltaggednode) containing an [AST](#ast) representation of the contents of the literal. + +## @live + +A docblock tag that can be added to mark a Relay resolver as live. To learn more, refer to the [live fields section](https://relay.dev/docs/guides/relay-resolvers/live-fields/) of the Relay resolver documentation. + + + +## @live_query +A directive used on GraphQL queries that enables data updates to be delivered over time without any custom server-side code. This directive provides a more efficient and maintainable alternative to polling (running the same query over and over again). + +Live queries are a feature of GraphQL within Meta and supported by the [Real-Time GraphQL team](https://www.internalfb.com/omh/view/real_time_graphql/oncall_profile). To learn more about GraphQL live queries, refer to the [GraphQL Live Queries wiki](https://www.internalfb.com/intern/wiki/GraphQL_Live_Queries/Overview/). + +You can learn more about how to use @live_query with Relay on Web with server-polling [here](https://www.internalfb.com/intern/wiki/GraphQL_Live_Queries/Live_Queries_for_Relay/) and client-polling [here](https://www.internalfb.com/intern/wiki/GraphQL_Live_Queries/Live_Queries_for_Relay_(Client_Polling)/). + + +## Lookup + +One of the main methods exposed by the Relay store. Using a [reader selector](#reader-selector), traverses the data in the store and returns a [snapshot](#snapshot), which contains the data being read, as well as information about whether data is missing and other pieces of information. Also exposed via the Relay environment. + +Calls [`Reader.read`](#reader). + +## @match + +A directive that, when used in combination with [@module](#module), allows users to download specific JS components alongside the rest of the GraphQL payload if the field decorated with @match has a certain type. See [3D](#3d). + +## MatchContainer + +A component that renders the component returned in conjunction with a field decorated with the [@match](#match) directive. See [3D](#3d). + +## Missing Field Handler + +A function that provides a [DataID](#dataid) for a field (for singular and plural linked fields) and default values (for scalar fields). + +For example, you may have already fetched an item with id: 4, and are executing a query which selects `node(id: 4)`. Without a missing field handler, Relay would not know that the item with id: 4 will be returned by `node(id: 4)`, and would thus attempt to fetch this data over the network. Providing a missing field handler can inform Relay that the results of this selection are present at id: 4, thus allowing Relay to avoid a network request. + +`getRelayFBMissingFieldHandlers.js` provides this and other missing field handlers. + +## @module + +A directive that, when used in combination with [@match](#match), allows users to specify which JS components to download if the field decorated with @match has a certain type. See [3D](#3d). + +## Module + +TODO + +## Mutation + +A mutation is a combination of two things: a mutation on the backend, followed by query against updated data. + + + +See the [guide on mutations](../guided-tour/updating-data/graphql-mutations), and [this article](https://www.internalfb.com/intern/wiki/Graphql-for-hack-developers/mutation-root-fields/) on defining mutations in your hack code. + + + + + +See the [guide on mutations](../guided-tour/updating-data/graphql-mutations). + + + +## Mutation Root Query + +The root object of a mutation query. In an `updater` or `optimisticUpdater`, calling `store.getRootField('field_name')` will return the object from the mutation root query named `field_name`. + +The fields exposed on this object are **not** the same as those available for queries, and differ across mutations. + +## Network + +Relay environments contain a `network` object, which exposes a single `execute` function. All network requests initiated by Relay will go through this piece of code. + +This provides a convenient place to handle cross-cutting concerns, like authentication and authorization. + +## Node + +TODO + +## Normalization + +Normalization is the process of turning nested data (such as the server response) and turning it into flat data (which is how Relay stores it in the store.) + +See the [response normalizer](#response-normalizer). + +## Normalization AST + +An [AST](#ast) that is associated with an [operation](#operation) that (in combination with [variables](#variables)) can be used to: +* write a network payload to the store, +* write an optimistic response to the store, +* determine whether a query can be fulfilled from data in the store, and +* determine which records in the store are reachable (used in [garbage collection](#garbage-collection)). + +Unlike the [reader AST](#reader-ast), the normalization AST includes information on the contents of nested fragments. + +The generated artifact associated with an operation (e.g. `FooQuery.graphql.js`) contains both a normalization AST and a reader AST. + +## Normalization Selector + +A selector defines the starting point for a traversal into the graph for the purposes of targeting a subgraph, combining a GraphQL fragment, variables, and the Data ID for the root object from which traversal should progress. + +## Notify + +A method exposed by the store which will notify each [subscriber](#subscribe) whose data has been modified. Causes components which are rendering data that has been modified to re-render with new data. + +## Observable + +The fundamental abstraction in Relay for representing data that may currently be present, but may also only be available in the future. + +Observables differ from promises in that if the data in an observable has already been loaded, you can access it synchronously as follows: + +```javascript +const completedObservable = Observable.from("Relay is awesome!"); +let valueFromObservable; +observable.subscribe({ + next: (value) => { + valueFromObservable = value; + /* this will execute in the same tick */ + }, +}); +console.log(valueFromObservable); // logs out "Relay is awesome!" +``` + +This is advantageous, as it allows Relay hooks to not suspend if data is already present in the store. + +In Relay, observables are a partial implementation of [RxJS Observables](https://rxjs-dev.firebaseapp.com/guide/observable). + +## Operation + +In [GraphQL](https://spec.graphql.org/June2018/#sec-Language.Operations), a query, subscription or mutation. + +In Relay, every operation also has an associated [fragment](#fragments-within-relay). So, an accurate mental model is that operations are fragments whose [type condition](#type-condition) is that they are on [Query/Mutation/Subscription](#root-type) and for which Relay knows how to make a network request. + +## Operation Descriptor + +Colloquially, an operation descriptor is an operation and variables. + +The operation descriptor flowtype contains the three pieces of information that Relay needs to work with the data: [a reader selector](#reader-selector), a [normalization selector](#normalization-selector) and a [request descriptor](#request-descriptor). + +The variables are filtered to exclude unneeded variables and are populated to include default values for missing variables, thus ensuring that requests that differ in irrelevant ways are cached using the same request ID. + +## Operation Mock Resolver + +A function taking an operation descriptor and returning a network response or error, used when testing. + +## Operation Tracker + +TODO + +## Optimistic Update + +TODO + +## Optimistic Updater + +TODO + +## Pagination + +Querying a list of data (a [connection](#connection)) in parts is known as pagination. + +See the [graphql docs](https://graphql.org/learn/pagination/) and our [guided tour](../guided-tour/list-data/pagination). + +## Payload + +The value returned from the GraphQL server as part of the response to a request. + +## Plural Field + +A field for which the value is an array of [values](#value) or [records](#record). + +## @preloadable + +A directive that modifies queries and which causes Relay to generate `$Parameters.js` files and preloadable concrete requests. Required if the query is going to be used as part of an entry point. + +## Preloadable Concrete Request + +A small, lightweight object that provides enough information to initiate the query and fetch the full query AST (the `ConcreteRequest`.) This object will only be generated if the query is annotated with `@preloadable`, and is the default export of `$parameters.js` files. It is only generated for queries which are annotated with `@preloadable`. + +Unlike concrete requests (the default export of `.graphql.js` files), preloadable concrete requests are extremely light weight. + +Note that entrypoints accept either preloadable concrete requests or concrete requests in the `.queries[queryName].parameters` position. However, ***because a concrete request is not a lightweight object, you should only include preloadable concrete requests here.*** + +Note also that preloadable queries have `id` fields, whereas other queries do not. + +## Preloadable Query Registry + +A central registry which will execute callbacks when a particular Query AST (concrete request) is loaded. + +Required because of current limitations on dynamically loading components in React Native. + +## Project + +For Relay to process a file with a GraphQL literal, it must be included in a project. A project specifies the folders to which it applies and the schema against which to evaluate GraphQL literals, and includes other information needed by the Relay compiler. + + + +Projects are defined in a single [config](#config) file, found [here](https://www.internalfb.com/intern/diffusion/WWW/browse/master/scripts/relay/compiler-rs/config.www.json) and [here](https://www.internalfb.com/intern/diffusion/FBS/browse/master/xplat/relay/compiler-rs/config.xplat.json). + + + +## Profiler + +TODO + +## Publish + +One of the main methods exposed by the `store`. Accepts a [record source](#record-source), from which the records in the store are updated. Also updates the mapping of which records in the store have been updated as a result of publishing. + +One or more calls to `publish` should be followed by a call to [`notify`](#notify). + +## Publish Queue + +A class used internally by the environment to keep track of, apply and revert pending (optimistic) updates; commit client updates; and commit server responses. + +Exposes mutator methods like `commitUpdate` that only add or remove updates from the queue, as well as a `run` method that actually performs these updates and calls `store.publish` and `store.notify`. + +## Query + +A [GraphQL query](https://graphql.org/learn/queries/) is a request that can be sent to a GraphQL server in combination with a set of [variables](../guided-tour/rendering/variables), in order to fetch some data. It consists of a [selection](#selection) of fields, and potentially includes other [fragments](#fragment). + +## Query Executor + +A class that normalizes and publishes optimistic responses and network responses from a network observable to the store. + +After each response is published to the store, `store.notify` is called, updating all components that need to re-render. + +Used by `environment` in methods such as `execute`, `executeWithSource` and `executeMutation`, among others. + +## Query Reference + +TODO + +## Query Resource + +A class for helping with lazily loaded queries and exposing two important methods: `prepare` and `retain`. + +* `prepare` is called during a component's render method, and will either read an existing cached value for the query, or fetch the query and suspend. It also stores the results of the attempted read (whether the data, a promise for the data or an error) in a local cache. +* `retain` is called after the component has successfully rendered. + +If the component which calls `.prepare` successfully loads a query, but suspends on a subsequent hook before committing, the data from that query can be garbage collected before the component ultimately renders. Thus, components which rely on `QueryResource` are at risk of rendering null data. + +Compare to [fragment resource](#fragment-resource). + +## `@raw_response_type` + +A directive added to queries which tells Relay to generate types that cover the `optimisticResponse` parameter to `commitMutation`. + +See the [guided tour on updating data](../guided-tour/updating-data/graphql-mutations/#optimistic-response) for more. + +## Reader + +TODO this section + +## Reader AST + +An [AST](#AST) that is used to read the data selected in a given fragment. + +Both [operations](#operation) and [fragments](#fragment) have reader ASTs. + +A reader AST contains information about which fragments are spread at a given location, but unlike a [normalization AST](#normalization-ast), does not include information about the fields selected within these fragments. + +## Reader Fragment + +TODO + +See [GraphQLTaggedNode](#graphqltaggednode). + +## Reader Selector + +An object containing enough information for the store to traverse its data and construct an object represented by a query or fragment. Intuitively, this "selects" a portion of the object graph. + +See also [lookup](#lookup). + +## Record + +A record refers to any item in the Relay [store](#store) that is stored by [ID](#id). [Values](#value) are not records; most everything else is. + +## Record Source + +An abstract interface for storing [records](#record), keyed by [DataID](#dataid), used both for representing the store's cache for updates to it. + +## Record Source Selector Proxy + +See [record proxy](#record-proxy). + +## Record Proxy + +See the [store documentation](../api-reference/store). + +## Ref Counting + +The pattern of keeping track of how many other objects can access a particular object, and cleaning it up or disposing of it when that number reaches zero. This pattern is implemented throughout the Relay codebase. + +## Reference Marker + +TODO + +## @refetchable + +A directive that modifies a fragment, and causes Relay to generate a query for that fragment. + +This yields efficiency gains. The fragment can be loaded as part of a single, larger query initially (thus requiring only a single request to fetch all of the data), and yet refetched independently. + +## @relay + +A directive that allows you to turn off data masking and is used on plural types. + +See [the documentation](../api-reference/graphql-and-directives/#relaymask-boolean). + +## Relay Classic + +An even older version of Relay. + +## Relay Hooks + +The easiest-to-use, safest Relay API. It relies on suspense, and is safe to use in React concurrent mode. + +You should not write new code using Relay Classic or Relay Modern. + +## Relay Modern + +An older version of Relay. This version of Relay had an API that was heavily focused on Containers. + +## Relay Resolvers + +Relay Resolvers is an experimental Relay feature which enables modeling derived state as client-only fields in Relay’s GraphQL graph. + +See also [the Relay Resolvers Introduction](../guides/relay-resolvers/introduction.md). + +## Release Buffer + +As queries are released (no longer [retained](#retain)), their root nodes are stored in a release buffer of fixed size, and only evicted by newly released queries when there isn't enough space in the release buffer. When Relay runs garbage collection, queries that are present in the release buffer and not disposed. + +The size of the release buffer is configured with the `gcReleaseBufferSize` parameter. + +## `@required` + +A Relay directive that makes handling potentially `null` values more ergonomic. + +See also [the `@required` guide](../guides/required-directive/). + +## Request + +A request refers to an API call made over the network to access or mutate some data, or both. + +A query, when initiated, may or may not involve making a request, depending on whether the query can be fulfilled from the store or not. + +## Request Descriptor + +An object associating a [concrete request](#concrete-request) and [variables](#variables), as well as a pre-computed request ID. The variables should be filtered to exclude unneeded variables and are populated to include default values for missing variables, thus ensuring that requests that differ in irrelevant ways are cached using the same request ID. + +## Resolver + +An overloaded term, mostly referring to virtual fields, but also occasionally referring to other things. + +### When describing a field + +A resolver field is a "virtual" field that is backed by a function from a fragment reference on the same type to some arbitrary value. + +A live resolver is a "virtual" field that is backed by an external data source. e.g. one might use an external resolver to expose some state that is stored in local storage, or in an external Flux store. + +### Other meanings + +It can also be a [fragment spec resolver](#fragment-spec-resolver) or a [operation mock resolver](#operation-mock-resolver). + +## Response + +TODO + +## Response Normalizer + +A class, exposing a single method `normalize`. This will traverse the denormalized response from the API request, normalize it and write the normalized results into a given `MutableRecordSource`. It is called from the query executor. + +## Restore + +TODO + +## Retain + +TODO + +## Render Policy + +TODO + +## Revert + +TODO + +## Root Field + +TODO + +## Root Type + +The [GraphQL spec](https://spec.graphql.org/June2018/#sec-Root-Operation-Types) defines three special root types: Query, Mutation and Subscription. Queries must select fields off of the Query root type, etc. + +## Root + +Outermost React Component for a given page or screen. Can be associated with an entrypoint. + +Roots for entrypoints are referred to by the [`JSResource`](#JSResource) to the root React component module. + +## Scalar + +TODO + +## Scheduler + +TODO + +## Schema + +A collection of all of the GraphQL types that are known to Relay, for a given [project](#project). + + + +## Schema Sync + +The GraphQL [schema](#schema) is derived from annotations on Hack classes in the www repository. + +Periodically, those changes are synced to fbsource in a schema sync diff. If the updated schema would break Relay on fbsource, these schema sync diffs will not land. + +If a field is removed from www, but is only used in fbsource, the application developer may not notice that the field cannot be removed. This is a common source of schema breakages. + +For more info, look [here](https://www.internalfb.com/intern/wiki/GraphQL/Build_Infra/Schema_Sync/) and [here](https://www.internalfb.com/intern/wiki/Relay-team/GraphQL_Schema_Sync/). + + +## Schema Extension + +TODO + +## Selection + +A "selection of fields" refers to the fields you are requesting on an object that you are accessing, as part of a query, mutation, subscription or fragment. + +## Selector + +See [normalization selector](#normalization-selector). + +## @skip + +A directive that is added to fields, inline fragments and fragment spreads, and allows for conditional inclusion. It is the opposite of the [`@include`](#include) directive. + +## Snapshot + +The results of running a reader selector against the data currently in the store. See [lookup](#lookup). + +## Stale + +TODO + +## Store + +TODO + +## @stream + +A directive which can be added to a field of `List` type that enables the individual items in the list to be delivered incrementally. The client can render the initial +set of items while waiting for the server to deliver the rest of the items. For more detail refer to GraphQL's [documentation on the @stream directive](https://github.com/graphql/graphql-wg/blob/main/rfcs/DeferStream.md#stream). + +## @stream_connection + +A directive that is like the @connection directive for pagination, except modified to enable items in the pagination queue to be delivered incrementally. It has the additional parameter `initial_count` to specify how many items to deliver in the initial payload. To learn more about how to use this directive refer to the [Streaming Pagination page](https://relay.dev/docs/guided-tour/list-data/streaming-pagination/). + +## Subscribe + +A method exposed by the Relay store. Accepts a callback and a snapshot (see [lookup](#lookup)). The Relay store will call this callback when [`notify`](#notify) is called, if the data referenced by that snapshot has been updated or invalidated. + +## Subscription + +[GraphQL Subscriptions](../guided-tour/updating-data/graphql-subscriptions) are a mechanism which allow clients to subscribe to changes in a piece of data from the server, and get notified whenever that data changes. + +A GraphQL Subscription looks very similar to a query, with the exception that it uses the subscription keyword: + +```graphql +subscription FeedbackLikeSubscription($input: FeedbackLikeSubscribeData!) { + feedback_like_subscribe(data: $input) { + feedback { + id + like_count + } + } +} +``` + + + +See also [the guide](../guides/writing-subscriptions). + + + +## Transaction ID + +A unique id for a given instance of a call to `network.execute`. This ID will be consistent for the entire duration of a network request. It can be consumed by custom log functions passed to `RelayModernEnvironment`. + +## Traversal + +There are four tree traversals that are core to understanding the internal behavior of Relay. + +* Using the normalization AST: + * When Relay normalizes the payload it receives from the GraphQL server in the Response Normalizer; + * When Relay reads determines whether there is enough data for to fulfill an operation, in the Data Checker; and + * When Relay determines what data is no longer accessible during garbage collection, in the Reference Marker. +* Using the reader AST: + * When Relay reads data for rendering, in the Reader. + +## Type + +The GraphQL type of a field is a description of a field on a schema, in terms of what subfields it has, or what it's representation is (String, number, etc.). + +See also [interface](#interface-graphql), [abstract type](#abstract-type) and [the GraphQL docs](https://graphql.org/learn/schema/#type-language) for more info. + +## Type Refinement + +The inclusion of a fragment of particular type in a location only known to potentially implement that type. This allows us to select fields if and only if they are defined on that particular object, and return null otherwise. + +For example, `node(id: 4) { ... on User { name } }`. In this case, we do now know ahead of time whether `node(id: 4)` is a User. If it is, this fragment will include the user name. + +See also [abstract type refinement](#abstract-type-refinement). + +## Updater + +A callback passed to `commitMutation`, which provides the application developer with imperative control over the data in the store. + + +See [the documentation](../guided-tour/updating-data/introduction.md) and also optimistic updater. + +## Value + +A single value on a record, such as `has_viewer_liked`, or `name`. + +Compare with [linked record](#linked-record). + +## Variables + +GraphQL variables are a construct that allows referencing dynamic values inside a GraphQL query. They must be provided when the query is initiated, and can be used throughout nested fragments. + +See the [variables section of the guided tour](../guided-tour/rendering/variables) and compare with [@argumentDefinitions](#argumentdefinitions). + + diff --git a/website/versioned_docs/version-v19.0.0/guided-tour/introduction.md b/website/versioned_docs/version-v19.0.0/guided-tour/introduction.md new file mode 100644 index 0000000000000..a99a99075d522 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/guided-tour/introduction.md @@ -0,0 +1,57 @@ +--- +id: introduction +title: Introduction +slug: /guided-tour/ +description: Relay guided tour +keywords: +- guided tour +- relay +- graphql +- documentation +--- + +import DocsRating from '@site/src/core/DocsRating'; +import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; + +import FbCrashCourse from './fb/FbCrashCourse.md'; + +In this guided tour, we're going to go over how to use Relay to build out some of the more common use cases in apps. If you're interested in a detailed reference of our APIs, check out our **[API Reference](../api-reference/relay-environment-provider/)**. + + +## Before you read + +Before getting started, bear in mind that we assume some level of familiarity with: + + + +* [Javascript](https://our.internmc.facebook.com/intern/wiki/JavaScript/) +* [React](https://our.internmc.facebook.com/intern/wiki/ReactGuide/) +* [GraphQL](https://our.internmc.facebook.com/intern/wiki/GraphQL/) and our internal [GraphQL Server](https://our.internmc.facebook.com/intern/wiki/Graphql-for-hack-developers/) + + + + + +* [Javascript](https://felix-kling.de/jsbasics/) +* [React](https://reactjs.org/docs/getting-started.html) +* [GraphQL](https://graphql.org/learn/) + + + +## On to the Tutorial + + + +* [Tutorial](https://www.internalfb.com/intern/staticdocs/relay/docs/tutorial/intro/) + + + + + +* [Tutorial](https://relay.dev/docs/tutorial/intro/) + + + + + + diff --git a/website/versioned_docs/version-v19.0.0/guided-tour/list-data/advanced-pagination.md b/website/versioned_docs/version-v19.0.0/guided-tour/list-data/advanced-pagination.md new file mode 100644 index 0000000000000..591e49ed22796 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/guided-tour/list-data/advanced-pagination.md @@ -0,0 +1,157 @@ +--- +id: advanced-pagination +title: Advanced Pagination +slug: /guided-tour/list-data/advanced-pagination/ +description: Relay guide for advanced pagination +keywords: +- pagination +- usePaginationFragment +- prefetching +--- + +import DocsRating from '@site/src/core/DocsRating'; +import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; + +In this section we're going to cover how to implement more advanced pagination use cases than the default cases covered by `usePaginationFragment`. + + +## Pagination Over Multiple Connections + +If you need to paginate over multiple connections within the same component, you can use `usePaginationFragment` multiple times: + +```js +import type {CombinedFriendsListComponent_user$key} from 'CombinedFriendsListComponent_user.graphql'; +import type {CombinedFriendsListComponent_viewer$key} from 'CombinedFriendsListComponent_viewer.graphql'; + +const React = require('React'); + +const {graphql, usePaginationFragment} = require('react-relay'); + +type Props = { + user: CombinedFriendsListComponent_user$key, + viewer: CombinedFriendsListComponent_viewer$key, +}; + +function CombinedFriendsListComponent(props: Props) { + + const {data: userData, ...userPagination} = usePaginationFragment( + graphql` + fragment CombinedFriendsListComponent_user on User { + name + friends + @connection( + key: "CombinedFriendsListComponent_user_friends_connection" + ) { + edges { + node { + name + age + } + } + } + } + `, + props.user, + ); + + const {data: viewerData, ...viewerPagination} = usePaginationFragment( + graphql` + fragment CombinedFriendsListComponent_user on Viewer { + actor { + ... on User { + name + friends + @connection( + key: "CombinedFriendsListComponent_viewer_friends_connection" + ) { + edges { + node { + name + age + } + } + } + } + } + } + `, + props.viewer, + ); + + return (...); +} +``` + +However, we recommend trying to keep a single connection per component, to keep the components easier to follow. + + + +## Bi-directional Pagination + +In the [Pagination](../pagination/) section we covered how to use `usePaginationFragment` to paginate in a single *"forward"* direction. However, connections also allow paginating in the opposite *"backward"* direction. The meaning of *"forward"* and *"backward"* directions will depend on how the items in the connection are sorted, for example *"forward"* could mean more recent*, and "backward"* could mean less recent. + +Regardless of the semantic meaning of the direction, Relay also provides the same APIs to paginate in the opposite direction, using `usePaginationFragment`, as long as the `before` and `last` connection arguments are also used along with `after` and `first`: + +```js +import type {FriendsListComponent_user$key} from 'FriendsListComponent_user.graphql'; + +const React = require('React'); +const {Suspense} = require('React'); + +const {graphql, usePaginationFragment} = require('react-relay'); + +type Props = { + userRef: FriendsListComponent_user$key, +}; + +function FriendsListComponent(props: Props) { + const { + data, + loadPrevious, + hasPrevious, + // ... forward pagination values + } = usePaginationFragment( + graphql` + fragment FriendsListComponent_user on User { + name + friends(after: $after, before: $before, first: $first, last: $last) + @connection(key: "FriendsListComponent_user_friends_connection") { + edges { + node { + name + age + } + } + } + } + `, + userRef, + ); + + return ( + <> +

    Friends of {data.name}:

    + edge.node)}> + {node => { + return ( +
    + {node.name} - {node.age} +
    + ); + }} +
    + + {hasPrevious ? ( + + ) : null} + + {/* Forward pagination controls can go simultaneously here */} + + ); +} +``` + +* The APIs for both *"forward"* and *"backward"* are exactly the same, they're only named differently. When paginating forward, then the `after` and `first` connection arguments will be used, when paginating backward, the `before` and `last` connection arguments will be used. +* Note that the primitives for both *"forward"* and *"backward"* pagination are exposed from a single call to `usePaginationFragment`, so both *"forward"* and *"backward"* pagination can be performed simultaneously in the same component. diff --git a/website/versioned_docs/version-v19.0.0/guided-tour/list-data/connections.md b/website/versioned_docs/version-v19.0.0/guided-tour/list-data/connections.md new file mode 100644 index 0000000000000..de0a92fde3023 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/guided-tour/list-data/connections.md @@ -0,0 +1,23 @@ +--- +id: connections +title: Connections +slug: /guided-tour/list-data/connections/ +description: Relay guide for connections +keywords: +- pagination +- connections +--- + +import DocsRating from '@site/src/core/DocsRating'; +import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; +import useBaseUrl from '@docusaurus/useBaseUrl'; + +There are several scenarios in which we'll want to query a list of data from the GraphQL server. Often times we don't want to query the *entire* set of data up front, but rather discrete sub-parts of the list, incrementally, usually in response to user input or other events. Querying a list of data in discrete parts is usually known as [Pagination](https://graphql.org/learn/pagination/). + + +Specifically in Relay, we do this via GraphQL fields known as [Connections](https://graphql.org/learn/pagination/#complete-connection-model). Connections are GraphQL fields that take a set of arguments to specify which "slice" of the list to query, and include in their response both the "slice" of the list that was requested, as well as information to indicate if there is more data available in the list and how to query it; this additional information can be used in order to perform pagination by querying for more "slices" or pages on the list. + +More specifically, we perform *cursor-based pagination,* in which the input used to query for "slices" of the list is a `cursor` and a `count`. Cursors are essentially opaque tokens that serve as markers or pointers to a position in the list. If you're curious to learn more about the details of cursor-based pagination and connections, check out the spec. + + + diff --git a/website/versioned_docs/version-v19.0.0/guided-tour/list-data/introduction.md b/website/versioned_docs/version-v19.0.0/guided-tour/list-data/introduction.md new file mode 100644 index 0000000000000..d956e77478544 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/guided-tour/list-data/introduction.md @@ -0,0 +1,17 @@ +--- +id: introduction +title: Introduction +slug: /guided-tour/list-data/introduction +description: Relay guide to fetching data +keywords: +- fetching data +--- + +import DocsRating from '@site/src/core/DocsRating'; +import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; + +After an app has been initially rendered, there are various scenarios in which you might want to fetch and show *new* or *different* data (e.g. change the currently displayed item), or maybe refresh the currently rendered data with the latest version from the server (e.g. refreshing a count), usually as a result of an event or user interaction. + +In this section we'll cover some of the most common scenarios and how to build them with Relay. + + diff --git a/website/versioned_docs/version-v19.0.0/guided-tour/list-data/pagination.md b/website/versioned_docs/version-v19.0.0/guided-tour/list-data/pagination.md new file mode 100644 index 0000000000000..ca2d662221436 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/guided-tour/list-data/pagination.md @@ -0,0 +1,141 @@ +--- +id: pagination +title: Pagination +slug: /guided-tour/list-data/pagination/ +description: Relay guide to pagination +keywords: +- pagination +- usePaginationFragment +--- + +import DocsRating from '@site/src/core/DocsRating'; +import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; +import FbPaginationUsingUseTransition from './fb/FbPaginationUsingUseTransition.md'; + +To actually perform pagination over the connection, we need use the `loadNext` function to fetch the next page of items, which is available from `usePaginationFragment`: + + + + + + + +```js +import type {FriendsListComponent_user$key} from 'FriendsList_user.graphql'; + +const React = require('React'); + +const {graphql, usePaginationFragment} = require('react-relay'); + +const {Suspense} = require('React'); + +type Props = { + user: FriendsListComponent_user$key, +}; + +function FriendsListComponent(props: Props) { + const {data, loadNext} = usePaginationFragment( + graphql` + fragment FriendsListComponent_user on User + @refetchable(queryName: "FriendsListPaginationQuery") { + name + friends(first: $count, after: $cursor) + @connection(key: "FriendsList_user_friends") { + edges { + node { + name + age + } + } + } + } + `, + props.user, + ); + + return ( + <> +

    Friends of {data.name}:

    +
    + {(data.friends?.edges ?? []).map(edge => { + const node = edge.node; + return ( + }> + + + ); + })} +
    + + + + ); +} + +module.exports = FriendsListComponent; +``` + +Let's distill what's happening here: + +* `loadNext` takes a count to specify how many more items in the connection to fetch from the server. In this case, when `loadNext` is called we'll fetch the next 10 friends in the friends list of our currently rendered `User`. +* When the request to fetch the next items completes, the connection will be automatically updated and the component will re-render with the latest items in the connection. In our case, this means that the `friends` field will always contain *all* of the friends that we've fetched so far. By default, *Relay will automatically append new items to the connection upon completing a pagination request,* and will make them available to your fragment component*.* If you need a different behavior, check out our [Advanced Pagination Use Cases](../advanced-pagination/) section. +* `loadNext` may cause the component or new children components to suspend (as explained in [Loading States with Suspense](../../rendering/loading-states/)). This means that you'll need to make sure that there's a `Suspense` boundary wrapping this component from above. + +
    + + +Often, you will also want to access information about whether there are more items available to load. To do this, you can use the `hasNext` value, also available from `usePaginationFragment`: + +```js +import type {FriendsListPaginationQuery} from 'FriendsListPaginationQuery.graphql'; +import type {FriendsListComponent_user$key} from 'FriendsList_user.graphql'; + +const React = require('React'); +const {Suspense} = require('React'); + +const {graphql, usePaginationFragment} = require('react-relay'); + +type Props = { + user: FriendsListComponent_user$key, +}; + +function FriendsListComponent(props: Props) { + // ... + const { + data, + loadNext, + hasNext, + } = usePaginationFragment( + graphql`...`, + props.user, + ); + + return ( + <> +

    Friends of {data.name}:

    + {/* ... */} + + {/* Only render button if there are more friends to load in the list */} + {hasNext ? ( + + ) : null} + + ); +} + +module.exports = FriendsListComponent; +``` + +* `hasNext` is a boolean which indicates if the connection has more items available. This information can be useful for determining if different UI controls should be rendered. In our specific case, we only render the `Button` if there are more friends available in the connection. + + + + diff --git a/website/versioned_docs/version-v19.0.0/guided-tour/list-data/rendering-connections.md b/website/versioned_docs/version-v19.0.0/guided-tour/list-data/rendering-connections.md new file mode 100644 index 0000000000000..377838ea5d99e --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/guided-tour/list-data/rendering-connections.md @@ -0,0 +1,112 @@ +--- +id: rendering-connections +title: Rendering Connections +slug: /guided-tour/list-data/rendering-connections/ +description: Relay guide to rendering connections +keywords: +- pagination +- usePaginationFragment +- connection +--- + +import DocsRating from '@site/src/core/DocsRating'; +import FbSuspenseListAlternative from './fb/FbSuspenseListAlternative.md'; +import FbRenderingConnectionsUsingSuspenseList from './fb/FbRenderingConnectionsUsingSuspenseList.md'; +import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; + +In Relay, in order to display a list of data that is backed by a GraphQL connection, first you need to declare a fragment that queries for a connection: + +```js +const {graphql} = require('RelayModern'); + +const userFragment = graphql` + fragment UserFragment on User { + name + friends(after: $cursor, first: $count) + @connection(key: "UserFragment_friends") { + edges { + node { + ...FriendComponent + } + } + } + } +`; +``` + +* In the example above, we're querying for the `friends` field, which is a connection; in other words, it adheres to the connection spec. Specifically, we can query the `edges` and `node`s in the connection; the `edges` usually contain information about the relationship between the entities, while the `node`s are the actual entities at the other end of the relationship; in this case, the `node`s are objects of type `User` representing the user's friends. +* In order to indicate to Relay that we want to perform pagination over this connection, we need to mark the field with the `@connection` directive. We must also provide a *static* unique identifier for this connection, known as the `key`. We recommend the following naming convention for the connection key: `_`. +* We will go into more detail later as to why it is necessary to mark the field as a `@connection` and give it a unique `key` in our [Updating Connections](../updating-connections/) section. + + +In order to render this fragment which queries for a connection, we can use the `usePaginationFragment` Hook: + + + + + + + +```js +import type {FriendsListComponent_user$key} from 'FriendsList_user.graphql'; + +const React = require('React'); +const {Suspense} = require('React'); + +const {graphql, usePaginationFragment} = require('react-relay'); + +type Props = { + user: FriendsListComponent_user$key, +}; + +function FriendsListComponent(props: Props) { + const {data} = usePaginationFragment( + graphql` + fragment FriendsListComponent_user on User + @refetchable(queryName: "FriendsListPaginationQuery") { + name + friends(first: $count, after: $cursor) + @connection(key: "FriendsList_user_friends") { + edges { + node { + ...FriendComponent + } + } + } + } + `, + props.user, + ); + + + return ( + <> + {data.name != null ?

    Friends of {data.name}:

    : null} + +
    + {/* Extract each friend from the resulting data */} + {(data.friends?.edges ?? []).map(edge => { + const node = edge.node; + return ( + }> + + + ); + })} +
    + + ); +} + +module.exports = FriendsListComponent; +``` + + +* `usePaginationFragment` behaves the same way as a `useFragment` (see the [Fragments](../../rendering/fragments/) section), so our list of friends is available under `data.friends.edges.node`, as declared by the fragment. However, it also has a few additions: + * It expects a fragment that is a connection field annotated with the `@connection` directive + * It expects a fragment that is annotated with the `@refetchable` directive. Note that `@refetchable` directive can only be added to fragments that are "refetchable", that is, on fragments that are on `Viewer`, on `Query`, on any type that implements `Node` (i.e. a type that has an `id` field), or on a `@fetchable` type. For more info on `@fetchable` types, see [this post](https://fb.workplace.com/groups/graphql.fyi/permalink/1539541276187011/). +* It takes two Flow type parameters: the type of the generated query (in our case `FriendsListPaginationQuery`), and a second type which can always be inferred, so you only need to pass underscore (`_`). + +
    + + diff --git a/website/versioned_docs/version-v19.0.0/guided-tour/list-data/streaming-pagination.md b/website/versioned_docs/version-v19.0.0/guided-tour/list-data/streaming-pagination.md new file mode 100644 index 0000000000000..2cb8795be20b4 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/guided-tour/list-data/streaming-pagination.md @@ -0,0 +1,87 @@ +--- +id: streaming-pagination +title: Streaming Pagination +slug: /guided-tour/list-data/streaming-pagination/ +description: Relay guide to streaming pagination +keywords: +- pagination +- usePaginationFragment +- connection +- streaming +--- + +import DocsRating from '@site/src/core/DocsRating'; +import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; + + + +Additionally, we can combine `usePaginationFragment` with Relay's [Incremental Data Delivery](../../../guides/incremental-data-delivery/) capabilities in order to fetch a connection and incrementally receive each item in the connection as it becomes ready, instead of waiting for the whole list of items to be returned in a single payload. This can be useful when for example computing each item in the connection is an expensive operation in the server, and we want to be able to show the first item(s) in the list as soon as possible without blocking on *all* the items that we need to become available; for example, on News Feed a user could ideally see and start interacting with the first story while additional stories loaded in below. + + + + + +Additionally, we can combine `usePaginationFragment` with Relay's Incremental Data Delivery capabilities in order to fetch a connection and incrementally receive each item in the connection as it becomes ready, instead of waiting for the whole list of items to be returned in a single payload. This can be useful when for example computing each item in the connection is an expensive operation in the server, and we want to be able to show the first item(s) in the list as soon as possible without blocking on *all* the items that we need to become available; for example, on News Feed a user could ideally see and start interacting with the first story while additional stories loaded in below. + + + +In order to do so, we can use the `@stream_connection` directive instead of the `@connection` directive: + +```js +import type {FriendsListComponent_user$key} from 'FriendsList_user.graphql'; + +const React = require('React'); + +const {graphql, usePaginationFragment} = require('react-relay'); + +type Props = { + user: FriendsListComponent_user$key, +}; + +function FriendsListComponent(props: Props) { + // ... + + const { + data, + loadNext, + hasNext, + } = usePaginationFragment( + graphql` + fragment FriendsListComponent_user on User + @refetchable(queryName: "FriendsListPaginationQuery") { + name + friends(first: $count, after: $cursor) + @stream_connection(key: "FriendsList_user_friends", initial_count: 2,) { + edges { + node { + name + age + } + } + } + } + `, + props.user, + ); + + return (...); +} + +module.exports = FriendsListComponent; +``` + +Let's distill what's happening here: + +* The `@stream_connection` directive can be used directly in place of the `@connection` directive; it accepts the same arguments as @connection plus additional, *optional* parameters to control streaming: + * `initial_count: Int`: A number (defaulting to zero) that controls how many items will be included in the initial payload. Any subsequent items are streamed, so when set to zero the list will initially be empty and all items will be streamed. Note that this number does not affect how many items are returned *total*, only how many items are included in the initial payload. For example, consider a product that today makes an initial fetch for 2 items and then *immediately* issues a pagination query to fetch 3 more. With streaming, this product could instead choose to fetch 5 items in the initial query with initial_count=2, in order to fetch the 2 items quickly while avoiding a round trip for the subsequent 3 items. +* As with regular usage of `usePaginationFragment`, the connection will be automatically updated as new items are streamed in from the server, and the component will re-render each time with the latest items in the connection. + + + + +For more information, see our docs on [Incremental Data Delivery](../../../guides/incremental-data-delivery/#stream_connection). + + + + + diff --git a/website/versioned_docs/version-v19.0.0/guided-tour/managing-data-outside-react/retaining-queries.md b/website/versioned_docs/version-v19.0.0/guided-tour/managing-data-outside-react/retaining-queries.md new file mode 100644 index 0000000000000..821f9baeb537d --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/guided-tour/managing-data-outside-react/retaining-queries.md @@ -0,0 +1,51 @@ +--- +id: retaining-queries +title: Retaining Queries +slug: /guided-tour/accessing-data-without-react/retaining-queries/ +description: Relay guide to retaining queries +keywords: +- retaining +- query +- environment +- garbage collection +- gc +--- + +import DocsRating from '@site/src/core/DocsRating'; +import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; + +In order to manually retain a query so that the data it references isn’t garbage collected by Relay, we can use the `environment.retain` method: + +```js +const { + createOperationDescriptor, + getRequest, + graphql, +} = require('relay-runtime') + +// Query graphql object +const query = graphql`...`; + +// Construct Relay's internal representation of the query +const queryRequest = getRequest(query); +const queryDescriptor = createOperationDescriptor( + queryRequest, + variables +); + +// Retain query; this will prevent the data for this query and +// variables from being garbage collected by Relay +const disposable = environment.retain(queryDescriptor); + +// Disposing of the disposable will release the data for this query +// and variables, meaning that it can be deleted at any moment +// by Relay's garbage collection if it hasn't been retained elsewhere +disposable.dispose(); +``` + +:::note +Relay automatically manages the query data retention based on any mounted query components that are rendering the data, so you usually should not need to call retain directly within product code. For any advanced or special use cases, query data retention should usually be handled within infra-level code, such as a Router. +::: + + + diff --git a/website/versioned_docs/version-v19.0.0/guided-tour/refetching/refetching-queries-with-different-data.md b/website/versioned_docs/version-v19.0.0/guided-tour/refetching/refetching-queries-with-different-data.md new file mode 100644 index 0000000000000..dcc0d05e553f9 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/guided-tour/refetching/refetching-queries-with-different-data.md @@ -0,0 +1,337 @@ +--- +id: refetching-queries-with-different-data +title: Refetching Queries with Different Data +slug: /guided-tour/refetching/refetching-queries-with-different-data/ +description: Relay guide to refetching queries with different data +keywords: +- refetching +- query +--- + +import DocsRating from '@site/src/core/DocsRating'; +import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; +import FbRefetchingQueriesUsingUseQueryLoader from './fb/FbRefetchingQueriesUsingUseQueryLoader.md'; +import FbRefetchingQueriesUsingUseLazyLoadQuery from './fb/FbRefetchingQueriesUsingUseLazyLoadQuery.md'; +import FbAvoidSuspenseCaution from './fb/FbAvoidSuspenseCaution.md'; + +When referring to **"refetching a query"**, we mean fetching the query again for *different* data than was originally rendered by the query. For example, this might be to change a currently selected item, to render a different list of items than the one being shown, or more generally to transition the currently rendered content to show new or different content. + +## When using `useQueryLoader` / `loadQuery` + +Similarly to [Refreshing Queries with `useQueryLoader`](../refreshing-queries/#when-using-usequeryloader--loadquery), we can also use the `useQueryLoader` Hook described in our [Fetching Queries for Render](../../rendering/queries/#fetching-queries-for-render) section, but this time passing *different query variables*: + + + + + + + +```js +/** + * App.react.js + */ +const AppQuery = require('__generated__/AppQuery.graphql'); + +function App(props: Props) { + const variables = {id: '4'}; + const [queryRef, loadQuery] = useQueryLoader( + AppQuery, + props.appQueryRef /* initial query ref */ + ); + + const refetch = useCallback(() => { + // Load the query again using the same original variables. + // Calling loadQuery will update the value of queryRef. + loadQuery({id: 'different-id'}); + }, [/* ... */]); + + return ( + + + + ); +} +``` + +```js +/** + * MainContent.react.js + */ + +// Renders the preloaded query, given the query reference +function MainContent(props) { + const {refetch, queryRef} = props; + const data = usePreloadedQuery( + graphql` + query AppQuery($id: ID!) { + user(id: $id) { + name + friends { + count + } + } + } + `, + queryRef, + ); + + return ( + <> +

    {data.user?.name}

    +
    Friends count: {data.user?.friends?.count}
    + + + ); +} +``` + +Let's distill what's going on here: + +* We call `loadQuery` in the event handler for refetching, so the network request starts immediately, and then pass the `queryRef` to `usePreloadedQuery`, so it renders the updated data. +* We are not passing a `fetchPolicy` to `loadQuery`, meaning that it will use the default value of `'store-or-network'`. We could provide a different policy in order to specify whether to use locally cached data (as we covered in [Reusing Cached Data For Render](../../reusing-cached-data/)). +* Calling `loadQuery` will re-render the component and may cause `usePreloadedQuery` to suspend (as explained in [Loading States with Suspense](../../rendering/loading-states/)). This means that we'll need to make sure that there's a `Suspense` boundary wrapping the `MainContent` component, in order to show a fallback loading state. + +
    + + +### If you need to avoid Suspense + +In some cases, you might want to avoid showing a Suspense fallback, which would hide the already rendered content. For these cases, you can use [`fetchQuery`](../../../api-reference/fetch-query/) instead, and manually keep track of a loading state: + + + + + +```js +/** + * App.react.js + */ +const AppQuery = require('__generated__/AppQuery.graphql'); + +function App(props: Props) { + const environment = useRelayEnvironment(); + const [queryRef, loadQuery] = useQueryLoader( + AppQuery, + props.appQueryRef /* initial query ref */ + ); + const [isRefetching, setIsRefetching] = useState(false) + + const refetch = useCallback(() => { + if (isRefetching) { return; } + setIsRefetching(true); + const variables = { id: 'different-id' }; + + // fetchQuery will fetch the query and write + // the data to the Relay store. This will ensure + // that when we re-render, the data is already + // cached and we don't suspend + fetchQuery(environment, AppQuery, variables) + .subscribe({ + complete: () => { + setIsRefetching(false); + + // *After* the query has been fetched, we call + // loadQuery again to re-render with a new + // queryRef. + // At this point the data for the query should + // be cached, so we use the 'store-only' + // fetchPolicy to avoid suspending. + loadQuery(variables, {fetchPolicy: 'store-only'}); + }, + error: () => { + setIsRefetching(false); + } + }); + }, [/* ... */]); + + return ( + + + + ); +} +``` + +Let's distill what's going on here: + +* When refetching, we now keep track of our own `isRefetching` loading state, since we are avoiding suspending. We can use this state to render a busy spinner or similar loading UI inside the `MainContent` component, *without* hiding the `MainContent`. +* In the event handler, we first call `fetchQuery`, which will fetch the query and write the data to the local Relay store. When the `fetchQuery` network request completes, we call `loadQuery` so that we obtain an updated `queryRef` that we then pass to `usePreloadedQuery` in order render the updated data, similar to the previous example. +* At this point, when `loadQuery` is called, the data for the query should already be cached in the local Relay store, so we use `fetchPolicy` of `'store-only'` to avoid suspending and only read the already cached data. + +## When using `useLazyLoadQuery` + +Similarly to [Refreshing Queries with `useLazyLoadQuery`](../refreshing-queries/#when-using-uselazyloadquery), we can also use the [`useLazyLoadQuery`](../../../api-reference/use-lazy-load-query/) Hook described in our [Lazily Fetching Queries during Render](../../rendering/queries/#lazily-fetching-queries-during-render) section, but this time passing *different query variables*: + + + + + + + +```js +/** + * App.react.js + */ +const AppQuery = require('__generated__/AppQuery.graphql'); + +function App(props: Props) { + const [queryArgs, setQueryArgs] = useState({ + options: {fetchKey: 0}, + variables: {id: '4'}, + }); + + const refetch = useCallback(() => { + // Trigger a re-render of useLazyLoadQuery with new variables, + // *and* an updated fetchKey. + // The new fetchKey will ensure that the query is fully + // re-evaluated and refetched. + setQueryArgs(prev => ({ + options: { + fetchKey: (prev?.options.fetchKey ?? 0) + 1, + }, + variables: {id: 'different-id'} + })); + }, [/* ... */]); + + return ( + + + + ); +} +``` + +```js +/** + * MainContent.react.js + */ +// Fetches and renders the query, given the fetch options +function MainContent(props) { + const {refetch, queryArgs} = props; + const data = useLazyLoadQuery( + graphql` + query AppQuery($id: ID!) { + user(id: $id) { + name + friends { + count + } + } + } + `, + queryArgs.variables, + queryArgs.options, + ); + + return ( + <> +

    {data.user?.name}

    +
    Friends count: {data.user.friends?.count}
    + + + ); +} +``` + +Let's distill what's going on here: + +* We update the component in the event handler for refreshing by setting new query args in state. This will cause the `MainContent` component that uses `useLazyLoadQuery` to re-render with the new `variables` and `fetchKey`, and refetch the query upon rendering. +* We are passing a new value of `fetchKey` which we increment on every update. Passing a new `fetchKey` to `useLazyLoadQuery` on every update will ensure that the query is fully re-evaluated and refetched. +* We are not passing a new `fetchPolicy` to `useLazyLoadQuery`, meaning that it will use the default value of `'store-or-network'`. We could provide a different policy in order to specify whether to use locally cached data (as we covered in [Reusing Cached Data For Render](../../reusing-cached-data/)). +* The state update in `refetch` will re-render the component and may cause the component to suspend (as explained in [Loading States with Suspense](../../rendering/loading-states/)). This means that we'll need to make sure that there's a `Suspense` boundary wrapping the `MainContent` component, in order to show a fallback loading state. + + +
    + +### If you need to avoid Suspense + +In some cases, you might want to avoid showing a Suspense fallback, which would hide the already rendered content. For these cases, you can use [`fetchQuery`](../../../api-reference/fetch-query/) instead, and manually keep track of a loading state: + + + + + +```js +/** + * App.react.js + */ +const AppQuery = require('__generated__/AppQuery.graphql'); + +function App(props: Props) { + const environment = useRelayEnvironment(); + const [isRefreshing, setIsRefreshing] = useState(false) + const [queryArgs, setQueryArgs] = useState({ + options: {fetchKey: 0, fetchPolicy: 'store-or-network'}, + variables: {id: '4'}, + }); + + const refetch = useCallback(() => { + if (isRefreshing) { return; } + setIsRefreshing(true); + const variables = { id: 'different-id' }; + + // fetchQuery will fetch the query and write + // the data to the Relay store. This will ensure + // that when we re-render, the data is already + // cached and we don't suspend + fetchQuery(environment, AppQuery, variables) + .subscribe({ + complete: () => { + setIsRefreshing(false); + + // *After* the query has been fetched, we update + // our state to re-render with the new fetchKey + // and fetchPolicy. + // At this point the data for the query should + // be cached, so we use the 'store-only' + // fetchPolicy to avoid suspending. + setQueryArgs(prev => ({ + options: { + fetchKey: (prev?.options.fetchKey ?? 0) + 1, + fetchPolicy: 'store-only', + }, + variables, + })); + }, + error: () => { + setIsRefreshing(false); + } + }); + }, [/* ... */]); + + return ( + + + + ); +} +``` + +Let's distill what's going on here: + +* When refetching, we now keep track of our own `isRefetching` loading state, since we are avoiding suspending. We can use this state to render a busy spinner or similar loading UI inside the `MainContent` component, *without* hiding the `MainContent`. +* In the event handler, we first call `fetchQuery`, which will fetch the query and write the data to the local Relay store. When the `fetchQuery` network request completes, we update our state so that we re-render an updated `fetchKey` and `fetchPolicy` that we then pass to `useLazyLoadQuery` in order render the updated data, similar to the previous example. +* At this point, when we update the state, the data for the query should already be cached in the local Relay store, so we use `fetchPolicy` of `'store-only'` to avoid suspending and only read the already cached data. + + + diff --git a/website/versioned_docs/version-v19.0.0/guided-tour/refetching/refreshing-queries.md b/website/versioned_docs/version-v19.0.0/guided-tour/refetching/refreshing-queries.md new file mode 100644 index 0000000000000..cec6e54164b73 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/guided-tour/refetching/refreshing-queries.md @@ -0,0 +1,354 @@ +--- +id: refreshing-queries +title: Refreshing Queries +slug: /guided-tour/refetching/refreshing-queries/ +description: Relay guide to refreshing queries +keywords: +- refreshing +- queries +--- + +import DocsRating from '@site/src/core/DocsRating'; +import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; +import FbRefreshingUsingRealTimeFeatures from './fb/FbRefreshingUsingRealTimeFeatures.md'; +import FbRefreshingQueriesUsingUseQueryLoader from './fb/FbRefreshingQueriesUsingUseQueryLoader.md'; +import FbAvoidSuspenseCaution from './fb/FbAvoidSuspenseCaution.md'; +import FbRefreshingQueriesUsingUseLazyLoadQuery from './fb/FbRefreshingQueriesUsingUseLazyLoadQuery.md'; + +When referring to **"refreshing a query"**, we mean fetching the *exact* same data that was originally rendered by the query, in order to get the most up-to-date version of that data from the server. + +## Using real-time features + + + + + + + +If we want to keep our data up to date with the latest version from the server, the first thing to consider is if it appropriate to use any real-time features, which can make it easier to automatically keep the data up to date without manually refreshing the data periodically. + +One example of this is using [GraphQL Subscriptions](https://relay.dev/docs/guided-tour/updating-data/graphql-subscriptions), which will require additional configuration on your server and [network layer](https://relay.dev/docs/guided-tour/updating-data/graphql-subscriptions/#configuring-the-network-layer). + + + +## When using `useQueryLoader` / `loadQuery` + +To refresh a query using the [`useQueryLoader`](../../../api-reference/use-query-loader/) Hook described in our [Fetching Queries for Render](../../rendering/queries/#fetching-queries-for-render) section, we only need to call `loadQuery` again: + + + + + + + +```js +/** + * App.react.js + */ + +const AppQuery = require('__generated__/AppQuery.graphql'); + +function App(props: Props) { + const [queryRef, loadQuery] = useQueryLoader( + AppQuery, + props.appQueryRef /* initial query ref */ + ); + + const refresh = useCallback(() => { + // Load the query again using the same original variables. + // Calling loadQuery will update the value of queryRef. + // The fetchPolicy ensures we always fetch from the server and skip + // the local data cache. + const {variables} = props.appQueryRef; + loadQuery(variables, {fetchPolicy: 'network-only'}); + }, [/* ... */]); + + return ( + + + + ); +} +``` + +```js +/** + * MainContent.react.js + */ + +// Renders the preloaded query, given the query reference +function MainContent(props) { + const {refresh, queryRef} = props; + const data = usePreloadedQuery( + graphql` + query AppQuery($id: ID!) { + user(id: $id) { + name + friends { + count + } + } + } + `, + queryRef, + ); + + return ( + <> +

    {data.user?.name}

    +
    Friends count: {data.user.friends?.count}
    + + + ); +} +``` + +Let's distill what's going on here: + +* We call `loadQuery` in the event handler for refreshing, so the network request starts immediately, and then pass the updated `queryRef` to the `MainContent` component that uses `usePreloadedQuery`, so it renders the updated data. +* We are passing a `fetchPolicy` of `'network-only'` to ensure that we always fetch from the network and skip the local data cache. +* Calling `loadQuery` will re-render the component and cause `usePreloadedQuery` to suspend (as explained in [Loading States with Suspense](../../rendering/loading-states/)), since a network request will always be made due to the `fetchPolicy` we are using. This means that we'll need to make sure that there's a `Suspense` boundary wrapping the `MainContent` component in order to show a fallback loading state. + +
    + +### If you need to avoid Suspense + +In some cases, you might want to avoid showing a Suspense fallback, which would hide the already rendered content. For these cases, you can use [`fetchQuery`](../../../api-reference/fetch-query/) instead, and manually keep track of a loading state: + + + + + +```js +/** + * App.react.js + */ + +const AppQuery = require('__generated__/AppQuery.graphql'); + +function App(props: Props) { + const environment = useRelayEnvironment(); + const [queryRef, loadQuery] = useQueryLoader( + AppQuery, + props.appQueryRef /* initial query ref */ + ); + const [isRefreshing, setIsRefreshing] = useState(false) + + const refresh = useCallback(() => { + if (isRefreshing) { return; } + const {variables} = props.appQueryRef; + setIsRefreshing(true); + + // fetchQuery will fetch the query and write + // the data to the Relay store. This will ensure + // that when we re-render, the data is already + // cached and we don't suspend + fetchQuery(environment, AppQuery, variables) + .subscribe({ + complete: () => { + setIsRefreshing(false); + + // *After* the query has been fetched, we call + // loadQuery again to re-render with a new + // queryRef. + // At this point the data for the query should + // be cached, so we use the 'store-only' + // fetchPolicy to avoid suspending. + loadQuery(variables, {fetchPolicy: 'store-only'}); + } + error: () => { + setIsRefreshing(false); + } + }); + }, [/* ... */]); + + return ( + + + + ); +} +``` + +Let's distill what's going on here: + +* When refreshing, we now keep track of our own `isRefreshing` loading state, since we are avoiding suspending. We can use this state to render a busy spinner or similar loading UI inside the `MainContent` component, *without* hiding the `MainContent`. +* In the event handler, we first call `fetchQuery`, which will fetch the query and write the data to the local Relay store. When the `fetchQuery` network request completes, we call `loadQuery` so that we obtain an updated `queryRef` that we then pass to `usePreloadedQuery` in order render the updated data, similar to the previous example. +* At this point, when `loadQuery` is called, the data for the query should already be cached in the local Relay store, so we use `fetchPolicy` of `'store-only'` to avoid suspending and only read the already cached data. + + +## When using `useLazyLoadQuery` + +To refresh a query using the [`useLazyLoadQuery`](../../../api-reference/use-lazy-load-query/) Hook described in our [Lazily Fetching Queries during Render](../../rendering/queries/#lazily-fetching-queries-during-render) section, we can do the following: + + + + + + + +```js +/** + * App.react.js + */ +const AppQuery = require('__generated__/AppQuery.graphql'); + +function App(props: Props) { + const variables = {id: '4'}; + const [refreshedQueryOptions, setRefreshedQueryOptions] = useState(null); + + const refresh = useCallback(() => { + // Trigger a re-render of useLazyLoadQuery with the same variables, + // but an updated fetchKey and fetchPolicy. + // The new fetchKey will ensure that the query is fully + // re-evaluated and refetched. + // The fetchPolicy ensures that we always fetch from the network + // and skip the local data cache. + setRefreshedQueryOptions(prev => ({ + fetchKey: (prev?.fetchKey ?? 0) + 1, + fetchPolicy: 'network-only', + })); + }, [/* ... */]); + + return ( + + + + ); +``` + +```js +/** + * MainContent.react.js + */ + +// Fetches and renders the query, given the fetch options +function MainContent(props) { + const {refresh, queryOptions, variables} = props; + const data = useLazyLoadQuery( + graphql` + query AppQuery($id: ID!) { + user(id: $id) { + name + friends { + count + } + } + } + `, + variables, + queryOptions, + ); + + return ( + <> +

    {data.user?.name}

    +
    Friends count: {data.user.friends?.count}
    + + + ); +} +``` + +Let's distill what's going on here: + +* We update the component in the event handler for refreshing by setting new options in state. This will cause the `MainContent` component that uses `useLazyLoadQuery` to re-render with the new `fetchKey` and `fetchPolicy`, and refetch the query upon rendering. +* We are passing a new value of `fetchKey` which we increment on every update. Passing a new `fetchKey` to `useLazyLoadQuery` on every update will ensure that the query is fully re-evaluated and refetched. +* We are passing a `fetchPolicy` of `'network-only'` to ensure that we always fetch from the network and skip the local data cache. +* The state update in `refresh` will cause the component to suspend (as explained in [Loading States with Suspense](../../rendering/loading-states/)), since a network request will always be made due to the `fetchPolicy` we are using. This means that we'll need to make sure that there's a `Suspense` boundary wrapping the `MainContent` component in order to show a fallback loading state. + +
    + +### If you need to avoid Suspense + +In some cases, you might want to avoid showing a Suspense fallback, which would hide the already rendered content. For these cases, you can use [`fetchQuery`](../../../api-reference/fetch-query/) instead, and manually keep track of a loading state: + + + + + + + + + +```js +/** + * App.react.js + */ +import type {AppQuery as AppQueryType} from 'AppQuery.graphql'; + +const AppQuery = require('__generated__/AppQuery.graphql'); + +function App(props: Props) { + const variables = {id: '4'} + const environment = useRelayEnvironment(); + const [refreshedQueryOptions, setRefreshedQueryOptions] = useState(null); + const [isRefreshing, setIsRefreshing] = useState(false) + + const refresh = useCallback(() => { + if (isRefreshing) { return; } + setIsRefreshing(true); + + // fetchQuery will fetch the query and write + // the data to the Relay store. This will ensure + // that when we re-render, the data is already + // cached and we don't suspend + fetchQuery(environment, AppQuery, variables) + .subscribe({ + complete: () => { + setIsRefreshing(false); + + // *After* the query has been fetched, we update + // our state to re-render with the new fetchKey + // and fetchPolicy. + // At this point the data for the query should + // be cached, so we use the 'store-only' + // fetchPolicy to avoid suspending. + setRefreshedQueryOptions(prev => ({ + fetchKey: (prev?.fetchKey ?? 0) + 1, + fetchPolicy: 'store-only', + })); + } + error: () => { + setIsRefreshing(false); + } + }); + }, [/* ... */]); + + return ( + + + + ); +} +``` + +Let's distill what's going on here: + +* When refreshing, we now keep track of our own `isRefreshing` loading state, since we are avoiding suspending. We can use this state to render a busy spinner or similar loading UI inside the `MainContent` component, *without* hiding the `MainContent`. +* In the event handler, we first call `fetchQuery`, which will fetch the query and write the data to the local Relay store. When the `fetchQuery` network request completes, we update our state so that we re-render an updated `fetchKey` and `fetchPolicy` that we then pass to `useLazyLoadQuery` in order render the updated data, similar to the previous example. +* At this point, when we update the state, the data for the query should already be cached in the local Relay store, so we use `fetchPolicy` of `'store-only'` to avoid suspending and only read the already cached data. + + diff --git a/website/versioned_docs/version-v19.0.0/guided-tour/rendering/environment.md b/website/versioned_docs/version-v19.0.0/guided-tour/rendering/environment.md new file mode 100644 index 0000000000000..e4fd6516fc560 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/guided-tour/rendering/environment.md @@ -0,0 +1,59 @@ +--- +id: environment +title: Environment +slug: /guided-tour/rendering/environment/ +description: Relay guide to the environment +keywords: +- environment +- RelayEnvironmentProvider +- useRelayEnvironment +--- + +import DocsRating from '@site/src/core/DocsRating'; +import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; +import FbActorsAndEnvironments from './fb/FbActorsAndEnvironments.md'; +import FbEnvironmentSetup from './fb/FbEnvironmentSetup.md'; + +## Relay Environment Provider + +In order to render Relay components, you need to render a `RelayEnvironmentProvider` component at the root of the app: + +```js +// App root + +const {RelayEnvironmentProvider} = require('react-relay'); +const Environment = require('MyEnvironment'); + +function Root() { + return ( + + {/*... */} + + ); +} +``` + +* The `RelayEnvironmentProvider` takes an environment, which it will make available to all descendant Relay components, and which is necessary for Relay to function. + + + +## Accessing the Relay Environment + +If you want to access the *current* Relay Environment within a descendant of a `RelayEnvironmentProvider` component, you can use the `useRelayEnvironment` Hook: + +```js +const {useRelayEnvironment} = require('react-relay'); + +function UserComponent(props: Props) { + const environment = useRelayEnvironment(); + + return (...); +} +``` + + + + + + + diff --git a/website/versioned_docs/version-v19.0.0/guided-tour/rendering/error-states.md b/website/versioned_docs/version-v19.0.0/guided-tour/rendering/error-states.md new file mode 100644 index 0000000000000..c07745359f189 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/guided-tour/rendering/error-states.md @@ -0,0 +1,295 @@ +--- +id: error-states +title: Error States with ErrorBoundaries +slug: /guided-tour/rendering/error-states/ +description: Relay guide to rendering error states +keywords: +- rendering +- error +- boundary +--- + +import DocsRating from '@site/src/core/DocsRating'; +import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; +import FbErrorBoundary from './fb/FbErrorBoundary.md'; + + + +As you may have noticed, we mentioned that using `usePreloadedQuery` will render data from a query that was (or is) being fetched from the server, but we didn't elaborate on how to render UI to show an error if an error occurred during fetch. We will cover that in this section. + +We can use [Error Boundary](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary) components to catch errors that occur during render (due to a network error, or any kind of error), and render an alternative error UI when that occurs. The way it works is similar to how `Suspense` works, by wrapping a component tree in an error boundary, we can specify how we want to react when an error occurs, for example by rendering a fallback UI. + +[Error boundaries](https://react.dev/reference/react/Component#catching-rendering-errors-with-an-error-boundary) are simply components that implement the static `getDerivedStateFromError` method: + +```js +const React = require('React'); + +type State = {error: ?Error}; + +class ErrorBoundary extends React.Component { + static getDerivedStateFromError(error): State { + // Set some state derived from the caught error + return {error: error}; + } +} +``` + +```js +/** + * App.react.js + */ + +const ErrorBoundary = require('ErrorBoundary'); +const React = require('React'); + +const MainContent = require('./MainContent.react'); +const SecondaryContent = require('./SecondaryContent.react'); + +function App() { + return ( + // Render an ErrorSection if an error occurs within + // MainContent or Secondary Content + }> + + + + ); +} +``` + +* We can use the Error Boundary to wrap subtrees and show a different UI when an error occurs within that subtree. When an error occurs, the specified `fallback` will be rendered instead of the content inside the boundary. +* Note that we can also control the granularity at which we render error UIs, by wrapping components at different levels with error boundaries. In this example, if any error occurs within `MainContent` or `SecondaryContent`, we will render an `ErrorSection` in place of the entire app content. + + + +## Retrying after an Error + +### When using `useQueryLoader` / `loadQuery` + +When using `useQueryLoader`/`loadQuery` to fetch a query, in order to retry after an error has occurred, you can call `loadQuery` again and pass the *new* query reference to `usePreloadedQuery`: + +```js +/** + * ErrorBoundaryWithRetry.react.js + */ + +const React = require('React'); + +// NOTE: This is NOT actual production code; +// it is only used to illustrate example +class ErrorBoundaryWithRetry extends React.Component { + state = {error: null}; + + static getDerivedStateFromError(error): State { + return {error: error}; + } + + _retry = () => { + // This ends up calling loadQuery again to get and render + // a new query reference + this.props.onRetry(); + this.setState({ + // Clear the error + error: null, + }); + } + + render() { + const {children, fallback} = this.props; + const {error} = this.state; + if (error) { + if (typeof fallback === 'function') { + return fallback({error, retry: this._retry}); + } + return fallback; + } + return children; + } +} +``` +* When an error occurs, we render the provided `fallback`. +* When `retry` is called, we will clear the error, and call `loadQuery` again. This will fetch the query again and provide us a new query reference, which we can then pass down to `usePreloadedQuery`. + +```js +/** + * App.react.js + */ + +const ErrorBoundaryWithRetry = require('ErrorBoundaryWithRetry'); +const React = require('React'); + +const MainContent = require('./MainContent.react'); + +const query = require('__generated__/MainContentQuery.graphql'); + +// NOTE: This is NOT actual production code; +// it is only used to illustrate example +function App(props) { + // E.g., initialQueryRef provided by router + const [queryRef, loadQuery] = useQueryLoader(query, props.initialQueryRef); + + return ( + loadQuery(/* ... */)} + fallback={({error, retry}) => + <> + + {/* Render a button to retry; this will attempt to re-render the + content inside the boundary, i.e. the query component */} + + + }> + {/* The value of queryRef will be updated after calling + loadQuery again */} + + + ); +} + +/** + * MainContent.react.js + */ +function MainContent(props) { + const data = usePreloadedQuery( + graphql`...`, + props.queryRef + ); + + return (/* ... */); +} +``` +* The sample Error Boundary in this example code will provide a `retry` function to the `fallback` which we can use to clear the error, re-load the query, and re-render with a new query ref that we can pass to the component that uses `usePreloadedQuery`. That component will consume the new query ref and suspend if necessary on the new network request. + + +### When using `useLazyLoadQuery` + +When using `useLazyLoadQuery` to fetch a query, in order to retry after an error has occurred, you can attempt to re-mount *and* re-evaluate the query component by passing it a new `fetchKey`: + +```js +/** + * ErrorBoundaryWithRetry.react.js + */ + +const React = require('React'); + +// NOTE: This is NOT actual production code; +// it is only used to illustrate example +class ErrorBoundaryWithRetry extends React.Component { + state = {error: null, fetchKey: 0}; + + static getDerivedStateFromError(error): State { + return {error: error, fetchKey: 0}; + } + + _retry = () => { + this.setState(prev => ({ + // Clear the error + error: null, + // Increment and set a new fetchKey in order + // to trigger a re-evaluation and refetching + // of the query using useLazyLoadQuery + fetchKey: prev.fetchKey + 1, + })); + } + + render() { + const {children, fallback} = this.props; + const {error, fetchKey} = this.state; + if (error) { + if (typeof fallback === 'function') { + return fallback({error, retry: this._retry}); + } + return fallback; + } + return children({fetchKey}); + } +} +``` +* When an error occurs, we render the provided `fallback`. +* When `retry` is called, we will clear the error, and increment our `fetchKey` which we can then pass down to `useLazyLoadQuery`. This will make it so we re-render the component that uses `useLazyLoadQuery` with a new `fetchKey`, ensuring that the query is refetched upon the new call to `useLazyLoadQuery`. + +```js +/** + * App.react.js + */ + +const ErrorBoundaryWithRetry = require('ErrorBoundaryWithRetry'); +const React = require('React'); + +const MainContent = require('./MainContent.react'); + +// NOTE: This is NOT actual production code; +// it is only used to illustrate example +function App() { + return ( + + <> + + {/* Render a button to retry; this will attempt to re-render the + content inside the boundary, i.e. the query component */} + + + }> + {({fetchKey}) => { + // If we have retried, use the new `retryQueryRef` provided + // by the Error Boundary + return ; + }} + + ); +} + +/** + * MainContent.react.js + */ +function MainContent(props) { + const data = useLazyLoadQuery( + graphql`...`, + variables, + {fetchKey: props.fetchKey} + ); + + return (/* ... */); +} +``` +* The sample Error Boundary in this example code will provide a `retry` function to the `fallback` which we can use to clear the error and re-render `useLazyLoadQuery` with a new `fetchKey`. This will cause the query to be re-evaluated and refetched, and `useLazyLoadQuery` start a new network request and suspend. + + + +## Accessing errors in GraphQL Responses + + + + +By default, internally at fb, Relay will *only* surface errors to React that are returned in the top-level [`errors` field](https://graphql.org/learn/validation/) if they are ether: + +* of `CRITICAL` severity, +* *or* if the top-level `data` field wasn't returned in the response. + + + + +If you wish to access error information in your application to display user friendly messages, the recommended approach is to model and expose the error information as part of your GraphQL schema. + +For example, you could expose a field in your schema that returns either the expected result, or an Error object if an error occurred while resolving that field (instead of returning null): + + +```js +type Error { + # User friendly message + message: String! +} + +type Foo { + bar: Result | Error +} +``` + + + + + diff --git a/website/versioned_docs/version-v19.0.0/guided-tour/rendering/fragments.md b/website/versioned_docs/version-v19.0.0/guided-tour/rendering/fragments.md new file mode 100644 index 0000000000000..300913a407fce --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/guided-tour/rendering/fragments.md @@ -0,0 +1,354 @@ +--- +id: fragments +title: Fragments +slug: /guided-tour/rendering/fragments/ +description: Relay guide to rendering fragments +keywords: +- useFragment +- rendering +- fragment +--- + +import DocsRating from '@site/src/core/DocsRating'; +import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; + +The main building block for declaring data dependencies for React Components in Relay are [GraphQL Fragments](https://graphql.org/learn/queries/#fragments). Fragments are reusable units in GraphQL that represent a set of data to query from a GraphQL type exposed in the [schema](https://graphql.org/learn/schema/). + +In practice, they are a selection of fields on a GraphQL Type: + +```graphql +fragment UserFragment on User { + name + age + profile_picture(scale: 2) { + uri + } +} +``` + + +In order to declare a fragment inside your JavaScript code, you must use the `graphql` tag: + +```js +const {graphql} = require('react-relay'); + +const userFragment = graphql` + fragment UserFragment_user on User { + name + age + profile_picture(scale: 2) { + uri + } + } +`; +``` + +## Rendering Fragments + +In order to *render* the data for a fragment, you can use the `useFragment` Hook: + +```js +import type {UserComponent_user$key} from 'UserComponent_user.graphql'; + +const React = require('React'); + +const {graphql, useFragment} = require('react-relay'); + +type Props = { + user: UserComponent_user$key, +}; + +function UserComponent(props: Props) { + const data = useFragment( + graphql` + fragment UserComponent_user on User { + name + profile_picture(scale: 2) { + uri + } + } + `, + props.user, + ); + + return ( + <> +

    {data.name}

    +
    + +
    + + ); +} + +module.exports = UserComponent; +``` + +Let's distill what's going on here: + +* `useFragment` takes a fragment definition and a *fragment reference*, and returns the corresponding `data` for that fragment and reference. + * This is similar to `usePreloadedQuery`, which takes a query definition and a query reference. +* A *fragment reference* is an object that Relay uses to *read* the data declared in the fragment definition; as you can see, the `UserComponent_user` fragment itself just declares fields on the `User` type, but we need to know *which* specific user to read those fields from; this is what the fragment reference corresponds to. In other words, a fragment reference is like *a pointer to a specific instance of a type* that we want to read data from. +* Note that *the component is automatically subscribed to updates to the fragment data*: if the data for this particular `User` is updated anywhere in the app (e.g. via fetching new data, or mutating existing data), the component will automatically re-render with the latest updated data. +* Relay will automatically generate Flow types for any declared fragments when the compiler is run, so you can use these types to declare the type for your Component's `props`. + * The generated Flow types include a type for the fragment reference, which is the type with the `$key` suffix: `$key`, and a type for the shape of the data, which is the type with the `$data` suffix: `$data`; these types are available to import from files that are generated with the following name: `.graphql.js`. + * We use our [lint rule](https://github.com/relayjs/eslint-plugin-relay) to enforce that the type of the fragment reference prop is correctly declared when using `useFragment`. By using a properly typed fragment reference as input, the type of the returned `data` will automatically be Flow-typed without requiring an explicit annotation. + * In our example, we're typing the `user` prop as the fragment reference we need for `useFragment`, which corresponds to the `UserComponent_user$key` imported from `UserComponent_user.graphql`, which means that the type of `data` above would be: `{ name: ?string, profile_picture: ?{ uri: ?string } }`. +* Fragment names need to be globally unique. In order to easily achieve this, we name fragments using the following convention based on the module name followed by an identifier: `_`. This makes it easy to identify which fragments are defined in which modules and avoids name collisions when multiple fragments are defined in the same module. + + +If you need to render data from multiple fragments inside the same component, you can use `useFragment` multiple times: + +```js +import type {UserComponent_user$key} from 'UserComponent_user.graphql'; +import type {UserComponent_viewer$key} from 'UserComponent_viewer.graphql'; + +const React = require('React'); +const {graphql, useFragment} = require('react-relay'); + +type Props = { + user: UserComponent_user$key, + viewer: UserComponent_viewer$key, +}; + +function UserComponent(props: Props) { + const userData = useFragment( + graphql` + fragment UserComponent_user on User { + name + profile_picture(scale: 2) { + uri + } + } + `, + props.user, + ); + + const viewerData = useFragment( + graphql` + fragment UserComponent_viewer on Viewer { + actor { + name + } + } + `, + props.viewer, + ); + + return ( + <> +

    {userData.name}

    +
    + + Acting as: {viewerData.actor?.name ?? 'Unknown'} +
    + + ); +} + +module.exports = UserComponent; +``` + +## Composing Fragments + +In GraphQL, fragments are reusable units, which means they can include *other* fragments, and consequently a fragment can be included within other fragments or [queries](../queries/): + +```graphql +fragment UserFragment on User { + name + age + profile_picture(scale: 2) { + uri + } + ...AnotherUserFragment +} + +fragment AnotherUserFragment on User { + username + ...FooUserFragment +} +``` + + +With Relay, you can compose fragment components in a similar way, using both component composition and fragment composition. Each React component is responsible for fetching the data dependencies of its direct children - just as it has to know about its children's props in order to render them correctly. This pattern means that developers are able to reason locally about components - what data they need, what components they render - but Relay is able to derive a global view of the data dependencies of an entire UI tree. + +```js +/** + * UsernameSection.react.js + * + * Child Fragment Component + */ + +import type {UsernameSection_user$key} from 'UsernameSection_user.graphql'; + +const React = require('React'); +const {graphql, useFragment} = require('react-relay'); + +type Props = { + user: UsernameSection_user$key, +}; + +function UsernameSection(props: Props) { + const data = useFragment( + graphql` + fragment UsernameSection_user on User { + username + } + `, + props.user, + ); + + return
    {data.username ?? 'Unknown'}
    ; +} + +module.exports = UsernameSection; +``` + +```js +/** + * UserComponent.react.js + * + * Parent Fragment Component + */ + +import type {UserComponent_user$key} from 'UserComponent_user.graphql'; + +const React = require('React'); +const {graphql, useFragment} = require('react-relay'); + +const UsernameSection = require('./UsernameSection.react'); + +type Props = { + user: UserComponent_user$key, +}; + +function UserComponent(props: Props) { + const user = useFragment( + graphql` + fragment UserComponent_user on User { + name + age + profile_picture(scale: 2) { + uri + } + + # Include child fragment: + ...UsernameSection_user + } + `, + props.user, + ); + + return ( + <> +

    {user.name}

    +
    + + {user.age} + + {/* Render child component, passing the _fragment reference_: */} + +
    + + ); +} + +module.exports = UserComponent; +``` + +There are a few things to note here: + +* `UserComponent` both renders `UsernameSection`, *and* includes the fragment declared by `UsernameSection` inside its own `graphql` fragment declaration. +* `UsernameSection` expects a *fragment reference* as the `user` prop. As we've mentioned before, a fragment reference is an object that Relay uses to *read* the data declared in the fragment definition; as you can see, the child `UsernameSection_user` fragment itself just declares fields on the `User` type, but we need to know *which* specific user to read those fields from; this is what the fragment reference corresponds to. In other words, a fragment reference is like *a pointer to a specific instance of a type* that we want to read data from. +* Note that in this case the `user` passed to `UsernameSection`, i.e. the fragment reference, *doesn't actually contain any of the data declared by the child `UsernameSection` component*; instead, `UsernameSection` will use the fragment reference to read the data *it* declared internally, using `useFragment`. + * This means that the parent component will not receive the data selected by a child component (unless that parent explicitly selected the same fields). Likewise, child components will not receive the data selected by their parents (again, unless the child selected those same fields). + * This prevents separate components from *even accidentally* having implicit dependencies on each other. If this wasn't the case, modifying a component could break other components! + * This allows us to reason locally about our components and modify them without worrying about affecting other components. + * This is known as [*data masking*](../../../principles-and-architecture/thinking-in-relay/). +* The *fragment reference* that the child (i.e. `UsernameSection`) expects is the result of reading a parent fragment that *includes* the child fragment. In our particular example, that means the result of reading a fragment that includes `...UsernameSection_user` will be the fragment reference that `UsernameSection` expects. In other words, the data obtained as a result of reading a fragment via `useFragment` also serves as the fragment reference for any child fragments included in that fragment. + + +## Composing Fragments into Queries + +Fragments in Relay allow declaring data dependencies for a component, but they ***can't be fetched by themselves***. Instead, they need to be included in a query, either directly or transitively. This means that *all fragments must belong to a query when they are rendered*, or in other words, they must be "rooted" under some query. Note that a single fragment can still be included by multiple queries, but when rendering a specific *instance* of a fragment component, it must have been included as part of a specific query request. + +To fetch and render a query that includes a fragment, you can compose them in the same way fragments are composed, as shown in the [Composing Fragments](#composing-fragments) section: + +```js +/** + * UserComponent.react.js + * + * Fragment Component + */ + +import type {UserComponent_user$key} from 'UserComponent_user.graphql'; + +const React = require('React'); +const {graphql, useFragment} = require('react-relay'); + +type Props = { + user: UserComponent_user$key, +}; + +function UserComponent(props: Props) { + const data = useFragment( + graphql`...`, + props.user, + ); + + return (...); +} + +module.exports = UserComponent; +``` + +```js +/** + * App.react.js + * + * Query Component + */ + +import type {AppQuery} from 'AppQuery.graphql'; +import type {PreloadedQuery} from 'react-relay'; + +const React = require('React'); +const {graphql, usePreloadedQuery} = require('react-relay'); + +const UserComponent = require('./UserComponent.react'); + +type Props = { + appQueryRef: PreloadedQuery, +} + +function App({appQueryRef}) { + const data = usePreloadedQuery( + graphql` + query AppQuery($id: ID!) { + user(id: $id) { + name + + # Include child fragment: + ...UserComponent_user + } + } + `, + appQueryRef, + ); + + return ( + <> +

    {data.user?.name}

    + {/* Render child component, passing the fragment reference: */} + + + ); +} +``` + +Note that: + +* The *fragment reference* that `UserComponent` expects is the result of reading a parent query that includes its fragment, which in our case means a query that includes `...UsernameSection_user`. In other words, the `data` obtained as a result of `usePreloadedQuery` also serves as the fragment reference for any child fragments included in that query. +* As mentioned previously, *all fragments must belong to a query when they are rendered,* which means that all fragment components *must* be descendants of a query. This guarantees that you will always be able to provide a fragment reference for `useFragment`, by starting from the result of reading a root query with `usePreloadedQuery`. + + diff --git a/website/versioned_docs/version-v19.0.0/guided-tour/rendering/loading-states.md b/website/versioned_docs/version-v19.0.0/guided-tour/rendering/loading-states.md new file mode 100644 index 0000000000000..fb9ed1bbc7014 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/guided-tour/rendering/loading-states.md @@ -0,0 +1,245 @@ +--- +id: loading-states +title: Loading States with Suspense +slug: /guided-tour/rendering/loading-states/ +description: Relay guide to loading states +keywords: +- suspense +- loading +- glimmer +- fallback +- spinner +--- + +import DocsRating from '@site/src/core/DocsRating'; +import FbSuspensePlaceholder from '../../fb/FbSuspensePlaceholder.md'; +import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; +import FbSuspenseDefinition from './fb/FbSuspenseDefinition.md'; +import FbSuspenseMoreInfo from './fb/FbSuspenseMoreInfo.md'; +import FbSuspenseTransitionsAndUpdatesThatSuspend from './fb/FbSuspenseTransitionsAndUpdatesThatSuspend.md'; +import FbSuspenseInRelayTransitions from './fb/FbSuspenseInRelayTransitions.md'; +import FbSuspenseInRelayFragments from './fb/FbSuspenseInRelayFragments.md'; + + +As you may have noticed, we mentioned that using `usePreloadedQuery` and `useLazyLoadQuery` will render data from a query that was being fetched from the server, but we didn't elaborate on how to render a loading UI (such as a glimmer) while that data is still being fetched. We will cover that in this section. + + + + + + + +To render loading states while a query is being fetched, we rely on [React Suspense](https://reactjs.org/docs/concurrent-mode-suspense.html). Suspense is a new feature in React that allows components to interrupt or *"suspend"* rendering in order to wait for some asynchronous resource (such as code, images or data) to be loaded; when a component "suspends", it indicates to React that the component isn't *"ready"* to be rendered yet, and won't be until the asynchronous resource it's waiting for is loaded. When the resource finally loads, React will try to render the component again. + + + +This capability is useful for components to express asynchronous dependencies like data, code, or images that they require in order to render, and lets React coordinate rendering the loading states across a component tree as these asynchronous resources become available. More generally, the use of Suspense give us better control to implement more deliberately designed loading states when our app is loading for the first time or when it's transitioning to different states, and helps prevent accidental flickering of loading elements (such as spinners), which can commonly occur when loading sequences aren't explicitly designed and coordinated. + + + + + + +## Loading fallbacks with Suspense Boundaries + +When a component is suspended, we need to render a *fallback* in place of the component while we wait for it to become *"ready"*. In order to do so, we use the `Suspense` component provided by React: + +```js +const React = require('React'); +const {Suspense} = require('React'); + +function App() { + return ( + // Render a fallback using Suspense as a wrapper + }> + + + ); +} +``` + + +`Suspense` components can be used to wrap any component; if the target component suspends, `Suspense` will render the provided fallback until all its descendants become *"ready"* (i.e. until *all* of the suspended components within the subtree resolve). Usually, the fallback is used to render fallback loading states such as a glimmers and placeholders. + +Usually, different pieces of content in our app might suspend, so we can show loading state until they are resolved by using `Suspense` : + +```js +/** + * App.react.js + */ + +const React = require('React'); +const {Suspense} = require('React'); + +function App() { + return ( + // LoadingGlimmer is rendered via the Suspense fallback + }> + {/* MainContent may suspend */} + + ); +} +``` + + + + + +Let's distill what's going on here: + +* If `MainContent` suspends because it's waiting on some asynchronous resource (like data), the `Suspense` component that wraps `MainContent` will detect that it suspended, and will render the `fallback` element (i.e. the `LoadingGlimmer` in this case) up until `MainContent` is ready to be rendered. Note that this also transitively includes descendants of `MainContent`, which might also suspend. + + +What's nice about Suspense is that you have granular control about how to accumulate loading states for different parts of your component tree: + +```js +/** + * App.react.js + */ + +const React = require('React'); +const {Suspense} = require('React'); + +function App() { + return ( + // A LoadingGlimmer for all content is rendered via the Suspense fallback + }> + + {/* SecondaryContent can also suspend */} + + ); +} +``` + + + +* In this case, both `MainContent` and `SecondaryContent` may suspend while they load their asynchronous resources; by wrapping both in a `Suspense`, we can show a single loading state up until they are *all* ready, and then render the entire content in a single paint, after everything has successfully loaded. +* In fact, `MainContent` and `SecondaryContent` may suspend for different reasons other than fetching data, but the same `Suspense` component can be used to render a fallback up until *all* components in the subtree are ready to be rendered. Note that this also transitively includes descendants of `MainContent` or `SecondaryContent`, which might also suspend. + + +Conversely, you can also decide to be more granular about your loading UI and wrap Suspense components around smaller or individual parts of your component tree: + +```js +/** + * App.react.js + */ + +const React = require('React'); +const {Suspense} = require('React'); + +function App() { + return ( + <> + {/* Show a separate loading UI for the LeftHandColumn */} + }> + + + + {/* Show a separate loading UI for both the Main and Secondary content */} + }> + + + + + ); +} +``` + + + +* In this case, we're showing 2 separate loading UIs: + * One to be shown until the `LeftColumn` becomes ready + * And one to be shown until both the `MainContent` and `SecondaryContent` become ready. +* What is powerful about this is that by more granularly wrapping our components in Suspense, *we allow other components to be rendered earlier as they become ready*. In our example, by separately wrapping `MainContent` and `SecondaryContent` under `Suspense`, we're allowing `LeftColumn` to render as soon as it becomes ready, which might be earlier than when the content sections become ready. + + +## Transitions and Updates that Suspend + + + + + + + +`Suspense` boundary fallbacks allow us to describe our loading placeholders when initially rendering some content, but our applications will also have transitions between different content. Specifically, when switching between two components within an already mounted boundary, the new component you're switching to might not have loaded all of its async dependencies, which means that it might also suspend. + +In these cases, we would still show the `Suspense` boundary fallbacks. However, this means that we would hide existing content in favor of showing the `Suspense` fallback. In future versions of React when concurrent rendering is supported, React will provide an option to support this case and avoid hiding already rendered content with a Suspense fallback when suspending. + + + +## How We Use Suspense in Relay + +### Queries + +In our case, our query components are components that can suspend, so we use Suspense to render loading states while a query is being fetched. Let's see what that looks like in practice: + +Say we have the following query renderer component: + +```js +/** + * MainContent.react.js + * + * Query Component + */ + +const React = require('React'); +const {graphql, usePreloadedQuery} = require('react-relay'); + +function MainContent(props) { + // Fetch and render a query + const data = usePreloadedQuery( + graphql`...`, + props.queryRef, + ); + + return (...); +} +``` + +```js +/** + * App.react.js + */ + +const React = require('React'); +const {Suspense} = require('React'); + +function App() { + return ( + // LoadingGlimmer is rendered via the Suspense fallback + }> + {/* MainContent may suspend */} + + ); +} +``` + + + +Let's distill what's going on here: + +* We have a `MainContent` component, which is a query renderer that fetches and renders a query. `MainContent` will *suspend* rendering when it attempts to fetch the query, indicating that it isn't ready to be rendered yet, and it will resolve when the query is fetched. +* The `Suspense `component that wraps `MainContent` will detect that `MainContent` suspended, and will render the `fallback` element (i.e. the `LoadingGlimmer` in this case) up until `MainContent` is ready to be rendered; that is, up until the query is fetched. + + +### Fragments + + + + + + + +Fragments are also integrated with Suspense in order to support rendering of data that's being `@defer'`d or data that's partially available in the Relay Store (i.e. [partial rendering](../../reusing-cached-data/rendering-partially-cached-data/)). + + + +### Transitions + + + + + +Additionally, our APIs for fetching ([Refreshing and Refetching](../list-data/introduction.md)) and for [rendering connections](../../list-data/connections/) are also integrated with Suspense; for these use cases, these APIs will also suspend. + + diff --git a/website/versioned_docs/version-v19.0.0/guided-tour/rendering/queries.md b/website/versioned_docs/version-v19.0.0/guided-tour/rendering/queries.md new file mode 100644 index 0000000000000..9cd08b286363b --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/guided-tour/rendering/queries.md @@ -0,0 +1,261 @@ +--- +id: queries +title: Queries +slug: /guided-tour/rendering/queries/ +description: Relay guide to queries +keywords: +- query +- usePreloadedQuery +- useLazyLoadQuery +- useQueryLoader +- loadQuery +--- + +import DocsRating from '@site/src/core/DocsRating'; +import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; + +import FbEntrypointsExtraInfo from './fb/FbEntrypointsExtraInfo.md'; + +A [GraphQL Query](https://graphql.org/learn/queries/) is a description of data you want to query from a GraphQL server. It consists of a set of fields (and potentially [fragments](../fragments/)) that we want to request from the GraphQL server. What we can query for will depend on the [GraphQL Schema](https://graphql.org/learn/schema/) exposed on the server, which describes the data that is available for querying. + +A query can be sent as a request over the network, along with an optional collection of [variables](../variables/) that the query uses, in order to fetch the data. The server response will be a JSON object that matches the shape of the query we sent: + +```graphql +query UserQuery($id: ID!) { + user(id: $id) { + id + name + ...UserFragment + } + viewer { + actor { + name + } + } +} + +fragment UserFragment on User { + username +} +``` + + + +[Sample response](https://fburl.com/graphiql/v5hs717f): + + + + + +Sample response: + + + +```json +{ + "data": { + "user": { + "id": "4", + "name": "Mark Zuckerberg", + "username": "zuck" + }, + "viewer": { + "actor": { + "name": "Your Name" + } + } + } +} +``` + + + +## Rendering Queries + +To *render* a query in Relay, you can use the `usePreloadedQuery` hook. `usePreloadedQuery` takes a query definition and a query reference, and returns the corresponding data for that query and reference. + +```js +import type {HomeTabQuery} from 'HomeTabQuery.graphql'; +import type {PreloadedQuery} from 'react-relay'; + +const React = require('React'); +const {graphql, usePreloadedQuery} = require('react-relay'); + +type Props = { + queryRef: PreloadedQuery, +}; + +function HomeTab(props: Props) { + const data = usePreloadedQuery( + graphql` + query HomeTabQuery($id: ID!) { + user(id: $id) { + name + } + } + `, + props.queryRef, + ); + + return ( +

    {data.user?.name}

    + ); +} +``` + +Lets see what's going on here: + +* `usePreloadedQuery` takes a `graphql` query and a `PreloadedQuery` reference, and returns the data that was fetched for that query. + * The `PreloadedQuery` (in this case `queryRef`) is an object that describes and references an *instance* of our query that is being (or was) fetched. + * We'll cover how to actually fetch the query in the next section below, and cover how to show loading states if the query is in-flight when we try to render it in the [Loading States with Suspense](../loading-states/) section. +* Similarly to [fragments](../fragments/), *the component is automatically subscribed to updates to the query data*: if the data for this query is updated anywhere in the app, the component will automatically re-render with the latest updated data. +* `usePreloadedQuery` also takes a Flow type parameter, which corresponds to the Flow type for the query, in this case `HomeTabQuery`. + * The Relay compiler automatically generates Flow types for any declared queries, which are available to import from the generated files with the following name format: *``*`.graphql.js`. + * Note that the `data` is already properly Flow-typed without requiring an explicit annotation, and is based on the types from the GraphQL schema. For example, the type of `data` above would be: `{ user: ?{ name: ?string } }`. +* Make sure you're providing a Relay environment using a [Relay Environment Provider](../environment/) at the root of your app before trying to render a query. + + +## Fetching Queries for Render + +Apart from *rendering* a query, we also need to fetch it from the server. Usually we want to fetch queries somewhere at the root of our app, and only have **one or a few queries that [*accumulate*](../fragments/#composing-fragments-into-queries) all the data required to render the screen**. Ideally, we'd fetch them as early as possible, before we even start rendering our app. + +In order to *fetch* a query for later rendering it, you can use the `useQueryLoader` Hook: + +```js +import type {HomeTabQuery as HomeTabQueryType} from 'HomeTabQuery.graphql'; +import type {PreloadedQuery} from 'react-relay'; + +const HomeTabQuery = require('HomeTabQuery.graphql') +const {useQueryLoader} = require('react-relay'); + + +type Props = { + initialQueryRef: PreloadedQuery, +}; + +function AppTabs(props) { + const [ + homeTabQueryRef, + loadHomeTabQuery, + ] = useQueryLoader( + HomeTabQuery, + props.initialQueryRef, /* e.g. provided by router */ + ); + + const onSelectHomeTab = () => { + // Start loading query for HomeTab immediately in the event handler + // that triggers navigation to that tab, *before* we even start + // rendering the target tab. + // Calling this function will update the value of homeTabQueryRef. + loadHomeTabQuery({id: '4'}); + + // ... + } + + // ... + + return ( + screen === 'HomeTab' && homeTabQueryRef != null ? + // Pass to component that uses usePreloadedQuery + : + // ... + ); +} +``` + +The example above is somewhat contrived, but let's distill what is happening: + +* We are calling `useQueryLoader` inside our `AppTabs` component. + * It takes a query, which in this case is our `HomeTabQuery` (the query that we declared in our previous example), and which we can obtain by requiring the auto-generated file: `'HomeTabQuery.graphql'`. + * It takes an optional initial `PreloadedQuery` to be used as the initial value of the `homeTabQueryRef` that is stored in state and returned by `useQueryLoader`. + * It also additionally takes a Flow type parameter, which corresponds to the Flow type for the query, in this case `HomeTabQueryType`, which you can also obtain from the auto-generated file: `'HomeTabQuery.graphql'`. +* Calling `useQueryLoader` allows us to obtain 2 things: + * `homeTabQueryRef`: A `?PreloadedQuery`, which is an object that describes and references an *instance* of our query that is being (or was) fetched. This value will be null if we haven't fetched the query, i.e. if we haven't called `loadHomeTabQuery`. + * `loadHomeTabQuery`: A function that will *fetch* the data for this query from the server (if it isn't already cached), and given an object with the [variables](../variables/) the query expects, in this case `{id: '4'}` (we'll go into more detail about how Relay uses cached data in the [Reusing Cached Data For Render](../../reusing-cached-data/) section). Calling this function will also update the value of `homeTabQueryRef` to an instance of a `PreloadedQuery`. + * Note that the `variables` we pass to this function will be checked by Flow to ensure that you are passing values that match what the GraphQL query expects. + * Also note that we are calling this function in the event handler that causes the `HomeTab` to be rendered. This allows us to start fetching the data for the screen as early as possible, even before the new tab starts rendering. + * In fact, `loadQuery` will throw an error if it is called during React's render phase! +* Note that `useQueryLoader` will automatically dispose of all queries that have been loaded when the component unmounts. Disposing of a query means that Relay will no longer hold on to the data for that particular instance of the query in its cache (we'll cover the lifetime of query data in [Reusing Cached Data For Render](../../reusing-cached-data/) section). Additionally, if the request for the query is still in flight when disposal occurs, it will be canceled. +* Our `AppTabs` component renders the `HomeTab` component from the previous example, and passes it the corresponding query reference. Note that this parent component owns the lifetime of the data for that query, meaning that when it unmounts, it will of dispose of that query, as mentioned above. +* Finally, make sure you're providing a Relay environment using a [Relay Environment Provider](../environment/) at the root of your app before trying to use `useQueryLoader`. + + +Sometimes, you want to start a fetch outside of the context of a parent component, for example to fetch the data required for the initial load of the application. For these cases, you can use the `loadQuery` API directly, without using `useQueryLoader`: + +```js +import type {HomeTabQuery as HomeTabQueryType} from 'HomeTabQuery.graphql'; + +const HomeTabQuery = require('HomeTabQuery.graphql') +const {loadQuery} = require('react-relay'); + + +const environment = createEnvironment(...); + +// At some point during app initialization +const initialQueryRef = loadQuery( + environment, + HomeTabQuery, + {id: '4'}, +); + +// ... + +// E.g. passing the initialQueryRef to the root component +render() +``` + +* In this example, we are calling the `loadQuery` function directly to obtain a `PreloadedQuery` instance that we can later pass to a component that uses `usePreloadedQuery`. +* In this case, we would expect the root `AppTabs` component to manage the lifetime of the query reference, and dispose of it at the appropriate time, if at all. +* We've left the details of "app initialization" vague in this example, since that will vary from application to application. The important thing to note here is that we should obtain a query reference before we start rendering the root component. In fact, `loadQuery` will throw an error if it is called during React's render phase! + + +### Render as you Fetch + +The examples above illustrate how to separate fetching the data from rendering it, in order to start the fetch as early as possible (as opposed to waiting until the component is rendered to start the fetch), and allow us to show content to our users a lot sooner. It also helps prevent waterfalling round trips, and gives us more control and predictability over when the fetch occurs, whereas if we fetch during render, it becomes harder to determine when the fetch will (or should) occur. This fits nicely with the [*"render-as-you-fetch"*](https://reactjs.org/docs/concurrent-mode-suspense.html#approach-3-render-as-you-fetch-using-suspense) pattern with [React Suspense](../loading-states/). + +This is the preferred pattern for fetching data with Relay, and it applies in several circumstances, such as the initial load of an application, during subsequent navigations, or generally when using UI elements which are initially hidden and later revealed upon an interaction (such as menus, popovers, dialogs, etc), and which also require fetching additional data. + + + + +## Lazily Fetching Queries during Render + +Another alternative for fetching a query is to lazily fetch the query when the component is rendered. However, as we've mentioned previously, the preferred pattern is to start fetching queries ahead of rendering. If lazy fetching is used without caution, it can trigger nested or waterfalling round trips, and can degrade performance. + +To fetch a query lazily, you can use the `useLazyLoadQuery` Hook: + +```js +const React = require('React'); +const {graphql, useLazyLoadQuery} = require('react-relay'); + +function App() { + const data = useLazyLoadQuery( + graphql` + query AppQuery($id: ID!) { + user(id: $id) { + name + } + } + `, + {id: '4'}, + ); + + return ( +

    {data.user?.name}

    + ); +} +``` +Lets see what's going on here: + +* `useLazyLoadQuery` takes a graphql query and some variables for that query, and returns the data that was fetched for that query. The variables are an object containing the values for the [variables](../variables/) referenced inside the GraphQL query. +* Similarly to [fragments](../fragments/), the component is automatically subscribed to updates to the query data: if the data for this query is updated anywhere in the app, the component will automatically re-render with the latest updated data. +* `useLazyLoadQuery` additionally takes a Flow type parameter, which corresponds to the Flow type for the query, in this case AppQuery. + * Remember that Relay automatically generates Flow types for any declared queries, which you can import and use with `useLazyLoadQuery`. These types are available in the generated files with the following name format: `.graphql.js`. + * Note that the `variables` will be checked by Flow to ensure that you are passing values that match what the GraphQL query expects. + * Note that the data is already properly Flow-typed without requiring an explicit annotation, and is based on the types from the GraphQL schema. For example, the type of `data` above would be: `{ user: ?{ name: ?string } }`. +* By default, when the component renders, Relay will *fetch* the data for this query (if it isn't already cached), and return it as a the result of the `useLazyLoadQuery` call. We'll go into more detail about how to show loading states in the [Loading States with Suspense](../loading-states/) section, and how Relay uses cached data in the [Reusing Cached Data For Rendering](../../reusing-cached-data/) section. +* Note that if you re-render your component and pass *different query variables* than the ones originally used, it will cause the query to be fetched again with the new variables, and potentially re-render with different data. +* Finally, make sure you're providing a Relay environment using a [Relay Environment Provider](../../../api-reference/relay-environment-provider/) at the root of your app before trying to render a query. + + + diff --git a/website/versioned_docs/version-v19.0.0/guided-tour/rendering/variables.md b/website/versioned_docs/version-v19.0.0/guided-tour/rendering/variables.md new file mode 100644 index 0000000000000..39154f6c9607a --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/guided-tour/rendering/variables.md @@ -0,0 +1,233 @@ +--- +id: variables +title: Variables +slug: /guided-tour/rendering/variables/ +description: Relay guide to query variables +keywords: +- query +- variables +--- + +import DocsRating from '@site/src/core/DocsRating'; +import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; + +You may have noticed that the query declarations in our examples above contain references to an `$id` symbol inside the GraphQL code: these are [GraphQL Variables](https://graphql.org/learn/queries/#variables). + +GraphQL variables are a construct that allows referencing dynamic values inside a GraphQL query. When fetching a query from the server, we also need to provide as input the actual set of values to use for the variables declared inside the query: + +```graphql +query UserQuery($id: ID!) { + # The value of $id is used as input to the user() call: + user(id: $id) { + id + name + } +} +``` + +In the above, `ID!` is the type of the `$id` variable. That is, it is a required ID. + +When sending a network request to fetch the query above, we need to provide both the query, and the variables to be used for this particular execution of the query. For example: + +```graphql +# Query: +query UserQuery($id: ID!) { + # ... +} + +# Variables: +{"id": 4} +``` + + + + +Fetching the above query and variables from the server would produce the following response, which can also be visualized in [GraphiQL](https://fburl.com/graphiql/kiuar058): + + + + + +Fetching the above query and variables from the server would produce the following response: + + + +```json +{ + "data": { + "user": { + "id": "4", + "name": "Mark Zuckerberg" + } + } +} +``` + + +* * * + +Fragments can also reference variables that have been declared by a query: + +```graphql +fragment UserFragment on User { + name + profile_picture(scale: $scale) { + uri + } +} + + +query ViewerQuery($scale: Float!) { + viewer { + actor { + ...UserFragment + } + } +} +``` + +* Even though the fragment above doesn't *declare* the `$scale` variable directly, it can still reference it. Doing so makes it so any query that includes this fragment, either directly or transitively, *must* declare the variable and its type, otherwise an error will be produced. +* In other words, *query variables are available globally by any fragment that is a descendant of the query*. +* A fragment which references a global variable can only be included (directly or transitively) in a query which defines that global variable. + + +In Relay, fragment declarations inside components can also reference query variables: + +```js +function UserComponent(props: Props) { + const data = useFragment( + graphql` + fragment UserComponent_user on User { + name + profile_picture(scale: $scale) { + uri + } + } + `, + props.user, + ); + + return (...); +} +``` + +* The above fragment could be included by multiple queries, and rendered by different components, which means that any query that ends up rendering/including the above fragment *must* declare the `$scale` variable. +* If any query that happens to include this fragment *doesn't* declare the `$scale` variable, an error will be produced by the Relay Compiler at build time, ensuring that an incorrect query never gets sent to the server (sending a query with missing variable declarations will also produce an error in the server). + + + +## @arguments and @argumentDefinitions + +Relay also provides a way to declare variables that are scoped locally to a fragment using the `@arguments` and `@argumentDefinitions` directives. Fragments that use local variables are easy to customize and reuse, since they do not depend on the value of global (query-level) variables. + +```js +/** + * Declare a fragment that accepts arguments with @argumentDefinitions + */ + +function TaskView(props) { + const data = useFragment( + graphql` + fragment TaskView_task on Task + @argumentDefinitions(showDetailedResults: {type: "Boolean!"}) { + name + is_completed + ... @include(if: $showDetailedResults) { + description + } + } + `, + props.task, + ); +} +``` + +```js +/** + * Include fragment using @arguments + */ + +function TaskList(props) { + const data = usePreloadedQuery( + graphql` + query TaskListQuery { + todays_tasks { + ...TaskView_task @arguments(showDetailedResults: true) + } + tomorrows_tasks { + ...TaskView_task @arguments(showDetailedResults: false) + } + } + `, + props.queryRef, + ); +} +``` + +* Locally-scoped variables also make it easier to reuse a fragment from another query. + * A query definition must list all variables that are used by any nested fragments, including in recursively-nested fragments. + * Since a fragment can potentially be accessible from many queries, modifying a fragment that uses global variables can require changes to many query definitions. + * This can also lead to awkward situations, like having multiple versions of the "same" variable, such as `$showDetailedResults` and `$showDetails`. + + * Since fragments with only locally-scoped variables do not use global variables, they do not suffer from this issue. + +* Note that when passing `@arguments` to a fragment, we can pass a literal value or pass another variable. The variable can be a global query variable, a local variable declared via `@argumentDefinitions` or a literal (e.g. `42.0`). +* When we actually fetch `TaskView_task` as part of a query, the `showDetailedResults` value will depend on the argument that was provided by the parent of `TaskView_task`: + +Fragments that expect arguments can also declare default values, making the arguments optional: + +```js +/** + * Declare a fragment that accepts arguments with default values + */ + +function TaskView(props) { + const data = useFragment( + graphql` + fragment TaskView_task on Task + @argumentDefinitions(showDetailedResults: {type: "Boolean!", defaultValue: true}) { + name + is_completed + ... @include(if: $showDetailedResults) { + description + } + } + `, + props.task, + ); +} +``` + +```js +function TaskList(props) { + const data = usePreloadedQuery( + graphql` + query TaskListQuery { + todays_tasks { + ...TaskView_task + } + tomorrows_tasks { + ...TaskView_task @arguments(showDetailedResults: false) + } + } + `, + props.queryRef, + ); +} +``` + +* Not passing the argument to `TaskView_task` makes it use the default value for its locally declared `$showDetailedResult` variable. + + + +## Accessing GraphQL Variables At Runtime + + +If you want to access the variables that were set at the query root, the recommended approach is to pass the variables down the component tree in your application, using props, or your own application-specific context. + +Relay currently does not expose the resolved variables (i.e. after applying argument definitions) for a specific fragment, and you should very rarely need to do so. + + + + + diff --git a/website/versioned_docs/version-v19.0.0/guided-tour/reusing-cached-data/fetch-policies.md b/website/versioned_docs/version-v19.0.0/guided-tour/reusing-cached-data/fetch-policies.md new file mode 100644 index 0000000000000..e44df74e5efa5 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/guided-tour/reusing-cached-data/fetch-policies.md @@ -0,0 +1,56 @@ +--- +id: fetch-policies +title: Fetch Policies +slug: /guided-tour/reusing-cached-data/fetch-policies/ +description: Relay guide to fetch policies +keywords: +- fetch policy +- network-only +- store-only +- store-and-network +- store-or-network +--- + +import DocsRating from '@site/src/core/DocsRating'; +import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; + +The first step to reusing locally cached data is to pass a `fetchPolicy` to the `loadQuery` function, which can be provided by `useQueryLoader` (see the [Fetching Queries section](../../rendering/queries/)): + +```js +const React = require('React'); +const {graphql} = require('react-relay'); + +function AppTabs() { + const [ + queryRef, + loadQuery, + ] = useQueryLoader(HomeTabQuery); + + const onSelectHomeTab = () => { + loadQuery({id: '4'}, {fetchPolicy: 'store-or-network'}); + } + + // ... +} +``` + +The provided `fetchPolicy` will determine: + +* *whether* the query should be fulfilled from the local cache, and +* *whether* a network request should be made to fetch the query from the server, depending on the availability of the data for that query in the store. + + +By default, Relay will try to read the query from the local cache; if any piece of data for that query is [missing](../presence-of-data/) or [stale](../staleness-of-data/), it will fetch the entire query from the network. This default `fetchPolicy` is called "*store-or-network".* + +Specifically, `fetchPolicy` can be any of the following options: ** + +* "store-or-network": *(default)* *will* reuse locally cached data, and will *only* send a network request if any data for the query is [missing](../presence-of-data/) or [stale](../staleness-of-data/). If the query is fully cached, a network request will *not* be made. +* "store-and-network": *will* reuse locally cached data and will *always* send a network request, regardless of whether any data was [missing](../presence-of-data/) or [stale](../staleness-of-data/) in the store. +* "network-only": *will* *not* reuse locally cached data, and will *always* send a network request to fetch the query, ignoring any data that might be locally cached and whether it's [missing](../presence-of-data/) or [stale](../staleness-of-data/). +* "store-only": *will* *only* reuse locally cached data, and will *never* send a network request to fetch the query. In this case, the responsibility of fetching the query falls to the caller, but this policy could also be used to read and operate on data that is entirely [local](../../updating-data/local-data-updates/). + + +Note that the `refetch` function discussed in the [Fetching and Rendering Different Data](../list-data/introduction.md) section also takes a `fetchPolicy`. + + + diff --git a/website/versioned_docs/version-v19.0.0/guided-tour/reusing-cached-data/filling-in-missing-data.md b/website/versioned_docs/version-v19.0.0/guided-tour/reusing-cached-data/filling-in-missing-data.md new file mode 100644 index 0000000000000..ee183a527fe5e --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/guided-tour/reusing-cached-data/filling-in-missing-data.md @@ -0,0 +1,102 @@ +--- +id: filling-in-missing-data +title: Filling in Missing Data (Missing Field Handlers) +slug: /guided-tour/reusing-cached-data/filling-in-missing-data/ +description: Relay guide to filling in missing data +keywords: +- missing field handler +- missing data +--- + +import DocsRating from '@site/src/core/DocsRating'; +import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; + +import FbMissingFieldHandlers from './fb/FbMissingFieldHandlers.md'; + +In the previous section we covered how to reuse data that is fully or partially cached, however there are cases in which Relay can't automatically tell that it can reuse some of the data it already has from other queries in order to fulfill a specific query. Specifically, Relay knows how to reuse data that is cached for a query that has been fetched before; that is, if you fetch the exact same query twice, Relay will know that it has the data cached for that query the second time it tries to evaluate it. + +However, when using different queries, there might still be cases where different queries point to the same data, which we'd want to be able to reuse. For example, imagine the following two queries: + +```js +// Query 1 + +query UserQuery { + user(id: 4) { + name + } +} +``` + +```js +// Query 2 + +query NodeQuery { + node(id: 4) { + ... on User { + name + } + } +} +``` + + +These two queries are different, but reference the exact same data. Ideally, if one of the queries was already cached in the store, we should be able to reuse that data when rendering the other query. However, Relay doesn't have this knowledge by default, so we need to configure it to encode the knowledge that a `node(id: 4)` *"is the same as"* `user(id: 4)`. + +To do so, we can provide `missingFieldHandlers` to the `RelayEnvironment` which specifies this knowledge. + + + +```js +const {ROOT_TYPE, Environment} = require('relay-runtime'); + +const missingFieldHandlers = [ + { + handle(field, record, argValues): ?string { + // Make sure to add a handler for the node field + if ( + record != null && + record.getType() === ROOT_TYPE && + field.name === 'node' && + argValues.hasOwnProperty('id') + ) { + return argValues.id + } + if ( + record != null && + record.getType() === ROOT_TYPE && + field.name === 'user' && + argValues.hasOwnProperty('id') + ) { + // If field is user(id: $id), look up the record by the value of $id + return argValues.id; + } + if ( + record != null && + record.getType() === ROOT_TYPE && + field.name === 'story' && + argValues.hasOwnProperty('story_id') + ) { + // If field is story(story_id: $story_id), look up the record by the + // value of $story_id. + return argValues.story_id; + } + return undefined; + }, + kind: 'linked', + }, +]; + +const environment = new Environment({/*...*/, missingFieldHandlers}); +``` + +* `missingFieldHandlers` is an array of *handlers*. Each handler must specify a `handle` function, and the kind of missing fields it knows how to handle. The 2 main types of fields that you'd want to handle are: + * *'scalar'*: This represents a field that contains a scalar value, for example a number or a string. + * *'linked'*: This represents a field that references another object, i.e. not a scalar. +* The `handle` function takes the field that is missing, the record that field belongs to, and any arguments that were passed to the field in the current execution of the query. + * When handling a *'scalar'* field, the handle function should return a scalar value, in order to use as the value for a missing field + * When handling a *'linked'* field*,* the handle function should return an *ID*, referencing another object in the store that should be use in place of the missing field. ** +* As Relay attempts to fulfill a query from the local cache, whenever it detects any missing data, it will run any of the provided missing field handlers that match the field type before definitively declaring that the data is missing. + + + + diff --git a/website/versioned_docs/version-v19.0.0/guided-tour/reusing-cached-data/introduction.md b/website/versioned_docs/version-v19.0.0/guided-tour/reusing-cached-data/introduction.md new file mode 100644 index 0000000000000..c4ea2384f6dbf --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/guided-tour/reusing-cached-data/introduction.md @@ -0,0 +1,22 @@ +--- +id: introduction +title: Reusing Cached Data +slug: /guided-tour/reusing-cached-data/ +description: Relay guide to reusing cached data +keywords: +- reusing +- cached +--- + +import DocsRating from '@site/src/core/DocsRating'; +import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; + +While an app is in use, Relay will accumulate and cache *(for some time)* the data for the multiple queries that have been fetched throughout usage of our app. Often times, we'll want to be able to reuse and immediately render this data that is locally cached instead of waiting for a network request when fulfilling a query; this is what we'll cover in this section. + +Some examples of when this might be useful are: + +* Navigating between tabs in an app, where each app renders a query. If a tab has already been visited, re-visiting the tab should render it instantly, without having to wait for a network request to fetch the data that we've already fetched before. +* Navigating to a post that was previously rendered on a feed. If the post has already been rendered on a feed, navigating to the post's permalink page should render the post immediately, since all of the data for the post should already be cached. + * Even if rendering the post in the permalink page requires more data than rendering the post on a feed, we'd still like to reuse and immediately render as much of the post's data that we already have available locally, without blocking render for the entire post if only a small bit of data is missing. + + diff --git a/website/versioned_docs/version-v19.0.0/guided-tour/reusing-cached-data/presence-of-data.md b/website/versioned_docs/version-v19.0.0/guided-tour/reusing-cached-data/presence-of-data.md new file mode 100644 index 0000000000000..b057115fbfede --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/guided-tour/reusing-cached-data/presence-of-data.md @@ -0,0 +1,93 @@ +--- +id: presence-of-data +title: Presence of Data +slug: /guided-tour/reusing-cached-data/presence-of-data/ +description: Relay guide to the presence of data +keywords: +- presence +--- + +import DocsRating from '@site/src/core/DocsRating'; +import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; +import FbGarbageCollection from './fb/FbGarbageCollection.md'; + + +An important thing to keep in mind when attempting to reuse data that is cached in the Relay store is to understand the lifetime of that data; that is, if it is present in the store, and for how long it will be. + +Data in the Relay store for a given query will generally be present after the query has been fetched for the first time, as long as that query is being rendered on the screen. If we've never fetched data for a specific query, then it will be missing from the store. + +However, even after we've fetched data for different queries, we can't keep all of the data that we've fetched indefinitely in memory, since over time it would grow to be too large and too stale. In order to mitigate this, Relay runs a process called *Garbage Collection*, in order to delete data that we're no longer using. + +## Garbage Collection in Relay + +Specifically, Relay runs garbage collection on the local in-memory store by deleting any data that is no longer being referenced by any component in the app. + +However, this can be at odds with reusing cached data; if the data is deleted too soon, before we try to reuse it again later, that will prevent us from reusing that data to render a screen without having to wait on a network request. To address this, this section will cover what you need to do in order to ensure that the data you want to reuse is kept cached for as long as you need it. + + +:::note +NOTE: Usually, you shouldn't need to worry about configuring garbage collection and data retention, as this should be configured by the app infrastructure at the RelayEnvironment level; however, we will cover it here for reference. +::: + + + + + +## Query Retention + +Retaining a query indicates to Relay that the data for that query and variables shouldn't be deleted (i.e. garbage collected). Multiple callers might retain a single query, and as long as there is at least one caller retaining a query, it won't be deleted from the store. + +By default, any query components using `useQueryLoader` / `usePreloadedQuery` or our other APIs will retain the query for as long as they are mounted. After they unmount, they will release the query, which means that the query might be deleted at any point in the future after that occurs. + +If you need to retain a specific query outside of the components lifecycle, you can use the [`retain`](../../accessing-data-without-react/retaining-queries/) operation: + +```js +// Retain query; this will prevent the data for this query and +// variables from being garbage collected by Relay +const disposable = environment.retain(queryDescriptor); + +// Disposing of the disposable will release the data for this query +// and variables, meaning that it can be deleted at any moment +// by Relay's garbage collection if it hasn't been retained elsewhere +disposable.dispose(); +``` + +* As mentioned, this will allow you to retain the query even after a query component has unmounted, allowing other components, or future instances of the same component, to reuse the retained data. + + +## Controlling Relay's Garbage Collection Policy + +There are currently 2 options you can provide to your Relay Store in to control the behavior of garbage collection: + +### GC Scheduler + +The `gcScheduler` is a function you can provide to the Relay Store which will determine when a GC execution should be scheduled to run: + +```js +// Sample scheduler function +// Accepts a callback and schedules it to run at some future time. +function gcScheduler(run: () => void) { + resolveImmediate(run); +} + +const store = new Store(source, {gcScheduler}); +``` + +* By default, if a `gcScheduler` option is not provided, Relay will schedule garbage collection using the `resolveImmediate` function. +* You can provide a scheduler function to make GC scheduling less aggressive than the default, for example based on time or [scheduler](https://github.com/facebook/react/tree/main/packages/scheduler) priorities, or any other heuristic. By convention, implementations should not execute the callback immediately. + + +### GC Release Buffer Size + +The Relay Store internally holds a release buffer to keep a specific (configurable) number of queries temporarily retained even *after* they have been released by their original owner (which will happen by default when a component rendering that query unmounts). This makes it possible (and more likely) to be able to reuse data when navigating back to a page, tab or piece of content that has been visited before. + +In order to configure the size of the release buffer, we can specify the `gcReleaseBufferSize` option to the Relay Store: + +```js +const store = new Store(source, {gcReleaseBufferSize: 10}); +``` + +* Note that having a buffer size of 0 is equivalent to not having the release buffer, which means that queries will be immediately released and collected. +* By default, environments have a release buffer size of 10. + + diff --git a/website/versioned_docs/version-v19.0.0/guided-tour/reusing-cached-data/rendering-partially-cached-data.md b/website/versioned_docs/version-v19.0.0/guided-tour/reusing-cached-data/rendering-partially-cached-data.md new file mode 100644 index 0000000000000..6028410b79f6b --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/guided-tour/reusing-cached-data/rendering-partially-cached-data.md @@ -0,0 +1,175 @@ +--- +id: rendering-partially-cached-data +title: Rendering Partially Cached Data +slug: /guided-tour/reusing-cached-data/rendering-partially-cached-data/ +description: Relay guide to rendering partially cached data +keywords: +- partially cached data +- renderPolicy +--- + +import DocsRating from '@site/src/core/DocsRating'; +import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; +import FbProfilePhotoHeaderExample from './fb/FbProfileHeaderExample.md'; +import FbSuspensePlaceholder from '../../fb/FbSuspensePlaceholder.md'; + +When rendering cached data in Relay, it is possible to perform partial rendering. We define *"partial rendering"* as the ability to immediately render a query that is partially cached. That is, parts of the query might be missing, but parts of the query might already be cached. In these cases, we want to be able to immediately render the parts of the query that are cached, without waiting on the full query to be fetched. + +This can be useful in scenarios where we want to render a screen or a page as fast as possible, and we know that some of the data for that page is already cached so we can skip a loading state. For example, take the profile page: it is very likely that the user's name has already been cached at some point during usage of the app, so when visiting a profile page, if the name of the user is cached, we'd like to render immediately, even if the rest of the data for the profile page isn't available yet. + + +### Fragments as boundaries for partial rendering + +To do this, we rely on the ability of fragment components to *suspend* (see the [Loading States with Suspense](../../rendering/loading-states/) section). A fragment component will suspend if any of the data it declared locally is missing during render, and is currently being fetched. Specifically, it will suspend until the data it requires is fetched, that is, until the query to which it belongs (its *parent query*) is fetched. + +Let's explain what this means with an example. Say we have the following fragment component: + +```js +/** + * UsernameComponent.react.js + * + * Fragment Component + */ + +import type {UsernameComponent_user$key} from 'UsernameComponent_user.graphql'; + +const React = require('React'); +const {graphql, useFragment} = require('react-relay'); + +type Props = { + user: UsernameComponent_user$key, +}; + +function UsernameComponent(props: Props) { + const user = useFragment( + graphql` + fragment UsernameComponent_user on User { + username + } + `, + props.user, + ); + return (...); +} + +module.exports = UsernameComponent; +``` + + +And we have the following query component, which queries for some data, and also includes the fragment above: + +```javascript +/** + * AppTabs.react.js + * + * Query Loader Component + */ + + // .... + + const onSelectHomeTab = () => { + loadHomeTabQuery({id: '4'}, {fetchPolicy: 'store-or-network'}); + } + + // ... + +/** + * HomeTab.react.js + * + * Query Component + */ + +const React = require('React'); +const {graphql, usePreloadedQuery} = require('react-relay'); + +const UsernameComponent = require('./UsernameComponent.react'); + +function HomeTab(props: Props) { + const data = usePreloadedQuery( + graphql` + query HomeTabQuery($id: ID!) { + user(id: $id) { + name + ...UsernameComponent_user + } + } + `, + props.queryRef, + ); + + return ( + <> +

    {data.user?.name}

    + + + ); +} +``` + + +Say that when this `HomeTab` component is rendered, we've already previously fetched *(_only_)* the `name` for the `User` with `{id: 4}`, and it is locally cached in the Relay store associated with our current Relay environment. + +If we attempt to render the query with a `fetchPolicy` that allows reusing locally cached data (`'store-or-network'`, or `'store-and-network'`), the following will occur: + +* The query will check if any of its locally-required data is missing. In this case, *it isn't*. Specifically, the query is only directly selecting the `name` field, and that field *is* available in the store. + * Relay considers data to be missing only if it is declared locally and missing. In other words, data that is selected within fragment spreads does not affect whether the outer query or fragment is determined to have missing data. +* Given that the query doesn't have any data missing, it will render, and then attempt to render the child `UsernameComponent`. +* When the `UsernameComponent` attempts to render the `UsernameComponent_user` fragment, Relay will notice that some of the data required to render is missing; specifically, the `username` is missing. At this point, since `UsernameComponent` has missing data, it will suspend rendering until the network request completes. Note that regardless of which `fetchPolicy` you choose, a network request will always be started if any piece of data for the full query, i.e. including fragments, is missing. + + +At this point, when `UsernameComponent` suspends due to the missing `username`, ideally we should still be able to render the `User`'s `name` immediately, since it's locally cached. However, since we aren't using a `Suspense` component to catch the fragment's suspension, the suspension will bubble up and the entire `App` component will be suspended. + +In order to achieve the desired effect of rendering the `name` when it's available even if the `username` is missing, we just need to wrap the `UsernameComponent` in `Suspense,` to *allow* the other parts of `App` to continue rendering: + +```js +/** + * HomeTab.react.js + * + * Query Component + */ + +const React = require('React'); +const {Suspense} = require('React'); +const {graphql, usePreloadedQuery} = require('react-relay'); + +const UsernameComponent = require('./UsernameComponent.react'); + + +function HomeTab() { + const data = usePreloadedQuery( + graphql` + query AppQuery($id: ID!) { + user(id: $id) { + name + ...UsernameComponent_user + } + } + `, + props.queryRef, + ); + + return ( + <> +

    {data.user?.name}

    + + {/* + Wrap the UserComponent in Suspense to allow other parts of the + App to be rendered even if the username is missing. + */} + }> + + + + ); +} +``` + + + +The process that we described above works the same way for nested fragments (i.e. fragments included within other fragments). This means that if the data required to render a fragment is locally cached, the fragment component will be able to render, regardless of whether data for any of its child or descendant fragments is missing. If data for a child fragment is missing, we can wrap it in a `Suspense` component to allow other fragments and parts of the app to continue rendering. + +As mentioned in our motivating example, this is desirable because it can allows us to skip loading states entirely. More specifically, the ability to render data that is partially available allows us to render intermediate UI states that more closely resemble the final rendered state. + + + + diff --git a/website/versioned_docs/version-v19.0.0/guided-tour/reusing-cached-data/staleness-of-data.md b/website/versioned_docs/version-v19.0.0/guided-tour/reusing-cached-data/staleness-of-data.md new file mode 100644 index 0000000000000..639a696917510 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/guided-tour/reusing-cached-data/staleness-of-data.md @@ -0,0 +1,116 @@ +--- +id: staleness-of-data +title: Staleness of Data +slug: /guided-tour/reusing-cached-data/staleness-of-data/ +description: Relay guide to the staleness of data +keywords: +- staleness +--- + +import DocsRating from '@site/src/core/DocsRating'; +import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; +import FbPushViews from './fb/FbPushViews.md'; + +Assuming our data is [present in the store](../presence-of-data/), we still need to consider the staleness of such data. + +By default, Relay will not consider data in the store to be stale (regardless of how long it has been in the cache), unless it's explicitly marked as stale using our data invalidation APIs or if it is older than the query cache expiration time. + +Marking data as stale is useful for cases when we explicitly know that some data is no longer fresh (for example after executing a [Mutation](../../updating-data/graphql-mutations/)). + +Relay exposes the following APIs to mark data as stale within an update to the store: + +## Globally Invalidating the Relay Store + +The coarsest type of data invalidation we can perform is invalidating the *whole* store, meaning that all currently cached data will be considered stale after invalidation. + +To invalidate the store, we can call `invalidateStore()` within an [updater](../../updating-data/graphql-mutations/) function: + +```js +function updater(store) { + store.invalidateStore(); +} +``` + +* Calling `invalidateStore()` will cause *all* data that was written to the store before invalidation occurred to be considered stale, and will require any query to be refetched again the next time it's evaluated. +* Note that an updater function can be specified as part of a [mutation](../../updating-data/graphql-mutations/), [subscription](../../updating-data/graphql-subscriptions/) or just a [local store update](../../updating-data/local-data-updates/). + +## Invalidating Specific Data In The Store + +We can also be more granular about which data we invalidate and only invalidate *specific records* in the store; compared to global invalidation, only queries that reference the invalidated records will be considered stale after invalidation. + +To invalidate a record, we can call `invalidateRecord()` within an [updater](../../updating-data/graphql-mutations/) function: + +```js +function updater(store) { + const user = store.get(''); + if (user != null) { + user.invalidateRecord(); + } +} +``` + +* Calling `invalidateRecord()` on the `user` record will mark *that* specific user in the store as stale. That means that any query that is cached and references that invalidated user will now be considered stale, and will require to be refetched again the next time it's evaluated. +* Note that an updater function can be specified as part of a [mutation](../../updating-data/graphql-mutations/), [subscription](../../updating-data/graphql-subscriptions/) or just a [local store update](../../updating-data/local-data-updates/). + +## Subscribing to Data Invalidation + +Just marking the store or records as stale will cause queries to be refetched they next time they are evaluated; so for example, the next time you navigate back to a page that renders a stale query, the query will be refetched even if the data is cached, since the query references stale data. + +This is useful for a lot of use cases, but there are some times when we'd like to immediately refetch some data upon invalidation, for example: + +* When invalidating data that is already visible in the current page. Since no navigation is occurring, we won't re-evaluate the queries for the current page, so even if some data is stale, it won't be immediately refetched and we will be showing stale data. +* When invalidating data that is rendered on a previous view that was never unmounted; since the view wasn't unmounted, if we navigate back, the queries for that view won't be re-evaluated, meaning that even if some is stale, it won't be refetched and we will be showing stale data. + + + +To support these use cases, Relay exposes the `useSubscribeToInvalidationState` hook: + +```js +function ProfilePage(props) { + // Example of querying data for the current page for a given user + const data = usePreloadedQuery( + graphql`...`, + props.preloadedQuery, + ) + + // Here we subscribe to changes in invalidation state for the given user ID. + // Whenever the user with that ID is marked as stale, the provided callback will + // be executed + useSubscribeToInvalidationState([props.userID], () => { + // Here we can do things like: + // - re-evaluate the query by passing a new preloadedQuery to usePreloadedQuery. + // - imperatively refetch any data + // - render a loading spinner or gray out the page to indicate that refetch + // is happening. + }) + + return (...); +} +``` + +* `useSubscribeToInvalidationState` takes an array of ids, and a callback. Whenever any of the records for those ids are marked as stale, the provided callback will fire. +* Inside the callback, we can react accordingly and refetch and/or update any current views that are rendering stale data. As an example, we could re-execute the top-level `usePreloadedQuery` by keeping the `preloadedQuery` in state and setting a new one here; since that query is stale at that point, the query will be refetched even if the data is cached in the store. + + +## Query Cache Expiration Time + +In addition, the query cache expiration time affects whether certain operations (i.e. a query and variables) can be fulfilled with data that is already present in the store, i.e. whether the data for a query has become stale. + + A stale query is one which can be fulfilled with records from the store, and + +* the time since it was last fetched is greater than the query cache expiration time, or +* which contains at least one record that was invalidated. + +This staleness check occurs when a new request is made (e.g. in a call to `loadQuery`). Components which reference stale data will continue to be able to render that data; however, any additional requests which would be fulfilled using stale data will go to the network. + +In order to configure the query cache expiration time, we can specify the `queryCacheExpirationTime` option to the Relay Store: + +```js +const store = new Store(source, {queryCacheExpirationTime: 5 * 60 * 1000 }); +``` + +If the query cache expiration time is not provided, staleness checks only look at whether the referenced records have been invalidated. + + + + diff --git a/website/versioned_docs/version-v19.0.0/guided-tour/updating-data/client-only-data.md b/website/versioned_docs/version-v19.0.0/guided-tour/updating-data/client-only-data.md new file mode 100644 index 0000000000000..1c7f1c81195c4 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/guided-tour/updating-data/client-only-data.md @@ -0,0 +1,115 @@ +--- +id: client-only-data +title: Client-only data +slug: /guided-tour/updating-data/client-only-data/ +description: Relay guide to client-only data +keywords: +- client-only +--- + +import DocsRating from '@site/src/core/DocsRating'; +import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; +import FbClientOnlyDataDir from './fb/FbClientOnlyDataDir.md'; + +## Client-Only Data (Client Schema Extensions) + +Relay provides the ability to extend the GraphQL schema *on the client* (i.e. in the browser), via client schema extensions, in order to model data that only needs to be created, read and updated on the client. This can be useful to add small pieces of information to data that is fetched from the server, or to entirely model client-specific state to be stored and managed by Relay. + +Client schema extensions allows you to modify existing types on the schema (e.g. by adding new fields to a type), or to create entirely new types that only exist in the client. + + +### Extending Existing Types + + + +In order to extend an existing type, add a `.graphql` file to the appropriate schema extension directory (depending on the repo): + + + + + +In order to extend an existing type, add a `.graphql` file to your appropriate source (`--src`) directory: + + + + +```graphql +extend type Comment { + is_new_comment: Boolean +} +``` + + + + + + + + + +* In this example, we're using the `extend` keyword to extend an existing type, and we're adding a new field, `is_new_comment` to the existing `Comment` type, which we will be able to [read](#reading-client-only-data) in our components, and [update](#updating-client-only-data) when necessary using normal Relay APIs; you might imagine that we might use this field to render a different visual treatment for a comment if it's new, and we might set it when creating a new comment. + + + +### Adding New Types + +You can define types using the same regular GraphQL syntax, by defining it inside a `.graphql` file in `html/js/relay/schema/`: + + +```graphql +# You can define more than one type in a single file +enum FetchStatus { + FETCHED + PENDING + ERRORED +} + + +type FetchState { + # You can reuse client types to define other types + status: FetchStatus + + # You can also reference regular server types + started_by: User! +} + +extend type Item { + # You can extend server types with client-only types + fetch_state: FetchState +} +``` + +* In this contrived example, we're defining 2 new client-only types, and `enum` and a regular `type`. Note that they can reference themselves as normal, and reference regular server defined types. Also note that we can extend server types and add fields that are of our client-only types. +* As mentioned previously, we will be able to [read](#reading-client-only-data) and [update](#updating-client-only-data) this data normally via Relay APIs. + + + +### Reading Client-Only Data + +We can read client-only data be selecting it inside [fragments](../../rendering/fragments/) or [queries](../../rendering/queries/) as normal: + +```js +const data = *useFragment*( + graphql` + fragment CommentComponent_comment on Comment { + + # We can select client-only fields as we would any other field + is_new_comment + + body { + text + } + } + `, + props.user, +); +``` + + + +### Updating Client-Only Data + +In order to update client-only data, you can do so regularly inside [mutation](../graphql-mutations/) or [subscription](../graphql-subscriptions/) updaters, or by using our primitives for doing [local updates](../local-data-updates/) to the store. + + + diff --git a/website/versioned_docs/version-v19.0.0/guided-tour/updating-data/graphql-mutations.md b/website/versioned_docs/version-v19.0.0/guided-tour/updating-data/graphql-mutations.md new file mode 100644 index 0000000000000..b4b3536f608cf --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/guided-tour/updating-data/graphql-mutations.md @@ -0,0 +1,378 @@ +--- +id: graphql-mutations +title: GraphQL mutations +slug: /guided-tour/updating-data/graphql-mutations/ +description: Relay guide to GraphQL mutations +keywords: +- mutation +--- + +import DocsRating from '@site/src/core/DocsRating'; +import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; + +In GraphQL, data on the server is updated using [GraphQL mutations](https://graphql.org/learn/queries/#mutations). Mutations are read-write server operations, which both modify the data on the backend and allow you to query the modified data in the same request. + +## Writing Mutations + +A GraphQL mutation looks very similar to a query, except that it uses the `mutation` keyword: + +```graphql +mutation FeedbackLikeMutation($input: FeedbackLikeData!) { + feedback_like(data: $input) { + feedback { + id + viewer_does_like + like_count + } + } +} +``` + +* The mutation above modifies the server data to "like" the specified `Feedback` object. +* `feedback_like` is a *mutation root field* (or just *mutation field*) which updates data on the backend. + + + +:::info +You can view mutation root fields in the GraphQL Schema Explorer by opening VSCode @ FB and executing the command "Relay: Open GraphQL Schema Explorer". Then, in the "Schema Explorer Tab", click on "Mutation". + +You can click on the various mutation fields to see their parameters, descriptions and exposed fields. +::: + + + +* A mutation is handled in two separate steps: first, the update is processed on the server, and then the query is executed. This ensures that you only see data that has already been updated as part of your mutation response. + +:::note +Note that queries are processed in the same way. Outer selections are calculated before inner selections. It is simply a matter of convention that top-level mutation fields have side-effects, while other fields tend not to. +::: + +* The mutation field (in this case, `feedback_like`) returns a specific GraphQL type which exposes the data for which we can query in the mutation response. + + + +* [It is a best practice](https://fb.workplace.com/groups/644933736023601/?multi_permalinks=823422684841371) to include the `viewer` object and all updated Ents as part of the mutation response. + + + +* In this case, we're querying for the *updated* feedback object, including the updated `like_count` and the updated value for `viewer_does_like`, indicating whether the current viewer likes the feedback object. + + + +* Check out the [Hack documentation on writing mutations](https://www.internalfb.com/intern/wiki/Graphql-for-hack-developers/mutation-root-fields/) for information on how to add a mutation field to your backend code. + + + +An example of a successful response for the above mutation could look like this: + +```json +{ + "feedback_like": { + "feedback": { + "id": "feedback-id", + "viewer_does_like": true, + "like_count": 1, + } + } +} +``` + +In Relay, we can declare GraphQL mutations using the `graphql` tag too: + +```js +const {graphql} = require('react-relay'); + +const feedbackLikeMutation = graphql` + mutation FeedbackLikeMutation($input: FeedbackLikeData!) { + feedback_like(data: $input) { + feedback { + id + viewer_does_like + like_count + } + } + } +`; +``` + +* Note that mutations can also reference GraphQL [variables](../../rendering/variables/) in the same way queries or fragments do. + +## Using `useMutation` to execute a mutation + +In order to execute a mutation against the server in Relay, we can use the `commitMutation` and [useMutation](../../../api-reference/use-mutation) APIs. Let's take a look at an example using the `useMutation` API: + +```js +import type {FeedbackLikeData, LikeButtonMutation} from 'LikeButtonMutation.graphql'; + +const {useMutation, graphql} = require('react-relay'); + +function LikeButton({ + feedbackId: string, +}) { + const [commitMutation, isMutationInFlight] = useMutation( + graphql` + mutation LikeButtonMutation($input: FeedbackLikeData!) { + feedback_like(data: $input) { + feedback { + viewer_does_like + like_count + } + } + } + ` + ); + + return +} +``` + +Let's distill what's happening here. + +* `useMutation` takes a graphql literal containing a mutation as its only argument. +* It returns a tuple of items: + * a callback (which we call `commitMutation`) which accepts a `UseMutationConfig`, and + * a boolean indicating whether a mutation is in flight. +* In addition, `useMutation` accepts a Flow type parameter. As with queries, the Flow type of the mutation is exported from the file that the Relay compiler generates. + * If this type is provided, the `UseMutationConfig` becomes statically typed as well. **It is a best practice to always provide this type.** +* Now, when `commitMutation` is called with the mutation variables, Relay will make a network request that executes the `feedback_like` field on the server. In this example, this would find the feedback specified by the variables, and record on the backend that the user liked that piece of feedback. +* Once that field is executed, the backend will select the updated Feedback object and select the `viewer_does_like` and `like_count` fields off of it. + * Since the `Feedback` type contains an `id` field, the Relay compiler will automatically add a selection for the `id` field. +* When the mutation response is received, Relay will find a feedback object in the store with a matching `id` and update it with the newly received `viewer_does_like` and `like_count` values. +* If these values have changed as a result, any components which selected these fields off of the feedback object will be re-rendered. Or, to put it colloquially, any component which depends on the updated data will re-render. + +:::note +The name of the type of the parameter `FeedbackLikeData` is derived from the name of the top-level mutation field, i.e. from `feedback_like`. This type is also exported from the generated `graphql.js` file. +::: + +## Refreshing components in response to mutations + +In the previous example, we manually selected `viewer_does_like` and `like_count`. Components that select these fields will be re-rendered, should the value of those fields change. + +However, it is generally better to spread fragments that correspond to components that we want to refresh in response to the mutation. This is because the data selected by components can change. + +Requiring developers to know about all mutations that might affect their components' data (and keeping them up-to-date) is an example of the kind of global reasoning that Relay wants to avoid requiring. + +For example, we might rewrite the mutation as follows: + +```graphql +mutation FeedbackLikeMutation($input: FeedbackLikeData!) { + feedback_like(data: $input) { + feedback { + ...FeedbackDisplay_feedback + ...FeedbackDetail_feedback + } + } +} +``` + +If this mutation is executed, then whatever fields were selected by the `FeedbackDisplay` and `FeedbackDetail` components will be refetched, and those components will remain in a consistent state. + +:::note +Spreading fragments is generally preferable to refetching the data after a mutation has completed, since the updated data can be fetched in a single round trip. +::: + +## Executing a callback when the mutation completes or errors + +We may want to update some state in response to the mutation succeeding or failing. For example, we might want to alert the user if the mutation failed. The `UseMutationConfig` object can include the following fields to handle such cases: + +* `onCompleted`, a callback that is executed when the mutation completes. It is passed the mutation response (stopping at fragment spread boundaries). + * The value passed to `onCompleted` is the the mutation fragment, as read out from the store, **after** updaters and declarative mutation directives are applied. This means that data from within unmasked fragments will not be read, and records that were deleted (e.g. by `@deleteRecord`) may also be null. +* `onError`, a callback that is executed when the mutation errors. It is passed the error that occurred. + +## Declarative mutation directives + +### Manipulating connections in response to mutations + +Relay makes it easy to respond to mutations by adding items to or removing items from connections (i.e. lists). For example, you might want to append a newly created user to a given connection. For more, see [Using declarative directives](../../list-data/updating-connections/#using-declarative-directives). + +### Deleting items in response to mutations + +In addition, you might want to delete an item from the store in response to a mutation. In order to do this, you would add the `@deleteRecord` directive to the deleted ID. For example: + +```graphql +mutation DeletePostMutation($input: DeletePostData!) { + delete_post(data: $input) { + deleted_post { + id @deleteRecord + } + } +} +``` + +## Imperatively modifying local data + +At times, the updates you wish to perform are more complex than just updating the values of fields and cannot be handled by the declarative mutation directives. For such situations, the `UseMutationConfig` accepts an `updater` function which gives you full control over how to update the store. + +This is discussed in more detail in the section on [Imperatively modifying store data](../imperatively-modifying-store-data/). + +## Optimistic updates + +Oftentimes, we don't want to wait for the server to respond before we respond to the user interaction. For example, if a user clicks the "Like" button, we would like to instantly show the affected comment, post, etc. has been liked by the user. + +More generally, in these cases, we want to immediately update the data in our store optimistically, i.e. under the assumption that the mutation will complete successfully. If the mutation ends up not succeeding, we would like to roll back that optimistic update. + +### Optimistic response + +In order to enable this, the `UseMutationConfig` can include an `optimisticResponse` field. + +For this field to be Flow-typed, the call to `useMutation` must be passed a Flow type parameter **and** the mutation must be decorated with a `@raw_response_type` directive. + +In the previous example, we might provide the following optimistic response: + +```js +{ + feedback_like: { + feedback: { + // Even though the id field is not explicitly selected, the + // compiler selected it for us + id: feedbackId, + viewer_does_like: true, + }, + }, +} +``` + +Now, when we call `commitMutation`, this data will be immediately written into the store. The item in the store with the matching id will be updated with a new value of `viewer_does_like`. Any components which have selected this field will be re-rendered. + +When the mutation succeeds or errors, the optimistic response will be rolled back. + +Updating the `like_count` field takes a bit more work. In order to update it, we should also read the **current like count** in the component. + +```js +import type {FeedbackLikeData, LikeButtonMutation} from 'LikeButtonMutation.graphql'; +import type {LikeButton_feedback$fragmentType} from 'LikeButton_feedback.graphql'; + +const {useMutation, graphql} = require('react-relay'); + +function LikeButton({ + feedback: LikeButton_feedback$fragmentType, +}) { + const data = useFragment( + graphql` + fragment LikeButton_feedback on Feedback { + __id + viewer_does_like @required(action: THROW) + like_count @required(action: THROW) + } + `, + feedback + ); + + const [commitMutation, isMutationInFlight] = useMutation( + graphql` + mutation LikeButtonMutation($input: FeedbackLikeData!) + @raw_response_type { + feedback_like(data: $input) { + feedback { + viewer_does_like + like_count + } + } + } + ` + ); + + const changeToLikeCount = data.viewer_does_like ? -1 : 1; + return +} +``` + +:::caution + +You should be careful, and consider using [optimistic updaters](../imperatively-modifying-store-data/#example) if the value of the optimistic response depends on the value of the store and if there can be multiple optimistic responses affecting that store value. + +For example, if **two** optimistic responses each increase the like count by one, and the **first** optimistic updater is rolled back, the second optimistic update will still be applied, and the like count in the store will remain increased by two. + +::: + +:::caution + +Optimistic responses contain **many pitfalls!** + +* An optimistic response can contain the data for the full query response, i.e. including the content of fragment spreads. This means that if a developer selects more fields in components whose fragments are spread in an optimistic response, these components may have inconsistent or partial data during an optimistic update. +* Because the type of the optimistic update includes the contents of all recursively nested fragments, it can be very large. Adding `@raw_response_type` to certain mutations can degrade the performance of the Relay compiler. + +::: + +### Optimistic updaters + +Optimistic responses aren't enough for every case. For example, we may want to optimistically update data that we aren't selecting in the mutation. Or, we may want to add or remove items from a connection (and the declarative mutation directives are insufficient for our use case.) + +For situations like these, the `UseMutationConfig` can contain an `optimisticUpdater` field, which allows developers to imperatively and optimistically update the data in the store. This is discussed in more detail in the section on [Imperatively updating store data](../imperatively-modifying-store-data/). + +## Order of execution of updater functions + +In general, execution of the `updater` and optimistic updates will occur in the following order: + +* If an `optimisticResponse` is provided, that data will be written into the store. +* If an `optimisticUpdater` is provided, Relay will execute it and update the store accordingly. +* If an `optimisticResponse` was provided, the declarative mutation directives present in the mutation will be processed on the optimistic response. +* If the mutation request succeeds: + * Any optimistic update that was applied will be rolled back. + * Relay will write the server response to the store. + * If an `updater` was provided, Relay will execute it and update the store accordingly. The server payload will be available to the `updater` as a root field in the store. + * Relay will process any declarative mutation directives using the server response. + * The `onCompleted` callback will be called. +* If the mutation request fails: + * Any optimistic update was applied will be rolled back. + * The `onError` callback will be called. + +## Invalidating data during a mutation + +The recommended approach when executing a mutation is to request *all* the relevant data that was affected by the mutation back from the server (as part of the mutation body), so that our local Relay store is consistent with the state of the server. + +However, often times it can be unfeasible to know and specify all the possible data the possible data that would be affected for mutations that have large rippling effects (e.g. imagine "blocking a user" or "leaving a group"). + +For these types of mutations, it's often more straightforward to explicitly mark some data as stale (or the whole store), so that Relay knows to refetch it the next time it is rendered. In order to do so, you can use the data invalidation APIs documented in our [Staleness of Data section](../../reusing-cached-data/staleness-of-data/). + + + +## Handling errors + +GraphQL errors can largely be differentiated as: + +1. Operation (query/mutation/subscription) level errors, and +2. Field level errors + +### Surfacing mutation level errors + +If you're surfacing an error in the mutation (eg the server rejects the entire mutation because it's invalid), as long as the error returned is considered a [`CRITICAL`](https://www.internalfb.com/code/www/[b5a08782893a]/flib/graphql/experimental/core/error/GraphQL2ErrorSeverity.php?lines=11) error, you can make use of the `onError` callback from `useMutation` to handle that error in whatever way you see fit for your use case. + +If you control the server resolver, the question you should ask is whether or not throwing a CRITICAL error is the correct behavior for the client. Note though that throwing a CRITICAL error means that Relay will no longer process the interaction, which may not always be what you want if you can still partially update your UI. For example, it's possible that the mutation errored, but still wrote some data to the database, in which case you might still want Relay to process the updated fields. + +In the non-CRITICAL case the mutation may have failed, but some data was successfully returned in the case of partial data and/or the error response if encoded in the schema. Relay will still process this data, update its store, as well as components relying on that data. That is not true for the case where you've returned a CRITICAL error. + +### Surfacing field level errors +Field level errors from the server are generally recommended to be at the [`ERROR`](https://www.internalfb.com/code/www/[9120ab8aa8a5]/flib/graphql/experimental/core/error/GraphQL2ErrorSeverity.php?lines=17) level, because your UI should still be able to process the other fields that were successfully returned. If you want to explicitly handle the field level error, then we still recommend [modeling that](../../rendering/error-states/#accessing-errors-in-graphql-responses) in your schema. + + + + + diff --git a/website/versioned_docs/version-v19.0.0/guided-tour/updating-data/graphql-subscriptions.md b/website/versioned_docs/version-v19.0.0/guided-tour/updating-data/graphql-subscriptions.md new file mode 100644 index 0000000000000..e042b3cbde0c7 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/guided-tour/updating-data/graphql-subscriptions.md @@ -0,0 +1,279 @@ +--- +id: graphql-subscriptions +title: GraphQL subscriptions +slug: /guided-tour/updating-data/graphql-subscriptions/ +description: Relay guide to GraphQL subscriptions +keywords: +- subscription +--- + +import DocsRating from '@site/src/core/DocsRating'; +import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; + + + +[GraphQL subscriptions](https://our.internmc.facebook.com/intern/wiki/GraphQL_Subscriptions/) are a mechanism to allow clients to query for data in response to a stream of server-side events. + + + + + +GraphQL subscriptions are a mechanism to allow clients to query for data in response to a stream of server-side events. + + + +A GraphQL subscription looks very similar to a query, except that it uses the `subscription` keyword: + +```graphql +subscription FeedbackLikeSubscription($input: FeedbackLikeSubscribeData!) { + feedback_like_subscribe(data: $input) { + feedback { + like_count + } + } +} +``` + +* Establishing a subscription using this GraphQL snippet will cause the application to be notified whenever an event is emitted from the `feedback_like_subscribe` stream. +* `feedback_like_subscribe` is a *subscription root field* (or just *subscription field*), which sets up the subscription on the backend. + + + +:::info +You can view subscription root fields in the GraphQL Schema Explorer by opening VSCode @ FB and executing the command "Relay: Open GraphQL Schema Explorer". Then, in the "Schema Explorer Tab", click on "Subscription". + +You can click on the various mutation fields to see their parameters, descriptions and exposed fields. +::: + + + +* Like mutations, a subscription is handled in two separate steps. First, a server-side event occurs. Then, the query is executed. + +:::note +Note that the event stream can be completely arbitrary, and can have no relation to the fields selected. In other words, there is no guarantee that the values selected in a subscription will have changed from notification to notification. +::: + +* `feedback_like_subscribe` returns a specific GraphQL type which exposes the data we can query in response to the server-side event. In this case, we're querying for the Feedback object and its updated `like_count`. This allows us to show the like count in real time. + +An example of a subscription payload received by the client could look like this: + +```json +{ + "feedback_like_subscribe": { + "feedback": { + "id": "feedback-id", + "like_count": 321, + } + } +} +``` + +In Relay, we can declare GraphQL subscriptions using the `graphql` tag too: + +```js +const {graphql} = require('react-relay'); + +const feedbackLikeSubscription = graphql` + subscription FeedbackLikeSubscription($input: FeedbackLikeSubscribeData!) { + feedback_like_subscribe(data: $input) { + feedback { + like_count + } + } + } +`; +``` + +* Note that subscriptions can also reference GraphQL [variables](../../rendering/variables/) in the same way queries or fragments do. + +## Using `useSubscription` to create a subscription + +In order to create a subscription in Relay, we can use the `useSubscription` and `requestSubscription` APIs. Let's take a look at an example using the `useSubscription` API: + +```js +import type {Environment} from 'react-relay'; +import type {FeedbackLikeSubscribeData} from 'FeedbackLikeSubscription.graphql'; + +const {graphql, useSubscription} = require('react-relay'); +const {useMemo} = require('React'); + +function useFeedbackSubscription( + input: FeedbackLikeSubscribeData, +) { + const config = useMemo(() => ({ + subscription: graphql` + subscription FeedbackLikeSubscription( + $input: FeedbackLikeSubscribeData! + ) { + feedback_like_subscribe(data: $input) { + feedback { + like_count + } + } + } + `, + variables: {input}, + }), [input]); + + return useSubscription(config); +} +``` + +Let's distill what's happening here. + +* `useSubscription` takes a `GraphQLSubscriptionConfig` object, which includes the following fields: + * `subscription`: the GraphQL literal containing the subscription, and + * `variables`: the variables with which to establish the subscription. +* In addition, `useSubscription` accepts a Flow type parameter. As with queries, the Flow type of the subscription is exported from the file that the Relay compiler generates. + * If this type is provided, the `GraphQLSubscriptionConfig` becomes statically typed as well. **It is a best practice to always provide this type.** +* Now, when the `useFeedbackSubscription` hook commits, Relay will establish a subscription. + * Unlike with APIs like `useLazyLoadQuery`, Relay will **not** attempt to establish this subscription during the render phase. +* Once it is established, whenever an event occurs, the backend will select the updated Feedback object and select the `like_count` fields off of it. + * Since the `Feedback` type contains an `id` field, the Relay compiler will automatically add a selection for the `id` field. +* When the subscription response is received, Relay will find a feedback object in the store with a matching `id` and update it with the newly received `like_count` value. +* If these values have changed as a result, any components which selected these fields off of the feedback object will be re-rendered. Or, to put it colloquially, any component which depends on the updated data will re-render. + +:::note +The name of the type of the parameter `FeedbackLikeSubscribeData` is derived from the name of the top-level mutation field, i.e. from `feedback_like_subscribe`. This type is also exported from the generated `graphql.js` file. +::: + +:::caution + +The `GraphQLSubscriptionConfig` object passed to `useSubscription` should be memoized! Otherwise, `useSubscription` will dispose the subscription and re-establish it with every render! + +::: + +## Refreshing components in response to subscription events + +In the previous example, we manually selected `like_count`. Components that select this field will be re-rendered, should we receive an updated value. + +However, it is generally better to spread fragments that correspond to the components that we want to refresh in response to the mutation. This is because the data selected by components can change. + +Requiring developers to know about all subscriptions that might fetch their components data (and keeping them up-to-date) is an example of the kind of global reasoning that Relay wants to avoid requiring. + +For example, we might rewrite the subscription as follows: + +```graphql +subscription FeedbackLikeSubscription($input: FeedbackLikeSubscribeData!) { + feedback_like_subscribe(data: $input) { + feedback { + ...FeedbackDisplay_feedback + ...FeedbackDetail_feedback + } + } +} +``` + +Now, whenever a event in the `feedback_like_subscribe` event stream occurred, the data selected by the `FeedbackDisplay` and `FeedbackDetail` components will be refetched, and those components will remain in a consistent state. + +:::note +Spreading fragments is generally preferable to refetching the data in response to subscription events, since the updated data can be fetched in a single round trip. +::: + +## Executing a callback when the subscription fires, errors or is closed by the server + +In addition to writing updated data to the Relay store, we may want to execute a callback whenever a subscription payload is received. We may want to execute a callback if an error is received or if an error is received or if the server ends the subscription. The `GraphQLSubscriptionConfig` can include the following fields to handle such cases: + +* `onNext`, a callback that is executed when a subscription payload is received. It is passed the subscription response (stopping at fragment spread boundaries). +* `onError`, a callback that is executed when the subscription errors. It is passed the error that occurred. +* `onCompleted`, a callback that is executed when the server ends the subscription. + +## Declarative mutation directives + +[Declarative mutation directives](../../list-data/updating-connections/#using-declarative-directives) and [`@deleteRecord`](../graphql-mutations/#deleting-items-in-response-to-mutations) work in subscriptions, too. + +### Manipulating connections in response to subscription events + +Relay makes it easy to respond to subscription events by adding items to or removing items from connections (i.e. lists). For example, you might want to append a newly created user to a given connection. For more, see [Using declarative directives](../../list-data/updating-connections/#using-declarative-directives). + +### Deleting items in response to mutations + +In addition, you might want to delete an item from the store in response to a mutation. In order to do this, you would add the `@deleteRecord` directive to the deleted ID. For example: + +```graphql +subscription DeletePostSubscription($input: DeletePostSubscribeData!) { + delete_post_subscribe(data: $input) { + deleted_post { + id @deleteRecord + } + } +} +``` + +## Imperatively modifying local data + +At times, the updates you wish to perform are more complex than just updating the values of fields and cannot be handled by the declarative mutation directives. For such situations, the `GraphQLSubscriptionConfig` accepts an `updater` function which gives you full control over how to update the store. + +This is discussed in more detail in the section on [Imperatively updating store data](../imperatively-modifying-store-data/). + +## Configuring the Network Layer + + + +You will need to Configure your [Network layer](../../../guides/network-layer) to handle subscriptions. + +Usually GraphQL subscriptions are communicated over [WebSockets](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API), here's an example using [graphql-ws](https://github.com/enisdenjo/graphql-ws): + +```javascript +import { + ... + Network, + Observable +} from 'relay-runtime'; +import { createClient } from 'graphql-ws'; + +const wsClient = createClient({ + url:'ws://localhost:3000', +}); + +const subscribe = (operation, variables) => { + return Observable.create((sink) => { + return wsClient.subscribe( + { + operationName: operation.name, + query: operation.text, + variables, + }, + sink, + ); + }); +} + +const network = Network.create(fetchQuery, subscribe); +``` + +Alternatively, the legacy [subscriptions-transport-ws](https://github.com/apollographql/subscriptions-transport-ws) library can be used too: + +```javascript +import { + ... + Network, + Observable +} from 'relay-runtime'; +import { SubscriptionClient } from 'subscriptions-transport-ws'; + +const subscriptionClient = new SubscriptionClient('ws://localhost:3000', { + reconnect: true, +}); + +const subscribe = (request, variables) => { + const subscribeObservable = subscriptionClient.request({ + query: request.text, + operationName: request.name, + variables, + }); + // Important: Convert subscriptions-transport-ws observable type to Relay's + return Observable.from(subscribeObservable); +}; + +const network = Network.create(fetchQuery, subscribe); +``` + + + + +At Facebook, the Network Layer has already been configured to handle GraphQL Subscriptions. For more details on writing subscriptions at Facebook, check out this [guide](../../../guides/writing-subscriptions/). For a guide on setting up subscriptions on the server side, check out this [wiki](https://our.internmc.facebook.com/intern/wiki/GraphQL_Subscriptions/creating-a-new-subscription/). + + + + diff --git a/website/versioned_docs/version-v19.0.0/guided-tour/updating-data/imperatively-modifying-linked-fields.md b/website/versioned_docs/version-v19.0.0/guided-tour/updating-data/imperatively-modifying-linked-fields.md new file mode 100644 index 0000000000000..7c98894e2c030 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/guided-tour/updating-data/imperatively-modifying-linked-fields.md @@ -0,0 +1,511 @@ +--- +id: imperatively-modifying-linked-fields +title: Imperatively modifying linked fields +slug: /guided-tour/updating-data/imperatively-modifying-linked-fields/ +description: Using readUpdatableQuery to update linked fields in the store +keywords: +- record source +- store +- updater +- typesafe updaters +- readUpdatableQuery +- readUpdatableFragment +- updatable +- assignable +--- + +import DocsRating from '@site/src/core/DocsRating'; +import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; + +:::note +See also [using readUpdatableQuery to update scalar fields in the store](../imperatively-modifying-store-data). +::: + + +The examples in the [previous section](../imperatively-modifying-store-data/) showed how to use the `readUpdatableQuery` API to update scalar fields like `is_new_comment` and `is_selected`. + +The examples did **not** cover how to assign to linked fields. Let's start with an example of a component which allows the user of the application to update the Viewer's `best_friend` field. + +## Example: setting the viewer's best friend + +In order to assign a viewer's best friend, that viewer must have such a field. It may be defined by the server schema, or it may be defined locally in a schema extension as follows: + +```graphql +extend type Viewer { + best_friend: User, +} +``` + +Next, let's define a fragment and give it the `@assignable` directive, making it an **assignable fragment**. Assignable fragments can only contain a single field, `__typename`. This fragment will be on the `User` type, which is the type of the `best_friend` field. + +```js +// AssignBestFriendButton.react.js +graphql` + fragment AssignBestFriendButton_assignable_user on User @assignable { + __typename + } +`; +``` + +The fragment must be spread at both the source (i.e. on the viewer's new best friend), and at the destination (within the viewer's `best_friend` field in the updatable query). + +Lets define a component with a fragment where we spread `AssignableBestFriendButton_assignable_user`. This user will be the viewer's new best friend. + +```js +// AssignBestFriendButton.react.js +import type {AssignBestFriendButton_user$key} from 'AssignBestFriendButton_user.graphql'; + +const {useFragment} = require('react-relay'); + +export default function AssignBestFriendButton({ + someTypeRef: AssignBestFriendButton_user$key, +}) { + const data = useFragment(graphql` + fragment AssignBestFriendButton_someType on SomeType { + user { + name + ...AssignableBestFriendButton_assignable_user + } + } + `, someTypeRef); + + // We will replace this stub with the real thing below. + const onClick = () => {}; + + return (); +} +``` + +That's great! Now, we have a component that renders a button. Let's fill out that button's click handler by using the `commitLocalUpdate` and `readUpdatableQuery` APIs to assign `viewer.best_friend`. + +* In order to make it valid to assign `data.user` to `best_friend`, we must **also** spread `AssignBestFriendButton_assignable_user` under the `best_friend` field in the viewer in the updatable query or fragment. + +```js +import type {RecordSourceSelectorProxy} from 'react-relay'; + +const {commitLocalUpdate, useRelayEnvironment} = require('react-relay'); + +// ... + +const environment = useRelayEnvironment(); +const onClick = () => { + const updatableData = commitLocalUpdate( + environment, + (store: RecordSourceSelectorProxy) => { + const {updatableData} = store.readUpdatableQuery( + graphql` + query AssignBestFriendButtonUpdatableQuery + @updatable { + viewer { + best_friend { + ...AssignableBestFriendButton_assignable_user + } + } + } + `, + {} + ); + + if (data.user != null && updatableData.viewer != null) { + updatableData.viewer.best_friend = data.user; + } + } + ); +}; +``` + +### Putting it all together + +The full example is as follows: + +```graphql +extend type Viewer { + best_friend: User, +} +``` + +```js +// AssignBestFriendButton.react.js +import type {AssignBestFriendButton_user$key} from 'AssignBestFriendButton_user.graphql'; +import type {RecordSourceSelectorProxy} from 'react-relay'; + +const {commitLocalUpdate, useFragment, useRelayEnvironment} = require('react-relay'); + +graphql` + fragment AssignBestFriendButton_assignable_user on User @assignable { + __typename + } +`; + +export default function AssignBestFriendButton({ + someTypeRef: AssignBestFriendButton_someType$key, +}) { + const data = useFragment(graphql` + fragment AssignBestFriendButton_someType on SomeType { + user { + name + ...AssignableBestFriendButton_assignable_user + } + } + `, someTypeRef); + + const environment = useRelayEnvironment(); + const onClick = () => { + const updatableData = commitLocalUpdate( + environment, + (store: RecordSourceSelectorProxy) => { + const {updatableData} = store.readUpdatableQuery( + graphql` + query AssignBestFriendButtonUpdatableQuery + @updatable { + viewer { + best_friend { + ...AssignableBestFriendButton_assignable_user + } + } + } + `, + {} + ); + + if (data.user != null && updatableData.viewer != null) { + updatableData.viewer.best_friend = data.user; + } + } + ); + }; + + return (); +} +``` + +Let's recap what is happening here. + +* We are writing a component in which clicking a button results in a user is being assigned to `viewer.best_friend`. After this button is clicked, all components which were previously reading the `viewer.best_friend` field will be re-rendered, if necessary. +* The source of the assignment is a user where an **assignable fragment** is spread. +* The target of the assignment is accessed using the `commitLocalUpdate` and `readUpdatableQuery` APIs. +* The query passed to `readUpdatableQuery` must include the `@updatable` directive. +* The target field must have that same **assignable fragment** spread. +* We are checking whether `data.user` is not null before assigning. This isn't strictly necessary. However, if we assign `updatableData.viewer.best_friend = null`, we will be nulling out the linked field in the store! This is (probably) not what you want. + +## Pitfalls + +* Note that there are no guarantees about what fields are present on the assigned user. This means that any consumes an updated field has no guarantee that the required fields were fetched and are present on the assigned object. + + + +:::note + +It is technically feasible to add fields to the assignable fragment, which would have the effect of guaranteeing that certain fields are present in the assigned object. + +If this is a need, please reach out to [Relay Support](https://fb.workplace.com/groups/relay.support). + +::: + + + +## Example: Assigning to a list + +Let's modify the previous example to append the user to a list of best friends. In this example, the following principle is relevant: + +> Every assigned linked field (i.e. the right hand side of the assignment) **must originate in a read-only fragment, query, mutation or subscription**. + +This means that `updatableData.foo = updatableData.foo` is invalid. For the same reason, `updatableData.viewer.best_friends = updatableData.viewer.best_friends.concat([newBestFriend])` is invalid. To work around this restriction, we must select the existing best friends from a read-only fragment, and perform the assignment as follows: `viewer.best_friends = existing_list.concat([newBestFriend])`. + +Consider the following full example: + +```graphql +extend type Viewer { + # We are now defined a "best_friends" field instead of a "best_friend" field + best_friends: [User!], +} +``` + +```js +// AssignBestFriendButton.react.js +import type {AssignBestFriendButton_user$key} from 'AssignBestFriendButton_user.graphql'; +import type {AssignBestFriendButton_viewer$key} from 'AssignBestFriendButton_viewer'; + +import type {RecordSourceSelectorProxy} from 'react-relay'; + +const {commitLocalUpdate, useFragment, useRelayEnvironment} = require('react-relay'); + +graphql` + fragment AssignBestFriendButton_assignable_user on User @assignable { + __typename + } +`; + +export default function AssignBestFriendButton({ + someTypeRef: AssignBestFriendButton_someType$key, + viewerFragmentRef: AssignBestFriendButton_viewer$key, +}) { + const data = useFragment(graphql` + fragment AssignBestFriendButton_someType on SomeType { + user { + name + ...AssignableBestFriendButton_assignable_user + } + } + `, someTypeRef); + + const viewer = useFragment(graphql` + fragment AssignBestFriendButton_viewer on Viewer { + best_friends { + # since viewer.best_friends appears in the right hand side of the assignment + # (i.e. updatableData.viewer.best_friends = viewer.best_friends.concat(...)), + # the best_friends field must contain the correct assignable fragment spread + ...AssignableBestFriendButton_assignable_user + } + } + `, viewerRef); + + const environment = useRelayEnvironment(); + const onClick = () => { + commitLocalUpdate( + environment, + (store: RecordSourceSelectorProxy) => { + const {updatableData} = store.readUpdatableQuery( + graphql` + query AssignBestFriendButtonUpdatableQuery + @updatable { + viewer { + best_friends { + ...AssignableBestFriendButton_assignable_user + } + } + } + `, + {} + ); + + if (data.user != null && updatableData.viewer != null && viewer.best_friends != null) { + updatableData.viewer.best_friends = [ + ...viewer.best_friends, + data.user, + ]; + } + } + ); + }; + + return (); +} +``` + +## Example: assigning from an abstract field to a concrete field + +If you are assigning from an abstract field, e.g. a `Node` to a `User` (which implements `Node`), you must use an inline fragment to refine the `Node` type to `User`. Consider this snippet: + +```js +const data = useFragment(graphql` + fragment AssignBestFriendButton_someType on Query { + node(id: "4") { + ... on User { + __typename + ...AssignableBestFriendButton_assignable_user + } + } + } +`, queryRef); + +const environment = useRelayEnvironment(); +const onClick = () => { + const updatableData = commitLocalUpdate( + environment, + (store: RecordSourceSelectorProxy) => { + const {updatableData} = store.readUpdatableQuery( + graphql` + query AssignBestFriendButtonUpdatableQuery + @updatable { + viewer { + best_friend { + ...AssignableBestFriendButton_assignable_user + } + } + } + `, + {} + ); + + if (data.node != null && data.node.__typename === "User" && updatableData.viewer != null) { + updatableData.viewer.best_friend = data.node; + } + } + ); +}; +``` + +In this snippet, we do two things: + +* We use an inline fragment to refine the `Node` type to the `User` type. Inside of this refinement, we spread the assignable fragment. +* We check that `data.node.__typename === "User"`. This indicates to Flow that within that if block, `data.node` is known to be a user, and therefore `updatableData.viewer.best_friend = data.node` can typecheck. + +## Example: assigning to an interface when the source is guaranteed to implement that interface + +You may wish to assign to a destination field that has an interface type (in this example, `Actor`). If the source field is guaranteed to implement that interface, then assignment is straightforward. + +For example, the source might have the same interface type or have a concrete type (`User`, in this example) that implements that interface. + +Consider the following snippet: + +```js +graphql` + fragment Foo_actor on Actor @assignable { + __typename + } +`; + +const data = useFragment(graphql` + fragment Foo_query on Query { + user { + ...Foo_actor + } + viewer { + actor { + ...Foo_actor + } + } + } +`, queryRef); + +const environment = useRelayEnvironment(); +const onClick = () => { + commitLocalUpdate(environment, store => { + const {updatableData} = store.readUpdatableQuery( + graphql` + query FooUpdatableQuery @updatable { + viewer { + actor { + ...Foo_actor + } + } + } + `, + {} + ); + + // Assigning the user works as you would expect + if (updatableData.viewer != null && data.user != null) { + updatableData.viewer = data.user; + } + + // As does assigning the viewer + if (updatableData.viewer != null && data.viewer?.actor != null) { + updatableData.viewer = data.viewer.actor; + } + }); +}; +``` + +## Example: assigning to an interface when the source is **not** guaranteed to implement that interface + +You may wish to assign to a destination field that has an interface type (in this example, `Actor`). If the source type (e.g. `Node`) is **not** known to implement that interface, then an extra step is involved: validation. + + + +:::note + +With additional changes to Relay's type generation, this can be made simpler. Please reach out to [Robert Balicki](https://www.internalfb.com/profile/view/1238951) if this is a pain point for you. + +::: + + + +In order to understand why, some background is necessary. The flow type for the setter for an interface field might look like: + +```js +set actor(value: ?{ + +__id: string, + +__isFoo_actor: string, + +$fragmentSpreads: Foo_actor$fragmentType, + ... +}): void, +``` + +The important thing to note is that the setter expects an object with a non-null `__isFoo_actor` field. + +When an assignable fragment with an abstract type is spread in a regular fragment, it results in an `__isFoo_actor: string` selection that is not optional if the type is known to implement the interface, and optional otherwise. + +Since a `Node` is **not** guaranteed to implement `Actor`, when the Relay compiler encounters the selection `node(id: "4") { ...Foo_actor }`, it will emit an optional field (`__isFoo_actor?: string`). Attempting to assign this to `updatableData.viewer.actor` will not typecheck! + +### Introducing validators + +The generated file for every generated artifact includes a named `validator` export. In our example, the function is as follows: + +```js +function validate(value/*: { + +__id: string, + +__isFoo_actor?: string, + +$fragmentSpreads: Foo_actor$fragmentType, + ... +}*/)/*: false | { + +__id: string, + +__isFoo_actor: string, + +$fragmentSpreads: Foo_actor$fragmentType, + ... +}*/ { + return value.__isFoo_actor != null ? (value/*: any*/) : false; +} +``` + +In other words, this function checks for the presence of the `__isFoo_actor` field. If it is found, it returns the same object, but with a flow type that is valid for assignment. If not, it returns false. + +### Example + +Let's put this all together in an example: + +```js +import {validate as validateActor} from 'Foo_actor.graphql'; + +graphql` + fragment Foo_actor on Actor @assignable { + __typename + } +`; + +const data = useFragment(graphql` + fragment Foo_query on Query { + node(id: "4") { + ...Foo_actor + } + } +`, queryRef); + +const environment = useRelayEnvironment(); +const onClick = () => { + commitLocalUpdate(environment, store => { + const {updatableData} = store.readUpdatableQuery( + graphql` + query FooUpdatableQuery @updatable { + viewer { + actor { + ...Foo_actor + } + } + } + `, + {} + ); + + if (updatableData.viewer != null && data.node != null) { + const validActor = validateActor(data.node); + if (validActor !== false) { + updatableData.viewer.actor = validActor; + } + } + }); +}; +``` + +### Can flow be used to infer the presence of this field? + +Unfortunately, if you check for the presence of `__isFoo_actor`, Flow does not infer that (on the type level), the field is not optional. Hence, we need to use validators. + + diff --git a/website/versioned_docs/version-v19.0.0/guided-tour/updating-data/imperatively-modifying-store-data-legacy.md b/website/versioned_docs/version-v19.0.0/guided-tour/updating-data/imperatively-modifying-store-data-legacy.md new file mode 100644 index 0000000000000..4ebb8049ba31c --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/guided-tour/updating-data/imperatively-modifying-store-data-legacy.md @@ -0,0 +1,142 @@ +--- +id: imperatively-modifying-store-data-unsafe +title: Imperatively modifying store data (unsafe) +slug: /guided-tour/updating-data/imperatively-modifying-store-data-unsafe/ +description: Imperatively modifying store data +keywords: +- record source +- store +- updater +--- + +import DocsRating from '@site/src/core/DocsRating'; +import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; + +Data in Relay stores can be imperatively modified within updater functions. + +## When to use updaters + +### Complex client updates + +You might provide an updater function if the changes to local data are more complex than what can be achieved by simply writing a network response to the store and cannot be handled by the declarative mutation directives. + +### Client schema extensions + +In addition, since the network response necessarily will not include data for fields defined in client schema extensions, you may wish to use an updater to initialize data defined in client schema extensions. + +### Use of other APIs + +Lastly, there are things you can only achieve using updaters, such as invalidating nodes, deleting nodes, finding all connections at a given field, etc. + +### If multiple optimistic responses modify a given store value + +If two optimistic responses affect a given value, and the first optimistic response is rolled back, the second one will remain applied. + +For example, if two optimistic responses each increase a story's like count by one, and the first optimistic response is rolled back, the second optimistic response remains applied. Since the second optimistic response **not recalculated**, the value of the like count will remain increased by two. + +An optimistic updater, on the other hand, would be re-run in this circumstance. + +## When **not** to use updaters + +### To trigger other side effects + +You should use the `onCompleted` callback to trigger other side effects. + +## The various types of updater functions + +The `useMutation` and `commitMutation` APIs accept configuration objects which can include `optimisticUpdater` and `updater` fields. The `requestSubscription` and `useSubscription` APIs accept configuration objects which can include `updater` fields. + +In addition, there is another API (`commitLocalUpdate`) which also accepts an updater function. It will be discussed in the [Other APIs for modifying local data](../local-data-updates/) section. + +## Optimistic updaters vs updaters + +Mutations can have both optimistic and regular updaters. Optimistic updaters are executed when a mutation is triggered. When that mutation completes or errors, the optimistic update is rolled back. At that point, the mutation response is written to the store and regular updaters are executed. See [order of execution of updater functions](../graphql-mutations/#order-of-execution-of-updater-functions). + +Regular updaters are executed when a mutation completes successfully. + +## Example + +Let's consider an example that provides an updater to `commitMutation`. + +```js +import type {Environment} from 'react-relay'; +import type {CommentCreateData, CreateCommentMutation} from 'CreateCommentMutation.graphql'; + +const {commitMutation, graphql} = require('react-relay'); +const {ConnectionHandler} = require('relay-runtime'); + +function commitCommentCreateMutation( + environment: Environment, + feedbackID: string, + input: CommentCreateData, +) { + return commitMutation(environment, { + mutation: graphql` + mutation CreateCommentMutation($input: CommentCreateData!) { + comment_create(input: $input) { + comment_edge { + cursor + node { + body { + text + } + } + } + } + } + `, + variables: {input}, + updater: (store: RecordSourceSelectorProxy, _response: ?CreateCommentMutation$data) => { + // we are not using _response in this example, but it is + // provided and statically typed. + + const feedbackRecord = store.get(feedbackID); + + // Get connection record + const connectionRecord = ConnectionHandler.getConnection( + feedbackRecord, + 'CommentsComponent_comments_connection', + ); + + // Get the payload returned from the server + const payload = store.getRootField('comment_create'); + + // Get the edge inside the payload + const serverEdge = payload.getLinkedRecord('comment_edge'); + + // Build edge for adding to the connection + const newEdge = ConnectionHandler.buildConnectionEdge( + store, + connectionRecord, + serverEdge, + ); + + // Add edge to the end of the connection + ConnectionHandler.insertEdgeAfter( + connectionRecord, + newEdge, + ); + }, + }); +} + +module.exports = {commit: commitCommentCreateMutation}; +``` + +Let's distill this example: + +* The updater receives a `store` argument, which is an instance of a [`RecordSourceSelectorProxy`](../../../api-reference/store/); this interface allows you to *imperatively* write and read data directly to and from the Relay store. This means that you have full control over how to update the store in response to the mutation response: you can *create entirely new records*, or *update or delete existing ones*. +* The updater receives a second `data` argument, which contains the data selected by the mutation fragment. This can be used to retrieve the payload data without interacting with the *`store`*. The type of this mutation response can be imported from the auto-generated `Mutation.graphql.js` file, and is given the name `MutationName$data`. + * The type of this `data` argument is a nullable version of the `$data` type. + * The `data` arguments contains just the data selected directly by the mutation argument. In other words, if another fragment is spread in the mutation, the data from that fragment will not be available within `data` by default. +* In our specific example, we're adding a new comment to our local store after it has successfully been added on the server. Specifically, we're adding a new item to a connection; for more details on the specifics of how that works, check out our section on [adding and removing items from a connection](../../list-data/updating-connections/). + * There is no need for an updater in this example — it would be a great place to use the `@appendEdge` directive instead! +* Note that the mutation response is a *root field* record that can be read from the `store` using the `store.getRootField` API. In our case, we're reading the `comment_create` root field, which is a root field in the mutation response. +* Note that the `root` field of the mutation is different from the `root` of queries, and `store.getRootField` in the mutation updater can only get the record from the mutation response. To get records from the root that's not in the mutation response, use `store.getRoot().getLinkedRecord` instead. +* Once the updater completes, any local data updates caused by the mutation `updater` will automatically cause components subscribed to the data to be notified of the change and re-render. + +## Learn more + +See the full APIs [here](../../../api-reference/store/). + + diff --git a/website/versioned_docs/version-v19.0.0/guided-tour/updating-data/imperatively-modifying-store-data.md b/website/versioned_docs/version-v19.0.0/guided-tour/updating-data/imperatively-modifying-store-data.md new file mode 100644 index 0000000000000..cb81f0dd4b5d5 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/guided-tour/updating-data/imperatively-modifying-store-data.md @@ -0,0 +1,275 @@ +--- +id: imperatively-modifying-store-data +title: Imperatively modifying store data +slug: /guided-tour/updating-data/imperatively-modifying-store-data/ +description: Using readUpdatableQuery to update scalar fields in the store +keywords: +- record source +- store +- updater +- typesafe updaters +- readUpdatableQuery +- readUpdatableFragment +- updatable +--- + +import DocsRating from '@site/src/core/DocsRating'; +import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; + +:::note +See also [this guide on updating linked fields in the store](../imperatively-modifying-linked-fields). +::: + +Data in Relay stores can be imperatively modified within updater functions. + +## When to use updaters + +### Complex client updates + +You might provide an updater function if the changes to local data are more complex than what can be achieved by simply writing a network response to the store and cannot be handled by the declarative mutation directives. + +### Client schema extensions + +In addition, since the network response necessarily will not include data for fields defined in client schema extensions, you may wish to use an updater to initialize data defined in client schema extensions. + +### Use of other APIs + +Lastly, there are things you can only achieve using updaters, such as invalidating nodes, deleting nodes, finding all connections at a given field, etc. + +### If multiple optimistic responses modify a given store value + +If two optimistic responses affect a given value, and the first optimistic response is rolled back, the second one will remain applied. + +For example, if two optimistic responses each increase a story's like count by one, and the first optimistic response is rolled back, the second optimistic response remains applied. However, it is **not recalculated**, and the value of the like count will remain increased by two. + +## When **not** to use updaters + +### To trigger other side effects + +You should use the `onCompleted` callback to trigger other side effects. `onCompleted` callbacks are guaranteed to be called once, but updaters and optimistic updaters can be called repeatedly. + +## The various types of updater functions + +The `useMutation` and `commitMutation` APIs accept configuration objects which can include `optimisticUpdater` and `updater` fields. The `requestSubscription` and `useSubscription` APIs accept configuration objects which can include `updater` fields. + +In addition, there is another API (`commitLocalUpdate`) which also accepts an updater function. It will be discussed in the [Other APIs for modifying local data](../local-data-updates/) section. + +## Optimistic updaters vs updaters + +Mutations can have both optimistic and regular updaters. Optimistic updaters are executed when a mutation is triggered. When that mutation completes or errors, the optimistic update is rolled back. + +Regular updaters are executed when a mutation completes successfully. + +## Example + +Let's construct an example in which an `is_new_comment` field (which is defined in a schema extension) is set to `true` on a newly created Feedback object in a mutation updater. + +```graphql +# Feedback.graphql +extend type Feedback { + is_new_comment: Boolean +} +``` + +```js +// CreateFeedback.js +import type {Environment} from 'react-relay'; +import type { + FeedbackCreateData, + CreateFeedbackMutation, + CreateFeedbackMutation$data, +} from 'CreateFeedbackMutation.graphql'; + +const {commitMutation, graphql} = require('react-relay'); +const {ConnectionHandler} = require('relay-runtime'); + +function commitCreateFeedbackMutation( + environment: Environment, + input: FeedbackCreateData, +) { + return commitMutation(environment, { + mutation: graphql` + mutation CreateFeedbackMutation($input: FeedbackCreateData!) { + feedback_create(input: $input) { + feedback { + id + # Step 1: in the mutation response, spread an updatable fragment (defined below). + # This updatable fragment will select the fields that we want to update on this + # particular feedback object. + ...CreateFeedback_updatable_feedback + } + } + } + `, + variables: {input}, + + // Step 2: define an updater + updater: (store: RecordSourceSelectorProxy, response: ?CreateCommentMutation$data) => { + // Step 3: Access and nullcheck the feedback object. + // Note that this could also have been achieved with the @required directive. + const feedbackRef = response?.feedback_create?.feedback; + if (feedbackRef == null) { + return; + } + + // Step 3: call store.readUpdatableFragment + const {updatableData} = store.readUpdatableFragment( + // Step 4: Pass it a fragment literal, where the fragment contains the @updatable directive. + // This fragment selects the fields that you wish to update on the feedback object. + // In step 1, we spread this fragment in the query response. + graphql` + fragment CreateFeedback_updatable_feedback on Feedback @updatable { + is_new_comment + } + `, + // Step 5: Pass the fragment reference. + feedbackRef + ); + + // Step 6: Mutate the updatableData object! + updatableData.is_new_comment = true; + }, + }); +} + +module.exports = {commit: commitCreateFeedbackMutation}; +``` + +Let's distill what's going on here. + +* The `updater` accepts two parameters: a `RecordSourceSelectorProxy` and an optional object that is the result of reading out the mutation response. + * The type of this second argument is a nullable version of the `$data` type that is imported from the generated mutation file. + * The second argument contains just the data selected directly by the mutation argument. In other words, it will not contain any fields selected solely by spread fragments. +* This `updater` is executed after the mutation response has been written to the store. +* In this example updater, we do three things: + * First, we spread an updatable fragment in the mutation response. + * Second, we read out the fields selected by this fragment by calling `readUpdatableFragment`. This returns an updatable proxy object. + * Third, we update fields on this updatable proxy. +* Once this updater completes, the updates that have been recorded are written to the store, and all affected components are re-rendered. + +## Example 2: Updating data in response to user interactions + +Let's consider the common case of updating store data in response to a user interaction. In a click handler, let's a toggle an `is_selected` field. This field is defined on Users in a client schema extension. + +```graphql +# User.graphql +extend type User { + is_selected: Boolean +} +``` + +```js +// UserSelectToggle.react.js +import type {RecordSourceSelectorProxy} from 'react-relay'; +import type {UserSelectToggle_viewer$key} from 'UserSelectToggle_viewer.graphql'; + +const {useRelayEnvironment, commitLocalUpdate} = require('react-relay'); + +function UserSelectToggle({ userId, viewerRef }: { + userId: string, + viewerRef: UserSelectToggle_viewer$key, +}) { + const viewer = useFragment(graphql` + fragment UserSelectToggle_viewer on Viewer { + user(user_id: $user_id) { + id + name + is_selected + ...UserSelectToggle_updatable_user + } + } + `, viewerRef); + + const environment = useRelayEnvironment(); + + return +} +``` + +Let's distill what's going on here. + +* In a click handler, we call `commitLocalUpdate`, which accepts a Relay environment and an updater function. **Unlike in the previous examples, this updater does not accept a second parameter** because there is no associated network payload. +* In this updater function, we access get an updatable proxy object by calling `store.readUpdatableFragment`, and toggle the `is_selected` field. +* Like the previous example in which we called `readUpdatableFragment`, this can be rewritten to use the `readUpdatableQuery` API. + +:::note +This example can be rewritten using the `environment.commitPayload` API, albeit without type safety. +::: + +## Alternative API: `readUpdatableQuery`. + +In the previous examples, we used an updatable fragment to access the record whose fields we want to update. This can also be possible to do with an updatable query. + +If we know the path from the root (i.e. the object whose type is `Query`) to the record we wish to modify, we can use the `readUpdatableQuery` API to achieve this. + +For example, we could set the viewer's `name` field in response to an event as follows: + +```js +// NameUpdater.react.js +function NameUpdater({ queryRef }: { + queryRef: NameUpdater_viewer$key, +}) { + const environment = useRelayEnvironment(); + const data = useFragment( + graphql` + fragment NameUpdater_viewer on Viewer { + name + } + `, + queryRef + ); + const [newName, setNewName] = useState(data?.viewer?.name); + const onSubmit = () => { + commitLocalUpdate(environment, store => { + const {updatableData} = store.readUpdatableQuery( + graphql` + query NameUpdaterUpdateQuery @updatable { + viewer { + name + } + } + `, + {} + ); + const viewer = updatableData.viewer; + if (viewer != null) { + viewer.name = newName; + } + }); + }; + + // etc +} +``` + +* This particular example can be rewritten using `readUpdatableFragment`. However, you may prefer `readUpdatableQuery` for several reasons: + * You do not have ready access to a fragment reference, e.g. if the call to `commitLocalUpdate` is not obviously associated with a component. + * You do not have ready access to a fragment where we select the **parent record** of the record we wish to modify (e.g. the `Query` in this example). Due to a known type hole in Relay, **updatable fragments cannot be spread at the top level.** + * You wish to use variables in the updatable fragment. Currently, updatable fragments reuse the variables that were passed to the query. This means that you cannot, for example, have an updatable fragment with fragment-local variables and call `readUpdatableFragment` multiple times, each time passing different variables. + + diff --git a/website/versioned_docs/version-v19.0.0/guided-tour/updating-data/introduction.md b/website/versioned_docs/version-v19.0.0/guided-tour/updating-data/introduction.md new file mode 100644 index 0000000000000..c2b1afc22c08a --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/guided-tour/updating-data/introduction.md @@ -0,0 +1,25 @@ +--- +id: introduction +title: Introduction +slug: /guided-tour/updating-data/introduction +description: Relay guide to updating data +keywords: +- updating +- mutation +- useMutation +- commitMutation +- relay store +--- + +import DocsRating from '@site/src/core/DocsRating'; +import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; + +In the [fetching data](../list-data/introduction.md) section, we discuss how to fetch data using GraphQL queries. Though fetching data can have the *incidental* effect of modifying data in Relay's local store (if the fetched data has changed), we haven't discussed any ways to *intentionally* modify our locally stored data. + +This section will do just that: it will discuss how to update our local data store, and data on the server. + +:::note +The **Relay store** is a cache of GraphQL data, associated with a given Relay environment, that we have encountered during the execution of an application. +::: + + diff --git a/website/versioned_docs/version-v19.0.0/guided-tour/updating-data/local-data-updates.md b/website/versioned_docs/version-v19.0.0/guided-tour/updating-data/local-data-updates.md new file mode 100644 index 0000000000000..90019fbdace84 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/guided-tour/updating-data/local-data-updates.md @@ -0,0 +1,71 @@ +--- +id: local-data-updates +title: Local Data Updates +slug: /guided-tour/updating-data/local-data-updates/ +description: Other APIs for modifying store data +keywords: +- client-only +- commitLocalUpdate +- commitPayload +--- + +import DocsRating from '@site/src/core/DocsRating'; +import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; +import FbLocalDataUpdatesFlow from './fb/FbLocalDataUpdatesFlow.md'; + +There are a couple of APIs that Relay provides in order to make purely local updates to the Relay store (i.e. updates not tied to a server operation). + +Note that local data updates can be made both on [client-only data](../client-only-data/), or on regular data that was fetched from the server via an operation. + +## commitLocalUpdate + +To make updates using an [`updater`](../imperatively-modifying-store-data/) function, you can use the `commitLocalUpdate` API: + +```js +import type {Environment} from 'react-relay'; + +const {commitLocalUpdate, graphql} = require('react-relay'); + +function commitCommentCreateLocally( + environment: Environment, + feedbackID: string, +) { + return commitLocalUpdate(environment, store => { + // Imperatively mutate the store here + }); +} + +module.exports = {commit: commitCommentCreateLocally}; +``` + +* `commitLocalUpdate` update simply takes an environment and an updater function. + * `updater` takes a *`store`* argument, which is an instance of a [`RecordSourceSelectorProxy`](../../../api-reference/store/); this interface allows you to *imperatively* write and read data directly to and from the Relay store. This means that you have full control over how to update the store: you can *create entirely new records*, or *update or delete existing ones*. + * Unlike regular and optimistic updaters that are accepted by the mutation and subscription APIs, the updater passed to `commitLocalUpdate` does not accept a second parameter. This is because there is no associated network response. +* Note that any local data updates will automatically cause components subscribed to the data to be notified of the change and re-render. + +## commitPayload + +`commitPayload` takes an `OperationDescriptor` and the payload for the query, and writes it to the Relay Store. The payload will be resolved like a normal server response for a query, and will also resolve Data Driven Dependencies that are passed as `JSResource`, `requireDefer`, etc. + +```js +import type {FooQueryRawResponse} from 'FooQuery.graphql' + +const {createOperationDescriptor} = require('relay-runtime'); + +const operationDescriptor = createOperationDescriptor(FooQuery, { + id: 'an-id', + otherVariable: 'value', +}); + +const payload: FooQueryRawResponse = {...}; + +environment.commitPayload(operationDescriptor, payload); +``` + +* An `OperationDescriptor` can be created by `createOperationDescriptor`; it takes the query and the query variables. +* The payload can be typed using the Flow type generated by adding the directive `@raw_response_type` to the query. +* Note that any local data updates will automatically cause components subscribed to the data to be notified of the change and re-render. + + + + diff --git a/website/versioned_docs/version-v19.0.0/guided-tour/updating-data/typesafe-updaters-faq.md b/website/versioned_docs/version-v19.0.0/guided-tour/updating-data/typesafe-updaters-faq.md new file mode 100644 index 0000000000000..3bc6a5c393460 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/guided-tour/updating-data/typesafe-updaters-faq.md @@ -0,0 +1,83 @@ +--- +id: typesafe-updaters-faq +title: Typesafe updaters FAQ +slug: /guided-tour/updating-data/typesafe-updaters-faq/ +description: Typesafe updater FAQ +keywords: +- typesafe updaters +- readUpdatableQuery +- readUpdatableFragment +- updater +- updatable +--- + +import DocsRating from '@site/src/core/DocsRating'; +import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; + +# Typesafe Updaters FAQ + + + +:::note + +Is something missing from this Q&A? Are you confused? Would you like help adopting these APIs? Please, reach out to [Robert Balicki](https://fb.workplace.com/profile.php?id=100042823931887). I am happy to help! + +::: + + + +# General + +## What is typesafe updaters? + +Typesafe updaters is the name given to a project to provide a typesafe and ergonomic alternative to the existing APIs for imperatively updating data in the Relay store. + +For example, [`readUpdatableFragment`](../../../api-reference/store/#readupdatablefragmentfragment-updatablefragmenttfragmenttype-tdatafragmentreference-hasupdatablespreadtfragmenttype-updatabledatatdata) and [`readUpdatableQuery`](../../../api-reference/store/#readupdatablequeryquery-updatablequerytvariables-tdatavariables-tvariables-updatabledatatdata) are two typesafe updaters that the store exposes. + +## Why? + +Relay provides typesafe and ergonomic APIs for fetching and managing data that originates on the server. In addition, Relay provides the ability to define local-only fields in **client schema extensions**. However, the APIs for mutating the data in these fields has hitherto been verbose and not ergonomic, meaning that we could not recommend Relay as a solution for managing local state. + +## What was wrong with the existing APIs? + +The pre-existing APIs are verbose and not typesafe. They make it easy to make a variety of mistakes and require that the developer understand a new set of APIs only when writing updaters. + +Typesafe updaters is a set of APIs that are typesafe and more ergonomic. They leverage well-known Relay idioms (queries, fragments, type refinement) and use getters and setters instead of requiring that the developer learn about a set of methods that are unused elsewhere. + +## How does a developer use typesafe updaters? + +With typesafe updaters, a developers writes an updatable query or a fragment that specifies the data to imperatively update. Then, the developer reads out that data from the store, returning a so-called **updatable proxy**. Then, the developer mutates that updatable proxy. Mutating that updatable proxy using setters (e.g. `updatableData.name = "Godzilla"`) results in calls to the old API, but with added type safety. + +## What is an updatable query or fragment? + +An updatable query or fragment is a query or fragment that has the `@updatable` directive. + +# Updatable queries and fragments are not fetched + +## Are fields selected in updatable queries and fragments fetched from the server? + +No! The server doesn't know about updatable queries and fragments. Their fields are never fetched. + +Even if you spread an updatable fragment in a regular query or fragment, the fields selected by that updatable fragment are not fetched as part of that request. + +## What if I want to fetch a field and also mutate it? + +You should select that field in both a regular query/fragment **and** in an updatable query/fragment. + +## What are some consequences of this? + +* When you read out updatable data, it can be missing if it isn't present in the store. +* You cannot spread regular fragments in updatable queries/fragments. +* The generated artifact for updatable queries/fragments does not contain a query ID and does not contain a normalization AST (which is used for writing network data to the store.) +* Directives like `@defer`, etc. do not make sense in this context, and are disallowed. + +# Misc + +## Where do I get a `store`? + +The classes `RelayRecordSourceSelectorProxy`, `RecordSourceProxy` and `RelayRecordSourceProxy` contain the methods `readUpdatableQuery` and `readUpdatableFragment`. One can acquire an instance of these classes: + +* In updaters of mutations and subscriptions +* In optimistic updaters of mutations +* When using `RelayModernEnvironment`'s `commitUpdate`, `applyUpdate`, etc. methods. +* When using the standalone `commitLocalUpdate` method. diff --git a/website/versioned_docs/version-v19.0.0/guided-tour/updating-data/updating-connections.md b/website/versioned_docs/version-v19.0.0/guided-tour/updating-data/updating-connections.md new file mode 100644 index 0000000000000..fcc0f73cfe8c1 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/guided-tour/updating-data/updating-connections.md @@ -0,0 +1,592 @@ +--- +id: updating-connections +title: Updating Connections +slug: /guided-tour/list-data/updating-connections/ +description: Relay guide to updating connections +keywords: +- pagination +- usePaginationFragment +- updating +- connection +--- + +import DocsRating from '@site/src/core/DocsRating'; +import {OssOnly, FbInternalOnly} from 'docusaurus-plugin-internaldocs-fb/internal'; + +Usually when you're rendering a connection, you'll also want to be able to add or remove items to/from the connection in response to user actions. + +Relay holds a local in-memory store of normalized GraphQL data, where records are stored by their IDs. When creating mutations, subscriptions, or local data updates with Relay, you must provide an [`updater`](../../updating-data/graphql-mutations/#updater-functions) function, inside which you can access and read records, as well as write and make updates to them. When records are updated, any components affected by the updated data will be notified and re-rendered. + + +## Connection Records + +In Relay, connection fields that are marked with the `@connection` directive are stored as special records in the store, and they hold and accumulate *all* of the items that have been fetched for the connection so far. In order to add or remove items from a connection, we need to access the connection record using the connection `key`, which was provided when declaring a `@connection`; specifically, this allows us to access a connection inside an [`updater`](../../updating-data/graphql-mutations/#updater-functions) function using the `ConnectionHandler` APIs. + +For example, given the following fragment that declares a `@connection`, we can access the connection record inside an `updater` function in a few different ways: + +```js +const {graphql} = require('react-relay'); + +const storyFragment = graphql` + fragment StoryComponent_story on Story { + comments @connection(key: "StoryComponent_story_comments_connection") { + nodes { + body { + text + } + } + } + } +`; +``` + +### Accessing connections using `__id` + +We can query for a connection's `__id` field, and then use that `__id` to access the record in the store: + +```js +const fragmentData = useFragment( + graphql` + fragment StoryComponent_story on Story { + comments @connection(key: "StoryComponent_story_comments_connection") { + # Query for the __id field + __id + + # ... + } + } + `, + props.story, +); + +// Get the connection record id +const connectionID = fragmentData?.comments?.__id; +``` + +Then use it to access the record in the store: + +```js +function updater(store: RecordSourceSelectorProxy) { + // connectionID is passed as input to the mutation/subscription + const connection = store.get(connectionID); + + // ... +} +``` + +:::note +The `__id` field is **NOT** something that your GraphQL API needs to expose. Instead, it's an identifier that Relay automatically adds to identify the connection record. +::: + +### Accessing connections using `ConnectionHandler.getConnectionID` + +If we have access to the ID of the parent record that holds the connection, we can access the connection record by using the `ConnectionHandler.getConnectionID` API: + +```js +const {ConnectionHandler} = require('relay-runtime'); + +function updater(store: RecordSourceSelectorProxy) { + // Get the connection ID + const connectionID = ConnectionHandler.getConnectionID( + storyID, // passed as input to the mutation/subscription + 'StoryComponent_story_comments_connection', + ); + + // Get the connection record + const connectionRecord = store.get(connectionID); + + // ... +} +``` + +### Accessing connections using `ConnectionHandler.getConnection` + +If we have access to the parent record that holds the connection, we can access the connection record via the parent, by using the `ConnectionHandler.getConnection` API: + +```js +const {ConnectionHandler} = require('relay-runtime'); + +function updater(store: RecordSourceSelectorProxy) { + // Get parent story record + // storyID is passed as input to the mutation/subscription + const storyRecord = store.get(storyID); + + // Get the connection record from the parent + const connectionRecord = ConnectionHandler.getConnection( + storyRecord, + 'StoryComponent_story_comments_connection', + ); + + // ... +} +``` + +## Adding edges + +There are a couple of alternatives for adding edges to a connection: + +### Using declarative directives + +Usually, mutation or subscription payloads will expose the new edges that were added on the server as a field with a single edge or list of edges. If your mutation or subscription exposes an edge or edges field that you can query for in the response, then you can use the `@appendEdge` and `@prependEdge` declarative mutation directives on that field in order to add the newly created edges to the specified connections (note that these directives also work on queries). + +Alternatively, mutation or subscription payloads might expose the new nodes that were added on the server as a field with a single node or list of nodes. If your mutation or subscription exposes a node or nodes field that you can query for in the response, then you can use the `@appendNode` and `@prependNode` declarative mutation directives on that field in order to add the newly created nodes, wrapped inside edges, to the specified connections (note that these directives also work on queries). + +These directives accept a `connections` parameter, which needs to be a GraphQL variable containing an array of connection IDs. Connection IDs can be obtained either by using the [`__id` field on connections](#accessing-connections-using-__id) or using the [`ConnectionHandler.getConnectionID`](#accessing-connections-using-connectionhandlergetconnectionid) API. + + +#### `@appendEdge` / `@prependEdge` + +These directives work on a field with a single edge or list of edges. `@prependEdge` will add the selected edges to the beginning of each connection defined in the `connections` array, whereas `@appendEdge` will add the selected edges to the end of each connection in the array. + +**Arguments:** +- `connections`: An array of connection IDs. Connection IDs can be obtained either by using the [`__id` field on connections](#accessing-connections-using-__id) or using the [`ConnectionHandler.getConnectionID`](#accessing-connections-using-connectionhandlergetconnectionid) API. + + +**Example:** + +```js +// Get the connection ID using the `__id` field +const connectionID = fragmentData?.comments?.__id; + +// Or get it using `ConnectionHandler.getConnectionID()` +const connectionID = ConnectionHandler.getConnectionID( + '', + 'StoryComponent_story_comments_connection', +); + +// ... + +// Mutation +commitMutation(environment, { + mutation: graphql` + mutation AppendCommentMutation( + # Define a GraphQL variable for the connections array + $connections: [ID!]! + $input: CommentCreateInput + ) { + commentCreate(input: $input) { + # Use @appendEdge or @prependEdge on the edge field + feedbackCommentEdge @appendEdge(connections: $connections) { + cursor + node { + id + } + } + } + } + `, + variables: { + input, + // Pass the `connections` array + connections: [connectionID], + }, +}); +``` + + +#### `@appendNode` / `@prependNode` + +These directives work on a field with a single node or list of nodes, and will create edges with the specified `edgeTypeName`. `@prependNode` will add edges containing the selected nodes to the beginning of each connection defined in the `connections` array, whereas `@appendNode` will add edges containing the selected nodes to the end of each connection in the array. + +**Arguments:** +- `connections`: An array of connection IDs. Connection IDs can be obtained either by using the [`__id` field on connections](#accessing-connections-using-__id) or using the [`ConnectionHandler.getConnectionID`](#accessing-connections-using-connectionhandlergetconnectionid) API. +- `edgeTypeName`: The type name of the edge that contains the node, corresponding to the edge type argument in `ConnectionHandler.createEdge`. + +**Example:** +```js +// Get the connection ID using the `__id` field +const connectionID = fragmentData?.comments?.__id; + +// Or get it using `ConnectionHandler.getConnectionID()` +const connectionID = ConnectionHandler.getConnectionID( + '', + 'StoryComponent_story_comments_connection', +); + +// ... + +// Mutation +commitMutation(environment, { + mutation: graphql` + mutation AppendCommentMutation( + # Define a GraphQL variable for the connections array + $connections: [ID!]! + $input: CommentCreateInput + ) { + commentCreate(input: $input) { + # Use @appendNode or @prependNode on the node field + feedbackCommentNode @appendNode(connections: $connections, edgeTypeName: "CommentsEdge") { + id + } + } + } + `, + variables: { + input, + // Pass the `connections` array + connections: [connectionID], + }, +}); +``` + + +#### Order of execution + +For all of these directives, they will be executed in the following order within the mutation or subscription, as per the [order of execution of updates](../../updating-data/graphql-mutations/#order-of-execution-of-updater-functions): + +* When the mutation is initiated, after the optimistic response is handled, and after the optimistic updater function is executed, the `@prependEdge`, `@appendEdge`, `@prependNode`, and `@appendNode` directives will be applied to the optimistic response. +* If the mutation succeeds, after the data from the network response is merged with the existing values in the store, and after the updater function is executed, the `@prependEdge`, `@appendEdge`, `@prependNode`, and `@appendNode` directives will be applied to the data in the network response. +* If the mutation failed, the updates from processing the `@prependEdge`, `@appendEdge`, `@prependNode`, and `@appendNode` directives will be rolled back. + + +### Manually adding edges + +The directives described [above](#using-declarative-directives) largely remove the need to manually add and remove items from a connection, however, they do not provide as much control as you can get with manually writing an updater, and may not fulfill every use case. + +In order to write an updater to modify the connection, we need to make sure we have access to the [connection record](#connection-record). Once we have the connection record, we also need a record for the new edge that we want to add to the connection. Usually, mutation or subscription payloads will contain the new edge that was added; if not, you can also construct a new edge from scratch. + +For example, in the following mutation we can query for the newly created edge in the mutation response: + +```js +const {graphql} = require('react-relay'); + +const createCommentMutation = graphql` + mutation CreateCommentMutation($input: CommentCreateData!) { + comment_create(input: $input) { + comment_edge { + cursor + node { + body { + text + } + } + } + } + } +`; +``` + +* Note that we also query for the `cursor` for the new edge; this isn't strictly necessary, but it is information that will be required if we need to perform pagination based on that `cursor`. + + +Inside an [`updater`](../../updating-data/graphql-mutations/#updater-functions), we can access the edge inside the mutation response using Relay store APIs: + +```js +const {ConnectionHandler} = require('relay-runtime'); + +function updater(store: RecordSourceSelectorProxy) { + const storyRecord = store.get(storyID); + const connectionRecord = ConnectionHandler.getConnection( + storyRecord, + 'StoryComponent_story_comments_connection', + ); + + // Get the payload returned from the server + const payload = store.getRootField('comment_create'); + + // Get the edge inside the payload + const serverEdge = payload.getLinkedRecord('comment_edge'); + + // Build edge for adding to the connection + const newEdge = ConnectionHandler.buildConnectionEdge( + store, + connectionRecord, + serverEdge, + ); + + // ... +} +``` + +* The mutation payload is available as a root field on that store, which can be read using the `store.getRootField` API. In our case, we're reading `comment_create`, which is the root field in the response. +* Note that we need to construct the new edge from the edge received from the server using `ConnectionHandler.buildConnectionEdge` before we can add it to the connection. + + +If you need to create a new edge from scratch, you can use `ConnectionHandler.createEdge`: + +```js +const {ConnectionHandler} = require('relay-runtime'); + +function updater(store: RecordSourceSelectorProxy) { + const storyRecord = store.get(storyID); + const connectionRecord = ConnectionHandler.getConnection( + storyRecord, + 'StoryComponent_story_comments_connection', + ); + + // Create a new local Comment record + const id = `client:new_comment:${randomID()}`; + const newCommentRecord = store.create(id, 'Comment'); + + // Create new edge + const newEdge = ConnectionHandler.createEdge( + store, + connectionRecord, + newCommentRecord, + 'CommentEdge', /* GraphQl Type for edge */ + ); + + // ... +} +``` + + +Once we have a new edge record, we can add it to the the connection using `ConnectionHandler.insertEdgeAfter` or `ConnectionHandler.insertEdgeBefore`: + +```js +const {ConnectionHandler} = require('relay-runtime'); + +function updater(store: RecordSourceSelectorProxy) { + const storyRecord = store.get(storyID); + const connectionRecord = ConnectionHandler.getConnection( + storyRecord, + 'StoryComponent_story_comments_connection', + ); + + const newEdge = (...); + + // Add edge to the end of the connection + ConnectionHandler.insertEdgeAfter( + connectionRecord, + newEdge, + ); + + // Add edge to the beginning of the connection + ConnectionHandler.insertEdgeBefore( + connectionRecord, + newEdge, + ); +} +``` + +* Note that these APIs will *mutate* the connection in place + +:::note +Check out our complete [Relay Store APIs](../../../api-reference/store/). +::: + +## Removing edges + +### Using the declarative deletion directive + +Similarly to the [directives to add edges](#using-declarative-directives), we can use the `@deleteEdge` directive to delete edges from connections. If your mutation or subscription exposes a field with the ID or IDs of the nodes that were deleted that you can query for in the response, then you can use the `@deleteEdge` directive on that field to delete the respective edges from the connection (note that this directive also works on queries). + +#### `@deleteEdge` + +Works on GraphQL fields that return an `ID` or `[ID]`. Will delete the edges with nodes that match the `id` from each connection defined in the `connections` array. + +**Arguments:** +- `connections`: An array of connection IDs. Connection IDs can be obtained either by using the [`__id` field on connections](#accessing-connections-using-__id) or using the [`ConnectionHandler.getConnectionID`](#accessing-connections-using-connectionhandlergetconnectionid) API. + + +**Example:** + +```js +// Get the connection ID using the `__id` field +const connectionID = fragmentData?.comments?.__id; + +// Or get it using `ConnectionHandler.getConnectionID()` +const connectionID = ConnectionHandler.getConnectionID( + '', + 'StoryComponent_story_comments_connection', +); + +// ... + +// Mutation +commitMutation(environment, { + mutation: graphql` + mutation DeleteCommentsMutation( + # Define a GraphQL variable for the connections array + $connections: [ID!]! + $input: CommentsDeleteInput + ) { + commentsDelete(input: $input) { + deletedCommentIds @deleteEdge(connections: $connections) + } + } + `, + variables: { + input, + // Pass the `connections` array + connections: [connectionID], + }, +}); +``` + +### Manually removing edges + +`ConnectionHandler` provides a similar API to remove an edge from a connection, via `ConnectionHandler.deleteNode`: + +```js +const {ConnectionHandler} = require('RelayModern'); + +function updater(store: RecordSourceSelectorProxy) { + const storyRecord = store.get(storyID); + const connectionRecord = ConnectionHandler.getConnection( + storyRecord, + 'StoryComponent_story_comments_connection', + ); + + // Remove edge from the connection, given the ID of the node + ConnectionHandler.deleteNode( + connectionRecord, + commentIDToDelete, + ); +} +``` + +* In this case `ConnectionHandler.deleteNode` will remove an edge given a *`node` ID*. This means it will look up which edge in the connection contains a node with the provided ID, and remove that edge. +* Note that this API will *mutate* the connection in place. + + +:::note +Remember: when performing any of the operations described here to mutate a connection, any fragment or query components that are rendering the affected connection will be notified and re-render with the latest version of the connection. +::: + + +## Connection identity with filters + +In our previous examples, our connections didn't take any arguments as filters. If you declared a connection that takes arguments as filters, the values used for the filters will be part of the connection identifier. In other words, *each of the values passed in as connection filters will be used to identify the connection in the Relay store.* + +:::note +Note that this excludes pagination arguments, i.e. it excludes `first`, `last`, `before`, and `after`. +::: + + +For example, let's say the `comments` field took the following arguments, which we pass in as GraphQL [variables](../../rendering/variables/): + +```js +const {graphql} = require('RelayModern'); + +const storyFragment = graphql` + fragment StoryComponent_story on Story { + comments( + order_by: $orderBy, + filter_mode: $filterMode, + language: $language, + ) @connection(key: "StoryComponent_story_comments_connection") { + edges { + nodes { + body { + text + } + } + } + } + } +`; +``` + +In the example above, this means that whatever values we used for `$orderBy`, `$filterMode` and `$language` when we queried for the `comments` field will be part of the connection identifier, and we'll need to use those values when accessing the connection record from the Relay store. + +In order to do so, we need to pass a third argument to `ConnectionHandler.getConnection`, with concrete filter values to identify the connection: + +```js +const {ConnectionHandler} = require('RelayModern'); + +function updater(store: RecordSourceSelectorProxy) { + const storyRecord = store.get(storyID); + + // Get the connection instance for the connection with comments sorted + // by the date they were added + const connectionRecordSortedByDate = ConnectionHandler.getConnection( + storyRecord, + 'StoryComponent_story_comments_connection', + {order_by: '*DATE_ADDED*', filter_mode: null, language: null} + ); + + // Get the connection instance for the connection that only contains + // comments made by friends + const connectionRecordFriendsOnly = ConnectionHandler.getConnection( + storyRecord, + 'StoryComponent_story_comments_connection', + {order_by: null, filter_mode: '*FRIENDS_ONLY*', language: null} + ); +} +``` + +This implies that by default, *each combination of values used for filters will produce a different record for the connection.* + +When making updates to a connection, you will need to make sure to update all of the relevant records affected by a change. For example, if we were to add a new comment to our example connection, we'd need to make sure *not* to add the comment to the `FRIENDS_ONLY` connection, if the new comment wasn't made by a friend of the user: + +```js +const {ConnectionHandler} = require('relay-runtime'); + +function updater(store: RecordSourceSelectorProxy) { + const storyRecord = store.get(storyID); + + // Get the connection instance for the connection with comments sorted + // by the date they were added + const connectionRecordSortedByDate = ConnectionHandler.getConnection( + storyRecord, + 'StoryComponent_story_comments_connection', + {order_by: '*DATE_ADDED*', filter_mode: null, language: null} + ); + + // Get the connection instance for the connection that only contains + // comments made by friends + const connectionRecordFriendsOnly = ConnectionHandler.getConnection( + storyRecord, + 'StoryComponent_story_comments_connection', + {order_by: null, filter_mode: '*FRIENDS_ONLY*', language: null} + ); + + const newComment = (...); + const newEdge = (...); + + ConnectionHandler.insertEdgeAfter( + connectionRecordSortedByDate, + newEdge, + ); + + if (isMadeByFriend(storyRecord, newComment) { + // Only add new comment to friends-only connection if the comment + // was made by a friend + ConnectionHandler.insertEdgeAfter( + connectionRecordFriendsOnly, + newEdge, + ); + } +} +``` + + + +### Managing connections with many filters + +As you can see, just adding a few filters to a connection can make the complexity and number of connection records that need to be managed explode. In order to more easily manage this, Relay allows you to specify exactly *which* filters should be used as connection identifiers. + +By default, *all* non-pagination filters will be used as part of the connection identifier. However, when declaring a `@connection`, you can specify the exact set of filters to use for connection identity: + +```js +const {graphql} = require('relay-runtime'); + +const storyFragment = graphql` + fragment StoryComponent_story on Story { + comments( + order_by: $orderBy + filter_mode: $filterMode + language: $language + ) + @connection( + key: "StoryComponent_story_comments_connection" + filters: ["order_by", "filter_mode"] + ) { + edges { + nodes { + body { + text + } + } + } + } + } +`; +``` + +* By specifying `filters` when declaring the `@connection`, we're indicating to Relay the exact set of filter values that should be used as part of connection identity. In this case, we're excluding `language`, which means that only values for `order_by` and `filter_mode` will affect connection identity and thus produce new connection records. +* Conceptually, this means that we're specifying which arguments affect the output of the connection from the server, or in other words, which arguments are *actually* *filters*. If one of the connection arguments doesn't actually change the set of items that are returned from the server, or their ordering, then it isn't really a filter on the connection, and we don't need to identify the connection differently when that value changes. In our example, changing the `language` of the comments we request doesn't change the set of comments that are returned by the connection, so it is safe to exclude it from `filters`. +* This can also be useful if we know that any of the connection arguments will never change in our app, in which case it would also be safe to exclude from `filters`. + + diff --git a/website/versioned_docs/version-v19.0.0/guides/alias-directive.md b/website/versioned_docs/version-v19.0.0/guides/alias-directive.md new file mode 100644 index 0000000000000..e56bfe0c41501 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/guides/alias-directive.md @@ -0,0 +1,160 @@ +--- +id: alias-directive +title: "@alias Directive" +slug: /guides/alias-directive/ +description: Relay guide to @alias +keywords: +- alias +- directive +- fragment +--- + +The `@alias` directive allows you to expose a spread fragment — either a named fragment spread or an inline fragment — as a named field within your selection. This allows Relay to provide additional type safety in the case where your fragment’s type may not match the parent selection. + +:::info +This document describes why the `@alias` directive was introduced, and how it can be used to improve type safety in your Relay applications. **To learn about it's API, see the [API Reference](../api-reference/graphql/graphql-directives.md#alias).** +::: + +Let’s look at an examples where `@alias` can be useful: + +## Abstract Types + +Imagine you have a component that renders information about a Viewer: + +```ts +function MyViewer({viewerKey}) { + const {name} = useFragment(graphql` + fragment MyViewer on Viewer { + name @required(action: THROW) + }`, viewerKey); + + return `My name is ${name}. That's ${name.length} letters long!`; +} +``` + +To use that component in a component that has a fragment on Node (which Viewer implements), you could write something like this: + +```ts +function MyNode({nodeKey}) { + const node = useFragment(graphql` + fragment MyFragment on Node { + ...MyViewer + }`, nodeKey); + + return +} +``` + +Can you spot the problem? We don’t actually know that the node we are passing to `` is actually a Viewer ``. If `` tries to render a Comment — which also implements Node — we will get a runtime error in `` because the field name is not present on Comment. + +``` +TypeError: Cannot read properties of undefined (reading 'length') +``` + +Not only do we not get a type letting us know that about this potential issue, but even at runtime, there is no way way to check if node implements Viewer because Viewer is an abstract type! + +## Aliased Fragments + +Aliased fragments can solve this problem. Here’s what `` would look like using them: + +```ts +function MyNode({nodeKey}) { + const node = useFragment(graphql` + fragment MyFragment on Node { + ...MyViewer @alias(as: "my_viewer") + }`, nodeKey); + + // Relay returns the fragment key as its own nullable property + if(node.my_viewer == null) { + return null; + } + + // Because `my_viewer` is typed as nullable, Flow/TypeScript will + // show an error if you try to use the `my_viewer` without first + // performing a null check. + // VVVVVVVVVVVVVV + return +} +``` + +With this approach, you can see that Relay exposes the fragment key as its own nullable property, which allows us to check that node actually implements Viewer and even allows Flow to enforce that the component handles the possibility! + +## @skip and @include + +A similar problem can occur when using `@skip` and `@include` directives on fragments. In order to safely use the spread fragment, you need to check if it was fetched. Historically this has required gaining access to the query variable that was used to determine if the fragment was skipped or included. + +With `@alias`, you can now check if the fragment was fetched by simply assigning the fragment an alias, and checking if the alias is null: + +```ts +function MyUser({userKey}) { + const user = useFragment(graphql` + fragment MyFragment on User { + ...ConditionalData @skip(if: $someVar) @alias + }`, userKey); + + if(user.ConditionalData == null) { + return "No data fetched"; + } + return +} +``` + +## Enforced Safety + +We've outlined two different ways that fragments can be unsafe in Relay today without `@alias`. To help prevent runtime issues resulting from these unsafe edge cases, Relay requires that all conditionally fetched fragments are aliased. + +To disable this validation in your project, you can disable the `enforce_fragment_alias_where_ambiguous` compiler feature flag for your project. If you need to enable incremental adoption of this enforcement, Relay exposes a directive `@dangerously_unaliased_fixme` which will suppress enforcement errors. This will allow you to enable the enforcement for all new spreads without first needing to migrate all existing issues. + +The [Relay VSCode extension](../editor-support.md) offers quick fixes to add either `@alias` or `@dangerously_unaliased_fixme` to unsafe fragments, and the +[mark-dangerous-conditional-fragment-spreads](../codemods/#mark-dangerous-conditional-fragment-spreads) codemod can be used to apply `@dangerously_unaliased_fixme` across your entire project. + +## Use with @required + +`@alias` can be used with [`@required(action: NONE)`](./required-directive.md) to group together required fields. In the following example, we group `name` and `email` together as `requiredFields`. If either is null, that null will bubble up to, the `user.requiredFields` field, making it null. This allows us to perform a single check, without impacting the `id` field. + +```ts +function MyUser({userKey}) { + const user = useFragment(graphql` + fragment MyFragment on User { + id + ... @alias(as: "requiredFields") { + name @required(action: NONE) + email @required(action: NONE) + } + }`, userKey); + + if(user.requiredFields == null) { + return `Missing required fields for user ${user.id}`; + } + return `Hello ${user.requiredFields.name} (${user.requiredFields.email}).!`; +} +``` + +:::note +Using `@required` on a fragment spread that has an `@alias` is not currently supported, but we may add support in the future. +::: + +## Under the Hood + +For people familiar with Relay, or curious to learn, here is a brief description of how this feature is implemented: + +Under the hood, `@alias` is implemented entirely within Relay (compiler and runtime). It does not require any server support. The Relay compiler interprets the `@alias` directive, and generates types indicating that the fragment key, or inline fragment data, will be attached to the new field, rather than directly on the parent object. In the Relay runtime artifact, it wraps the fragment node with a new node indicating the name of the alias and additional information about the type of the fragment. + +The Relay compiler also inserts an additional field into the spread which allows it to determine if the fragment has matched: + +```graphql +fragment Foo on Node { + ... on Viewer { + isViewer: __typename # <-- Relay inserts this + name + } +} +``` + +Relay can now check for the existence of the `isViewer` field in the response to know if the fragment matched. + +When Relay reads the content of your fragment out of the store using its runtime artifact, it uses this information to attach the fragment key to this new field, rather than attaching it directly to the parent object. + +### Related + +While `@alias` is a Relay-specific feature, it draws inspiration from fragment modularity as outlined in the GraphQL [RFC Fragment Modularity](https://github.com/graphql/graphql-wg/blob/main/rfcs/FragmentModularity.md). diff --git a/website/versioned_docs/version-v19.0.0/guides/catch-directive.md b/website/versioned_docs/version-v19.0.0/guides/catch-directive.md new file mode 100644 index 0000000000000..b545135b561c9 --- /dev/null +++ b/website/versioned_docs/version-v19.0.0/guides/catch-directive.md @@ -0,0 +1,161 @@ +--- +id: catch-directive +title: '@catch Directive' +slug: /guides/catch-directive/ +description: Relay guide to @catch +keywords: + - catch + - directive + - optional +--- + +import DocsRating from '@site/src/core/DocsRating'; + +The `@catch` directive can be added to fields, fragment/operation definitions or +aliased inline fragment spreads declare how exceptions and unexpected values +should be handled at runtime. Using `@catch` allows Relay to surface these error +states as part of your fragment/query/mutation data instead of a null value +(which has been the default behavior), or a runtime exception if +[`@throwOnFieldError`](./throw-on-field-error-directive.md) is being used. + +## `to` Argument + +The `@catch` directive accepts an optional `to` argument which has two options: + +- `RESULT` (default): The value is returned as `{ ok: true, value: T } | { ok: false, errors: [error] }`. This allows you implement explicit field-granular error handling in your application. +- `NULL`: If an error is encountered within the `@catch`, the value will be replaced with `null`. + +## Examples + +If a `@catch` error is caught directly on the field that the error originated +from - the error is provided on that field. Here's an example: + +```graphql +query MyQuery { + viewer { + name @catch + age + } +} +``` + +If `name` contains an error - it would be provided in the response data on the +`name` field - like so: + +```js +{ + viewer: { + name: { + ok: false, + errors: [{path: ['viewer', 'name']}] + } + age: 39 + } +} +``` + +However, if `@catch` exists on one of the ancestors of a field, that error will +bubble up to there, like so: + +```graphql +query MyQuery { + viewer @catch { + name + age + } +} +``` + +```js +{ + viewer: { + ok: false, + errors: [{ path: ['viewer', 'name'] } ] + } +} +``` + +## Implications for nullability + +Fields whose errors are explicitly handled by `@catch`, either by being +annotated with `@catch` or by being nested with a `@catch` ancestor will be +typed using their [Semantic Nullability](./semantic-nullability.md). In other +words, if a field has been marked as `@semanticNonNull` in the server schema to +indicate that it will only be null in the case of error, Relay will type that +field as non-nullable in its generated Flow/TypeScript types. + +## What can be caught with `@catch`? + +### Payload Field Errors + +Payload [field +errors](https://spec.graphql.org/October2021/#sec-Errors.Field-errors) are +errors that occur as the result of a server-side exception while executing a +given field's resolver. In this case, the GraphQL specifies that the sever must +provide a null value where a value should be, and a separate errors object. + +When you `@catch` on a field, Relay takes those errors and provides them to you +in-line instead, making them easier to handle, and no longer invisible. + +### @required(action: THROW) within an @catch + +If you have an `@required(action: THROW)` with an ancestor that contains a +`@catch` - instead of throwing an exception, the `@required` error would bubble +up and be provided in the same way normal errors would. Here's an example: + +```graphql +query MyQuery { + viewer @catch { + name @required(action: THROW) + age + } +} +``` + +```js +{ + viewer: { + ok: false, + errors: [{ path: ["viewer", "name"] }] + } +} +``` + +### Missing Data in response + +[Here is an example of where missing data may occur in Relay](https://relay.dev/docs/next/debugging/why-null/#graph-relationship-change) + +If a field is expected to have a value, and that field is undefined - the field +is considered to be "missing data". This is also an unexpected state - and when +it happens with an `@catch` as an ancestor, it will also be caught like so: + +```js +{ + viewer: { + ok: false, + errors: [{ path: ["viewer", "name"] }] + } +} +``` + +## How does `@catch` interact with `@throwOnFieldError`? + +Using `@throwOnFieldError` enables fields to throw a JavaScript exception when a +field error occurs. By using `@catch` - you tell Relay that you don't want a +JavaScript exception in this case. Instead, you are requesting that the error be +provided in the data object, with the same behaviors and rules as are listed +above (including bubbling to a parent field). + +It is important to note that you can still use @catch without +@throwOnFieldError. It will still provide you the error in the data object. But +other fields that are not under a `@catch` will still not throw - because +`@throwOnFieldError` would be missing. + +Read more about `@throwOnFieldError` +[here](https://relay.dev/docs/next/api-reference/graphql-and-directives/#throwonfielderror-experimental). + +## GraphQL Conf Talk + +The Relay team gave a talk at GraphQL Conf 2024 about `@catch` and explicit error handling in Relay. You can watch it here: + + + +## Facebook F8 2017 + +### [The Evolution of React and GraphQL at Facebook and Beyond](https://developers.facebook.com/videos/f8-2017/the-evolution-of-react-and-graphql-at-facebook-and-beyond/) + + + +## Facebook F8 2017 + +### [The Evolution of React and GraphQL at Facebook and Beyond](https://developers.facebook.com/videos/f8-2017/the-evolution-of-react-and-graphql-at-facebook-and-beyond/) + + + +## Facebook F8 2017 + +### [The Evolution of React and GraphQL at Facebook and Beyond](https://developers.facebook.com/videos/f8-2017/the-evolution-of-react-and-graphql-at-facebook-and-beyond/) + +