Skip to content

Commit

Permalink
feat(json-file-loader): support glob expressions
Browse files Browse the repository at this point in the history
  • Loading branch information
ardatan committed Aug 12, 2021
1 parent bf779b8 commit 4247b02
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 21 deletions.
5 changes: 5 additions & 0 deletions .changeset/soft-jokes-run.md
@@ -0,0 +1,5 @@
---
'@graphql-tools/json-file-loader': minor
---

feat(json-file-loader): support glob expressions
76 changes: 56 additions & 20 deletions packages/loaders/json-file/src/index.ts
@@ -1,7 +1,11 @@
import { Source, parseGraphQLJSON, Loader, isValidPath, BaseLoaderOptions } from '@graphql-tools/utils';
import type { GlobbyOptions } from 'globby';

import { Source, Loader, isValidPath, BaseLoaderOptions, asArray, parseGraphQLJSON } from '@graphql-tools/utils';
import { isAbsolute, resolve } from 'path';
import { readFileSync, promises as fsPromises, existsSync } from 'fs';
import { cwd } from 'process';
import { cwd as processCwd } from 'process';
import globby from 'globby';
import unixify from 'unixify';

const { readFile, access } = fsPromises;

Expand All @@ -12,6 +16,12 @@ const FILE_EXTENSIONS = ['.json'];
*/
export interface JsonFileLoaderOptions extends BaseLoaderOptions {}

function createGlobbyOptions(options: JsonFileLoaderOptions): GlobbyOptions {
return { absolute: true, ...options, ignore: [] };
}

const buildIgnoreGlob = (path: string) => `!${path}`;

/**
* This loader loads documents and type definitions from JSON files.
*
Expand Down Expand Up @@ -39,7 +49,7 @@ export class JsonFileLoader implements Loader {
async canLoad(pointer: string, options: JsonFileLoaderOptions): Promise<boolean> {
if (isValidPath(pointer)) {
if (FILE_EXTENSIONS.find(extension => pointer.endsWith(extension))) {
const normalizedFilePath = isAbsolute(pointer) ? pointer : resolve(options.cwd || cwd(), pointer);
const normalizedFilePath = isAbsolute(pointer) ? pointer : resolve(options.cwd || processCwd(), pointer);
try {
await access(normalizedFilePath);
return true;
Expand All @@ -55,37 +65,63 @@ export class JsonFileLoader implements Loader {
canLoadSync(pointer: string, options: JsonFileLoaderOptions): boolean {
if (isValidPath(pointer)) {
if (FILE_EXTENSIONS.find(extension => pointer.endsWith(extension))) {
const normalizedFilePath = isAbsolute(pointer) ? pointer : resolve(options.cwd || cwd(), pointer);
const normalizedFilePath = isAbsolute(pointer) ? pointer : resolve(options.cwd || processCwd(), pointer);
return existsSync(normalizedFilePath);
}
}

return false;
}

private _buildGlobs(glob: string, options: JsonFileLoaderOptions) {
const ignores = asArray(options.ignore || []);
const globs = [unixify(glob), ...ignores.map(v => buildIgnoreGlob(unixify(v)))];
return globs;
}

async resolveGlobs(glob: string, options: JsonFileLoaderOptions) {
const globs = this._buildGlobs(glob, options);
const result = await globby(globs, createGlobbyOptions(options));
return result;
}

resolveGlobsSync(glob: string, options: JsonFileLoaderOptions) {
const globs = this._buildGlobs(glob, options);
const result = globby.sync(globs, createGlobbyOptions(options));
return result;
}

async load(pointer: string, options: JsonFileLoaderOptions): Promise<Source[]> {
const normalizedFilePath = isAbsolute(pointer) ? pointer : resolve(options.cwd || cwd(), pointer);
if (!(await this.canLoad(normalizedFilePath, options))) {
return [];
}
const resolvedPaths = await this.resolveGlobs(pointer, options);
const finalResult: Source[] = [];

try {
const jsonContent: string = await readFile(normalizedFilePath, { encoding: 'utf8' });
return [parseGraphQLJSON(pointer, jsonContent, options)];
} catch (e) {
throw new Error(`Unable to read JSON file: ${normalizedFilePath}: ${e.message || /* istanbul ignore next */ e}`);
}
await Promise.all(
resolvedPaths.map(async path => {
if (await this.canLoad(path, options)) {
const normalizedFilePath = isAbsolute(path) ? path : resolve(options.cwd || processCwd(), path);
const rawSDL: string = await readFile(normalizedFilePath, { encoding: 'utf8' });
finalResult.push(this.handleFileContent(normalizedFilePath, rawSDL, options));
}
})
);
return finalResult;
}

loadSync(pointer: string, options: JsonFileLoaderOptions): Source[] {
const normalizedFilePath = isAbsolute(pointer) ? pointer : resolve(options.cwd || cwd(), pointer);
if (!this.canLoadSync(normalizedFilePath, options)) {
return [];
const resolvedPaths = this.resolveGlobsSync(pointer, options);
const finalResult: Source[] = [];
for (const path of resolvedPaths) {
if (this.canLoadSync(path, options)) {
const normalizedFilePath = isAbsolute(path) ? path : resolve(options.cwd || processCwd(), path);
const rawSDL = readFileSync(normalizedFilePath, { encoding: 'utf8' });
finalResult.push(this.handleFileContent(normalizedFilePath, rawSDL, options));
}
}
return finalResult;
}

handleFileContent(normalizedFilePath: string, rawSDL: string, options: JsonFileLoaderOptions): Source {
try {
const jsonContent = readFileSync(normalizedFilePath, 'utf8');
return [parseGraphQLJSON(pointer, jsonContent, options)];
return parseGraphQLJSON(normalizedFilePath, rawSDL, options);
} catch (e) {
throw new Error(`Unable to read JSON file: ${normalizedFilePath}: ${e.message || /* istanbul ignore next */ e}`);
}
Expand Down
7 changes: 6 additions & 1 deletion packages/loaders/json-file/tests/loader.spec.ts
Expand Up @@ -56,8 +56,13 @@ describe('JsonFileLoader', () => {
expect(result.document).toBeDefined();
});

it('should load multiple files from glob expression', async () => {
const results = await load(join(process.cwd(), getPointer('*.json')), {});
expect(results).toHaveLength(2);
})

it('should throw when the file content is malformed', async () => {
await expect(load(getPointer('malformed.json'), {})).rejects.toThrowError('Unable to read JSON file');
await expect(load(getPointer('failing/malformed.json'), {})).rejects.toThrowError('Unable to read JSON file');
});
it('should skip file it cannot load', async () => {
const result = await load(getPointer('id_do_not_exist.json'), {});
Expand Down

1 comment on commit 4247b02

@vercel
Copy link

@vercel vercel bot commented on 4247b02 Aug 12, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.