@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
JavaScript
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;