UNPKG

10.8 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const utils_js_1 = require("./utils.js");
4const types_1 = require("@babel/types");
5const utils_1 = require("@graphql-tools/utils");
6const defaults = {
7 modules: [
8 {
9 name: 'graphql-tag',
10 },
11 {
12 name: 'graphql-tag.macro',
13 },
14 {
15 name: '@apollo/client',
16 identifier: 'gql',
17 },
18 {
19 name: '@apollo/client/core',
20 identifier: 'gql',
21 },
22 {
23 name: 'apollo-angular',
24 identifier: 'gql',
25 },
26 {
27 name: 'gatsby',
28 identifier: 'graphql',
29 },
30 {
31 name: 'apollo-server-express',
32 identifier: 'gql',
33 },
34 {
35 name: 'apollo-server',
36 identifier: 'gql',
37 },
38 {
39 name: 'react-relay',
40 identifier: 'graphql',
41 },
42 {
43 name: 'react-relay/hooks',
44 identifier: 'graphql',
45 },
46 {
47 name: 'relay-runtime',
48 identifier: 'graphql',
49 },
50 {
51 name: 'babel-plugin-relay/macro',
52 identifier: 'graphql',
53 },
54 {
55 name: 'apollo-boost',
56 identifier: 'gql',
57 },
58 {
59 name: 'apollo-server-koa',
60 identifier: 'gql',
61 },
62 {
63 name: 'apollo-server-hapi',
64 identifier: 'gql',
65 },
66 {
67 name: 'apollo-server-fastify',
68 identifier: 'gql',
69 },
70 {
71 name: ' apollo-server-lambda',
72 identifier: 'gql',
73 },
74 {
75 name: 'apollo-server-micro',
76 identifier: 'gql',
77 },
78 {
79 name: 'apollo-server-azure-functions',
80 identifier: 'gql',
81 },
82 {
83 name: 'apollo-server-cloud-functions',
84 identifier: 'gql',
85 },
86 {
87 name: 'apollo-server-cloudflare',
88 identifier: 'gql',
89 },
90 {
91 name: 'graphql.macro',
92 identifier: 'gql',
93 },
94 {
95 name: '@urql/core',
96 identifier: 'gql',
97 },
98 {
99 name: 'urql',
100 identifier: 'gql',
101 },
102 {
103 name: '@urql/preact',
104 identifier: 'gql',
105 },
106 {
107 name: '@urql/svelte',
108 identifier: 'gql',
109 },
110 {
111 name: '@urql/vue',
112 identifier: 'gql',
113 },
114 ],
115 gqlMagicComment: 'graphql',
116 globalGqlIdentifierName: ['gql', 'graphql'],
117};
118exports.default = (code, out, options = {}) => {
119 // Apply defaults to options
120 let { modules = [], globalGqlIdentifierName, gqlMagicComment, } = {
121 ...defaults,
122 ...options,
123 };
124 // Prevent case related potential errors
125 gqlMagicComment = gqlMagicComment.toLowerCase();
126 // normalize `name` and `identifier` values
127 modules = modules.map(mod => {
128 return {
129 name: mod.name,
130 identifier: mod.identifier && mod.identifier.toLowerCase(),
131 };
132 });
133 globalGqlIdentifierName = (0, utils_1.asArray)(globalGqlIdentifierName).map(s => s.toLowerCase());
134 // Keep imported identifiers
135 // import gql from 'graphql-tag' -> gql
136 // import { graphql } from 'gatsby' -> graphql
137 // Will result with ['gql', 'graphql']
138 const definedIdentifierNames = [];
139 const alreadyProcessedOperationsCache = new Set();
140 // Will accumulate all template literals
141 const gqlTemplateLiterals = [];
142 // Check if package is registered
143 function isValidPackage(name) {
144 return modules.some(pkg => pkg.name && name && pkg.name.toLowerCase() === name.toLowerCase());
145 }
146 // Check if identifier is defined and imported from registered packages
147 function isValidIdentifier(name) {
148 return definedIdentifierNames.some(id => id === name) || globalGqlIdentifierName.includes(name);
149 }
150 const pluckStringFromFile = ({ start, end }) => {
151 return (0, utils_js_1.freeText)(code
152 // Slice quotes
153 .slice(start + 1, end - 1)
154 // Erase string interpolations as we gonna export everything as a single
155 // string anyway
156 .replace(/\$\{[^}]*\}/g, '')
157 .split('\\`')
158 .join('`'), options.skipIndent);
159 };
160 const addTemplateLiteralToResult = (content) => {
161 const cacheKey = `end/${content.end}/start/${content.start}/${content.content}`;
162 if (alreadyProcessedOperationsCache.has(cacheKey)) {
163 return;
164 }
165 alreadyProcessedOperationsCache.add(cacheKey);
166 gqlTemplateLiterals.push(content);
167 };
168 // Push all template literals leaded by graphql magic comment
169 // e.g. /* GraphQL */ `query myQuery {}` -> query myQuery {}
170 const pluckMagicTemplateLiteral = (node, takeExpression = false) => {
171 const leadingComments = node.leadingComments;
172 if (!leadingComments) {
173 return;
174 }
175 if (!leadingComments.length) {
176 return;
177 }
178 const leadingComment = leadingComments[leadingComments.length - 1];
179 const leadingCommentValue = leadingComment.value.trim().toLowerCase();
180 if (leadingCommentValue !== gqlMagicComment) {
181 return;
182 }
183 const nodeToUse = takeExpression ? node.expression : node;
184 const gqlTemplateLiteral = pluckStringFromFile(nodeToUse);
185 if (gqlTemplateLiteral) {
186 addTemplateLiteralToResult({
187 content: gqlTemplateLiteral,
188 loc: node.loc,
189 end: node.end,
190 start: node.start,
191 });
192 }
193 };
194 const visitor = {
195 CallExpression: {
196 enter(path) {
197 // Find the identifier name used from graphql-tag, commonJS
198 // e.g. import gql from 'graphql-tag' -> gql
199 const arg0 = path.node.arguments[0];
200 if ('name' in path.node.callee &&
201 path.node.callee.name === 'require' &&
202 'value' in arg0 &&
203 typeof arg0.value === 'string' &&
204 isValidPackage(arg0.value)) {
205 if (!(0, types_1.isVariableDeclarator)(path.parent)) {
206 return;
207 }
208 if (!(0, types_1.isIdentifier)(path.parent.id)) {
209 return;
210 }
211 definedIdentifierNames.push(path.parent.id.name);
212 return;
213 }
214 // Push strings template literals to gql calls
215 // e.g. gql(`query myQuery {}`) -> query myQuery {}
216 if ((0, types_1.isIdentifier)(path.node.callee) && isValidIdentifier(path.node.callee.name) && (0, types_1.isTemplateLiteral)(arg0)) {
217 const { start, end, loc } = arg0;
218 if (start != null && end != null && start != null && loc != null) {
219 const gqlTemplateLiteral = pluckStringFromFile({ start, end });
220 // If the entire template was made out of interpolations it should be an empty
221 // string by now and thus should be ignored
222 if (gqlTemplateLiteral) {
223 addTemplateLiteralToResult({
224 content: gqlTemplateLiteral,
225 loc,
226 end,
227 start,
228 });
229 }
230 }
231 }
232 },
233 },
234 ImportDeclaration: {
235 enter(path) {
236 // Find the identifier name used from graphql-tag, es6
237 // e.g. import gql from 'graphql-tag' -> gql
238 if (!isValidPackage(path.node.source.value)) {
239 return;
240 }
241 const moduleNode = modules.find(pkg => pkg.name.toLowerCase() === path.node.source.value.toLowerCase());
242 if (moduleNode == null) {
243 return;
244 }
245 const gqlImportSpecifier = path.node.specifiers.find((importSpecifier) => {
246 // When it's a default import and registered package has no named identifier
247 if ((0, types_1.isImportDefaultSpecifier)(importSpecifier) && !moduleNode.identifier) {
248 return true;
249 }
250 // When it's a named import that matches registered package's identifier
251 if ((0, types_1.isImportSpecifier)(importSpecifier) &&
252 'name' in importSpecifier.imported &&
253 importSpecifier.imported.name === moduleNode.identifier) {
254 return true;
255 }
256 return false;
257 });
258 if (!gqlImportSpecifier) {
259 return;
260 }
261 definedIdentifierNames.push(gqlImportSpecifier.local.name);
262 },
263 },
264 ExpressionStatement: {
265 exit(path) {
266 // Push all template literals leaded by graphql magic comment
267 // e.g. /* GraphQL */ `query myQuery {}` -> query myQuery {}
268 if (!(0, types_1.isTemplateLiteral)(path.node.expression)) {
269 return;
270 }
271 pluckMagicTemplateLiteral(path.node, true);
272 },
273 },
274 TemplateLiteral: {
275 exit(path) {
276 pluckMagicTemplateLiteral(path.node);
277 },
278 },
279 TaggedTemplateExpression: {
280 exit(path) {
281 // Push all template literals provided to the found identifier name
282 // e.g. gql `query myQuery {}` -> query myQuery {}
283 if (!(0, types_1.isIdentifier)(path.node.tag) || !isValidIdentifier(path.node.tag.name)) {
284 return;
285 }
286 const gqlTemplateLiteral = pluckStringFromFile(path.node.quasi);
287 if (gqlTemplateLiteral) {
288 addTemplateLiteralToResult({
289 content: gqlTemplateLiteral,
290 end: path.node.quasi.end,
291 start: path.node.quasi.start,
292 loc: path.node.quasi.loc,
293 });
294 }
295 },
296 },
297 exit() {
298 out.returnValue = gqlTemplateLiterals;
299 },
300 };
301 return visitor;
302};