relay-compiler 7.0.0 → 9.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (242) hide show
  1. package/bin/RelayCompilerBin.js.flow +169 -0
  2. package/bin/RelayCompilerMain.js.flow +508 -0
  3. package/bin/__fixtures__/plugin-module.js.flow +17 -0
  4. package/bin/relay-compiler +8554 -8142
  5. package/codegen/CodegenDirectory.js.flow +375 -0
  6. package/codegen/CodegenRunner.js.flow +431 -0
  7. package/codegen/CodegenTypes.js.flow +28 -0
  8. package/codegen/CodegenWatcher.js.flow +254 -0
  9. package/codegen/NormalizationCodeGenerator.js.flow +499 -0
  10. package/codegen/ReaderCodeGenerator.js.flow +453 -0
  11. package/codegen/RelayCodeGenerator.js.flow +76 -0
  12. package/codegen/RelayFileWriter.js.flow +366 -0
  13. package/codegen/SourceControl.js.flow +58 -0
  14. package/codegen/compileRelayArtifacts.js.flow +182 -0
  15. package/codegen/createPrintRequireModuleDependency.js.flow +21 -0
  16. package/codegen/writeRelayGeneratedFile.js.flow +194 -0
  17. package/core/ASTCache.js.flow +73 -0
  18. package/core/ASTConvert.js.flow +233 -0
  19. package/core/CompilerContext.js.flow +190 -0
  20. package/core/CompilerError.js.flow +250 -0
  21. package/core/DotGraphQLParser.js.flow +39 -0
  22. package/core/GraphQLCompilerProfiler.js.flow +341 -0
  23. package/core/GraphQLDerivedFromMetadata.js.flow +48 -0
  24. package/core/GraphQLWatchmanClient.js.flow +111 -0
  25. package/core/IR.js.flow +329 -0
  26. package/core/IRPrinter.js.flow +488 -0
  27. package/core/IRTransformer.js.flow +377 -0
  28. package/core/IRValidator.js.flow +260 -0
  29. package/core/IRVisitor.js.flow +150 -0
  30. package/core/JSModuleParser.js.flow +24 -0
  31. package/core/RelayCompilerScope.js.flow +199 -0
  32. package/core/RelayFindGraphQLTags.js.flow +119 -0
  33. package/core/RelayGraphQLEnumsGenerator.js.flow +55 -0
  34. package/core/RelayIRTransforms.js.flow +130 -0
  35. package/core/RelayParser.js.flow +1759 -0
  36. package/core/RelaySourceModuleParser.js.flow +135 -0
  37. package/core/Schema.js.flow +1985 -0
  38. package/core/SchemaUtils.js.flow +109 -0
  39. package/core/filterContextForNode.js.flow +50 -0
  40. package/core/getFieldDefinition.js.flow +156 -0
  41. package/core/getIdentifierForArgumentValue.js.flow +49 -0
  42. package/core/getIdentifierForSelection.js.flow +69 -0
  43. package/core/getLiteralArgumentValues.js.flow +32 -0
  44. package/core/getNormalizationOperationName.js.flow +19 -0
  45. package/core/inferRootArgumentDefinitions.js.flow +323 -0
  46. package/index.js +1 -1
  47. package/index.js.flow +202 -0
  48. package/language/RelayLanguagePluginInterface.js.flow +283 -0
  49. package/language/javascript/FindGraphQLTags.js.flow +233 -0
  50. package/language/javascript/RelayFlowBabelFactories.js.flow +180 -0
  51. package/language/javascript/RelayFlowGenerator.js.flow +1040 -0
  52. package/language/javascript/RelayFlowTypeTransformers.js.flow +184 -0
  53. package/language/javascript/RelayLanguagePluginJavaScript.js.flow +34 -0
  54. package/language/javascript/formatGeneratedModule.js.flow +65 -0
  55. package/lib/bin/RelayCompilerBin.js +25 -7
  56. package/lib/bin/RelayCompilerMain.js +134 -125
  57. package/lib/bin/__fixtures__/plugin-module.js +1 -0
  58. package/lib/codegen/CodegenDirectory.js +14 -8
  59. package/lib/codegen/CodegenRunner.js +35 -75
  60. package/lib/codegen/CodegenTypes.js +1 -0
  61. package/lib/codegen/CodegenWatcher.js +14 -21
  62. package/lib/codegen/NormalizationCodeGenerator.js +80 -127
  63. package/lib/codegen/ReaderCodeGenerator.js +85 -111
  64. package/lib/codegen/RelayCodeGenerator.js +9 -6
  65. package/lib/codegen/RelayFileWriter.js +22 -41
  66. package/lib/codegen/SourceControl.js +1 -0
  67. package/lib/codegen/compileRelayArtifacts.js +18 -31
  68. package/lib/codegen/createPrintRequireModuleDependency.js +1 -0
  69. package/lib/codegen/writeRelayGeneratedFile.js +62 -90
  70. package/lib/core/ASTCache.js +4 -4
  71. package/lib/core/ASTConvert.js +1 -0
  72. package/lib/core/{GraphQLCompilerContext.js → CompilerContext.js} +10 -11
  73. package/lib/core/{RelayCompilerError.js → CompilerError.js} +29 -55
  74. package/lib/core/DotGraphQLParser.js +1 -0
  75. package/lib/core/GraphQLCompilerProfiler.js +9 -12
  76. package/lib/core/GraphQLDerivedFromMetadata.js +1 -0
  77. package/lib/core/GraphQLWatchmanClient.js +5 -12
  78. package/lib/core/{GraphQLIR.js → IR.js} +1 -0
  79. package/lib/core/{GraphQLIRPrinter.js → IRPrinter.js} +39 -17
  80. package/lib/core/{GraphQLIRTransformer.js → IRTransformer.js} +21 -16
  81. package/lib/core/{GraphQLIRValidator.js → IRValidator.js} +18 -10
  82. package/lib/core/{GraphQLIRVisitor.js → IRVisitor.js} +1 -2
  83. package/lib/core/JSModuleParser.js +18 -0
  84. package/lib/core/RelayCompilerScope.js +6 -5
  85. package/lib/core/RelayFindGraphQLTags.js +1 -0
  86. package/lib/core/RelayGraphQLEnumsGenerator.js +26 -12
  87. package/lib/core/RelayIRTransforms.js +12 -9
  88. package/lib/core/RelayParser.js +113 -75
  89. package/lib/core/RelaySourceModuleParser.js +4 -3
  90. package/lib/core/Schema.js +808 -317
  91. package/lib/core/SchemaUtils.js +1 -0
  92. package/lib/core/filterContextForNode.js +5 -4
  93. package/lib/core/getFieldDefinition.js +14 -16
  94. package/lib/core/getIdentifierForArgumentValue.js +18 -0
  95. package/lib/core/getIdentifierForSelection.js +4 -5
  96. package/lib/core/getLiteralArgumentValues.js +1 -0
  97. package/lib/core/getNormalizationOperationName.js +1 -0
  98. package/lib/core/inferRootArgumentDefinitions.js +79 -99
  99. package/lib/index.js +69 -19
  100. package/lib/language/RelayLanguagePluginInterface.js +1 -0
  101. package/lib/language/javascript/FindGraphQLTags.js +1 -0
  102. package/lib/language/javascript/RelayFlowBabelFactories.js +15 -0
  103. package/lib/language/javascript/RelayFlowGenerator.js +94 -173
  104. package/lib/language/javascript/RelayFlowTypeTransformers.js +2 -3
  105. package/lib/language/javascript/RelayLanguagePluginJavaScript.js +7 -4
  106. package/lib/language/javascript/formatGeneratedModule.js +14 -4
  107. package/lib/reporters/ConsoleReporter.js +2 -3
  108. package/lib/reporters/MultiReporter.js +2 -3
  109. package/lib/reporters/Reporter.js +1 -0
  110. package/lib/runner/Artifacts.js +327 -0
  111. package/lib/runner/BufferedFilesystem.js +265 -0
  112. package/lib/runner/GraphQLASTNodeGroup.js +260 -0
  113. package/lib/runner/GraphQLASTUtils.js +23 -0
  114. package/lib/runner/GraphQLNodeMap.js +85 -0
  115. package/lib/runner/Sources.js +266 -0
  116. package/lib/runner/StrictMap.js +136 -0
  117. package/lib/runner/compileArtifacts.js +39 -0
  118. package/lib/runner/extractAST.js +77 -0
  119. package/lib/runner/getChangedNodeNames.js +84 -0
  120. package/lib/runner/getSchemaInstance.js +30 -0
  121. package/lib/runner/types.js +12 -0
  122. package/lib/transforms/ApplyFragmentArgumentTransform.js +49 -55
  123. package/lib/transforms/ClientExtensionsTransform.js +11 -17
  124. package/lib/transforms/ConnectionTransform.js +35 -28
  125. package/lib/transforms/DeferStreamTransform.js +26 -74
  126. package/lib/transforms/DisallowIdAsAlias.js +5 -4
  127. package/lib/transforms/DisallowTypenameOnRoot.js +55 -0
  128. package/lib/transforms/FieldHandleTransform.js +8 -3
  129. package/lib/transforms/FilterDirectivesTransform.js +4 -3
  130. package/lib/transforms/FlattenTransform.js +23 -47
  131. package/lib/transforms/GenerateIDFieldTransform.js +9 -4
  132. package/lib/transforms/GenerateTypeNameTransform.js +8 -3
  133. package/lib/transforms/InlineDataFragmentTransform.js +11 -6
  134. package/lib/transforms/InlineFragmentsTransform.js +3 -2
  135. package/lib/transforms/MaskTransform.js +20 -19
  136. package/lib/transforms/MatchTransform.js +113 -34
  137. package/lib/transforms/RefetchableFragmentTransform.js +25 -41
  138. package/lib/transforms/RelayDirectiveTransform.js +13 -4
  139. package/lib/transforms/SkipClientExtensionsTransform.js +11 -2
  140. package/lib/transforms/SkipHandleFieldTransform.js +8 -3
  141. package/lib/transforms/SkipRedundantNodesTransform.js +9 -6
  142. package/lib/transforms/SkipSplitOperationTransform.js +32 -0
  143. package/lib/transforms/SkipUnreachableNodeTransform.js +12 -12
  144. package/lib/transforms/SkipUnusedVariablesTransform.js +19 -17
  145. package/lib/transforms/SplitModuleImportTransform.js +4 -3
  146. package/lib/transforms/TestOperationTransform.js +9 -6
  147. package/lib/transforms/TransformUtils.js +1 -0
  148. package/lib/transforms/ValidateGlobalVariablesTransform.js +20 -31
  149. package/lib/transforms/ValidateRequiredArgumentsTransform.js +17 -20
  150. package/lib/transforms/ValidateServerOnlyDirectivesTransform.js +20 -33
  151. package/lib/transforms/ValidateUnusedVariablesTransform.js +20 -31
  152. package/lib/transforms/query-generators/FetchableQueryGenerator.js +161 -0
  153. package/lib/transforms/query-generators/NodeQueryGenerator.js +9 -3
  154. package/lib/transforms/query-generators/QueryQueryGenerator.js +2 -0
  155. package/lib/transforms/query-generators/ViewerQueryGenerator.js +6 -3
  156. package/lib/transforms/query-generators/index.js +25 -7
  157. package/lib/transforms/query-generators/utils.js +13 -15
  158. package/lib/util/CodeMarker.js +1 -0
  159. package/lib/util/DefaultHandleKey.js +1 -0
  160. package/lib/util/RelayCompilerCache.js +2 -3
  161. package/lib/util/Rollout.js +1 -0
  162. package/lib/util/TimeReporter.js +83 -0
  163. package/lib/util/areEqualOSS.js +1 -0
  164. package/lib/util/dedupeJSONStringify.js +16 -12
  165. package/lib/util/getDefinitionNodeHash.js +22 -0
  166. package/lib/util/getModuleName.js +4 -5
  167. package/lib/util/joinArgumentDefinitions.js +2 -1
  168. package/lib/util/md5.js +17 -0
  169. package/lib/util/murmurHash.js +1 -0
  170. package/lib/util/nullthrowsOSS.js +1 -0
  171. package/lib/util/orList.js +2 -1
  172. package/lib/util/partitionArray.js +1 -0
  173. package/package.json +4 -4
  174. package/relay-compiler.js +4 -4
  175. package/relay-compiler.min.js +4 -4
  176. package/reporters/ConsoleReporter.js.flow +81 -0
  177. package/reporters/MultiReporter.js.flow +43 -0
  178. package/reporters/Reporter.js.flow +19 -0
  179. package/runner/Artifacts.js.flow +219 -0
  180. package/runner/BufferedFilesystem.js.flow +194 -0
  181. package/runner/GraphQLASTNodeGroup.js.flow +176 -0
  182. package/runner/GraphQLASTUtils.js.flow +26 -0
  183. package/runner/GraphQLNodeMap.js.flow +55 -0
  184. package/runner/Sources.js.flow +218 -0
  185. package/runner/StrictMap.js.flow +96 -0
  186. package/runner/compileArtifacts.js.flow +76 -0
  187. package/runner/extractAST.js.flow +100 -0
  188. package/runner/getChangedNodeNames.js.flow +48 -0
  189. package/runner/getSchemaInstance.js.flow +36 -0
  190. package/runner/types.js.flow +37 -0
  191. package/transforms/ApplyFragmentArgumentTransform.js.flow +474 -0
  192. package/transforms/ClientExtensionsTransform.js.flow +220 -0
  193. package/transforms/ConnectionTransform.js.flow +869 -0
  194. package/transforms/DeferStreamTransform.js.flow +258 -0
  195. package/transforms/DisallowIdAsAlias.js.flow +47 -0
  196. package/transforms/DisallowTypenameOnRoot.js.flow +45 -0
  197. package/transforms/FieldHandleTransform.js.flow +80 -0
  198. package/transforms/FilterDirectivesTransform.js.flow +45 -0
  199. package/transforms/FlattenTransform.js.flow +456 -0
  200. package/transforms/GenerateIDFieldTransform.js.flow +134 -0
  201. package/transforms/GenerateTypeNameTransform.js.flow +81 -0
  202. package/transforms/InlineDataFragmentTransform.js.flow +124 -0
  203. package/transforms/InlineFragmentsTransform.js.flow +71 -0
  204. package/transforms/MaskTransform.js.flow +126 -0
  205. package/transforms/MatchTransform.js.flow +583 -0
  206. package/transforms/RefetchableFragmentTransform.js.flow +272 -0
  207. package/transforms/RelayDirectiveTransform.js.flow +99 -0
  208. package/transforms/SkipClientExtensionsTransform.js.flow +54 -0
  209. package/transforms/SkipHandleFieldTransform.js.flow +44 -0
  210. package/transforms/SkipRedundantNodesTransform.js.flow +253 -0
  211. package/transforms/SkipSplitOperationTransform.js.flow +37 -0
  212. package/transforms/SkipUnreachableNodeTransform.js.flow +149 -0
  213. package/transforms/SkipUnusedVariablesTransform.js.flow +59 -0
  214. package/transforms/SplitModuleImportTransform.js.flow +98 -0
  215. package/transforms/TestOperationTransform.js.flow +138 -0
  216. package/transforms/TransformUtils.js.flow +26 -0
  217. package/transforms/ValidateGlobalVariablesTransform.js.flow +81 -0
  218. package/transforms/ValidateRequiredArgumentsTransform.js.flow +127 -0
  219. package/transforms/ValidateServerOnlyDirectivesTransform.js.flow +112 -0
  220. package/transforms/ValidateUnusedVariablesTransform.js.flow +89 -0
  221. package/transforms/query-generators/FetchableQueryGenerator.js.flow +190 -0
  222. package/transforms/query-generators/NodeQueryGenerator.js.flow +206 -0
  223. package/transforms/query-generators/QueryQueryGenerator.js.flow +57 -0
  224. package/transforms/query-generators/ViewerQueryGenerator.js.flow +97 -0
  225. package/transforms/query-generators/index.js.flow +90 -0
  226. package/transforms/query-generators/utils.js.flow +72 -0
  227. package/util/CodeMarker.js.flow +79 -0
  228. package/util/DefaultHandleKey.js.flow +17 -0
  229. package/util/RelayCompilerCache.js.flow +88 -0
  230. package/util/Rollout.js.flow +39 -0
  231. package/util/TimeReporter.js.flow +79 -0
  232. package/util/areEqualOSS.js.flow +123 -0
  233. package/util/dedupeJSONStringify.js.flow +152 -0
  234. package/util/getDefinitionNodeHash.js.flow +25 -0
  235. package/util/getModuleName.js.flow +39 -0
  236. package/util/joinArgumentDefinitions.js.flow +99 -0
  237. package/util/md5.js.flow +22 -0
  238. package/util/murmurHash.js.flow +94 -0
  239. package/util/nullthrowsOSS.js.flow +25 -0
  240. package/util/orList.js.flow +37 -0
  241. package/util/partitionArray.js.flow +37 -0
  242. package/lib/transforms/ConnectionFieldTransform.js +0 -275
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow
8
+ * @format
9
+ */
10
+
11
+ // flowlint ambiguous-object-type:error
12
+
13
+ 'use strict';
14
+
15
+ const _yargs = require('yargs');
16
+
17
+ const {main} = require('./RelayCompilerMain');
18
+
19
+ import type {Config} from './RelayCompilerMain';
20
+
21
+ let RelayConfig;
22
+ try {
23
+ // eslint-disable-next-line no-eval
24
+ RelayConfig = eval('require')('relay-config');
25
+ // eslint-disable-next-line lint/no-unused-catch-bindings
26
+ } catch (_) {}
27
+
28
+ const options = {
29
+ schema: {
30
+ describe: 'Path to schema.graphql or schema.json',
31
+ demandOption: true,
32
+ type: 'string',
33
+ array: false,
34
+ },
35
+ src: {
36
+ describe: 'Root directory of application code',
37
+ demandOption: true,
38
+ type: 'string',
39
+ array: false,
40
+ },
41
+ include: {
42
+ describe: 'Directories to include under src',
43
+ type: 'string',
44
+ array: true,
45
+ default: ['**'],
46
+ },
47
+ exclude: {
48
+ describe: 'Directories to ignore under src',
49
+ type: 'string',
50
+ array: true,
51
+ default: ['**/node_modules/**', '**/__mocks__/**', '**/__generated__/**'],
52
+ },
53
+ extensions: {
54
+ array: true,
55
+ describe:
56
+ 'File extensions to compile (defaults to extensions provided by the ' +
57
+ 'language plugin)',
58
+ type: 'string',
59
+ },
60
+ verbose: {
61
+ describe: 'More verbose logging',
62
+ type: 'boolean',
63
+ default: false,
64
+ },
65
+ quiet: {
66
+ describe: 'No output to stdout',
67
+ type: 'boolean',
68
+ default: false,
69
+ },
70
+ watchman: {
71
+ describe: 'Use watchman when not in watch mode',
72
+ type: 'boolean',
73
+ default: true,
74
+ },
75
+ watch: {
76
+ describe: 'If specified, watches files and regenerates on changes',
77
+ type: 'boolean',
78
+ default: false,
79
+ },
80
+ validate: {
81
+ describe:
82
+ 'Looks for pending changes and exits with non-zero code instead of ' +
83
+ 'writing to disk',
84
+ type: 'boolean',
85
+ default: false,
86
+ },
87
+ persistFunction: {
88
+ describe:
89
+ 'An async function (or path to a module exporting this function) which will persist the query text and return the id.',
90
+ demandOption: false,
91
+ type: 'string',
92
+ array: false,
93
+ },
94
+ persistOutput: {
95
+ describe:
96
+ 'A path to a .json file where persisted query metadata should be saved. Will use the default implementation (md5 hash) if `persistFunction` is not passed.',
97
+ demandOption: false,
98
+ type: 'string',
99
+ array: false,
100
+ },
101
+ repersist: {
102
+ describe: 'Run the persist function even if the query has not changed.',
103
+ type: 'boolean',
104
+ default: false,
105
+ },
106
+ noFutureProofEnums: {
107
+ describe:
108
+ 'This option controls whether or not a catch-all entry is added to enum type definitions ' +
109
+ 'for values that may be added in the future. Enabling this means you will have to update ' +
110
+ 'your application whenever the GraphQL server schema adds new enum values to prevent it ' +
111
+ 'from breaking.',
112
+ type: 'boolean',
113
+ default: false,
114
+ },
115
+ language: {
116
+ describe:
117
+ 'The name of the language plugin used for input files and artifacts',
118
+ demandOption: false,
119
+ type: 'string',
120
+ array: false,
121
+ default: 'javascript',
122
+ },
123
+ artifactDirectory: {
124
+ describe:
125
+ 'A specific directory to output all artifacts to. When enabling this ' +
126
+ 'the babel plugin needs `artifactDirectory` set as well.',
127
+ demandOption: false,
128
+ type: 'string',
129
+ array: false,
130
+ },
131
+ customScalars: {
132
+ describe:
133
+ 'Mappings from custom scalars in your schema to built-in GraphQL ' +
134
+ 'types, for type emission purposes. (Uses yargs dot-notation, e.g. ' +
135
+ '--customScalars.URL=String)',
136
+ type: ('object': $FlowFixMe),
137
+ },
138
+ eagerESModules: {
139
+ describe: 'This option enables emitting es modules artifacts.',
140
+ type: 'boolean',
141
+ default: false,
142
+ },
143
+ };
144
+
145
+ // Parse CLI args
146
+ let yargs = _yargs
147
+ .usage(
148
+ 'Create Relay generated files\n\n' +
149
+ '$0 --schema <path> --src <path> [--watch]',
150
+ )
151
+ .options(options)
152
+ .strict();
153
+
154
+ // Load external config
155
+ const config = RelayConfig && RelayConfig.loadConfig();
156
+ if (config) {
157
+ // Apply externally loaded config through the yargs API so that we can leverage yargs' defaults and have them show up
158
+ // in the help banner. We add it conditionally otherwise yargs would add new option `--config` which is confusing for
159
+ // Relay users (it's not Relay Config file).
160
+ yargs = yargs.config(config);
161
+ }
162
+
163
+ const argv: Config = (yargs.help().argv: $FlowFixMe);
164
+
165
+ // Start the application
166
+ main(argv).catch(error => {
167
+ console.error(String(error.stack || error));
168
+ process.exit(1);
169
+ });
@@ -0,0 +1,508 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow
8
+ * @format
9
+ */
10
+
11
+ // flowlint ambiguous-object-type:error
12
+
13
+ 'use strict';
14
+
15
+ const CodegenRunner = require('../codegen/CodegenRunner');
16
+ const ConsoleReporter = require('../reporters/ConsoleReporter');
17
+ const DotGraphQLParser = require('../core/DotGraphQLParser');
18
+ const RelayFileWriter = require('../codegen/RelayFileWriter');
19
+ const RelayIRTransforms = require('../core/RelayIRTransforms');
20
+ const RelayLanguagePluginJavaScript = require('../language/javascript/RelayLanguagePluginJavaScript');
21
+ const RelaySourceModuleParser = require('../core/RelaySourceModuleParser');
22
+ const WatchmanClient = require('../core/GraphQLWatchmanClient');
23
+
24
+ const crypto = require('crypto');
25
+ const fs = require('fs');
26
+ const invariant = require('invariant');
27
+ const path = require('path');
28
+
29
+ const {buildClientSchema, Source, printSchema} = require('graphql');
30
+
31
+ const {
32
+ commonTransforms,
33
+ codegenTransforms,
34
+ fragmentTransforms,
35
+ printTransforms,
36
+ queryTransforms,
37
+ schemaExtensions: relaySchemaExtensions,
38
+ } = RelayIRTransforms;
39
+
40
+ import type {ScalarTypeMapping} from '../language/javascript/RelayFlowTypeTransformers';
41
+ import type {WriteFilesOptions} from '../codegen/CodegenRunner';
42
+ import type {
43
+ PluginInitializer,
44
+ PluginInterface,
45
+ } from '../language/RelayLanguagePluginInterface';
46
+
47
+ export type Config = {|
48
+ schema: string,
49
+ src: string,
50
+ extensions: Array<string>,
51
+ include: Array<string>,
52
+ exclude: Array<string>,
53
+ verbose: boolean,
54
+ watchman: boolean,
55
+ watch?: ?boolean,
56
+ validate: boolean,
57
+ quiet: boolean,
58
+ persistOutput?: ?string,
59
+ noFutureProofEnums: boolean,
60
+ eagerESModules?: boolean,
61
+ language: string | PluginInitializer,
62
+ persistFunction?: ?string | ?((text: string) => Promise<string>),
63
+ repersist: boolean,
64
+ artifactDirectory?: ?string,
65
+ customScalars?: ScalarTypeMapping,
66
+ |};
67
+
68
+ function buildWatchExpression(config: {
69
+ extensions: Array<string>,
70
+ include: Array<string>,
71
+ exclude: Array<string>,
72
+ ...
73
+ }) {
74
+ return [
75
+ 'allof',
76
+ ['type', 'f'],
77
+ ['anyof', ...config.extensions.map(ext => ['suffix', ext])],
78
+ [
79
+ 'anyof',
80
+ ...config.include.map(include => ['match', include, 'wholename']),
81
+ ],
82
+ ...config.exclude.map(exclude => ['not', ['match', exclude, 'wholename']]),
83
+ ];
84
+ }
85
+
86
+ function getFilepathsFromGlob(
87
+ baseDir,
88
+ config: {
89
+ extensions: Array<string>,
90
+ include: Array<string>,
91
+ exclude: Array<string>,
92
+ ...
93
+ },
94
+ ): Array<string> {
95
+ const {extensions, include, exclude} = config;
96
+ const patterns = include.map(inc => `${inc}/*.+(${extensions.join('|')})`);
97
+ const glob = require('fast-glob');
98
+ return glob.sync(patterns, {
99
+ cwd: baseDir,
100
+ ignore: exclude,
101
+ });
102
+ }
103
+
104
+ type LanguagePlugin = PluginInitializer | {default: PluginInitializer, ...};
105
+
106
+ /**
107
+ * Unless the requested plugin is the builtin `javascript` one, import a
108
+ * language plugin as either a CommonJS or ES2015 module.
109
+ *
110
+ * When importing, first check if it’s a path to an existing file, otherwise
111
+ * assume it’s a package and prepend the plugin namespace prefix.
112
+ *
113
+ * Make sure to always use Node's `require` function, which otherwise would get
114
+ * replaced with `__webpack_require__` when bundled using webpack, by using
115
+ * `eval` to get it at runtime.
116
+ */
117
+ function getLanguagePlugin(
118
+ language: string | PluginInitializer,
119
+ options?: {|
120
+ eagerESModules: boolean,
121
+ |},
122
+ ): PluginInterface {
123
+ if (language === 'javascript') {
124
+ return RelayLanguagePluginJavaScript({
125
+ eagerESModules: Boolean(options && options.eagerESModules),
126
+ });
127
+ } else {
128
+ let languagePlugin: LanguagePlugin;
129
+ if (typeof language === 'string') {
130
+ const pluginPath = path.resolve(process.cwd(), language);
131
+ const requirePath = fs.existsSync(pluginPath)
132
+ ? pluginPath
133
+ : `relay-compiler-language-${language}`;
134
+ try {
135
+ // eslint-disable-next-line no-eval
136
+ languagePlugin = eval('require')(requirePath);
137
+ if (languagePlugin.default) {
138
+ languagePlugin = languagePlugin.default;
139
+ }
140
+ } catch (err) {
141
+ const e = new Error(
142
+ `Unable to load language plugin ${requirePath}: ${err.message}`,
143
+ );
144
+ e.stack = err.stack;
145
+ throw e;
146
+ }
147
+ } else {
148
+ languagePlugin = language;
149
+ }
150
+ if (languagePlugin.default != null) {
151
+ // $FlowFixMe - Flow no longer considers statics of functions as any
152
+ languagePlugin = languagePlugin.default;
153
+ }
154
+ if (typeof languagePlugin === 'function') {
155
+ // $FlowFixMe
156
+ return languagePlugin();
157
+ } else {
158
+ throw new Error('Expected plugin to be a initializer function.');
159
+ }
160
+ }
161
+ }
162
+
163
+ function getPersistQueryFunction(
164
+ config: Config,
165
+ ): ?(text: string) => Promise<string> {
166
+ const configValue = config.persistFunction;
167
+ if (configValue == null) {
168
+ return null;
169
+ } else if (typeof configValue === 'string') {
170
+ try {
171
+ // eslint-disable-next-line no-eval
172
+ const persistFunction = eval('require')(
173
+ path.resolve(process.cwd(), configValue),
174
+ );
175
+ if (persistFunction.default) {
176
+ return persistFunction.default;
177
+ }
178
+ return persistFunction;
179
+ } catch (err) {
180
+ const e = new Error(
181
+ `Unable to load persistFunction ${configValue}: ${err.message}`,
182
+ );
183
+ e.stack = err.stack;
184
+ throw e;
185
+ }
186
+ } else if (typeof configValue === 'function') {
187
+ return configValue;
188
+ } else {
189
+ throw new Error(
190
+ 'Expected persistFunction to be a path string or a function.',
191
+ );
192
+ }
193
+ }
194
+
195
+ async function main(defaultConfig: Config) {
196
+ if (defaultConfig.verbose && defaultConfig.quiet) {
197
+ throw new Error("I can't be quiet and verbose at the same time");
198
+ }
199
+
200
+ let config = getPathBasedConfig(defaultConfig);
201
+ config = await getWatchConfig(config);
202
+
203
+ // Use function from module.exports to be able to mock it for tests
204
+ const codegenRunner = module.exports.getCodegenRunner(config);
205
+
206
+ const result = config.watch
207
+ ? await codegenRunner.watchAll()
208
+ : await codegenRunner.compileAll();
209
+
210
+ if (result === 'ERROR') {
211
+ process.exit(100);
212
+ }
213
+ if (config.validate && result !== 'NO_CHANGES') {
214
+ process.exit(101);
215
+ }
216
+ }
217
+
218
+ function getPathBasedConfig(config: Config) {
219
+ const schema = path.resolve(process.cwd(), config.schema);
220
+ if (!fs.existsSync(schema)) {
221
+ throw new Error(`--schema path does not exist: ${schema}`);
222
+ }
223
+
224
+ const src = path.resolve(process.cwd(), config.src);
225
+ if (!fs.existsSync(src)) {
226
+ throw new Error(`--src path does not exist: ${src}`);
227
+ }
228
+
229
+ let persistOutput = config.persistOutput;
230
+ if (typeof persistOutput === 'string') {
231
+ persistOutput = path.resolve(process.cwd(), persistOutput);
232
+ const persistOutputDir = path.dirname(persistOutput);
233
+ if (!fs.existsSync(persistOutputDir)) {
234
+ throw new Error(`--persistOutput path does not exist: ${persistOutput}`);
235
+ }
236
+ }
237
+
238
+ return {...config, schema, src, persistOutput};
239
+ }
240
+
241
+ async function getWatchConfig(config: Config): Promise<Config> {
242
+ const watchman = config.watchman && (await WatchmanClient.isAvailable());
243
+
244
+ if (config.watch) {
245
+ if (!watchman) {
246
+ console.error(
247
+ 'Watchman is required to watch for changes. Running with watch mode disabled.',
248
+ );
249
+ return {...config, watch: false, watchman: false};
250
+ }
251
+ if (!module.exports.hasWatchmanRootFile(config.src)) {
252
+ throw new Error(
253
+ `
254
+ --watch requires that the src directory have a valid watchman "root" file.
255
+
256
+ Root files can include:
257
+ - A .git/ Git folder
258
+ - A .hg/ Mercurial folder
259
+ - A .watchmanconfig file
260
+
261
+ Ensure that one such file exists in ${config.src} or its parents.
262
+ `.trim(),
263
+ );
264
+ }
265
+ } else if (watchman && !config.validate) {
266
+ // eslint-disable-next-line no-console
267
+ console.log('HINT: pass --watch to keep watching for changes.');
268
+ }
269
+
270
+ return {...config, watchman};
271
+ }
272
+
273
+ function getCodegenRunner(config: Config): CodegenRunner {
274
+ const reporter = new ConsoleReporter({
275
+ verbose: config.verbose,
276
+ quiet: config.quiet,
277
+ });
278
+ const schema = getSchemaSource(config.schema);
279
+ const languagePlugin = getLanguagePlugin(config.language, {
280
+ eagerESModules: config.eagerESModules === true,
281
+ });
282
+ const persistQueryFunction = getPersistQueryFunction(config);
283
+ const inputExtensions = config.extensions || languagePlugin.inputExtensions;
284
+ const outputExtension = languagePlugin.outputExtension;
285
+ const sourceParserName = inputExtensions.join('/');
286
+ const sourceWriterName = outputExtension;
287
+ const sourceModuleParser = RelaySourceModuleParser(
288
+ languagePlugin.findGraphQLTags,
289
+ languagePlugin.getFileFilter,
290
+ );
291
+ const providedArtifactDirectory = config.artifactDirectory;
292
+ const artifactDirectory =
293
+ providedArtifactDirectory != null
294
+ ? path.resolve(process.cwd(), providedArtifactDirectory)
295
+ : null;
296
+ const generatedDirectoryName = artifactDirectory ?? '__generated__';
297
+ const sourceSearchOptions = {
298
+ extensions: inputExtensions,
299
+ include: config.include,
300
+ exclude: ['**/*.graphql.*', ...config.exclude],
301
+ };
302
+ const graphqlSearchOptions = {
303
+ extensions: ['graphql'],
304
+ include: config.include,
305
+ exclude: [path.relative(config.src, config.schema)].concat(config.exclude),
306
+ };
307
+ const defaultIsGeneratedFile = (filePath: string) =>
308
+ filePath.endsWith('.graphql.' + outputExtension) &&
309
+ filePath.includes(generatedDirectoryName);
310
+ const schemaExtensions = languagePlugin.schemaExtensions
311
+ ? [...languagePlugin.schemaExtensions, ...relaySchemaExtensions]
312
+ : relaySchemaExtensions;
313
+ const parserConfigs = {
314
+ [sourceParserName]: {
315
+ baseDir: config.src,
316
+ getFileFilter: sourceModuleParser.getFileFilter,
317
+ getParser: sourceModuleParser.getParser,
318
+ getSchemaSource: () => schema,
319
+ schemaExtensions,
320
+ watchmanExpression: config.watchman
321
+ ? buildWatchExpression(sourceSearchOptions)
322
+ : null,
323
+ filepaths: config.watchman
324
+ ? null
325
+ : getFilepathsFromGlob(config.src, sourceSearchOptions),
326
+ },
327
+ graphql: {
328
+ baseDir: config.src,
329
+ getParser: DotGraphQLParser.getParser,
330
+ getSchemaSource: () => schema,
331
+ schemaExtensions,
332
+ watchmanExpression: config.watchman
333
+ ? buildWatchExpression(graphqlSearchOptions)
334
+ : null,
335
+ filepaths: config.watchman
336
+ ? null
337
+ : getFilepathsFromGlob(config.src, graphqlSearchOptions),
338
+ },
339
+ };
340
+ const writerConfigs = {
341
+ [sourceWriterName]: {
342
+ writeFiles: getRelayFileWriter(
343
+ config.src,
344
+ languagePlugin,
345
+ config.noFutureProofEnums,
346
+ artifactDirectory,
347
+ config.persistOutput,
348
+ config.customScalars,
349
+ persistQueryFunction,
350
+ config.repersist,
351
+ ),
352
+ isGeneratedFile: languagePlugin.isGeneratedFile
353
+ ? languagePlugin.isGeneratedFile
354
+ : defaultIsGeneratedFile,
355
+ parser: sourceParserName,
356
+ baseParsers: ['graphql'],
357
+ },
358
+ };
359
+ const codegenRunner = new CodegenRunner({
360
+ reporter,
361
+ parserConfigs,
362
+ writerConfigs,
363
+ onlyValidate: config.validate,
364
+ // TODO: allow passing in a flag or detect?
365
+ sourceControl: null,
366
+ });
367
+ return codegenRunner;
368
+ }
369
+
370
+ function defaultPersistFunction(text: string): Promise<string> {
371
+ const hasher = crypto.createHash('md5');
372
+ hasher.update(text);
373
+ const id = hasher.digest('hex');
374
+ return Promise.resolve(id);
375
+ }
376
+
377
+ function getRelayFileWriter(
378
+ baseDir: string,
379
+ languagePlugin: PluginInterface,
380
+ noFutureProofEnums: boolean,
381
+ outputDir?: ?string,
382
+ persistedQueryPath?: ?string,
383
+ customScalars?: ScalarTypeMapping,
384
+ persistFunction?: ?(text: string) => Promise<string>,
385
+ repersist?: boolean,
386
+ ) {
387
+ return async ({
388
+ onlyValidate,
389
+ schema,
390
+ documents,
391
+ baseDocuments,
392
+ sourceControl,
393
+ reporter,
394
+ }: WriteFilesOptions) => {
395
+ let persistQuery;
396
+ let queryMap;
397
+ if (persistFunction != null || persistedQueryPath != null) {
398
+ queryMap = new Map();
399
+ const persistImplmentation = persistFunction || defaultPersistFunction;
400
+ persistQuery = async (text: string) => {
401
+ const id = await persistImplmentation(text);
402
+ invariant(
403
+ typeof id === 'string',
404
+ 'Expected persist function to return a string, got `%s`.',
405
+ id,
406
+ );
407
+ queryMap.set(id, text);
408
+ return id;
409
+ };
410
+ }
411
+ const schemaExtensions = languagePlugin.schemaExtensions
412
+ ? [...languagePlugin.schemaExtensions, ...relaySchemaExtensions]
413
+ : relaySchemaExtensions;
414
+ const results = await RelayFileWriter.writeAll({
415
+ config: {
416
+ baseDir,
417
+ compilerTransforms: {
418
+ commonTransforms,
419
+ codegenTransforms,
420
+ fragmentTransforms,
421
+ printTransforms,
422
+ queryTransforms,
423
+ },
424
+ customScalars: customScalars || {},
425
+ formatModule: languagePlugin.formatModule,
426
+ optionalInputFieldsForFlow: [],
427
+ schemaExtensions,
428
+ useHaste: false,
429
+ noFutureProofEnums,
430
+ extension: languagePlugin.outputExtension,
431
+ typeGenerator: languagePlugin.typeGenerator,
432
+ outputDir,
433
+ persistQuery,
434
+ repersist,
435
+ },
436
+ onlyValidate,
437
+ schema,
438
+ baseDocuments,
439
+ documents,
440
+ reporter,
441
+ sourceControl,
442
+ languagePlugin,
443
+ });
444
+ if (queryMap != null && persistedQueryPath != null) {
445
+ let object = {};
446
+ if (fs.existsSync(persistedQueryPath)) {
447
+ try {
448
+ const prevText = fs.readFileSync(persistedQueryPath, 'utf8');
449
+ const prevData = JSON.parse(prevText);
450
+ if (prevData != null && typeof prevData === 'object') {
451
+ object = prevData;
452
+ } else {
453
+ console.error(
454
+ `Invalid data in persisted query file '${persistedQueryPath}', expected an object.`,
455
+ );
456
+ }
457
+ } catch (error) {
458
+ console.error(error);
459
+ }
460
+ }
461
+ for (const [id, text] of queryMap.entries()) {
462
+ object[id] = text;
463
+ }
464
+ const data = JSON.stringify(object, null, 2);
465
+ fs.writeFileSync(persistedQueryPath, data, 'utf8');
466
+ }
467
+ return results;
468
+ };
469
+ }
470
+
471
+ function getSchemaSource(schemaPath: string): Source {
472
+ let source = fs.readFileSync(schemaPath, 'utf8');
473
+ if (path.extname(schemaPath) === '.json') {
474
+ source = printSchema(buildClientSchema(JSON.parse(source).data));
475
+ }
476
+ source = `
477
+ directive @include(if: Boolean) on FRAGMENT_SPREAD | FIELD | INLINE_FRAGMENT
478
+ directive @skip(if: Boolean) on FRAGMENT_SPREAD | FIELD | INLINE_FRAGMENT
479
+
480
+ ${source}
481
+ `;
482
+ return new Source(source, schemaPath);
483
+ }
484
+
485
+ // Ensure that a watchman "root" file exists in the given directory
486
+ // or a parent so that it can be watched
487
+ const WATCHMAN_ROOT_FILES = ['.git', '.hg', '.watchmanconfig'];
488
+ function hasWatchmanRootFile(testPath: string): boolean {
489
+ while (path.dirname(testPath) !== testPath) {
490
+ if (
491
+ WATCHMAN_ROOT_FILES.some(file => {
492
+ return fs.existsSync(path.join(testPath, file));
493
+ })
494
+ ) {
495
+ return true;
496
+ }
497
+ testPath = path.dirname(testPath);
498
+ }
499
+ return false;
500
+ }
501
+
502
+ module.exports = {
503
+ getCodegenRunner,
504
+ getLanguagePlugin,
505
+ getWatchConfig,
506
+ hasWatchmanRootFile,
507
+ main,
508
+ };
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @flow strict-local
8
+ * @format
9
+ * @emails oncall+relay
10
+ */
11
+
12
+ // flowlint ambiguous-object-type:error
13
+
14
+ 'use strict';
15
+ const RelayLanguagePluginJavaScript = require('../../language/javascript/RelayLanguagePluginJavaScript');
16
+
17
+ module.exports = RelayLanguagePluginJavaScript;