graphile-export 0.0.2-beta.3 → 0.0.2-beta.31

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.
@@ -1,15 +1,23 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.exportSchema = exports.exportSchemaAsString = exports.objectNullPrototype = exports.isNotNullish = exports.canRepresentAsIdentifier = void 0;
3
+ exports.canRepresentAsIdentifier = void 0;
4
+ exports.objectNullPrototype = objectNullPrototype;
5
+ exports.exportSchemaAsString = exportSchemaAsString;
6
+ exports.exportValueAsString = exportValueAsString;
7
+ exports.exportSchema = exportSchema;
4
8
  const tslib_1 = require("tslib");
5
9
  const promises_1 = require("node:fs/promises");
6
10
  const node_util_1 = require("node:util");
7
11
  const generator_1 = tslib_1.__importDefault(require("@babel/generator"));
8
12
  const parser_1 = require("@babel/parser");
9
13
  const template_1 = tslib_1.__importDefault(require("@babel/template"));
14
+ const traverse_1 = tslib_1.__importDefault(require("@babel/traverse"));
10
15
  const t = tslib_1.__importStar(require("@babel/types"));
11
16
  const graphql_1 = require("grafast/graphql");
17
+ const helpers_js_1 = require("./helpers.js");
12
18
  const index_js_1 = require("./optimize/index.js");
19
+ const reservedWords_js_1 = require("./reservedWords.js");
20
+ const utils_js_1 = require("./utils.js");
13
21
  const wellKnown_js_1 = require("./wellKnown.js");
14
22
  // Cannot import sql because it's optional
15
23
  // import { sql } from "pg-sql2";
@@ -27,7 +35,7 @@ function isSQL(thing) {
27
35
  else {
28
36
  // An approximation
29
37
  if (typeof sql === "object" && sql !== null) {
30
- return Object.getOwnPropertySymbols(thing).some((s) => s.description === "pg-sql2-type");
38
+ return Object.getOwnPropertySymbols(thing).some((s) => s.description?.startsWith("pg-sql2-type"));
31
39
  }
32
40
  else {
33
41
  return false;
@@ -48,6 +56,14 @@ function identifierOrLiteral(key) {
48
56
  return t.stringLiteral(key);
49
57
  }
50
58
  }
59
+ function literal(key) {
60
+ if (typeof key === "number") {
61
+ return t.numericLiteral(key);
62
+ }
63
+ else {
64
+ return t.stringLiteral(key);
65
+ }
66
+ }
51
67
  function locationHintToIdentifierName(locationHint) {
52
68
  let result = locationHint;
53
69
  result = result.replace(/[[.]/g, "__").replace(/\]/g, "");
@@ -59,7 +75,10 @@ function locationHintToIdentifierName(locationHint) {
59
75
  return result;
60
76
  }
61
77
  function getNameForThing(thing, locationHint, baseNameHint) {
62
- if (typeof thing === "function") {
78
+ if (thing.$exporter$name) {
79
+ return thing.$exporter$name;
80
+ }
81
+ else if (typeof thing === "function") {
63
82
  if (baseNameHint) {
64
83
  return baseNameHint;
65
84
  }
@@ -71,14 +90,17 @@ function getNameForThing(thing, locationHint, baseNameHint) {
71
90
  }
72
91
  else {
73
92
  const thingConstructor = thing.constructor;
74
- const thingConstructorNameRaw = thingConstructor?.name ?? thingConstructor?.displayName ?? null;
93
+ const thingConstructorNameRaw = thingConstructor?.$exporter$name ??
94
+ thingConstructor?.name ??
95
+ thingConstructor?.displayName ??
96
+ null;
75
97
  const thingConstructorName = ["Array", "Object", "Set", "Map"].includes(thingConstructorNameRaw)
76
98
  ? null
77
99
  : thingConstructorNameRaw;
78
100
  const thingName = thing.name ?? thing.displayName ?? null;
79
101
  const name = thingConstructorName && thingName
80
102
  ? `${thingName}${thingConstructorName}`
81
- : thingName ?? thingConstructorName ?? null;
103
+ : (thingName ?? thingConstructorName ?? null);
82
104
  return baseNameHint || name
83
105
  ? (baseNameHint ?? "") + (baseNameHint && name ? "-" : "") + (name ?? "")
84
106
  : "value";
@@ -102,15 +124,6 @@ const reallyGenerate = generator_1.default;
102
124
  const templateOptions = {
103
125
  plugins: ["typescript"],
104
126
  };
105
- function isNotNullish(input) {
106
- return input != null;
107
- }
108
- exports.isNotNullish = isNotNullish;
109
- function isImportable(thing) {
110
- return ((typeof thing === "object" || typeof thing === "function") &&
111
- thing !== null &&
112
- "$$export" in thing);
113
- }
114
127
  function isExportedFromFactory(thing) {
115
128
  return ((typeof thing === "object" || typeof thing === "function") &&
116
129
  thing !== null &&
@@ -120,54 +133,60 @@ const BUILTINS = ["Int", "Float", "Boolean", "ID", "String"];
120
133
  function isBuiltinType(type) {
121
134
  return type.name.startsWith("__") || BUILTINS.includes(type.name);
122
135
  }
136
+ const RESERVED_VARIABLES = {
137
+ // Reserved variables
138
+ AbortController: true,
139
+ Array: true,
140
+ Buffer: true,
141
+ DOMException: true,
142
+ Error: true,
143
+ Event: true,
144
+ EventTarget: true,
145
+ JSON: true,
146
+ Math: true,
147
+ MessageChannel: true,
148
+ MessageEvent: true,
149
+ MessagePort: true,
150
+ Object: true,
151
+ TextDecoder: true,
152
+ TextEncoder: true,
153
+ URL: true,
154
+ URLSearchParams: true,
155
+ WebAssembly: true,
156
+ __dirname: true,
157
+ __filename: true,
158
+ atob: true,
159
+ btoa: true,
160
+ clearImmediate: true,
161
+ clearInterval: true,
162
+ clearTimeout: true,
163
+ console: true,
164
+ exports: true,
165
+ global: true,
166
+ module: true,
167
+ performance: true,
168
+ process: true,
169
+ queueMicrotask: true,
170
+ require: true,
171
+ setImmediate: true,
172
+ setInterval: true,
173
+ setTimeout: true,
174
+ structuredClone: true,
175
+ };
176
+ for (const reservedWord of reservedWords_js_1.reservedWords) {
177
+ RESERVED_VARIABLES[reservedWord] = true;
178
+ }
179
+ Object.freeze(RESERVED_VARIABLES);
123
180
  class CodegenFile {
124
181
  constructor(options) {
125
182
  this.options = options;
126
- this._variables = Object.assign(Object.create(null), {
127
- // Reserved variables
128
- AbortController: true,
129
- Array: true,
130
- Buffer: true,
131
- DOMException: true,
132
- Error: true,
133
- Event: true,
134
- EventTarget: true,
135
- JSON: true,
136
- Math: true,
137
- MessageChannel: true,
138
- MessageEvent: true,
139
- MessagePort: true,
140
- Object: true,
141
- TextDecoder: true,
142
- TextEncoder: true,
143
- URL: true,
144
- URLSearchParams: true,
145
- WebAssembly: true,
146
- __dirname: true,
147
- __filename: true,
148
- atob: true,
149
- btoa: true,
150
- clearImmediate: true,
151
- clearInterval: true,
152
- clearTimeout: true,
153
- console: true,
154
- exports: true,
155
- global: true,
156
- module: true,
157
- performance: true,
158
- process: true,
159
- queueMicrotask: true,
160
- require: true,
161
- setImmediate: true,
162
- setInterval: true,
163
- setTimeout: true,
164
- structuredClone: true,
165
- });
183
+ this._variables = Object.assign(Object.create(null), RESERVED_VARIABLES);
166
184
  this._imports = Object.create(null);
167
185
  this._types = Object.create(null);
168
186
  this._directives = Object.create(null);
169
187
  this._statements = [];
170
188
  this._values = new Map();
189
+ this._funcToAstCache = new Map();
171
190
  }
172
191
  addStatements(statements) {
173
192
  if (Array.isArray(statements)) {
@@ -431,6 +450,9 @@ class CodegenFile {
431
450
  name: t.stringLiteral(config.name),
432
451
  description: desc(config.description),
433
452
  extensions: extensions(this, config.extensions, `${config.name}.extensions`, `${config.name}.extensions`),
453
+ isOneOf: config.isOneOf
454
+ ? t.booleanLiteral(true)
455
+ : t.identifier("undefined"),
434
456
  fields: t.arrowFunctionExpression([], this.makeInputObjectFields(config.fields, config.name)),
435
457
  });
436
458
  }
@@ -534,6 +556,9 @@ function canBeRegularObjectKey(key) {
534
556
  return key !== "__proto__";
535
557
  }
536
558
  function _convertToAST(file, thing, locationHint, nameHint, depth, reference) {
559
+ if ((0, helpers_js_1.isForbidden)(thing)) {
560
+ throw new Error(`The value at ${locationHint} is forbidden from being exported; please be more specific in your EXPORTABLE factories!`);
561
+ }
537
562
  const handleSubvalue = (value, tKey, key) => {
538
563
  const existingIdentifier = getExistingIdentifier(file, value);
539
564
  if (existingIdentifier) {
@@ -566,22 +591,25 @@ function _convertToAST(file, thing, locationHint, nameHint, depth, reference) {
566
591
  return func(file, thing, locationHint, nameHint);
567
592
  }
568
593
  else if ((0, graphql_1.isSchema)(thing)) {
569
- throw new Error("Attempted to export GraphQLSchema directly from `_convertToAST`; this is currently unsupported.");
594
+ throw new Error(`Attempted to export GraphQLSchema directly from \`_convertToAST\` (at ${locationHint}); this is currently unsupported.`);
570
595
  }
571
596
  else if (typeof thing === "object" && thing != null) {
572
597
  const prototype = Object.getPrototypeOf(thing);
573
598
  if (prototype !== null && prototype !== Object.prototype) {
574
- throw new Error(`Attempting to export an instance of a class; you should wrap this definition in EXPORTABLE! (Class: ${thing.constructor})`);
599
+ if (thing.constructor) {
600
+ throw new Error(`Attempting to export an instance of a class (at ${locationHint}); you should wrap this definition in EXPORTABLE! (Class: ${thing.constructor})`);
601
+ }
602
+ else {
603
+ throw new Error(`Attempting to export non-POJO object (at ${locationHint}); you should wrap this definition in EXPORTABLE! (Prototype: ${(0, node_util_1.inspect)(prototype)})`);
604
+ }
575
605
  }
576
606
  const propertyPairs = [];
577
- let hasUnsafeKeys = false;
578
- Object.entries(thing).forEach(([key, value]) => {
579
- const tKey = identifierOrLiteral(key);
607
+ const entries = Object.entries(thing);
608
+ const hasUnsafeKeys = entries.some(([key]) => !canBeRegularObjectKey(key));
609
+ entries.forEach(([key, value]) => {
610
+ const tKey = hasUnsafeKeys ? literal(key) : identifierOrLiteral(key);
580
611
  const subvalue = handleSubvalue(value, tKey, key);
581
612
  propertyPairs.push([tKey, subvalue]);
582
- if (!canBeRegularObjectKey(key)) {
583
- hasUnsafeKeys = true;
584
- }
585
613
  });
586
614
  if (prototype === null) {
587
615
  if (hasUnsafeKeys) {
@@ -590,20 +618,14 @@ function _convertToAST(file, thing, locationHint, nameHint, depth, reference) {
590
618
  t.arrayExpression(propertyPairs.map(([key, val]) => t.arrayExpression([key, val]))),
591
619
  ]);
592
620
  }
593
- else if (propertyPairs.length === 0) {
594
- return t.callExpression(t.memberExpression(t.identifier("Object"), t.identifier("create")), [t.nullLiteral()]);
595
- }
596
621
  else {
597
- const obj = t.objectExpression(propertyPairs.map(([key, val]) => t.objectProperty(key, val)));
598
- return t.callExpression(t.memberExpression(t.identifier("Object"), t.identifier("assign")), [
599
- t.callExpression(t.memberExpression(t.identifier("Object"), t.identifier("create")), [t.nullLiteral()]),
600
- obj,
601
- ]);
622
+ const obj = objectNullPrototype(propertyPairs.map(([key, val]) => t.objectProperty(key, val)));
623
+ return obj;
602
624
  }
603
625
  }
604
626
  else {
605
627
  if (hasUnsafeKeys) {
606
- throw new Error(`Unexportable key found on non-null-prototype object`);
628
+ throw new Error(`Unexportable key found on non-null-prototype object (at ${locationHint})`);
607
629
  }
608
630
  else {
609
631
  const obj = t.objectExpression(propertyPairs.map(([key, val]) => t.objectProperty(key, val)));
@@ -639,7 +661,7 @@ const getExistingIdentifier = (file, thing) => {
639
661
  const { moduleName, exportName } = (0, wellKnown_js_1.wellKnown)(file.options, thing);
640
662
  return file.import(moduleName, exportName);
641
663
  }
642
- else if (isImportable(thing)) {
664
+ else if ((0, utils_js_1.isImportable)(thing)) {
643
665
  const { moduleName, exportName } = thing.$$export;
644
666
  return file.import(moduleName, exportName);
645
667
  }
@@ -650,6 +672,21 @@ const getExistingIdentifier = (file, thing) => {
650
672
  return file.declareType(thing);
651
673
  }
652
674
  };
675
+ function importWellKnownOrFactory(file, value, locationHint, nameHint) {
676
+ if ((0, utils_js_1.isImportable)(value)) {
677
+ return file.import(value.$$export.moduleName, value.$$export.exportName);
678
+ }
679
+ else if ((0, wellKnown_js_1.wellKnown)(file.options, value)) {
680
+ const { moduleName, exportName } = (0, wellKnown_js_1.wellKnown)(file.options, value);
681
+ return file.import(moduleName, exportName);
682
+ }
683
+ else if (isExportedFromFactory(value)) {
684
+ return factoryAst(file, value, locationHint, nameHint);
685
+ }
686
+ else {
687
+ return undefined;
688
+ }
689
+ }
653
690
  function convertToIdentifierViaAST(file, thing, baseNameHint, locationHint, depth = 0) {
654
691
  const existingIdentifier = getExistingIdentifier(file, thing);
655
692
  if (existingIdentifier) {
@@ -659,9 +696,8 @@ function convertToIdentifierViaAST(file, thing, baseNameHint, locationHint, dept
659
696
  const nameHint = getNameForThing(thing, locationHint, baseNameHint);
660
697
  const variableIdentifier = file.makeVariable(nameHint || "value");
661
698
  file._values.set(thing, variableIdentifier);
662
- const ast = isExportedFromFactory(thing)
663
- ? factoryAst(file, thing, locationHint, nameHint)
664
- : _convertToAST(file, thing, locationHint, nameHint, depth, variableIdentifier);
699
+ const ast = importWellKnownOrFactory(file, thing, locationHint, nameHint) ??
700
+ _convertToAST(file, thing, locationHint, nameHint, depth, variableIdentifier);
665
701
  if (ast.type === "Identifier") {
666
702
  console.warn(`graphile-export error: AST returned an identifier '${ast.name}'; this could cause an infinite loop.`);
667
703
  }
@@ -679,20 +715,20 @@ function objectToObjectProperties(o) {
679
715
  .map(([key, value]) => t.objectProperty(identifierOrLiteral(key), value));
680
716
  }
681
717
  function extensions(file, extensions, locationHint, nameHint) {
682
- if (extensions == null || Object.keys(extensions).length === 0) {
683
- return null;
684
- }
685
- return convertToIdentifierViaAST(file, extensions, nameHint, locationHint);
718
+ return isNotEmpty(extensions)
719
+ ? convertToIdentifierViaAST(file, extensions, nameHint, locationHint)
720
+ : null;
686
721
  }
687
- /** Maps to `Object.assign(Object.create(null), {...})` */
722
+ /**
723
+ * Maps to `{__proto__: null, ...}` which is similar to
724
+ * `Object.assign(Object.create(null), {...})`
725
+ */
688
726
  function objectNullPrototype(properties) {
689
- const objectCreateNull = t.callExpression(t.memberExpression(t.identifier("Object"), t.identifier("create")), [t.nullLiteral()]);
690
- if (properties.length === 0) {
691
- return objectCreateNull;
692
- }
693
- return t.callExpression(t.memberExpression(t.identifier("Object"), t.identifier("assign")), [objectCreateNull, t.objectExpression(properties)]);
727
+ return t.objectExpression([
728
+ t.objectProperty(t.identifier("__proto__"), t.nullLiteral()),
729
+ ...properties,
730
+ ]);
694
731
  }
695
- exports.objectNullPrototype = objectNullPrototype;
696
732
  /*
697
733
  function iife(statements: t.Statement[]): t.Expression {
698
734
  return t.callExpression(
@@ -709,24 +745,13 @@ function func(file, fn, locationHint, nameHint) {
709
745
  // scope; e.g.:
710
746
  //
711
747
  // `(() => { const foo = 1, bar = 2; return /*>*/() => {return foo+bar}/*<*/})();`
712
- if (isExportedFromFactory(fn)) {
713
- return factoryAst(file, fn, locationHint, nameHint);
714
- }
715
- else if ((0, wellKnown_js_1.wellKnown)(file.options, fn)) {
716
- const { moduleName, exportName } = (0, wellKnown_js_1.wellKnown)(file.options, fn);
717
- return file.import(moduleName, exportName);
718
- }
719
- else if (isImportable(fn)) {
720
- return file.import(fn.$$export.moduleName, fn.$$export.exportName);
721
- }
722
- else {
723
- return funcToAst(fn, locationHint, nameHint);
724
- }
748
+ return (importWellKnownOrFactory(file, fn, locationHint, nameHint) ??
749
+ funcToAst(file, fn, locationHint, nameHint).ast);
725
750
  }
726
751
  const shouldOptimizeFactoryCalls = true;
727
752
  function factoryAst(file, fn, locationHint, nameHint) {
728
753
  const factory = fn.$exporter$factory;
729
- const funcAST = funcToAst(factory, locationHint, nameHint);
754
+ const { functionWithoutOwnAttributesAST: funcAST } = funcToAst(file, factory, locationHint, nameHint);
730
755
  const depArgs = fn.$exporter$args.map((arg, i) => {
731
756
  if (typeof arg === "string") {
732
757
  return t.stringLiteral(arg);
@@ -778,13 +803,79 @@ function factoryAst(file, fn, locationHint, nameHint) {
778
803
  return t.callExpression(funcAST, depArgs);
779
804
  }
780
805
  }
781
- function funcToAst(fn, locationHint, _nameHint) {
806
+ function funcToAst(file, fn, locationHint, _nameHint) {
807
+ if (file._funcToAstCache.has(fn)) {
808
+ return file._funcToAstCache.get(fn);
809
+ }
810
+ const path = _funcToAst(fn, locationHint, _nameHint);
811
+ const externalReferences = new Set();
812
+ const localBindings = path.scope.bindings;
813
+ path.traverse({
814
+ Identifier(path) {
815
+ if (t.isReferenced(path.node, path.parent) && // Is a variable reference
816
+ !path.scope.hasBinding(path.node.name) && // Not defined in local scope
817
+ !localBindings[path.node.name] // Not a parameter of the function
818
+ ) {
819
+ externalReferences.add(path.node.name);
820
+ }
821
+ },
822
+ });
823
+ // Remove global things they're allowed to reference
824
+ externalReferences.delete("Buffer");
825
+ externalReferences.delete("console");
826
+ externalReferences.delete("process");
827
+ externalReferences.delete("setTimeout");
828
+ externalReferences.delete("setInterval");
829
+ if (externalReferences.size > 0) {
830
+ throw new Error(`The function being exported as ${locationHint} references external variables: \`${[
831
+ ...externalReferences,
832
+ ].join("`, `")}\`. Please ensure this function is wrapped in \`EXPORTABLE(() => ...)\`. Fn:\n${fn}`);
833
+ }
834
+ const fnExpression = path.node;
835
+ const ownProps = Object.entries(fn);
836
+ const result = (() => {
837
+ if (ownProps.length > 0) {
838
+ // Need to assign things to it
839
+ const properties = ownProps.map(([key, value]) => {
840
+ return t.objectProperty(identifierOrLiteral(key), convertToIdentifierViaAST(file, value, `${locationHint}.${key}`, `${locationHint}['${key}']`));
841
+ });
842
+ return {
843
+ functionWithoutOwnAttributesAST: fnExpression,
844
+ ast: t.callExpression(t.memberExpression(t.identifier("Object"), t.identifier("assign")), [fnExpression, t.objectExpression(properties)]),
845
+ };
846
+ }
847
+ else {
848
+ return {
849
+ functionWithoutOwnAttributesAST: fnExpression,
850
+ ast: fnExpression,
851
+ };
852
+ }
853
+ })();
854
+ file._funcToAstCache.set(fn, result);
855
+ return result;
856
+ }
857
+ function parseExpressionViaDoc(funcString) {
858
+ const doc = (0, parser_1.parse)(`const f = ${funcString}`, {
859
+ sourceType: "module",
860
+ plugins: ["typescript"],
861
+ });
862
+ let result = null;
863
+ (0, traverse_1.default)(doc, {
864
+ VariableDeclaration(path) {
865
+ result = path.get("declarations.0.init");
866
+ path.stop();
867
+ },
868
+ });
869
+ if (!result) {
870
+ throw new Error(`graphile-export internal error - failed to find the variable declaration (?!!)`);
871
+ }
872
+ return result;
873
+ }
874
+ function _funcToAst(fn, locationHint, _nameHint) {
782
875
  const funcString = fn.toString().trim();
783
876
  try {
784
- const result = (0, parser_1.parseExpression)(funcString, {
785
- sourceType: "module",
786
- plugins: ["typescript"],
787
- });
877
+ const path = parseExpressionViaDoc(funcString);
878
+ const result = path.node;
788
879
  if (result.type !== "FunctionExpression" &&
789
880
  result.type !== "ArrowFunctionExpression") {
790
881
  if (result.type === "ClassExpression") {
@@ -793,7 +884,7 @@ Object.defineProperty(${result.id?.name ?? "MyClass"}, '$$export', { value: { mo
793
884
  }
794
885
  throw new Error(`Expected FunctionExpression or ArrowFunctionExpression but saw ${result.type}`);
795
886
  }
796
- return result;
887
+ return path;
797
888
  }
798
889
  catch (e) {
799
890
  if (e.retry === false) {
@@ -811,15 +902,13 @@ Object.defineProperty(${result.id?.name ?? "MyClass"}, '$$export', { value: { mo
811
902
  const modifiedDefinition = funcString.startsWith("async ")
812
903
  ? "async function " + funcString.slice(6)
813
904
  : "function " + funcString;
814
- const result = (0, parser_1.parseExpression)(modifiedDefinition, {
815
- sourceType: "module",
816
- plugins: ["typescript"],
817
- });
905
+ const path = parseExpressionViaDoc(modifiedDefinition);
906
+ const result = path.node;
818
907
  if (result.type !== "FunctionExpression" &&
819
908
  result.type !== "ArrowFunctionExpression") {
820
909
  throw new Error(`Expected FunctionExpression or ArrowFunctionExpression but saw ${result.type}`);
821
910
  }
822
- return result;
911
+ return path;
823
912
  }
824
913
  catch {
825
914
  throw new Error(`Function export error at ${locationHint} - failed to process function definition '${trimDef(fn.toString())}'\n ${String(e.stack ?? e)
@@ -836,6 +925,7 @@ function exportSchemaGraphQLJS({ config, customTypes, customDirectives, file, })
836
925
  const types = customTypes.map((type) => {
837
926
  return file.declareType(type);
838
927
  });
928
+ const specifiedDirectivesAST = file.import("graphql", "specifiedDirectives");
839
929
  file.addStatements(declareGraphQLEntity(file, schemaExportName, "GraphQLSchema", {
840
930
  description: desc(config.description),
841
931
  query: config.query ? file.declareType(config.query) : t.nullLiteral(),
@@ -845,17 +935,19 @@ function exportSchemaGraphQLJS({ config, customTypes, customDirectives, file, })
845
935
  : null,
846
936
  types: t.arrayExpression(types),
847
937
  directives: customDirectives.length > 0
848
- ? t.arrayExpression(customDirectives.map((directive) => file.declareDirective(directive)))
938
+ ? t.arrayExpression([
939
+ t.spreadElement(specifiedDirectivesAST),
940
+ ...customDirectives.map((directive) => file.declareDirective(directive)),
941
+ ])
849
942
  : null,
850
943
  extensions: extensions(file, config.extensions, "schema.extensions", "schema.extensions"),
851
- enableDeferStream: t.booleanLiteral(process.env.ENABLE_DEFER_STREAM === "1"),
852
- /*
853
- // TODO: use the below once https://github.com/graphql/graphql-js/pull/3450 is fixed:
854
- enableDeferStream:
855
- config.enableDeferStream != null
856
- ? t.booleanLiteral(config.enableDeferStream)
944
+ // @ts-ignore
945
+ enableDeferStream:
946
+ // @ts-ignore
947
+ config.enableDeferStream != null
948
+ ? // @ts-ignore
949
+ t.booleanLiteral(config.enableDeferStream)
857
950
  : null,
858
- */
859
951
  assumeValid: null, // TODO: t.booleanLiteral(true),
860
952
  }));
861
953
  }
@@ -866,19 +958,30 @@ function exportSchemaGraphQLJS({ config, customTypes, customDirectives, file, })
866
958
  */
867
959
  function exportSchemaTypeDefs({ schema, customTypes, file, }) {
868
960
  const typeDefsExportName = file.makeVariable("typeDefs");
869
- const plansExportName = file.makeVariable("plans");
961
+ const objectPlansProperties = Object.create(null);
962
+ const interfacePlansProperties = Object.create(null);
963
+ const unionPlansProperties = Object.create(null);
964
+ const inputObjectPlansProperties = Object.create(null);
965
+ const scalarPlansProperties = Object.create(null);
966
+ const enumPlansProperties = Object.create(null);
870
967
  const schemaExportName = file.makeVariable("schema");
871
968
  const typeDefsString = (0, graphql_1.printSchema)(schema);
872
969
  const graphqlAST = t.templateLiteral([t.templateElement({ raw: typeDefsString.replace(/[\\`]/g, "\\$&") })], []);
873
970
  graphqlAST.leadingComments = [
874
971
  { type: "CommentBlock", value: " GraphQL " },
875
972
  ];
876
- const plansProperties = [];
877
973
  customTypes.forEach((type) => {
878
974
  if (type instanceof graphql_1.GraphQLObjectType) {
879
- const typeProperties = [];
975
+ const typeProperties = Object.create(null);
976
+ const plansProperties = Object.create(null);
880
977
  if (type.extensions.grafast?.assertStep) {
881
- typeProperties.push(t.objectProperty(t.identifier("__assertStep"), convertToIdentifierViaAST(file, type.extensions.grafast.assertStep, `${type.name}AssertStep`, `${type.name}.extensions.assertStep`)));
978
+ typeProperties.assertStep = convertToIdentifierViaAST(file, type.extensions.grafast.assertStep, `${type.name}AssertStep`, `${type.name}.extensions.assertStep`);
979
+ }
980
+ if (type.isTypeOf) {
981
+ typeProperties.isTypeOf = convertToIdentifierViaAST(file, type.isTypeOf, `${type.name}IsTypeOf`, `${type.name}.extensions.isTypeOf`);
982
+ }
983
+ if (type.extensions.grafast?.planType) {
984
+ typeProperties.planType = convertToIdentifierViaAST(file, type.extensions.grafast.planType, `${type.name}PlanType`, `${type.name}.extensions.planType`);
882
985
  }
883
986
  for (const [fieldName, field] of Object.entries(type.toConfig().fields)) {
884
987
  // Use shorthand if there's only a `plan` and nothing else
@@ -899,9 +1002,38 @@ function exportSchemaTypeDefs({ schema, customTypes, file, }) {
899
1002
  const args = field.args
900
1003
  ? Object.entries(field.args)
901
1004
  .map(([argName, arg]) => {
902
- return t.objectProperty(identifierOrLiteral(argName), convertToIdentifierViaAST(file, arg.extensions?.grafast, `${type.name}.${fieldName}.${argName}`, `${type.name}.fields[${fieldName}].args[${argName}].extensions.grafast`));
1005
+ if (arg.extensions) {
1006
+ const { grafast, ...rest } = arg.extensions;
1007
+ const extensionsAST = extensions(file, rest, `${type.name}.${fieldName}.${argName}`, `${type.name}.fields[${fieldName}].args[${argName}].extensions`);
1008
+ if (!extensionsAST) {
1009
+ if (!grafast)
1010
+ return null;
1011
+ const keys = Object.keys(grafast);
1012
+ if (keys.length === 1 && keys[0] === "applyPlan") {
1013
+ // Shorthand
1014
+ return t.objectProperty(identifierOrLiteral(argName), convertToIdentifierViaAST(file, grafast.applyPlan, `${type.name}.${fieldName}${argName}ApplyPlan`, `${type.name}.fields[${fieldName}].args[${argName}].applyPlan`));
1015
+ }
1016
+ }
1017
+ return t.objectProperty(identifierOrLiteral(argName), t.objectExpression([
1018
+ ...objectToObjectProperties({
1019
+ extensions: extensionsAST,
1020
+ }),
1021
+ ...(grafast
1022
+ ? Object.entries(grafast)
1023
+ .map(([k, v]) => {
1024
+ if (v == null)
1025
+ return null;
1026
+ return t.objectProperty(t.identifier(k), convertToIdentifierViaAST(file, grafast.applyPlan, `${type.name}.${fieldName}${argName}${k}`, `${type.name}.fields[${fieldName}].args[${argName}].extensions.grafast[${k}]`));
1027
+ })
1028
+ .filter(utils_js_1.isNotNullish)
1029
+ : []),
1030
+ ]));
1031
+ }
1032
+ else {
1033
+ return null;
1034
+ }
903
1035
  })
904
- .filter(isNotNullish)
1036
+ .filter(utils_js_1.isNotNullish)
905
1037
  : null;
906
1038
  const argsAST = args && args.length ? t.objectExpression(args) : null;
907
1039
  if (!planAST && !subscribePlanAST && !resolveAST && !subscribeAST) {
@@ -925,24 +1057,69 @@ function exportSchemaTypeDefs({ schema, customTypes, file, }) {
925
1057
  subscribe: subscribeAST,
926
1058
  args: argsAST,
927
1059
  }));
928
- typeProperties.push(t.objectProperty(identifierOrLiteral(fieldName), fieldSpec));
1060
+ plansProperties[fieldName] = fieldSpec;
929
1061
  }
930
- plansProperties.push(t.objectProperty(identifierOrLiteral(type.name), t.objectExpression(typeProperties)));
1062
+ setIfNotEmpty(typeProperties, "plans", plansProperties, true);
1063
+ setIfNotEmpty(objectPlansProperties, type.name, typeProperties, false);
931
1064
  }
932
1065
  else if (type instanceof graphql_1.GraphQLInputObjectType) {
933
- const typeProperties = [];
1066
+ const typeProperties = Object.create(null);
1067
+ const plansProperties = Object.create(null);
1068
+ if (type.extensions?.grafast?.baked) {
1069
+ typeProperties.baked = convertToIdentifierViaAST(file, type.extensions?.grafast.baked, `${type.name}.inputPlan`, `${type.name}.extensions.grafast.baked`);
1070
+ }
934
1071
  for (const [fieldName, field] of Object.entries(type.toConfig().fields)) {
935
- typeProperties.push(t.objectProperty(identifierOrLiteral(fieldName), convertToIdentifierViaAST(file, field.extensions?.grafast, `${type.name}.${fieldName}`, `${type.name}.fields[${fieldName}].extensions.grafast`)));
1072
+ if (!field.extensions)
1073
+ continue;
1074
+ const { grafast, ...rest } = field.extensions;
1075
+ const extensionsAST = extensions(file, rest, `${type.name}_${fieldName}Extensions`, `${type.name}.fields[${fieldName}].extensions`);
1076
+ if (!extensionsAST) {
1077
+ if (!grafast)
1078
+ continue;
1079
+ const keys = Object.keys(grafast);
1080
+ if (keys.length === 1 && keys[0] === "apply") {
1081
+ plansProperties[fieldName] = convertToIdentifierViaAST(file, grafast.apply, `${type.name}.${fieldName}Apply`, `${type.name}.fields[${fieldName}].extensions.grafast.apply`);
1082
+ continue;
1083
+ }
1084
+ }
1085
+ plansProperties[fieldName] = t.objectExpression([
1086
+ ...objectToObjectProperties({
1087
+ extensions: extensionsAST,
1088
+ }),
1089
+ ...(grafast
1090
+ ? Object.entries(grafast)
1091
+ .map(([k, v]) => {
1092
+ if (v == null)
1093
+ return null;
1094
+ return t.objectProperty(t.identifier(k), convertToIdentifierViaAST(file, v, `${type.name}.${fieldName}${k}`, `${type.name}.fields[${fieldName}].extensions.grafast[${k}]`));
1095
+ })
1096
+ .filter(utils_js_1.isNotNullish)
1097
+ : []),
1098
+ ]);
936
1099
  }
937
- plansProperties.push(t.objectProperty(identifierOrLiteral(type.name), t.objectExpression(typeProperties)));
1100
+ setIfNotEmpty(typeProperties, "plans", plansProperties, true);
1101
+ setIfNotEmpty(inputObjectPlansProperties, type.name, typeProperties, false);
938
1102
  }
939
1103
  else if (type instanceof graphql_1.GraphQLInterfaceType ||
940
1104
  type instanceof graphql_1.GraphQLUnionType) {
941
1105
  const config = type.toConfig();
942
- if (config.resolveType) {
943
- plansProperties.push(t.objectProperty(identifierOrLiteral(type.name), t.objectExpression(objectToObjectProperties({
944
- __resolveType: convertToIdentifierViaAST(file, type.resolveType, `${type.name}ResolveType`, `${type.name}.resolveType`),
945
- }))));
1106
+ if (config.resolveType ||
1107
+ config.extensions.grafast?.toSpecifier ||
1108
+ config.extensions.grafast?.planType) {
1109
+ const target = type instanceof graphql_1.GraphQLInterfaceType
1110
+ ? interfacePlansProperties
1111
+ : unionPlansProperties;
1112
+ target[type.name] = t.objectExpression(objectToObjectProperties({
1113
+ resolveType: type.resolveType
1114
+ ? convertToIdentifierViaAST(file, type.resolveType, `${type.name}ResolveType`, `${type.name}.resolveType`)
1115
+ : null,
1116
+ toSpecifier: type.extensions?.grafast?.toSpecifier
1117
+ ? convertToIdentifierViaAST(file, type.extensions?.grafast?.toSpecifier, `${type.name}ToSpecifier`, `${type.name}.toSpecifier`)
1118
+ : null,
1119
+ planType: type.extensions?.grafast?.planType
1120
+ ? convertToIdentifierViaAST(file, type.extensions?.grafast?.planType, `${type.name}PlanType`, `${type.name}.planType`)
1121
+ : null,
1122
+ }));
946
1123
  }
947
1124
  }
948
1125
  else if (type instanceof graphql_1.GraphQLScalarType) {
@@ -950,36 +1127,65 @@ function exportSchemaTypeDefs({ schema, customTypes, file, }) {
950
1127
  const planAST = config.extensions.grafast?.plan
951
1128
  ? convertToIdentifierViaAST(file, config.extensions?.grafast?.plan, `${type.name}Plan`, `${type.name}.extensions.grafast.plan`)
952
1129
  : null;
953
- if (planAST) {
954
- plansProperties.push(t.objectProperty(identifierOrLiteral(type.name), t.objectExpression(objectToObjectProperties({
955
- serialize: convertToIdentifierViaAST(file, type.serialize, `${type.name}Serialize`, `${type.name}.serialize`),
956
- parseValue: convertToIdentifierViaAST(file, type.parseValue, `${type.name}ParseValue`, `${type.name}.parseValue`),
957
- parseLiteral: convertToIdentifierViaAST(file, type.parseLiteral, `${type.name}ParseLiteral`, `${type.name}.parseLiteral`),
1130
+ if (planAST ||
1131
+ type.serialize !== graphql_1.GraphQLScalarType.prototype.serialize ||
1132
+ type.parseValue !== graphql_1.GraphQLScalarType.prototype.parseValue ||
1133
+ type.parseLiteral !== graphql_1.GraphQLScalarType.prototype.parseLiteral) {
1134
+ scalarPlansProperties[type.name] = t.objectExpression(objectToObjectProperties({
1135
+ serialize: type.serialize !== graphql_1.GraphQLScalarType.prototype.serialize
1136
+ ? convertToIdentifierViaAST(file, type.serialize, `${type.name}Serialize`, `${type.name}.serialize`)
1137
+ : null,
1138
+ parseValue: type.parseValue !== graphql_1.GraphQLScalarType.prototype.parseValue
1139
+ ? convertToIdentifierViaAST(file, type.parseValue, `${type.name}ParseValue`, `${type.name}.parseValue`)
1140
+ : null,
1141
+ parseLiteral: type.parseLiteral !== graphql_1.GraphQLScalarType.prototype.parseLiteral
1142
+ ? convertToIdentifierViaAST(file, type.parseLiteral, `${type.name}ParseLiteral`, `${type.name}.parseLiteral`)
1143
+ : null,
958
1144
  plan: planAST,
959
- }))));
1145
+ }));
960
1146
  }
961
1147
  }
962
1148
  else if (type instanceof graphql_1.GraphQLEnumType) {
963
1149
  const config = type.toConfig();
964
- const enumValues = [];
1150
+ const typeProperties = Object.create(null);
1151
+ const enumValueProperties = Object.create(null);
965
1152
  for (const [enumValueName, enumValueConfig] of Object.entries(config.values)) {
966
1153
  const valueAST = enumValueConfig.value !== undefined &&
967
1154
  enumValueConfig.value !== enumValueName
968
1155
  ? convertToIdentifierViaAST(file, enumValueConfig.value, `${type.name}_${enumValueName}`, `${type.name}.values[${enumValueName}].value`)
969
1156
  : null;
970
- const applyPlanAST = enumValueConfig.extensions?.grafast?.applyPlan
971
- ? convertToIdentifierViaAST(file, enumValueConfig.extensions.grafast.applyPlan, `${type.name}_${enumValueName}ApplyPlan`, `${type.name}.values[${enumValueName}].extensions.grafast.applyPlan`)
972
- : null;
973
- if (valueAST || applyPlanAST) {
974
- enumValues.push(t.objectProperty(identifierOrLiteral(enumValueName), t.objectExpression(objectToObjectProperties({
975
- value: valueAST,
976
- applyPlan: applyPlanAST,
977
- }))));
1157
+ const { grafast, ...rest } = enumValueConfig.extensions ?? {};
1158
+ const extensionsAST = extensions(file, rest, `${type.name}_${enumValueName}Extensions`, `${type.name}.values[${enumValueName}].extensions`);
1159
+ if (!valueAST && !extensionsAST) {
1160
+ if (!grafast)
1161
+ continue;
1162
+ const keys = Object.keys(grafast);
1163
+ if (keys.length === 1 && keys[0] === "apply") {
1164
+ enumValueProperties[enumValueName] = convertToIdentifierViaAST(file, grafast.apply, `${type.name}.${enumValueName}Apply`, `${type.name}.values[${enumValueName}].extensions.grafast.apply`);
1165
+ continue;
1166
+ }
1167
+ }
1168
+ const grafastProperties = grafast
1169
+ ? Object.entries(grafast)
1170
+ .map(([k, v]) => {
1171
+ if (v == null)
1172
+ return null;
1173
+ return t.objectProperty(t.identifier(k), convertToIdentifierViaAST(file, v, `${type.name}.${enumValueName}${k}`, `${type.name}.values[${enumValueName}].extensions.grafast[${k}]`));
1174
+ })
1175
+ .filter(utils_js_1.isNotNullish)
1176
+ : [];
1177
+ if (valueAST || extensionsAST || grafastProperties.length) {
1178
+ enumValueProperties[enumValueName] = t.objectExpression([
1179
+ ...objectToObjectProperties({
1180
+ value: valueAST,
1181
+ extensions: extensionsAST,
1182
+ }),
1183
+ ...grafastProperties,
1184
+ ]);
978
1185
  }
979
1186
  }
980
- if (enumValues.length > 0) {
981
- plansProperties.push(t.objectProperty(identifierOrLiteral(type.name), t.objectExpression(enumValues)));
982
- }
1187
+ setIfNotEmpty(typeProperties, "values", enumValueProperties, true);
1188
+ setIfNotEmpty(enumPlansProperties, type.name, typeProperties, false);
983
1189
  }
984
1190
  else {
985
1191
  const never = type;
@@ -989,33 +1195,55 @@ function exportSchemaTypeDefs({ schema, customTypes, file, }) {
989
1195
  const typeDefs = t.exportNamedDeclaration(t.variableDeclaration("const", [
990
1196
  t.variableDeclarator(typeDefsExportName, graphqlAST),
991
1197
  ]));
992
- const plans = t.exportNamedDeclaration(t.variableDeclaration("const", [
993
- t.variableDeclarator(plansExportName, t.objectExpression(plansProperties)),
994
- ]));
995
1198
  file.addStatements(typeDefs);
996
- file.addStatements(plans);
1199
+ const typeDefsEtc = {
1200
+ typeDefs: typeDefsExportName,
1201
+ };
1202
+ const stuff = Object.create(null);
1203
+ setIfNotEmpty(stuff, "objects", objectPlansProperties, [
1204
+ schema.getQueryType()?.name,
1205
+ schema.getMutationType()?.name,
1206
+ schema.getSubscriptionType()?.name,
1207
+ ].filter((n) => n != null));
1208
+ setIfNotEmpty(stuff, "interfaces", interfacePlansProperties, true);
1209
+ setIfNotEmpty(stuff, "unions", unionPlansProperties, true);
1210
+ setIfNotEmpty(stuff, "inputObjects", inputObjectPlansProperties, true);
1211
+ setIfNotEmpty(stuff, "scalars", scalarPlansProperties, true);
1212
+ setIfNotEmpty(stuff, "enums", enumPlansProperties, true);
1213
+ const todos = [];
1214
+ for (const [key, props] of Object.entries(stuff)) {
1215
+ const exportName = file.makeVariable(key);
1216
+ // Do this afterwards so all the variables are reserved first.
1217
+ todos.push(() => {
1218
+ const plans = t.exportNamedDeclaration(t.variableDeclaration("const", [
1219
+ t.variableDeclarator(exportName, props),
1220
+ ]));
1221
+ file.addStatements(plans);
1222
+ typeDefsEtc[key] = exportName;
1223
+ });
1224
+ }
1225
+ // Now all variables are declared, we can populate them
1226
+ for (const todo of todos) {
1227
+ todo();
1228
+ }
997
1229
  const makeGrafastSchemaAST = file.import("grafast", "makeGrafastSchema");
998
1230
  const schemaAST = t.callExpression(makeGrafastSchemaAST, [
999
- t.objectExpression(objectToObjectProperties({
1000
- typeDefs: typeDefsExportName,
1001
- plans: plansExportName,
1002
- })),
1231
+ t.objectExpression(objectToObjectProperties(typeDefsEtc)),
1003
1232
  ]);
1004
1233
  file.addStatements(t.exportNamedDeclaration(t.variableDeclaration("const", [
1005
1234
  t.variableDeclarator(schemaExportName, schemaAST),
1006
1235
  ])));
1007
1236
  }
1237
+ const specifiedDirectiveNames = graphql_1.specifiedDirectives.map((d) => d.name);
1008
1238
  async function exportSchemaAsString(schema, options) {
1009
1239
  const config = schema.toConfig();
1010
1240
  const customTypes = config.types.filter((type) => !isBuiltinType(type));
1011
- const customDirectives = config.directives.filter((d) => ![
1012
- "skip",
1013
- "include",
1014
- "deprecated",
1015
- "specifiedBy",
1016
- "defer",
1017
- "stream",
1018
- ].includes(d.name));
1241
+ const customDirectives = config.directives.filter((d) => !specifiedDirectiveNames.includes(d.name));
1242
+ if (config.directives.some((d) => d.name === "defer" || d.name === "skip")) {
1243
+ // Ref: https://github.com/graphql/graphql-js/pull/3450
1244
+ // @ts-ignore
1245
+ config.enableDeferStream = true;
1246
+ }
1019
1247
  const file = new CodegenFile(options);
1020
1248
  const schemaExportDetails = {
1021
1249
  schema,
@@ -1031,16 +1259,79 @@ async function exportSchemaAsString(schema, options) {
1031
1259
  else {
1032
1260
  exportSchemaGraphQLJS(schemaExportDetails);
1033
1261
  }
1262
+ return exportFile(file, options);
1263
+ }
1264
+ function exportFile(file, { disableOptimize }) {
1034
1265
  const ast = file.toAST();
1035
- const optimizedAst = (0, index_js_1.optimize)(ast);
1266
+ const optimizedAst = disableOptimize ? ast : (0, index_js_1.optimize)(ast);
1036
1267
  const { code } = reallyGenerate(optimizedAst, {});
1037
1268
  return { code };
1038
1269
  }
1039
- exports.exportSchemaAsString = exportSchemaAsString;
1040
- async function exportSchema(schema, toPath, options = {}) {
1041
- const { code } = await exportSchemaAsString(schema, options);
1042
- const HEADER = `/* eslint-disable graphile-export/export-instances, graphile-export/export-methods, graphile-export/exhaustive-deps */\n`;
1043
- const toFormat = HEADER + code;
1270
+ async function exportValueAsString(name, value, options) {
1271
+ const file = new CodegenFile(options);
1272
+ const exportName = file.makeVariable(name);
1273
+ const valueAST = convertToIdentifierViaAST(file, value, name, name);
1274
+ file.addStatements(t.exportNamedDeclaration(t.variableDeclaration("const", [
1275
+ t.variableDeclarator(exportName, valueAST),
1276
+ ])));
1277
+ return exportFile(file, options);
1278
+ }
1279
+ async function loadESLint() {
1280
+ try {
1281
+ return await import("eslint");
1282
+ }
1283
+ catch (e) {
1284
+ return null;
1285
+ }
1286
+ }
1287
+ async function lint(code, rawFilePath) {
1288
+ const eslintModule = await loadESLint();
1289
+ if (eslintModule == null) {
1290
+ console.warn(`graphile-export could not find 'eslint' so disabling additional checks`);
1291
+ return;
1292
+ }
1293
+ const filePath = typeof rawFilePath === "string" ? rawFilePath : rawFilePath.pathname;
1294
+ const { ESLint } = eslintModule;
1295
+ const eslint = new ESLint({
1296
+ overrideConfigFile: true, // Don't use external config
1297
+ allowInlineConfig: false, // Ignore `/* eslint-disable ... */` comments
1298
+ overrideConfig: {
1299
+ linterOptions: { reportUnusedDisableDirectives: false },
1300
+ languageOptions: {
1301
+ ecmaVersion: 2022,
1302
+ sourceType: "module",
1303
+ },
1304
+ rules: {
1305
+ "no-use-before-define": [
1306
+ "error",
1307
+ {
1308
+ functions: false,
1309
+ classes: false,
1310
+ // We often have cyclic dependencies between types, this is handled via callbacks, so we don't care about that.
1311
+ variables: false,
1312
+ allowNamedExports: false,
1313
+ },
1314
+ ],
1315
+ },
1316
+ },
1317
+ });
1318
+ const results = await eslint.lintText(code, {
1319
+ warnIgnored: true,
1320
+ // DO NOT PASS THE `filePath`; it can result in the file being ignored!
1321
+ });
1322
+ if (results.length !== 1) {
1323
+ console.dir({ filePath, results });
1324
+ throw new Error(`Expected ESLint results to have exactly one entry`);
1325
+ }
1326
+ const [result] = results;
1327
+ if (result.warningCount > 0 || result.errorCount > 0) {
1328
+ console.log(`ESLint found problems in the export; this likely indicates some issue with \`EXPORTABLE\` calls`);
1329
+ const formatter = await eslint.loadFormatter("stylish");
1330
+ const output = formatter.format(results);
1331
+ console.log(output);
1332
+ }
1333
+ }
1334
+ async function format(toFormat, toPath, options) {
1044
1335
  if (options.prettier) {
1045
1336
  const prettier = await import("prettier");
1046
1337
  const config = await prettier.resolveConfig(toPath.toString());
@@ -1048,11 +1339,65 @@ async function exportSchema(schema, toPath, options = {}) {
1048
1339
  parser: "babel",
1049
1340
  ...(config ?? {}),
1050
1341
  });
1051
- await (0, promises_1.writeFile)(toPath, formatted);
1342
+ return formatted;
1052
1343
  }
1053
1344
  else {
1054
- await (0, promises_1.writeFile)(toPath, toFormat);
1345
+ return toFormat;
1346
+ }
1347
+ }
1348
+ const HEADER = `/* eslint-disable graphile-export/export-instances, graphile-export/export-methods, graphile-export/exhaustive-deps */\n`;
1349
+ async function exportSchema(schema, toPath, options = {}) {
1350
+ const { code } = await exportSchemaAsString(schema, options);
1351
+ const toFormat = HEADER + code;
1352
+ const formatted = await format(toFormat, toPath, options);
1353
+ await (0, promises_1.writeFile)(toPath, formatted);
1354
+ await lint(formatted, toPath);
1355
+ }
1356
+ /**
1357
+ * Returns `false` for nullish values and empty objects, true otherwise.
1358
+ */
1359
+ function isNotEmpty(value) {
1360
+ if (value == null)
1361
+ return false;
1362
+ if (typeof value !== "object")
1363
+ return true;
1364
+ const proto = Object.getPrototypeOf(value);
1365
+ if (proto !== null && proto !== Object.prototype)
1366
+ return true;
1367
+ if (Object.getOwnPropertyNames(value).length === 0 &&
1368
+ Object.getOwnPropertySymbols(value).length === 0) {
1369
+ // Empty object!
1370
+ return false;
1371
+ }
1372
+ return true;
1373
+ }
1374
+ function setIfNotEmpty(target, key, value, sort) {
1375
+ if (Object.keys(value).length > 0) {
1376
+ const entries = Object.entries(value);
1377
+ if (typeof sort === "boolean") {
1378
+ entries.sort((a, z) => a[0].localeCompare(z[0], "und"));
1379
+ }
1380
+ else if (sort) {
1381
+ entries.sort((a, z) => {
1382
+ const ka = a[0];
1383
+ const kz = z[0];
1384
+ // First, compare keys against the specific defined order
1385
+ const sa = sort.indexOf(ka);
1386
+ const sz = sort.indexOf(kz);
1387
+ if (sa >= 0) {
1388
+ if (sz < 0)
1389
+ return -1;
1390
+ return sa - sz;
1391
+ }
1392
+ else if (sz >= 0) {
1393
+ return 1;
1394
+ }
1395
+ // Failing that, compare them alphabetically
1396
+ return ka.localeCompare(kz, "und");
1397
+ });
1398
+ }
1399
+ const finalProps = entries.map(([k, v]) => t.objectProperty(identifierOrLiteral(k), v));
1400
+ target[key] = t.objectExpression(finalProps);
1055
1401
  }
1056
1402
  }
1057
- exports.exportSchema = exportSchema;
1058
1403
  //# sourceMappingURL=exportSchema.js.map