1 | import { isSchema, parse } from 'graphql';
|
2 | import { asArray, isValidPath, parseGraphQLSDL, isDocumentNode, AggregateError, } from '@graphql-tools/utils';
|
3 | import { gqlPluckFromCodeString, gqlPluckFromCodeStringSync, } from '@graphql-tools/graphql-tag-pluck';
|
4 | import globby from 'globby';
|
5 | import unixify from 'unixify';
|
6 | import { tryToLoadFromExport, tryToLoadFromExportSync } from './load-from-module.js';
|
7 | import { isAbsolute, resolve } from 'path';
|
8 | import { cwd, env } from 'process';
|
9 | import { readFileSync, promises as fsPromises, existsSync } from 'fs';
|
10 | import { createRequire } from 'module';
|
11 | const { readFile, access } = fsPromises;
|
12 | const FILE_EXTENSIONS = ['.ts', '.tsx', '.js', '.jsx', '.vue', '.svelte'];
|
13 | function createGlobbyOptions(options) {
|
14 | return { absolute: true, ...options, ignore: [] };
|
15 | }
|
16 | const buildIgnoreGlob = (path) => `!${path}`;
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 | export class CodeFileLoader {
|
32 | constructor(config) {
|
33 | this.config = config !== null && config !== void 0 ? config : {};
|
34 | }
|
35 | getMergedOptions(options) {
|
36 | return { ...this.config, ...options };
|
37 | }
|
38 | async canLoad(pointer, options) {
|
39 | options = this.getMergedOptions(options);
|
40 | if (isValidPath(pointer)) {
|
41 | if (FILE_EXTENSIONS.find(extension => pointer.endsWith(extension))) {
|
42 | const normalizedFilePath = isAbsolute(pointer) ? pointer : resolve(options.cwd || cwd(), pointer);
|
43 | try {
|
44 | await access(normalizedFilePath);
|
45 | return true;
|
46 | }
|
47 | catch (_a) {
|
48 | return false;
|
49 | }
|
50 | }
|
51 | }
|
52 | return false;
|
53 | }
|
54 | canLoadSync(pointer, options) {
|
55 | options = this.getMergedOptions(options);
|
56 | if (isValidPath(pointer)) {
|
57 | if (FILE_EXTENSIONS.find(extension => pointer.endsWith(extension))) {
|
58 | const normalizedFilePath = isAbsolute(pointer) ? pointer : resolve(options.cwd || cwd(), pointer);
|
59 | return existsSync(normalizedFilePath);
|
60 | }
|
61 | }
|
62 | return false;
|
63 | }
|
64 | _buildGlobs(glob, options) {
|
65 | const ignores = asArray(options.ignore || []);
|
66 | const globs = [unixify(glob), ...ignores.map(v => buildIgnoreGlob(unixify(v)))];
|
67 | return globs;
|
68 | }
|
69 | async resolveGlobs(glob, options) {
|
70 | options = this.getMergedOptions(options);
|
71 | const globs = this._buildGlobs(glob, options);
|
72 | return globby(globs, createGlobbyOptions(options));
|
73 | }
|
74 | resolveGlobsSync(glob, options) {
|
75 | options = this.getMergedOptions(options);
|
76 | const globs = this._buildGlobs(glob, options);
|
77 | return globby.sync(globs, createGlobbyOptions(options));
|
78 | }
|
79 | async load(pointer, options) {
|
80 | options = this.getMergedOptions(options);
|
81 | const resolvedPaths = await this.resolveGlobs(pointer, options);
|
82 | const finalResult = [];
|
83 | const errors = [];
|
84 | await Promise.all(resolvedPaths.map(async (path) => {
|
85 | try {
|
86 | const result = await this.handleSinglePath(path, options);
|
87 | result === null || result === void 0 ? void 0 : result.forEach(result => finalResult.push(result));
|
88 | }
|
89 | catch (e) {
|
90 | if (env['DEBUG']) {
|
91 | console.error(e);
|
92 | }
|
93 | errors.push(e);
|
94 | }
|
95 | }));
|
96 | if (errors.length > 0 && (options.noSilentErrors || finalResult.length === 0)) {
|
97 | if (errors.length === 1) {
|
98 | throw errors[0];
|
99 | }
|
100 | throw new AggregateError(errors, `Reading from ${pointer} failed ; \n ` + errors.map((e) => e.message).join('\n'));
|
101 | }
|
102 | return finalResult;
|
103 | }
|
104 | loadSync(pointer, options) {
|
105 | options = this.getMergedOptions(options);
|
106 | const resolvedPaths = this.resolveGlobsSync(pointer, options);
|
107 | const finalResult = [];
|
108 | const errors = [];
|
109 | for (const path of resolvedPaths) {
|
110 | if (this.canLoadSync(path, options)) {
|
111 | try {
|
112 | const result = this.handleSinglePathSync(path, options);
|
113 | result === null || result === void 0 ? void 0 : result.forEach(result => finalResult.push(result));
|
114 | }
|
115 | catch (e) {
|
116 | if (env['DEBUG']) {
|
117 | console.error(e);
|
118 | }
|
119 | errors.push(e);
|
120 | }
|
121 | }
|
122 | }
|
123 | if (errors.length > 0 && (options.noSilentErrors || finalResult.length === 0)) {
|
124 | if (errors.length === 1) {
|
125 | throw errors[0];
|
126 | }
|
127 | throw new AggregateError(errors, `Reading from ${pointer} failed ; \n ` + errors.map((e) => e.message).join('\n'));
|
128 | }
|
129 | return finalResult;
|
130 | }
|
131 | async handleSinglePath(location, options) {
|
132 | if (!(await this.canLoad(location, options))) {
|
133 | return [];
|
134 | }
|
135 | options = this.getMergedOptions(options);
|
136 | const normalizedFilePath = ensureAbsolutePath(location, options);
|
137 | const errors = [];
|
138 | if (!options.noPluck) {
|
139 | try {
|
140 | const content = await readFile(normalizedFilePath, { encoding: 'utf-8' });
|
141 | const sources = await gqlPluckFromCodeString(normalizedFilePath, content, options.pluckConfig);
|
142 | if (sources.length) {
|
143 | return sources.map(source => ({
|
144 | rawSDL: source.body,
|
145 | document: parse(source),
|
146 | location,
|
147 | }));
|
148 | }
|
149 | }
|
150 | catch (e) {
|
151 | if (env['DEBUG']) {
|
152 | console.error(`Failed to load schema from code file "${normalizedFilePath}": ${e.message}`);
|
153 | }
|
154 | errors.push(e);
|
155 | }
|
156 | }
|
157 | if (!options.noRequire) {
|
158 | try {
|
159 | if (options && options.require) {
|
160 | await Promise.all(asArray(options.require).map(m => import(m)));
|
161 | }
|
162 | const loaded = await tryToLoadFromExport(normalizedFilePath);
|
163 | const sources = asArray(loaded)
|
164 | .map(value => resolveSource(location, value, options))
|
165 | .filter(Boolean);
|
166 | if (sources.length) {
|
167 | return sources;
|
168 | }
|
169 | }
|
170 | catch (e) {
|
171 | errors.push(e);
|
172 | }
|
173 | }
|
174 | if (errors.length > 0) {
|
175 | throw errors[0];
|
176 | }
|
177 | return [];
|
178 | }
|
179 | handleSinglePathSync(location, options) {
|
180 | if (!this.canLoadSync(location, options)) {
|
181 | return [];
|
182 | }
|
183 | options = this.getMergedOptions(options);
|
184 | const normalizedFilePath = ensureAbsolutePath(location, options);
|
185 | const errors = [];
|
186 | if (!options.noPluck) {
|
187 | try {
|
188 | const content = readFileSync(normalizedFilePath, { encoding: 'utf-8' });
|
189 | const sources = gqlPluckFromCodeStringSync(normalizedFilePath, content, options.pluckConfig);
|
190 | if (sources.length) {
|
191 | return sources.map(source => ({
|
192 | rawSDL: source.body,
|
193 | document: parse(source),
|
194 | location,
|
195 | }));
|
196 | }
|
197 | }
|
198 | catch (e) {
|
199 | if (env['DEBUG']) {
|
200 | console.error(`Failed to load schema from code file "${normalizedFilePath}": ${e.message}`);
|
201 | }
|
202 | errors.push(e);
|
203 | }
|
204 | }
|
205 | if (!options.noRequire) {
|
206 | try {
|
207 | if (options && options.require) {
|
208 | const cwdRequire = createRequire(options.cwd || cwd());
|
209 | for (const m of asArray(options.require)) {
|
210 | cwdRequire(m);
|
211 | }
|
212 | }
|
213 | const loaded = tryToLoadFromExportSync(normalizedFilePath);
|
214 | const sources = asArray(loaded)
|
215 | .map(value => resolveSource(location, value, options))
|
216 | .filter(Boolean);
|
217 | if (sources.length) {
|
218 | return sources;
|
219 | }
|
220 | }
|
221 | catch (e) {
|
222 | errors.push(e);
|
223 | }
|
224 | }
|
225 | if (errors.length > 0) {
|
226 | throw errors[0];
|
227 | }
|
228 | return null;
|
229 | }
|
230 | }
|
231 | function resolveSource(pointer, value, options) {
|
232 | if (typeof value === 'string') {
|
233 | return parseGraphQLSDL(pointer, value, options);
|
234 | }
|
235 | else if (isSchema(value)) {
|
236 | return {
|
237 | location: pointer,
|
238 | schema: value,
|
239 | };
|
240 | }
|
241 | else if (isDocumentNode(value)) {
|
242 | return {
|
243 | location: pointer,
|
244 | document: value,
|
245 | };
|
246 | }
|
247 | return null;
|
248 | }
|
249 | function ensureAbsolutePath(pointer, options) {
|
250 | return isAbsolute(pointer) ? pointer : resolve(options.cwd || cwd(), pointer);
|
251 | }
|