Skip to content

Commit 247662c

Browse files
authored
feat: add support to relative paths for media content (#12)
* docs: update docs * feat: support images with relative paths * feat: use raw user content url to parse relative paths
1 parent 0f31a51 commit 247662c

File tree

5 files changed

+93
-17
lines changed

5 files changed

+93
-17
lines changed

README.md

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,16 +63,20 @@ Example:
6363
---
6464
title: New Article
6565
description: Some description
66-
tags:
67-
- test
68-
- ci
66+
tags: test, ci
6967
license: public-domain
7068
---
7169
# This is my new Article
7270
7371
In this article we will learn how to setup with `blogpub`
7472

75-
![ci-meme.jpg](./meme.jpg)
73+
## Relative images from the repository
74+
75+
![ci-meme.jpg](@/assets/meme.jpg)
76+
77+
## External Images
78+
79+
![meme.jpg](https://sources.com/meme.jpg)
7680

7781
## Requirements
7882

@@ -113,6 +117,17 @@ This is only for Medium
113117
{{/if}}
114118
```
115119

120+
## Relative Images
121+
122+
To use images contained in the same folder of the blog articles use `@` as source path. `@` will be parsed
123+
as the URL to the repository/<articles_folder>.
124+
E.g.
125+
```
126+
![image](@/img1.png)
127+
128+
<img src="@/img2.jpg" />
129+
```
130+
116131
## Developing
117132

118133
Run tests

src/main.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,20 @@ export async function run() {
4242
const mediumUserId = core.getInput('medium_user_id', { required: true });
4343
const mediumBaseUrl = core.getInput('medium_base_url', { required: false });
4444
const devtoApiKey = core.getInput('devto_api_key', { required: true });
45+
46+
// https://docs.github.com/en/actions/learn-github-actions/environment-variables#default-environment-variables
47+
const GITHUB_SERVER_URL = process.env['GITHUB_SERVER_URL'] as string;
48+
const GITHUB_REPOSITORY = process.env['GITHUB_REPOSITORY'] as string;
49+
const GITHUB_REF_NAME = process.env['GITHUB_REF_NAME'] as string;
50+
51+
const rawGithubUrl = GITHUB_SERVER_URL
52+
.replace('//github.com', '//raw.githubusercontent.com');
4553

4654
const github = getOctokit(ghToken);
4755

4856
const articleContent = await loadArticleContent(github, articlesFolder);
49-
const article = parseArticle(articleContent);
57+
const baseUrl = `${rawGithubUrl}/${GITHUB_REPOSITORY}/${GITHUB_REF_NAME}/${articlesFolder}`;
58+
const article = parseArticle(articleContent, baseUrl);
5059

5160
const template = Handlebars.compile(article.content);
5261

src/parser.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import { load as loadYaml } from 'js-yaml';
22

33
import { Article, ArticleConfig, MediumLicense } from './types';
44

5+
const RelativeImagesRegex = /!\[.*\]\(@\/.*\)|<img.*src=["'](@)\/.*["']/i;
6+
57
function getMetadataIndexes(lines: string[]): number[] {
68
const indexes: number[] = [];
79
for (let i = 0; i < lines.length; i++) {
@@ -26,7 +28,20 @@ function getArticleTitle(lines: string[]): string | null {
2628
return null;
2729
}
2830

29-
export function parseArticle(content: string): Article {
31+
function parseRelativeImages(lines: string[], baseUrl: string) {
32+
for (let i = 0; i < lines.length; i++) {
33+
if (RelativeImagesRegex.test(lines[i])) {
34+
lines[i] = lines[i].replace('@', baseUrl);
35+
}
36+
}
37+
}
38+
39+
/*
40+
* Parses an article into an Article object.
41+
*
42+
* It loads the metadata and parses all the images with `@` relative path.
43+
*/
44+
export function parseArticle(content: string, baseUrl: string): Article {
3045
const lines = content.split('\n');
3146
const metadataIndexes = getMetadataIndexes(lines);
3247
if (metadataIndexes.length !== 2) {
@@ -43,6 +58,7 @@ export function parseArticle(content: string): Article {
4358
config.title = title;
4459
}
4560

61+
parseRelativeImages(contentLines, baseUrl);
4662
config.description ??= '';
4763
config.license ??= MediumLicense.PublicDomain;
4864
config.published ??= true;

test/main.spec.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ jest.mock('@actions/github', () => ({
3535
jest.mock('@actions/core');
3636

3737
describe('blogpub', () => {
38+
beforeAll(() => {
39+
process.env['GITHUB_SERVER_URL'] = 'https://github.com';
40+
process.env['GITHUB_REPOSITORY'] = 'protiumx/blogpub';
41+
process.env['GITHUB_REF_NAME'] = 'main';
42+
});
43+
3844
beforeEach(jest.clearAllMocks);
3945

4046
(getOctokit as jest.Mock).mockReturnValue(octokitMock);
@@ -247,7 +253,6 @@ describe('blogpub', () => {
247253
it('should upload article to dev.to and set dev.tp url output', async () => {
248254
const template = jest.fn(() => 'compiled');
249255
(Handlebars.compile as jest.Mock).mockReturnValue(template);
250-
251256
(core.getInput as jest.Mock).mockImplementation((key: string) => {
252257
switch (key) {
253258
case 'articles_folder':
@@ -283,5 +288,7 @@ describe('blogpub', () => {
283288
content: 'compiled',
284289
});
285290
expect(core.setOutput).toHaveBeenCalledWith('devto_url', 'dev.to/new');
291+
expect(parseArticle).toHaveBeenCalledWith(
292+
'content', 'https://raw.githubusercontent.com/protiumx/blogpub/main/blogs');
286293
});
287294
});

test/parser.spec.ts

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,18 @@ describe('parser', () => {
55
beforeEach(jest.clearAllMocks);
66

77
it('should error when article does not contain metadata', () => {
8-
expect(parseArticle.bind(null, '')).toThrowError('Incorrect metadata');
8+
expect(parseArticle.bind(null, '', '')).toThrowError('Incorrect metadata');
99
});
1010

1111
it('should error when article does not contain a title', () => {
1212
const article = `
1313
---
14-
tags:
15-
- tagOne
14+
tags: tagOne
1615
---
1716
## some sub title
1817
some content
1918
`;
20-
expect(parseArticle.bind(null, article)).toThrowError('Article does not have a title');
19+
expect(parseArticle.bind(null, article, '')).toThrowError('Article does not have a title');
2120
});
2221

2322
it('should get title from article content', () => {
@@ -29,7 +28,7 @@ tags:
2928
# Main Title
3029
some content
3130
`;
32-
const parsed = parseArticle(article);
31+
const parsed = parseArticle(article, '');
3332
expect(parsed.config.title).toEqual('Main Title');
3433
});
3534

@@ -40,7 +39,7 @@ title: Metadata Title
4039
---
4140
# Main Title
4241
some content`;
43-
const parsed = parseArticle(article);
42+
const parsed = parseArticle(article, '');
4443
expect(parsed.config.title).toEqual('Metadata Title');
4544
expect(parsed.config.description).toEqual('');
4645
expect(parsed.config.license).toEqual(MediumLicense.PublicDomain);
@@ -54,20 +53,50 @@ some content`);
5453
---
5554
title: Metadata Title
5655
description: New Article
57-
tags:
58-
- one
56+
tags: one, two
5957
license: ${MediumLicense.CC40Zero}
6058
published: false
6159
---
6260
# Main Title
6361
some content`;
64-
const parsed = parseArticle(article);
62+
const parsed = parseArticle(article, '');
6563
expect(parsed.config.title).toEqual('Metadata Title');
6664
expect(parsed.config.description).toEqual('New Article');
67-
expect(parsed.config.tags).toEqual(['one']);
65+
expect(parsed.config.tags).toEqual('one, two');
6866
expect(parsed.config.license).toEqual(MediumLicense.CC40Zero);
6967
expect(parsed.config.published).toEqual(false);
7068
expect(parsed.content).toEqual(`# Main Title
7169
some content`);
7270
});
71+
72+
73+
it('should parse all images with relative path', () => {
74+
const article = `
75+
---
76+
title: Some Title
77+
description: New Article
78+
tags: one, images
79+
published: false
80+
---
81+
82+
# Main Title
83+
some content
84+
85+
![img](@/assets/img.png)
86+
87+
<img alt="image" src="@/assets/img.jpg" />
88+
89+
## Description
90+
`;
91+
const parsed = parseArticle(article, 'https://raw.github.com/protiumx/blogpub/main/articles');
92+
console.info(parsed.content);
93+
expect(
94+
parsed.content.includes(
95+
'![img](https://raw.github.com/protiumx/blogpub/main/articles/assets/img.png)',
96+
)).toBeTruthy();
97+
expect(
98+
parsed.content.includes(
99+
'<img alt="image" src="https://raw.github.com/protiumx/blogpub/main/articles/assets/img.jpg"',
100+
)).toBeTruthy();
101+
});
73102
});

0 commit comments

Comments
 (0)