UNPKG

@graphql-codegen/near-operation-file-preset

Version:

GraphQL Code Generator preset for generating operation code near the operation file

139 lines (138 loc) 6.69 kB
import { join } from 'path'; import { buildASTSchema, Kind } from 'graphql'; import addPlugin from '@graphql-codegen/add'; import { getConfigValue, } from '@graphql-codegen/visitor-plugin-common'; import { resolveDocumentImports, } from './resolve-document-imports.js'; import { appendFileNameToFilePath, defineFilepathSubfolder } from './utils.js'; export { resolveDocumentImports }; export const preset = { buildGeneratesSection: options => { var _a; const schemaObject = options.schemaAst ? options.schemaAst : buildASTSchema(options.schema, options.config); const baseDir = options.presetConfig.cwd || process.cwd(); const fileName = options.presetConfig.fileName || ''; const extension = options.presetConfig.extension || '.generated.ts'; const folder = options.presetConfig.folder || ''; const importTypesNamespace = options.presetConfig.importTypesNamespace || 'Types'; const importAllFragmentsFrom = options.presetConfig.importAllFragmentsFrom || null; const { baseTypesPath } = options.presetConfig; if (!baseTypesPath) { throw new Error(`Preset "near-operation-file" requires you to specify "baseTypesPath" configuration and point it to your base types file (generated by "typescript" plugin)!`); } const shouldAbsolute = !baseTypesPath.startsWith('~'); const pluginMap = { ...options.pluginMap, add: addPlugin, }; const sources = resolveDocumentImports(options, schemaObject, { baseDir, generateFilePath(location) { const newFilePath = defineFilepathSubfolder(location, folder); return appendFileNameToFilePath(newFilePath, fileName, extension); }, schemaTypesSource: { path: shouldAbsolute ? join(options.baseOutputDir, baseTypesPath) : baseTypesPath, namespace: importTypesNamespace, }, typesImport: (_a = options.config.useTypeImports) !== null && _a !== void 0 ? _a : false, }, getConfigValue(options.config.dedupeFragments, false)); const filePathsMap = new Map(); for (const source of sources) { let record = filePathsMap.get(source.filename); if (record === undefined) { record = { importStatements: new Set(), documents: [], externalFragments: [], fragmentImports: [], }; filePathsMap.set(source.filename, record); } for (const importStatement of source.importStatements) { record.importStatements.add(importStatement); } record.documents.push(...source.documents); record.externalFragments.push(...source.externalFragments); record.fragmentImports.push(...source.fragmentImports); } const artifacts = []; for (const [filename, record] of filePathsMap.entries()) { let fragmentImportsArr = record.fragmentImports; if (importAllFragmentsFrom) { fragmentImportsArr = record.fragmentImports.map(t => { const newImportSource = typeof importAllFragmentsFrom === 'string' ? { ...t.importSource, path: importAllFragmentsFrom } : importAllFragmentsFrom(t.importSource, filename); return { ...t, importSource: newImportSource || t.importSource, }; }); } // Merge multiple fragment imports from the same file const fragmentImportsByImportSource = {}; fragmentImportsArr.forEach(fi => { if (!fragmentImportsByImportSource[fi.importSource.path]) { fragmentImportsByImportSource[fi.importSource.path] = fi; } else { const mergedIdentifiersByName = {}; fragmentImportsByImportSource[fi.importSource.path].importSource.identifiers.forEach(identifier => { mergedIdentifiersByName[identifier.name] = identifier; }); fi.importSource.identifiers.forEach(identifier => { mergedIdentifiersByName[identifier.name] = identifier; }); fragmentImportsByImportSource[fi.importSource.path].importSource.identifiers = Object.values(mergedIdentifiersByName); } }); fragmentImportsArr = Object.values(fragmentImportsByImportSource); const plugins = [ // TODO/NOTE I made globalNamespace include schema types - is that correct? ...(options.config.globalNamespace ? [] : Array.from(record.importStatements).map(importStatement => ({ add: { content: importStatement }, }))), ...options.plugins, ]; const config = { ...options.config, // This is set here in order to make sure the fragment spreads sub types // are exported from operations file exportFragmentSpreadSubTypes: true, namespacedImportName: importTypesNamespace, externalFragments: record.externalFragments, fragmentImports: fragmentImportsArr, }; const document = { kind: Kind.DOCUMENT, definitions: [] }; const combinedSource = { rawSDL: '', document, location: record.documents[0].location, }; for (const source of record.documents) { combinedSource.rawSDL += source.rawSDL; combinedSource.document.definitions.push(...source.document.definitions); } artifacts.push({ ...options, filename, documents: [combinedSource], plugins, pluginMap, config, schema: options.schema, schemaAst: schemaObject, skipDocumentsValidation: typeof options.config.skipDocumentsValidation === 'undefined' ? { skipDuplicateValidation: true } : options.config.skipDocumentsValidation, }); } return artifacts; }, }; export default preset;