Skip to content

Commit c90d8c8

Browse files
committed
Refactors plugin to modularize code and improve maintainability
Modularizes core functionality by extracting large functions into separate files, removing duplicate and now-unneeded utility and definition modules. Simplifies the main entry point, clarifies code structure, and updates import paths to reflect new file organization for easier future updates and testing. Increments version to indicate breaking changes and significant refactor.
1 parent b98e68f commit c90d8c8

File tree

11 files changed

+336
-327
lines changed

11 files changed

+336
-327
lines changed

packages/gatsby-source-github-graphql/create-schema-customization.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
const { getPluginNodeTypes } = require(`./github-graphql-defs`);
2-
const { removeKey } = require("./utils");
1+
const { getPluginNodeTypes } = require(`./src/github-graphql-defs`);
2+
const { removeKey } = require("./src/utils");
33

4-
module.exports = async (...args) => {
4+
module.exports = async function createSchemaCustomization(...args) {
55
const [gatsbyNodeApis, pluginOptions] = args;
66

77
const pluginNodeTypes = getPluginNodeTypes(pluginOptions.pluginNodeTypes);
Lines changed: 3 additions & 323 deletions
Original file line numberDiff line numberDiff line change
@@ -1,329 +1,9 @@
1-
const githubOctokit = require(`./github-octokit`);
2-
const { mergeDeep, removeKey } = require(`./utils`);
3-
const { getGithubPlainResolverFields } = require(`./plugin-github-fragments`);
4-
5-
const { createRemoteFileNode } = require(`gatsby-source-filesystem`);
6-
const {
7-
getPluginNodeTypes,
8-
getGithubApiTypes,
9-
} = require("./github-graphql-defs");
10-
const { MEDIA_TYPES } = require(`./media-types`);
11-
121
exports.onPreInit = () => {};
132

14-
exports.sourceNodes = async (...args) => {
15-
const [
16-
{
17-
actions: { createNode },
18-
createContentDigest,
19-
createNodeId,
20-
getNodesByType,
21-
},
22-
userPluginOptions,
23-
] = args;
24-
25-
const pluginOptions = userPluginOptions ?? {};
26-
27-
const { token, pluginNodeTypes: userPluginNodeTypes } = pluginOptions;
28-
29-
const rawSubpluginsData = [];
30-
31-
const githubApiTypes = getGithubApiTypes();
32-
const pluginNodeTypes = getPluginNodeTypes(userPluginNodeTypes);
33-
const githubPlainResolverFields = getGithubPlainResolverFields({
34-
pluginNodeTypes,
35-
});
36-
37-
for (const plugin of pluginOptions.plugins ?? []) {
38-
const resolvedPlugin = plugin.module;
39-
40-
const subpluginOptions = { ...(plugin.pluginOptions ?? {}) };
41-
42-
// Allow all plugins to have a custom token implicitly.
43-
const { token: customToken } = subpluginOptions;
44-
45-
const graphql = githubOctokit.createGraphQLWithAuth(customToken ?? token);
46-
47-
const githubSourcePlugin = {
48-
graphql,
49-
githubPlainResolverFields,
50-
githubApiTypes,
51-
pluginNodeTypes,
52-
};
53-
54-
const subpluginArgs = [
55-
{ ...args[0], githubSourcePlugin },
56-
{
57-
...removeKey(subpluginOptions, `token`),
58-
},
59-
...args.slice(2),
60-
];
61-
62-
const pluginData = await resolvedPlugin?.sourceNodes?.(...subpluginArgs);
63-
64-
// Plugins that doesn't return anything probably created
65-
// the nodes by their own, so just skip it.
66-
if (typeof pluginData === `undefined`) continue;
67-
68-
for (const k in pluginData) {
69-
pluginData[k] = pluginData[k].map((data) => ({
70-
...data,
71-
// Track each node instance by plugin.
72-
// This way we can filter the nodes by the source plugin.
73-
// This is required since we can fetch multiple repositories
74-
// for different purposes.
75-
meta: {
76-
plugin: plugin.name,
77-
key: subpluginOptions.key,
78-
},
79-
}));
80-
}
81-
82-
// Otherwise they want we to create the nodes for them.
83-
rawSubpluginsData.push(pluginData);
84-
}
85-
86-
const dataByType = {};
87-
88-
for (const pluginData of rawSubpluginsData) {
89-
for (const k in pluginData) {
90-
dataByType[k] = [...(dataByType[k] ?? []), ...(pluginData[k] ?? [])];
91-
}
92-
}
93-
94-
if (typeof pluginOptions?.createCustomMapper === `function`) {
95-
const customMapper = pluginOptions.createCustomMapper({
96-
githubSourcePlugin: { pluginNodeTypes },
97-
});
98-
99-
for (const type of Object.values(pluginNodeTypes)) {
100-
const mapper = customMapper[type];
101-
102-
if (typeof mapper !== `function`) continue;
103-
104-
dataByType[type] = dataByType[type].map(mapper);
105-
}
106-
}
107-
108-
/** [dataByType]:
109-
* {
110-
* [pluginNodeTypes.USER]: [{ ...GitHubGraphQLUserType }, { ...GitHubGraphQLUserType }],
111-
* [pluginNodeTypes.REPOSITORY]: [... /** Same as above *\/],
112-
* ...
113-
* }
114-
*/
115-
for (const type in dataByType) {
116-
// we do not recognize this node type, skip it.
117-
if (!Object.values(pluginNodeTypes).includes(type)) continue;
118-
119-
const items = dataByType[type] ?? [];
120-
121-
function getMediaType(nodeType) {
122-
switch (nodeType) {
123-
case pluginNodeTypes.DISCUSSION:
124-
case pluginNodeTypes.ISSUE:
125-
return MEDIA_TYPES.MARKDOWN;
126-
default:
127-
return undefined;
128-
}
129-
}
130-
131-
function getNodeContent(node, type) {
132-
switch (type) {
133-
case pluginNodeTypes.DISCUSSION:
134-
case pluginNodeTypes.ISSUE:
135-
return node.body;
136-
default:
137-
return undefined;
138-
}
139-
}
140-
141-
const mediaType = getMediaType(type);
142-
143-
for (const item of items) {
144-
const content = getNodeContent(item, type);
145-
146-
await createNode({
147-
...item,
148-
githubId: item.id,
149-
id: createNodeId(`${type} >>> ${item.id}`),
150-
parent: null,
151-
children: [],
152-
internal: {
153-
type: type,
154-
contentDigest: createContentDigest(item),
155-
content: content,
156-
mediaType: mediaType,
157-
},
158-
});
159-
}
160-
}
161-
};
162-
163-
exports.onCreateNode = async (...args) => {
164-
const [
165-
{
166-
node,
167-
actions: { createNode, createNodeField },
168-
createNodeId,
169-
getCache,
170-
},
171-
pluginOptions,
172-
] = args;
173-
174-
const pluginNodeTypes = getPluginNodeTypes(pluginOptions.pluginNodeTypes);
175-
176-
const internalTypes = [...Object.values(pluginNodeTypes)];
177-
178-
const checkIfIsInternalType = (type) => internalTypes.includes(type);
179-
180-
const isInternalType = checkIfIsInternalType(node.internal.type);
181-
182-
async function createFileNodeFrom({ node, key, fieldName } = {}) {
183-
if (typeof node[key] !== `string`) return;
184-
if (!node[key].startsWith(`http`)) return;
185-
186-
const fileNode = await createRemoteFileNode({
187-
url: node[key],
188-
parentNodeId: node.id,
189-
createNode,
190-
createNodeId,
191-
getCache,
192-
});
193-
194-
if (fileNode) {
195-
createNodeField({ node, name: fieldName ?? key, value: fileNode.id });
196-
}
197-
}
198-
199-
// Allow subplugins make use of `onCreateNode` APIs.
200-
for (const plugin of pluginOptions.plugins) {
201-
const resolvedPlugin = plugin.module;
202-
const onCreateNode = resolvedPlugin.onCreateNode;
203-
204-
const subpluginOptions = plugin.pluginOptions;
205-
206-
const githubSourcePlugin = {
207-
createFileNodeFrom,
208-
checkIfIsInternalType,
209-
isInternalType,
210-
pluginNodeTypes,
211-
};
212-
213-
const subpluginArgs = [
214-
{ ...args[0], githubSourcePlugin },
215-
{
216-
...removeKey(subpluginOptions, `token`),
217-
},
218-
...args.slice(2),
219-
];
220-
221-
if (typeof onCreateNode === `function`) {
222-
onCreateNode(...subpluginArgs);
223-
}
224-
}
225-
226-
// Allow end-users make use of `onCreateNode` APIs.
227-
if (typeof pluginOptions?.onCreateNode === `function`) {
228-
const githubSourcePlugin = {
229-
createFileNodeFrom,
230-
checkIfIsInternalType,
231-
isInternalType,
232-
pluginNodeTypes,
233-
};
234-
235-
pluginOptions.onCreateNode(
236-
...[
237-
{
238-
...args[0],
239-
githubSourcePlugin,
240-
},
241-
{
242-
...removeKey(args[1], `token`),
243-
},
244-
...args.slice(2),
245-
]
246-
);
247-
}
248-
249-
if (!isInternalType) return;
250-
251-
async function handleImageOptimizationForNode({
252-
node,
253-
targetNodeType,
254-
key,
255-
optionableKey,
256-
}) {
257-
const IMAGE_OPTIMIZATION_KEY_SUFFIX = `SharpOptimized`;
258-
259-
if (node.internal.type !== targetNodeType) return;
260-
261-
if (pluginOptions[optionableKey] !== false) {
262-
if (key in node) {
263-
await createFileNodeFrom({
264-
node,
265-
key: key,
266-
// This is also linked on [createSchemaCustomization] step. See the [pluginNodeTypes.USER] type def.
267-
fieldName: key + IMAGE_OPTIMIZATION_KEY_SUFFIX,
268-
});
269-
}
270-
}
271-
}
272-
273-
const targetOptimizationOptions = [
274-
{
275-
targetNodeType: pluginNodeTypes.USER,
276-
key: `avatarUrl`,
277-
optionableKey: `generateOptimizedGitHubUserAvatarUrl`,
278-
},
279-
{
280-
targetNodeType: pluginNodeTypes.REPOSITORY,
281-
key: `openGraphImageUrl`,
282-
optionableKey: `generateOptimizedGitHubRepositoryOpenGraphImageUrl`,
283-
},
284-
];
285-
286-
for (const options of targetOptimizationOptions) {
287-
await handleImageOptimizationForNode({ ...options, node });
288-
}
3+
exports.sourceNodes = require(`./source-nodes`);
2894

290-
// for (const key in node) {
291-
// /**
292-
// * Any key of any node of this plugin that has a key ending with `optimizedsharpimage`
293-
// * will be automatically optimized.
294-
// * This way subplugins can return the data like:
295-
// * ```
296-
// * return {
297-
// * ...user,
298-
// * avatarOptimizedSharpImage: user.avatarUrl, // optimized version of the avatarUrl of the user
299-
// * }
300-
// * ```
301-
// */
302-
// if (key.toLowerCase().endsWith(`optimizedsharpimage`)) {
303-
// await createFileNodeFrom({node, key})
304-
// }
305-
// }
306-
};
5+
exports.onCreateNode = require(`./on-create-node`);
3076

3087
exports.createSchemaCustomization = require(`./create-schema-customization`);
3098

310-
exports.pluginOptionsSchema = function ({ Joi }) {
311-
return Joi.object({
312-
token: Joi.string().description(
313-
`Default token will be provided to subplugins to allow fetch data using GitHub GraphQL API v4. Though used for auth, this token is not directly shared.`
314-
),
315-
optimizedImagesKeyPrefix: Joi.string().description(
316-
`'optimizedsharpimage' by default`
317-
),
318-
generateOptimizedGitHubUserAvatarUrl: Joi.boolean().description(
319-
`Wether or not should generate the optimized version of the GitHub user [avatarUrl] field. Set to [false] if you do not want to. Defaults to [true].`
320-
),
321-
generateOptimizedGitHubRepositoryOpenGraphImageUrl:
322-
Joi.boolean().description(
323-
`Wether or not should generate the optimized version of the GitHub repository [openGraphImageUrl] field. Set to [false] if you do not want to. Defaults to [true].`
324-
),
325-
plugins: Joi.subPlugins().description(
326-
`A list of subplugins. See also: https://github.com/libsrcdev/gatsby-source-github-graphql for examples`
327-
),
328-
});
329-
};
9+
exports.pluginOptionsSchema = require(`./plugin-options-schema`);

0 commit comments

Comments
 (0)