Skip to content

Commit 3a5bb40

Browse files
committed
Refactors plugin for improved modularity and schema handling
Removes legacy entry points and updates exports for direct usage from source files. Refactors transformer API to use context-based collection and processing of nodes, enabling more flexible and accurate schema customization. Adds enhanced logging for debugging and clarity. Simplifies build configuration and package exports to streamline module format and type declaration output. Improves transformer logic for handling markdown images and associated file nodes.
1 parent 922ab90 commit 3a5bb40

File tree

10 files changed

+95
-68
lines changed

10 files changed

+95
-68
lines changed

gatsby-node.ts

Lines changed: 0 additions & 5 deletions
This file was deleted.

package.json

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@
2323
},
2424
"license": "MIT",
2525
"author": "@libsrcdev",
26-
"type": "module",
27-
"main": "./dist/index.cjs",
26+
"main": "./dist/index.js",
2827
"files": [
2928
"dist"
3029
],
@@ -57,19 +56,5 @@
5756
"semi": true,
5857
"singleQuote": true,
5958
"trailingComma": "es5"
60-
},
61-
"module": "./dist/index.mjs",
62-
"types": "./dist/index.d.cts",
63-
"exports": {
64-
".": {
65-
"import": "./dist/index.mjs",
66-
"require": "./dist/index.cjs"
67-
},
68-
"./gatsby-node": {
69-
"import": "./dist/gatsby-node.mjs",
70-
"require": "./dist/gatsby-node.cjs"
71-
},
72-
"./package.json": "./package.json",
73-
"./foo": "./foo.js"
7459
}
7560
}

src/create-schema-customization.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,14 @@ interface StructuredContentPluginOptions {
55
transformers?: RemarkStructuredContentTransformer[];
66
}
77

8-
export default async function createSchemaCustomization(
8+
export async function createSchemaCustomization(
99
gatsbyNodeApis: CreateSchemaCustomizationArgs,
1010
pluginOptions: StructuredContentPluginOptions
1111
): Promise<void> {
12-
const { actions } = gatsbyNodeApis;
12+
const { actions, reporter } = gatsbyNodeApis;
13+
14+
reporter.info("Starting createSchemaCustomization in remark-structured-content plugin");
15+
1316
const { createTypes } = actions;
1417

1518
const typeDefs = `
@@ -29,7 +32,7 @@ export default async function createSchemaCustomization(
2932
if (callbacks && callbacks.length > 0) {
3033
for (const callback of callbacks) {
3134
// Allow each transformer to extend types
32-
await callback({ actions: gatsbyNodeApis.actions });
35+
await callback({ ...gatsbyNodeApis });
3336
}
3437
}
3538
}

src/index.ts

Lines changed: 64 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,37 @@ import { createRemoteFileNode } from "gatsby-source-filesystem";
22
import { visit, EXIT } from "unist-util-visit";
33
import type { Node as UnistNode, Parent as UnistParent } from "unist";
44
import type { Image } from "mdast";
5-
import { RemarkPluginApi, RemarkStructuredContentTransformer, StructuredContentPluginOptions as RemarkStructuredContentPluginOptions, TransformerContext } from "./types.ts";
6-
5+
import { RemarkPluginApi, RemarkStructuredContentTransformer, StructuredContentPluginOptions as RemarkStructuredContentPluginOptions, TransformerContext } from "./types";
76

87
/**
98
* Extract ALL images from the markdown AST and save them to File nodes.
109
*/
1110
export function createImageExtractorTransformer(): RemarkStructuredContentTransformer<Image> {
1211
return {
13-
createSchemaCustomization: ({ actions }) => {
12+
createSchemaCustomization: ({ reporter, actions, schema }) => {
1413
const { createTypes } = actions;
15-
const typeDefs = `
14+
15+
reporter.info("Creating schema customization for createImageExtractorTransformer");
16+
17+
const typeDefs = [
18+
`
1619
type MarkdownRemark implements Node {
17-
embeddedImages: [File!] @link(by: "parent.id", from: "id")
20+
embeddedImages: [File] @link(by: "fields.imageExtractedFromMarkdownRemarkId", from: "id")
1821
}
19-
`;
22+
`,
23+
]
24+
2025
createTypes(typeDefs);
2126
},
2227
traverse: (markdownAST, _utils, context) => {
2328
getAllImagesFromMarkdownAST(markdownAST).forEach((imageNode) => {
24-
context.scheduleTransformOf(imageNode);
29+
context.collect(imageNode);
2530
});
2631
},
27-
transform: async (node, { saveNodeToFile }) => {
28-
await saveNodeToFile(node, { transformer: true });
32+
transform: async (context, { createFileNode }, { markdownNode: markdownRemarkGatsbyNode }) => {
33+
for (const node of context.collected) {
34+
await createFileNode(node, { imageExtractedFromMarkdownRemarkId: markdownRemarkGatsbyNode.id });
35+
}
2936
},
3037
};
3138
}
@@ -37,13 +44,17 @@ export type CreateThumbnailImageTransformerOptions = {
3744
/**
3845
* Extract a single "thumbnail" image with special rules, then remove it from the AST.
3946
*/
40-
export function createThumbnailImageTransformer({ keepImageInMdAST }: CreateThumbnailImageTransformerOptions): RemarkStructuredContentTransformer<Image> {
47+
export function createThumbnailImageTransformer(options?: CreateThumbnailImageTransformerOptions): RemarkStructuredContentTransformer<Image> {
48+
const { keepImageInMdAST } = options || {};
49+
50+
const LINK_FIELD_NAME = "thumbnailImage";
51+
4152
return {
42-
createSchemaCustomization: ({ actions }) => {
53+
createSchemaCustomization: ({ actions, schema }) => {
4354
const { createTypes } = actions;
4455
const typeDefs = `
4556
type MarkdownRemark implements Node {
46-
thumbnailImage: File @link(by: "parent.id", from: "id")
57+
${LINK_FIELD_NAME}: File @link(from: "fields.${LINK_FIELD_NAME}", by: "id")
4758
}
4859
`;
4960
createTypes(typeDefs);
@@ -52,16 +63,29 @@ export function createThumbnailImageTransformer({ keepImageInMdAST }: CreateThum
5263
const thumbImgNode = getThumbnailImageOnly(markdownAST);
5364

5465
if (thumbImgNode) {
55-
context.scheduleTransformOf(thumbImgNode);
66+
context.collect(thumbImgNode);
5667
}
5768
},
58-
transform: async (node, { saveNodeToFile, removeNodeFromMdAST }) => {
59-
await saveNodeToFile(node, { isThumbnail: true });
69+
transform: async (context, { createFileNode, removeNodeFromMdAST }, gatsbyApis) => {
70+
const { markdownNode: markdownRemarkGatsbyNode, actions } = gatsbyApis;
71+
72+
const [thumbMdASTNode] = context.collected;
73+
74+
if (!thumbMdASTNode) {
75+
// No thumbnail image found
76+
return;
77+
}
78+
79+
const { createNodeField } = actions;
80+
81+
const thumbImgGatsbyNode = await createFileNode(thumbMdASTNode);
82+
83+
createNodeField({ node: markdownRemarkGatsbyNode, name: LINK_FIELD_NAME, value: thumbImgGatsbyNode.id });
6084

6185
if (keepImageInMdAST === true) {
6286
// do nothing, keep the node in the AST
6387
} else {
64-
await removeNodeFromMdAST(node);
88+
await removeNodeFromMdAST(thumbMdASTNode);
6589
}
6690
},
6791
};
@@ -79,17 +103,22 @@ export default async function remarkStructuredContentPlugin(
79103
markdownNode,
80104
getCache,
81105
actions,
106+
reporter,
82107
createNodeId,
83108
...rest
84109
} = remarkPluginApi;
85110

111+
reporter.info("Starting remark-structured-content plugin for a markdown node with id: " + markdownNode.id);
112+
86113
const { createNode, createNodeField } = actions;
87114
const { transformers } = pluginOptions;
88115

89-
async function saveNodeToFile(
116+
async function createFileNode(
90117
node: Image,
91118
extraFields: Record<string, unknown> = {}
92119
) {
120+
reporter.info(`Saving remote file node for image: ${node.url}`);
121+
93122
const fileNode = await createRemoteFileNode({
94123
url: node.url,
95124
parentNodeId: markdownNode.id,
@@ -98,8 +127,10 @@ export default async function remarkStructuredContentPlugin(
98127
createNodeId,
99128
});
100129

130+
reporter.info(`Created file node with id: ${fileNode?.id} for image: ${node.url}`);
131+
101132
for (const [key, value] of Object.entries(extraFields)) {
102-
await createNodeField({ node: fileNode, name: key, value });
133+
createNodeField({ node: fileNode, name: key, value });
103134
}
104135

105136
return fileNode;
@@ -115,20 +146,19 @@ export default async function remarkStructuredContentPlugin(
115146
for (const transformer of transformers) {
116147
const context: TransformerContext<any> = {
117148
collected: [],
118-
scheduleTransformOf(item) {
149+
collect(item) {
119150
this.collected.push(item);
120151
},
152+
meta: {},
121153
};
122154

123155
transformer.traverse(markdownAST, { visit }, context);
124156

125-
for (const collectedItem of context.collected) {
126-
await transformer.transform(
127-
collectedItem,
128-
{ saveNodeToFile, removeNodeFromMdAST },
129-
remarkPluginApi
130-
);
131-
}
157+
await transformer.transform(
158+
context,
159+
{ createFileNode, removeNodeFromMdAST },
160+
remarkPluginApi,
161+
);
132162
}
133163

134164
return markdownAST;
@@ -157,6 +187,8 @@ function getThumbnailImageOnly(markdownAST: UnistNode): Image | null {
157187
markdownAST,
158188
"image",
159189
(node, index, parent) => {
190+
thumbnailImage = node as Image;
191+
return [EXIT];
160192
if (!parent || typeof index !== "number") {
161193
return;
162194
}
@@ -174,10 +206,15 @@ function getThumbnailImageOnly(markdownAST: UnistNode): Image | null {
174206

175207
if (!hasTextBefore && !hasTextAfter) {
176208
thumbnailImage = node as Image;
177-
return EXIT;
209+
return [EXIT];
178210
}
179211
}
180212
);
181213

182214
return thumbnailImage;
183215
}
216+
217+
export { sourceNodes } from "./source-nodes";
218+
export { onCreateNode } from "./on-create-node";
219+
export { createSchemaCustomization } from "./create-schema-customization";
220+
export { pluginOptionsSchema } from "./plugin-options-schema";

src/on-create-node.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ interface StructuredContentPluginOptions extends PluginOptions {
55
// transformers?: Transformer[];
66
}
77

8-
export default async function onCreateNode(
8+
export async function onCreateNode(
99
...args: [CreateNodeArgs, StructuredContentPluginOptions]
1010
): Promise<void> {
1111
const [

src/plugin-options-schema.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { PluginOptionsSchemaArgs } from "gatsby";
22

3-
export default function pluginOptionsSchema(
3+
export function pluginOptionsSchema(
44
{ Joi }: PluginOptionsSchemaArgs
55
) {
66
return Joi.object({});

src/source-nodes.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { SourceNodesArgs, PluginOptions } from "gatsby";
2-
import { RemarkPluginApi } from "./types.ts";
2+
import { RemarkPluginApi } from "./types";
33

4-
export default async function sourceNodes(
4+
export async function sourceNodes(
55
gatsbyArgs: SourceNodesArgs,
66
pluginOptions: RemarkPluginApi
77
): Promise<void> {

src/types.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,26 @@
1-
import type { Actions, PluginOptions } from "gatsby";
1+
import type { Actions, CreateSchemaCustomizationArgs, PluginOptions } from "gatsby";
22
import { visit, EXIT } from "unist-util-visit";
33
import type { Node as UnistNode, Parent as UnistParent } from "unist";
44
import type { Image } from "mdast";
55

66
export interface RemarkStructuredContentTransformer<T = any> {
7-
createSchemaCustomization?: (args: { actions: Actions }) => void | Promise<void>;
7+
createSchemaCustomization?: (args: CreateSchemaCustomizationArgs) => void | Promise<void>;
88
traverse: (
99
markdownAST: UnistNode,
1010
utils: { visit: typeof visit },
1111
context: TransformerContext<T>
1212
) => void;
1313
transform: (
14-
collected: T,
14+
context: TransformerContext<T>,
1515
helpers: {
16-
saveNodeToFile: (node: Image, extraFields?: Record<string, unknown>) => Promise<any>;
16+
createFileNode: (node: Image, extraFields?: Record<string, unknown>) => Promise<any>;
1717
removeNodeFromMdAST: (node: UnistNode) => Promise<void>;
1818
},
1919
api: RemarkPluginApi
2020
) => Promise<void>;
2121
}
2222

23-
export interface RemarkPluginApi {
23+
export interface RemarkPluginApi extends CreateSchemaCustomizationArgs {
2424
markdownAST: UnistNode;
2525
markdownNode: any;
2626
getCache: (id: string) => any;
@@ -32,7 +32,8 @@ export interface RemarkPluginApi {
3232

3333
export interface TransformerContext<T = any> {
3434
collected: T[];
35-
scheduleTransformOf: (item: T) => void;
35+
collect: (item: T) => void;
36+
meta: Record<string, unknown>;
3637
}
3738

3839
export interface StructuredContentPluginOptions extends PluginOptions {

tsconfig.json

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
{
2-
"include": ["src", "types", "test", "gatsby-node.ts"],
2+
"include": ["src", "types", "test"],
33
"compilerOptions": {
44
"target": "es5",
5-
"module": "nodenext",
5+
"module": "esnext",
66
"lib": ["dom", "esnext"],
77
"importHelpers": true,
88
"declaration": true,
99
"sourceMap": true,
1010
"rootDir": "./",
11-
"allowImportingTsExtensions": true,
12-
"noEmit": true,
1311
"strict": true,
1412
"noImplicitAny": true,
1513
"strictNullChecks": true,
@@ -21,7 +19,7 @@
2119
"noUnusedParameters": true,
2220
"noImplicitReturns": true,
2321
"noFallthroughCasesInSwitch": true,
24-
"moduleResolution": "nodenext",
22+
"moduleResolution": "node",
2523
"baseUrl": "./",
2624
"paths": {
2725
"*": ["src/*", "node_modules/*"]

tsdown.config.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,19 @@ import { defineConfig } from "tsdown";
22

33
export default defineConfig({
44
entry: {
5-
"gatsby-node": "gatsby-node.ts",
65
"index": "src/index.ts",
76
},
8-
format: ["cjs", "esm"],
7+
format: ["cjs"],
98
dts: true,
9+
outExtensions: (c) => {
10+
if (c.format === "cjs") {
11+
return {
12+
js: ".js",
13+
dts: ".d.ts",
14+
};
15+
}
16+
return undefined;
17+
},
1018
clean: true,
1119
platform: "node",
1220
unbundle: false,

0 commit comments

Comments
 (0)