@graphql-mesh/utils
Version:
148 lines (147 loc) • 6.16 kB
JavaScript
import { parse, Source } from 'graphql';
import { DEFAULT_SCHEMA, load as loadYamlFromJsYaml, Type } from 'js-yaml';
import { fs, path as pathModule } from '@graphql-mesh/cross-helpers';
import { isUrl, isValidPath, mapMaybePromise } from '@graphql-tools/utils';
import { fetch as defaultFetch } from '@whatwg-node/fetch';
import { loadFromModuleExportExpression } from './load-from-module-export-expression.js';
export { isUrl };
export async function readFileOrUrl(filePathOrUrl, config) {
if (isUrl(filePathOrUrl)) {
config.logger.debug(`Fetching ${filePathOrUrl} via HTTP`);
return readUrl(filePathOrUrl, config);
}
else if (filePathOrUrl.startsWith('{') ||
filePathOrUrl.startsWith('[') ||
filePathOrUrl.startsWith('"')) {
return JSON.parse(filePathOrUrl);
}
else if (isValidPath(filePathOrUrl)) {
config.logger.debug(`Reading ${filePathOrUrl} from the file system`);
return readFile(filePathOrUrl, config);
}
else {
return filePathOrUrl;
}
}
function getSchema(filepath, logger) {
return DEFAULT_SCHEMA.extend([
new Type('!include', {
kind: 'scalar',
resolve(path) {
return typeof path === 'string';
},
construct(path) {
const newCwd = pathModule.dirname(filepath);
const absoluteFilePath = pathModule.isAbsolute(path)
? path
: pathModule.resolve(newCwd, path);
const content = fs.readFileSync(absoluteFilePath, 'utf8');
return loadYaml(absoluteFilePath, content, logger);
},
}),
new Type('!includes', {
kind: 'scalar',
resolve(path) {
return typeof path === 'string';
},
construct(path) {
const newCwd = pathModule.dirname(filepath);
const absoluteDirPath = pathModule.isAbsolute(path)
? path
: pathModule.resolve(newCwd, path);
const files = fs.readdirSync(absoluteDirPath);
return files.map(filePath => {
const absoluteFilePath = pathModule.resolve(absoluteDirPath, filePath);
const fileContent = fs.readFileSync(absoluteFilePath, 'utf8');
return loadYaml(absoluteFilePath, fileContent, logger);
});
},
}),
]);
}
export function loadYaml(filepath, content, logger) {
return loadYamlFromJsYaml(content, {
filename: filepath,
schema: getSchema(filepath, logger),
onWarning(warning) {
logger.warn(`${filepath}: ${warning.message}\n${warning.stack}`);
},
});
}
function isAbsolute(path) {
return path.startsWith('/') || /^[A-Z]:\\/i.test(path);
}
export function readFile(fileExpression, { allowUnknownExtensions, cwd, fallbackFormat, importFn, logger, fetch = defaultFetch, }) {
const [filePath] = fileExpression.split('#');
if (/js$/.test(filePath) || /ts$/.test(filePath) || /json$/.test(filePath)) {
return mapMaybePromise(loadFromModuleExportExpression(fileExpression, {
cwd,
importFn,
defaultExportName: 'default',
}), res => JSON.parse(JSON.stringify(res)));
}
const actualPath = isAbsolute(filePath) ? filePath : `${cwd}/${filePath}`;
const url = `file://${actualPath}`;
return mapMaybePromise(fetch(url), res => {
if (/json$/.test(actualPath) || res.headers.get('content-type')?.includes('json')) {
return res.json();
}
return mapMaybePromise(res.text(), rawResult => {
if (/yaml$/.test(actualPath) || /yml$/.test(actualPath)) {
return loadYaml(actualPath, rawResult, logger);
}
else if (/graphql$/.test(actualPath) ||
/graphqls$/.test(actualPath) ||
/gql$/.test(actualPath) ||
/gqls$/.test(actualPath) ||
res.headers.get('content-type')?.includes('graphql')) {
const source = new Source(rawResult, actualPath);
return parse(source);
}
else if (fallbackFormat) {
switch (fallbackFormat) {
case 'json':
return JSON.parse(rawResult);
case 'yaml':
return loadYaml(actualPath, rawResult, logger);
case 'ts':
case 'js':
return importFn(actualPath);
case 'graphql':
return parse(new Source(rawResult, actualPath));
}
}
else if (!allowUnknownExtensions) {
throw new Error(`Failed to parse JSON/YAML. Ensure file '${filePath}' has ` +
`the correct extension (i.e. '.json', '.yaml', or '.yml).`);
}
return rawResult;
});
});
}
export async function readUrl(path, config) {
const { allowUnknownExtensions, fallbackFormat } = config || {};
config.headers ||= {};
config.fetch ||= defaultFetch;
const response = await config.fetch(path, config);
const contentType = response.headers?.get('content-type') || '';
const responseText = await response.text();
config?.logger?.debug(`${path} returned `, responseText);
if (/json$/.test(path) ||
contentType.startsWith('application/json') ||
fallbackFormat === 'json') {
return JSON.parse(responseText);
}
else if (/yaml$/.test(path) ||
/yml$/.test(path) ||
contentType.includes('yaml') ||
contentType.includes('yml') ||
fallbackFormat === 'yaml') {
return loadYaml(path, responseText, config?.logger);
}
else if (!allowUnknownExtensions) {
throw new Error(`Failed to parse JSON/YAML. Ensure URL '${path}' has ` +
`the correct extension (i.e. '.json', '.yaml', or '.yml) or mime type in the response headers.`);
}
return responseText;
}