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.
Files changed (43) hide show
  1. package/.github/workflows/ci.yml +32 -0
  2. package/.nvmrc +1 -0
  3. package/.prettierignore +1 -0
  4. package/.prettierrc +7 -0
  5. package/LICENSE +21 -0
  6. package/README.md +89 -0
  7. package/dist/api.d.ts +46 -0
  8. package/dist/api.js +13 -0
  9. package/dist/api.js.map +1 -0
  10. package/dist/chunk-Z7B4AQ2U.js +124 -0
  11. package/dist/chunk-Z7B4AQ2U.js.map +1 -0
  12. package/dist/cli.d.ts +1 -0
  13. package/dist/cli.js +122 -0
  14. package/dist/cli.js.map +1 -0
  15. package/eslint.config.ts +168 -0
  16. package/package.json +69 -0
  17. package/pnpm-workspace.yaml +3 -0
  18. package/src/api/commands/bundle.test.ts +138 -0
  19. package/src/api/commands/bundle.ts +29 -0
  20. package/src/api/commands/index.ts +2 -0
  21. package/src/api/commands/validate.test.ts +35 -0
  22. package/src/api/commands/validate.ts +49 -0
  23. package/src/api/index.ts +2 -0
  24. package/src/api/shared/index.ts +1 -0
  25. package/src/api/shared/loadSchema.ts +88 -0
  26. package/src/cli/commands/bundle.ts +62 -0
  27. package/src/cli/commands/index.ts +2 -0
  28. package/src/cli/commands/validate.ts +59 -0
  29. package/src/cli/index.ts +52 -0
  30. package/src/cli/shared/index.ts +1 -0
  31. package/src/cli/shared/parsePathAliases.test.ts +43 -0
  32. package/src/cli/shared/parsePathAliases.ts +25 -0
  33. package/test_fixtures/missing_scalar/schema.graphql +16 -0
  34. package/test_fixtures/multiple_files/Bar.graphql +5 -0
  35. package/test_fixtures/multiple_files/Baz.graphql +5 -0
  36. package/test_fixtures/multiple_files/Foo.graphql +9 -0
  37. package/test_fixtures/multiple_files/Mutation.graphql +9 -0
  38. package/test_fixtures/multiple_files/Query.graphql +5 -0
  39. package/test_fixtures/single_file/schema.graphql +16 -0
  40. package/test_fixtures/single_file_with_extra_deps/schema.graphql +46 -0
  41. package/tsconfig.json +20 -0
  42. package/tsup.config.ts +14 -0
  43. 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
@@ -0,0 +1 @@
1
+ pnpm-lock.yaml
package/.prettierrc ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "printWidth": 80,
3
+ "trailingComma": "all",
4
+ "tabWidth": 2,
5
+ "semi": true,
6
+ "singleQuote": true
7
+ }
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
+ [![npm version](https://badge.fury.io/js/graphql-buddy.svg)](https://badge.fury.io/js/graphql-buddy)
4
+ [![CI](https://github.com/HunterLarco/graphql-buddy/actions/workflows/ci.yml/badge.svg)](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
@@ -0,0 +1,13 @@
1
+ import {
2
+ bundle,
3
+ loadSchema,
4
+ normalizeLoadSchemaOptions,
5
+ validate
6
+ } from "./chunk-Z7B4AQ2U.js";
7
+ export {
8
+ bundle,
9
+ loadSchema,
10
+ normalizeLoadSchemaOptions,
11
+ validate
12
+ };
13
+ //# sourceMappingURL=api.js.map
@@ -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
@@ -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"]}
@@ -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;