Skip to content

Commit d68ffec

Browse files
committed
Add loader for .graqhql files
See: facebook/create-react-app#3909
1 parent f1e5c22 commit d68ffec

File tree

10 files changed

+167
-42
lines changed

10 files changed

+167
-42
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// @remove-on-eject-begin
2+
/**
3+
* Copyright (c) 2018-present, Facebook, Inc.
4+
* Copyright (c) 2016 Remind
5+
*
6+
* This source code is licensed under the MIT license found in the
7+
* LICENSE file in the root directory of this source tree.
8+
*/
9+
// @remove-on-eject-end
10+
'use strict';
11+
12+
const loader = require('graphql-tag/loader');
13+
14+
module.exports = {
15+
process(src) {
16+
return loader.call({ cacheable() {} }, src);
17+
},
18+
};

packages/react-scripts/config/webpack.config.dev.js

+5
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,11 @@ module.exports = {
183183
},
184184
],
185185
},
186+
// The GraphQL loader preprocesses GraphQL queries in .graphql files.
187+
{
188+
test: /\.(graphql)$/,
189+
loader: 'graphql-tag/loader',
190+
},
186191
// "postcss" loader applies autoprefixer to our CSS.
187192
// "css" loader resolves paths in CSS and adds assets as dependencies.
188193
// "style" loader turns CSS into JS modules that inject <style> tags.

packages/react-scripts/fixtures/kitchensink/integration/webpack.test.js

+10
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,16 @@ describe('Integration', () => {
2121
).to.match(/#feature-css-inclusion\{background:.+;color:.+}/);
2222
});
2323

24+
it('graphql files inclusion', async () => {
25+
const doc = await initDOM('graphql-inclusion');
26+
const children = doc.getElementById('graphql-inclusion').children;
27+
28+
// .graphql
29+
expect(children[0].textContent.replace(/\s/g, '')).to.equal(
30+
'{"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","variableDefinitions":[],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"test"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"test"},"value":{"kind":"StringValue","value":"test","block":false}}],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"test"},"arguments":[],"directives":[]}]}}]}}],"loc":{"start":0,"end":40,"source":{"body":"{\\ntest(test:\\"test\\"){\\ntest\\n}\\n}\\n","name":"GraphQLrequest","locationOffset":{"line":1,"column":1}}}}'
31+
);
32+
});
33+
2434
it('image inclusion', async () => {
2535
const doc = await initDOM('image-inclusion');
2636

packages/react-scripts/fixtures/kitchensink/src/App.js

+62-39
Original file line numberDiff line numberDiff line change
@@ -52,114 +52,137 @@ class App extends Component {
5252
const feature = window.location.hash.slice(1);
5353
switch (feature) {
5454
case 'array-destructuring':
55-
import(
56-
'./features/syntax/ArrayDestructuring'
57-
).then(f => this.setFeature(f.default));
55+
import('./features/syntax/ArrayDestructuring').then(f =>
56+
this.setFeature(f.default)
57+
);
5858
break;
5959
case 'array-spread':
6060
import('./features/syntax/ArraySpread').then(f =>
61-
this.setFeature(f.default));
61+
this.setFeature(f.default)
62+
);
6263
break;
6364
case 'async-await':
6465
import('./features/syntax/AsyncAwait').then(f =>
65-
this.setFeature(f.default));
66+
this.setFeature(f.default)
67+
);
6668
break;
6769
case 'class-properties':
6870
import('./features/syntax/ClassProperties').then(f =>
69-
this.setFeature(f.default));
71+
this.setFeature(f.default)
72+
);
7073
break;
7174
case 'computed-properties':
72-
import(
73-
'./features/syntax/ComputedProperties'
74-
).then(f => this.setFeature(f.default));
75+
import('./features/syntax/ComputedProperties').then(f =>
76+
this.setFeature(f.default)
77+
);
7578
break;
7679
case 'css-inclusion':
7780
import('./features/webpack/CssInclusion').then(f =>
78-
this.setFeature(f.default));
81+
this.setFeature(f.default)
82+
);
7983
break;
8084
case 'custom-interpolation':
81-
import(
82-
'./features/syntax/CustomInterpolation'
83-
).then(f => this.setFeature(f.default));
85+
import('./features/syntax/CustomInterpolation').then(f =>
86+
this.setFeature(f.default)
87+
);
8488
break;
8589
case 'default-parameters':
8690
import('./features/syntax/DefaultParameters').then(f =>
87-
this.setFeature(f.default));
91+
this.setFeature(f.default)
92+
);
8893
break;
8994
case 'destructuring-and-await':
90-
import(
91-
'./features/syntax/DestructuringAndAwait'
92-
).then(f => this.setFeature(f.default));
95+
import('./features/syntax/DestructuringAndAwait').then(f =>
96+
this.setFeature(f.default)
97+
);
9398
break;
9499
case 'file-env-variables':
95100
import('./features/env/FileEnvVariables').then(f =>
96-
this.setFeature(f.default));
101+
this.setFeature(f.default)
102+
);
97103
break;
98104
case 'generators':
99105
import('./features/syntax/Generators').then(f =>
100-
this.setFeature(f.default));
106+
this.setFeature(f.default)
107+
);
108+
break;
109+
case 'graphql-inclusion':
110+
import('./features/webpack/GraphQLInclusion').then(f =>
111+
this.setFeature(f.default)
112+
);
101113
break;
102114
case 'image-inclusion':
103115
import('./features/webpack/ImageInclusion').then(f =>
104-
this.setFeature(f.default));
116+
this.setFeature(f.default)
117+
);
105118
break;
106119
case 'json-inclusion':
107120
import('./features/webpack/JsonInclusion').then(f =>
108-
this.setFeature(f.default));
121+
this.setFeature(f.default)
122+
);
109123
break;
110124
case 'linked-modules':
111125
import('./features/webpack/LinkedModules').then(f =>
112-
this.setFeature(f.default));
126+
this.setFeature(f.default)
127+
);
113128
break;
114129
case 'node-path':
115130
import('./features/env/NodePath').then(f => this.setFeature(f.default));
116131
break;
117132
case 'no-ext-inclusion':
118133
import('./features/webpack/NoExtInclusion').then(f =>
119-
this.setFeature(f.default));
134+
this.setFeature(f.default)
135+
);
120136
break;
121137
case 'object-destructuring':
122-
import(
123-
'./features/syntax/ObjectDestructuring'
124-
).then(f => this.setFeature(f.default));
138+
import('./features/syntax/ObjectDestructuring').then(f =>
139+
this.setFeature(f.default)
140+
);
125141
break;
126142
case 'object-spread':
127143
import('./features/syntax/ObjectSpread').then(f =>
128-
this.setFeature(f.default));
144+
this.setFeature(f.default)
145+
);
129146
break;
130147
case 'promises':
131148
import('./features/syntax/Promises').then(f =>
132-
this.setFeature(f.default));
149+
this.setFeature(f.default)
150+
);
133151
break;
134152
case 'public-url':
135153
import('./features/env/PublicUrl').then(f =>
136-
this.setFeature(f.default));
154+
this.setFeature(f.default)
155+
);
137156
break;
138157
case 'rest-and-default':
139158
import('./features/syntax/RestAndDefault').then(f =>
140-
this.setFeature(f.default));
159+
this.setFeature(f.default)
160+
);
141161
break;
142162
case 'rest-parameters':
143163
import('./features/syntax/RestParameters').then(f =>
144-
this.setFeature(f.default));
164+
this.setFeature(f.default)
165+
);
145166
break;
146167
case 'shell-env-variables':
147168
import('./features/env/ShellEnvVariables').then(f =>
148-
this.setFeature(f.default));
169+
this.setFeature(f.default)
170+
);
149171
break;
150172
case 'svg-inclusion':
151173
import('./features/webpack/SvgInclusion').then(f =>
152-
this.setFeature(f.default));
174+
this.setFeature(f.default)
175+
);
153176
break;
154177
case 'template-interpolation':
155-
import(
156-
'./features/syntax/TemplateInterpolation'
157-
).then(f => this.setFeature(f.default));
178+
import('./features/syntax/TemplateInterpolation').then(f =>
179+
this.setFeature(f.default)
180+
);
158181
break;
159182
case 'unknown-ext-inclusion':
160-
import(
161-
'./features/webpack/UnknownExtInclusion'
162-
).then(f => this.setFeature(f.default));
183+
import('./features/webpack/UnknownExtInclusion').then(f =>
184+
this.setFeature(f.default)
185+
);
163186
break;
164187
case 'expand-env-variables':
165188
import('./features/env/ExpandEnvVariables').then(f =>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/**
2+
* Copyright (c) 2018-present, Facebook, Inc.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
import React from 'react';
9+
import A from './assets/graphql.graphql';
10+
11+
export default () => (
12+
<p id="graphql-inclusion">
13+
<span>{JSON.stringify(A)}</span>
14+
</p>
15+
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/**
2+
* Copyright (c) 2018-present, Facebook, Inc.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
import React from 'react';
9+
import ReactDOM from 'react-dom';
10+
import GraphQLInclusion from './GraphQLInclusion';
11+
12+
describe('graphql files inclusion', () => {
13+
it('renders without crashing', () => {
14+
const div = document.createElement('div');
15+
ReactDOM.render(<GraphQLInclusion />, div);
16+
});
17+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
test(test: "test") {
3+
test
4+
}
5+
}

packages/react-scripts/package.json

+2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
"file-loader": "0.11.2",
3535
"fork-ts-checker-webpack-plugin": "^0.2.8",
3636
"fs-extra": "3.0.1",
37+
"graphql": "0.12.3",
38+
"graphql-tag": "2.6.1",
3739
"html-webpack-plugin": "2.29.0",
3840
"jest": "22.4.2",
3941
"object-assign": "4.1.1",

packages/react-scripts/scripts/utils/createJestConfig.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ module.exports = (resolve, rootDir, isEjecting) => {
3636
: resolve('config/jest/babelTransform.js'),
3737
'^.+\\.tsx?$': resolve('config/jest/typescriptTransform.js'),
3838
'^.+\\.css$': resolve('config/jest/cssTransform.js'),
39-
'^(?!.*\\.(js|jsx|mjs|css|json)$)': resolve(
39+
'^.+\\.(graphql)$': resolve('config/jest/graphqlTransform.js'),
40+
'^(?!.*\\.(js|jsx|mjs|css|json|graphql)$)': resolve(
4041
'config/jest/fileTransform.js'
4142
),
4243
},

packages/react-scripts/template/README.md

+31-2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ You can find the most recent version of this guide [here](https://github.com/fac
2727
- [Post-Processing CSS](#post-processing-css)
2828
- [Adding a CSS Preprocessor (Sass, Less etc.)](#adding-a-css-preprocessor-sass-less-etc)
2929
- [Adding Images, Fonts, and Files](#adding-images-fonts-and-files)
30+
- [Adding GraphQL files](#adding-graphql-files)
3031
- [Using the `public` Folder](#using-the-public-folder)
3132
- [Changing the HTML](#changing-the-html)
3233
- [Adding Assets Outside of the Module System](#adding-assets-outside-of-the-module-system)
@@ -299,7 +300,7 @@ In the WebStorm menu `Run` select `Edit Configurations...`. Then click `+` and s
299300
300301
Start your app by running `npm start`, then press `^D` on macOS or `F9` on Windows and Linux or click the green debug icon to start debugging in WebStorm.
301302

302-
The same way you can debug your application in IntelliJ IDEA Ultimate, PhpStorm, PyCharm Pro, and RubyMine.
303+
The same way you can debug your application in IntelliJ IDEA Ultimate, PhpStorm, PyCharm Pro, and RubyMine.
303304

304305
## Formatting Code Automatically
305306

@@ -695,6 +696,34 @@ Please be advised that this is also a custom feature of Webpack.
695696
**It is not required for React** but many people enjoy it (and React Native uses a similar mechanism for images).<br>
696697
An alternative way of handling static assets is described in the next section.
697698

699+
## Adding GraphQL files
700+
701+
> Note: this feature is available with react-scripts@2.0.0 and higher.
702+
703+
If you are using GraphQL, you can **`import` GraphQL files in a JavaScript module**.
704+
705+
By importing GraphQL queries instead of using a [template tag](https://github.com/apollographql/graphql-tag), they are preprocessed at build time. This eliminates the need to process them on the client at run time. It also allows you to separate your GraphQL queries from your code. You can put a GraphQL query in a file with a `.graphql` extension.
706+
707+
Here is an example:
708+
709+
```js
710+
// query.graphql
711+
{
712+
githubStats(repository: "facebook/react") {
713+
stars
714+
}
715+
}
716+
717+
// foo.js
718+
719+
import query from './query.graphql';
720+
721+
console.log(query);
722+
// {
723+
// "kind": "Document",
724+
// ...
725+
```
726+
698727
## Using the `public` Folder
699728

700729
>Note: this feature is available with `react-scripts@0.5.0` and higher.
@@ -2002,7 +2031,7 @@ If you’re using [Apache HTTP Server](https://httpd.apache.org/), you need to c
20022031
RewriteRule ^ index.html [QSA,L]
20032032
```
20042033
2005-
It will get copied to the `build` folder when you run `npm run build`.
2034+
It will get copied to the `build` folder when you run `npm run build`.
20062035
20072036
If you’re using [Apache Tomcat](http://tomcat.apache.org/), you need to follow [this Stack Overflow answer](https://stackoverflow.com/a/41249464/4878474).
20082037

0 commit comments

Comments
 (0)