Skip to content

Commit 26f93ec

Browse files
committed
fix: resolve ES module import issue with marked library
- Convert static import to dynamic import for marked library - Make MarkdownParser methods async to support dynamic imports - Update Note.operations.ts to handle async MarkdownParser calls - Fix test file to use await with async MarkdownParser methods - Resolves n8n loading error: "require() of ES Module not supported"
1 parent 1af7343 commit 26f93ec

File tree

3 files changed

+32
-38
lines changed

3 files changed

+32
-38
lines changed

nodes/Substack/MarkdownParser.ts

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,5 @@
1-
import { marked } from 'marked';
21
import type { OwnProfile } from 'substack-api';
32

4-
// Configure marked library
5-
marked.setOptions({
6-
gfm: true,
7-
breaks: false
8-
});
9-
103
// Helper function to decode HTML entities
114
function decodeHtmlEntities(text: string): string {
125
const entityMap: Record<string, string> = {
@@ -30,11 +23,20 @@ export class MarkdownParser {
3023
* Parse markdown text and apply it to a NoteBuilder using structured approach
3124
* Returns the final ParagraphBuilder with all content applied (handles immutable builders)
3225
*/
33-
static parseMarkdownToNoteStructured(markdown: string, noteBuilder: ReturnType<OwnProfile['newNote']>): ReturnType<ReturnType<OwnProfile['newNote']>['paragraph']> {
26+
static async parseMarkdownToNoteStructured(markdown: string, noteBuilder: ReturnType<OwnProfile['newNote']>): Promise<ReturnType<ReturnType<OwnProfile['newNote']>['paragraph']>> {
3427
if (!markdown.trim()) {
3528
throw new Error('Note body cannot be empty - at least one paragraph with content is required');
3629
}
3730

31+
// Dynamically import marked library
32+
const { marked } = await import('marked');
33+
34+
// Configure marked library
35+
marked.setOptions({
36+
gfm: true,
37+
breaks: false
38+
});
39+
3840
// Parse markdown into tokens using marked
3941
const tokens = marked.lexer(markdown);
4042

@@ -70,8 +72,8 @@ export class MarkdownParser {
7072
/**
7173
* Legacy method for backward compatibility
7274
*/
73-
static parseMarkdownToNote(markdown: string, noteBuilder: ReturnType<OwnProfile['newNote']>): ReturnType<ReturnType<OwnProfile['newNote']>['paragraph']> {
74-
return this.parseMarkdownToNoteStructured(markdown, noteBuilder);
75+
static async parseMarkdownToNote(markdown: string, noteBuilder: ReturnType<OwnProfile['newNote']>): Promise<ReturnType<ReturnType<OwnProfile['newNote']>['paragraph']>> {
76+
return await this.parseMarkdownToNoteStructured(markdown, noteBuilder);
7577
}
7678

7779
/**

nodes/Substack/Note.operations.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ async function createAdvancedNote(
235235
}
236236

237237
try {
238-
const finalBuilder = MarkdownParser.parseMarkdownToNoteStructured(body.trim(), ownProfile.newNote());
238+
const finalBuilder = await MarkdownParser.parseMarkdownToNoteStructured(body.trim(), ownProfile.newNote());
239239
return await finalBuilder.publish();
240240
} catch (error) {
241241
let userMessage = error.message;

tests/unit/markdown-parser.test.ts

Lines changed: 19 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ describe('MarkdownParser - Real NoteBuilder', () => {
100100
const profile = await mockSubstackClient.ownProfile();
101101
const noteBuilder = profile.newNote();
102102

103-
const result = MarkdownParser.parseMarkdownToNoteStructured(markdown, noteBuilder);
103+
const result = await MarkdownParser.parseMarkdownToNoteStructured(markdown, noteBuilder);
104104
await result.publish();
105105

106106
expect(capturedPayload).toMatchObject(expectedJson);
@@ -132,7 +132,7 @@ describe('MarkdownParser - Real NoteBuilder', () => {
132132
const profile = await mockSubstackClient.ownProfile();
133133
const noteBuilder = profile.newNote();
134134

135-
const result = MarkdownParser.parseMarkdownToNoteStructured(markdown, noteBuilder);
135+
const result = await MarkdownParser.parseMarkdownToNoteStructured(markdown, noteBuilder);
136136
await result.publish();
137137

138138
expect(capturedPayload).toMatchObject(expectedJson);
@@ -164,7 +164,7 @@ describe('MarkdownParser - Real NoteBuilder', () => {
164164
const profile = await mockSubstackClient.ownProfile();
165165
const noteBuilder = profile.newNote();
166166

167-
const result = MarkdownParser.parseMarkdownToNoteStructured(markdown, noteBuilder);
167+
const result = await MarkdownParser.parseMarkdownToNoteStructured(markdown, noteBuilder);
168168
await result.publish();
169169

170170
expect(capturedPayload).toMatchObject(expectedJson);
@@ -196,7 +196,7 @@ describe('MarkdownParser - Real NoteBuilder', () => {
196196
const profile = await mockSubstackClient.ownProfile();
197197
const noteBuilder = profile.newNote();
198198

199-
const result = MarkdownParser.parseMarkdownToNoteStructured(markdown, noteBuilder);
199+
const result = await MarkdownParser.parseMarkdownToNoteStructured(markdown, noteBuilder);
200200
await result.publish();
201201

202202
expect(capturedPayload).toMatchObject(expectedJson);
@@ -246,7 +246,7 @@ describe('MarkdownParser - Real NoteBuilder', () => {
246246
const profile = await mockSubstackClient.ownProfile();
247247
const noteBuilder = profile.newNote();
248248

249-
const result = MarkdownParser.parseMarkdownToNoteStructured(markdown, noteBuilder);
249+
const result = await MarkdownParser.parseMarkdownToNoteStructured(markdown, noteBuilder);
250250
await result.publish();
251251

252252
expect(capturedPayload).toMatchObject(expectedJson);
@@ -272,7 +272,7 @@ describe('MarkdownParser - Real NoteBuilder', () => {
272272
const profile = await mockSubstackClient.ownProfile();
273273
const noteBuilder = profile.newNote();
274274

275-
const result = MarkdownParser.parseMarkdownToNoteStructured(markdown, noteBuilder);
275+
const result = await MarkdownParser.parseMarkdownToNoteStructured(markdown, noteBuilder);
276276
await result.publish();
277277

278278
expect(capturedPayload).toMatchObject(expectedJson);
@@ -330,7 +330,7 @@ describe('MarkdownParser - Real NoteBuilder', () => {
330330
const profile = await mockSubstackClient.ownProfile();
331331
const noteBuilder = profile.newNote();
332332

333-
const result = MarkdownParser.parseMarkdownToNoteStructured(markdown, noteBuilder);
333+
const result = await MarkdownParser.parseMarkdownToNoteStructured(markdown, noteBuilder);
334334
await result.publish();
335335

336336
expect(capturedPayload).toMatchObject(expectedJson);
@@ -397,7 +397,7 @@ Second paragraph with *italic* text.`;
397397
const profile = await mockSubstackClient.ownProfile();
398398
const noteBuilder = profile.newNote();
399399

400-
const result = MarkdownParser.parseMarkdownToNoteStructured(markdown, noteBuilder);
400+
const result = await MarkdownParser.parseMarkdownToNoteStructured(markdown, noteBuilder);
401401
await result.publish();
402402

403403
expect(capturedPayload).toMatchObject(expectedJson);
@@ -430,7 +430,7 @@ Second paragraph with *italic* text.`;
430430
const profile = await mockSubstackClient.ownProfile();
431431
const noteBuilder = profile.newNote();
432432

433-
const result = MarkdownParser.parseMarkdownToNoteStructured(markdown, noteBuilder);
433+
const result = await MarkdownParser.parseMarkdownToNoteStructured(markdown, noteBuilder);
434434
await result.publish();
435435

436436
expect(capturedPayload).toMatchObject(expectedJson);
@@ -469,7 +469,7 @@ Second paragraph with *italic* text.`;
469469
const profile = await mockSubstackClient.ownProfile();
470470
const noteBuilder = profile.newNote();
471471

472-
const result = MarkdownParser.parseMarkdownToNoteStructured(markdown, noteBuilder);
472+
const result = await MarkdownParser.parseMarkdownToNoteStructured(markdown, noteBuilder);
473473
await result.publish();
474474

475475
expect(capturedPayload).toMatchObject(expectedJson);
@@ -509,7 +509,7 @@ Second paragraph with *italic* text.`;
509509
const profile = await mockSubstackClient.ownProfile();
510510
const noteBuilder = profile.newNote();
511511

512-
const result = MarkdownParser.parseMarkdownToNoteStructured(markdown, noteBuilder);
512+
const result = await MarkdownParser.parseMarkdownToNoteStructured(markdown, noteBuilder);
513513
await result.publish();
514514

515515
expect(capturedPayload).toMatchObject(expectedJson);
@@ -547,7 +547,7 @@ Second paragraph with *italic* text.`;
547547
const profile = await mockSubstackClient.ownProfile();
548548
const noteBuilder = profile.newNote();
549549

550-
const result = MarkdownParser.parseMarkdownToNoteStructured(markdown, noteBuilder);
550+
const result = await MarkdownParser.parseMarkdownToNoteStructured(markdown, noteBuilder);
551551
await result.publish();
552552

553553
expect(capturedPayload).toMatchObject(expectedJson);
@@ -591,7 +591,7 @@ Second paragraph with *italic* text.`;
591591
const profile = await mockSubstackClient.ownProfile();
592592
const noteBuilder = profile.newNote();
593593

594-
const result = MarkdownParser.parseMarkdownToNoteStructured(markdown, noteBuilder);
594+
const result = await MarkdownParser.parseMarkdownToNoteStructured(markdown, noteBuilder);
595595
await result.publish();
596596

597597
expect(capturedPayload).toMatchObject(expectedJson);
@@ -622,7 +622,7 @@ Second paragraph with *italic* text.`;
622622
const profile = await mockSubstackClient.ownProfile();
623623
const noteBuilder = profile.newNote();
624624

625-
const result = MarkdownParser.parseMarkdownToNoteStructured(markdown, noteBuilder);
625+
const result = await MarkdownParser.parseMarkdownToNoteStructured(markdown, noteBuilder);
626626
await result.publish();
627627

628628
expect(capturedPayload).toMatchObject(expectedJson);
@@ -728,7 +728,7 @@ Final paragraph.`;
728728
const profile = await mockSubstackClient.ownProfile();
729729
const noteBuilder = profile.newNote();
730730

731-
const result = MarkdownParser.parseMarkdownToNoteStructured(markdown, noteBuilder);
731+
const result = await MarkdownParser.parseMarkdownToNoteStructured(markdown, noteBuilder);
732732
await result.publish();
733733

734734
expect(capturedPayload).toMatchObject(expectedJson);
@@ -740,36 +740,28 @@ Final paragraph.`;
740740
const profile = await mockSubstackClient.ownProfile();
741741
const noteBuilder = profile.newNote();
742742

743-
expect(() => {
744-
MarkdownParser.parseMarkdownToNoteStructured('', noteBuilder);
745-
}).toThrow('Note body cannot be empty - at least one paragraph with content is required');
743+
await expect(MarkdownParser.parseMarkdownToNoteStructured('', noteBuilder)).rejects.toThrow('Note body cannot be empty - at least one paragraph with content is required');
746744
});
747745

748746
it('should throw error for whitespace-only markdown', async () => {
749747
const profile = await mockSubstackClient.ownProfile();
750748
const noteBuilder = profile.newNote();
751749

752-
expect(() => {
753-
MarkdownParser.parseMarkdownToNoteStructured(' \n\t ', noteBuilder);
754-
}).toThrow('Note body cannot be empty - at least one paragraph with content is required');
750+
await expect(MarkdownParser.parseMarkdownToNoteStructured(' \n\t ', noteBuilder)).rejects.toThrow('Note body cannot be empty - at least one paragraph with content is required');
755751
});
756752

757753
it('should throw error for empty headings', async () => {
758754
const profile = await mockSubstackClient.ownProfile();
759755
const noteBuilder = profile.newNote();
760756

761-
expect(() => {
762-
MarkdownParser.parseMarkdownToNoteStructured('## \n### \n#### ', noteBuilder);
763-
}).toThrow('Note must contain at least one paragraph with actual content');
757+
await expect(MarkdownParser.parseMarkdownToNoteStructured('## \n### \n#### ', noteBuilder)).rejects.toThrow('Note must contain at least one paragraph with actual content');
764758
});
765759

766760
it('should throw error for empty list items only', async () => {
767761
const profile = await mockSubstackClient.ownProfile();
768762
const noteBuilder = profile.newNote();
769763

770-
expect(() => {
771-
MarkdownParser.parseMarkdownToNoteStructured('- \n* \n1. ', noteBuilder);
772-
}).toThrow('Note must contain at least one paragraph with actual content');
764+
await expect(MarkdownParser.parseMarkdownToNoteStructured('- \n* \n1. ', noteBuilder)).rejects.toThrow('Note must contain at least one paragraph with actual content');
773765
});
774766
});
775767
});

0 commit comments

Comments
 (0)