;
15 |
16 | /**
17 | * List of additional fragments to include in query (optional)
18 | * @default []
19 | */
20 | fragments: IsolatedQueryInput[];
21 |
22 | /**
23 | * If enabled, the result of the query will automatically
24 | * merge with last data props.
25 | * @default true
26 | */
27 | composeData: boolean;
28 | }
29 |
30 | export declare type WithGraphQLClient = P & {
31 | graphql(fieldName, QueryParameters)
32 | };
33 |
34 | export function getOptions(name: string): any;
35 | export function setOptions(name: string, options: any): void;
36 | export function getIsolatedQuery(query: IsolatedQueryInput, fieldName: string, typeName: string): DocumentNode
37 | export function withGraphql(WrappedComponent: React.ComponentType>): React.ComponentClass;
38 |
--------------------------------------------------------------------------------
/example/gatsby-config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | siteMetadata: {
3 | title: `Gatsby Default Starter`,
4 | description: `Kick off your next, great Gatsby project with this default starter. This barebones starter ships with the main Gatsby configuration files you might need.`,
5 | author: `@gatsbyjs`,
6 | },
7 | plugins: [
8 | `gatsby-plugin-typescript`,
9 | {
10 | resolve: "@prismicio/gatsby-source-graphql-universal",
11 | options: {
12 | typeName: "SWAPI",
13 | fieldName: "swapi",
14 | url: "https://swapi-graphql.netlify.app/.netlify/functions/index",
15 | },
16 | },
17 | `gatsby-plugin-react-helmet`,
18 | {
19 | resolve: `gatsby-source-filesystem`,
20 | options: {
21 | name: `images`,
22 | path: `${__dirname}/src/images`,
23 | },
24 | },
25 | `gatsby-transformer-sharp`,
26 | `gatsby-plugin-sharp`,
27 | {
28 | resolve: `gatsby-plugin-manifest`,
29 | options: {
30 | name: `gatsby-starter-default`,
31 | short_name: `starter`,
32 | start_url: `/`,
33 | background_color: `#663399`,
34 | theme_color: `#663399`,
35 | display: `minimal-ui`,
36 | icon: `src/images/gatsby-icon.png`, // This path is relative to the root of the site.
37 | },
38 | },
39 | // this (optional) plugin enables Progressive Web App + Offline functionality
40 | // To learn more, visit: https://gatsby.app/offline
41 | // 'gatsby-plugin-offline',
42 | ],
43 | }
44 |
--------------------------------------------------------------------------------
/packages/gatsby-source-graphql-universal/src/gatsby-node.js:
--------------------------------------------------------------------------------
1 | const { sourceNodes } = require('gatsby-source-graphql/gatsby-node');
2 | const { getRootQuery } = require('./getRootQuery');
3 |
4 | exports.sourceNodes = ({ actions, ...rest }, options) => sourceNodes({
5 | ...rest,
6 | actions: {
7 | ...actions,
8 | createNode: (node, opts = {}) => actions.createNode(node, {
9 | ...opts,
10 | name: '@prismicio/gatsby-source-graphql-universal'
11 | })
12 | }
13 | }, options);
14 |
15 | exports.onCreatePage = ({ page, actions }) => {
16 | const rootQuery = getRootQuery(page.componentPath);
17 | if (rootQuery) {
18 | page.context = page.context || {};
19 | page.context.rootQuery = rootQuery;
20 | actions.createPage(page);
21 | }
22 | };
23 |
24 | exports.onCreateWebpackConfig = ({ stage, actions, getConfig }) => {
25 | const config = getConfig()
26 |
27 | if (stage.indexOf('html') >= 0) {
28 | return;
29 | }
30 |
31 | const replaceRule = ruleUse => {
32 | if (ruleUse.loader && ruleUse.loader.indexOf(`gatsby/dist/utils/babel-loader.js`) >= 0) {
33 | ruleUse.loader = require.resolve(`@prismicio/gatsby-source-graphql-universal/babel-loader.js`);
34 | }
35 | }
36 |
37 | const traverseRule = rule => {
38 | if (rule.oneOf && Array.isArray(rule.oneOf)) {
39 | rule.oneOf.forEach(traverseRule);
40 | }
41 |
42 | if (rule.use) {
43 | if (Array.isArray(rule.use)) {
44 | rule.use.forEach(replaceRule);
45 | } else {
46 | replaceRule(rule.use);
47 | }
48 | }
49 |
50 | };
51 |
52 | config.module.rules.forEach(traverseRule)
53 |
54 | actions.replaceWebpackConfig(config)
55 | };
--------------------------------------------------------------------------------
/packages/gatsby-source-graphql-universal/dist/gatsby-node.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const {
4 | sourceNodes
5 | } = require('gatsby-source-graphql/gatsby-node');
6 |
7 | const {
8 | getRootQuery
9 | } = require('./getRootQuery');
10 |
11 | exports.sourceNodes = ({
12 | actions,
13 | ...rest
14 | }, options) => sourceNodes({ ...rest,
15 | actions: { ...actions,
16 | createNode: (node, opts = {}) => actions.createNode(node, { ...opts,
17 | name: '@prismicio/gatsby-source-graphql-universal'
18 | })
19 | }
20 | }, options);
21 |
22 | exports.onCreatePage = ({
23 | page,
24 | actions
25 | }) => {
26 | const rootQuery = getRootQuery(page.componentPath);
27 |
28 | if (rootQuery) {
29 | page.context = page.context || {};
30 | page.context.rootQuery = rootQuery;
31 | actions.createPage(page);
32 | }
33 | };
34 |
35 | exports.onCreateWebpackConfig = ({
36 | stage,
37 | actions,
38 | getConfig
39 | }) => {
40 | const config = getConfig();
41 |
42 | if (stage.indexOf('html') >= 0) {
43 | return;
44 | }
45 |
46 | const replaceRule = ruleUse => {
47 | if (ruleUse.loader && ruleUse.loader.indexOf(`gatsby/dist/utils/babel-loader.js`) >= 0) {
48 | ruleUse.loader = require.resolve(`@prismicio/gatsby-source-graphql-universal/babel-loader.js`);
49 | }
50 | };
51 |
52 | const traverseRule = rule => {
53 | if (rule.oneOf && Array.isArray(rule.oneOf)) {
54 | rule.oneOf.forEach(traverseRule);
55 | }
56 |
57 | if (rule.use) {
58 | if (Array.isArray(rule.use)) {
59 | rule.use.forEach(replaceRule);
60 | } else {
61 | replaceRule(rule.use);
62 | }
63 | }
64 | };
65 |
66 | config.module.rules.forEach(traverseRule);
67 | actions.replaceWebpackConfig(config);
68 | };
--------------------------------------------------------------------------------
/packages/gatsby-source-graphql-universal/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@prismicio/gatsby-source-graphql-universal",
3 | "description": "Gatsby plugin which adds a third-party GraphQL API to Gatsby GraphQL with client side execution",
4 | "version": "3.4.15",
5 | "author": "Birkir Gudjonsson ",
6 | "contributors": [
7 | "Marc McIntosh "
8 | ],
9 | "bugs": {
10 | "url": "https://github.com/prismicio/gatsby-source-graphql-universal/issues"
11 | },
12 | "main": "index.js",
13 | "files": [
14 | "dist",
15 | "*.js",
16 | "index.d.ts",
17 | "!jest.config.js"
18 | ],
19 | "dependencies": {
20 | "apollo-boost": "^0.4.9",
21 | "babel-loader": "^8.1.0",
22 | "babel-preset-gatsby": "^0.5.1",
23 | "gatsby-source-graphql": "^2.6.2",
24 | "graphql-tag": "^2.10.0",
25 | "graphql-tools": "^6.0.12",
26 | "lodash.clonedeep": "4.5.0",
27 | "lodash.get": "^4.4.2",
28 | "traverse": "^0.6.6"
29 | },
30 | "devDependencies": {
31 | "@babel/cli": "^7.0.0",
32 | "@babel/core": "^7.0.0",
33 | "@babel/preset-react": "^7.10.4",
34 | "@types/graphql": "^14.0.4",
35 | "babel-preset-gatsby-package": "^0.5.1",
36 | "cross-env": "^5.1.4",
37 | "jest": "^26.1.0"
38 | },
39 | "homepage": "https://github.com/birkir/gatsby-source-graphql-universal/#readme",
40 | "keywords": [
41 | "gatsby",
42 | "gatsby-plugin",
43 | "graphql",
44 | "universal"
45 | ],
46 | "license": "MIT",
47 | "peerDependencies": {
48 | "gatsby": "^2.24.11",
49 | "graphql": "^14.6.0"
50 | },
51 | "repository": "https://github.com/prismicio/gatsby-source-graphql-universal",
52 | "scripts": {
53 | "build": "babel src --out-dir dist --ignore **/__tests__",
54 | "prepare": "cross-env NODE_ENV=production npm run build",
55 | "watch": "babel -w src --out-dir dist --ignore **/__tests__",
56 | "clean": "rm -rf ./dist/*.js",
57 | "test": "jest"
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gatsby-starter-default",
3 | "private": true,
4 | "description": "A simple starter to get up and developing quickly with Gatsby",
5 | "version": "0.1.0",
6 | "author": "Kyle Mathews ",
7 | "dependencies": {
8 | "@prismicio/gatsby-source-graphql-universal": "^3.4.5",
9 | "atob": "^2.1.2",
10 | "gatsby": "^2.24.11",
11 | "gatsby-image": "^2.0.20",
12 | "gatsby-plugin-manifest": "^2.0.9",
13 | "gatsby-plugin-offline": "^2.0.16",
14 | "gatsby-plugin-react-helmet": "^3.0.2",
15 | "gatsby-plugin-sharp": "^2.0.14",
16 | "gatsby-plugin-typescript": "^2.0.3",
17 | "gatsby-source-filesystem": "^2.0.8",
18 | "gatsby-transformer-sharp": "^2.1.8",
19 | "graphql-tools": "5.0.0",
20 | "prop-types": "^15.6.2",
21 | "react": "^16.6.3",
22 | "react-dom": "^16.6.3",
23 | "react-helmet": "^5.2.0"
24 | },
25 | "resolutions": {
26 | "graphql": "14.6.0"
27 | },
28 | "keywords": [
29 | "gatsby"
30 | ],
31 | "license": "MIT",
32 | "scripts": {
33 | "build": "gatsby build",
34 | "develop": "gatsby develop --verbose",
35 | "start": "npm run develop",
36 | "serve": "gatsby serve",
37 | "clean": "gatsby clean",
38 | "format": "prettier --write \"src/**/*.js\"",
39 | "copy-plugin": "cp -R ../*.js node_modules/gatsby-source-graphql-universal/.",
40 | "test": "yarn test:e2e:ci",
41 | "cy:open": "cypress open",
42 | "cy:run": "cypress run",
43 | "cy:run:prod": "cypress run -C ./cypress.prod.json",
44 | "test:e2e": "start-server-and-test develop http://localhost:8000 cy:open",
45 | "test:e2e:ci": "start-server-and-test develop http://localhost:8000 cy:run && npm run build && start-server-and-test serve http://localhost:9000 cy:run:prod"
46 | },
47 | "devDependencies": {
48 | "cypress": "^4.10.0",
49 | "prettier": "^1.15.2",
50 | "start-server-and-test": "^1.11.1"
51 | },
52 | "repository": {
53 | "type": "git",
54 | "url": "https://github.com/gatsbyjs/gatsby-starter-default"
55 | },
56 | "bugs": {
57 | "url": "https://github.com/gatsbyjs/gatsby/issues"
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/example/src/components/seo.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import PropTypes from 'prop-types'
3 | import Helmet from 'react-helmet'
4 | import { StaticQuery, graphql } from 'gatsby'
5 |
6 | function SEO({ description, lang, meta, keywords, title }) {
7 | return (
8 | {
11 | const metaDescription =
12 | description || data.site.siteMetadata.description
13 | return (
14 | 0
56 | ? {
57 | name: `keywords`,
58 | content: keywords.join(`, `),
59 | }
60 | : []
61 | )
62 | .concat(meta)}
63 | />
64 | )
65 | }}
66 | />
67 | )
68 | }
69 |
70 | SEO.defaultProps = {
71 | lang: `en`,
72 | meta: [],
73 | keywords: [],
74 | }
75 |
76 | SEO.propTypes = {
77 | description: PropTypes.string,
78 | lang: PropTypes.string,
79 | meta: PropTypes.array,
80 | keywords: PropTypes.arrayOf(PropTypes.string),
81 | title: PropTypes.string.isRequired,
82 | }
83 |
84 | export default SEO
85 |
86 | const detailsQuery = graphql`
87 | query DefaultSEOQuery {
88 | site {
89 | siteMetadata {
90 | title
91 | description
92 | author
93 | }
94 | }
95 | }
96 | `
97 |
--------------------------------------------------------------------------------
/packages/gatsby-source-graphql-universal/dist/babel-loader.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | const babelLoader = require(`babel-loader`);
4 |
5 | const {
6 | getCustomOptions,
7 | mergeConfigItemOptions
8 | } = require(`gatsby/dist/utils/babel-loader-helpers`);
9 |
10 | const {
11 | prepareOptions
12 | } = require(`./utils`);
13 | /**
14 | * Gatsby's custom loader for webpack & babel
15 | *
16 | * Gatsby allows sites to either use our Babel setup (the default)
17 | * or to add a .babelrc to take control.
18 | *
19 | * Our default setup is defined in the fallbackPlugins/fallbackPresets arrays
20 | * below.
21 | *
22 | * After using either the fallback or user supplied setup, we add on a handful
23 | * of required plugins and finally merge in any presets/plugins supplied
24 | * by Gatsby plugins.
25 | *
26 | * You can find documentation for the custom loader here: https://babeljs.io/docs/en/next/babel-core.html#loadpartialconfig
27 | */
28 |
29 |
30 | module.exports = babelLoader.custom(babel => {
31 | const toReturn = {
32 | // Passed the loader options.
33 | customOptions({
34 | stage = `test`,
35 | reactRuntime = `classic`,
36 | ...options
37 | }) {
38 | return {
39 | custom: {
40 | stage,
41 | reactRuntime
42 | },
43 | loader: {
44 | cacheDirectory: true,
45 | sourceType: `unambiguous`,
46 | ...getCustomOptions(stage)
47 | },
48 | ...options
49 | };
50 | },
51 |
52 | // Passed Babel's 'PartialConfig' object.
53 | config(partialConfig, {
54 | customOptions
55 | }) {
56 | let {
57 | options
58 | } = partialConfig;
59 | const [reduxPresets, reduxPlugins, requiredPresets, requiredPlugins, fallbackPresets] = prepareOptions(babel, customOptions); // If there is no filesystem babel config present, add our fallback
60 | // presets/plugins.
61 |
62 | if (!partialConfig.hasFilesystemConfig()) {
63 | options = { ...options,
64 | plugins: requiredPlugins,
65 | presets: [...fallbackPresets, ...requiredPresets]
66 | };
67 | } else {
68 | // With a babelrc present, only add our required plugins/presets
69 | options = { ...options,
70 | plugins: [...options.plugins, ...requiredPlugins],
71 | presets: [...options.presets, ...requiredPresets]
72 | };
73 | } // Merge in presets/plugins added from gatsby plugins.
74 |
75 |
76 | reduxPresets.forEach(preset => {
77 | options.presets = mergeConfigItemOptions({
78 | items: options.presets,
79 | itemToMerge: preset,
80 | type: `preset`,
81 | babel
82 | });
83 | });
84 | reduxPlugins.forEach(plugin => {
85 | options.plugins = mergeConfigItemOptions({
86 | items: options.plugins,
87 | itemToMerge: plugin,
88 | type: `plugin`,
89 | babel
90 | });
91 | });
92 | return options;
93 | }
94 |
95 | };
96 | return toReturn;
97 | });
--------------------------------------------------------------------------------
/packages/gatsby-source-graphql-universal/src/babel-loader.js:
--------------------------------------------------------------------------------
1 | const babelLoader = require(`babel-loader`)
2 |
3 | const {
4 | getCustomOptions,
5 | mergeConfigItemOptions,
6 | } = require(`gatsby/dist/utils/babel-loader-helpers`)
7 | const { prepareOptions } = require(`./utils`);
8 |
9 | /**
10 | * Gatsby's custom loader for webpack & babel
11 | *
12 | * Gatsby allows sites to either use our Babel setup (the default)
13 | * or to add a .babelrc to take control.
14 | *
15 | * Our default setup is defined in the fallbackPlugins/fallbackPresets arrays
16 | * below.
17 | *
18 | * After using either the fallback or user supplied setup, we add on a handful
19 | * of required plugins and finally merge in any presets/plugins supplied
20 | * by Gatsby plugins.
21 | *
22 | * You can find documentation for the custom loader here: https://babeljs.io/docs/en/next/babel-core.html#loadpartialconfig
23 | */
24 | module.exports = babelLoader.custom((babel) => {
25 | const toReturn = {
26 | // Passed the loader options.
27 | customOptions({ stage = `test`, reactRuntime = `classic`, ...options }) {
28 | return {
29 | custom: {
30 | stage,
31 | reactRuntime
32 | },
33 | loader: {
34 | cacheDirectory: true,
35 | sourceType: `unambiguous`,
36 | ...getCustomOptions(stage),
37 | },
38 | ...options
39 | }
40 | },
41 |
42 | // Passed Babel's 'PartialConfig' object.
43 | config(partialConfig, { customOptions }) {
44 | let { options } = partialConfig
45 | const [
46 | reduxPresets,
47 | reduxPlugins,
48 | requiredPresets,
49 | requiredPlugins,
50 | fallbackPresets,
51 | ] = prepareOptions(babel, customOptions)
52 |
53 | // If there is no filesystem babel config present, add our fallback
54 | // presets/plugins.
55 | if (!partialConfig.hasFilesystemConfig()) {
56 | options = {
57 | ...options,
58 | plugins: requiredPlugins,
59 | presets: [...fallbackPresets, ...requiredPresets],
60 | }
61 | } else {
62 | // With a babelrc present, only add our required plugins/presets
63 | options = {
64 | ...options,
65 | plugins: [...options.plugins, ...requiredPlugins],
66 | presets: [...options.presets, ...requiredPresets],
67 | }
68 | }
69 |
70 | // Merge in presets/plugins added from gatsby plugins.
71 | reduxPresets.forEach(preset => {
72 | options.presets = mergeConfigItemOptions({
73 | items: options.presets,
74 | itemToMerge: preset,
75 | type: `preset`,
76 | babel,
77 | })
78 | })
79 |
80 | reduxPlugins.forEach(plugin => {
81 | options.plugins = mergeConfigItemOptions({
82 | items: options.plugins,
83 | itemToMerge: plugin,
84 | type: `plugin`,
85 | babel,
86 | })
87 | })
88 |
89 | return options
90 | },
91 | }
92 |
93 | return toReturn
94 | })
95 |
--------------------------------------------------------------------------------
/example/src/pages/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { graphql, useStaticQuery } from 'gatsby';
3 | import Layout from '../components/layout'
4 | import SEO from '../components/seo'
5 | import { withGraphql } from '@prismicio/gatsby-source-graphql-universal';
6 | import { BestFilm } from '../components/best-film';
7 | import atob from 'atob'
8 |
9 | export const planetFragment = graphql`
10 | fragment Planet on SWAPI_Planet {
11 | id
12 | name
13 | }
14 | `;
15 |
16 | export const query = graphql`
17 | query homepage($first: Int = 2, $last: Int, $before:String, $after: String) {
18 | site {
19 | siteMetadata {
20 | title
21 | }
22 | }
23 | swapi {
24 | allFilms(first: $first, last: $last, after: $after, before: $before) {
25 | edges {
26 | cursor
27 | node {
28 | id
29 | title
30 | planetConnection {
31 | edges {
32 | node {
33 | ...Planet
34 | }
35 | }
36 | }
37 | }
38 | }
39 | }
40 | }
41 | }
42 | `;
43 |
44 | const limit = 2;
45 |
46 | class IndexPage extends React.Component {
47 |
48 | state = {
49 | after:null,
50 | before:null,
51 | first:limit,
52 | last:null,
53 | loading: false,
54 | }
55 |
56 | update = async () => {
57 | const { after, before, first, last } = this.state;
58 | this.setState({ loading: true });
59 |
60 | await this.props.graphql('swapi', {
61 | query,
62 | fragments: [planetFragment],
63 | variables: this.state,
64 | });
65 | this.setState({ loading: false });
66 | }
67 |
68 | onPrevClick = () => {
69 | const { cursor } = this.props.data.swapi.allFilms.edges.length ? this.props.data.swapi.allFilms.edges.slice(0).shift() : { cursor: this.state.after };
70 | this.setState({ before: cursor, after: null, first: null, last: limit }, this.update);
71 | }
72 |
73 | onNextClick = () => {
74 | const { cursor } = this.props.data.swapi.allFilms.edges.slice(0).pop();
75 | this.setState({ before: null, after: cursor, first: limit, last: null }, this.update);
76 | }
77 |
78 | renderFilm({ node: film }) {
79 | return (
80 |
81 | {film.title}
82 |
83 | );
84 | }
85 |
86 | render() {
87 | const { data } = this.props;
88 | const d = data.swapi.allFilms.edges.length ? atob(data.swapi.allFilms.edges[0].cursor).split(':') : [];
89 | const index = Number(d.length ? d[1] : -1);
90 | return (
91 |
92 |
93 |
94 |
95 | List of movies
96 | {this.state.loading && }
97 |
98 | {data.swapi.allFilms.edges.map(this.renderFilm)}
99 | Prev
100 | Next
101 |
102 | );
103 | }
104 | }
105 |
106 | export default withGraphql(IndexPage);
107 |
--------------------------------------------------------------------------------
/packages/gatsby-source-graphql-universal/scripts/patches/babel-plugin-remove-graphql-queries.patch:
--------------------------------------------------------------------------------
1 | From 5415fd3a335fedf0fd6dfb8b05a16d6c7016bf63 Mon Sep 17 00:00:00 2001
2 | From: Birkir Gudjonsson
3 | Date: Thu, 3 Jan 2019 00:56:20 -0500
4 | Subject: Change babel-plugin-remove-graphql-queries
5 |
6 | ---
7 | .../src/index.js | 39 ++++++++++++++++---
8 | 1 file changed, 33 insertions(+), 6 deletions(-)
9 |
10 | diff --git a/packages/babel-plugin-remove-graphql-queries/src/index.js b/packages/babel-plugin-remove-graphql-queries/src/index.js
11 | index fd771c8..f94dd6d 100644
12 | --- a/packages/babel-plugin-remove-graphql-queries/src/index.js
13 | +++ b/packages/babel-plugin-remove-graphql-queries/src/index.js
14 | @@ -1,11 +1,34 @@
15 | /* eslint-disable new-cap */
16 | const graphql = require(`gatsby/graphql`)
17 | -const murmurhash = require(`./murmur`)
18 | +const murmurhash = require(`babel-plugin-remove-graphql-queries/murmur`)
19 | const nodePath = require(`path`)
20 |
21 | const isGlobalIdentifier = tag =>
22 | tag.isIdentifier({ name: `graphql` }) && tag.scope.hasGlobal(`graphql`)
23 |
24 | +function getGraphqlExpr(t, queryHash, source) {
25 | + return t.objectExpression([
26 | + t.objectProperty(
27 | + t.identifier('id'),
28 | + t.stringLiteral(queryHash)
29 | + ),
30 | + t.objectProperty(
31 | + t.identifier('source'),
32 | + t.stringLiteral(source)
33 | + ),
34 | + t.objectMethod(
35 | + 'method',
36 | + t.identifier('toString'),
37 | + [],
38 | + t.blockStatement([
39 | + t.returnStatement(
40 | + t.memberExpression(t.identifier('this'), t.identifier('id'))
41 | + )
42 | + ])
43 | + )
44 | + ])
45 | +}
46 | +
47 | function getTagImport(tag) {
48 | const name = tag.node.name
49 | const binding = tag.scope.getBinding(name)
50 | @@ -77,11 +100,11 @@ function removeImport(tag) {
51 | }
52 | if (importPath.isObjectProperty()) {
53 | if (parent.node.properties.length === 1)
54 | - importPath.findParent(p => p.isVariableDeclaration())?.remove()
55 | + importPath.findParent(p => p.isVariableDeclaration()).remove()
56 | else importPath.remove()
57 | }
58 | if (importPath.isIdentifier()) {
59 | - importPath.findParent(p => p.isVariableDeclaration())?.remove()
60 | + importPath.findParent(p => p.isVariableDeclaration()).remove()
61 | }
62 | }
63 |
64 | @@ -181,7 +204,9 @@ export default function({ types: t }) {
65 | }
66 |
67 | // Replace the query with the hash of the query.
68 | - templatePath.replaceWith(t.StringLiteral(queryHash))
69 | + templatePath.replaceWith(
70 | + getGraphqlExpr(t, queryHash, text)
71 | + )
72 |
73 | // modify StaticQuery elements and import data only if query is inside StaticQuery
74 | templatePath.parentPath.parentPath.parentPath.traverse(
75 | @@ -244,7 +269,7 @@ export default function({ types: t }) {
76 | path.traverse({
77 | // Run it again to remove non-staticquery versions
78 | TaggedTemplateExpression(path2, state) {
79 | - const { ast, hash, isGlobal } = getGraphQLTag(path2)
80 | + const { ast, text, hash, isGlobal } = getGraphQLTag(path2)
81 |
82 | if (!ast) return null
83 |
84 | @@ -258,7 +283,9 @@ export default function({ types: t }) {
85 | }
86 |
87 | // Replace the query with the hash of the query.
88 | - path2.replaceWith(t.StringLiteral(queryHash))
89 | + path2.replaceWith(
90 | + getGraphqlExpr(t, queryHash, text)
91 | + )
92 | return null
93 | },
94 | })
95 | --
96 | 2.17.2 (Apple Git-113)
97 |
98 |
--------------------------------------------------------------------------------
/packages/gatsby-source-graphql-universal/dist/third-party/gatsby-node.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | // mirror off gatsby-source-graphql
4 | // sigh...
5 | const uuidv4 = require(`uuid/v4`);
6 |
7 | const {
8 | buildSchema,
9 | printSchema
10 | } = require(`gatsby/graphql`);
11 |
12 | const {
13 | wrapSchema,
14 | introspectSchema,
15 | RenameTypes
16 | } = require(`graphql-tools`);
17 |
18 | const {
19 | createHttpLink
20 | } = require(`apollo-link-http`);
21 |
22 | const nodeFetch = require(`node-fetch`);
23 |
24 | const invariant = require(`invariant`);
25 |
26 | const {
27 | createDataloaderLink
28 | } = require(`gatsby-source-graphql/batching/dataloader-link`);
29 |
30 | const {
31 | NamespaceUnderFieldTransform,
32 | StripNonQueryTransform
33 | } = require(`gatsby-source-graphql/transforms`);
34 |
35 | const {
36 | linkToExecutor
37 | } = require(`@graphql-tools/links`);
38 |
39 | exports.sourceNodes = async ({
40 | actions,
41 | createNodeId,
42 | cache,
43 | createContentDigest
44 | }, options) => {
45 | const {
46 | addThirdPartySchema,
47 | createNode
48 | } = actions;
49 | const {
50 | url,
51 | typeName,
52 | fieldName,
53 | headers = {},
54 | fetch = nodeFetch,
55 | fetchOptions = {},
56 | createLink,
57 | createSchema,
58 | refetchInterval,
59 | batch = false
60 | } = options;
61 | invariant(typeName && typeName.length > 0, `gatsby-source-graphql requires option \`typeName\` to be specified`);
62 | invariant(fieldName && fieldName.length > 0, `gatsby-source-graphql requires option \`fieldName\` to be specified`);
63 | invariant(url && url.length > 0 || createLink, `gatsby-source-graphql requires either option \`url\` or \`createLink\` callback`);
64 | let link;
65 |
66 | if (createLink) {
67 | link = await createLink(options);
68 | } else {
69 | const options = {
70 | uri: url,
71 | fetch,
72 | fetchOptions,
73 | headers: typeof headers === `function` ? await headers() : headers
74 | };
75 | link = batch ? createDataloaderLink(options) : createHttpLink(options);
76 | }
77 |
78 | let introspectionSchema;
79 |
80 | if (createSchema) {
81 | introspectionSchema = await createSchema(options);
82 | } else {
83 | const cacheKey = `gatsby-source-graphql-schema-${typeName}-${fieldName}`;
84 | let sdl = await cache.get(cacheKey);
85 |
86 | if (!sdl) {
87 | introspectionSchema = await introspectSchema(linkToExecutor(link));
88 | sdl = printSchema(introspectionSchema);
89 | } else {
90 | introspectionSchema = buildSchema(sdl);
91 | }
92 |
93 | await cache.set(cacheKey, sdl);
94 | }
95 |
96 | const nodeId = createNodeId(`gatsby-source-graphql-${typeName}`);
97 | const node = createSchemaNode({
98 | id: nodeId,
99 | typeName,
100 | fieldName,
101 | createContentDigest
102 | });
103 | createNode(node);
104 |
105 | const resolver = (parent, args, context) => {
106 | context.nodeModel.createPageDependency({
107 | path: context.path,
108 | nodeId: nodeId
109 | });
110 | return {};
111 | };
112 |
113 | const schema = wrapSchema({
114 | schema: introspectionSchema,
115 | link,
116 | executor: linkToExecutor(link)
117 | }, [new StripNonQueryTransform(), new RenameTypes(name => `${typeName}_${name}`), new NamespaceUnderFieldTransform({
118 | typeName,
119 | fieldName,
120 | resolver
121 | })]);
122 | addThirdPartySchema({
123 | schema
124 | });
125 |
126 | if (process.env.NODE_ENV !== `production`) {
127 | if (refetchInterval) {
128 | const msRefetchInterval = refetchInterval * 1000;
129 |
130 | const refetcher = () => {
131 | createNode(createSchemaNode({
132 | id: nodeId,
133 | typeName,
134 | fieldName,
135 | createContentDigest
136 | }));
137 | setTimeout(refetcher, msRefetchInterval);
138 | };
139 |
140 | setTimeout(refetcher, msRefetchInterval);
141 | }
142 | }
143 | };
144 |
145 | function createSchemaNode({
146 | id,
147 | typeName,
148 | fieldName,
149 | createContentDigest
150 | }) {
151 | const nodeContent = uuidv4();
152 | const nodeContentDigest = createContentDigest(nodeContent);
153 | return {
154 | id,
155 | typeName: typeName,
156 | fieldName: fieldName,
157 | parent: null,
158 | children: [],
159 | internal: {
160 | type: `GraphQLSource_${typeName}`,
161 | contentDigest: nodeContentDigest,
162 | ignoreType: true
163 | }
164 | };
165 | }
--------------------------------------------------------------------------------
/packages/gatsby-source-graphql-universal/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import gql from 'graphql-tag';
3 | import ApolloClient from 'apollo-boost';
4 | import traverse from 'traverse';
5 | import cloneDeep from 'lodash.clonedeep';
6 | import { StaticQuery } from 'gatsby';
7 | import PropTypes from 'prop-types';
8 |
9 | // Allow string OR patched queries format (in development)
10 | if (StaticQuery && typeof StaticQuery === 'object' && StaticQuery.propTypes) {
11 | StaticQuery.propTypes.query = PropTypes.oneOfType([
12 | PropTypes.string,
13 | PropTypes.shape({
14 | id: PropTypes.string.isRequired,
15 | source: PropTypes.string.isRequired,
16 | })
17 | ]).isRequired;
18 | }
19 |
20 | const options = new Map();
21 |
22 | export const getOptions = (name) => {
23 | if (!options.has(name)) {
24 | if (typeof window !== 'undefined') {
25 | setOptions(name, window.___graphqlUniversal[name])
26 | }
27 | }
28 |
29 | return options.get(name);
30 | }
31 |
32 | export const setOptions = (name, opts) => {
33 | if (!opts) {
34 | throw new Error('GraphQL Universal: No options "' + name + '".');
35 | }
36 | if (!opts.client && !opts.url) {
37 | throw new Error('GraphQL Universal: Could not get "url" for "' + name + '".');
38 | }
39 | if (!opts.typeName) {
40 | throw new Error('GraphQL Universal: Could not get "typeName" for "' + name + '".');
41 | }
42 | if (!opts.client) {
43 | opts.client = new ApolloClient({
44 | uri: opts.url,
45 | headers: opts.headers,
46 | });
47 | }
48 | options.set(name, opts);
49 | }
50 |
51 | const getQuery = query => {
52 | if (typeof query === 'object' && query.definitions) {
53 | return query;
54 | } else if (typeof query === 'string') {
55 | return gql(query);
56 | } else if (typeof query === 'object' && query.source) {
57 | return gql(query.source);
58 | } else {
59 | throw new Error('Could not parse query: ' + query);
60 | }
61 | }
62 |
63 | export const getIsolatedQuery = (querySource, fieldName, typeName) => {
64 |
65 | const query = getQuery(querySource);
66 | const updatedQuery = cloneDeep(query);
67 |
68 | const updatedRoot = updatedQuery.definitions[0].selectionSet.selections
69 | .find(selection => selection.name && selection.name.kind === 'Name' && selection.name.value === fieldName);
70 |
71 | if (updatedRoot) {
72 | updatedQuery.definitions[0].selectionSet.selections = updatedRoot.selectionSet.selections;
73 | } else if (fieldName) {
74 | console.warn('Failed to update query root');
75 | return;
76 | }
77 |
78 | traverse(updatedQuery).forEach(function (x) {
79 | if (this.isLeaf && this.parent && this.parent.key === 'name') {
80 | if (this.parent.parent && this.parent.parent.node.kind === 'NamedType') {
81 | if (typeof x === 'string' && x.indexOf(`${typeName}_`) === 0) {
82 | this.update(x.substr(typeName.length + 1));
83 | }
84 | }
85 | }
86 | });
87 |
88 | return updatedQuery;
89 | }
90 |
91 | export const withGraphql = WrappedComponent => {
92 | return class extends React.Component {
93 |
94 | state = {
95 | data: this.props.data,
96 | }
97 |
98 | graphql = (fieldName, { query, client, fragments = [], composeData = true, ...queryProps }) => {
99 | // Get options for graphql source plugin
100 | const options = getOptions(fieldName);
101 |
102 | if (!client && (!options || !options.client)) {
103 | if (typeof window === 'undefined') {
104 | console.warn('GraphQL Universal: Options cannot be passed to plugin on server');
105 | } else {
106 | console.warn('GraphQL Universal: No options found for plugin "' + fieldName + '"');
107 | }
108 | return;
109 | }
110 |
111 | const { typeName } = options;
112 | const apolloClient = client || options.client;
113 |
114 | const updatedQuery = getIsolatedQuery(query, fieldName, typeName);
115 |
116 | updatedQuery.definitions = updatedQuery.definitions.concat(
117 | ...fragments.map(fragment => getIsolatedQuery(fragment, null, typeName).definitions)
118 | );
119 |
120 | const rootValue = (this.state.data && this.state.data[fieldName]) || {};
121 |
122 | const res = apolloClient.query({
123 | query: updatedQuery,
124 | fetchPolicy: 'network-only',
125 | ...queryProps
126 | });
127 |
128 | if (composeData) {
129 | return res.then(res => {
130 | this.setState({
131 | data: {
132 | ...this.state.data,
133 | [fieldName]: {
134 | ...rootValue,
135 | ...res.data,
136 | },
137 | },
138 | });
139 | return res;
140 | });
141 | }
142 |
143 | return res;
144 | }
145 |
146 | render() {
147 | return ;
152 | }
153 | };
154 | };
155 |
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Gatsby's default starter
9 |
10 |
11 | Kick off your project with this default boilerplate. This starter ships with the main Gatsby configuration files you might need to get up and running blazing fast with the blazing fast app generator for React.
12 |
13 | _Have another more specific idea? You may want to check out our vibrant collection of [official and community-created starters](https://www.gatsbyjs.org/docs/gatsby-starters/)._
14 |
15 | ## 🚀 Quick start
16 |
17 | 1. **Create a Gatsby site.**
18 |
19 | Use the Gatsby CLI to create a new site, specifying the default starter.
20 |
21 | ```sh
22 | # create a new Gatsby site using the default starter
23 | npx gatsby new my-default-starter https://github.com/gatsbyjs/gatsby-starter-default
24 | ```
25 |
26 | 1. **Start developing.**
27 |
28 | Navigate into your new site’s directory and start it up.
29 |
30 | ```sh
31 | cd my-default-starter/
32 | gatsby develop
33 | ```
34 |
35 | 1. **Open the source code and start editing!**
36 |
37 | Your site is now running at `http://localhost:8000`!
38 |
39 | _Note: You'll also see a second link: _`http://localhost:8000/___graphql`_. This is a tool you can use to experiment with querying your data. Learn more about using this tool in the [Gatsby tutorial](https://www.gatsbyjs.org/tutorial/part-five/#introducing-graphiql)._
40 |
41 | Open the `my-default-starter` directory in your code editor of choice and edit `src/pages/index.js`. Save your changes and the browser will update in real time!
42 |
43 | ## 🧐 What's inside?
44 |
45 | A quick look at the top-level files and directories you'll see in a Gatsby project.
46 |
47 | .
48 | ├── node_modules
49 | ├── src
50 | ├── .gitignore
51 | ├── .prettierrc
52 | ├── gatsby-browser.js
53 | ├── gatsby-config.js
54 | ├── gatsby-node.js
55 | ├── gatsby-ssr.js
56 | ├── LICENSE
57 | ├── package-lock.json
58 | ├── package.json
59 | └── README.md
60 |
61 | 1. **`/node_modules`**: This directory contains all of the modules of code that your project depends on (npm packages) are automatically installed.
62 |
63 | 2. **`/src`**: This directory will contain all of the code related to what you will see on the front-end of your site (what you see in the browser) such as your site header or a page template. `src` is a convention for “source code”.
64 |
65 | 3. **`.gitignore`**: This file tells git which files it should not track / not maintain a version history for.
66 |
67 | 4. **`.prettierrc`**: This is a configuration file for [Prettier](https://prettier.io/). Prettier is a tool to help keep the formatting of your code consistent.
68 |
69 | 5. **`gatsby-browser.js`**: This file is where Gatsby expects to find any usage of the [Gatsby browser APIs](https://www.gatsbyjs.org/docs/browser-apis/) (if any). These allow customization/extension of default Gatsby settings affecting the browser.
70 |
71 | 6. **`gatsby-config.js`**: This is the main configuration file for a Gatsby site. This is where you can specify information about your site (metadata) like the site title and description, which Gatsby plugins you’d like to include, etc. (Check out the [config docs](https://www.gatsbyjs.org/docs/gatsby-config/) for more detail).
72 |
73 | 7. **`gatsby-node.js`**: This file is where Gatsby expects to find any usage of the [Gatsby Node APIs](https://www.gatsbyjs.org/docs/node-apis/) (if any). These allow customization/extension of default Gatsby settings affecting pieces of the site build process.
74 |
75 | 8. **`gatsby-ssr.js`**: This file is where Gatsby expects to find any usage of the [Gatsby server-side rendering APIs](https://www.gatsbyjs.org/docs/ssr-apis/) (if any). These allow customization of default Gatsby settings affecting server-side rendering.
76 |
77 | 9. **`LICENSE`**: Gatsby is licensed under the MIT license.
78 |
79 | 10. **`package-lock.json`** (See `package.json` below, first). This is an automatically generated file based on the exact versions of your npm dependencies that were installed for your project. **(You won’t change this file directly).**
80 |
81 | 11. **`package.json`**: A manifest file for Node.js projects, which includes things like metadata (the project’s name, author, etc). This manifest is how npm knows which packages to install for your project.
82 |
83 | 12. **`README.md`**: A text file containing useful reference information about your project.
84 |
85 | ## 🎓 Learning Gatsby
86 |
87 | Looking for more guidance? Full documentation for Gatsby lives [on the website](https://www.gatsbyjs.org/). Here are some places to start:
88 |
89 | - **For most developers, we recommend starting with our [in-depth tutorial for creating a site with Gatsby](https://www.gatsbyjs.org/tutorial/).** It starts with zero assumptions about your level of ability and walks through every step of the process.
90 |
91 | - **To dive straight into code samples, head [to our documentation](https://www.gatsbyjs.org/docs/).** In particular, check out the _Guides_, _API Reference_, and _Advanced Tutorials_ sections in the sidebar.
92 |
93 | ## 💫 Deploy
94 |
95 | [](https://app.netlify.com/start/deploy?repository=https://github.com/gatsbyjs/gatsby-starter-default)
96 |
97 |
98 |
--------------------------------------------------------------------------------
/packages/gatsby-source-graphql-universal/dist/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4 |
5 | exports.__esModule = true;
6 | exports.withGraphql = exports.getIsolatedQuery = exports.setOptions = exports.getOptions = void 0;
7 |
8 | var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
9 |
10 | var _react = _interopRequireDefault(require("react"));
11 |
12 | var _graphqlTag = _interopRequireDefault(require("graphql-tag"));
13 |
14 | var _apolloBoost = _interopRequireDefault(require("apollo-boost"));
15 |
16 | var _traverse = _interopRequireDefault(require("traverse"));
17 |
18 | var _lodash = _interopRequireDefault(require("lodash.clonedeep"));
19 |
20 | var _gatsby = require("gatsby");
21 |
22 | var _propTypes = _interopRequireDefault(require("prop-types"));
23 |
24 | // Allow string OR patched queries format (in development)
25 | if (_gatsby.StaticQuery && typeof _gatsby.StaticQuery === 'object' && _gatsby.StaticQuery.propTypes) {
26 | _gatsby.StaticQuery.propTypes.query = _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.shape({
27 | id: _propTypes.default.string.isRequired,
28 | source: _propTypes.default.string.isRequired
29 | })]).isRequired;
30 | }
31 |
32 | const options = new Map();
33 |
34 | const getOptions = name => {
35 | if (!options.has(name)) {
36 | if (typeof window !== 'undefined') {
37 | setOptions(name, window.___graphqlUniversal[name]);
38 | }
39 | }
40 |
41 | return options.get(name);
42 | };
43 |
44 | exports.getOptions = getOptions;
45 |
46 | const setOptions = (name, opts) => {
47 | if (!opts) {
48 | throw new Error('GraphQL Universal: No options "' + name + '".');
49 | }
50 |
51 | if (!opts.client && !opts.url) {
52 | throw new Error('GraphQL Universal: Could not get "url" for "' + name + '".');
53 | }
54 |
55 | if (!opts.typeName) {
56 | throw new Error('GraphQL Universal: Could not get "typeName" for "' + name + '".');
57 | }
58 |
59 | if (!opts.client) {
60 | opts.client = new _apolloBoost.default({
61 | uri: opts.url,
62 | headers: opts.headers
63 | });
64 | }
65 |
66 | options.set(name, opts);
67 | };
68 |
69 | exports.setOptions = setOptions;
70 |
71 | const getQuery = query => {
72 | if (typeof query === 'object' && query.definitions) {
73 | return query;
74 | } else if (typeof query === 'string') {
75 | return (0, _graphqlTag.default)(query);
76 | } else if (typeof query === 'object' && query.source) {
77 | return (0, _graphqlTag.default)(query.source);
78 | } else {
79 | throw new Error('Could not parse query: ' + query);
80 | }
81 | };
82 |
83 | const getIsolatedQuery = (querySource, fieldName, typeName) => {
84 | const query = getQuery(querySource);
85 | const updatedQuery = (0, _lodash.default)(query);
86 | const updatedRoot = updatedQuery.definitions[0].selectionSet.selections.find(selection => selection.name && selection.name.kind === 'Name' && selection.name.value === fieldName);
87 |
88 | if (updatedRoot) {
89 | updatedQuery.definitions[0].selectionSet.selections = updatedRoot.selectionSet.selections;
90 | } else if (fieldName) {
91 | console.warn('Failed to update query root');
92 | return;
93 | }
94 |
95 | (0, _traverse.default)(updatedQuery).forEach(function (x) {
96 | if (this.isLeaf && this.parent && this.parent.key === 'name') {
97 | if (this.parent.parent && this.parent.parent.node.kind === 'NamedType') {
98 | if (typeof x === 'string' && x.indexOf(`${typeName}_`) === 0) {
99 | this.update(x.substr(typeName.length + 1));
100 | }
101 | }
102 | }
103 | });
104 | return updatedQuery;
105 | };
106 |
107 | exports.getIsolatedQuery = getIsolatedQuery;
108 |
109 | const withGraphql = WrappedComponent => {
110 | var _temp;
111 |
112 | return _temp = class extends _react.default.Component {
113 | constructor(...args) {
114 | super(...args);
115 | this.state = {
116 | data: this.props.data
117 | };
118 |
119 | this.graphql = (fieldName, {
120 | query,
121 | client,
122 | fragments = [],
123 | composeData = true,
124 | ...queryProps
125 | }) => {
126 | // Get options for graphql source plugin
127 | const options = getOptions(fieldName);
128 |
129 | if (!client && (!options || !options.client)) {
130 | if (typeof window === 'undefined') {
131 | console.warn('GraphQL Universal: Options cannot be passed to plugin on server');
132 | } else {
133 | console.warn('GraphQL Universal: No options found for plugin "' + fieldName + '"');
134 | }
135 |
136 | return;
137 | }
138 |
139 | const {
140 | typeName
141 | } = options;
142 | const apolloClient = client || options.client;
143 | const updatedQuery = getIsolatedQuery(query, fieldName, typeName);
144 | updatedQuery.definitions = updatedQuery.definitions.concat(...fragments.map(fragment => getIsolatedQuery(fragment, null, typeName).definitions));
145 | const rootValue = this.state.data && this.state.data[fieldName] || {};
146 | const res = apolloClient.query({
147 | query: updatedQuery,
148 | fetchPolicy: 'network-only',
149 | ...queryProps
150 | });
151 |
152 | if (composeData) {
153 | return res.then(res => {
154 | this.setState({
155 | data: { ...this.state.data,
156 | [fieldName]: { ...rootValue,
157 | ...res.data
158 | }
159 | }
160 | });
161 | return res;
162 | });
163 | }
164 |
165 | return res;
166 | };
167 | }
168 |
169 | render() {
170 | return /*#__PURE__*/_react.default.createElement(WrappedComponent, (0, _extends2.default)({}, this.props, {
171 | graphql: this.graphql,
172 | data: this.state.data
173 | }));
174 | }
175 |
176 | }, _temp;
177 | };
178 |
179 | exports.withGraphql = withGraphql;
--------------------------------------------------------------------------------
/packages/gatsby-source-graphql-universal/jest.config.js:
--------------------------------------------------------------------------------
1 | // For a detailed explanation regarding each configuration property, visit:
2 | // https://jestjs.io/docs/en/configuration.html
3 |
4 | module.exports = {
5 | // All imported modules in your tests should be mocked automatically
6 | // automock: false,
7 |
8 | // Stop running tests after `n` failures
9 | // bail: 0,
10 |
11 | // The directory where Jest should store its cached dependency information
12 | // cacheDirectory: "/private/var/folders/h7/kxnzf6ss2_bgs3_n3f2t29z80000gn/T/jest_dx",
13 |
14 | // Automatically clear mock calls and instances between every test
15 | // clearMocks: false,
16 |
17 | // Indicates whether the coverage information should be collected while executing the test
18 | // collectCoverage: false,
19 |
20 | // An array of glob patterns indicating a set of files for which coverage information should be collected
21 | // collectCoverageFrom: undefined,
22 |
23 | // The directory where Jest should output its coverage files
24 | // coverageDirectory: undefined,
25 |
26 | // An array of regexp pattern strings used to skip coverage collection
27 | // coveragePathIgnorePatterns: [
28 | // "/node_modules/"
29 | // ],
30 |
31 | // Indicates which provider should be used to instrument code for coverage
32 | // coverageProvider: "babel",
33 |
34 | // A list of reporter names that Jest uses when writing coverage reports
35 | // coverageReporters: [
36 | // "json",
37 | // "text",
38 | // "lcov",
39 | // "clover"
40 | // ],
41 |
42 | // An object that configures minimum threshold enforcement for coverage results
43 | // coverageThreshold: undefined,
44 |
45 | // A path to a custom dependency extractor
46 | // dependencyExtractor: undefined,
47 |
48 | // Make calling deprecated APIs throw helpful error messages
49 | // errorOnDeprecated: false,
50 |
51 | // Force coverage collection from ignored files using an array of glob patterns
52 | // forceCoverageMatch: [],
53 |
54 | // A path to a module which exports an async function that is triggered once before all test suites
55 | // globalSetup: undefined,
56 |
57 | // A path to a module which exports an async function that is triggered once after all test suites
58 | // globalTeardown: undefined,
59 |
60 | // A set of global variables that need to be available in all test environments
61 | // globals: {},
62 |
63 | // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.
64 | // maxWorkers: "50%",
65 |
66 | // An array of directory names to be searched recursively up from the requiring module's location
67 | // moduleDirectories: [
68 | // "node_modules"
69 | // ],
70 |
71 | // An array of file extensions your modules use
72 | // moduleFileExtensions: [
73 | // "js",
74 | // "json",
75 | // "jsx",
76 | // "ts",
77 | // "tsx",
78 | // "node"
79 | // ],
80 |
81 | // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
82 | // moduleNameMapper: {},
83 |
84 | // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
85 | // modulePathIgnorePatterns: [],
86 |
87 | // Activates notifications for test results
88 | // notify: false,
89 |
90 | // An enum that specifies notification mode. Requires { notify: true }
91 | // notifyMode: "failure-change",
92 |
93 | // A preset that is used as a base for Jest's configuration
94 | // preset: undefined,
95 |
96 | // Run tests from one or more projects
97 | // projects: undefined,
98 |
99 | // Use this configuration option to add custom reporters to Jest
100 | // reporters: undefined,
101 |
102 | // Automatically reset mock state between every test
103 | // resetMocks: false,
104 |
105 | // Reset the module registry before running each individual test
106 | // resetModules: false,
107 |
108 | // A path to a custom resolver
109 | // resolver: undefined,
110 |
111 | // Automatically restore mock state between every test
112 | // restoreMocks: false,
113 |
114 | // The root directory that Jest should scan for tests and modules within
115 | // rootDir: undefined,
116 |
117 | // A list of paths to directories that Jest should use to search for files in
118 | // roots: [
119 | // ""
120 | // ],
121 |
122 | // Allows you to use a custom runner instead of Jest's default test runner
123 | // runner: "jest-runner",
124 |
125 | // The paths to modules that run some code to configure or set up the testing environment before each test
126 | // setupFiles: [],
127 |
128 | // A list of paths to modules that run some code to configure or set up the testing framework before each test
129 | // setupFilesAfterEnv: [],
130 |
131 | // A list of paths to snapshot serializer modules Jest should use for snapshot testing
132 | // snapshotSerializers: [],
133 |
134 | // The test environment that will be used for testing
135 | // testEnvironment: "jest-environment-jsdom",
136 |
137 | // Options that will be passed to the testEnvironment
138 | // testEnvironmentOptions: {},
139 |
140 | // Adds a location field to test results
141 | // testLocationInResults: false,
142 |
143 | // The glob patterns Jest uses to detect test files
144 | // testMatch: [
145 | // "**/__tests__/**/*.[jt]s?(x)",
146 | // "**/?(*.)+(spec|test).[tj]s?(x)"
147 | // ],
148 |
149 | // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
150 | // testPathIgnorePatterns: [
151 | // "/node_modules/"
152 | // ],
153 |
154 | // The regexp pattern or array of patterns that Jest uses to detect test files
155 | // testRegex: [],
156 |
157 | // This option allows the use of a custom results processor
158 | // testResultsProcessor: undefined,
159 |
160 | // This option allows use of a custom test runner
161 | // testRunner: "jasmine2",
162 |
163 | // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href
164 | // testURL: "http://localhost",
165 |
166 | // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout"
167 | // timers: "real",
168 |
169 | // A map from regular expressions to paths to transformers
170 | // transform: undefined,
171 |
172 | // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
173 | // transformIgnorePatterns: [
174 | // "/node_modules/"
175 | // ],
176 |
177 | // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
178 | // unmockedModulePathPatterns: undefined,
179 |
180 | // Indicates whether each individual test should be reported during the run
181 | // verbose: undefined,
182 |
183 | // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
184 | // watchPathIgnorePatterns: [],
185 |
186 | // Whether to use watchman for file crawling
187 | // watchman: true,
188 | };
189 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # gatsby-source-graphql-universal
2 |
3 | > NOTE: This is an universal version of the official `gatsby-source-graphql` source plugin. It modifies the babel plugins to skip the removal of graphql queries so they can be re-used.
4 |
5 | ## How to use
6 |
7 | The plugin provides higher order component as well as direct manipulation tools for custom operations
8 |
9 | [See TypeScript definitions for more details](/index.d.ts)
10 |
11 |
12 | ### Higher-Order Component
13 |
14 | There is a higher order component to wrap components to get access to graphql queries in the browser.
15 |
16 | ```jsx
17 | import { graphql } from 'gatsby';
18 | import { withGraphql } from '@prismicio/gatsby-source-graphql-universal';
19 |
20 | export const fooFragment = graphql`
21 | fragment Planet on ...SWAPI_Planet {
22 | id
23 | title
24 | }
25 | `
26 |
27 | export const query = graphql`
28 | query {
29 | swapi {
30 | ...
31 | }
32 | }
33 | `;
34 |
35 | export const Demo = withGraphql(
36 | ({ data, graphql }) => {
37 | const onClick = () => graphql('swapi', {
38 | query,
39 | fragments: [fooFragment],
40 | fetchPolicy: 'network-only',
41 | variables: { page: 3 }
42 | });
43 |
44 | return (
45 | Reload
46 | );
47 | }
48 | );
49 | ```
50 |
51 | #### Props
52 |
53 | - **`data`**: Same as data from gatsby, but when `graphql()` (below) is called, it will be overwritten with new data when `composeData` prop is set to true.
54 |
55 | - **`graphql(fieldName, options): Promise`**
56 | - **`fieldName`**: the same fieldName as provided in gatsby-config.js
57 | - **`options.query`**: the query variable defined above the component
58 | - **`options.fragments`**: list of fragments to inject into the query
59 | - **`options.composeData`**: _(default: true)_ will overwrite component gatsby data with composed data from the browser when true
60 | - **`...options`** optional parameters to pass to `ApolloClient.query` (sets fetchPolicy to 'network-only' by default)
61 |
62 |
63 | ### getIsolatedQuery
64 |
65 | The following code will now result in an object that has the original graphql query source accessible where you are free to do anything with it.
66 |
67 | ```js
68 | const query = graphql\`...\`;
69 | ```
70 |
71 | ```json
72 | {
73 | "id": "1234567",
74 | "source": "{ \"swapi\": { ... } }"
75 | }
76 | ```
77 |
78 | You can get isolated query to your graphql endpoint by re-using the composing function:
79 |
80 | ```js
81 | import { graphql } from 'gatsby';
82 | import { getIsolatedQuery } from '@prismicio/gatsby-source-graphql-universal';
83 |
84 | const query = gatsby`
85 | query {
86 | siteMetadata {
87 | title
88 | }
89 | swapi {
90 | allPersons {
91 | ... on SWAPI_Person {
92 | id
93 | }
94 | }
95 | }
96 | }
97 | `;
98 |
99 | const onlySwapi = getIsolatedQuery(query, 'swapi', 'SWAPI');
100 |
101 | // Output:
102 | //
103 | // query {
104 | // allPersons {
105 | // ... on Person {
106 | // id
107 | // }
108 | // }
109 | // }
110 | ```
111 |
112 |
113 |
114 | ---
115 |
116 | ---
117 |
118 | ### gatsby-source-graphql (previous documentation)
119 |
120 | Plugin for connecting arbitrary GraphQL APIs to Gatsby GraphQL. Remote schemas are stitched together by adding a type that wraps the remote schema
121 | Query type and putting it under field of Gatsby GraphQL Query.
122 |
123 | - [Example website](https://using-gatsby-source-graphql.netlify.com/)
124 | - [Example website source](https://github.com/gatsbyjs/gatsby/tree/master/examples/using-gatsby-source-graphql)
125 |
126 |
127 | ## Install
128 |
129 | `npm install --save @prismicio/gatsby-source-graphql-universal`
130 |
131 | ## How to use
132 |
133 | First, you need a way to pass environment variables to the build process, so secrets and other secured data aren't committed to source control. We
134 | recommend using [`dotenv`][dotenv] which will then expose environment variables. [Read more about dotenv and using environment variables
135 | here][envvars]. Then we can _use_ these environment variables and configure our plugin.
136 |
137 | ```javascript
138 | // In your gatsby-config.js
139 | module.exports = {
140 | plugins: [
141 | // Simple config, passing URL
142 | {
143 | resolve: "@prismicio/gatsby-source-graphql-universal",
144 | options: {
145 | // This type will contain remote schema Query type
146 | typeName: "SWAPI",
147 | // This is field under which it's accessible
148 | fieldName: "swapi",
149 | // Url to query from
150 | url: "https://api.graphcms.com/simple/v1/swapi",
151 | },
152 | },
153 | // Passing paramaters (passed to apollo-link)
154 | {
155 | resolve: "@prismicio/gatsby-source-graphql-universal",
156 | options: {
157 | typeName: "GitHub",
158 | fieldName: "github",
159 | // Url to query from
160 | url: "https://api.github.com/graphql",
161 | // HTTP headers
162 | headers: {
163 | // Learn about environment variables: https://gatsby.app/env-vars
164 | Authorization: `bearer ${process.env.GITHUB_TOKEN}`,
165 | },
166 | // Additional options to pass to node-fetch
167 | fetchOptions: {},
168 | },
169 | },
170 | // Creating arbitrary Apollo Link (for advanced situations)
171 | {
172 | resolve: "@prismicio/gatsby-source-graphql-universal",
173 | options: {
174 | typeName: "GitHub",
175 | fieldName: "github",
176 | // Create Apollo Link manually. Can return a Promise.
177 | createLink: (pluginOptions) => {
178 | return createHttpLink({
179 | uri: 'https://api.github.com/graphql',
180 | headers: {
181 | 'Authorization': `bearer ${process.env.GITHUB_TOKEN}`,
182 | },
183 | fetch,
184 | })
185 | },
186 | },
187 | ],
188 | }
189 | ```
190 |
191 | ## How to Query
192 |
193 | ```graphql
194 | {
195 | # Field name parameter defines how you can access third party api
196 | swapi {
197 | allSpecies {
198 | name
199 | }
200 | }
201 | github {
202 | viewer {
203 | email
204 | }
205 | }
206 | }
207 | ```
208 |
209 | ## Schema definitions
210 |
211 | By default schema is introspected from the remote schema. Schema is cached in `.cache` in this case and refreshing the schema requires deleting the
212 | cache.
213 |
214 | To control schema consumption, you can alternatively construct schema definition by passing `createSchema` callback. This way you could, for
215 | example, read schema SDL or introspection JSON. When `createSchema` callback is used, schema isn't cached. `createSchema` can return a Promise to
216 | GraphQLSchema instance or GraphQLSchema instance.
217 |
218 | ```js
219 | const fs = require("fs")
220 | const { buildSchema, buildClientSchema } = require("graphql")
221 |
222 | module.exports = {
223 | plugins: [
224 | {
225 | resolve: "@prismicio/gatsby-source-graphql-universal",
226 | options: {
227 | typeName: "SWAPI",
228 | fieldName: "swapi",
229 | url: "https://api.graphcms.com/simple/v1/swapi",
230 |
231 | createSchema: async () => {
232 | const json = JSON.parse(
233 | fs.readFileSync(`${__dirname}/introspection.json`)
234 | )
235 | return buildClientSchema(json.data)
236 | },
237 | },
238 | },
239 | {
240 | resolve: "@prismicio/gatsby-source-graphql-universal",
241 | options: {
242 | typeName: "SWAPI",
243 | fieldName: "swapi",
244 | url: "https://api.graphcms.com/simple/v1/swapi",
245 |
246 | createSchema: async () => {
247 | const sdl = fs.readFileSync(`${__dirname}/schema.sdl`).toString()
248 | return buildSchema(sdl)
249 | },
250 | },
251 | },
252 | ],
253 | }
254 | ```
255 |
256 | # Refetching data
257 |
258 | By default, `@prismicio/gatsby-source-graphql-universal` will only refetch the data once the server is restarted. It's also possible to configure the plugin to
259 | periodically refetch the data. The option is called `refetchInterval` and specifies the timeout in seconds.
260 |
261 | ```js
262 | module.exports = {
263 | plugins: [
264 | // Simple config, passing URL
265 | {
266 | resolve: "@prismicio/gatsby-source-graphql-universal",
267 | options: {
268 | // This type will contain remote schema Query type
269 | typeName: "SWAPI",
270 | // This is field under which it's accessible
271 | fieldName: "swapi",
272 | // Url to query from
273 | url: "https://api.graphcms.com/simple/v1/swapi",
274 |
275 | // refetch interval in seconds
276 | refetchInterval: 60,
277 | },
278 | },
279 | ],
280 | }
281 | ```
282 |
283 | [dotenv]: https://github.com/motdotla/dotenv
284 | [envvars]: https://gatsby.app/env-vars
285 |
286 |
--------------------------------------------------------------------------------
/packages/gatsby-source-graphql-universal/__tests__/babel-plugin-remove-graphql-queries.spec.js:
--------------------------------------------------------------------------------
1 | const babel = require(`@babel/core`)
2 | const plugin = require(`../src/babel-plugin-remove-graphql-queries.js`)
3 |
4 | function matchesSnapshot(query) {
5 | const { code } = babel.transform(query, {
6 | presets: [`@babel/preset-react`],
7 | plugins: [plugin],
8 | })
9 | expect(code).toMatchSnapshot()
10 | }
11 |
12 | it.todo(
13 | `Works correctly with the kitchen sink`
14 | // , () => {
15 | // matchesSnapshot(`
16 | // import * as React from 'react'
17 | // import { graphql, useStaticQuery, StaticQuery } from 'gatsby'
18 |
19 | // export default () => {
20 | // const query = graphql\`{site { siteMetadata { title }}}\`
21 | // const siteDescription = useStaticQuery(query)
22 |
23 | // return (
24 | // (
27 | //
28 | //
{data.site.siteMetadata.title}
29 | //
{siteDescription.site.siteMetadata.description}
30 | //
31 | // )}
32 | // />
33 | // )
34 | // }
35 | // `)
36 | // }
37 | )
38 |
39 | it(`Transforms queries in useStaticQuery`, () => {
40 | matchesSnapshot(`
41 | import * as React from 'react'
42 | import { graphql, useStaticQuery } from 'gatsby'
43 |
44 | export default () => {
45 | const siteTitle = useStaticQuery(graphql\`{site { siteMetadata { title }}}\`)
46 |
47 | return (
48 | {siteTitle.site.siteMetadata.title}
49 | )
50 | }
51 | `)
52 | })
53 |
54 | it(`Transforms exported queries in useStaticQuery`, () => {
55 | matchesSnapshot(`
56 | import * as React from 'react'
57 | import { graphql, useStaticQuery } from 'gatsby'
58 |
59 | export default () => {
60 | const data = useStaticQuery(query)
61 |
62 | return (
63 | <>
64 | {data.site.siteMetadata.title}
65 | {data.site.siteMetadata.description}
66 | >
67 | )
68 | }
69 |
70 | export const query = graphql\`{site { siteMetadata { title }}}\`
71 | `)
72 | })
73 |
74 | it(`Transforms queries defined in own variable in useStaticQuery`, () => {
75 | matchesSnapshot(`
76 | import * as React from 'react'
77 | import { graphql, useStaticQuery } from 'gatsby'
78 |
79 | export default () => {
80 | const query = graphql\`{site { siteMetadata { title }}}\`
81 | const siteTitle = useStaticQuery(query)
82 |
83 | return (
84 | {siteTitle.site.siteMetadata.title}
85 | )
86 | }
87 | `)
88 | })
89 |
90 | it(`Transforms queries and preserves destructuring in useStaticQuery`, () => {
91 | matchesSnapshot(`
92 | import * as React from 'react'
93 | import { graphql, useStaticQuery } from 'gatsby'
94 |
95 | export default () => {
96 | const query = graphql\`{site { siteMetadata { title }}}\`
97 | const { site } = useStaticQuery(query)
98 |
99 | return (
100 | {site.siteMetadata.title}
101 | )
102 | }
103 | `)
104 | })
105 |
106 | it(`Transforms queries and preserves variable type in useStaticQuery`, () => {
107 | matchesSnapshot(`
108 | import * as React from 'react'
109 | import { graphql, useStaticQuery } from 'gatsby'
110 |
111 | export default () => {
112 | const query = graphql\`{site { siteMetadata { title }}}\`
113 | let { site } = useStaticQuery(query)
114 |
115 | return (
116 | {site.siteMetadata.title}
117 | )
118 | }
119 | `)
120 | })
121 |
122 | it(`Transformation does not break custom hooks`, () => {
123 | matchesSnapshot(`
124 | import React from "react"
125 | import { graphql, useStaticQuery } from "gatsby"
126 |
127 | const useSiteMetadata = () => {
128 | const data = useStaticQuery(graphql\`{site { siteMetadata { title }}}\`)
129 | return data.site.siteMetadata
130 | }
131 |
132 | export default () => {
133 | const siteMetadata = useSiteMetadata()
134 |
135 | return {site.siteMetadata.title}
136 | }
137 |
138 | `)
139 | })
140 |
141 | it(`Transforms only the call expression in useStaticQuery`, () => {
142 | matchesSnapshot(`
143 | import React from "react"
144 | import { graphql, useStaticQuery } from "gatsby"
145 |
146 | const useSiteMetadata = () => {
147 | return useStaticQuery(
148 | graphql\`{site { siteMetadata { title }}}\`
149 | ).site.siteMetadata
150 | }
151 |
152 | export default () => {
153 | const siteMetadata = useSiteMetadata()
154 |
155 | return {siteMetadata.title}
156 | }
157 | `)
158 | })
159 |
160 | it(`Only runs transforms if useStaticQuery is imported from gatsby`, () => {
161 | matchesSnapshot(`
162 | import * as React from 'react'
163 | import { graphql } from 'gatsby'
164 |
165 | export default () => {
166 | const query = graphql\`{site { siteMetadata { title }}}\`
167 | const siteTitle = useStaticQuery(query)
168 |
169 | return (
170 | {siteTitle.site.siteMetadata.title}
171 | )
172 | }
173 | `)
174 | })
175 |
176 | it(`Allow alternative import of useStaticQuery`, () => {
177 | matchesSnapshot(`
178 | import * as React from 'react'
179 | import * as Gatsby from 'gatsby'
180 |
181 | export default () => {
182 | const query = Gatsby.graphql\`{site { siteMetadata { title }}}\`
183 | const siteTitle = Gatsby.useStaticQuery(query)
184 |
185 | return (
186 | {siteTitle.site.siteMetadata.title}
187 | )
188 | }
189 | `)
190 | })
191 |
192 | it(`Transforms queries in `, () => {
193 | matchesSnapshot(`
194 | import * as React from 'react'
195 | import { graphql, StaticQuery } from 'gatsby'
196 |
197 | export default () => (
198 | {data.site.siteMetadata.title}
}
201 | />
202 | )
203 | `)
204 | })
205 |
206 | it(`Transforms queries defined in own variable in `, () => {
207 | matchesSnapshot(`
208 | import * as React from 'react'
209 | import { graphql, StaticQuery } from 'gatsby'
210 |
211 | const query = graphql\`{site { siteMetadata { title }}}\`
212 |
213 | export default () => (
214 | {data.site.siteMetadata.title}
}
217 | />
218 | )
219 | `)
220 | })
221 |
222 | it(`transforms exported variable queries in `, () => {
223 | matchesSnapshot(`
224 | import * as React from 'react'
225 | import { graphql, StaticQuery } from 'gatsby'
226 |
227 | export const query = graphql\`{site { siteMetadata { title }}}\`
228 |
229 | export default () => (
230 | {data.site.siteMetadata.title}
}
233 | />
234 | )
235 | `)
236 | })
237 |
238 | it(`Transforms queries in page components`, () => {
239 | matchesSnapshot(`
240 | import { graphql } from 'gatsby'
241 |
242 | export const query = graphql\`
243 | {
244 | site { siteMetadata { title }}
245 | }
246 | \`
247 | `)
248 | })
249 |
250 | it(`allows the global tag`, () => {
251 | matchesSnapshot(
252 | `
253 | export const query = graphql\`
254 | {
255 | site { siteMetadata { title }}
256 | }
257 | \`
258 | `
259 | )
260 | })
261 |
262 | it(`distinguishes between the right tags`, () => {
263 | matchesSnapshot(
264 | `
265 | const foo = styled('div')\`
266 | {
267 | $\{foo}
268 | }
269 | \`
270 |
271 | const pulse = keyframes\`
272 | 0% {
273 | transform: scale(1);
274 | animation-timing-function: ease-in;
275 | }
276 | 25% {
277 | animation-timing-function: ease-out;
278 | transform: scale(1.05);
279 | }
280 | 50% {
281 | transform: scale(1.12);
282 | animation-timing-function: ease-in;
283 | }
284 | to {
285 | transform: scale(1);
286 | animation-timing-function: ease-out;
287 | }
288 | \`;
289 |
290 | export const query = graphql\`
291 | {
292 | site { siteMetadata { title }}
293 | }
294 | \`
295 | `
296 | )
297 | })
298 |
299 | it(`handles import aliasing`, () => {
300 | matchesSnapshot(
301 | `
302 | import { graphql as gql } from 'gatsby'
303 |
304 | export const query = gql\`
305 | {
306 | site { siteMetadata { title }}
307 | }
308 | \`
309 | `
310 | )
311 | })
312 |
313 | it(`handles require`, () => {
314 | matchesSnapshot(
315 | `
316 | const { graphql } = require('gatsby')
317 |
318 | export const query = graphql\`
319 | {
320 | site { siteMetadata { title }}
321 | }
322 | \`
323 | `
324 | )
325 | })
326 |
327 | it(`handles require namespace`, () => {
328 | matchesSnapshot(
329 | `
330 | const Gatsby = require('gatsby')
331 |
332 | export const query = Gatsby.graphql\`
333 | {
334 | site { siteMetadata { title }}
335 | }
336 | \`
337 | `
338 | )
339 | })
340 | it(`handles require alias`, () => {
341 | matchesSnapshot(
342 | `
343 | const { graphql: gql } = require('gatsby')
344 |
345 | export const query = gql\`
346 | {
347 | site { siteMetadata { title }}
348 | }
349 | \`
350 | `
351 | )
352 | })
353 |
354 | it(`Leaves other graphql tags alone`, () => {
355 | matchesSnapshot(
356 | `
357 | import * as React from 'react'
358 | import { graphql } from 'relay'
359 |
360 | export default () => (
361 | {data.site.siteMetadata.title}
362 | )
363 |
364 | export const query = graphql\`
365 | {
366 | site { siteMetadata { title }}
367 | }
368 | \`
369 | `
370 | )
371 | })
372 |
373 | it(`Removes all gatsby queries`, () => {
374 | matchesSnapshot(
375 | `
376 | import { graphql } from 'gatsby'
377 |
378 | export default () => (
379 | {data.site.siteMetadata.title}
380 | )
381 |
382 | export const siteMetaQuery = graphql\`
383 | fragment siteMetaQuery on RootQueryType {
384 | site {
385 | siteMetadata {
386 | title
387 | }
388 | }
389 | }
390 | \`
391 |
392 | export const query = graphql\`
393 | {
394 | ...siteMetaQuery
395 | }
396 | \`
397 | `
398 | )
399 | })
400 |
401 | it(`Handles closing StaticQuery tag`, () => {
402 | matchesSnapshot(`
403 | import * as React from 'react'
404 | import { graphql, StaticQuery } from 'gatsby'
405 |
406 | export default () => (
407 |
410 | {data => {data.site.siteMetadata.title}
}
411 |
412 | )
413 | `)
414 | })
415 |
416 | it(`Doesn't add data import for non static queries`, () => {
417 | matchesSnapshot(`
418 | import * as React from 'react'
419 | import { StaticQuery, graphql } from "gatsby"
420 |
421 | const Test = () => (
422 | {data.site.siteMetadata.title}
}
433 | />
434 | )
435 |
436 | export default Test
437 |
438 | export const fragment = graphql\`
439 | fragment MarkdownNodeFragment on MarkdownRemark {
440 | html
441 | }
442 | \`
443 | `)
444 | })
445 |
--------------------------------------------------------------------------------
/packages/gatsby-source-graphql-universal/__tests__/__snapshots__/babel-plugin-remove-graphql-queries.spec.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Allow alternative import of useStaticQuery 1`] = `
4 | "import staticQueryData from \\"public/static/d/426988268.json\\";
5 | import * as React from 'react';
6 | import * as Gatsby from 'gatsby';
7 | export default (() => {
8 | const query = {
9 | id: \\"426988268\\",
10 | source: \\"{site{siteMetadata{title}}}\\",
11 |
12 | toString() {
13 | return this.id;
14 | }
15 |
16 | };
17 | const siteTitle = staticQueryData.data;
18 | return /*#__PURE__*/React.createElement(\\"h1\\", null, siteTitle.site.siteMetadata.title);
19 | });"
20 | `;
21 |
22 | exports[`Doesn't add data import for non static queries 1`] = `
23 | "import staticQueryData from \\"public/static/d/426988268.json\\";
24 | import * as React from 'react';
25 | import { StaticQuery } from \\"gatsby\\";
26 |
27 | const Test = () => /*#__PURE__*/React.createElement(StaticQuery, {
28 | query: {
29 | id: \\"426988268\\",
30 | source: \\"{site{siteMetadata{title}}}\\",
31 |
32 | toString() {
33 | return this.id;
34 | }
35 |
36 | },
37 | render: data => /*#__PURE__*/React.createElement(\\"div\\", null, data.site.siteMetadata.title),
38 | data: staticQueryData
39 | });
40 |
41 | export default Test;
42 | export const fragment = {
43 | id: \\"4176178832\\",
44 | source: \\"fragment MarkdownNodeFragment on MarkdownRemark{html}\\",
45 |
46 | toString() {
47 | return this.id;
48 | }
49 |
50 | };"
51 | `;
52 |
53 | exports[`Handles closing StaticQuery tag 1`] = `
54 | "import staticQueryData from \\"public/static/d/426988268.json\\";
55 | import * as React from 'react';
56 | import { StaticQuery } from 'gatsby';
57 | export default (() => /*#__PURE__*/React.createElement(StaticQuery, {
58 | query: {
59 | id: \\"426988268\\",
60 | source: \\"{site{siteMetadata{title}}}\\",
61 |
62 | toString() {
63 | return this.id;
64 | }
65 |
66 | },
67 | data: staticQueryData
68 | }, data => /*#__PURE__*/React.createElement(\\"div\\", null, data.site.siteMetadata.title)));"
69 | `;
70 |
71 | exports[`Leaves other graphql tags alone 1`] = `
72 | "import * as React from 'react';
73 | import { graphql } from 'relay';
74 | export default (() => /*#__PURE__*/React.createElement(\\"div\\", null, data.site.siteMetadata.title));
75 | export const query = graphql\`
76 | {
77 | site { siteMetadata { title }}
78 | }
79 | \`;"
80 | `;
81 |
82 | exports[`Only runs transforms if useStaticQuery is imported from gatsby 1`] = `
83 | "import * as React from 'react';
84 | export default (() => {
85 | const query = {
86 | id: \\"426988268\\",
87 | source: \\"{site{siteMetadata{title}}}\\",
88 |
89 | toString() {
90 | return this.id;
91 | }
92 |
93 | };
94 | const siteTitle = useStaticQuery(query);
95 | return /*#__PURE__*/React.createElement(\\"h1\\", null, siteTitle.site.siteMetadata.title);
96 | });"
97 | `;
98 |
99 | exports[`Removes all gatsby queries 1`] = `
100 | "export default (() => /*#__PURE__*/React.createElement(\\"div\\", null, data.site.siteMetadata.title));
101 | export const siteMetaQuery = {
102 | id: \\"504726680\\",
103 | source: \\"fragment siteMetaQuery on RootQueryType{site{siteMetadata{title}}}\\",
104 |
105 | toString() {
106 | return this.id;
107 | }
108 |
109 | };
110 | export const query = {
111 | id: \\"3211238532\\",
112 | source: \\"{...siteMetaQuery}\\",
113 |
114 | toString() {
115 | return this.id;
116 | }
117 |
118 | };"
119 | `;
120 |
121 | exports[`Transformation does not break custom hooks 1`] = `
122 | "import staticQueryData from \\"public/static/d/426988268.json\\";
123 | import React from \\"react\\";
124 |
125 | const useSiteMetadata = () => {
126 | const data = staticQueryData.data;
127 | return data.site.siteMetadata;
128 | };
129 |
130 | export default (() => {
131 | const siteMetadata = useSiteMetadata();
132 | return /*#__PURE__*/React.createElement(\\"h1\\", null, site.siteMetadata.title);
133 | });"
134 | `;
135 |
136 | exports[`Transforms exported queries in useStaticQuery 1`] = `
137 | "import staticQueryData from \\"public/static/d/426988268.json\\";
138 | import * as React from 'react';
139 | export default (() => {
140 | const data = staticQueryData.data;
141 | return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(\\"h1\\", null, data.site.siteMetadata.title), /*#__PURE__*/React.createElement(\\"p\\", null, data.site.siteMetadata.description));
142 | });
143 | export const query = {
144 | id: \\"426988268\\",
145 | source: \\"{site{siteMetadata{title}}}\\",
146 |
147 | toString() {
148 | return this.id;
149 | }
150 |
151 | };"
152 | `;
153 |
154 | exports[`Transforms only the call expression in useStaticQuery 1`] = `
155 | "import staticQueryData from \\"public/static/d/426988268.json\\";
156 | import React from \\"react\\";
157 |
158 | const useSiteMetadata = () => {
159 | return staticQueryData.data.site.siteMetadata;
160 | };
161 |
162 | export default (() => {
163 | const siteMetadata = useSiteMetadata();
164 | return /*#__PURE__*/React.createElement(\\"h1\\", null, siteMetadata.title);
165 | });"
166 | `;
167 |
168 | exports[`Transforms queries and preserves destructuring in useStaticQuery 1`] = `
169 | "import staticQueryData from \\"public/static/d/426988268.json\\";
170 | import * as React from 'react';
171 | export default (() => {
172 | const query = {
173 | id: \\"426988268\\",
174 | source: \\"{site{siteMetadata{title}}}\\",
175 |
176 | toString() {
177 | return this.id;
178 | }
179 |
180 | };
181 | const {
182 | site
183 | } = staticQueryData.data;
184 | return /*#__PURE__*/React.createElement(\\"h1\\", null, site.siteMetadata.title);
185 | });"
186 | `;
187 |
188 | exports[`Transforms queries and preserves variable type in useStaticQuery 1`] = `
189 | "import staticQueryData from \\"public/static/d/426988268.json\\";
190 | import * as React from 'react';
191 | export default (() => {
192 | const query = {
193 | id: \\"426988268\\",
194 | source: \\"{site{siteMetadata{title}}}\\",
195 |
196 | toString() {
197 | return this.id;
198 | }
199 |
200 | };
201 | let {
202 | site
203 | } = staticQueryData.data;
204 | return /*#__PURE__*/React.createElement(\\"h1\\", null, site.siteMetadata.title);
205 | });"
206 | `;
207 |
208 | exports[`Transforms queries defined in own variable in 1`] = `
209 | "import staticQueryData from \\"public/static/d/426988268.json\\";
210 | import * as React from 'react';
211 | import { StaticQuery } from 'gatsby';
212 | const query = {
213 | id: \\"426988268\\",
214 | source: \\"{site{siteMetadata{title}}}\\",
215 |
216 | toString() {
217 | return this.id;
218 | }
219 |
220 | };
221 | export default (() => /*#__PURE__*/React.createElement(StaticQuery, {
222 | query: query,
223 | render: data => /*#__PURE__*/React.createElement(\\"div\\", null, data.site.siteMetadata.title),
224 | data: staticQueryData
225 | }));"
226 | `;
227 |
228 | exports[`Transforms queries defined in own variable in useStaticQuery 1`] = `
229 | "import staticQueryData from \\"public/static/d/426988268.json\\";
230 | import * as React from 'react';
231 | export default (() => {
232 | const query = {
233 | id: \\"426988268\\",
234 | source: \\"{site{siteMetadata{title}}}\\",
235 |
236 | toString() {
237 | return this.id;
238 | }
239 |
240 | };
241 | const siteTitle = staticQueryData.data;
242 | return /*#__PURE__*/React.createElement(\\"h1\\", null, siteTitle.site.siteMetadata.title);
243 | });"
244 | `;
245 |
246 | exports[`Transforms queries in 1`] = `
247 | "import staticQueryData from \\"public/static/d/426988268.json\\";
248 | import * as React from 'react';
249 | import { StaticQuery } from 'gatsby';
250 | export default (() => /*#__PURE__*/React.createElement(StaticQuery, {
251 | query: {
252 | id: \\"426988268\\",
253 | source: \\"{site{siteMetadata{title}}}\\",
254 |
255 | toString() {
256 | return this.id;
257 | }
258 |
259 | },
260 | render: data => /*#__PURE__*/React.createElement(\\"div\\", null, data.site.siteMetadata.title),
261 | data: staticQueryData
262 | }));"
263 | `;
264 |
265 | exports[`Transforms queries in page components 1`] = `
266 | "export const query = {
267 | id: \\"426988268\\",
268 | source: \\"{site{siteMetadata{title}}}\\",
269 |
270 | toString() {
271 | return this.id;
272 | }
273 |
274 | };"
275 | `;
276 |
277 | exports[`Transforms queries in useStaticQuery 1`] = `
278 | "import staticQueryData from \\"public/static/d/426988268.json\\";
279 | import * as React from 'react';
280 | export default (() => {
281 | const siteTitle = staticQueryData.data;
282 | return /*#__PURE__*/React.createElement(\\"h1\\", null, siteTitle.site.siteMetadata.title);
283 | });"
284 | `;
285 |
286 | exports[`allows the global tag 1`] = `
287 | "export const query = {
288 | id: \\"426988268\\",
289 | source: \\"{site{siteMetadata{title}}}\\",
290 |
291 | toString() {
292 | return this.id;
293 | }
294 |
295 | };"
296 | `;
297 |
298 | exports[`distinguishes between the right tags 1`] = `
299 | "const foo = styled('div')\`
300 | {
301 | \${foo}
302 | }
303 | \`;
304 | const pulse = keyframes\`
305 | 0% {
306 | transform: scale(1);
307 | animation-timing-function: ease-in;
308 | }
309 | 25% {
310 | animation-timing-function: ease-out;
311 | transform: scale(1.05);
312 | }
313 | 50% {
314 | transform: scale(1.12);
315 | animation-timing-function: ease-in;
316 | }
317 | to {
318 | transform: scale(1);
319 | animation-timing-function: ease-out;
320 | }
321 | \`;
322 | export const query = {
323 | id: \\"426988268\\",
324 | source: \\"{site{siteMetadata{title}}}\\",
325 |
326 | toString() {
327 | return this.id;
328 | }
329 |
330 | };"
331 | `;
332 |
333 | exports[`handles import aliasing 1`] = `
334 | "export const query = {
335 | id: \\"426988268\\",
336 | source: \\"{site{siteMetadata{title}}}\\",
337 |
338 | toString() {
339 | return this.id;
340 | }
341 |
342 | };"
343 | `;
344 |
345 | exports[`handles require 1`] = `
346 | "export const query = {
347 | id: \\"426988268\\",
348 | source: \\"{site{siteMetadata{title}}}\\",
349 |
350 | toString() {
351 | return this.id;
352 | }
353 |
354 | };"
355 | `;
356 |
357 | exports[`handles require alias 1`] = `
358 | "export const query = {
359 | id: \\"426988268\\",
360 | source: \\"{site{siteMetadata{title}}}\\",
361 |
362 | toString() {
363 | return this.id;
364 | }
365 |
366 | };"
367 | `;
368 |
369 | exports[`handles require namespace 1`] = `
370 | "export const query = {
371 | id: \\"426988268\\",
372 | source: \\"{site{siteMetadata{title}}}\\",
373 |
374 | toString() {
375 | return this.id;
376 | }
377 |
378 | };"
379 | `;
380 |
381 | exports[`transforms exported variable queries in 1`] = `
382 | "import staticQueryData from \\"public/static/d/426988268.json\\";
383 | import * as React from 'react';
384 | import { StaticQuery } from 'gatsby';
385 | export const query = {
386 | id: \\"426988268\\",
387 | source: \\"{site{siteMetadata{title}}}\\",
388 |
389 | toString() {
390 | return this.id;
391 | }
392 |
393 | };
394 | export default (() => /*#__PURE__*/React.createElement(StaticQuery, {
395 | query: query,
396 | render: data => /*#__PURE__*/React.createElement(\\"div\\", null, data.site.siteMetadata.title),
397 | data: staticQueryData
398 | }));"
399 | `;
400 |
--------------------------------------------------------------------------------
/example/src/components/layout.css:
--------------------------------------------------------------------------------
1 | html {
2 | font-family: sans-serif;
3 | -ms-text-size-adjust: 100%;
4 | -webkit-text-size-adjust: 100%;
5 | }
6 | body {
7 | margin: 0;
8 | }
9 | article,
10 | aside,
11 | details,
12 | figcaption,
13 | figure,
14 | footer,
15 | header,
16 | main,
17 | menu,
18 | nav,
19 | section,
20 | summary {
21 | display: block;
22 | }
23 | audio,
24 | canvas,
25 | progress,
26 | video {
27 | display: inline-block;
28 | }
29 | audio:not([controls]) {
30 | display: none;
31 | height: 0;
32 | }
33 | progress {
34 | vertical-align: baseline;
35 | }
36 | [hidden],
37 | template {
38 | display: none;
39 | }
40 | a {
41 | background-color: transparent;
42 | -webkit-text-decoration-skip: objects;
43 | }
44 | a:active,
45 | a:hover {
46 | outline-width: 0;
47 | }
48 | abbr[title] {
49 | border-bottom: none;
50 | text-decoration: underline;
51 | text-decoration: underline dotted;
52 | }
53 | b,
54 | strong {
55 | font-weight: inherit;
56 | font-weight: bolder;
57 | }
58 | dfn {
59 | font-style: italic;
60 | }
61 | h1 {
62 | font-size: 2em;
63 | margin: 0.67em 0;
64 | }
65 | mark {
66 | background-color: #ff0;
67 | color: #000;
68 | }
69 | small {
70 | font-size: 80%;
71 | }
72 | sub,
73 | sup {
74 | font-size: 75%;
75 | line-height: 0;
76 | position: relative;
77 | vertical-align: baseline;
78 | }
79 | sub {
80 | bottom: -0.25em;
81 | }
82 | sup {
83 | top: -0.5em;
84 | }
85 | img {
86 | border-style: none;
87 | }
88 | svg:not(:root) {
89 | overflow: hidden;
90 | }
91 | code,
92 | kbd,
93 | pre,
94 | samp {
95 | font-family: monospace, monospace;
96 | font-size: 1em;
97 | }
98 | figure {
99 | margin: 1em 40px;
100 | }
101 | hr {
102 | box-sizing: content-box;
103 | height: 0;
104 | overflow: visible;
105 | }
106 | button,
107 | input,
108 | optgroup,
109 | select,
110 | textarea {
111 | font: inherit;
112 | margin: 0;
113 | }
114 | optgroup {
115 | font-weight: 700;
116 | }
117 | button,
118 | input {
119 | overflow: visible;
120 | }
121 | button,
122 | select {
123 | text-transform: none;
124 | }
125 | [type='reset'],
126 | [type='submit'],
127 | button,
128 | html [type='button'] {
129 | -webkit-appearance: button;
130 | }
131 | [type='button']::-moz-focus-inner,
132 | [type='reset']::-moz-focus-inner,
133 | [type='submit']::-moz-focus-inner,
134 | button::-moz-focus-inner {
135 | border-style: none;
136 | padding: 0;
137 | }
138 | [type='button']:-moz-focusring,
139 | [type='reset']:-moz-focusring,
140 | [type='submit']:-moz-focusring,
141 | button:-moz-focusring {
142 | outline: 1px dotted ButtonText;
143 | }
144 | fieldset {
145 | border: 1px solid silver;
146 | margin: 0 2px;
147 | padding: 0.35em 0.625em 0.75em;
148 | }
149 | legend {
150 | box-sizing: border-box;
151 | color: inherit;
152 | display: table;
153 | max-width: 100%;
154 | padding: 0;
155 | white-space: normal;
156 | }
157 | textarea {
158 | overflow: auto;
159 | }
160 | [type='checkbox'],
161 | [type='radio'] {
162 | box-sizing: border-box;
163 | padding: 0;
164 | }
165 | [type='number']::-webkit-inner-spin-button,
166 | [type='number']::-webkit-outer-spin-button {
167 | height: auto;
168 | }
169 | [type='search'] {
170 | -webkit-appearance: textfield;
171 | outline-offset: -2px;
172 | }
173 | [type='search']::-webkit-search-cancel-button,
174 | [type='search']::-webkit-search-decoration {
175 | -webkit-appearance: none;
176 | }
177 | ::-webkit-input-placeholder {
178 | color: inherit;
179 | opacity: 0.54;
180 | }
181 | ::-webkit-file-upload-button {
182 | -webkit-appearance: button;
183 | font: inherit;
184 | }
185 | html {
186 | font: 112.5%/1.45em georgia, serif;
187 | box-sizing: border-box;
188 | overflow-y: scroll;
189 | }
190 | * {
191 | box-sizing: inherit;
192 | }
193 | *:before {
194 | box-sizing: inherit;
195 | }
196 | *:after {
197 | box-sizing: inherit;
198 | }
199 | body {
200 | color: hsla(0, 0%, 0%, 0.8);
201 | font-family: georgia, serif;
202 | font-weight: normal;
203 | word-wrap: break-word;
204 | font-kerning: normal;
205 | -moz-font-feature-settings: 'kern', 'liga', 'clig', 'calt';
206 | -ms-font-feature-settings: 'kern', 'liga', 'clig', 'calt';
207 | -webkit-font-feature-settings: 'kern', 'liga', 'clig', 'calt';
208 | font-feature-settings: 'kern', 'liga', 'clig', 'calt';
209 | }
210 | img {
211 | max-width: 100%;
212 | margin-left: 0;
213 | margin-right: 0;
214 | margin-top: 0;
215 | padding-bottom: 0;
216 | padding-left: 0;
217 | padding-right: 0;
218 | padding-top: 0;
219 | margin-bottom: 1.45rem;
220 | }
221 | h1 {
222 | margin-left: 0;
223 | margin-right: 0;
224 | margin-top: 0;
225 | padding-bottom: 0;
226 | padding-left: 0;
227 | padding-right: 0;
228 | padding-top: 0;
229 | margin-bottom: 1.45rem;
230 | color: inherit;
231 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
232 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
233 | font-weight: bold;
234 | text-rendering: optimizeLegibility;
235 | font-size: 2.25rem;
236 | line-height: 1.1;
237 | }
238 | h2 {
239 | margin-left: 0;
240 | margin-right: 0;
241 | margin-top: 0;
242 | padding-bottom: 0;
243 | padding-left: 0;
244 | padding-right: 0;
245 | padding-top: 0;
246 | margin-bottom: 1.45rem;
247 | color: inherit;
248 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
249 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
250 | font-weight: bold;
251 | text-rendering: optimizeLegibility;
252 | font-size: 1.62671rem;
253 | line-height: 1.1;
254 | }
255 | h3 {
256 | margin-left: 0;
257 | margin-right: 0;
258 | margin-top: 0;
259 | padding-bottom: 0;
260 | padding-left: 0;
261 | padding-right: 0;
262 | padding-top: 0;
263 | margin-bottom: 1.45rem;
264 | color: inherit;
265 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
266 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
267 | font-weight: bold;
268 | text-rendering: optimizeLegibility;
269 | font-size: 1.38316rem;
270 | line-height: 1.1;
271 | }
272 | h4 {
273 | margin-left: 0;
274 | margin-right: 0;
275 | margin-top: 0;
276 | padding-bottom: 0;
277 | padding-left: 0;
278 | padding-right: 0;
279 | padding-top: 0;
280 | margin-bottom: 1.45rem;
281 | color: inherit;
282 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
283 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
284 | font-weight: bold;
285 | text-rendering: optimizeLegibility;
286 | font-size: 1rem;
287 | line-height: 1.1;
288 | }
289 | h5 {
290 | margin-left: 0;
291 | margin-right: 0;
292 | margin-top: 0;
293 | padding-bottom: 0;
294 | padding-left: 0;
295 | padding-right: 0;
296 | padding-top: 0;
297 | margin-bottom: 1.45rem;
298 | color: inherit;
299 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
300 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
301 | font-weight: bold;
302 | text-rendering: optimizeLegibility;
303 | font-size: 0.85028rem;
304 | line-height: 1.1;
305 | }
306 | h6 {
307 | margin-left: 0;
308 | margin-right: 0;
309 | margin-top: 0;
310 | padding-bottom: 0;
311 | padding-left: 0;
312 | padding-right: 0;
313 | padding-top: 0;
314 | margin-bottom: 1.45rem;
315 | color: inherit;
316 | font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
317 | Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
318 | font-weight: bold;
319 | text-rendering: optimizeLegibility;
320 | font-size: 0.78405rem;
321 | line-height: 1.1;
322 | }
323 | hgroup {
324 | margin-left: 0;
325 | margin-right: 0;
326 | margin-top: 0;
327 | padding-bottom: 0;
328 | padding-left: 0;
329 | padding-right: 0;
330 | padding-top: 0;
331 | margin-bottom: 1.45rem;
332 | }
333 | ul {
334 | margin-left: 1.45rem;
335 | margin-right: 0;
336 | margin-top: 0;
337 | padding-bottom: 0;
338 | padding-left: 0;
339 | padding-right: 0;
340 | padding-top: 0;
341 | margin-bottom: 1.45rem;
342 | list-style-position: outside;
343 | list-style-image: none;
344 | }
345 | ol {
346 | margin-left: 1.45rem;
347 | margin-right: 0;
348 | margin-top: 0;
349 | padding-bottom: 0;
350 | padding-left: 0;
351 | padding-right: 0;
352 | padding-top: 0;
353 | margin-bottom: 1.45rem;
354 | list-style-position: outside;
355 | list-style-image: none;
356 | }
357 | dl {
358 | margin-left: 0;
359 | margin-right: 0;
360 | margin-top: 0;
361 | padding-bottom: 0;
362 | padding-left: 0;
363 | padding-right: 0;
364 | padding-top: 0;
365 | margin-bottom: 1.45rem;
366 | }
367 | dd {
368 | margin-left: 0;
369 | margin-right: 0;
370 | margin-top: 0;
371 | padding-bottom: 0;
372 | padding-left: 0;
373 | padding-right: 0;
374 | padding-top: 0;
375 | margin-bottom: 1.45rem;
376 | }
377 | p {
378 | margin-left: 0;
379 | margin-right: 0;
380 | margin-top: 0;
381 | padding-bottom: 0;
382 | padding-left: 0;
383 | padding-right: 0;
384 | padding-top: 0;
385 | margin-bottom: 1.45rem;
386 | }
387 | figure {
388 | margin-left: 0;
389 | margin-right: 0;
390 | margin-top: 0;
391 | padding-bottom: 0;
392 | padding-left: 0;
393 | padding-right: 0;
394 | padding-top: 0;
395 | margin-bottom: 1.45rem;
396 | }
397 | pre {
398 | margin-left: 0;
399 | margin-right: 0;
400 | margin-top: 0;
401 | padding-bottom: 0;
402 | padding-left: 0;
403 | padding-right: 0;
404 | padding-top: 0;
405 | margin-bottom: 1.45rem;
406 | font-size: 0.85rem;
407 | line-height: 1.42;
408 | background: hsla(0, 0%, 0%, 0.04);
409 | border-radius: 3px;
410 | overflow: auto;
411 | word-wrap: normal;
412 | padding: 1.45rem;
413 | }
414 | table {
415 | margin-left: 0;
416 | margin-right: 0;
417 | margin-top: 0;
418 | padding-bottom: 0;
419 | padding-left: 0;
420 | padding-right: 0;
421 | padding-top: 0;
422 | margin-bottom: 1.45rem;
423 | font-size: 1rem;
424 | line-height: 1.45rem;
425 | border-collapse: collapse;
426 | width: 100%;
427 | }
428 | fieldset {
429 | margin-left: 0;
430 | margin-right: 0;
431 | margin-top: 0;
432 | padding-bottom: 0;
433 | padding-left: 0;
434 | padding-right: 0;
435 | padding-top: 0;
436 | margin-bottom: 1.45rem;
437 | }
438 | blockquote {
439 | margin-left: 1.45rem;
440 | margin-right: 1.45rem;
441 | margin-top: 0;
442 | padding-bottom: 0;
443 | padding-left: 0;
444 | padding-right: 0;
445 | padding-top: 0;
446 | margin-bottom: 1.45rem;
447 | }
448 | form {
449 | margin-left: 0;
450 | margin-right: 0;
451 | margin-top: 0;
452 | padding-bottom: 0;
453 | padding-left: 0;
454 | padding-right: 0;
455 | padding-top: 0;
456 | margin-bottom: 1.45rem;
457 | }
458 | noscript {
459 | margin-left: 0;
460 | margin-right: 0;
461 | margin-top: 0;
462 | padding-bottom: 0;
463 | padding-left: 0;
464 | padding-right: 0;
465 | padding-top: 0;
466 | margin-bottom: 1.45rem;
467 | }
468 | iframe {
469 | margin-left: 0;
470 | margin-right: 0;
471 | margin-top: 0;
472 | padding-bottom: 0;
473 | padding-left: 0;
474 | padding-right: 0;
475 | padding-top: 0;
476 | margin-bottom: 1.45rem;
477 | }
478 | hr {
479 | margin-left: 0;
480 | margin-right: 0;
481 | margin-top: 0;
482 | padding-bottom: 0;
483 | padding-left: 0;
484 | padding-right: 0;
485 | padding-top: 0;
486 | margin-bottom: calc(1.45rem - 1px);
487 | background: hsla(0, 0%, 0%, 0.2);
488 | border: none;
489 | height: 1px;
490 | }
491 | address {
492 | margin-left: 0;
493 | margin-right: 0;
494 | margin-top: 0;
495 | padding-bottom: 0;
496 | padding-left: 0;
497 | padding-right: 0;
498 | padding-top: 0;
499 | margin-bottom: 1.45rem;
500 | }
501 | b {
502 | font-weight: bold;
503 | }
504 | strong {
505 | font-weight: bold;
506 | }
507 | dt {
508 | font-weight: bold;
509 | }
510 | th {
511 | font-weight: bold;
512 | }
513 | li {
514 | margin-bottom: calc(1.45rem / 2);
515 | }
516 | ol li {
517 | padding-left: 0;
518 | }
519 | ul li {
520 | padding-left: 0;
521 | }
522 | li > ol {
523 | margin-left: 1.45rem;
524 | margin-bottom: calc(1.45rem / 2);
525 | margin-top: calc(1.45rem / 2);
526 | }
527 | li > ul {
528 | margin-left: 1.45rem;
529 | margin-bottom: calc(1.45rem / 2);
530 | margin-top: calc(1.45rem / 2);
531 | }
532 | blockquote *:last-child {
533 | margin-bottom: 0;
534 | }
535 | li *:last-child {
536 | margin-bottom: 0;
537 | }
538 | p *:last-child {
539 | margin-bottom: 0;
540 | }
541 | li > p {
542 | margin-bottom: calc(1.45rem / 2);
543 | }
544 | code {
545 | font-size: 0.85rem;
546 | line-height: 1.45rem;
547 | }
548 | kbd {
549 | font-size: 0.85rem;
550 | line-height: 1.45rem;
551 | }
552 | samp {
553 | font-size: 0.85rem;
554 | line-height: 1.45rem;
555 | }
556 | abbr {
557 | border-bottom: 1px dotted hsla(0, 0%, 0%, 0.5);
558 | cursor: help;
559 | }
560 | acronym {
561 | border-bottom: 1px dotted hsla(0, 0%, 0%, 0.5);
562 | cursor: help;
563 | }
564 | abbr[title] {
565 | border-bottom: 1px dotted hsla(0, 0%, 0%, 0.5);
566 | cursor: help;
567 | text-decoration: none;
568 | }
569 | thead {
570 | text-align: left;
571 | }
572 | td,
573 | th {
574 | text-align: left;
575 | border-bottom: 1px solid hsla(0, 0%, 0%, 0.12);
576 | font-feature-settings: 'tnum';
577 | -moz-font-feature-settings: 'tnum';
578 | -ms-font-feature-settings: 'tnum';
579 | -webkit-font-feature-settings: 'tnum';
580 | padding-left: 0.96667rem;
581 | padding-right: 0.96667rem;
582 | padding-top: 0.725rem;
583 | padding-bottom: calc(0.725rem - 1px);
584 | }
585 | th:first-child,
586 | td:first-child {
587 | padding-left: 0;
588 | }
589 | th:last-child,
590 | td:last-child {
591 | padding-right: 0;
592 | }
593 | tt,
594 | code {
595 | background-color: hsla(0, 0%, 0%, 0.04);
596 | border-radius: 3px;
597 | font-family: 'SFMono-Regular', Consolas, 'Roboto Mono', 'Droid Sans Mono',
598 | 'Liberation Mono', Menlo, Courier, monospace;
599 | padding: 0;
600 | padding-top: 0.2em;
601 | padding-bottom: 0.2em;
602 | }
603 | pre code {
604 | background: none;
605 | line-height: 1.42;
606 | }
607 | code:before,
608 | code:after,
609 | tt:before,
610 | tt:after {
611 | letter-spacing: -0.2em;
612 | content: ' ';
613 | }
614 | pre code:before,
615 | pre code:after,
616 | pre tt:before,
617 | pre tt:after {
618 | content: '';
619 | }
620 | @media only screen and (max-width: 480px) {
621 | html {
622 | font-size: 100%;
623 | }
624 | }
625 |
--------------------------------------------------------------------------------
/packages/gatsby-source-graphql-universal/dist/babel-plugin-remove-graphql-queries.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 |
3 | exports.__esModule = true;
4 | exports.followVariableDeclarations = followVariableDeclarations;
5 | exports.default = _default;
6 | exports.getGraphQLTag = getGraphQLTag;
7 | exports.murmurhash = exports.GraphQLSyntaxError = exports.EmptyGraphQLTagError = exports.StringInterpolationNotAllowedError = void 0;
8 |
9 | /* eslint-disable new-cap */
10 | const graphql = require(`gatsby/graphql`);
11 |
12 | const nodePath = require(`path`);
13 |
14 | const murmurModule = require(`babel-plugin-remove-graphql-queries/murmur`);
15 |
16 | const getGraphqlExpr = require('./getGraphqlExpr');
17 |
18 | const murmurhash = typeof murmurModule === 'function' ? murmurModule : murmurModule.murmurhash;
19 | exports.murmurhash = murmurhash;
20 |
21 | class StringInterpolationNotAllowedError extends Error {
22 | constructor(interpolationStart, interpolationEnd) {
23 | super(`BabelPluginRemoveGraphQLQueries: String interpolations are not allowed in graphql ` + `fragments. Included fragments should be referenced ` + `as \`...MyModule_foo\`.`);
24 | this.interpolationStart = JSON.parse(JSON.stringify(interpolationStart));
25 | this.interpolationEnd = JSON.parse(JSON.stringify(interpolationEnd));
26 | Error.captureStackTrace(this, StringInterpolationNotAllowedError);
27 | }
28 |
29 | }
30 |
31 | exports.StringInterpolationNotAllowedError = StringInterpolationNotAllowedError;
32 |
33 | class EmptyGraphQLTagError extends Error {
34 | constructor(locationOfGraphqlString) {
35 | super(`BabelPluginRemoveGraphQLQueries: Unexpected empty graphql tag.`);
36 | this.templateLoc = locationOfGraphqlString;
37 | Error.captureStackTrace(this, EmptyGraphQLTagError);
38 | }
39 |
40 | }
41 |
42 | exports.EmptyGraphQLTagError = EmptyGraphQLTagError;
43 |
44 | class GraphQLSyntaxError extends Error {
45 | constructor(documentText, originalError, locationOfGraphqlString) {
46 | super(`BabelPluginRemoveGraphQLQueries: GraphQL syntax error in query:\n\n${documentText}\n\nmessage:\n\n${originalError}`);
47 | this.documentText = documentText;
48 | this.originalError = originalError;
49 | this.templateLoc = locationOfGraphqlString;
50 | Error.captureStackTrace(this, GraphQLSyntaxError);
51 | }
52 |
53 | }
54 |
55 | exports.GraphQLSyntaxError = GraphQLSyntaxError;
56 |
57 | const isGlobalIdentifier = (tag, tagName = `graphql`) => tag.isIdentifier({
58 | name: tagName
59 | }) && tag.scope.hasGlobal(tagName);
60 |
61 | function followVariableDeclarations(binding) {
62 | var _binding$path, _node$init;
63 |
64 | const node = (_binding$path = binding.path) === null || _binding$path === void 0 ? void 0 : _binding$path.node;
65 |
66 | if ((node === null || node === void 0 ? void 0 : node.type) === `VariableDeclarator` && (node === null || node === void 0 ? void 0 : node.id.type) === `Identifier` && (node === null || node === void 0 ? void 0 : (_node$init = node.init) === null || _node$init === void 0 ? void 0 : _node$init.type) === `Identifier`) {
67 | return followVariableDeclarations(binding.path.scope.getBinding(node.init.name));
68 | }
69 |
70 | return binding;
71 | }
72 |
73 | function getTagImport(tag) {
74 | const name = tag.node.name;
75 | const binding = tag.scope.getBinding(name);
76 | if (!binding) return null;
77 | const path = binding.path;
78 | const parent = path.parentPath;
79 | if (binding.kind === `module` && parent.isImportDeclaration() && parent.node.source.value === `gatsby`) return path;
80 |
81 | if (path.isVariableDeclarator() && path.get(`init`).isCallExpression() && path.get(`init.callee`).isIdentifier({
82 | name: `require`
83 | }) && path.get(`init`).node.arguments[0].value === `gatsby`) {
84 | const id = path.get(`id`);
85 |
86 | if (id.isObjectPattern()) {
87 | return id.get(`properties`).find(path => path.get(`value`).node.name === name);
88 | }
89 |
90 | return id;
91 | }
92 |
93 | return null;
94 | }
95 |
96 | function isGraphqlTag(tag, tagName = `graphql`) {
97 | const isExpression = tag.isMemberExpression();
98 | const identifier = isExpression ? tag.get(`object`) : tag;
99 | const importPath = getTagImport(identifier);
100 | if (!importPath) return isGlobalIdentifier(tag, tagName);
101 |
102 | if (isExpression && (importPath.isImportNamespaceSpecifier() || importPath.isIdentifier())) {
103 | return tag.get(`property`).node.name === tagName;
104 | }
105 |
106 | if (importPath.isImportSpecifier()) return importPath.node.imported.name === tagName;
107 | if (importPath.isObjectProperty()) return importPath.get(`key`).node.name === tagName;
108 | return false;
109 | }
110 |
111 | function removeImport(tag) {
112 | const isExpression = tag.isMemberExpression();
113 | const identifier = isExpression ? tag.get(`object`) : tag;
114 | const importPath = getTagImport(identifier);
115 |
116 | const removeVariableDeclaration = statement => {
117 | let declaration = statement.findParent(p => p.isVariableDeclaration());
118 |
119 | if (declaration) {
120 | declaration.remove();
121 | }
122 | };
123 |
124 | if (!importPath) return;
125 | const parent = importPath.parentPath;
126 |
127 | if (importPath.isImportSpecifier()) {
128 | if (parent.node.specifiers.length === 1) parent.remove();else importPath.remove();
129 | }
130 |
131 | if (importPath.isObjectProperty()) {
132 | if (parent.node.properties.length === 1) {
133 | removeVariableDeclaration(importPath);
134 | } else importPath.remove();
135 | }
136 |
137 | if (importPath.isIdentifier()) {
138 | removeVariableDeclaration(importPath);
139 | }
140 | }
141 |
142 | function getGraphQLTag(path, tagName = `graphql`) {
143 | const tag = path.get(`tag`);
144 | const isGlobal = isGlobalIdentifier(tag, tagName);
145 | if (!isGlobal && !isGraphqlTag(tag, tagName)) return {};
146 | const quasis = path.node.quasi.quasis;
147 |
148 | if (quasis.length !== 1) {
149 | throw new StringInterpolationNotAllowedError(quasis[0].loc.end, quasis[1].loc.start);
150 | }
151 |
152 | const text = quasis[0].value.raw;
153 | const normalizedText = graphql.stripIgnoredCharacters(text);
154 | const hash = murmurhash(normalizedText, `abc`);
155 |
156 | try {
157 | const ast = graphql.parse(text);
158 |
159 | if (ast.definitions.length === 0) {
160 | throw new EmptyGraphQLTagError(quasis[0].loc);
161 | }
162 |
163 | return {
164 | ast,
165 | text: normalizedText,
166 | hash,
167 | isGlobal
168 | };
169 | } catch (err) {
170 | throw new GraphQLSyntaxError(text, err, quasis[0].loc);
171 | }
172 | }
173 |
174 | function isUseStaticQuery(path) {
175 | return path.node.callee.type === `MemberExpression` && path.node.callee.property.name === `useStaticQuery` && path.get(`callee`).get(`object`).referencesImport(`gatsby`) || path.node.callee.name === `useStaticQuery` && path.get(`callee`).referencesImport(`gatsby`);
176 | }
177 |
178 | function _default({
179 | types: t
180 | }) {
181 | return {
182 | visitor: {
183 | Program(path, state) {
184 | const nestedJSXVistor = {
185 | JSXIdentifier(path2) {
186 | if ((process.env.NODE_ENV === `test` || state.opts.stage === `build-html`) && path2.isJSXIdentifier({
187 | name: `StaticQuery`
188 | }) && path2.referencesImport(`gatsby`) && path2.parent.type !== `JSXClosingElement`) {
189 | const identifier = t.identifier(`staticQueryData`);
190 | const filename = state.file.opts.filename;
191 | const staticQueryDir = state.opts.staticQueryDir || `static/d`;
192 | const shortResultPath = `public/${staticQueryDir}/${this.queryHash}.json`;
193 | const resultPath = nodePath.join(process.cwd(), shortResultPath); // Add query
194 |
195 | path2.parent.attributes.push(t.jSXAttribute(t.jSXIdentifier(`data`), t.jSXExpressionContainer(identifier))); // Add import
196 |
197 | const importDefaultSpecifier = t.importDefaultSpecifier(identifier);
198 | const importDeclaration = t.importDeclaration([importDefaultSpecifier], t.stringLiteral(filename ? nodePath.relative(nodePath.parse(filename).dir, resultPath) : shortResultPath));
199 | path.unshiftContainer(`body`, importDeclaration);
200 | }
201 | }
202 |
203 | };
204 | const nestedHookVisitor = {
205 | CallExpression(path2) {
206 | if ((process.env.NODE_ENV === `test` || state.opts.stage === `build-html`) && isUseStaticQuery(path2)) {
207 | const identifier = t.identifier(`staticQueryData`);
208 | const filename = state.file.opts.filename;
209 | const staticQueryDir = state.opts.staticQueryDir || `static/d`;
210 | const shortResultPath = `public/${staticQueryDir}/${this.queryHash}.json`;
211 | const resultPath = nodePath.join(process.cwd(), shortResultPath); // only remove the import if its like:
212 | // import { useStaticQuery } from 'gatsby'
213 | // but not if its like:
214 | // import * as Gatsby from 'gatsby'
215 | // because we know we can remove the useStaticQuery import,
216 | // but we don't know if other 'gatsby' exports are used, so we
217 | // cannot remove all 'gatsby' imports.
218 |
219 | if (path2.node.callee.type !== `MemberExpression`) {
220 | // Remove imports to useStaticQuery
221 | const importPath = path2.scope.getBinding(`useStaticQuery`).path;
222 | const parent = importPath.parentPath;
223 | if (importPath.isImportSpecifier()) if (parent.node.specifiers.length === 1) parent.remove();else importPath.remove();
224 | } // Add query
225 |
226 |
227 | path2.replaceWith(getGraphqlExpr(t, this.queryHash, this.query));
228 | path2.replaceWith(t.memberExpression(identifier, t.identifier(`data`))); // Add import
229 |
230 | const importDefaultSpecifier = t.importDefaultSpecifier(identifier);
231 | const importDeclaration = t.importDeclaration([importDefaultSpecifier], t.stringLiteral(filename ? nodePath.relative(nodePath.parse(filename).dir, resultPath) : shortResultPath));
232 | path.unshiftContainer(`body`, importDeclaration);
233 | }
234 | }
235 |
236 | };
237 | const tagsToRemoveImportsFrom = new Set();
238 |
239 | const setImportForStaticQuery = templatePath => {
240 | const {
241 | ast,
242 | text,
243 | hash,
244 | isGlobal
245 | } = getGraphQLTag(templatePath);
246 | if (!ast) return null;
247 | const queryHash = hash.toString();
248 | const query = text;
249 | const tag = templatePath.get(`tag`);
250 |
251 | if (!isGlobal) {
252 | // Enqueue import removal. If we would remove it here, subsequent named exports
253 | // wouldn't be handled properly
254 | tagsToRemoveImportsFrom.add(tag);
255 | } // Replace the query with the hash of the query.
256 | // templatePath.replaceWith(t.StringLiteral(queryHash))
257 |
258 |
259 | templatePath.replaceWith(getGraphqlExpr(t, queryHash, text)); // traverse upwards until we find top-level JSXOpeningElement or Program
260 | // this handles exported queries and variable queries
261 |
262 | let parent = templatePath;
263 |
264 | while (parent && ![`Program`, `JSXOpeningElement`].includes(parent.node.type)) {
265 | parent = parent.parentPath;
266 | } // modify StaticQuery elements and import data only if query is inside StaticQuery
267 |
268 |
269 | parent.traverse(nestedJSXVistor, {
270 | queryHash,
271 | query
272 | }); // modify useStaticQuery elements and import data only if query is inside useStaticQuery
273 |
274 | parent.traverse(nestedHookVisitor, {
275 | queryHash,
276 | query,
277 | templatePath
278 | });
279 | return null;
280 | }; // Traverse for instances
281 |
282 |
283 | path.traverse({
284 | JSXElement(jsxElementPath) {
285 | if (jsxElementPath.node.openingElement.name.name !== `StaticQuery`) {
286 | return;
287 | }
288 |
289 | jsxElementPath.traverse({
290 | JSXAttribute(jsxPath) {
291 | if (jsxPath.node.name.name !== `query`) {
292 | return;
293 | }
294 |
295 | jsxPath.traverse({
296 | TaggedTemplateExpression(templatePath, state) {
297 | setImportForStaticQuery(templatePath);
298 | },
299 |
300 | Identifier(identifierPath) {
301 | if (identifierPath.node.name !== `graphql`) {
302 | const varName = identifierPath.node.name;
303 | path.traverse({
304 | VariableDeclarator(varPath) {
305 | if (varPath.node.id.name === varName && varPath.node.init.type === `TaggedTemplateExpression`) {
306 | varPath.traverse({
307 | TaggedTemplateExpression(templatePath) {
308 | setImportForStaticQuery(templatePath);
309 | }
310 |
311 | });
312 | }
313 | }
314 |
315 | });
316 | }
317 | }
318 |
319 | });
320 | }
321 |
322 | });
323 | }
324 |
325 | }); // Traverse once again for useStaticQuery instances
326 |
327 | path.traverse({
328 | CallExpression(hookPath) {
329 | if (!isUseStaticQuery(hookPath)) return;
330 |
331 | function TaggedTemplateExpression(templatePath) {
332 | setImportForStaticQuery(templatePath);
333 | } // See if the query is a variable that's being passed in
334 | // and if it is, go find it.
335 |
336 |
337 | if (hookPath.node.arguments.length === 1 && hookPath.node.arguments[0].type === `Identifier`) {
338 | const [{
339 | name: varName
340 | }] = hookPath.node.arguments;
341 | let binding = hookPath.scope.getBinding(varName);
342 |
343 | if (binding) {
344 | followVariableDeclarations(binding).path.traverse({
345 | TaggedTemplateExpression
346 | });
347 | }
348 | }
349 |
350 | hookPath.traverse({
351 | // Assume the query is inline in the component and extract that.
352 | TaggedTemplateExpression
353 | });
354 | }
355 |
356 | }); // Run it again to remove non-staticquery versions
357 |
358 | path.traverse({
359 | TaggedTemplateExpression(path2, state) {
360 | const {
361 | ast,
362 | hash,
363 | text,
364 | isGlobal
365 | } = getGraphQLTag(path2);
366 | if (!ast) return null;
367 | const queryHash = hash.toString();
368 | const tag = path2.get(`tag`);
369 |
370 | if (!isGlobal) {
371 | // Enqueue import removal. If we would remove it here, subsequent named exports
372 | // wouldn't be handled properly
373 | tagsToRemoveImportsFrom.add(tag);
374 | } // Replace the query with the hash of the query.
375 | // path2.replaceWith(t.StringLiteral(queryHash))
376 |
377 |
378 | path2.replaceWith(getGraphqlExpr(t, queryHash, text));
379 | return null;
380 | }
381 |
382 | });
383 | tagsToRemoveImportsFrom.forEach(removeImport);
384 | }
385 |
386 | }
387 | };
388 | }
--------------------------------------------------------------------------------
/packages/gatsby-source-graphql-universal/src/babel-plugin-remove-graphql-queries.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable new-cap */
2 | const graphql = require(`gatsby/graphql`)
3 | const nodePath = require(`path`)
4 | const murmurModule = require(`babel-plugin-remove-graphql-queries/murmur`)
5 | const getGraphqlExpr = require('./getGraphqlExpr')
6 | const murmurhash = typeof murmurModule === 'function' ? murmurModule : murmurModule.murmurhash
7 |
8 | class StringInterpolationNotAllowedError extends Error {
9 | constructor(interpolationStart, interpolationEnd) {
10 | super(
11 | `BabelPluginRemoveGraphQLQueries: String interpolations are not allowed in graphql ` +
12 | `fragments. Included fragments should be referenced ` +
13 | `as \`...MyModule_foo\`.`
14 | )
15 | this.interpolationStart = JSON.parse(JSON.stringify(interpolationStart))
16 | this.interpolationEnd = JSON.parse(JSON.stringify(interpolationEnd))
17 | Error.captureStackTrace(this, StringInterpolationNotAllowedError)
18 | }
19 | }
20 |
21 | class EmptyGraphQLTagError extends Error {
22 | constructor(locationOfGraphqlString) {
23 | super(`BabelPluginRemoveGraphQLQueries: Unexpected empty graphql tag.`)
24 | this.templateLoc = locationOfGraphqlString
25 | Error.captureStackTrace(this, EmptyGraphQLTagError)
26 | }
27 | }
28 |
29 | class GraphQLSyntaxError extends Error {
30 | constructor(documentText, originalError, locationOfGraphqlString) {
31 | super(
32 | `BabelPluginRemoveGraphQLQueries: GraphQL syntax error in query:\n\n${documentText}\n\nmessage:\n\n${originalError}`
33 | )
34 | this.documentText = documentText
35 | this.originalError = originalError
36 | this.templateLoc = locationOfGraphqlString
37 | Error.captureStackTrace(this, GraphQLSyntaxError)
38 | }
39 | }
40 |
41 | const isGlobalIdentifier = (tag, tagName = `graphql`) =>
42 | tag.isIdentifier({ name: tagName }) && tag.scope.hasGlobal(tagName)
43 |
44 | export function followVariableDeclarations(binding) {
45 | const node = binding.path?.node
46 | if (
47 | node?.type === `VariableDeclarator` &&
48 | node?.id.type === `Identifier` &&
49 | node?.init?.type === `Identifier`
50 | ) {
51 | return followVariableDeclarations(
52 | binding.path.scope.getBinding(node.init.name)
53 | )
54 | }
55 | return binding
56 | }
57 |
58 | function getTagImport(tag) {
59 | const name = tag.node.name
60 | const binding = tag.scope.getBinding(name)
61 |
62 | if (!binding) return null
63 |
64 | const path = binding.path
65 | const parent = path.parentPath
66 |
67 | if (
68 | binding.kind === `module` &&
69 | parent.isImportDeclaration() &&
70 | parent.node.source.value === `gatsby`
71 | )
72 | return path
73 |
74 | if (
75 | path.isVariableDeclarator() &&
76 | path.get(`init`).isCallExpression() &&
77 | path.get(`init.callee`).isIdentifier({ name: `require` }) &&
78 | path.get(`init`).node.arguments[0].value === `gatsby`
79 | ) {
80 | const id = path.get(`id`)
81 | if (id.isObjectPattern()) {
82 | return id
83 | .get(`properties`)
84 | .find(path => path.get(`value`).node.name === name)
85 | }
86 | return id
87 | }
88 | return null
89 | }
90 |
91 | function isGraphqlTag(tag, tagName = `graphql`) {
92 | const isExpression = tag.isMemberExpression()
93 | const identifier = isExpression ? tag.get(`object`) : tag
94 |
95 | const importPath = getTagImport(identifier)
96 | if (!importPath) return isGlobalIdentifier(tag, tagName)
97 |
98 | if (
99 | isExpression &&
100 | (importPath.isImportNamespaceSpecifier() || importPath.isIdentifier())
101 | ) {
102 | return tag.get(`property`).node.name === tagName
103 | }
104 |
105 | if (importPath.isImportSpecifier())
106 | return importPath.node.imported.name === tagName
107 |
108 | if (importPath.isObjectProperty())
109 | return importPath.get(`key`).node.name === tagName
110 |
111 | return false
112 | }
113 |
114 | function removeImport(tag) {
115 | const isExpression = tag.isMemberExpression()
116 | const identifier = isExpression ? tag.get(`object`) : tag
117 | const importPath = getTagImport(identifier)
118 |
119 | const removeVariableDeclaration = statement => {
120 | let declaration = statement.findParent(p => p.isVariableDeclaration())
121 | if (declaration) {
122 | declaration.remove()
123 | }
124 | }
125 |
126 | if (!importPath) return
127 |
128 | const parent = importPath.parentPath
129 |
130 | if (importPath.isImportSpecifier()) {
131 | if (parent.node.specifiers.length === 1) parent.remove()
132 | else importPath.remove()
133 | }
134 | if (importPath.isObjectProperty()) {
135 | if (parent.node.properties.length === 1) {
136 | removeVariableDeclaration(importPath)
137 | } else importPath.remove()
138 | }
139 | if (importPath.isIdentifier()) {
140 | removeVariableDeclaration(importPath)
141 | }
142 | }
143 |
144 | function getGraphQLTag(path, tagName = `graphql`) {
145 | const tag = path.get(`tag`)
146 | const isGlobal = isGlobalIdentifier(tag, tagName)
147 |
148 | if (!isGlobal && !isGraphqlTag(tag, tagName)) return {}
149 |
150 | const quasis = path.node.quasi.quasis
151 |
152 | if (quasis.length !== 1) {
153 | throw new StringInterpolationNotAllowedError(
154 | quasis[0].loc.end,
155 | quasis[1].loc.start
156 | )
157 | }
158 |
159 | const text = quasis[0].value.raw
160 | const normalizedText = graphql.stripIgnoredCharacters(text)
161 |
162 | const hash = murmurhash(normalizedText, `abc`)
163 |
164 | try {
165 | const ast = graphql.parse(text)
166 |
167 | if (ast.definitions.length === 0) {
168 | throw new EmptyGraphQLTagError(quasis[0].loc)
169 | }
170 | return { ast, text: normalizedText, hash, isGlobal }
171 | } catch (err) {
172 | throw new GraphQLSyntaxError(text, err, quasis[0].loc)
173 | }
174 | }
175 |
176 | function isUseStaticQuery(path) {
177 | return (
178 | (path.node.callee.type === `MemberExpression` &&
179 | path.node.callee.property.name === `useStaticQuery` &&
180 | path.get(`callee`).get(`object`).referencesImport(`gatsby`)) ||
181 | (path.node.callee.name === `useStaticQuery` &&
182 | path.get(`callee`).referencesImport(`gatsby`))
183 | )
184 | }
185 |
186 | export default function ({ types: t }) {
187 | return {
188 | visitor: {
189 | Program(path, state) {
190 | const nestedJSXVistor = {
191 | JSXIdentifier(path2) {
192 | if (
193 | (process.env.NODE_ENV === `test` ||
194 | state.opts.stage === `build-html`) &&
195 | path2.isJSXIdentifier({ name: `StaticQuery` }) &&
196 | path2.referencesImport(`gatsby`) &&
197 | path2.parent.type !== `JSXClosingElement`
198 | ) {
199 | const identifier = t.identifier(`staticQueryData`)
200 | const filename = state.file.opts.filename
201 | const staticQueryDir = state.opts.staticQueryDir || `static/d`
202 | const shortResultPath = `public/${staticQueryDir}/${this.queryHash}.json`
203 | const resultPath = nodePath.join(process.cwd(), shortResultPath)
204 | // Add query
205 | path2.parent.attributes.push(
206 | t.jSXAttribute(
207 | t.jSXIdentifier(`data`),
208 | t.jSXExpressionContainer(identifier)
209 | )
210 | )
211 | // Add import
212 | const importDefaultSpecifier = t.importDefaultSpecifier(
213 | identifier
214 | )
215 | const importDeclaration = t.importDeclaration(
216 | [importDefaultSpecifier],
217 | t.stringLiteral(
218 | filename
219 | ? nodePath.relative(
220 | nodePath.parse(filename).dir,
221 | resultPath
222 | )
223 | : shortResultPath
224 | )
225 | )
226 | path.unshiftContainer(`body`, importDeclaration)
227 | }
228 | },
229 | }
230 |
231 | const nestedHookVisitor = {
232 | CallExpression(path2) {
233 | if (
234 | (process.env.NODE_ENV === `test` ||
235 | state.opts.stage === `build-html`) &&
236 | isUseStaticQuery(path2)
237 | ) {
238 | const identifier = t.identifier(`staticQueryData`)
239 | const filename = state.file.opts.filename
240 | const staticQueryDir = state.opts.staticQueryDir || `static/d`
241 | const shortResultPath = `public/${staticQueryDir}/${this.queryHash}.json`
242 | const resultPath = nodePath.join(process.cwd(), shortResultPath)
243 |
244 | // only remove the import if its like:
245 | // import { useStaticQuery } from 'gatsby'
246 | // but not if its like:
247 | // import * as Gatsby from 'gatsby'
248 | // because we know we can remove the useStaticQuery import,
249 | // but we don't know if other 'gatsby' exports are used, so we
250 | // cannot remove all 'gatsby' imports.
251 | if (path2.node.callee.type !== `MemberExpression`) {
252 | // Remove imports to useStaticQuery
253 | const importPath = path2.scope.getBinding(`useStaticQuery`).path
254 | const parent = importPath.parentPath
255 | if (importPath.isImportSpecifier())
256 | if (parent.node.specifiers.length === 1) parent.remove()
257 | else importPath.remove()
258 | }
259 |
260 | // Add query
261 |
262 | path2.replaceWith(
263 | getGraphqlExpr(t, this.queryHash, this.query)
264 | )
265 | path2.replaceWith(
266 | t.memberExpression(identifier, t.identifier(`data`))
267 | )
268 |
269 | // Add import
270 | const importDefaultSpecifier = t.importDefaultSpecifier(
271 | identifier
272 | )
273 | const importDeclaration = t.importDeclaration(
274 | [importDefaultSpecifier],
275 | t.stringLiteral(
276 | filename
277 | ? nodePath.relative(
278 | nodePath.parse(filename).dir,
279 | resultPath
280 | )
281 | : shortResultPath
282 | )
283 | )
284 | path.unshiftContainer(`body`, importDeclaration)
285 | }
286 | },
287 | }
288 |
289 | const tagsToRemoveImportsFrom = new Set()
290 |
291 | const setImportForStaticQuery = templatePath => {
292 | const { ast, text, hash, isGlobal } = getGraphQLTag(templatePath)
293 |
294 | if (!ast) return null
295 |
296 | const queryHash = hash.toString()
297 | const query = text
298 |
299 | const tag = templatePath.get(`tag`)
300 | if (!isGlobal) {
301 | // Enqueue import removal. If we would remove it here, subsequent named exports
302 | // wouldn't be handled properly
303 | tagsToRemoveImportsFrom.add(tag)
304 | }
305 |
306 | // Replace the query with the hash of the query.
307 | // templatePath.replaceWith(t.StringLiteral(queryHash))
308 | templatePath.replaceWith(getGraphqlExpr(t, queryHash, text))
309 |
310 | // traverse upwards until we find top-level JSXOpeningElement or Program
311 | // this handles exported queries and variable queries
312 | let parent = templatePath
313 | while (
314 | parent &&
315 | ![`Program`, `JSXOpeningElement`].includes(parent.node.type)
316 | ) {
317 | parent = parent.parentPath
318 | }
319 |
320 | // modify StaticQuery elements and import data only if query is inside StaticQuery
321 | parent.traverse(nestedJSXVistor, {
322 | queryHash,
323 | query,
324 | })
325 |
326 | // modify useStaticQuery elements and import data only if query is inside useStaticQuery
327 | parent.traverse(nestedHookVisitor, {
328 | queryHash,
329 | query,
330 | templatePath,
331 | })
332 |
333 | return null
334 | }
335 |
336 | // Traverse for instances
337 | path.traverse({
338 | JSXElement(jsxElementPath) {
339 | if (
340 | jsxElementPath.node.openingElement.name.name !== `StaticQuery`
341 | ) {
342 | return
343 | }
344 |
345 | jsxElementPath.traverse({
346 | JSXAttribute(jsxPath) {
347 | if (jsxPath.node.name.name !== `query`) {
348 | return
349 | }
350 | jsxPath.traverse({
351 | TaggedTemplateExpression(templatePath, state) {
352 | setImportForStaticQuery(templatePath)
353 | },
354 | Identifier(identifierPath) {
355 | if (identifierPath.node.name !== `graphql`) {
356 | const varName = identifierPath.node.name
357 | path.traverse({
358 | VariableDeclarator(varPath) {
359 | if (
360 | varPath.node.id.name === varName &&
361 | varPath.node.init.type ===
362 | `TaggedTemplateExpression`
363 | ) {
364 | varPath.traverse({
365 | TaggedTemplateExpression(templatePath) {
366 | setImportForStaticQuery(templatePath)
367 | },
368 | })
369 | }
370 | },
371 | })
372 | }
373 | },
374 | })
375 | },
376 | })
377 | },
378 | })
379 |
380 | // Traverse once again for useStaticQuery instances
381 | path.traverse({
382 | CallExpression(hookPath) {
383 | if (!isUseStaticQuery(hookPath)) return
384 |
385 | function TaggedTemplateExpression(templatePath) {
386 | setImportForStaticQuery(templatePath)
387 | }
388 |
389 | // See if the query is a variable that's being passed in
390 | // and if it is, go find it.
391 | if (
392 | hookPath.node.arguments.length === 1 &&
393 | hookPath.node.arguments[0].type === `Identifier`
394 | ) {
395 | const [{ name: varName }] = hookPath.node.arguments
396 |
397 | let binding = hookPath.scope.getBinding(varName)
398 |
399 | if (binding) {
400 | followVariableDeclarations(binding).path.traverse({
401 | TaggedTemplateExpression,
402 | })
403 | }
404 | }
405 |
406 | hookPath.traverse({
407 | // Assume the query is inline in the component and extract that.
408 | TaggedTemplateExpression,
409 | })
410 | },
411 | })
412 |
413 | // Run it again to remove non-staticquery versions
414 | path.traverse({
415 | TaggedTemplateExpression(path2, state) {
416 | const { ast, hash, text, isGlobal } = getGraphQLTag(path2)
417 |
418 | if (!ast) return null
419 |
420 | const queryHash = hash.toString()
421 |
422 | const tag = path2.get(`tag`)
423 | if (!isGlobal) {
424 | // Enqueue import removal. If we would remove it here, subsequent named exports
425 | // wouldn't be handled properly
426 | tagsToRemoveImportsFrom.add(tag)
427 | }
428 |
429 | // Replace the query with the hash of the query.
430 | // path2.replaceWith(t.StringLiteral(queryHash))
431 | path2.replaceWith(getGraphqlExpr(t, queryHash, text))
432 | return null
433 | },
434 | })
435 |
436 | tagsToRemoveImportsFrom.forEach(removeImport)
437 | },
438 | },
439 | }
440 | }
441 |
442 | export {
443 | getGraphQLTag,
444 | StringInterpolationNotAllowedError,
445 | EmptyGraphQLTagError,
446 | GraphQLSyntaxError,
447 | murmurhash,
448 | }
--------------------------------------------------------------------------------