graphql-buddy 0.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/.github/workflows/ci.yml +32 -0
- package/.nvmrc +1 -0
- package/.prettierignore +1 -0
- package/.prettierrc +7 -0
- package/LICENSE +21 -0
- package/README.md +89 -0
- package/dist/api.d.ts +46 -0
- package/dist/api.js +13 -0
- package/dist/api.js.map +1 -0
- package/dist/chunk-Z7B4AQ2U.js +124 -0
- package/dist/chunk-Z7B4AQ2U.js.map +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +122 -0
- package/dist/cli.js.map +1 -0
- package/eslint.config.ts +168 -0
- package/package.json +69 -0
- package/pnpm-workspace.yaml +3 -0
- package/src/api/commands/bundle.test.ts +138 -0
- package/src/api/commands/bundle.ts +29 -0
- package/src/api/commands/index.ts +2 -0
- package/src/api/commands/validate.test.ts +35 -0
- package/src/api/commands/validate.ts +49 -0
- package/src/api/index.ts +2 -0
- package/src/api/shared/index.ts +1 -0
- package/src/api/shared/loadSchema.ts +88 -0
- package/src/cli/commands/bundle.ts +62 -0
- package/src/cli/commands/index.ts +2 -0
- package/src/cli/commands/validate.ts +59 -0
- package/src/cli/index.ts +52 -0
- package/src/cli/shared/index.ts +1 -0
- package/src/cli/shared/parsePathAliases.test.ts +43 -0
- package/src/cli/shared/parsePathAliases.ts +25 -0
- package/test_fixtures/missing_scalar/schema.graphql +16 -0
- package/test_fixtures/multiple_files/Bar.graphql +5 -0
- package/test_fixtures/multiple_files/Baz.graphql +5 -0
- package/test_fixtures/multiple_files/Foo.graphql +9 -0
- package/test_fixtures/multiple_files/Mutation.graphql +9 -0
- package/test_fixtures/multiple_files/Query.graphql +5 -0
- package/test_fixtures/single_file/schema.graphql +16 -0
- package/test_fixtures/single_file_with_extra_deps/schema.graphql +46 -0
- package/tsconfig.json +20 -0
- package/tsup.config.ts +14 -0
- package/vite.config.ts +14 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
pull_request:
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
actions: read
|
|
11
|
+
contents: read
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
main:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
with:
|
|
19
|
+
fetch-depth: 0
|
|
20
|
+
|
|
21
|
+
- uses: pnpm/action-setup@v4
|
|
22
|
+
with:
|
|
23
|
+
version: 10.13.1
|
|
24
|
+
- uses: actions/setup-node@v4
|
|
25
|
+
with:
|
|
26
|
+
node-version-file: '.nvmrc'
|
|
27
|
+
cache: 'pnpm'
|
|
28
|
+
|
|
29
|
+
- run: pnpm install
|
|
30
|
+
- run: pnpm run lint
|
|
31
|
+
- run: pnpm run build
|
|
32
|
+
- run: pnpm run test
|
package/.nvmrc
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
lts/jod
|
package/.prettierignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pnpm-lock.yaml
|
package/.prettierrc
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Hunter Larco
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# graphql-buddy
|
|
2
|
+
|
|
3
|
+
[](https://badge.fury.io/js/graphql-buddy)
|
|
4
|
+
[](https://github.com/HunterLarco/graphql-buddy/actions/workflows/ci.yml)
|
|
5
|
+
|
|
6
|
+
> CLI tools for manipulating graphql // designed for build system tooling.
|
|
7
|
+
|
|
8
|
+
Includes useful tools like:
|
|
9
|
+
|
|
10
|
+
- `bundle` ~ merge multiple schema's into one (wth tree shaking).
|
|
11
|
+
- `validate` ~ validate GraphQL schema.
|
|
12
|
+
|
|
13
|
+
Necessary for build tool integrations such as [rules_graphql].
|
|
14
|
+
|
|
15
|
+
[rules_graphql]: https://github.com/HunterLarco/rules_graphql
|
|
16
|
+
|
|
17
|
+
## Table of Contents
|
|
18
|
+
|
|
19
|
+
<!-- TOC start (generated with https://github.com/derlin/bitdowntoc) -->
|
|
20
|
+
|
|
21
|
+
- [graphql-buddy](#graphql-buddy)
|
|
22
|
+
- [Quick Start](#quick-start)
|
|
23
|
+
- [Documentation](#documentation)
|
|
24
|
+
- [Bundle](#bundle)
|
|
25
|
+
- [Validate](#validate)
|
|
26
|
+
- [Programmatic Use](#programmatic-use)
|
|
27
|
+
- [Contributions](#contributions)
|
|
28
|
+
<!-- TOC end -->
|
|
29
|
+
|
|
30
|
+
## Quick Start
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
npm install graphql-buddy
|
|
34
|
+
|
|
35
|
+
npx graphql-buddy bundle **/*.graphql
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Documentation
|
|
39
|
+
|
|
40
|
+
You can always use `graphql-buddy --help` for CLI documentation.
|
|
41
|
+
|
|
42
|
+
### Bundle
|
|
43
|
+
|
|
44
|
+
Bundles multiple schema into a single file _and_ prunes any unused types from
|
|
45
|
+
the bundle.
|
|
46
|
+
|
|
47
|
+
For example:
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
npx graphql-buddy bundle **/*.graphql
|
|
51
|
+
|
|
52
|
+
# If you want to preserve unused types.
|
|
53
|
+
npx graphql-buddy bundle **/*.graphql --no-shake
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Validate
|
|
57
|
+
|
|
58
|
+
Validates schema, ensuring valid syntax and that all symbols are found.
|
|
59
|
+
|
|
60
|
+
For example:
|
|
61
|
+
|
|
62
|
+
```sh
|
|
63
|
+
npx graphql-buddy validate **/*.graphql
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
For many build systems (such as BUCK and BAZEL) all build steps _must_ emit a
|
|
67
|
+
file. Validate accomodates this by writing "validation stamps", a file only
|
|
68
|
+
written if validation is successful and documents the exact settings used during
|
|
69
|
+
validation. For example:
|
|
70
|
+
|
|
71
|
+
```
|
|
72
|
+
npx graphql-buddy validate **/*.graphql --stamp schema.stamp
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Programmatic Use
|
|
76
|
+
|
|
77
|
+
All commands and most helpers are available programmatically.
|
|
78
|
+
|
|
79
|
+
```js
|
|
80
|
+
import * as graphqlBuddy from 'graphql-buddy';
|
|
81
|
+
|
|
82
|
+
await graphqlBuddy.bundle({ ... });
|
|
83
|
+
await graphqlBuddy.validate({ ... });
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Contributions
|
|
87
|
+
|
|
88
|
+
Contributions, issues and feature requests are very welcome. If you are using
|
|
89
|
+
this package and fixed a bug for yourself, please consider submitting a PR!
|
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import * as graphql from 'graphql';
|
|
2
|
+
|
|
3
|
+
type LoadSchemaOptions = {
|
|
4
|
+
normalized?: false | null;
|
|
5
|
+
files: Array<string>;
|
|
6
|
+
base?: string | null;
|
|
7
|
+
pathAliases?: Map<string, string> | null;
|
|
8
|
+
};
|
|
9
|
+
declare const loadSchema: (options: LoadSchemaOptions | NormalizedLoadSchemaOptions) => Promise<graphql.GraphQLSchema>;
|
|
10
|
+
type NormalizedLoadSchemaOptions = {
|
|
11
|
+
normalized: true;
|
|
12
|
+
files: Array<string>;
|
|
13
|
+
pathAliases: Map<string, string>;
|
|
14
|
+
};
|
|
15
|
+
declare const normalizeLoadSchemaOptions: (options: LoadSchemaOptions) => Promise<NormalizedLoadSchemaOptions>;
|
|
16
|
+
|
|
17
|
+
type BundleOptions = {
|
|
18
|
+
schema: LoadSchemaOptions;
|
|
19
|
+
shake?: boolean | null;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Bundles multiple schema together into a single file.
|
|
23
|
+
*
|
|
24
|
+
* @param options - Bundle options.
|
|
25
|
+
*
|
|
26
|
+
* @returns The merged schema as text.
|
|
27
|
+
*/
|
|
28
|
+
declare const bundle: (options: BundleOptions) => Promise<string>;
|
|
29
|
+
|
|
30
|
+
type ValidateOptions = {
|
|
31
|
+
schema: LoadSchemaOptions;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Validates that the provided GraphQL schema contain valid syntax and have no
|
|
35
|
+
* unknown symbols.
|
|
36
|
+
*
|
|
37
|
+
* @param options - Validate options.
|
|
38
|
+
*
|
|
39
|
+
* @returns If validation is successful, returns a validation stamp: a text blob
|
|
40
|
+
* describing the successful outcome and options used during validation. This
|
|
41
|
+
* is often useful in build systems where a build step *must* emit a file. If
|
|
42
|
+
* validation fails, a runtime error will be thrown.
|
|
43
|
+
*/
|
|
44
|
+
declare const validate: (options: ValidateOptions) => Promise<string>;
|
|
45
|
+
|
|
46
|
+
export { type BundleOptions, type LoadSchemaOptions, type NormalizedLoadSchemaOptions, type ValidateOptions, bundle, loadSchema, normalizeLoadSchemaOptions, validate };
|
package/dist/api.js
ADDED
package/dist/api.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defProps = Object.defineProperties;
|
|
3
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
4
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
7
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
8
|
+
var __spreadValues = (a, b) => {
|
|
9
|
+
for (var prop in b || (b = {}))
|
|
10
|
+
if (__hasOwnProp.call(b, prop))
|
|
11
|
+
__defNormalProp(a, prop, b[prop]);
|
|
12
|
+
if (__getOwnPropSymbols)
|
|
13
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
14
|
+
if (__propIsEnum.call(b, prop))
|
|
15
|
+
__defNormalProp(a, prop, b[prop]);
|
|
16
|
+
}
|
|
17
|
+
return a;
|
|
18
|
+
};
|
|
19
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
20
|
+
var __async = (__this, __arguments, generator) => {
|
|
21
|
+
return new Promise((resolve2, reject) => {
|
|
22
|
+
var fulfilled = (value) => {
|
|
23
|
+
try {
|
|
24
|
+
step(generator.next(value));
|
|
25
|
+
} catch (e) {
|
|
26
|
+
reject(e);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
var rejected = (value) => {
|
|
30
|
+
try {
|
|
31
|
+
step(generator.throw(value));
|
|
32
|
+
} catch (e) {
|
|
33
|
+
reject(e);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
var step = (x) => x.done ? resolve2(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
37
|
+
step((generator = generator.apply(__this, __arguments)).next());
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// src/api/commands/bundle.ts
|
|
42
|
+
import * as graphqlToolsUtils from "@graphql-tools/utils";
|
|
43
|
+
import * as graphql from "graphql";
|
|
44
|
+
|
|
45
|
+
// src/api/shared/loadSchema.ts
|
|
46
|
+
import * as nodeFs from "fs/promises";
|
|
47
|
+
import * as nodePath from "path";
|
|
48
|
+
import * as graphqlFileLoader from "@graphql-tools/graphql-file-loader";
|
|
49
|
+
import * as graphqlLoad from "@graphql-tools/load";
|
|
50
|
+
import * as glob from "glob";
|
|
51
|
+
var loadSchema2 = (options) => __async(null, null, function* () {
|
|
52
|
+
const { files, pathAliases } = options.normalized ? options : yield normalizeLoadSchemaOptions(options);
|
|
53
|
+
for (const file of files) {
|
|
54
|
+
try {
|
|
55
|
+
yield nodeFs.access(file, nodeFs.constants.F_OK);
|
|
56
|
+
} catch (e) {
|
|
57
|
+
throw new Error(`File does not exist: ${file}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return graphqlLoad.loadSchema(files, {
|
|
61
|
+
loaders: [new graphqlFileLoader.GraphQLFileLoader()],
|
|
62
|
+
pathAliases: {
|
|
63
|
+
mappings: Object.fromEntries(pathAliases.entries())
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
var normalizeLoadSchemaOptions = (options) => __async(null, null, function* () {
|
|
68
|
+
var _a;
|
|
69
|
+
const base = (_a = options.base) != null ? _a : process.cwd();
|
|
70
|
+
const files = yield glob.glob(
|
|
71
|
+
options.files.map((file) => nodePath.resolve(base, file))
|
|
72
|
+
);
|
|
73
|
+
const pathAliases = /* @__PURE__ */ new Map();
|
|
74
|
+
if (options.pathAliases != null) {
|
|
75
|
+
for (const [alias, source] of options.pathAliases.entries()) {
|
|
76
|
+
pathAliases.set(alias, nodePath.resolve(base, source));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
normalized: true,
|
|
81
|
+
files,
|
|
82
|
+
pathAliases
|
|
83
|
+
};
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// src/api/commands/bundle.ts
|
|
87
|
+
var bundle = (options) => __async(null, null, function* () {
|
|
88
|
+
var _a;
|
|
89
|
+
const shake = (_a = options.shake) != null ? _a : true;
|
|
90
|
+
let schema = yield loadSchema2(options.schema);
|
|
91
|
+
if (shake) {
|
|
92
|
+
schema = graphqlToolsUtils.pruneSchema(schema);
|
|
93
|
+
}
|
|
94
|
+
return graphql.printSchema(schema);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// src/api/commands/validate.ts
|
|
98
|
+
import * as stringifyObject from "stringify-object";
|
|
99
|
+
var validate = (options) => __async(null, null, function* () {
|
|
100
|
+
const normalizedSchemaOptions = yield normalizeLoadSchemaOptions(
|
|
101
|
+
options.schema
|
|
102
|
+
);
|
|
103
|
+
yield loadSchema2(normalizedSchemaOptions);
|
|
104
|
+
return createValidationStamp(__spreadProps(__spreadValues({}, options), {
|
|
105
|
+
schema: normalizedSchemaOptions
|
|
106
|
+
}));
|
|
107
|
+
});
|
|
108
|
+
var createValidationStamp = (options) => stringifyObject.default(
|
|
109
|
+
{ options, outcome: { valid: true } },
|
|
110
|
+
{
|
|
111
|
+
indent: ` `,
|
|
112
|
+
filter: (container, property) => !(container === options.schema && property === `normalized`)
|
|
113
|
+
}
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
export {
|
|
117
|
+
__spreadValues,
|
|
118
|
+
__async,
|
|
119
|
+
loadSchema2 as loadSchema,
|
|
120
|
+
normalizeLoadSchemaOptions,
|
|
121
|
+
bundle,
|
|
122
|
+
validate
|
|
123
|
+
};
|
|
124
|
+
//# sourceMappingURL=chunk-Z7B4AQ2U.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/api/commands/bundle.ts","../src/api/shared/loadSchema.ts","../src/api/commands/validate.ts"],"sourcesContent":["import * as graphqlToolsUtils from '@graphql-tools/utils';\nimport * as graphql from 'graphql';\n\nimport * as shared from '../shared';\n\nexport type BundleOptions = {\n schema: shared.LoadSchemaOptions;\n\n // Defaults to true.\n shake?: boolean | null;\n};\n\n/**\n * Bundles multiple schema together into a single file.\n *\n * @param options - Bundle options.\n *\n * @returns The merged schema as text.\n */\nexport const bundle = async (options: BundleOptions): Promise<string> => {\n const shake = options.shake ?? true;\n\n let schema = await shared.loadSchema(options.schema);\n if (shake) {\n schema = graphqlToolsUtils.pruneSchema(schema);\n }\n\n return graphql.printSchema(schema);\n};\n","import * as nodeFs from 'node:fs/promises';\nimport * as nodePath from 'node:path';\n\nimport * as graphqlFileLoader from '@graphql-tools/graphql-file-loader';\nimport * as graphqlLoad from '@graphql-tools/load';\nimport * as glob from 'glob';\n\nimport type * as graphql from 'graphql';\n\nexport type LoadSchemaOptions = {\n normalized?: false | null;\n\n // Relative paths will be resolved using `base`.\n //\n // Also accepts glob patterns.\n files: Array<string>;\n\n // Directory from which `files` are resolved.\n //\n // Defaults to `process.cwd()`.\n base?: string | null;\n\n // See @graphql-tools/import#PathAliases\n //\n // Note that relative paths will be resolved relative to `base` to maintain\n // parity with `files`.\n pathAliases?: Map<string, string> | null;\n};\n\nexport const loadSchema = async (\n options: LoadSchemaOptions | NormalizedLoadSchemaOptions,\n): Promise<graphql.GraphQLSchema> => {\n const { files, pathAliases } = options.normalized\n ? options\n : await normalizeLoadSchemaOptions(options);\n\n // @graphql-tools/graphql-file-loader silently skips input files which are not\n // found. I suspect this is so that it can delegate missing inputs to other\n // loaders. For our purposes, this is undesirable because we want to inform\n // clients if expected inputs are missing. For that reason, we manually\n // validate that the files exist first.\n for (const file of files) {\n try {\n await nodeFs.access(file, nodeFs.constants.F_OK);\n } catch {\n throw new Error(`File does not exist: ${file}`);\n }\n }\n\n return graphqlLoad.loadSchema(files, {\n loaders: [new graphqlFileLoader.GraphQLFileLoader()],\n pathAliases: {\n mappings: Object.fromEntries(pathAliases.entries()),\n },\n });\n};\n\nexport type NormalizedLoadSchemaOptions = {\n normalized: true;\n\n // Absolute paths.\n files: Array<string>;\n\n // See @graphql-tools/import#PathAliases\n pathAliases: Map<string, string>;\n};\n\nexport const normalizeLoadSchemaOptions = async (\n options: LoadSchemaOptions,\n): Promise<NormalizedLoadSchemaOptions> => {\n const base = options.base ?? process.cwd();\n const files = await glob.glob(\n options.files.map((file) => nodePath.resolve(base, file)),\n );\n\n const pathAliases = new Map<string, string>();\n if (options.pathAliases != null) {\n for (const [alias, source] of options.pathAliases.entries()) {\n pathAliases.set(alias, nodePath.resolve(base, source));\n }\n }\n\n return {\n normalized: true,\n files,\n pathAliases,\n };\n};\n","import * as stringifyObject from 'stringify-object';\n\nimport * as shared from '../shared';\n\nexport type ValidateOptions = {\n schema: shared.LoadSchemaOptions;\n};\n\n/**\n * Validates that the provided GraphQL schema contain valid syntax and have no\n * unknown symbols.\n *\n * @param options - Validate options.\n *\n * @returns If validation is successful, returns a validation stamp: a text blob\n * describing the successful outcome and options used during validation. This\n * is often useful in build systems where a build step *must* emit a file. If\n * validation fails, a runtime error will be thrown.\n */\nexport const validate = async (options: ValidateOptions): Promise<string> => {\n // We manually normalize the `LoadSchemaOptions` so that we can write the most\n // accurate data in our validation stamp. For example, normalization expands\n // globs and resolves absolute paths. These are critical steps to ensure the\n // validation stamp describes without ambiguity which schema were loaded.\n const normalizedSchemaOptions = await shared.normalizeLoadSchemaOptions(\n options.schema,\n );\n\n await shared.loadSchema(normalizedSchemaOptions);\n\n return createValidationStamp({\n ...options,\n schema: normalizedSchemaOptions,\n });\n};\n\nconst createValidationStamp = (\n options: Omit<ValidateOptions, `schema`> & {\n schema: shared.NormalizedLoadSchemaOptions;\n },\n): string =>\n stringifyObject.default(\n { options, outcome: { valid: true } },\n {\n indent: ` `,\n filter: (container, property) =>\n !(container === options.schema && property === `normalized`),\n },\n );\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,YAAY,uBAAuB;AACnC,YAAY,aAAa;;;ACDzB,YAAY,YAAY;AACxB,YAAY,cAAc;AAE1B,YAAY,uBAAuB;AACnC,YAAY,iBAAiB;AAC7B,YAAY,UAAU;AAwBf,IAAMA,cAAa,CACxB,YACmC;AACnC,QAAM,EAAE,OAAO,YAAY,IAAI,QAAQ,aACnC,UACA,MAAM,2BAA2B,OAAO;AAO5C,aAAW,QAAQ,OAAO;AACxB,QAAI;AACF,YAAa,cAAO,MAAa,iBAAU,IAAI;AAAA,IACjD,SAAQ;AACN,YAAM,IAAI,MAAM,wBAAwB,IAAI,EAAE;AAAA,IAChD;AAAA,EACF;AAEA,SAAmB,uBAAW,OAAO;AAAA,IACnC,SAAS,CAAC,IAAsB,oCAAkB,CAAC;AAAA,IACnD,aAAa;AAAA,MACX,UAAU,OAAO,YAAY,YAAY,QAAQ,CAAC;AAAA,IACpD;AAAA,EACF,CAAC;AACH;AAYO,IAAM,6BAA6B,CACxC,YACyC;AArE3C;AAsEE,QAAM,QAAO,aAAQ,SAAR,YAAgB,QAAQ,IAAI;AACzC,QAAM,QAAQ,MAAW;AAAA,IACvB,QAAQ,MAAM,IAAI,CAAC,SAAkB,iBAAQ,MAAM,IAAI,CAAC;AAAA,EAC1D;AAEA,QAAM,cAAc,oBAAI,IAAoB;AAC5C,MAAI,QAAQ,eAAe,MAAM;AAC/B,eAAW,CAAC,OAAO,MAAM,KAAK,QAAQ,YAAY,QAAQ,GAAG;AAC3D,kBAAY,IAAI,OAAgB,iBAAQ,MAAM,MAAM,CAAC;AAAA,IACvD;AAAA,EACF;AAEA,SAAO;AAAA,IACL,YAAY;AAAA,IACZ;AAAA,IACA;AAAA,EACF;AACF;;;ADpEO,IAAM,SAAS,CAAO,YAA4C;AAnBzE;AAoBE,QAAM,SAAQ,aAAQ,UAAR,YAAiB;AAE/B,MAAI,SAAS,MAAaC,YAAW,QAAQ,MAAM;AACnD,MAAI,OAAO;AACT,aAA2B,8BAAY,MAAM;AAAA,EAC/C;AAEA,SAAe,oBAAY,MAAM;AACnC;;;AE5BA,YAAY,qBAAqB;AAmB1B,IAAM,WAAW,CAAO,YAA8C;AAK3E,QAAM,0BAA0B,MAAa;AAAA,IAC3C,QAAQ;AAAA,EACV;AAEA,QAAaC,YAAW,uBAAuB;AAE/C,SAAO,sBAAsB,iCACxB,UADwB;AAAA,IAE3B,QAAQ;AAAA,EACV,EAAC;AACH;AAEA,IAAM,wBAAwB,CAC5B,YAIgB;AAAA,EACd,EAAE,SAAS,SAAS,EAAE,OAAO,KAAK,EAAE;AAAA,EACpC;AAAA,IACE,QAAQ;AAAA,IACR,QAAQ,CAAC,WAAW,aAClB,EAAE,cAAc,QAAQ,UAAU,aAAa;AAAA,EACnD;AACF;","names":["loadSchema","loadSchema","loadSchema"]}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
__async,
|
|
4
|
+
__spreadValues,
|
|
5
|
+
bundle,
|
|
6
|
+
validate
|
|
7
|
+
} from "./chunk-Z7B4AQ2U.js";
|
|
8
|
+
|
|
9
|
+
// src/cli/index.ts
|
|
10
|
+
import * as commander3 from "@commander-js/extra-typings";
|
|
11
|
+
import * as graphql from "graphql";
|
|
12
|
+
|
|
13
|
+
// src/cli/commands/bundle.ts
|
|
14
|
+
import * as nodeFs from "fs/promises";
|
|
15
|
+
import * as nodePath from "path";
|
|
16
|
+
import * as commander from "@commander-js/extra-typings";
|
|
17
|
+
|
|
18
|
+
// src/cli/shared/parsePathAliases.ts
|
|
19
|
+
var parsePathAliases = (encodedAliases) => {
|
|
20
|
+
const decodedAliases = /* @__PURE__ */ new Map();
|
|
21
|
+
for (const encodedAlias of encodedAliases) {
|
|
22
|
+
const parts = encodedAlias.split(`=`);
|
|
23
|
+
if (parts.length !== 2) {
|
|
24
|
+
throw new Error(
|
|
25
|
+
`Expected an alias in the form "alias=source" but found "${encodedAlias}"`
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
const [alias, source] = parts;
|
|
29
|
+
if (decodedAliases.has(alias)) {
|
|
30
|
+
throw new Error(`Alias "${alias}" is set multiple times.`);
|
|
31
|
+
}
|
|
32
|
+
decodedAliases.set(alias, source);
|
|
33
|
+
}
|
|
34
|
+
return decodedAliases;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// src/cli/commands/bundle.ts
|
|
38
|
+
var createBundleCommand = () => new commander.Command().name(`bundle`).description(`Bundles multiple GraphQL inputs into a single file.`).argument(`<schema...>`, `Input GraphQL schema files to bundle.`).option(
|
|
39
|
+
`-b, --base <directory>`,
|
|
40
|
+
`Base directory from which paths are resolved.`
|
|
41
|
+
).option(
|
|
42
|
+
`-a, --alias <pattern...>`,
|
|
43
|
+
`Path alias options for @graphql-tools/load.`
|
|
44
|
+
).option(
|
|
45
|
+
`--out <file>`,
|
|
46
|
+
`Where to write the bundled schema.`,
|
|
47
|
+
`bundle.graphql`
|
|
48
|
+
).option(`--no-shake`, `Prevents pruning of unused types.`).option(`-s, --silent`, `Only log critical information.`, false).action(
|
|
49
|
+
(schema, options) => bundle2(__spreadValues({
|
|
50
|
+
schema
|
|
51
|
+
}, options))
|
|
52
|
+
);
|
|
53
|
+
var bundle2 = (options) => __async(null, null, function* () {
|
|
54
|
+
const { schema, base, alias, shake, silent } = options;
|
|
55
|
+
const out = nodePath.resolve(base != null ? base : process.cwd(), options.out);
|
|
56
|
+
yield nodeFs.writeFile(
|
|
57
|
+
out,
|
|
58
|
+
yield bundle({
|
|
59
|
+
schema: {
|
|
60
|
+
files: schema,
|
|
61
|
+
base,
|
|
62
|
+
pathAliases: parsePathAliases(alias != null ? alias : [])
|
|
63
|
+
},
|
|
64
|
+
shake
|
|
65
|
+
})
|
|
66
|
+
);
|
|
67
|
+
if (!silent) {
|
|
68
|
+
console.log(`\u2705 bundler encountered no errors.`);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// src/cli/commands/validate.ts
|
|
73
|
+
import * as nodeFs2 from "fs/promises";
|
|
74
|
+
import * as nodePath2 from "path";
|
|
75
|
+
import * as commander2 from "@commander-js/extra-typings";
|
|
76
|
+
var createValidateCommand = () => new commander2.Command().name(`validate`).description(`Checks that all provided GraphQL files form a valid schema.`).argument(`<schema...>`, `Input GraphQL schema files to validate.`).option(
|
|
77
|
+
`-b, --base <directory>`,
|
|
78
|
+
`Base directory from which paths are resolved.`
|
|
79
|
+
).option(
|
|
80
|
+
`-a, --alias <pattern...>`,
|
|
81
|
+
`Path alias options for @graphql-tools/load.`
|
|
82
|
+
).option(
|
|
83
|
+
`--stamp <file>`,
|
|
84
|
+
`Creates a validation stamp to confirm valid GraphQL schema.`
|
|
85
|
+
).option(`-s, --silent`, `Only log critical information.`, false).action(
|
|
86
|
+
(schema, options) => validate2(__spreadValues({
|
|
87
|
+
schema
|
|
88
|
+
}, options))
|
|
89
|
+
);
|
|
90
|
+
var validate2 = (options) => __async(null, null, function* () {
|
|
91
|
+
const { schema, base, alias, stamp, silent } = options;
|
|
92
|
+
const validationStamp = yield validate({
|
|
93
|
+
schema: {
|
|
94
|
+
files: schema,
|
|
95
|
+
base,
|
|
96
|
+
pathAliases: parsePathAliases(alias != null ? alias : [])
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
if (stamp != null) {
|
|
100
|
+
const destination = nodePath2.resolve(base != null ? base : process.cwd(), stamp);
|
|
101
|
+
yield nodeFs2.writeFile(destination, validationStamp);
|
|
102
|
+
}
|
|
103
|
+
if (!silent) {
|
|
104
|
+
console.log(`\u2705 validation encountered no errors.`);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// src/cli/index.ts
|
|
109
|
+
var main = () => {
|
|
110
|
+
process.setUncaughtExceptionCaptureCallback((error) => {
|
|
111
|
+
if (error instanceof graphql.GraphQLError) {
|
|
112
|
+
console.error(error.toString());
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
console.error(error);
|
|
116
|
+
});
|
|
117
|
+
commander3.program.name(`graphql-buddy`).description(
|
|
118
|
+
`CLI tools for manipulating graphql // designed for build system tooling.`
|
|
119
|
+
).addCommand(createBundleCommand()).addCommand(createValidateCommand()).parse();
|
|
120
|
+
};
|
|
121
|
+
main();
|
|
122
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli/index.ts","../src/cli/commands/bundle.ts","../src/cli/shared/parsePathAliases.ts","../src/cli/commands/validate.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport * as commander from '@commander-js/extra-typings';\nimport * as graphql from 'graphql';\n\nimport * as commands from './commands';\n\nconst main = () => {\n process.setUncaughtExceptionCaptureCallback((error) => {\n // When thrown natively, GraphQLError prints like any other error with\n // minimal context, however, when logged with `.toString()` it prints\n // helpful debugging information critical for debugging syntax errors (such\n // as source file, location, symbol, etc...).\n //\n // For example:\n //\n // ```\n // Syntax Error: Expected \":\", found Name \"ID\".\n //\n // src/services/api/schema/CurrentUser.graphql:2:6\n // 1 | type CurrentUser {\n // 2 | id ID!\n // | ^\n // 3 | dateCreated: DateTime!\n // ```\n //\n // Versus the naive output:\n //\n // ```\n // return new _GraphQLError.GraphQLError(`Syntax Error: ${description}`, {\n // GraphQLError: Syntax Error: Expected \":\", found Name \"ID\".\n // at syntaxError (node_modules/graphql/error/syntaxError.js:15:10)\n // ```\n if (error instanceof graphql.GraphQLError) {\n console.error(error.toString());\n return;\n }\n\n console.error(error);\n });\n\n commander.program\n .name(`graphql-buddy`)\n .description(\n `CLI tools for manipulating graphql // designed for build system tooling.`,\n )\n .addCommand(commands.createBundleCommand())\n .addCommand(commands.createValidateCommand())\n .parse();\n};\n\nmain();\n","import * as nodeFs from 'node:fs/promises';\nimport * as nodePath from 'node:path';\n\nimport * as commander from '@commander-js/extra-typings';\n\nimport * as api from '../../api';\nimport * as shared from '../shared';\n\nexport const createBundleCommand = () =>\n new commander.Command()\n .name(`bundle`)\n .description(`Bundles multiple GraphQL inputs into a single file.`)\n .argument(`<schema...>`, `Input GraphQL schema files to bundle.`)\n .option(\n `-b, --base <directory>`,\n `Base directory from which paths are resolved.`,\n )\n .option(\n `-a, --alias <pattern...>`,\n `Path alias options for @graphql-tools/load.`,\n )\n .option(\n `--out <file>`,\n `Where to write the bundled schema.`,\n `bundle.graphql`,\n )\n .option(`--no-shake`, `Prevents pruning of unused types.`)\n .option(`-s, --silent`, `Only log critical information.`, false)\n .action((schema, options) =>\n bundle({\n schema,\n ...options,\n }),\n );\n\nconst bundle = async (options: {\n schema: Array<string>;\n base?: string;\n alias?: Array<string>;\n out: string;\n shake: boolean;\n silent: boolean;\n}): Promise<void> => {\n const { schema, base, alias, shake, silent } = options;\n const out = nodePath.resolve(base ?? process.cwd(), options.out);\n\n await nodeFs.writeFile(\n out,\n await api.bundle({\n schema: {\n files: schema,\n base,\n pathAliases: shared.parsePathAliases(alias ?? []),\n },\n shake,\n }),\n );\n\n if (!silent) {\n console.log(`✅ bundler encountered no errors.`);\n }\n};\n","export type PathAliases = Map<string, string>;\n\nexport const parsePathAliases = (\n encodedAliases: Array<string>,\n): PathAliases => {\n const decodedAliases = new Map<string, string>();\n\n for (const encodedAlias of encodedAliases) {\n const parts = encodedAlias.split(`=`);\n if (parts.length !== 2) {\n throw new Error(\n `Expected an alias in the form \"alias=source\" but found \"${encodedAlias}\"`,\n );\n }\n\n const [alias, source] = parts;\n if (decodedAliases.has(alias)) {\n throw new Error(`Alias \"${alias}\" is set multiple times.`);\n }\n\n decodedAliases.set(alias, source);\n }\n\n return decodedAliases;\n};\n","import * as nodeFs from 'node:fs/promises';\nimport * as nodePath from 'node:path';\n\nimport * as commander from '@commander-js/extra-typings';\n\nimport * as api from '../../api';\nimport * as shared from '../shared';\n\nexport const createValidateCommand = () =>\n new commander.Command()\n .name(`validate`)\n .description(`Checks that all provided GraphQL files form a valid schema.`)\n .argument(`<schema...>`, `Input GraphQL schema files to validate.`)\n .option(\n `-b, --base <directory>`,\n `Base directory from which paths are resolved.`,\n )\n .option(\n `-a, --alias <pattern...>`,\n `Path alias options for @graphql-tools/load.`,\n )\n .option(\n `--stamp <file>`,\n `Creates a validation stamp to confirm valid GraphQL schema.`,\n )\n .option(`-s, --silent`, `Only log critical information.`, false)\n .action((schema, options) =>\n validate({\n schema,\n ...options,\n }),\n );\n\nconst validate = async (options: {\n schema: Array<string>;\n base?: string;\n alias?: Array<string>;\n stamp?: string;\n silent: boolean;\n}): Promise<void> => {\n const { schema, base, alias, stamp, silent } = options;\n\n const validationStamp = await api.validate({\n schema: {\n files: schema,\n base,\n pathAliases: shared.parsePathAliases(alias ?? []),\n },\n });\n\n if (stamp != null) {\n const destination = nodePath.resolve(base ?? process.cwd(), stamp);\n await nodeFs.writeFile(destination, validationStamp);\n }\n\n if (!silent) {\n console.log(`✅ validation encountered no errors.`);\n }\n};\n"],"mappings":"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","names":["commander","bundle","nodeFs","nodePath","commander","validate"]}
|
package/eslint.config.ts
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import * as nodeUrl from 'node:url';
|
|
2
|
+
|
|
3
|
+
import * as eslintCompat from '@eslint/compat';
|
|
4
|
+
import * as eslint from '@eslint/js';
|
|
5
|
+
import * as eslintConfig from 'eslint/config';
|
|
6
|
+
import * as eslintImportPlugin from 'eslint-plugin-import';
|
|
7
|
+
import * as tseslint from 'typescript-eslint';
|
|
8
|
+
|
|
9
|
+
const CONFIG: tseslint.ConfigArray = tseslint.config(
|
|
10
|
+
eslintConfig.globalIgnores([`pnpm-lock.yaml`]),
|
|
11
|
+
eslintCompat.includeIgnoreFile(
|
|
12
|
+
nodeUrl.fileURLToPath(new URL(`.gitignore`, import.meta.url)),
|
|
13
|
+
`Imported .gitignore patterns`,
|
|
14
|
+
),
|
|
15
|
+
eslint.configs.recommended,
|
|
16
|
+
...tseslint.configs.recommended,
|
|
17
|
+
{
|
|
18
|
+
files: [`**/*.{ts,cts,mts,js,cjs,mjs}`],
|
|
19
|
+
extends: [
|
|
20
|
+
eslintImportPlugin.flatConfigs.recommended,
|
|
21
|
+
eslintImportPlugin.flatConfigs.typescript,
|
|
22
|
+
],
|
|
23
|
+
settings: {
|
|
24
|
+
'import/resolver': {
|
|
25
|
+
typescript: {},
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
rules: {
|
|
29
|
+
// Allow unused variables only when prefixed with '_'.
|
|
30
|
+
'@typescript-eslint/no-unused-vars': [
|
|
31
|
+
`warn`,
|
|
32
|
+
{
|
|
33
|
+
varsIgnorePattern: `^_`,
|
|
34
|
+
argsIgnorePattern: `^_`,
|
|
35
|
+
caughtErrorsIgnorePattern: `^_`,
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
|
|
39
|
+
// Require a single line of vertical whitespace between imports and file
|
|
40
|
+
// content.
|
|
41
|
+
'import/newline-after-import': [`error`, { count: 1 }],
|
|
42
|
+
|
|
43
|
+
// Dependency cycles can create hard-to-reason through execution
|
|
44
|
+
// lifecycles and form unexpected bundling artifacts. To simplify, we
|
|
45
|
+
// disallow them.
|
|
46
|
+
'import/no-cycle': [`error`, {}],
|
|
47
|
+
|
|
48
|
+
'import/namespace': 0,
|
|
49
|
+
'import/export': 0,
|
|
50
|
+
'import/order': [
|
|
51
|
+
`error`,
|
|
52
|
+
{
|
|
53
|
+
groups: [`builtin`, `external`, `internal`, `type`],
|
|
54
|
+
pathGroups: [
|
|
55
|
+
{
|
|
56
|
+
pattern: `//**`,
|
|
57
|
+
group: `internal`,
|
|
58
|
+
position: `before`,
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
pathGroupsExcludedImportTypes: [],
|
|
62
|
+
'newlines-between': `always`,
|
|
63
|
+
named: true,
|
|
64
|
+
alphabetize: {
|
|
65
|
+
order: `asc`,
|
|
66
|
+
caseInsensitive: true,
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
|
|
71
|
+
// When selecting either T[] or Array<T> we've decided to prefer the
|
|
72
|
+
// latter because it is more consistent in syntax with our other container
|
|
73
|
+
// types (for example, Map<T>, Set<T>, Custom<T>).
|
|
74
|
+
'@typescript-eslint/array-type': [
|
|
75
|
+
`error`,
|
|
76
|
+
{ default: `generic`, readonly: `generic` },
|
|
77
|
+
],
|
|
78
|
+
|
|
79
|
+
// Interfaces and types are not equivalent. Most notably, interfaces
|
|
80
|
+
// support recursive structures with fewer constraints however they do not
|
|
81
|
+
// support "index signature types". The result is that we cannot cast an
|
|
82
|
+
// interface to something like `Record<string, any>` while types can. This
|
|
83
|
+
// is because interfaces are considered mutable whereas types are static
|
|
84
|
+
// by the compiler. For this reason we prefer types for their immutability
|
|
85
|
+
// treatment.
|
|
86
|
+
'@typescript-eslint/consistent-type-definitions': [`error`, `type`],
|
|
87
|
+
|
|
88
|
+
// TypeScript allows specifying a type keyword on imports to indicate that
|
|
89
|
+
// the export exists only in the type system, not at runtime. This allows
|
|
90
|
+
// transpilers to drop imports without knowing the types of the
|
|
91
|
+
// dependencies. We always prefer using `import type` when importing type
|
|
92
|
+
// symbols.
|
|
93
|
+
'@typescript-eslint/consistent-type-imports': [
|
|
94
|
+
`error`,
|
|
95
|
+
{
|
|
96
|
+
prefer: `type-imports`,
|
|
97
|
+
disallowTypeAnnotations: true,
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
|
|
101
|
+
// An arbitrary decision to favor consistency, we use function expressions
|
|
102
|
+
// which are more lightweight rather than function declarations.
|
|
103
|
+
'func-style': [`error`, `expression`, { allowArrowFunctions: true }],
|
|
104
|
+
|
|
105
|
+
// We adhere to the Google style guide which *does* allow inline bodies
|
|
106
|
+
// for single line statements such as:
|
|
107
|
+
//
|
|
108
|
+
// ```ts
|
|
109
|
+
// if (foo) return foo;
|
|
110
|
+
// ```
|
|
111
|
+
//
|
|
112
|
+
// However, they also share that they only support this style to
|
|
113
|
+
// historical legacy reasons and that new projects do not support inline
|
|
114
|
+
// bodies. Here we disable all inline bodies.
|
|
115
|
+
curly: [`error`, `all`],
|
|
116
|
+
|
|
117
|
+
// Prefer `const` instead of `let` when a variable is never modified.
|
|
118
|
+
'prefer-const': `error`,
|
|
119
|
+
|
|
120
|
+
// Always prefer `let` over `var`.
|
|
121
|
+
'no-var': `error`,
|
|
122
|
+
|
|
123
|
+
// Prevents the comma operator from being used to inline multiple
|
|
124
|
+
// expressions into a single line.
|
|
125
|
+
//
|
|
126
|
+
// For example:
|
|
127
|
+
//
|
|
128
|
+
// ```ts
|
|
129
|
+
// a = b += 5, a + b;
|
|
130
|
+
// ```
|
|
131
|
+
'no-sequences': `error`,
|
|
132
|
+
|
|
133
|
+
// Prevents escaping the multiline character in a file to create multiline
|
|
134
|
+
// strings. Instead prefer to concatenate multiple strings together like
|
|
135
|
+
// so:
|
|
136
|
+
//
|
|
137
|
+
// ```ts
|
|
138
|
+
// const longText =
|
|
139
|
+
// "this is a really " +
|
|
140
|
+
// "really long line";
|
|
141
|
+
// ```
|
|
142
|
+
'no-multi-str': `error`,
|
|
143
|
+
|
|
144
|
+
// Prevents assigning multiple variables in a single expressions.
|
|
145
|
+
//
|
|
146
|
+
// See https://eslint.org/docs/latest/rules/no-multi-assign
|
|
147
|
+
'no-multi-assign': `error`,
|
|
148
|
+
|
|
149
|
+
// Prevents blocks (bodies of code wrapped with curly brackets) which are
|
|
150
|
+
// not part of control flow.
|
|
151
|
+
//
|
|
152
|
+
// See https://eslint.org/docs/latest/rules/no-lone-blocks
|
|
153
|
+
'no-lone-blocks': `error`,
|
|
154
|
+
|
|
155
|
+
// Prevents if statements as the only statement in else blocks.
|
|
156
|
+
//
|
|
157
|
+
// See https://eslint.org/docs/latest/rules/no-lonely-if
|
|
158
|
+
'no-lonely-if': `error`,
|
|
159
|
+
|
|
160
|
+
// Enforces that we use single-quotes unless backticks are required.
|
|
161
|
+
//
|
|
162
|
+
// See https://eslint.org/docs/latest/rules/quotes
|
|
163
|
+
quotes: [`error`, `backtick`],
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
export default CONFIG;
|