UNPKG

13.8 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.shouldEmitLegacyCommonJSImports = exports.ensureContext = exports.CodegenContext = exports.updateContextWithCliFlags = exports.createContext = exports.parseArgv = exports.buildOptions = exports.loadContext = exports.loadCodegenConfig = exports.generateSearchPlaces = void 0;
4const tslib_1 = require("tslib");
5const cosmiconfig_1 = require("cosmiconfig");
6const cosmiconfig_typescript_loader_1 = require("cosmiconfig-typescript-loader");
7const path_1 = require("path");
8const plugin_helpers_1 = require("@graphql-codegen/plugin-helpers");
9const string_env_interpolation_1 = require("string-env-interpolation");
10const yargs_1 = tslib_1.__importDefault(require("yargs"));
11const graphql_config_js_1 = require("./graphql-config.js");
12const load_js_1 = require("./load.js");
13const graphql_1 = require("graphql");
14const yaml_1 = tslib_1.__importDefault(require("yaml"));
15const module_1 = require("module");
16const fs_1 = require("fs");
17const crypto_1 = require("crypto");
18const { lstat } = fs_1.promises;
19function generateSearchPlaces(moduleName) {
20 const extensions = ['json', 'yaml', 'yml', 'js', 'ts', 'config.js'];
21 // gives codegen.json...
22 const regular = extensions.map(ext => `${moduleName}.${ext}`);
23 // gives .codegenrc.json... but no .codegenrc.config.js
24 const dot = extensions.filter(ext => ext !== 'config.js').map(ext => `.${moduleName}rc.${ext}`);
25 return [...regular.concat(dot), 'package.json'];
26}
27exports.generateSearchPlaces = generateSearchPlaces;
28function customLoader(ext) {
29 function loader(filepath, content) {
30 if (typeof process !== 'undefined' && 'env' in process) {
31 content = (0, string_env_interpolation_1.env)(content);
32 }
33 if (ext === 'json') {
34 return cosmiconfig_1.defaultLoaders['.json'](filepath, content);
35 }
36 if (ext === 'yaml') {
37 try {
38 const result = yaml_1.default.parse(content, { prettyErrors: true, merge: true });
39 return result;
40 }
41 catch (error) {
42 error.message = `YAML Error in ${filepath}:\n${error.message}`;
43 throw error;
44 }
45 }
46 if (ext === 'js') {
47 return cosmiconfig_1.defaultLoaders['.js'](filepath, content);
48 }
49 if (ext === 'ts') {
50 return (0, cosmiconfig_typescript_loader_1.TypeScriptLoader)()(filepath, content);
51 }
52 }
53 return loader;
54}
55async function loadCodegenConfig({ configFilePath, moduleName, searchPlaces: additionalSearchPlaces, packageProp, loaders: customLoaders, }) {
56 configFilePath = configFilePath || process.cwd();
57 moduleName = moduleName || 'codegen';
58 packageProp = packageProp || moduleName;
59 const cosmi = (0, cosmiconfig_1.cosmiconfig)(moduleName, {
60 searchPlaces: generateSearchPlaces(moduleName).concat(additionalSearchPlaces || []),
61 packageProp,
62 loaders: {
63 '.json': customLoader('json'),
64 '.yaml': customLoader('yaml'),
65 '.yml': customLoader('yaml'),
66 '.js': customLoader('js'),
67 '.ts': customLoader('ts'),
68 noExt: customLoader('yaml'),
69 ...customLoaders,
70 },
71 });
72 const pathStats = await lstat(configFilePath);
73 return pathStats.isDirectory() ? cosmi.search(configFilePath) : cosmi.load(configFilePath);
74}
75exports.loadCodegenConfig = loadCodegenConfig;
76async function loadContext(configFilePath) {
77 const graphqlConfig = await (0, graphql_config_js_1.findAndLoadGraphQLConfig)(configFilePath);
78 if (graphqlConfig) {
79 return new CodegenContext({
80 graphqlConfig,
81 });
82 }
83 const result = await loadCodegenConfig({ configFilePath });
84 if (!result) {
85 if (configFilePath) {
86 throw new plugin_helpers_1.DetailedError(`Config ${configFilePath} does not exist`, `
87 Config ${configFilePath} does not exist.
88
89 $ graphql-codegen --config ${configFilePath}
90
91 Please make sure the --config points to a correct file.
92 `);
93 }
94 throw new plugin_helpers_1.DetailedError(`Unable to find Codegen config file!`, `
95 Please make sure that you have a configuration file under the current directory!
96 `);
97 }
98 if (result.isEmpty) {
99 throw new plugin_helpers_1.DetailedError(`Found Codegen config file but it was empty!`, `
100 Please make sure that you have a valid configuration file under the current directory!
101 `);
102 }
103 return new CodegenContext({
104 filepath: result.filepath,
105 config: result.config,
106 });
107}
108exports.loadContext = loadContext;
109function getCustomConfigPath(cliFlags) {
110 const configFile = cliFlags.config;
111 return configFile ? (0, path_1.resolve)(process.cwd(), configFile) : null;
112}
113function buildOptions() {
114 return {
115 c: {
116 alias: 'config',
117 type: 'string',
118 describe: 'Path to GraphQL codegen YAML config file, defaults to "codegen.yml" on the current directory',
119 },
120 w: {
121 alias: 'watch',
122 describe: 'Watch for changes and execute generation automatically. You can also specify a glob expression for custom watch list.',
123 coerce: (watch) => {
124 if (watch === 'false') {
125 return false;
126 }
127 if (typeof watch === 'string' || Array.isArray(watch)) {
128 return watch;
129 }
130 return !!watch;
131 },
132 },
133 r: {
134 alias: 'require',
135 describe: 'Loads specific require.extensions before running the codegen and reading the configuration',
136 type: 'array',
137 default: [],
138 },
139 o: {
140 alias: 'overwrite',
141 describe: 'Overwrites existing files',
142 type: 'boolean',
143 },
144 s: {
145 alias: 'silent',
146 describe: 'Suppresses printing errors',
147 type: 'boolean',
148 },
149 e: {
150 alias: 'errors-only',
151 describe: 'Only print errors',
152 type: 'boolean',
153 },
154 profile: {
155 describe: 'Use profiler to measure performance',
156 type: 'boolean',
157 },
158 p: {
159 alias: 'project',
160 describe: 'Name of a project in GraphQL Config',
161 type: 'string',
162 },
163 v: {
164 alias: 'verbose',
165 describe: 'output more detailed information about performed tasks',
166 type: 'boolean',
167 default: false,
168 },
169 d: {
170 alias: 'debug',
171 describe: 'Print debug logs to stdout',
172 type: 'boolean',
173 default: false,
174 },
175 };
176}
177exports.buildOptions = buildOptions;
178function parseArgv(argv = process.argv) {
179 return (0, yargs_1.default)(argv).options(buildOptions()).parse(argv);
180}
181exports.parseArgv = parseArgv;
182async function createContext(cliFlags = parseArgv(process.argv)) {
183 if (cliFlags.require && cliFlags.require.length > 0) {
184 const relativeRequire = (0, module_1.createRequire)(process.cwd());
185 await Promise.all(cliFlags.require.map(mod => Promise.resolve().then(() => tslib_1.__importStar(require(relativeRequire.resolve(mod, {
186 paths: [process.cwd()],
187 }))))));
188 }
189 const customConfigPath = getCustomConfigPath(cliFlags);
190 const context = await loadContext(customConfigPath);
191 updateContextWithCliFlags(context, cliFlags);
192 return context;
193}
194exports.createContext = createContext;
195function updateContextWithCliFlags(context, cliFlags) {
196 const config = {
197 configFilePath: context.filepath,
198 };
199 if (cliFlags.watch !== undefined) {
200 config.watch = cliFlags.watch;
201 }
202 if (cliFlags.overwrite === true) {
203 config.overwrite = cliFlags.overwrite;
204 }
205 if (cliFlags.silent === true) {
206 config.silent = cliFlags.silent;
207 }
208 if (cliFlags.verbose === true || process.env.VERBOSE) {
209 config.verbose = true;
210 }
211 if (cliFlags.debug === true || process.env.DEBUG) {
212 config.debug = true;
213 }
214 if (cliFlags.errorsOnly === true) {
215 config.errorsOnly = cliFlags.errorsOnly;
216 }
217 if (cliFlags['ignore-no-documents'] !== undefined) {
218 // for some reason parsed value is `'false'` string so this ensure it always is a boolean.
219 config.ignoreNoDocuments = cliFlags['ignore-no-documents'] === true;
220 }
221 if (cliFlags['emit-legacy-common-js-imports'] !== undefined) {
222 // for some reason parsed value is `'false'` string so this ensure it always is a boolean.
223 config.emitLegacyCommonJSImports = cliFlags['emit-legacy-common-js-imports'] === true;
224 }
225 if (cliFlags.project) {
226 context.useProject(cliFlags.project);
227 }
228 if (cliFlags.profile === true) {
229 context.useProfiler();
230 }
231 if (cliFlags.check === true) {
232 context.enableCheckMode();
233 }
234 context.updateConfig(config);
235}
236exports.updateContextWithCliFlags = updateContextWithCliFlags;
237class CodegenContext {
238 constructor({ config, graphqlConfig, filepath, }) {
239 this._checkMode = false;
240 this._pluginContext = {};
241 this.checkModeStaleFiles = [];
242 this._config = config;
243 this._graphqlConfig = graphqlConfig;
244 this.filepath = this._graphqlConfig ? this._graphqlConfig.filepath : filepath;
245 this.cwd = this._graphqlConfig ? this._graphqlConfig.dirpath : process.cwd();
246 this.profiler = (0, plugin_helpers_1.createNoopProfiler)();
247 }
248 useProject(name) {
249 this._project = name;
250 }
251 getConfig(extraConfig) {
252 if (!this.config) {
253 if (this._graphqlConfig) {
254 const project = this._graphqlConfig.getProject(this._project);
255 this.config = {
256 ...project.extension('codegen'),
257 schema: project.schema,
258 documents: project.documents,
259 pluginContext: this._pluginContext,
260 };
261 }
262 else {
263 this.config = { ...this._config, pluginContext: this._pluginContext };
264 }
265 }
266 return {
267 ...extraConfig,
268 ...this.config,
269 };
270 }
271 updateConfig(config) {
272 this.config = {
273 ...this.getConfig(),
274 ...config,
275 };
276 }
277 enableCheckMode() {
278 this._checkMode = true;
279 }
280 get checkMode() {
281 return this._checkMode;
282 }
283 useProfiler() {
284 this.profiler = (0, plugin_helpers_1.createProfiler)();
285 const now = new Date(); // 2011-10-05T14:48:00.000Z
286 const datetime = now.toISOString().split('.')[0]; // 2011-10-05T14:48:00
287 const datetimeNormalized = datetime.replace(/-|:/g, ''); // 20111005T144800
288 this.profilerOutput = `codegen-${datetimeNormalized}.json`;
289 }
290 getPluginContext() {
291 return this._pluginContext;
292 }
293 async loadSchema(pointer) {
294 const config = this.getConfig(load_js_1.defaultSchemaLoadOptions);
295 if (this._graphqlConfig) {
296 // TODO: SchemaWithLoader won't work here
297 return addHashToSchema(this._graphqlConfig.getProject(this._project).loadSchema(pointer, 'GraphQLSchema', config));
298 }
299 return addHashToSchema((0, load_js_1.loadSchema)(pointer, config));
300 }
301 async loadDocuments(pointer) {
302 const config = this.getConfig(load_js_1.defaultDocumentsLoadOptions);
303 if (this._graphqlConfig) {
304 // TODO: pointer won't work here
305 return addHashToDocumentFiles(this._graphqlConfig.getProject(this._project).loadDocuments(pointer, config));
306 }
307 return addHashToDocumentFiles((0, load_js_1.loadDocuments)(pointer, config));
308 }
309}
310exports.CodegenContext = CodegenContext;
311function ensureContext(input) {
312 return input instanceof CodegenContext ? input : new CodegenContext({ config: input });
313}
314exports.ensureContext = ensureContext;
315function hashContent(content) {
316 return (0, crypto_1.createHash)('sha256').update(content).digest('hex');
317}
318function hashSchema(schema) {
319 return hashContent((0, graphql_1.print)((0, plugin_helpers_1.getCachedDocumentNodeFromSchema)(schema)));
320}
321function addHashToSchema(schemaPromise) {
322 return schemaPromise.then(schema => {
323 // It's consumed later on. The general purpose is to use it for caching.
324 if (!schema.extensions) {
325 schema.extensions = {};
326 }
327 schema.extensions['hash'] = hashSchema(schema);
328 return schema;
329 });
330}
331function hashDocument(doc) {
332 if (doc.rawSDL) {
333 return hashContent(doc.rawSDL);
334 }
335 if (doc.document) {
336 return hashContent((0, graphql_1.print)(doc.document));
337 }
338 return null;
339}
340function addHashToDocumentFiles(documentFilesPromise) {
341 return documentFilesPromise.then(documentFiles => documentFiles.map(doc => {
342 doc.hash = hashDocument(doc);
343 return doc;
344 }));
345}
346function shouldEmitLegacyCommonJSImports(config, outputPath) {
347 const globalValue = config.emitLegacyCommonJSImports === undefined ? true : !!config.emitLegacyCommonJSImports;
348 // const outputConfig = config.generates[outputPath];
349 // if (!outputConfig) {
350 // debugLog(`Couldn't find a config of ${outputPath}`);
351 // return globalValue;
352 // }
353 // if (isConfiguredOutput(outputConfig) && typeof outputConfig.emitLegacyCommonJSImports === 'boolean') {
354 // return outputConfig.emitLegacyCommonJSImports;
355 // }
356 return globalValue;
357}
358exports.shouldEmitLegacyCommonJSImports = shouldEmitLegacyCommonJSImports;