@graphql-codegen/cli 2.7.0-alpha-05066c85f.0 → 2.8.0-alpha-36b27f965.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/cjs/bin.js +13 -0
  2. package/cjs/cli.js +60 -0
  3. package/cjs/codegen.js +332 -0
  4. package/cjs/config.js +303 -0
  5. package/cjs/generate-and-save.js +110 -0
  6. package/cjs/graphql-config.js +69 -0
  7. package/cjs/hooks.js +87 -0
  8. package/cjs/index.js +13 -0
  9. package/cjs/init/helpers.js +64 -0
  10. package/cjs/init/index.js +60 -0
  11. package/cjs/init/plugins.js +163 -0
  12. package/cjs/init/questions.js +147 -0
  13. package/cjs/init/targets.js +44 -0
  14. package/cjs/init/types.js +14 -0
  15. package/cjs/load.js +93 -0
  16. package/cjs/package.json +1 -0
  17. package/cjs/plugins.js +45 -0
  18. package/cjs/presets.js +46 -0
  19. package/cjs/utils/cli-error.js +25 -0
  20. package/cjs/utils/debugging.js +27 -0
  21. package/cjs/utils/file-system.js +26 -0
  22. package/cjs/utils/indentString.js +37 -0
  23. package/cjs/utils/listr-renderer.js +136 -0
  24. package/cjs/utils/logger.js +25 -0
  25. package/cjs/utils/watcher.js +140 -0
  26. package/esm/bin.js +11 -0
  27. package/esm/cli.js +32 -0
  28. package/esm/codegen.js +327 -0
  29. package/esm/config.js +290 -0
  30. package/esm/generate-and-save.js +105 -0
  31. package/esm/graphql-config.js +64 -0
  32. package/esm/hooks.js +83 -0
  33. package/esm/index.js +7 -0
  34. package/esm/init/helpers.js +55 -0
  35. package/esm/init/index.js +55 -0
  36. package/esm/init/plugins.js +160 -0
  37. package/esm/init/questions.js +140 -0
  38. package/esm/init/targets.js +40 -0
  39. package/esm/init/types.js +11 -0
  40. package/esm/load.js +88 -0
  41. package/esm/plugins.js +41 -0
  42. package/esm/presets.js +42 -0
  43. package/esm/utils/cli-error.js +20 -0
  44. package/esm/utils/debugging.js +21 -0
  45. package/esm/utils/file-system.js +19 -0
  46. package/esm/utils/indentString.js +33 -0
  47. package/esm/utils/listr-renderer.js +130 -0
  48. package/esm/utils/logger.js +18 -0
  49. package/esm/utils/watcher.js +135 -0
  50. package/package.json +34 -26
  51. package/typings/bin.d.ts +2 -0
  52. package/{cli.d.ts → typings/cli.d.ts} +0 -0
  53. package/{codegen.d.ts → typings/codegen.d.ts} +1 -1
  54. package/{config.d.ts → typings/config.d.ts} +0 -0
  55. package/{generate-and-save.d.ts → typings/generate-and-save.d.ts} +1 -1
  56. package/{graphql-config.d.ts → typings/graphql-config.d.ts} +0 -0
  57. package/{hooks.d.ts → typings/hooks.d.ts} +0 -0
  58. package/typings/index.d.ts +7 -0
  59. package/{init → typings/init}/helpers.d.ts +1 -1
  60. package/{init → typings/init}/index.d.ts +0 -0
  61. package/{init → typings/init}/plugins.d.ts +1 -1
  62. package/{init → typings/init}/questions.d.ts +1 -1
  63. package/{init → typings/init}/targets.d.ts +1 -1
  64. package/{init → typings/init}/types.d.ts +0 -0
  65. package/{load.d.ts → typings/load.d.ts} +0 -0
  66. package/{plugins.d.ts → typings/plugins.d.ts} +0 -0
  67. package/{presets.d.ts → typings/presets.d.ts} +0 -0
  68. package/{utils → typings/utils}/cli-error.d.ts +0 -0
  69. package/{utils → typings/utils}/debugging.d.ts +0 -0
  70. package/{utils → typings/utils}/file-system.d.ts +0 -0
  71. package/{utils → typings/utils}/indentString.d.ts +0 -0
  72. package/{utils → typings/utils}/listr-renderer.d.ts +0 -0
  73. package/{utils → typings/utils}/logger.d.ts +0 -0
  74. package/{utils → typings/utils}/watcher.d.ts +1 -1
  75. package/bin.d.ts +0 -1
  76. package/bin.js +0 -1888
  77. package/index.d.ts +0 -7
  78. package/index.js +0 -1899
  79. package/index.mjs +0 -1855
  80. package/utils/try-to-build-schema.d.ts +0 -2
@@ -0,0 +1,105 @@
1
+ import { lifecycleHooks } from './hooks.js';
2
+ import { executeCodegen } from './codegen.js';
3
+ import { createWatcher } from './utils/watcher.js';
4
+ import { fileExists, readFile, writeFile, unlinkFile } from './utils/file-system.js';
5
+ import mkdirp from 'mkdirp';
6
+ import { dirname, join, isAbsolute } from 'path';
7
+ import { debugLog } from './utils/debugging.js';
8
+ import { ensureContext } from './config.js';
9
+ import { createHash } from 'crypto';
10
+ const hash = (content) => createHash('sha1').update(content).digest('base64');
11
+ export async function generate(input, saveToFile = true) {
12
+ const context = ensureContext(input);
13
+ const config = context.getConfig();
14
+ await context.profiler.run(() => lifecycleHooks(config.hooks).afterStart(), 'Lifecycle: afterStart');
15
+ let previouslyGeneratedFilenames = [];
16
+ function removeStaleFiles(config, generationResult) {
17
+ const filenames = generationResult.map(o => o.filename);
18
+ // find stale files from previous build which are not present in current build
19
+ const staleFilenames = previouslyGeneratedFilenames.filter(f => !filenames.includes(f));
20
+ staleFilenames.forEach(filename => {
21
+ if (shouldOverwrite(config, filename)) {
22
+ return unlinkFile(filename, err => {
23
+ const prettyFilename = filename.replace(`${input.cwd || process.cwd()}/`, '');
24
+ if (err) {
25
+ debugLog(`Cannot remove stale file: ${prettyFilename}\n${err}`);
26
+ }
27
+ else {
28
+ debugLog(`Removed stale file: ${prettyFilename}`);
29
+ }
30
+ });
31
+ }
32
+ });
33
+ previouslyGeneratedFilenames = filenames;
34
+ }
35
+ const recentOutputHash = new Map();
36
+ async function writeOutput(generationResult) {
37
+ if (!saveToFile) {
38
+ return generationResult;
39
+ }
40
+ if (config.watch) {
41
+ removeStaleFiles(config, generationResult);
42
+ }
43
+ await context.profiler.run(async () => {
44
+ await lifecycleHooks(config.hooks).beforeAllFileWrite(generationResult.map(r => r.filename));
45
+ }, 'Lifecycle: beforeAllFileWrite');
46
+ await context.profiler.run(() => Promise.all(generationResult.map(async (result) => {
47
+ const exists = await fileExists(result.filename);
48
+ if (!shouldOverwrite(config, result.filename) && exists) {
49
+ return;
50
+ }
51
+ const content = result.content || '';
52
+ const currentHash = hash(content);
53
+ let previousHash = recentOutputHash.get(result.filename);
54
+ if (!previousHash && exists) {
55
+ previousHash = hash(await readFile(result.filename));
56
+ }
57
+ if (previousHash && currentHash === previousHash) {
58
+ debugLog(`Skipping file (${result.filename}) writing due to indentical hash...`);
59
+ return;
60
+ }
61
+ if (content.length === 0) {
62
+ return;
63
+ }
64
+ recentOutputHash.set(result.filename, currentHash);
65
+ const basedir = dirname(result.filename);
66
+ await lifecycleHooks(result.hooks).beforeOneFileWrite(result.filename);
67
+ await lifecycleHooks(config.hooks).beforeOneFileWrite(result.filename);
68
+ await mkdirp(basedir);
69
+ const absolutePath = isAbsolute(result.filename)
70
+ ? result.filename
71
+ : join(input.cwd || process.cwd(), result.filename);
72
+ await writeFile(absolutePath, result.content);
73
+ await lifecycleHooks(result.hooks).afterOneFileWrite(result.filename);
74
+ await lifecycleHooks(config.hooks).afterOneFileWrite(result.filename);
75
+ })), 'Write files');
76
+ await context.profiler.run(() => lifecycleHooks(config.hooks).afterAllFileWrite(generationResult.map(r => r.filename)), 'Lifecycle: afterAllFileWrite');
77
+ return generationResult;
78
+ }
79
+ // watch mode
80
+ if (config.watch) {
81
+ return createWatcher(context, writeOutput);
82
+ }
83
+ const outputFiles = await context.profiler.run(() => executeCodegen(context), 'executeCodegen');
84
+ await context.profiler.run(() => writeOutput(outputFiles), 'writeOutput');
85
+ await context.profiler.run(() => lifecycleHooks(config.hooks).beforeDone(), 'Lifecycle: beforeDone');
86
+ if (context.profilerOutput) {
87
+ await writeFile(join(context.cwd, context.profilerOutput), JSON.stringify(context.profiler.collect()));
88
+ }
89
+ return outputFiles;
90
+ }
91
+ function shouldOverwrite(config, outputPath) {
92
+ const globalValue = config.overwrite === undefined ? true : !!config.overwrite;
93
+ const outputConfig = config.generates[outputPath];
94
+ if (!outputConfig) {
95
+ debugLog(`Couldn't find a config of ${outputPath}`);
96
+ return globalValue;
97
+ }
98
+ if (isConfiguredOutput(outputConfig) && typeof outputConfig.overwrite === 'boolean') {
99
+ return outputConfig.overwrite;
100
+ }
101
+ return globalValue;
102
+ }
103
+ function isConfiguredOutput(output) {
104
+ return typeof output.plugins !== 'undefined';
105
+ }
@@ -0,0 +1,64 @@
1
+ import { loadConfig } from 'graphql-config';
2
+ import { ApolloEngineLoader } from '@graphql-tools/apollo-engine-loader';
3
+ import { CodeFileLoader } from '@graphql-tools/code-file-loader';
4
+ import { GitLoader } from '@graphql-tools/git-loader';
5
+ import { GithubLoader } from '@graphql-tools/github-loader';
6
+ import { PrismaLoader } from '@graphql-tools/prisma-loader';
7
+ export const CodegenExtension = (api) => {
8
+ // Schema
9
+ api.loaders.schema.register(new CodeFileLoader({
10
+ pluckConfig: {
11
+ skipIndent: true,
12
+ },
13
+ }));
14
+ api.loaders.schema.register(new GitLoader());
15
+ api.loaders.schema.register(new GithubLoader());
16
+ api.loaders.schema.register(new ApolloEngineLoader());
17
+ api.loaders.schema.register(new PrismaLoader());
18
+ // Documents
19
+ api.loaders.documents.register(new CodeFileLoader({
20
+ pluckConfig: {
21
+ skipIndent: true,
22
+ },
23
+ }));
24
+ api.loaders.documents.register(new GitLoader());
25
+ api.loaders.documents.register(new GithubLoader());
26
+ return {
27
+ name: 'codegen',
28
+ };
29
+ };
30
+ export async function findAndLoadGraphQLConfig(filepath) {
31
+ const config = await loadConfig({
32
+ filepath,
33
+ rootDir: process.cwd(),
34
+ extensions: [CodegenExtension],
35
+ throwOnEmpty: false,
36
+ throwOnMissing: false,
37
+ });
38
+ if (isGraphQLConfig(config)) {
39
+ return config;
40
+ }
41
+ }
42
+ // Kamil: user might load a config that is not GraphQL Config
43
+ // so we need to check if it's a regular config or not
44
+ function isGraphQLConfig(config) {
45
+ if (!config) {
46
+ return false;
47
+ }
48
+ try {
49
+ return config.getDefault().hasExtension('codegen');
50
+ }
51
+ catch (e) { }
52
+ try {
53
+ for (const projectName in config.projects) {
54
+ if (config.projects.hasOwnProperty(projectName)) {
55
+ const project = config.projects[projectName];
56
+ if (project.hasExtension('codegen')) {
57
+ return true;
58
+ }
59
+ }
60
+ }
61
+ }
62
+ catch (e) { }
63
+ return false;
64
+ }
package/esm/hooks.js ADDED
@@ -0,0 +1,83 @@
1
+ import { debugLog } from './utils/debugging.js';
2
+ import { exec } from 'child_process';
3
+ import { delimiter, sep } from 'path';
4
+ const DEFAULT_HOOKS = {
5
+ afterStart: [],
6
+ beforeDone: [],
7
+ onWatchTriggered: [],
8
+ onError: [],
9
+ afterOneFileWrite: [],
10
+ afterAllFileWrite: [],
11
+ beforeOneFileWrite: [],
12
+ beforeAllFileWrite: [],
13
+ };
14
+ function normalizeHooks(_hooks) {
15
+ const keys = Object.keys({
16
+ ...DEFAULT_HOOKS,
17
+ ..._hooks,
18
+ });
19
+ return keys.reduce((prev, hookName) => {
20
+ if (typeof _hooks[hookName] === 'string') {
21
+ return {
22
+ ...prev,
23
+ [hookName]: [_hooks[hookName]],
24
+ };
25
+ }
26
+ if (typeof _hooks[hookName] === 'function') {
27
+ return {
28
+ ...prev,
29
+ [hookName]: [_hooks[hookName]],
30
+ };
31
+ }
32
+ if (Array.isArray(_hooks[hookName])) {
33
+ return {
34
+ ...prev,
35
+ [hookName]: _hooks[hookName],
36
+ };
37
+ }
38
+ return prev;
39
+ }, {});
40
+ }
41
+ function execShellCommand(cmd) {
42
+ return new Promise((resolve, reject) => {
43
+ exec(cmd, {
44
+ env: {
45
+ ...process.env,
46
+ PATH: `${process.env.PATH}${delimiter}${process.cwd()}${sep}node_modules${sep}.bin`,
47
+ },
48
+ }, (error, stdout, stderr) => {
49
+ if (error) {
50
+ reject(error);
51
+ }
52
+ else {
53
+ resolve(stdout || stderr);
54
+ }
55
+ });
56
+ });
57
+ }
58
+ async function executeHooks(hookName, scripts = [], args = []) {
59
+ debugLog(`Running lifecycle hook "${hookName}" scripts...`);
60
+ for (const script of scripts) {
61
+ if (typeof script === 'string') {
62
+ debugLog(`Running lifecycle hook "${hookName}" script: ${script} with args: ${args.join(' ')}...`);
63
+ await execShellCommand(`${script} ${args.join(' ')}`);
64
+ }
65
+ else {
66
+ debugLog(`Running lifecycle hook "${hookName}" script: ${script.name} with args: ${args.join(' ')}...`);
67
+ await script(...args);
68
+ }
69
+ }
70
+ }
71
+ export const lifecycleHooks = (_hooks = {}) => {
72
+ const hooks = normalizeHooks(_hooks);
73
+ return {
74
+ afterStart: async () => executeHooks('afterStart', hooks.afterStart),
75
+ onWatchTriggered: async (event, path) => executeHooks('onWatchTriggered', hooks.onWatchTriggered, [event, path]),
76
+ onError: async (error) => executeHooks('onError', hooks.onError, [`"${error}"`]),
77
+ afterOneFileWrite: async (path) => executeHooks('afterOneFileWrite', hooks.afterOneFileWrite, [path]),
78
+ afterAllFileWrite: async (paths) => executeHooks('afterAllFileWrite', hooks.afterAllFileWrite, paths),
79
+ beforeOneFileWrite: async (path) => executeHooks('beforeOneFileWrite', hooks.beforeOneFileWrite, [path]),
80
+ beforeAllFileWrite: async (paths) => executeHooks('beforeAllFileWrite', hooks.beforeAllFileWrite, paths),
81
+ beforeDone: async () => executeHooks('beforeDone', hooks.beforeDone),
82
+ };
83
+ };
package/esm/index.js ADDED
@@ -0,0 +1,7 @@
1
+ export { executeCodegen } from './codegen.js';
2
+ export { generate } from './generate-and-save.js';
3
+ export * from './config.js';
4
+ export * from './init/index.js';
5
+ export * from './utils/cli-error.js';
6
+ export * from './cli.js';
7
+ export * from './graphql-config.js';
@@ -0,0 +1,55 @@
1
+ import chalk from 'chalk';
2
+ import { resolve, relative } from 'path';
3
+ import { writeFileSync, readFileSync } from 'fs';
4
+ import detectIndent from 'detect-indent';
5
+ import getLatestVersion from 'latest-version';
6
+ // Parses config and writes it to a file
7
+ export async function writeConfig(answers, config) {
8
+ const YAML = await import('json-to-pretty-yaml').then(m => ('default' in m ? m.default : m));
9
+ const ext = answers.config.toLocaleLowerCase().endsWith('.json') ? 'json' : 'yml';
10
+ const content = ext === 'json' ? JSON.stringify(config) : YAML.stringify(config);
11
+ const fullPath = resolve(process.cwd(), answers.config);
12
+ const relativePath = relative(process.cwd(), answers.config);
13
+ writeFileSync(fullPath, content, {
14
+ encoding: 'utf-8',
15
+ });
16
+ return {
17
+ relativePath,
18
+ fullPath,
19
+ };
20
+ }
21
+ // Updates package.json (script and plugins as dependencies)
22
+ export async function writePackage(answers, configLocation) {
23
+ // script
24
+ const pkgPath = resolve(process.cwd(), 'package.json');
25
+ const pkgContent = readFileSync(pkgPath, {
26
+ encoding: 'utf-8',
27
+ });
28
+ const pkg = JSON.parse(pkgContent);
29
+ const { indent } = detectIndent(pkgContent);
30
+ if (!pkg.scripts) {
31
+ pkg.scripts = {};
32
+ }
33
+ pkg.scripts[answers.script] = `graphql-codegen --config ${configLocation}`;
34
+ // plugin
35
+ if (!pkg.devDependencies) {
36
+ pkg.devDependencies = {};
37
+ }
38
+ await Promise.all(answers.plugins.map(async (plugin) => {
39
+ pkg.devDependencies[plugin.package] = await getLatestVersion(plugin.package);
40
+ }));
41
+ if (answers.introspection) {
42
+ pkg.devDependencies['@graphql-codegen/introspection'] = await getLatestVersion('@graphql-codegen/introspection');
43
+ }
44
+ pkg.devDependencies['@graphql-codegen/cli'] = await getLatestVersion('@graphql-codegen/cli');
45
+ writeFileSync(pkgPath, JSON.stringify(pkg, null, indent));
46
+ }
47
+ export function bold(str) {
48
+ return chalk.bold(str);
49
+ }
50
+ export function grey(str) {
51
+ return chalk.grey(str);
52
+ }
53
+ export function italic(str) {
54
+ return chalk.italic(str);
55
+ }
@@ -0,0 +1,55 @@
1
+ import inquirer from 'inquirer';
2
+ import { getQuestions } from './questions.js';
3
+ import { guessTargets } from './targets.js';
4
+ import { Tags } from './types.js';
5
+ import { writeConfig, writePackage, bold } from './helpers.js';
6
+ function log(...msgs) {
7
+ // eslint-disable-next-line no-console
8
+ console.log(...msgs);
9
+ }
10
+ export async function init() {
11
+ log(`
12
+ Welcome to ${bold('GraphQL Code Generator')}!
13
+ Answer few questions and we will setup everything for you.
14
+ `);
15
+ const possibleTargets = await guessTargets();
16
+ const answers = await inquirer.prompt(getQuestions(possibleTargets));
17
+ // define config
18
+ const config = {
19
+ overwrite: true,
20
+ schema: answers.schema,
21
+ documents: answers.targets.includes(Tags.browser) ? answers.documents : null,
22
+ generates: {
23
+ [answers.output]: {
24
+ plugins: answers.plugins.map(p => p.value),
25
+ },
26
+ },
27
+ };
28
+ // introspection
29
+ if (answers.introspection) {
30
+ addIntrospection(config);
31
+ }
32
+ // config file
33
+ const { relativePath } = await writeConfig(answers, config);
34
+ log(`Fetching latest versions of selected plugins...`);
35
+ // write package.json
36
+ await writePackage(answers, relativePath);
37
+ // Emit status to the terminal
38
+ log(`
39
+ Config file generated at ${bold(relativePath)}
40
+
41
+ ${bold('$ npm install')}
42
+
43
+ To install the plugins.
44
+
45
+ ${bold(`$ npm run ${answers.script}`)}
46
+
47
+ To run GraphQL Code Generator.
48
+ `);
49
+ }
50
+ // adds an introspection to `generates`
51
+ function addIntrospection(config) {
52
+ config.generates['./graphql.schema.json'] = {
53
+ plugins: ['introspection'],
54
+ };
55
+ }
@@ -0,0 +1,160 @@
1
+ import { italic } from './helpers.js';
2
+ import { Tags } from './types.js';
3
+ export const plugins = [
4
+ {
5
+ name: `TypeScript ${italic('(required by other typescript plugins)')}`,
6
+ package: '@graphql-codegen/typescript',
7
+ value: 'typescript',
8
+ pathInRepo: 'typescript/typescript',
9
+ available: hasTag(Tags.typescript),
10
+ shouldBeSelected: tags => oneOf(tags, Tags.angular, Tags.stencil) || allOf(tags, Tags.typescript, Tags.react) || noneOf(tags, Tags.flow),
11
+ defaultExtension: '.ts',
12
+ },
13
+ {
14
+ name: `TypeScript Operations ${italic('(operations and fragments)')}`,
15
+ package: '@graphql-codegen/typescript-operations',
16
+ value: 'typescript-operations',
17
+ pathInRepo: 'typescript/operations',
18
+ available: tags => allOf(tags, Tags.browser, Tags.typescript),
19
+ shouldBeSelected: tags => oneOf(tags, Tags.angular, Tags.stencil) || allOf(tags, Tags.typescript, Tags.react),
20
+ defaultExtension: '.ts',
21
+ },
22
+ {
23
+ name: `TypeScript Resolvers ${italic('(strongly typed resolve functions)')}`,
24
+ package: '@graphql-codegen/typescript-resolvers',
25
+ value: 'typescript-resolvers',
26
+ pathInRepo: 'typescript/resolvers',
27
+ available: tags => allOf(tags, Tags.node, Tags.typescript),
28
+ shouldBeSelected: tags => noneOf(tags, Tags.flow),
29
+ defaultExtension: '.ts',
30
+ },
31
+ {
32
+ name: `Flow ${italic('(required by other flow plugins)')}`,
33
+ package: '@graphql-codegen/flow',
34
+ value: 'flow',
35
+ pathInRepo: 'flow/flow',
36
+ available: hasTag(Tags.flow),
37
+ shouldBeSelected: tags => noneOf(tags, Tags.typescript),
38
+ defaultExtension: '.js',
39
+ },
40
+ {
41
+ name: `Flow Operations ${italic('(operations and fragments)')}`,
42
+ package: '@graphql-codegen/flow-operations',
43
+ value: 'flow-operations',
44
+ pathInRepo: 'flow/operations',
45
+ available: tags => allOf(tags, Tags.browser, Tags.flow),
46
+ shouldBeSelected: tags => noneOf(tags, Tags.typescript),
47
+ defaultExtension: '.js',
48
+ },
49
+ {
50
+ name: `Flow Resolvers ${italic('(strongly typed resolve functions)')}`,
51
+ package: '@graphql-codegen/flow-resolvers',
52
+ value: 'flow-resolvers',
53
+ pathInRepo: 'flow/resolvers',
54
+ available: tags => allOf(tags, Tags.node, Tags.flow),
55
+ shouldBeSelected: tags => noneOf(tags, Tags.typescript),
56
+ defaultExtension: '.js',
57
+ },
58
+ {
59
+ name: `TypeScript Apollo Angular ${italic('(typed GQL services)')}`,
60
+ package: '@graphql-codegen/typescript-apollo-angular',
61
+ value: 'typescript-apollo-angular',
62
+ pathInRepo: 'typescript/apollo-angular',
63
+ available: hasTag(Tags.angular),
64
+ shouldBeSelected: () => true,
65
+ defaultExtension: '.js',
66
+ },
67
+ {
68
+ name: `TypeScript Vue Apollo Composition API ${italic('(typed functions)')}`,
69
+ package: '@graphql-codegen/typescript-vue-apollo',
70
+ value: 'typescript-vue-apollo',
71
+ pathInRepo: 'typescript/vue-apollo',
72
+ available: tags => allOf(tags, Tags.vue, Tags.typescript),
73
+ shouldBeSelected: () => true,
74
+ defaultExtension: '.ts',
75
+ },
76
+ {
77
+ name: `TypeScript Vue Apollo Smart Operations ${italic('(typed functions)')}`,
78
+ package: '@graphql-codegen/typescript-vue-apollo-smart-ops',
79
+ value: 'typescript-vue-apollo-smart-ops',
80
+ pathInRepo: 'typescript/vue-apollo-smart-ops',
81
+ available: tags => allOf(tags, Tags.vue, Tags.typescript),
82
+ shouldBeSelected: () => true,
83
+ defaultExtension: '.ts',
84
+ },
85
+ {
86
+ name: `TypeScript React Apollo ${italic('(typed components and HOCs)')}`,
87
+ package: '@graphql-codegen/typescript-react-apollo',
88
+ value: 'typescript-react-apollo',
89
+ pathInRepo: 'typescript/react-apollo',
90
+ available: tags => allOf(tags, Tags.react, Tags.typescript),
91
+ shouldBeSelected: () => true,
92
+ defaultExtension: '.tsx',
93
+ },
94
+ {
95
+ name: `TypeScript Stencil Apollo ${italic('(typed components)')}`,
96
+ package: '@graphql-codegen/typescript-stencil-apollo',
97
+ value: 'typescript-stencil-apollo',
98
+ pathInRepo: 'typescript/stencil-apollo',
99
+ available: hasTag(Tags.stencil),
100
+ shouldBeSelected: () => true,
101
+ defaultExtension: '.tsx',
102
+ },
103
+ {
104
+ name: `TypeScript MongoDB ${italic('(typed MongoDB objects)')}`,
105
+ package: '@graphql-codegen/typescript-mongodb',
106
+ value: 'typescript-mongodb',
107
+ pathInRepo: 'typescript/mongodb',
108
+ available: tags => allOf(tags, Tags.node, Tags.typescript),
109
+ shouldBeSelected: () => false,
110
+ defaultExtension: '.ts',
111
+ },
112
+ {
113
+ name: `TypeScript GraphQL files modules ${italic('(declarations for .graphql files)')}`,
114
+ package: '@graphql-codegen/typescript-graphql-files-modules',
115
+ value: 'typescript-graphql-files-modules',
116
+ pathInRepo: 'typescript/graphql-files-modules',
117
+ available: tags => allOf(tags, Tags.browser, Tags.typescript),
118
+ shouldBeSelected: () => false,
119
+ defaultExtension: '.ts',
120
+ },
121
+ {
122
+ name: `TypeScript GraphQL document nodes ${italic('(embedded GraphQL document)')}`,
123
+ package: '@graphql-codegen/typescript-document-nodes',
124
+ value: 'typescript-document-nodes',
125
+ pathInRepo: 'typescript/document-nodes',
126
+ available: tags => allOf(tags, Tags.typescript),
127
+ shouldBeSelected: () => false,
128
+ defaultExtension: '.ts',
129
+ },
130
+ {
131
+ name: `Introspection Fragment Matcher ${italic('(for Apollo Client)')}`,
132
+ package: '@graphql-codegen/fragment-matcher',
133
+ value: 'fragment-matcher',
134
+ pathInRepo: 'other/fragment-matcher',
135
+ available: hasTag(Tags.browser),
136
+ shouldBeSelected: () => false,
137
+ defaultExtension: '.ts',
138
+ },
139
+ {
140
+ name: `Urql Introspection ${italic('(for Urql Client)')}`,
141
+ package: '@graphql-codegen/urql-introspection',
142
+ value: 'urql-introspection',
143
+ pathInRepo: 'other/urql-introspection',
144
+ available: hasTag(Tags.browser),
145
+ shouldBeSelected: () => false,
146
+ defaultExtension: '.ts',
147
+ },
148
+ ];
149
+ function hasTag(tag) {
150
+ return (tags) => tags.includes(tag);
151
+ }
152
+ function oneOf(list, ...items) {
153
+ return list.some(i => items.includes(i));
154
+ }
155
+ function noneOf(list, ...items) {
156
+ return !list.some(i => items.includes(i));
157
+ }
158
+ function allOf(list, ...items) {
159
+ return items.every(i => list.includes(i));
160
+ }
@@ -0,0 +1,140 @@
1
+ import { grey } from './helpers.js';
2
+ import { Tags } from './types.js';
3
+ import { plugins } from './plugins.js';
4
+ export function getQuestions(possibleTargets) {
5
+ return [
6
+ {
7
+ type: 'checkbox',
8
+ name: 'targets',
9
+ message: `What type of application are you building?`,
10
+ choices: getApplicationTypeChoices(possibleTargets),
11
+ validate: ((targets) => targets.length > 0),
12
+ },
13
+ {
14
+ type: 'input',
15
+ name: 'schema',
16
+ message: 'Where is your schema?:',
17
+ suffix: grey(' (path or url)'),
18
+ default: 'http://localhost:4000',
19
+ validate: (str) => str.length > 0,
20
+ },
21
+ {
22
+ type: 'input',
23
+ name: 'documents',
24
+ message: 'Where are your operations and fragments?:',
25
+ when: answers => {
26
+ // flatten targets
27
+ // I can't find an API in Inquirer that would do that
28
+ answers.targets = normalizeTargets(answers.targets);
29
+ return answers.targets.includes(Tags.browser);
30
+ },
31
+ default: 'src/**/*.graphql',
32
+ validate: (str) => str.length > 0,
33
+ },
34
+ {
35
+ type: 'checkbox',
36
+ name: 'plugins',
37
+ message: 'Pick plugins:',
38
+ choices: getPluginChoices,
39
+ validate: ((plugins) => plugins.length > 0),
40
+ },
41
+ {
42
+ type: 'input',
43
+ name: 'output',
44
+ message: 'Where to write the output:',
45
+ default: getOutputDefaultValue,
46
+ validate: (str) => str.length > 0,
47
+ },
48
+ {
49
+ type: 'confirm',
50
+ name: 'introspection',
51
+ message: 'Do you want to generate an introspection file?',
52
+ },
53
+ {
54
+ type: 'input',
55
+ name: 'config',
56
+ message: 'How to name the config file?',
57
+ default: 'codegen.yml',
58
+ validate: (str) => {
59
+ const isNotEmpty = str.length > 0;
60
+ const hasCorrectExtension = ['json', 'yml', 'yaml'].some(ext => str.toLocaleLowerCase().endsWith(`.${ext}`));
61
+ return isNotEmpty && hasCorrectExtension;
62
+ },
63
+ },
64
+ {
65
+ type: 'input',
66
+ name: 'script',
67
+ message: 'What script in package.json should run the codegen?',
68
+ validate: (str) => str.length > 0,
69
+ },
70
+ ];
71
+ }
72
+ export function getApplicationTypeChoices(possibleTargets) {
73
+ function withFlowOrTypescript(tags) {
74
+ if (possibleTargets.TypeScript) {
75
+ tags.push(Tags.typescript);
76
+ }
77
+ else if (possibleTargets.Flow) {
78
+ tags.push(Tags.flow);
79
+ }
80
+ else {
81
+ tags.push(Tags.flow, Tags.typescript);
82
+ }
83
+ return tags;
84
+ }
85
+ return [
86
+ {
87
+ name: 'Backend - API or server',
88
+ key: 'backend',
89
+ value: withFlowOrTypescript([Tags.node]),
90
+ checked: possibleTargets.Node,
91
+ },
92
+ {
93
+ name: 'Application built with Angular',
94
+ key: 'angular',
95
+ value: [Tags.angular, Tags.browser, Tags.typescript],
96
+ checked: possibleTargets.Angular,
97
+ },
98
+ {
99
+ name: 'Application built with React',
100
+ key: 'react',
101
+ value: withFlowOrTypescript([Tags.react, Tags.browser]),
102
+ checked: possibleTargets.React,
103
+ },
104
+ {
105
+ name: 'Application built with Stencil',
106
+ key: 'stencil',
107
+ value: [Tags.stencil, Tags.browser, Tags.typescript],
108
+ checked: possibleTargets.Stencil,
109
+ },
110
+ {
111
+ name: 'Application built with other framework or vanilla JS',
112
+ key: 'client',
113
+ value: [Tags.browser, Tags.typescript, Tags.flow],
114
+ checked: possibleTargets.Browser && !possibleTargets.Angular && !possibleTargets.React && !possibleTargets.Stencil,
115
+ },
116
+ ];
117
+ }
118
+ export function getPluginChoices(answers) {
119
+ return plugins
120
+ .filter(p => p.available(answers.targets))
121
+ .map(p => {
122
+ return {
123
+ name: p.name,
124
+ value: p,
125
+ checked: p.shouldBeSelected(answers.targets),
126
+ };
127
+ });
128
+ }
129
+ function normalizeTargets(targets) {
130
+ return [].concat(...targets);
131
+ }
132
+ export function getOutputDefaultValue(answers) {
133
+ if (answers.plugins.some(plugin => plugin.defaultExtension === '.tsx')) {
134
+ return 'src/generated/graphql.tsx';
135
+ }
136
+ if (answers.plugins.some(plugin => plugin.defaultExtension === '.ts')) {
137
+ return 'src/generated/graphql.ts';
138
+ }
139
+ return 'src/generated/graphql.js';
140
+ }