UNPKG

@graphql-mesh/utils

Version:
148 lines (147 loc) • 6.16 kB
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; }