@khanacademy/graphql-flow 3.0.0 → 3.1.0
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/.eslintrc.js +15 -4
- package/.github/workflows/changeset-release.yml +3 -5
- package/.github/workflows/pr-checks.yml +17 -17
- package/.prettierrc +2 -2
- package/.vscode/settings.json +6 -0
- package/CHANGELOG.md +12 -0
- package/dist/cli/config.js +6 -15
- package/dist/cli/run.js +31 -37
- package/dist/enums.js +9 -9
- package/dist/generateResponseType.js +36 -40
- package/dist/generateTypeFiles.js +10 -10
- package/dist/generateVariablesType.js +12 -13
- package/dist/index.js +4 -6
- package/dist/parser/parse.js +47 -53
- package/dist/parser/resolve.js +16 -16
- package/dist/parser/utils.js +25 -11
- package/dist/schemaFromIntrospectionData.js +5 -5
- package/dist/utils.js +8 -8
- package/package.json +12 -13
- package/src/__test__/generateTypeFileContents.test.ts +26 -25
- package/src/__test__/graphql-flow.test.ts +32 -33
- package/src/__test__/processPragmas.test.ts +14 -13
- package/src/cli/__test__/config.test.ts +51 -56
- package/src/cli/config.ts +23 -20
- package/src/cli/run.ts +45 -46
- package/src/enums.ts +17 -17
- package/src/generateResponseType.ts +120 -91
- package/src/generateTypeFiles.ts +24 -22
- package/src/generateVariablesType.ts +20 -20
- package/src/index.ts +28 -29
- package/src/parser/__test__/parse.test.ts +114 -23
- package/src/parser/__test__/utils.test.ts +80 -0
- package/src/parser/parse.ts +122 -106
- package/src/parser/resolve.ts +61 -34
- package/src/parser/utils.ts +28 -11
- package/src/schemaFromIntrospectionData.ts +10 -8
- package/src/types.ts +57 -53
- package/src/utils.ts +30 -16
- package/tools/find-files-with-gql.ts +7 -11
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import generate from
|
|
2
|
-
import * as babelTypes from
|
|
3
|
-
import type {OperationDefinitionNode, TypeNode} from
|
|
4
|
-
import type {IntrospectionInputTypeRef} from
|
|
5
|
-
import {builtinScalars, enumTypeToFlow, scalarTypeToFlow} from
|
|
6
|
-
import {nullableType, isnNullableType, objectTypeFromProperties} from
|
|
7
|
-
import type {Context, Schema} from
|
|
1
|
+
import generate from "@babel/generator";
|
|
2
|
+
import * as babelTypes from "@babel/types";
|
|
3
|
+
import type {OperationDefinitionNode, TypeNode} from "graphql/language/ast";
|
|
4
|
+
import type {IntrospectionInputTypeRef} from "graphql";
|
|
5
|
+
import {builtinScalars, enumTypeToFlow, scalarTypeToFlow} from "./enums";
|
|
6
|
+
import {nullableType, isnNullableType, objectTypeFromProperties} from "./utils";
|
|
7
|
+
import type {Context, Schema} from "./types";
|
|
8
8
|
import {
|
|
9
9
|
liftLeadingPropertyComments,
|
|
10
10
|
maybeAddDescriptionComment,
|
|
11
11
|
transferLeadingComments,
|
|
12
|
-
} from
|
|
12
|
+
} from "./utils";
|
|
13
13
|
|
|
14
14
|
export const inputObjectToFlow = (
|
|
15
15
|
ctx: Context,
|
|
@@ -59,7 +59,7 @@ export const inputRefToFlow = (
|
|
|
59
59
|
ctx: Context,
|
|
60
60
|
inputRef: IntrospectionInputTypeRef,
|
|
61
61
|
): babelTypes.TSType => {
|
|
62
|
-
if (inputRef.kind ===
|
|
62
|
+
if (inputRef.kind === "NON_NULL") {
|
|
63
63
|
return _inputRefToFlow(ctx, inputRef.ofType);
|
|
64
64
|
}
|
|
65
65
|
const result = _inputRefToFlow(ctx, inputRef);
|
|
@@ -70,18 +70,18 @@ const _inputRefToFlow = (
|
|
|
70
70
|
ctx: Context,
|
|
71
71
|
inputRef: IntrospectionInputTypeRef,
|
|
72
72
|
): babelTypes.TSType => {
|
|
73
|
-
if (inputRef.kind ===
|
|
73
|
+
if (inputRef.kind === "SCALAR") {
|
|
74
74
|
return scalarTypeToFlow(ctx, inputRef.name);
|
|
75
75
|
}
|
|
76
|
-
if (inputRef.kind ===
|
|
76
|
+
if (inputRef.kind === "ENUM") {
|
|
77
77
|
return enumTypeToFlow(ctx, inputRef.name);
|
|
78
78
|
}
|
|
79
|
-
if (inputRef.kind ===
|
|
79
|
+
if (inputRef.kind === "INPUT_OBJECT") {
|
|
80
80
|
return inputObjectToFlow(ctx, inputRef.name);
|
|
81
81
|
}
|
|
82
|
-
if (inputRef.kind ===
|
|
82
|
+
if (inputRef.kind === "LIST") {
|
|
83
83
|
return babelTypes.tsTypeReference(
|
|
84
|
-
babelTypes.identifier(
|
|
84
|
+
babelTypes.identifier("ReadonlyArray"),
|
|
85
85
|
babelTypes.tsTypeParameterInstantiation([
|
|
86
86
|
inputRefToFlow(ctx, inputRef.ofType),
|
|
87
87
|
]),
|
|
@@ -93,7 +93,7 @@ const _inputRefToFlow = (
|
|
|
93
93
|
};
|
|
94
94
|
|
|
95
95
|
const variableToFlow = (ctx: Context, type: TypeNode): babelTypes.TSType => {
|
|
96
|
-
if (type.kind ===
|
|
96
|
+
if (type.kind === "NonNullType") {
|
|
97
97
|
return _variableToFlow(ctx, type.type);
|
|
98
98
|
}
|
|
99
99
|
const result = _variableToFlow(ctx, type);
|
|
@@ -101,7 +101,7 @@ const variableToFlow = (ctx: Context, type: TypeNode): babelTypes.TSType => {
|
|
|
101
101
|
};
|
|
102
102
|
|
|
103
103
|
const _variableToFlow = (ctx: Context, type: TypeNode): babelTypes.TSType => {
|
|
104
|
-
if (type.kind ===
|
|
104
|
+
if (type.kind === "NamedType") {
|
|
105
105
|
if (builtinScalars[type.name.value]) {
|
|
106
106
|
return scalarTypeToFlow(ctx, type.name.value);
|
|
107
107
|
}
|
|
@@ -116,16 +116,16 @@ const _variableToFlow = (ctx: Context, type: TypeNode): babelTypes.TSType => {
|
|
|
116
116
|
}
|
|
117
117
|
return inputObjectToFlow(ctx, type.name.value);
|
|
118
118
|
}
|
|
119
|
-
if (type.kind ===
|
|
119
|
+
if (type.kind === "ListType") {
|
|
120
120
|
return babelTypes.tsTypeReference(
|
|
121
|
-
babelTypes.identifier(
|
|
121
|
+
babelTypes.identifier("ReadonlyArray"),
|
|
122
122
|
babelTypes.tsTypeParameterInstantiation([
|
|
123
123
|
variableToFlow(ctx, type.type),
|
|
124
124
|
]),
|
|
125
125
|
);
|
|
126
126
|
}
|
|
127
127
|
return babelTypes.tsLiteralType(
|
|
128
|
-
babelTypes.stringLiteral(
|
|
128
|
+
babelTypes.stringLiteral("UNKNOWN" + JSON.stringify(type)),
|
|
129
129
|
);
|
|
130
130
|
};
|
|
131
131
|
|
|
@@ -142,5 +142,5 @@ export const generateVariablesType = (
|
|
|
142
142
|
);
|
|
143
143
|
}),
|
|
144
144
|
);
|
|
145
|
-
return generate(variableObject).code;
|
|
145
|
+
return generate(variableObject).code;
|
|
146
146
|
};
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {isTruthy} from
|
|
1
|
+
import {isTruthy} from "@khanacademy/wonder-stuff-core";
|
|
2
2
|
/* eslint-disable no-console */
|
|
3
3
|
/* flow-uncovered-file */
|
|
4
4
|
/**
|
|
@@ -7,17 +7,17 @@ import {isTruthy} from '@khanacademy/wonder-stuff-core';
|
|
|
7
7
|
* It relies on `introspection-query.json` existing in this directory,
|
|
8
8
|
* which is produced by running `./tools/graphql-flow/sendIntrospection.js`.
|
|
9
9
|
*/
|
|
10
|
-
import type {DefinitionNode, DocumentNode} from
|
|
10
|
+
import type {DefinitionNode, DocumentNode} from "graphql";
|
|
11
11
|
|
|
12
|
-
import generate from
|
|
12
|
+
import generate from "@babel/generator";
|
|
13
13
|
import {
|
|
14
14
|
generateFragmentType,
|
|
15
15
|
generateResponseType,
|
|
16
|
-
} from
|
|
17
|
-
import {generateVariablesType} from
|
|
18
|
-
import type {Node} from
|
|
16
|
+
} from "./generateResponseType";
|
|
17
|
+
import {generateVariablesType} from "./generateVariablesType";
|
|
18
|
+
import type {Node} from "@babel/types";
|
|
19
19
|
|
|
20
|
-
import type {Context, Schema, GenerateConfig} from
|
|
20
|
+
import type {Context, Schema, GenerateConfig} from "./types";
|
|
21
21
|
|
|
22
22
|
const optionsToConfig = (
|
|
23
23
|
schema: Schema,
|
|
@@ -34,7 +34,7 @@ const optionsToConfig = (
|
|
|
34
34
|
} as const;
|
|
35
35
|
const fragments: Record<string, any> = {};
|
|
36
36
|
definitions.forEach((def) => {
|
|
37
|
-
if (def.kind ===
|
|
37
|
+
if (def.kind === "FragmentDefinition") {
|
|
38
38
|
fragments[def.name.value] = def;
|
|
39
39
|
}
|
|
40
40
|
});
|
|
@@ -56,22 +56,26 @@ const optionsToConfig = (
|
|
|
56
56
|
export class FlowGenerationError extends Error {
|
|
57
57
|
messages: Array<string>;
|
|
58
58
|
constructor(errors: Array<string>) {
|
|
59
|
-
super(`Graphql-flow type generation failed! ${errors.join(
|
|
59
|
+
super(`Graphql-flow type generation failed! ${errors.join("; ")}`);
|
|
60
60
|
this.messages = errors;
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
export const documentToFlowTypes = (
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
64
|
+
export const documentToFlowTypes = (
|
|
65
|
+
document: DocumentNode,
|
|
66
|
+
schema: Schema,
|
|
67
|
+
options?: GenerateConfig,
|
|
68
|
+
): ReadonlyArray<{
|
|
69
|
+
name: string;
|
|
70
|
+
typeName: string;
|
|
71
|
+
code: string;
|
|
72
|
+
isFragment?: boolean;
|
|
69
73
|
extraTypes: {
|
|
70
|
-
[key: string]: string
|
|
71
|
-
}
|
|
74
|
+
[key: string]: string;
|
|
75
|
+
};
|
|
72
76
|
experimentalEnums: {
|
|
73
|
-
[key: string]: string
|
|
74
|
-
}
|
|
77
|
+
[key: string]: string;
|
|
78
|
+
};
|
|
75
79
|
}> => {
|
|
76
80
|
const errors: Array<string> = [];
|
|
77
81
|
const config = optionsToConfig(
|
|
@@ -82,7 +86,7 @@ export const documentToFlowTypes = (document: DocumentNode, schema: Schema, opti
|
|
|
82
86
|
);
|
|
83
87
|
const result = document.definitions
|
|
84
88
|
.map((item) => {
|
|
85
|
-
if (item.kind ===
|
|
89
|
+
if (item.kind === "FragmentDefinition") {
|
|
86
90
|
const name = item.name.value;
|
|
87
91
|
const types: Record<string, any> = {};
|
|
88
92
|
const code = `export type ${name} = ${generateFragmentType(
|
|
@@ -112,8 +116,8 @@ export const documentToFlowTypes = (document: DocumentNode, schema: Schema, opti
|
|
|
112
116
|
};
|
|
113
117
|
}
|
|
114
118
|
if (
|
|
115
|
-
item.kind ===
|
|
116
|
-
(item.operation ===
|
|
119
|
+
item.kind === "OperationDefinition" &&
|
|
120
|
+
(item.operation === "query" || item.operation === "mutation") &&
|
|
117
121
|
item.name
|
|
118
122
|
) {
|
|
119
123
|
const types: Record<string, any> = {};
|
|
@@ -150,18 +154,13 @@ export const documentToFlowTypes = (document: DocumentNode, schema: Schema, opti
|
|
|
150
154
|
return result;
|
|
151
155
|
};
|
|
152
156
|
|
|
153
|
-
function codegenExtraTypes(
|
|
154
|
-
|
|
155
|
-
[key: string]: Node
|
|
156
|
-
},
|
|
157
|
-
): {
|
|
158
|
-
[key: string]: string
|
|
157
|
+
function codegenExtraTypes(types: {[key: string]: Node}): {
|
|
158
|
+
[key: string]: string;
|
|
159
159
|
} {
|
|
160
160
|
const extraTypes: {
|
|
161
|
-
[key: string]: string
|
|
161
|
+
[key: string]: string;
|
|
162
162
|
} = {};
|
|
163
163
|
Object.keys(types).forEach((k: string) => {
|
|
164
|
-
// eslint-disable-next-line flowtype-errors/uncovered
|
|
165
164
|
extraTypes[k] = generate(types[k]).code;
|
|
166
165
|
});
|
|
167
166
|
return extraTypes;
|
|
@@ -1,15 +1,20 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {resolveDocuments} from '../resolve';
|
|
1
|
+
import {describe, it, expect} from "@jest/globals";
|
|
3
2
|
|
|
4
|
-
import {
|
|
3
|
+
import {Config} from "../../types";
|
|
4
|
+
import {processFiles} from "../parse";
|
|
5
|
+
import {resolveDocuments} from "../resolve";
|
|
6
|
+
|
|
7
|
+
import {print} from "graphql/language/printer";
|
|
5
8
|
|
|
6
9
|
const fixtureFiles: {
|
|
7
|
-
[key: string]:
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
[key: string]:
|
|
11
|
+
| string
|
|
12
|
+
| {
|
|
13
|
+
text: string;
|
|
14
|
+
resolvedPath: string;
|
|
15
|
+
};
|
|
11
16
|
} = {
|
|
12
|
-
|
|
17
|
+
"/firstFile.js": `
|
|
13
18
|
// Note that you can import graphql-tag as
|
|
14
19
|
// something other than gql.
|
|
15
20
|
import tagme from 'graphql-tag';
|
|
@@ -35,7 +40,7 @@ const fixtureFiles: {
|
|
|
35
40
|
}
|
|
36
41
|
\`;`,
|
|
37
42
|
|
|
38
|
-
|
|
43
|
+
"/secondFile.js": `
|
|
39
44
|
import gql from 'graphql-tag';
|
|
40
45
|
import {fromFirstFile} from './firstFile.js';
|
|
41
46
|
// This import won't be followed, because it's not exported
|
|
@@ -52,7 +57,7 @@ const fixtureFiles: {
|
|
|
52
57
|
\`;
|
|
53
58
|
export {secondFragment};`,
|
|
54
59
|
|
|
55
|
-
|
|
60
|
+
"/thirdFile.js": `
|
|
56
61
|
import {fromFirstFile, alsoFirst, secondFragment} from './secondFile.js';
|
|
57
62
|
import gql from 'graphql-tag';
|
|
58
63
|
import type {someType} from './somePlace';
|
|
@@ -91,7 +96,7 @@ const fixtureFiles: {
|
|
|
91
96
|
\`;
|
|
92
97
|
}`,
|
|
93
98
|
|
|
94
|
-
|
|
99
|
+
"/invalidThings.js": `
|
|
95
100
|
import gql from 'graphql-tag';
|
|
96
101
|
// Importing a fragment from an npm module is invalid.
|
|
97
102
|
import someExternalFragment from 'somewhere';
|
|
@@ -107,7 +112,7 @@ const fixtureFiles: {
|
|
|
107
112
|
\`;
|
|
108
113
|
`,
|
|
109
114
|
|
|
110
|
-
|
|
115
|
+
"/circular.js": `
|
|
111
116
|
import gql from 'graphql-tag';
|
|
112
117
|
export {otherThing} from './invalidReferences.js';
|
|
113
118
|
import {one} from './invalidReferences.js';
|
|
@@ -119,7 +124,7 @@ const fixtureFiles: {
|
|
|
119
124
|
\`;
|
|
120
125
|
`,
|
|
121
126
|
|
|
122
|
-
|
|
127
|
+
"/invalidReferences.js": `
|
|
123
128
|
import gql from 'graphql-tag';
|
|
124
129
|
import {otherThing, two, doesntExist} from './circular.js';
|
|
125
130
|
// 'otherThing' is imported circularly
|
|
@@ -149,13 +154,39 @@ const getFileSource = (name: string) => {
|
|
|
149
154
|
return fixtureFiles[name];
|
|
150
155
|
};
|
|
151
156
|
|
|
152
|
-
describe(
|
|
153
|
-
it(
|
|
154
|
-
const
|
|
157
|
+
describe("processing fragments in various ways", () => {
|
|
158
|
+
it("should work", () => {
|
|
159
|
+
const config: Config = {
|
|
160
|
+
crawl: {
|
|
161
|
+
root: "/here/we/crawl",
|
|
162
|
+
},
|
|
163
|
+
generate: {
|
|
164
|
+
match: [/\.fixture\.js$/],
|
|
165
|
+
exclude: [
|
|
166
|
+
"_test\\.js$",
|
|
167
|
+
"\\bcourse-editor-package\\b",
|
|
168
|
+
"\\.fixture\\.js$",
|
|
169
|
+
"\\b__flowtests__\\b",
|
|
170
|
+
"\\bcourse-editor\\b",
|
|
171
|
+
],
|
|
172
|
+
readOnlyArray: false,
|
|
173
|
+
regenerateCommand: "make gqlflow",
|
|
174
|
+
scalars: {
|
|
175
|
+
JSONString: "string",
|
|
176
|
+
KALocale: "string",
|
|
177
|
+
NaiveDateTime: "string",
|
|
178
|
+
},
|
|
179
|
+
splitTypes: true,
|
|
180
|
+
generatedDirectory: "__graphql-types__",
|
|
181
|
+
exportAllObjectTypes: true,
|
|
182
|
+
schemaFilePath: "./composed_schema.graphql",
|
|
183
|
+
},
|
|
184
|
+
};
|
|
185
|
+
const files = processFiles(["/thirdFile.js"], config, getFileSource);
|
|
155
186
|
Object.keys(files).forEach((k: any) => {
|
|
156
187
|
expect(files[k].errors).toEqual([]);
|
|
157
188
|
});
|
|
158
|
-
const {resolved, errors} = resolveDocuments(files);
|
|
189
|
+
const {resolved, errors} = resolveDocuments(files, config);
|
|
159
190
|
expect(errors).toEqual([]);
|
|
160
191
|
const printed: Record<string, any> = {};
|
|
161
192
|
Object.keys(resolved).map(
|
|
@@ -215,9 +246,39 @@ describe('processing fragments in various ways', () => {
|
|
|
215
246
|
`);
|
|
216
247
|
});
|
|
217
248
|
|
|
218
|
-
it(
|
|
219
|
-
const
|
|
220
|
-
|
|
249
|
+
it("should flag things it doesnt support", () => {
|
|
250
|
+
const config: Config = {
|
|
251
|
+
crawl: {
|
|
252
|
+
root: "/here/we/crawl",
|
|
253
|
+
},
|
|
254
|
+
generate: {
|
|
255
|
+
match: [/\.fixture\.js$/],
|
|
256
|
+
exclude: [
|
|
257
|
+
"_test\\.js$",
|
|
258
|
+
"\\bcourse-editor-package\\b",
|
|
259
|
+
"\\.fixture\\.js$",
|
|
260
|
+
"\\b__flowtests__\\b",
|
|
261
|
+
"\\bcourse-editor\\b",
|
|
262
|
+
],
|
|
263
|
+
readOnlyArray: false,
|
|
264
|
+
regenerateCommand: "make gqlflow",
|
|
265
|
+
scalars: {
|
|
266
|
+
JSONString: "string",
|
|
267
|
+
KALocale: "string",
|
|
268
|
+
NaiveDateTime: "string",
|
|
269
|
+
},
|
|
270
|
+
splitTypes: true,
|
|
271
|
+
generatedDirectory: "__graphql-types__",
|
|
272
|
+
exportAllObjectTypes: true,
|
|
273
|
+
schemaFilePath: "./composed_schema.graphql",
|
|
274
|
+
},
|
|
275
|
+
};
|
|
276
|
+
const files = processFiles(
|
|
277
|
+
["/invalidThings.js"],
|
|
278
|
+
config,
|
|
279
|
+
getFileSource,
|
|
280
|
+
);
|
|
281
|
+
expect(files["/invalidThings.js"].errors.map((m: any) => m.message))
|
|
221
282
|
.toMatchInlineSnapshot(`
|
|
222
283
|
Array [
|
|
223
284
|
"Unable to resolve someExternalFragment",
|
|
@@ -227,12 +288,42 @@ describe('processing fragments in various ways', () => {
|
|
|
227
288
|
`);
|
|
228
289
|
});
|
|
229
290
|
|
|
230
|
-
it(
|
|
231
|
-
const
|
|
291
|
+
it("should flag resolution errors", () => {
|
|
292
|
+
const config: Config = {
|
|
293
|
+
crawl: {
|
|
294
|
+
root: "/here/we/crawl",
|
|
295
|
+
},
|
|
296
|
+
generate: {
|
|
297
|
+
match: [/\.fixture\.js$/],
|
|
298
|
+
exclude: [
|
|
299
|
+
"_test\\.js$",
|
|
300
|
+
"\\bcourse-editor-package\\b",
|
|
301
|
+
"\\.fixture\\.js$",
|
|
302
|
+
"\\b__flowtests__\\b",
|
|
303
|
+
"\\bcourse-editor\\b",
|
|
304
|
+
],
|
|
305
|
+
readOnlyArray: false,
|
|
306
|
+
regenerateCommand: "make gqlflow",
|
|
307
|
+
scalars: {
|
|
308
|
+
JSONString: "string",
|
|
309
|
+
KALocale: "string",
|
|
310
|
+
NaiveDateTime: "string",
|
|
311
|
+
},
|
|
312
|
+
splitTypes: true,
|
|
313
|
+
generatedDirectory: "__graphql-types__",
|
|
314
|
+
exportAllObjectTypes: true,
|
|
315
|
+
schemaFilePath: "./composed_schema.graphql",
|
|
316
|
+
},
|
|
317
|
+
};
|
|
318
|
+
const files = processFiles(
|
|
319
|
+
["/invalidReferences.js"],
|
|
320
|
+
config,
|
|
321
|
+
getFileSource,
|
|
322
|
+
);
|
|
232
323
|
Object.keys(files).forEach((k: any) => {
|
|
233
324
|
expect(files[k].errors).toEqual([]);
|
|
234
325
|
});
|
|
235
|
-
const {resolved, errors} = resolveDocuments(files);
|
|
326
|
+
const {resolved, errors} = resolveDocuments(files, config);
|
|
236
327
|
expect(errors.map((m: any) => m.message)).toMatchInlineSnapshot(`
|
|
237
328
|
Array [
|
|
238
329
|
"Circular import /circular.js -> /invalidReferences.js -> /circular.js",
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import {describe, it, expect, jest} from "@jest/globals";
|
|
3
|
+
import type {Config} from "../../types";
|
|
4
|
+
|
|
5
|
+
import {getPathWithExtension} from "../utils";
|
|
6
|
+
|
|
7
|
+
const generate = {
|
|
8
|
+
match: [/\.fixture\.js$/],
|
|
9
|
+
exclude: [
|
|
10
|
+
"_test\\.js$",
|
|
11
|
+
"\\bcourse-editor-package\\b",
|
|
12
|
+
"\\.fixture\\.js$",
|
|
13
|
+
"\\b__flowtests__\\b",
|
|
14
|
+
"\\bcourse-editor\\b",
|
|
15
|
+
],
|
|
16
|
+
readOnlyArray: false,
|
|
17
|
+
regenerateCommand: "make gqlflow",
|
|
18
|
+
scalars: {
|
|
19
|
+
JSONString: "string",
|
|
20
|
+
KALocale: "string",
|
|
21
|
+
NaiveDateTime: "string",
|
|
22
|
+
},
|
|
23
|
+
splitTypes: true,
|
|
24
|
+
generatedDirectory: "__graphql-types__",
|
|
25
|
+
exportAllObjectTypes: true,
|
|
26
|
+
schemaFilePath: "./composed_schema.graphql",
|
|
27
|
+
} as const;
|
|
28
|
+
|
|
29
|
+
const config: Config = {
|
|
30
|
+
crawl: {
|
|
31
|
+
root: "/here/we/crawl",
|
|
32
|
+
},
|
|
33
|
+
generate: [
|
|
34
|
+
{...generate, match: [/^static/], exportAllObjectTypes: false},
|
|
35
|
+
generate,
|
|
36
|
+
],
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
describe("getPathWithExtension", () => {
|
|
40
|
+
it("should handle a basic missing extension", () => {
|
|
41
|
+
// Arrange
|
|
42
|
+
jest.spyOn(fs, "existsSync").mockImplementation((path) =>
|
|
43
|
+
typeof path === "string" ? path.endsWith(".js") : false,
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
// Act
|
|
47
|
+
const result = getPathWithExtension("/path/to/file", config);
|
|
48
|
+
|
|
49
|
+
// Assert
|
|
50
|
+
expect(result).toBe("/path/to/file.js");
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("returns an empty string if no file is found", () => {
|
|
54
|
+
// Arrange
|
|
55
|
+
jest.spyOn(fs, "existsSync").mockImplementation((path) => false);
|
|
56
|
+
|
|
57
|
+
// Act
|
|
58
|
+
const result = getPathWithExtension("/path/to/file", config);
|
|
59
|
+
|
|
60
|
+
// Assert
|
|
61
|
+
expect(result).toBe("");
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("maps aliases to their correct value", () => {
|
|
65
|
+
// Arrange
|
|
66
|
+
jest.spyOn(fs, "existsSync").mockImplementation((path) =>
|
|
67
|
+
typeof path === "string" ? path.endsWith(".js") : false,
|
|
68
|
+
);
|
|
69
|
+
const tmpConfig: Config = {
|
|
70
|
+
...config,
|
|
71
|
+
alias: [{find: "~", replacement: "../../some/prefix"}],
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// Act
|
|
75
|
+
const result = getPathWithExtension("~/dir/file", tmpConfig);
|
|
76
|
+
|
|
77
|
+
// Assert
|
|
78
|
+
expect(result).toBe("../../some/prefix/dir/file.js");
|
|
79
|
+
});
|
|
80
|
+
});
|