1 | #!/usr/bin/env node
|
2 |
|
3 | import 'apollo-codegen-core/lib/polyfills';
|
4 |
|
5 | import * as glob from 'glob';
|
6 | import * as process from 'process';
|
7 | import * as path from 'path';
|
8 | import * as yargs from 'yargs';
|
9 |
|
10 | import downloadSchema from './downloadSchema';
|
11 | import introspectSchema from './introspectSchema';
|
12 | import printSchema from './printSchema';
|
13 | import { ToolError, logError } from 'apollo-codegen-core/lib/errors';
|
14 |
|
15 | import generate from './generate';
|
16 |
|
17 | import 'source-map-support/register';
|
18 |
|
19 |
|
20 | process.on('unhandledRejection', (error) => { throw error });
|
21 |
|
22 | process.on('uncaughtException', handleError);
|
23 |
|
24 | function handleError(error: Error) {
|
25 | logError(error);
|
26 | process.exit(1);
|
27 | }
|
28 |
|
29 | yargs
|
30 | .command(
|
31 | ['introspect-schema <schema>', 'download-schema'],
|
32 | 'Generate an introspection JSON from a local GraphQL file or from a remote GraphQL server',
|
33 | {
|
34 | output: {
|
35 | demand: true,
|
36 | describe: 'Output path for GraphQL schema file',
|
37 | default: 'schema.json',
|
38 | normalize: true,
|
39 | coerce: path.resolve,
|
40 | },
|
41 | header: {
|
42 | alias: 'H',
|
43 | describe: 'Additional header to send to the server as part of the introspection query request',
|
44 | type: 'array',
|
45 | coerce: (arg) => {
|
46 | let additionalHeaders: {
|
47 | [key: string]: any
|
48 | } = {};
|
49 | for (const header of arg) {
|
50 | const separator = header.indexOf(":");
|
51 | const name = header.substring(0, separator).trim();
|
52 | const value = header.substring(separator + 1).trim();
|
53 | if (!(name && value)) {
|
54 | throw new ToolError('Headers should be specified as "Name: Value"');
|
55 | }
|
56 | additionalHeaders[name] = value;
|
57 | }
|
58 | return additionalHeaders;
|
59 | }
|
60 | },
|
61 | insecure: {
|
62 | alias: 'K',
|
63 | describe: 'Allows "insecure" SSL connection to the server',
|
64 | type: 'boolean'
|
65 | },
|
66 | method: {
|
67 | demand: false,
|
68 | describe: 'The HTTP request method to use for the introspection query request',
|
69 | type: 'string',
|
70 | default: 'POST',
|
71 | choices: ['POST', 'GET', 'post', 'get']
|
72 | }
|
73 | },
|
74 | async argv => {
|
75 | const { schema, output, header, insecure, method } = argv;
|
76 |
|
77 | const urlRegex = /^https?:\/\//i;
|
78 | if (urlRegex.test(schema)) {
|
79 | await downloadSchema(schema, output, header, insecure, method);
|
80 | } else {
|
81 | await introspectSchema(schema, output);
|
82 | }
|
83 | }
|
84 | )
|
85 | .command(
|
86 | ['print-schema [schema]'],
|
87 | 'Print the provided schema in the GraphQL schema language format',
|
88 | {
|
89 | schema: {
|
90 | demand: true,
|
91 | describe: 'Path to GraphQL introspection query result',
|
92 | default: 'schema.json',
|
93 | normalize: true,
|
94 | coerce: path.resolve,
|
95 | },
|
96 | output: {
|
97 | demand: true,
|
98 | describe: 'Output path for GraphQL schema language file',
|
99 | default: 'schema.graphql',
|
100 | normalize: true,
|
101 | coerce: path.resolve,
|
102 | }
|
103 | },
|
104 | async argv => {
|
105 | const { schema, output } = argv;
|
106 | await printSchema(schema, output);
|
107 | }
|
108 | )
|
109 | .command(
|
110 | 'generate [input...]',
|
111 | 'Generate code from a GraphQL schema and query documents',
|
112 | {
|
113 | schema: {
|
114 | demand: false,
|
115 | describe: 'Path to GraphQL schema file. (Defaults to using .graphqlconfig or schema.json)',
|
116 | normalize: true,
|
117 | coerce: path.resolve,
|
118 | },
|
119 | output: {
|
120 | demand: true,
|
121 | describe: 'Output directory for the generated files',
|
122 | normalize: true,
|
123 | coerce: path.resolve,
|
124 | },
|
125 | target: {
|
126 | demand: false,
|
127 | describe: 'Code generation target language',
|
128 | choices: ['swift', 'scala', 'json', 'ts-legacy', 'ts', 'typescript-legacy', 'typescript', 'flow-legacy', 'flow'],
|
129 | default: 'swift'
|
130 | },
|
131 | only: {
|
132 | describe: 'Parse all input files, but only output generated code for the specified file [Swift only]',
|
133 | normalize: true,
|
134 | coerce: path.resolve,
|
135 | },
|
136 | namespace: {
|
137 | demand: false,
|
138 | describe: 'Optional namespace for generated types [currently Swift and Scala-only]',
|
139 | type: 'string'
|
140 | },
|
141 | "passthrough-custom-scalars": {
|
142 | demand: false,
|
143 | describe: "Use the names of custom scalars as their type name [temporary option]",
|
144 | default: false
|
145 | },
|
146 | "custom-scalars-prefix": {
|
147 | demand: false,
|
148 | describe: "Prefix for custom scalars. (Implies that passthrough-custom-scalars is true if set)",
|
149 | default: '',
|
150 | normalize: true
|
151 | },
|
152 | "add-typename": {
|
153 | demand: false,
|
154 | describe: "For non-swift targets, always add the __typename GraphQL introspection type when generating target types",
|
155 | default: false
|
156 | },
|
157 | "use-flow-exact-objects": {
|
158 | demand: false,
|
159 | describe: "Use Flow exact objects for generated types [flow only]",
|
160 | default: false,
|
161 | type: 'boolean'
|
162 | },
|
163 | "use-flow-read-only-types": {
|
164 | demand: false,
|
165 | describe: "Use Flow read only types for generated types [flow only]",
|
166 | default: false,
|
167 | type: 'boolean'
|
168 | },
|
169 | "tag-name": {
|
170 | demand: false,
|
171 | describe: "Name of the template literal tag used to identify template literals containing GraphQL queries in Javascript/Typescript code",
|
172 | default: 'gql'
|
173 | },
|
174 | "project-name": {
|
175 | demand: false,
|
176 | describe: "Name of the project to use in a multi-project .graphqlconfig file",
|
177 | },
|
178 | "operation-ids-path": {
|
179 | demand: false,
|
180 | describe: "Path to an operation id JSON map file. If specified, also stores the operation ids (hashes) as properties on operation types [currently Swift-only]",
|
181 | default: null,
|
182 | normalize: true
|
183 | },
|
184 | "merge-in-fields-from-fragment-spreads": {
|
185 | demand: false,
|
186 | describe: "Merge fragment fields onto its enclosing type",
|
187 | default: true,
|
188 | type: 'boolean'
|
189 | }
|
190 | },
|
191 | argv => {
|
192 | let { input } = argv;
|
193 |
|
194 |
|
195 | if (input.length === 1 && glob.hasMagic(input[0])) {
|
196 | input = glob.sync(input[0]);
|
197 | }
|
198 |
|
199 | const inputPaths = (input as string[])
|
200 | .map(input => path.resolve(input))
|
201 |
|
202 | .sort();
|
203 |
|
204 | const options = {
|
205 | passthroughCustomScalars: argv["passthrough-custom-scalars"] || argv["custom-scalars-prefix"] !== '',
|
206 | customScalarsPrefix: argv["custom-scalars-prefix"] || '',
|
207 | addTypename: argv["add-typename"],
|
208 | namespace: argv.namespace,
|
209 | operationIdsPath: argv["operation-ids-path"],
|
210 | generateOperationIds: !!argv["operation-ids-path"],
|
211 | mergeInFieldsFromFragmentSpreads: argv["merge-in-fields-from-fragment-spreads"],
|
212 | useFlowExactObjects: argv['use-flow-exact-objects'],
|
213 | useFlowReadOnlyTypes: argv['use-flow-read-only-types'],
|
214 | };
|
215 |
|
216 | generate(inputPaths, argv.schema, argv.output, argv.only, argv.target, argv.tagName, argv.projectName, options);
|
217 | },
|
218 | )
|
219 | .fail(function(message, error) {
|
220 | handleError(error ? error : new ToolError(message));
|
221 | })
|
222 | .help()
|
223 | .version()
|
224 | .strict()
|
225 | .argv
|