@graphql-tools/webpack-loader 6.6.3 → 6.7.0-alpha-b76ec274.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cjs/index.js +85 -0
- package/cjs/package.json +1 -0
- package/cjs/parser.js +68 -0
- package/{index.mjs → esm/index.js} +3 -69
- package/esm/parser.js +64 -0
- package/package.json +33 -12
- package/{index.d.ts → typings/index.d.ts} +0 -0
- package/{parser.d.ts → typings/parser.d.ts} +0 -0
- package/README.md +0 -55
- package/index.js +0 -151
package/cjs/index.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const os_1 = tslib_1.__importDefault(require("os"));
|
|
5
|
+
const graphql_1 = require("graphql");
|
|
6
|
+
const webpack_loader_runtime_1 = require("@graphql-tools/webpack-loader-runtime");
|
|
7
|
+
const parser_js_1 = require("./parser.js");
|
|
8
|
+
const optimize_1 = require("@graphql-tools/optimize");
|
|
9
|
+
function isSDL(doc) {
|
|
10
|
+
return !doc.definitions.some(def => (0, graphql_1.isExecutableDefinitionNode)(def));
|
|
11
|
+
}
|
|
12
|
+
function expandImports(source, options) {
|
|
13
|
+
const lines = source.split(/\r\n|\r|\n/);
|
|
14
|
+
let outputCode = options.importHelpers
|
|
15
|
+
? `
|
|
16
|
+
var useUnique = require('@graphql-tools/webpack-loader-runtime').useUnique;
|
|
17
|
+
var unique = useUnique();
|
|
18
|
+
`
|
|
19
|
+
: `
|
|
20
|
+
${webpack_loader_runtime_1.uniqueCode}
|
|
21
|
+
`;
|
|
22
|
+
lines.some(line => {
|
|
23
|
+
if (line[0] === '#' && line.slice(1).split(' ')[0] === 'import') {
|
|
24
|
+
const importFile = line.slice(1).split(' ')[1];
|
|
25
|
+
const parseDocument = `require(${importFile})`;
|
|
26
|
+
const appendDef = `doc.definitions = doc.definitions.concat(unique(${parseDocument}.definitions));`;
|
|
27
|
+
outputCode += appendDef + os_1.default.EOL;
|
|
28
|
+
}
|
|
29
|
+
return line.length !== 0 && line[0] !== '#';
|
|
30
|
+
});
|
|
31
|
+
return outputCode;
|
|
32
|
+
}
|
|
33
|
+
function graphqlLoader(source) {
|
|
34
|
+
this.cacheable();
|
|
35
|
+
// TODO: This should probably use this.getOptions()
|
|
36
|
+
const options = this.query || {};
|
|
37
|
+
let doc = (0, parser_js_1.parseDocument)(source);
|
|
38
|
+
const optimizers = [];
|
|
39
|
+
if (options.noDescription) {
|
|
40
|
+
optimizers.push(optimize_1.removeDescriptions);
|
|
41
|
+
}
|
|
42
|
+
if (options.noEmptyNodes) {
|
|
43
|
+
optimizers.push(optimize_1.removeEmptyNodes);
|
|
44
|
+
}
|
|
45
|
+
if (optimizers.length > 0 && isSDL(doc)) {
|
|
46
|
+
doc = (0, optimize_1.optimizeDocumentNode)(doc, optimizers);
|
|
47
|
+
}
|
|
48
|
+
let stringifiedDoc = JSON.stringify(doc);
|
|
49
|
+
if (options.replaceKinds) {
|
|
50
|
+
for (const identifier in graphql_1.Kind) {
|
|
51
|
+
const value = graphql_1.Kind[identifier];
|
|
52
|
+
stringifiedDoc = stringifiedDoc.replace(new RegExp(`"kind":"${value}"`, 'g'), `"kind": Kind.${identifier}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const headerCode = `
|
|
56
|
+
${options.replaceKinds ? "var Kind = require('graphql/language/kinds');" : ''}
|
|
57
|
+
var doc = ${stringifiedDoc};
|
|
58
|
+
`;
|
|
59
|
+
let outputCode = '';
|
|
60
|
+
// Allow multiple query/mutation definitions in a file. This parses out dependencies
|
|
61
|
+
// at compile time, and then uses those at load time to create minimal query documents
|
|
62
|
+
// We cannot do the latter at compile time due to how the #import code works.
|
|
63
|
+
const operationCount = doc.definitions.reduce((accum, op) => {
|
|
64
|
+
if (op.kind === graphql_1.Kind.OPERATION_DEFINITION) {
|
|
65
|
+
return accum + 1;
|
|
66
|
+
}
|
|
67
|
+
return accum;
|
|
68
|
+
}, 0);
|
|
69
|
+
function exportDefaultStatement(identifier) {
|
|
70
|
+
if (options.esModule) {
|
|
71
|
+
return `export default ${identifier}`;
|
|
72
|
+
}
|
|
73
|
+
return `module.exports = ${identifier}`;
|
|
74
|
+
}
|
|
75
|
+
if (operationCount > 1) {
|
|
76
|
+
throw new Error('GraphQL Webpack Loader allows only for one GraphQL Operation per file');
|
|
77
|
+
}
|
|
78
|
+
outputCode += `
|
|
79
|
+
${exportDefaultStatement('doc')}
|
|
80
|
+
`;
|
|
81
|
+
const importOutputCode = expandImports(source, options);
|
|
82
|
+
const allCode = [headerCode, importOutputCode, outputCode, ''].join(os_1.default.EOL);
|
|
83
|
+
return allCode;
|
|
84
|
+
}
|
|
85
|
+
exports.default = graphqlLoader;
|
package/cjs/package.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"commonjs"}
|
package/cjs/parser.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseDocument = void 0;
|
|
4
|
+
const graphql_1 = require("graphql");
|
|
5
|
+
/**
|
|
6
|
+
* Strip insignificant whitespace
|
|
7
|
+
* Note that this could do a lot more, such as reorder fields etc.
|
|
8
|
+
*/
|
|
9
|
+
function normalize(str) {
|
|
10
|
+
return str.replace(/[\s,]+/g, ' ').trim();
|
|
11
|
+
}
|
|
12
|
+
// A map docString -> graphql document
|
|
13
|
+
const docCache = {};
|
|
14
|
+
// A map fragmentName -> [normalized source]
|
|
15
|
+
const fragmentSourceMap = {};
|
|
16
|
+
function cacheKeyFromFragment(fragment) {
|
|
17
|
+
return normalize((0, graphql_1.print)(fragment));
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Take a unstripped parsed document (query/mutation or even fragment), and
|
|
21
|
+
* check all fragment definitions, checking for name->source uniqueness.
|
|
22
|
+
* We also want to make sure only unique fragments exist in the document.
|
|
23
|
+
*/
|
|
24
|
+
function processFragments(ast) {
|
|
25
|
+
const astFragmentMap = {};
|
|
26
|
+
const definitions = [];
|
|
27
|
+
for (let i = 0; i < ast.definitions.length; i++) {
|
|
28
|
+
const fragmentDefinition = ast.definitions[i];
|
|
29
|
+
if (fragmentDefinition.kind === graphql_1.Kind.FRAGMENT_DEFINITION) {
|
|
30
|
+
const fragmentName = fragmentDefinition.name.value;
|
|
31
|
+
const sourceKey = cacheKeyFromFragment(fragmentDefinition);
|
|
32
|
+
// We know something about this fragment
|
|
33
|
+
if (fragmentSourceMap.hasOwnProperty(fragmentName) && !fragmentSourceMap[fragmentName][sourceKey]) {
|
|
34
|
+
fragmentSourceMap[fragmentName][sourceKey] = true;
|
|
35
|
+
}
|
|
36
|
+
else if (!fragmentSourceMap.hasOwnProperty(fragmentName)) {
|
|
37
|
+
fragmentSourceMap[fragmentName] = {};
|
|
38
|
+
fragmentSourceMap[fragmentName][sourceKey] = true;
|
|
39
|
+
}
|
|
40
|
+
if (!astFragmentMap[sourceKey]) {
|
|
41
|
+
astFragmentMap[sourceKey] = true;
|
|
42
|
+
definitions.push(fragmentDefinition);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
definitions.push(fragmentDefinition);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
ast.definitions = definitions;
|
|
50
|
+
return ast;
|
|
51
|
+
}
|
|
52
|
+
function parseDocument(doc) {
|
|
53
|
+
const cacheKey = normalize(doc);
|
|
54
|
+
if (docCache[cacheKey]) {
|
|
55
|
+
return docCache[cacheKey];
|
|
56
|
+
}
|
|
57
|
+
const parsed = (0, graphql_1.parse)(doc, {
|
|
58
|
+
noLocation: true,
|
|
59
|
+
});
|
|
60
|
+
if (!parsed || parsed.kind !== 'Document') {
|
|
61
|
+
throw new Error('Not a valid GraphQL document.');
|
|
62
|
+
}
|
|
63
|
+
// check that all "new" fragments inside the documents are consistent with
|
|
64
|
+
// existing fragments of the same name
|
|
65
|
+
docCache[cacheKey] = processFragments(parsed);
|
|
66
|
+
return parsed;
|
|
67
|
+
}
|
|
68
|
+
exports.parseDocument = parseDocument;
|
|
@@ -1,72 +1,8 @@
|
|
|
1
1
|
import os from 'os';
|
|
2
|
-
import {
|
|
2
|
+
import { isExecutableDefinitionNode, Kind } from 'graphql';
|
|
3
3
|
import { uniqueCode } from '@graphql-tools/webpack-loader-runtime';
|
|
4
|
+
import { parseDocument } from './parser.js';
|
|
4
5
|
import { optimizeDocumentNode, removeDescriptions, removeEmptyNodes } from '@graphql-tools/optimize';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Strip insignificant whitespace
|
|
8
|
-
* Note that this could do a lot more, such as reorder fields etc.
|
|
9
|
-
*/
|
|
10
|
-
function normalize(str) {
|
|
11
|
-
return str.replace(/[\s,]+/g, ' ').trim();
|
|
12
|
-
}
|
|
13
|
-
// A map docString -> graphql document
|
|
14
|
-
const docCache = {};
|
|
15
|
-
// A map fragmentName -> [normalized source]
|
|
16
|
-
const fragmentSourceMap = {};
|
|
17
|
-
function cacheKeyFromFragment(fragment) {
|
|
18
|
-
return normalize(print(fragment));
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Take a unstripped parsed document (query/mutation or even fragment), and
|
|
22
|
-
* check all fragment definitions, checking for name->source uniqueness.
|
|
23
|
-
* We also want to make sure only unique fragments exist in the document.
|
|
24
|
-
*/
|
|
25
|
-
function processFragments(ast) {
|
|
26
|
-
const astFragmentMap = {};
|
|
27
|
-
const definitions = [];
|
|
28
|
-
for (let i = 0; i < ast.definitions.length; i++) {
|
|
29
|
-
const fragmentDefinition = ast.definitions[i];
|
|
30
|
-
if (fragmentDefinition.kind === Kind.FRAGMENT_DEFINITION) {
|
|
31
|
-
const fragmentName = fragmentDefinition.name.value;
|
|
32
|
-
const sourceKey = cacheKeyFromFragment(fragmentDefinition);
|
|
33
|
-
// We know something about this fragment
|
|
34
|
-
if (fragmentSourceMap.hasOwnProperty(fragmentName) && !fragmentSourceMap[fragmentName][sourceKey]) {
|
|
35
|
-
fragmentSourceMap[fragmentName][sourceKey] = true;
|
|
36
|
-
}
|
|
37
|
-
else if (!fragmentSourceMap.hasOwnProperty(fragmentName)) {
|
|
38
|
-
fragmentSourceMap[fragmentName] = {};
|
|
39
|
-
fragmentSourceMap[fragmentName][sourceKey] = true;
|
|
40
|
-
}
|
|
41
|
-
if (!astFragmentMap[sourceKey]) {
|
|
42
|
-
astFragmentMap[sourceKey] = true;
|
|
43
|
-
definitions.push(fragmentDefinition);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
else {
|
|
47
|
-
definitions.push(fragmentDefinition);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
ast.definitions = definitions;
|
|
51
|
-
return ast;
|
|
52
|
-
}
|
|
53
|
-
function parseDocument(doc) {
|
|
54
|
-
const cacheKey = normalize(doc);
|
|
55
|
-
if (docCache[cacheKey]) {
|
|
56
|
-
return docCache[cacheKey];
|
|
57
|
-
}
|
|
58
|
-
const parsed = parse(doc, {
|
|
59
|
-
noLocation: true,
|
|
60
|
-
});
|
|
61
|
-
if (!parsed || parsed.kind !== 'Document') {
|
|
62
|
-
throw new Error('Not a valid GraphQL document.');
|
|
63
|
-
}
|
|
64
|
-
// check that all "new" fragments inside the documents are consistent with
|
|
65
|
-
// existing fragments of the same name
|
|
66
|
-
docCache[cacheKey] = processFragments(parsed);
|
|
67
|
-
return parsed;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
6
|
function isSDL(doc) {
|
|
71
7
|
return !doc.definitions.some(def => isExecutableDefinitionNode(def));
|
|
72
8
|
}
|
|
@@ -91,7 +27,7 @@ function expandImports(source, options) {
|
|
|
91
27
|
});
|
|
92
28
|
return outputCode;
|
|
93
29
|
}
|
|
94
|
-
function graphqlLoader(source) {
|
|
30
|
+
export default function graphqlLoader(source) {
|
|
95
31
|
this.cacheable();
|
|
96
32
|
// TODO: This should probably use this.getOptions()
|
|
97
33
|
const options = this.query || {};
|
|
@@ -143,5 +79,3 @@ function graphqlLoader(source) {
|
|
|
143
79
|
const allCode = [headerCode, importOutputCode, outputCode, ''].join(os.EOL);
|
|
144
80
|
return allCode;
|
|
145
81
|
}
|
|
146
|
-
|
|
147
|
-
export default graphqlLoader;
|
package/esm/parser.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { parse, print, Kind } from 'graphql';
|
|
2
|
+
/**
|
|
3
|
+
* Strip insignificant whitespace
|
|
4
|
+
* Note that this could do a lot more, such as reorder fields etc.
|
|
5
|
+
*/
|
|
6
|
+
function normalize(str) {
|
|
7
|
+
return str.replace(/[\s,]+/g, ' ').trim();
|
|
8
|
+
}
|
|
9
|
+
// A map docString -> graphql document
|
|
10
|
+
const docCache = {};
|
|
11
|
+
// A map fragmentName -> [normalized source]
|
|
12
|
+
const fragmentSourceMap = {};
|
|
13
|
+
function cacheKeyFromFragment(fragment) {
|
|
14
|
+
return normalize(print(fragment));
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Take a unstripped parsed document (query/mutation or even fragment), and
|
|
18
|
+
* check all fragment definitions, checking for name->source uniqueness.
|
|
19
|
+
* We also want to make sure only unique fragments exist in the document.
|
|
20
|
+
*/
|
|
21
|
+
function processFragments(ast) {
|
|
22
|
+
const astFragmentMap = {};
|
|
23
|
+
const definitions = [];
|
|
24
|
+
for (let i = 0; i < ast.definitions.length; i++) {
|
|
25
|
+
const fragmentDefinition = ast.definitions[i];
|
|
26
|
+
if (fragmentDefinition.kind === Kind.FRAGMENT_DEFINITION) {
|
|
27
|
+
const fragmentName = fragmentDefinition.name.value;
|
|
28
|
+
const sourceKey = cacheKeyFromFragment(fragmentDefinition);
|
|
29
|
+
// We know something about this fragment
|
|
30
|
+
if (fragmentSourceMap.hasOwnProperty(fragmentName) && !fragmentSourceMap[fragmentName][sourceKey]) {
|
|
31
|
+
fragmentSourceMap[fragmentName][sourceKey] = true;
|
|
32
|
+
}
|
|
33
|
+
else if (!fragmentSourceMap.hasOwnProperty(fragmentName)) {
|
|
34
|
+
fragmentSourceMap[fragmentName] = {};
|
|
35
|
+
fragmentSourceMap[fragmentName][sourceKey] = true;
|
|
36
|
+
}
|
|
37
|
+
if (!astFragmentMap[sourceKey]) {
|
|
38
|
+
astFragmentMap[sourceKey] = true;
|
|
39
|
+
definitions.push(fragmentDefinition);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
definitions.push(fragmentDefinition);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
ast.definitions = definitions;
|
|
47
|
+
return ast;
|
|
48
|
+
}
|
|
49
|
+
export function parseDocument(doc) {
|
|
50
|
+
const cacheKey = normalize(doc);
|
|
51
|
+
if (docCache[cacheKey]) {
|
|
52
|
+
return docCache[cacheKey];
|
|
53
|
+
}
|
|
54
|
+
const parsed = parse(doc, {
|
|
55
|
+
noLocation: true,
|
|
56
|
+
});
|
|
57
|
+
if (!parsed || parsed.kind !== 'Document') {
|
|
58
|
+
throw new Error('Not a valid GraphQL document.');
|
|
59
|
+
}
|
|
60
|
+
// check that all "new" fragments inside the documents are consistent with
|
|
61
|
+
// existing fragments of the same name
|
|
62
|
+
docCache[cacheKey] = processFragments(parsed);
|
|
63
|
+
return parsed;
|
|
64
|
+
}
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@graphql-tools/webpack-loader",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.7.0-alpha-b76ec274.0",
|
|
4
4
|
"description": "A set of utils for faster development of GraphQL tools",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"peerDependencies": {
|
|
7
7
|
"graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"@graphql-tools/optimize": "1.
|
|
11
|
-
"@graphql-tools/webpack-loader-runtime": "6.
|
|
10
|
+
"@graphql-tools/optimize": "1.3.0-alpha-b76ec274.0",
|
|
11
|
+
"@graphql-tools/webpack-loader-runtime": "6.4.0-alpha-b76ec274.0",
|
|
12
12
|
"tslib": "^2.4.0"
|
|
13
13
|
},
|
|
14
14
|
"repository": {
|
|
@@ -17,21 +17,42 @@
|
|
|
17
17
|
"directory": "packages/webpack-loader"
|
|
18
18
|
},
|
|
19
19
|
"license": "MIT",
|
|
20
|
-
"main": "index.js",
|
|
21
|
-
"module": "index.
|
|
22
|
-
"typings": "index.d.ts",
|
|
20
|
+
"main": "cjs/index.js",
|
|
21
|
+
"module": "esm/index.js",
|
|
22
|
+
"typings": "typings/index.d.ts",
|
|
23
23
|
"typescript": {
|
|
24
|
-
"definition": "index.d.ts"
|
|
24
|
+
"definition": "typings/index.d.ts"
|
|
25
25
|
},
|
|
26
|
+
"type": "module",
|
|
26
27
|
"exports": {
|
|
27
28
|
".": {
|
|
28
|
-
"require":
|
|
29
|
-
|
|
29
|
+
"require": {
|
|
30
|
+
"types": "./typings/index.d.ts",
|
|
31
|
+
"default": "./cjs/index.js"
|
|
32
|
+
},
|
|
33
|
+
"import": {
|
|
34
|
+
"types": "./typings/index.d.ts",
|
|
35
|
+
"default": "./esm/index.js"
|
|
36
|
+
},
|
|
37
|
+
"default": {
|
|
38
|
+
"types": "./typings/index.d.ts",
|
|
39
|
+
"default": "./esm/index.js"
|
|
40
|
+
}
|
|
30
41
|
},
|
|
31
42
|
"./*": {
|
|
32
|
-
"require":
|
|
33
|
-
|
|
43
|
+
"require": {
|
|
44
|
+
"types": "./typings/*.d.ts",
|
|
45
|
+
"default": "./cjs/*.js"
|
|
46
|
+
},
|
|
47
|
+
"import": {
|
|
48
|
+
"types": "./typings/*.d.ts",
|
|
49
|
+
"default": "./esm/*.js"
|
|
50
|
+
},
|
|
51
|
+
"default": {
|
|
52
|
+
"types": "./typings/*.d.ts",
|
|
53
|
+
"default": "./esm/*.js"
|
|
54
|
+
}
|
|
34
55
|
},
|
|
35
56
|
"./package.json": "./package.json"
|
|
36
57
|
}
|
|
37
|
-
}
|
|
58
|
+
}
|
|
File without changes
|
|
File without changes
|
package/README.md
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
# GraphQL Tools Webpack Loader
|
|
2
|
-
|
|
3
|
-
A webpack loader to preprocess GraphQL Documents (operations, fragments and SDL)
|
|
4
|
-
|
|
5
|
-
Slightly different fork of [graphql-tag/loader](https://github.com/apollographql/graphql-tag/pull/304).
|
|
6
|
-
|
|
7
|
-
yarn add @graphql-tools/webpack-loader
|
|
8
|
-
|
|
9
|
-
How is it different from `graphql-tag`? It removes locations entirely, doesn't include sources (string content of imported files), no warnings about duplicated fragment names and supports more custom scenarios.
|
|
10
|
-
|
|
11
|
-
## Options
|
|
12
|
-
|
|
13
|
-
- noDescription (_default: false_) - removes descriptions
|
|
14
|
-
- esModule (_default: false_) - uses import and export statements instead of CommonJS
|
|
15
|
-
|
|
16
|
-
## Importing GraphQL files
|
|
17
|
-
|
|
18
|
-
_To add support for importing `.graphql`/`.gql` files, see [Webpack loading and preprocessing](#webpack-loading-and-preprocessing) below._
|
|
19
|
-
|
|
20
|
-
Given a file `MyQuery.graphql`
|
|
21
|
-
|
|
22
|
-
```graphql
|
|
23
|
-
query MyQuery {
|
|
24
|
-
...
|
|
25
|
-
}
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
If you have configured [the webpack @graphql-tools/webpack-loader](#webpack-loading-and-preprocessing), you can import modules containing graphQL queries. The imported value will be the pre-built AST.
|
|
29
|
-
|
|
30
|
-
```ts
|
|
31
|
-
import MyQuery from './query.graphql'
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
### Preprocessing queries and fragments
|
|
35
|
-
|
|
36
|
-
Preprocessing GraphQL queries and fragments into ASTs at build time can greatly improve load times.
|
|
37
|
-
|
|
38
|
-
#### Webpack loading and preprocessing
|
|
39
|
-
|
|
40
|
-
Using the included `@graphql-tools/webpack-loader` it is possible to maintain query logic that is separate from the rest of your application logic. With the loader configured, imported graphQL files will be converted to AST during the webpack build process.
|
|
41
|
-
|
|
42
|
-
```js
|
|
43
|
-
{
|
|
44
|
-
loaders: [
|
|
45
|
-
{
|
|
46
|
-
test: /\.(graphql|gql)$/,
|
|
47
|
-
exclude: /node_modules/,
|
|
48
|
-
loader: '@graphql-tools/webpack-loader',
|
|
49
|
-
options: {
|
|
50
|
-
/* ... */
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
],
|
|
54
|
-
}
|
|
55
|
-
```
|
package/index.js
DELETED
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
|
|
4
|
-
|
|
5
|
-
const os = _interopDefault(require('os'));
|
|
6
|
-
const graphql = require('graphql');
|
|
7
|
-
const webpackLoaderRuntime = require('@graphql-tools/webpack-loader-runtime');
|
|
8
|
-
const optimize = require('@graphql-tools/optimize');
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Strip insignificant whitespace
|
|
12
|
-
* Note that this could do a lot more, such as reorder fields etc.
|
|
13
|
-
*/
|
|
14
|
-
function normalize(str) {
|
|
15
|
-
return str.replace(/[\s,]+/g, ' ').trim();
|
|
16
|
-
}
|
|
17
|
-
// A map docString -> graphql document
|
|
18
|
-
const docCache = {};
|
|
19
|
-
// A map fragmentName -> [normalized source]
|
|
20
|
-
const fragmentSourceMap = {};
|
|
21
|
-
function cacheKeyFromFragment(fragment) {
|
|
22
|
-
return normalize(graphql.print(fragment));
|
|
23
|
-
}
|
|
24
|
-
/**
|
|
25
|
-
* Take a unstripped parsed document (query/mutation or even fragment), and
|
|
26
|
-
* check all fragment definitions, checking for name->source uniqueness.
|
|
27
|
-
* We also want to make sure only unique fragments exist in the document.
|
|
28
|
-
*/
|
|
29
|
-
function processFragments(ast) {
|
|
30
|
-
const astFragmentMap = {};
|
|
31
|
-
const definitions = [];
|
|
32
|
-
for (let i = 0; i < ast.definitions.length; i++) {
|
|
33
|
-
const fragmentDefinition = ast.definitions[i];
|
|
34
|
-
if (fragmentDefinition.kind === graphql.Kind.FRAGMENT_DEFINITION) {
|
|
35
|
-
const fragmentName = fragmentDefinition.name.value;
|
|
36
|
-
const sourceKey = cacheKeyFromFragment(fragmentDefinition);
|
|
37
|
-
// We know something about this fragment
|
|
38
|
-
if (fragmentSourceMap.hasOwnProperty(fragmentName) && !fragmentSourceMap[fragmentName][sourceKey]) {
|
|
39
|
-
fragmentSourceMap[fragmentName][sourceKey] = true;
|
|
40
|
-
}
|
|
41
|
-
else if (!fragmentSourceMap.hasOwnProperty(fragmentName)) {
|
|
42
|
-
fragmentSourceMap[fragmentName] = {};
|
|
43
|
-
fragmentSourceMap[fragmentName][sourceKey] = true;
|
|
44
|
-
}
|
|
45
|
-
if (!astFragmentMap[sourceKey]) {
|
|
46
|
-
astFragmentMap[sourceKey] = true;
|
|
47
|
-
definitions.push(fragmentDefinition);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
else {
|
|
51
|
-
definitions.push(fragmentDefinition);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
ast.definitions = definitions;
|
|
55
|
-
return ast;
|
|
56
|
-
}
|
|
57
|
-
function parseDocument(doc) {
|
|
58
|
-
const cacheKey = normalize(doc);
|
|
59
|
-
if (docCache[cacheKey]) {
|
|
60
|
-
return docCache[cacheKey];
|
|
61
|
-
}
|
|
62
|
-
const parsed = graphql.parse(doc, {
|
|
63
|
-
noLocation: true,
|
|
64
|
-
});
|
|
65
|
-
if (!parsed || parsed.kind !== 'Document') {
|
|
66
|
-
throw new Error('Not a valid GraphQL document.');
|
|
67
|
-
}
|
|
68
|
-
// check that all "new" fragments inside the documents are consistent with
|
|
69
|
-
// existing fragments of the same name
|
|
70
|
-
docCache[cacheKey] = processFragments(parsed);
|
|
71
|
-
return parsed;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function isSDL(doc) {
|
|
75
|
-
return !doc.definitions.some(def => graphql.isExecutableDefinitionNode(def));
|
|
76
|
-
}
|
|
77
|
-
function expandImports(source, options) {
|
|
78
|
-
const lines = source.split(/\r\n|\r|\n/);
|
|
79
|
-
let outputCode = options.importHelpers
|
|
80
|
-
? `
|
|
81
|
-
var useUnique = require('@graphql-tools/webpack-loader-runtime').useUnique;
|
|
82
|
-
var unique = useUnique();
|
|
83
|
-
`
|
|
84
|
-
: `
|
|
85
|
-
${webpackLoaderRuntime.uniqueCode}
|
|
86
|
-
`;
|
|
87
|
-
lines.some(line => {
|
|
88
|
-
if (line[0] === '#' && line.slice(1).split(' ')[0] === 'import') {
|
|
89
|
-
const importFile = line.slice(1).split(' ')[1];
|
|
90
|
-
const parseDocument = `require(${importFile})`;
|
|
91
|
-
const appendDef = `doc.definitions = doc.definitions.concat(unique(${parseDocument}.definitions));`;
|
|
92
|
-
outputCode += appendDef + os.EOL;
|
|
93
|
-
}
|
|
94
|
-
return line.length !== 0 && line[0] !== '#';
|
|
95
|
-
});
|
|
96
|
-
return outputCode;
|
|
97
|
-
}
|
|
98
|
-
function graphqlLoader(source) {
|
|
99
|
-
this.cacheable();
|
|
100
|
-
// TODO: This should probably use this.getOptions()
|
|
101
|
-
const options = this.query || {};
|
|
102
|
-
let doc = parseDocument(source);
|
|
103
|
-
const optimizers = [];
|
|
104
|
-
if (options.noDescription) {
|
|
105
|
-
optimizers.push(optimize.removeDescriptions);
|
|
106
|
-
}
|
|
107
|
-
if (options.noEmptyNodes) {
|
|
108
|
-
optimizers.push(optimize.removeEmptyNodes);
|
|
109
|
-
}
|
|
110
|
-
if (optimizers.length > 0 && isSDL(doc)) {
|
|
111
|
-
doc = optimize.optimizeDocumentNode(doc, optimizers);
|
|
112
|
-
}
|
|
113
|
-
let stringifiedDoc = JSON.stringify(doc);
|
|
114
|
-
if (options.replaceKinds) {
|
|
115
|
-
for (const identifier in graphql.Kind) {
|
|
116
|
-
const value = graphql.Kind[identifier];
|
|
117
|
-
stringifiedDoc = stringifiedDoc.replace(new RegExp(`"kind":"${value}"`, 'g'), `"kind": Kind.${identifier}`);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
const headerCode = `
|
|
121
|
-
${options.replaceKinds ? "var Kind = require('graphql/language/kinds');" : ''}
|
|
122
|
-
var doc = ${stringifiedDoc};
|
|
123
|
-
`;
|
|
124
|
-
let outputCode = '';
|
|
125
|
-
// Allow multiple query/mutation definitions in a file. This parses out dependencies
|
|
126
|
-
// at compile time, and then uses those at load time to create minimal query documents
|
|
127
|
-
// We cannot do the latter at compile time due to how the #import code works.
|
|
128
|
-
const operationCount = doc.definitions.reduce((accum, op) => {
|
|
129
|
-
if (op.kind === graphql.Kind.OPERATION_DEFINITION) {
|
|
130
|
-
return accum + 1;
|
|
131
|
-
}
|
|
132
|
-
return accum;
|
|
133
|
-
}, 0);
|
|
134
|
-
function exportDefaultStatement(identifier) {
|
|
135
|
-
if (options.esModule) {
|
|
136
|
-
return `export default ${identifier}`;
|
|
137
|
-
}
|
|
138
|
-
return `module.exports = ${identifier}`;
|
|
139
|
-
}
|
|
140
|
-
if (operationCount > 1) {
|
|
141
|
-
throw new Error('GraphQL Webpack Loader allows only for one GraphQL Operation per file');
|
|
142
|
-
}
|
|
143
|
-
outputCode += `
|
|
144
|
-
${exportDefaultStatement('doc')}
|
|
145
|
-
`;
|
|
146
|
-
const importOutputCode = expandImports(source, options);
|
|
147
|
-
const allCode = [headerCode, importOutputCode, outputCode, ''].join(os.EOL);
|
|
148
|
-
return allCode;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
module.exports = graphqlLoader;
|