diff --git a/src/images/varejao.jpeg b/src/images/varejao.jpeg new file mode 100644 index 0000000..7ebad97 Binary files /dev/null and b/src/images/varejao.jpeg differ diff --git a/src/mocks/images.md b/src/mocks/images.md new file mode 100644 index 0000000..ec3efb7 --- /dev/null +++ b/src/mocks/images.md @@ -0,0 +1,11 @@ +![Adriana Varejão Gallery](../images/varejao.jpeg) + +_Adriana Varejão Gallery_ + +![Valeska Soares Gallery](http://valeskasoares.net/wp-content/uploads/2009/10/DSC8218.jpg) + +_Valeska Soares Gallery_ + +![Sonic Pavillion](https://www.inhotim.org.br/wp-content/webp-express/webp-images/uploads/2021/02/CAPA_02042021DSC07311Joao_Kehl_.jpg.webp) + +_Sonic Pavillion_ diff --git a/src/services/article.js b/src/services/article.js index e62fc2a..9d711c0 100644 --- a/src/services/article.js +++ b/src/services/article.js @@ -1,8 +1,9 @@ -const markdownService = require('./markdown'); +const assetsService = require('./assets'); const dateService = require('./date'); const domService = require('./dom'); const excerptService = require('./excerpt'); const { fileService } = require('./file'); +const markdownService = require('./markdown'); const stylesService = require('./styles'); const summaryService = require('./summary'); const templateService = require('./template'); @@ -39,7 +40,7 @@ function fillTemplate(template, article, summary){ const $ = parseHTMLString(template.replace('{{article}}', wrapArticle(summary, article))); $('html').attr('lang', summary.lang); $('head').append(`${summary.title}`).append(buildMetaTags(summary)); - return stylesService.appendBaseStylesheet($.html()); + return stylesService.appendBaseStylesheet(assetsService.handleRelativeImages($.html())); } function wrapArticle({ title, date, lang }, article){ diff --git a/src/services/article.test.js b/src/services/article.test.js new file mode 100644 index 0000000..bcff8a6 --- /dev/null +++ b/src/services/article.test.js @@ -0,0 +1,32 @@ +const path = require('path'); +const configService = require('./config'); +const { fileService } = require('./file'); +const articleService = require('./article'); + +describe('Articles Service', () => { + function mockTrivenConfig(){ + const config = { + sourceDirectory: path.join(__dirname, '../mocks'), + outputDirectory: path.join(process.cwd(), './triven') + }; + configService.get = jest.fn(() => config); + return config; + } + + beforeEach(() => { + fileService.copySync = jest.fn(); + }); + + it('should copy relative images to assets directory and update its source in markup', () => { + const { outputDirectory } = mockTrivenConfig(); + const filepath = path.join(__dirname, '../mocks/images.md'); + const { article } = articleService.build(filepath); + const expectedFilename = 'varejao-a8774002d2f7fef27b27f665c7e7227c.jpeg'; + expect(article).toContain(`Adriana Varejão Gallery`); + expect(fileService.copySync).toHaveBeenCalledTimes(1); + expect(fileService.copySync).toHaveBeenCalledWith( + `${path.join(__dirname, '../images/varejao.jpeg')}`, + `${outputDirectory}/assets/${expectedFilename}` + ); + }); +}); diff --git a/src/services/assets.js b/src/services/assets.js index 75d4f8d..ac638c0 100644 --- a/src/services/assets.js +++ b/src/services/assets.js @@ -1,4 +1,6 @@ const md5 = require('md5'); +const path = require('path'); +const domService = require('./dom'); const configService = require('./config'); const minifyService = require('./minify'); const { fileService } = require('./file'); @@ -7,34 +9,79 @@ const _public = {}; let cache = []; -_public.save = filepath => { - const cachedFilename = findFilenameByFilepath(filepath); - if(cachedFilename) return cachedFilename; - const filename = saveAsset(filepath); - cache.push({ filepath, filename }); - return filename; +_public.save = filepath => handle(filepath, () => saveAsset(filepath)); + +_public.copy = filepath => handle(filepath, () => copyAsset(filepath)); + +_public.handleRelativeImages = htmlString => { + const $ = domService.parseHTMLString(htmlString); + $('img').filter((index, el) => isRelativeImage($(el).attr('src'))).each((index, el) => { + const filename = _public.copy(buildLocalImageFilepath($(el).attr('src'))); + $(el).attr('src', `${getAssetsDirectoryName()}/${filename}`); + }); + return $.html(); }; _public.flushCache = () => { cache = []; }; +function handle(filepath, act){ + const cachedFilename = findFilenameByFilepath(filepath); + if(cachedFilename) return cachedFilename; + const filename = act(); + cache.push({ filepath, filename }); + return filename; +} + function findFilenameByFilepath(filepath){ const cachedItem = cache.find(item => item.filepath === filepath); return cachedItem && cachedItem.filename; } function saveAsset(filepath){ + return store(filepath, ({ filename, file }) => { + fileService.write(`${getAssetsDirectoryFilepath()}/${filename}`, file); + }); +} + +function copyAsset(filepath){ + return store(filepath, ({ filename }) => { + fileService.copySync(filepath, `${getAssetsDirectoryFilepath()}/${filename}`); + }); +} + +function store(filepath, storeAsset){ + const { filename, file } = identifyFile(filepath); + storeAsset({ filename, file }); + return filename; +} + +function identifyFile(filepath){ const { name, extension } = fileService.getFileInfoByFilepath(filepath); const file = minifyService.minifyByFilepath(filepath); const filename = `${name}-${md5(file)}.${extension}`; - fileService.write(`${getAssetsDirectory()}/${filename}`, file); - return filename; + return { filename, file }; } -function getAssetsDirectory(){ +function getAssetsDirectoryFilepath(){ const { outputDirectory } = configService.get(); - return `${outputDirectory}/assets`; + return `${outputDirectory}/${getAssetsDirectoryName()}`; } +function getAssetsDirectoryName(){ + return 'assets'; +} + +function isRelativeImage(imgSrc){ + return !isAbsoluteSrcRegex.test(imgSrc); +} + +function buildLocalImageFilepath(imgSrc){ + const { sourceDirectory } = configService.get(); + return path.join(sourceDirectory, imgSrc); +} + +const isAbsoluteSrcRegex = new RegExp(/^https?:\/\/.+/); + module.exports = _public;