@khanacademy/graphql-flow 4.0.0 → 4.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @khanacademy/graphql-flow
2
2
 
3
+ ## 4.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 7fd8814: Improve unresolved import errors
8
+
3
9
  ## 4.0.0
4
10
 
5
11
  ### Major Changes
@@ -82,7 +82,8 @@ const processFile = (filePath, contents, config) => {
82
82
  exports: {},
83
83
  exportAlls: [],
84
84
  locals: {},
85
- errors: []
85
+ errors: [],
86
+ unresolvedImports: {}
86
87
  };
87
88
  const text = typeof contents === "string" ? contents : contents.text;
88
89
  const plugins = filePath.endsWith("x") ? ["typescript", "jsx"] : ["typescript"];
@@ -96,6 +97,23 @@ const processFile = (filePath, contents, config) => {
96
97
  ast.program.body.forEach(toplevel => {
97
98
  var _toplevel$declaration;
98
99
  if (toplevel.type === "ImportDeclaration") {
100
+ const isUnresolvedModule = !toplevel.source.value.startsWith(".") && !_path.default.isAbsolute(toplevel.source.value) && toplevel.source.value !== "graphql-tag";
101
+ if (isUnresolvedModule) {
102
+ toplevel.specifiers.forEach(spec => {
103
+ if (spec.type === "ImportSpecifier" || spec.type === "ImportDefaultSpecifier") {
104
+ var _spec$start, _spec$end, _spec$loc$start$line, _spec$loc;
105
+ result.unresolvedImports[spec.local.name] = {
106
+ source: toplevel.source.value,
107
+ loc: {
108
+ start: (_spec$start = spec.start) !== null && _spec$start !== void 0 ? _spec$start : -1,
109
+ end: (_spec$end = spec.end) !== null && _spec$end !== void 0 ? _spec$end : -1,
110
+ line: (_spec$loc$start$line = (_spec$loc = spec.loc) === null || _spec$loc === void 0 ? void 0 : _spec$loc.start.line) !== null && _spec$loc$start$line !== void 0 ? _spec$loc$start$line : -1,
111
+ path: filePath
112
+ }
113
+ };
114
+ }
115
+ });
116
+ }
99
117
  const newLocals = getLocals(toplevel, filePath);
100
118
  if (newLocals) {
101
119
  Object.keys(newLocals).forEach(k => {
@@ -119,15 +137,15 @@ const processFile = (filePath, contents, config) => {
119
137
  const importPath = resolvedPath !== null && resolvedPath !== void 0 ? resolvedPath : source.value;
120
138
  (_toplevel$specifiers = toplevel.specifiers) === null || _toplevel$specifiers === void 0 || _toplevel$specifiers.forEach(spec => {
121
139
  if (spec.type === "ExportSpecifier" && spec.exported.type === "Identifier") {
122
- var _spec$start, _spec$end, _spec$loc$start$line, _spec$loc;
140
+ var _spec$start2, _spec$end2, _spec$loc$start$line2, _spec$loc2;
123
141
  result.exports[spec.exported.name] = {
124
142
  type: "import",
125
143
  name: spec.local.name,
126
144
  path: importPath,
127
145
  loc: {
128
- start: (_spec$start = spec.start) !== null && _spec$start !== void 0 ? _spec$start : -1,
129
- end: (_spec$end = spec.end) !== null && _spec$end !== void 0 ? _spec$end : -1,
130
- line: (_spec$loc$start$line = (_spec$loc = spec.loc) === null || _spec$loc === void 0 ? void 0 : _spec$loc.start.line) !== null && _spec$loc$start$line !== void 0 ? _spec$loc$start$line : -1,
146
+ start: (_spec$start2 = spec.start) !== null && _spec$start2 !== void 0 ? _spec$start2 : -1,
147
+ end: (_spec$end2 = spec.end) !== null && _spec$end2 !== void 0 ? _spec$end2 : -1,
148
+ line: (_spec$loc$start$line2 = (_spec$loc2 = spec.loc) === null || _spec$loc2 === void 0 ? void 0 : _spec$loc2.start.line) !== null && _spec$loc$start$line2 !== void 0 ? _spec$loc$start$line2 : -1,
131
149
  path: filePath
132
150
  }
133
151
  };
@@ -268,14 +286,23 @@ const processTemplate = (tpl, result, getTemplate) => {
268
286
  return null;
269
287
  }
270
288
  if (!result.locals[expr.name]) {
289
+ var _result$unresolvedImp;
271
290
  if (getTemplate) {
272
291
  const found = getTemplate(expr.name);
273
292
  return found;
274
293
  }
275
- result.errors.push({
276
- loc,
277
- message: `Unable to resolve ${expr.name}`
278
- });
294
+ const unresolved = (_result$unresolvedImp = result.unresolvedImports) === null || _result$unresolvedImp === void 0 ? void 0 : _result$unresolvedImp[expr.name];
295
+ if (unresolved) {
296
+ result.errors.push({
297
+ loc: unresolved.loc,
298
+ message: `Unable to resolve import ${expr.name} from "${unresolved.source}" at ${unresolved.loc.path}:${unresolved.loc.line}.`
299
+ });
300
+ } else {
301
+ result.errors.push({
302
+ loc,
303
+ message: `Unable to resolve ${expr.name}`
304
+ });
305
+ }
279
306
  return null;
280
307
  }
281
308
  return result.locals[expr.name];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@khanacademy/graphql-flow",
3
- "version": "4.0.0",
3
+ "version": "4.0.1",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/Khan/graphql-flow.git"
@@ -356,7 +356,7 @@ describe("processing fragments in various ways", () => {
356
356
  expect(files["/invalidThings.js"].errors.map((m: any) => m.message))
357
357
  .toMatchInlineSnapshot(`
358
358
  Array [
359
- "Unable to resolve someExternalFragment",
359
+ "Unable to resolve import someExternalFragment from \\"somewhere\\" at /invalidThings.js:4.",
360
360
  "Unable to resolve someUndefinedFragment",
361
361
  "Template literal interpolation must be an identifier",
362
362
  ]
@@ -91,6 +91,12 @@ export type FileResult = {
91
91
  loc: Loc;
92
92
  message: string;
93
93
  }>;
94
+ unresolvedImports?: {
95
+ [key: string]: {
96
+ source: string;
97
+ loc: Loc;
98
+ };
99
+ };
94
100
  };
95
101
 
96
102
  export type Files = {
@@ -164,6 +170,7 @@ export const processFile = (
164
170
  exportAlls: [],
165
171
  locals: {},
166
172
  errors: [],
173
+ unresolvedImports: {},
167
174
  };
168
175
  const text = typeof contents === "string" ? contents : contents.text;
169
176
  const plugins: Array<ParserPlugin> = filePath.endsWith("x")
@@ -181,6 +188,28 @@ export const processFile = (
181
188
 
182
189
  ast.program.body.forEach((toplevel) => {
183
190
  if (toplevel.type === "ImportDeclaration") {
191
+ const isUnresolvedModule =
192
+ !toplevel.source.value.startsWith(".") &&
193
+ !path.isAbsolute(toplevel.source.value) &&
194
+ toplevel.source.value !== "graphql-tag";
195
+ if (isUnresolvedModule) {
196
+ toplevel.specifiers.forEach((spec) => {
197
+ if (
198
+ spec.type === "ImportSpecifier" ||
199
+ spec.type === "ImportDefaultSpecifier"
200
+ ) {
201
+ result.unresolvedImports![spec.local.name] = {
202
+ source: toplevel.source.value,
203
+ loc: {
204
+ start: spec.start ?? -1,
205
+ end: spec.end ?? -1,
206
+ line: spec.loc?.start.line ?? -1,
207
+ path: filePath,
208
+ },
209
+ };
210
+ }
211
+ });
212
+ }
184
213
  const newLocals = getLocals(toplevel, filePath);
185
214
  if (newLocals) {
186
215
  Object.keys(newLocals).forEach((k) => {
@@ -381,10 +410,18 @@ const processTemplate = (
381
410
  const found = getTemplate(expr.name);
382
411
  return found;
383
412
  }
384
- result.errors.push({
385
- loc,
386
- message: `Unable to resolve ${expr.name}`,
387
- });
413
+ const unresolved = result.unresolvedImports?.[expr.name];
414
+ if (unresolved) {
415
+ result.errors.push({
416
+ loc: unresolved.loc,
417
+ message: `Unable to resolve import ${expr.name} from "${unresolved.source}" at ${unresolved.loc.path}:${unresolved.loc.line}.`,
418
+ });
419
+ } else {
420
+ result.errors.push({
421
+ loc,
422
+ message: `Unable to resolve ${expr.name}`,
423
+ });
424
+ }
388
425
  return null;
389
426
  }
390
427
  return result.locals[expr.name];