@graphql-codegen/hasura-allow-list 1.0.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/config.d.ts +14 -0
- package/index.d.ts +7 -0
- package/index.js +82 -0
- package/index.mjs +76 -0
- package/package.json +36 -0
package/config.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface HasuraAllowListPluginConfig {
|
|
2
|
+
/**
|
|
3
|
+
* @default allowed-queries
|
|
4
|
+
* @description Choose the collection name to be generated. Defaults to allowed-queries
|
|
5
|
+
*/
|
|
6
|
+
collection_name?: string;
|
|
7
|
+
/**
|
|
8
|
+
* @default 3
|
|
9
|
+
* @description Target metadata config version. Supported versions are 2 and 3.
|
|
10
|
+
* This is mostly for future proofing, currently has no impact as both versions use the same format.
|
|
11
|
+
* The default value _will change_ in the future if/when newer config versions are released.
|
|
12
|
+
*/
|
|
13
|
+
config_version?: 2 | 3;
|
|
14
|
+
}
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { PluginFunction, Types } from '@graphql-codegen/plugin-helpers';
|
|
2
|
+
import { HasuraAllowListPluginConfig } from './config';
|
|
3
|
+
export declare const plugin: PluginFunction<HasuraAllowListPluginConfig>;
|
|
4
|
+
declare const _default: {
|
|
5
|
+
plugin: PluginFunction<HasuraAllowListPluginConfig, Types.PluginOutput>;
|
|
6
|
+
};
|
|
7
|
+
export default _default;
|
package/index.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
|
|
6
|
+
|
|
7
|
+
const graphql = require('graphql');
|
|
8
|
+
const yaml = _interopDefault(require('yaml'));
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Returns an array of fragments required for a given operation, recursively.
|
|
12
|
+
* Will throw an error if it cannot find one of the fragments required for the operation.
|
|
13
|
+
* @param operationDefinition the operation we want to find fragements for.
|
|
14
|
+
* @param fragmentDefinitions a list of fragments from the same document, some of which may be required by the operation.
|
|
15
|
+
* @param documentLocation location of the document the operation is sourced from. Only used to improve error messages.
|
|
16
|
+
* @returns an array of fragments required for the operation.
|
|
17
|
+
*/
|
|
18
|
+
function getOperationFragmentsRecursively(operationDefinition, fragmentDefinitions, documentLocation) {
|
|
19
|
+
const requiredFragmentNames = new Set();
|
|
20
|
+
getRequiredFragments(operationDefinition);
|
|
21
|
+
// note: we first get a list of required fragments names, then filter the original list.
|
|
22
|
+
// this means order of fragments is preserved.
|
|
23
|
+
return fragmentDefinitions.filter(definition => requiredFragmentNames.has(definition.name.value));
|
|
24
|
+
/**
|
|
25
|
+
* Given a definition adds required fragments to requieredFragmentsNames, recursively.
|
|
26
|
+
* @param definition either an operation definition or a fragment definition.
|
|
27
|
+
*/
|
|
28
|
+
function getRequiredFragments(definition) {
|
|
29
|
+
graphql.visit(definition, {
|
|
30
|
+
FragmentSpread(fragmentSpreadNode) {
|
|
31
|
+
// added this check to prevent infinite recursion on recursive fragment definition (which itself isn't legal graphql)
|
|
32
|
+
// it seems graphql crashes anyways if a recursive fragment is defined, so maybe remove this check?
|
|
33
|
+
if (!requiredFragmentNames.has(fragmentSpreadNode.name.value)) {
|
|
34
|
+
requiredFragmentNames.add(fragmentSpreadNode.name.value);
|
|
35
|
+
const fragmentDefinition = fragmentDefinitions.find(definition => definition.name.value === fragmentSpreadNode.name.value);
|
|
36
|
+
if (!fragmentDefinition) {
|
|
37
|
+
throw new Error(`Missing fragment ${fragmentSpreadNode.name.value} for ${definition.kind === graphql.Kind.FRAGMENT_DEFINITION ? 'fragment' : 'operation'} ${definition.name.value} in file ${documentLocation}`);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
getRequiredFragments(fragmentDefinition);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return fragmentSpreadNode;
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const plugin = async (schema, documents, config) => {
|
|
49
|
+
var _a;
|
|
50
|
+
const queries = [];
|
|
51
|
+
// each document (graphql file) is handled independently.
|
|
52
|
+
// any fragment required by an operation will need to be definied in the same file as the operation.
|
|
53
|
+
for (const document of documents) {
|
|
54
|
+
// filter out anonymous operations
|
|
55
|
+
const documentOperations = document.document.definitions.filter((definition) => definition.kind === graphql.Kind.OPERATION_DEFINITION && !!definition.name);
|
|
56
|
+
// filter out anonymous fragments
|
|
57
|
+
const documentFragments = document.document.definitions.filter((definition) => definition.kind === graphql.Kind.FRAGMENT_DEFINITION && !!definition.name);
|
|
58
|
+
// for each operation in the document
|
|
59
|
+
for (const operation of documentOperations) {
|
|
60
|
+
// get fragments required by the operations
|
|
61
|
+
const requiredFragmentDefinitions = getOperationFragmentsRecursively(operation, documentFragments, document.location);
|
|
62
|
+
// insert the operation and any fragments to our queries definition.
|
|
63
|
+
// fragment order is preserved, and each fragment is separated by a new line
|
|
64
|
+
queries.push({
|
|
65
|
+
name: operation.name.value,
|
|
66
|
+
query: [operation, ...requiredFragmentDefinitions].map(graphql.print).join('\n'),
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return yaml.stringify([
|
|
71
|
+
{
|
|
72
|
+
name: (_a = config.collection_name) !== null && _a !== void 0 ? _a : 'allowed-queries',
|
|
73
|
+
definition: {
|
|
74
|
+
queries,
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
]);
|
|
78
|
+
};
|
|
79
|
+
const index = { plugin };
|
|
80
|
+
|
|
81
|
+
exports.default = index;
|
|
82
|
+
exports.plugin = plugin;
|
package/index.mjs
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { Kind, print, visit } from 'graphql';
|
|
2
|
+
import yaml from 'yaml';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Returns an array of fragments required for a given operation, recursively.
|
|
6
|
+
* Will throw an error if it cannot find one of the fragments required for the operation.
|
|
7
|
+
* @param operationDefinition the operation we want to find fragements for.
|
|
8
|
+
* @param fragmentDefinitions a list of fragments from the same document, some of which may be required by the operation.
|
|
9
|
+
* @param documentLocation location of the document the operation is sourced from. Only used to improve error messages.
|
|
10
|
+
* @returns an array of fragments required for the operation.
|
|
11
|
+
*/
|
|
12
|
+
function getOperationFragmentsRecursively(operationDefinition, fragmentDefinitions, documentLocation) {
|
|
13
|
+
const requiredFragmentNames = new Set();
|
|
14
|
+
getRequiredFragments(operationDefinition);
|
|
15
|
+
// note: we first get a list of required fragments names, then filter the original list.
|
|
16
|
+
// this means order of fragments is preserved.
|
|
17
|
+
return fragmentDefinitions.filter(definition => requiredFragmentNames.has(definition.name.value));
|
|
18
|
+
/**
|
|
19
|
+
* Given a definition adds required fragments to requieredFragmentsNames, recursively.
|
|
20
|
+
* @param definition either an operation definition or a fragment definition.
|
|
21
|
+
*/
|
|
22
|
+
function getRequiredFragments(definition) {
|
|
23
|
+
visit(definition, {
|
|
24
|
+
FragmentSpread(fragmentSpreadNode) {
|
|
25
|
+
// added this check to prevent infinite recursion on recursive fragment definition (which itself isn't legal graphql)
|
|
26
|
+
// it seems graphql crashes anyways if a recursive fragment is defined, so maybe remove this check?
|
|
27
|
+
if (!requiredFragmentNames.has(fragmentSpreadNode.name.value)) {
|
|
28
|
+
requiredFragmentNames.add(fragmentSpreadNode.name.value);
|
|
29
|
+
const fragmentDefinition = fragmentDefinitions.find(definition => definition.name.value === fragmentSpreadNode.name.value);
|
|
30
|
+
if (!fragmentDefinition) {
|
|
31
|
+
throw new Error(`Missing fragment ${fragmentSpreadNode.name.value} for ${definition.kind === Kind.FRAGMENT_DEFINITION ? 'fragment' : 'operation'} ${definition.name.value} in file ${documentLocation}`);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
getRequiredFragments(fragmentDefinition);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return fragmentSpreadNode;
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const plugin = async (schema, documents, config) => {
|
|
43
|
+
var _a;
|
|
44
|
+
const queries = [];
|
|
45
|
+
// each document (graphql file) is handled independently.
|
|
46
|
+
// any fragment required by an operation will need to be definied in the same file as the operation.
|
|
47
|
+
for (const document of documents) {
|
|
48
|
+
// filter out anonymous operations
|
|
49
|
+
const documentOperations = document.document.definitions.filter((definition) => definition.kind === Kind.OPERATION_DEFINITION && !!definition.name);
|
|
50
|
+
// filter out anonymous fragments
|
|
51
|
+
const documentFragments = document.document.definitions.filter((definition) => definition.kind === Kind.FRAGMENT_DEFINITION && !!definition.name);
|
|
52
|
+
// for each operation in the document
|
|
53
|
+
for (const operation of documentOperations) {
|
|
54
|
+
// get fragments required by the operations
|
|
55
|
+
const requiredFragmentDefinitions = getOperationFragmentsRecursively(operation, documentFragments, document.location);
|
|
56
|
+
// insert the operation and any fragments to our queries definition.
|
|
57
|
+
// fragment order is preserved, and each fragment is separated by a new line
|
|
58
|
+
queries.push({
|
|
59
|
+
name: operation.name.value,
|
|
60
|
+
query: [operation, ...requiredFragmentDefinitions].map(print).join('\n'),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return yaml.stringify([
|
|
65
|
+
{
|
|
66
|
+
name: (_a = config.collection_name) !== null && _a !== void 0 ? _a : 'allowed-queries',
|
|
67
|
+
definition: {
|
|
68
|
+
queries,
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
]);
|
|
72
|
+
};
|
|
73
|
+
const index = { plugin };
|
|
74
|
+
|
|
75
|
+
export default index;
|
|
76
|
+
export { plugin };
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@graphql-codegen/hasura-allow-list",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "GraphQL Code Generator plugin to generate hasura allow liste metadata from graphql files",
|
|
5
|
+
"peerDependencies": {
|
|
6
|
+
"graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0"
|
|
7
|
+
},
|
|
8
|
+
"dependencies": {
|
|
9
|
+
"@graphql-codegen/plugin-helpers": "^2.3.2",
|
|
10
|
+
"tslib": "~2.3.0",
|
|
11
|
+
"yaml": "^1.10.2"
|
|
12
|
+
},
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "https://github.com/dotansimha/graphql-code-generator.git",
|
|
16
|
+
"directory": "packages/plugins/other/hasura-allow-list"
|
|
17
|
+
},
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"main": "index.js",
|
|
20
|
+
"module": "index.mjs",
|
|
21
|
+
"typings": "index.d.ts",
|
|
22
|
+
"typescript": {
|
|
23
|
+
"definition": "index.d.ts"
|
|
24
|
+
},
|
|
25
|
+
"exports": {
|
|
26
|
+
"./package.json": "./package.json",
|
|
27
|
+
".": {
|
|
28
|
+
"require": "./index.js",
|
|
29
|
+
"import": "./index.mjs"
|
|
30
|
+
},
|
|
31
|
+
"./*": {
|
|
32
|
+
"require": "./*.js",
|
|
33
|
+
"import": "./*.mjs"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|