Skip to content

Commit

Permalink
feat: automatically remap local images to remote ones when publishing
Browse files Browse the repository at this point in the history
  • Loading branch information
maxime1992 committed Jul 7, 2019
1 parent 41ca271 commit c0b1797
Show file tree
Hide file tree
Showing 8 changed files with 136 additions and 42 deletions.
6 changes: 2 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,9 @@ Simple and from there you have control over the following properties: `title`, `

## How do I add images to my blog posts?

Instead of uploading them manually on dev.to, simply put them on your git repo and within the markdown use a link like the following example:
Instead of uploading them manually on dev.to, simply put them within your git repo and within the blog post use a relative link. Here's an example: `The following is an image: ![alt text](./assets/image.png 'Title image')`.

https://raw.githubusercontent.com/YOUR-USERNAME/YOUR-REPO/master/blog-posts/name-of-your-blog-post/assets/your-asset.png

Alternatively you can use a CDN like https://gitcdn.xyz where you'd just need to paste the previous URL and use the one that website gives you.
If you've got some plugin to preview your markdown from your IDE, the images will be correctly displayed. Then, on CI, right before they're published, the link will be updated to match the raw file.

## How to setup CI for auto deploying the blog posts?

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"author": "Maxime Robert <maxime.robert1992@gmail.com>",
"repository": {
"type": "git",
"url": ""
"url": "https://github.com/maxime1992/dev-to-git.git"
},
"license": "MIT",
"engines": {
Expand Down
43 changes: 43 additions & 0 deletions src/article.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Article } from './article';
import { Repository } from './dev-to-git.interface';

describe(`Article`, () => {
let article: Article;
const repository: Repository = { username: `maxime1992`, name: 'dev-to-git' };
const relativePathToArticle = `./test/article.md`;

beforeEach(() => {
article = new Article({
id: 0,
relativePathToArticle,
repository,
});
});

describe(`Read`, () => {
let articleRead: string;

beforeEach(() => {
articleRead = article.readArticleOnDisk();
});

it(`should read an article from the configuration`, () => {
expect(articleRead).toContain(`This is my awesome article!`);
expect(articleRead).toContain(`Hey, some text!`);
});

it(`should rewrite the local images URLs to match the raw file on github`, () => {
expect(articleRead).toContain(
`Image 1: ![alt text 1](https://raw.githubusercontent.com/${repository.username}/${repository.name}/master/test/image-1.png 'Title image 1')`,
);

expect(articleRead).toContain(
`Image 2: ![alt text 2](https://raw.githubusercontent.com/${repository.username}/${repository.name}/master/test/image-2.png 'Title image 2')`,
);

expect(articleRead).toContain(
`Image 3: ![alt text 3](https://raw.githubusercontent.com/${repository.username}/${repository.name}/master/test/image-3.png)`,
);
});
});
});
41 changes: 40 additions & 1 deletion src/article.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,50 @@ import { ArticleConfig, ArticleApi } from './dev-to-git.interface';
import got from 'got';
import fs from 'fs';

const imagesRe: RegExp = /\!\[.*\]\(.*\)/g;
const imageRe: RegExp = /\!\[(.*)\]\(([^ \)]*)(?: '(.*)')?\)/;

const excludeArticleFromPath = (path: string): string => path.replace(/\/[^\/]+\.md$/, '');

interface ImageToReplace {
localImage: string;
remoteImage: string;
}

export class Article {
constructor(private articleConfig: ArticleConfig) {}

private updateLocalImageLinks(article: string): string {
let searchImageResult,
localImagesToReplace: ImageToReplace[] = [];

while ((searchImageResult = imagesRe.exec(article))) {
const [image] = searchImageResult;

const [_, alt = null, path, title = null] = imageRe.exec(image) || [null, null, null, null];

if (path) {
const basePath: string = excludeArticleFromPath(this.articleConfig.relativePathToArticle.substr(2));
const assetPath = path.substr(2);

localImagesToReplace.push({
localImage: image,
remoteImage: `![${alt || ''}](https://raw.githubusercontent.com/${this.articleConfig.repository.username}\/${
this.articleConfig.repository.name
}/master/${basePath}/${assetPath}${title ? ` '${title}'` : ``})`,
});
}
}

return localImagesToReplace.reduce(
(articleTemp, imageToReplace) => articleTemp.replace(imageToReplace.localImage, imageToReplace.remoteImage),
article,
);
}

public readArticleOnDisk(): string {
return fs.readFileSync(this.articleConfig.relativePathToArticle).toString();
const article = fs.readFileSync(this.articleConfig.relativePathToArticle).toString();
return this.updateLocalImageLinks(article);
}

public publishArticle(token: string): got.GotPromise<any> {
Expand Down
13 changes: 11 additions & 2 deletions src/dev-to-git.interface.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
export interface ArticleConfig {
id: string;
export type Repository = {
readonly username: string;
readonly name: string;
};

export interface ArticleConfigFile {
id: number;
relativePathToArticle: string;
}

export interface ArticleConfig extends ArticleConfigFile {
repository: Repository;
}

// https://dev.to/api#available-json-parameters
export interface ArticleApi {
body_markdown: string;
Expand Down
35 changes: 33 additions & 2 deletions src/dev-to-git.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import minimist from 'minimist';
import fs from 'fs';
import dotenv from 'dotenv';
import { ArticleConfig } from './dev-to-git.interface';
import { ArticleConfig, ArticleConfigFile, Repository } from './dev-to-git.interface';
import { Article } from './article';

export const DEFAULT_CONFIG_PATH: string = './dev-to-git.json';

const repositoryRe: RegExp = /.*\/(.*)\/(.*)\.git/;

export class DevToGit {
private configPath: string = DEFAULT_CONFIG_PATH;
private token: string = '';
private repository: Repository = { username: '', name: '' };

constructor() {
dotenv.config();
Expand All @@ -19,21 +22,49 @@ export class DevToGit {
this.configPath = config;
}

this.extractRepository();

if (!process.env.DEV_TO_GIT_TOKEN) {
throw new Error('Token is required');
}

this.token = process.env.DEV_TO_GIT_TOKEN;
}

private extractRepository(): void {
try {
const packageJson = JSON.parse(fs.readFileSync('./package.json').toString());

const matchRepositoryUrl = (packageJson.repository.url as string).match(repositoryRe);

if (matchRepositoryUrl) {
const [_, username, name] = matchRepositoryUrl;
this.repository = { username, name };
} else {
throw new Error();
}
} catch (error) {
throw new Error(
'You must have within your "package.json" a "repository" attribute which is an object and contains itself an attribute "url" like the following: https://github-gitlab-whatever.com/username/repository-name.git - this will be used to generate images links if necessary',
);
}
}

public getConfigPath(): string {
return this.configPath;
}

public readConfigFile(): ArticleConfig[] {
// @todo check structure of the object

return JSON.parse(fs.readFileSync(this.getConfigPath()).toString()) as ArticleConfig[];
const articleConfigFiles: ArticleConfigFile[] = JSON.parse(
fs.readFileSync(this.getConfigPath()).toString(),
) as ArticleConfigFile[];

return articleConfigFiles.map(articleConfigFile => ({
...articleConfigFile,
repository: this.repository,
}));
}

public publishArticles() {
Expand Down
6 changes: 6 additions & 0 deletions test/article.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,9 @@ canonical_url:
# This is my awesome article!

Hey, some text!

Image 1: ![alt text 1](./image-1.png 'Title image 1')

Image 2: ![alt text 2](./image-2.png 'Title image 2')

Image 3: ![alt text 3](./image-3.png)
32 changes: 0 additions & 32 deletions test/dev-to-git.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,36 +37,4 @@ describe(`DevToGit`, () => {
});
});
});

// describe(`Article`, () => {
// describe(`Read`, () => {
// it(`should read an article from the configuration`, () => {
// const CUSTOM_CONFIG_PATH: string = './test/dev-to-git.json'
// process.argv = [
// 'don-t-care',
// 'don-t-care',
// '--config',
// CUSTOM_CONFIG_PATH
// ]
// const devToGit = new DevToGit()
// expect(devToGit.readArticleOnDisk()).toContain(
// 'This is my awesome article!'
// )
// expect(devToGit.readArticleOnDisk()).toContain('Hey, some text!')
// })
// })
// describe(`Publish`, () => {
// it(`should publish the article`, () => {
// const CUSTOM_CONFIG_PATH: string = './test/dev-to-git.json'
// process.argv = [
// 'don-t-care',
// 'don-t-care',
// '--config',
// CUSTOM_CONFIG_PATH
// ]
// const devToGit = new DevToGit()
// devToGit.publishArticle(devToGit.readConfigFile()[0])
// })
// })
// })
});

0 comments on commit c0b1797

Please sign in to comment.