diff --git a/output.png b/output.png new file mode 100644 index 0000000..ba25a8b Binary files /dev/null and b/output.png differ diff --git a/package-lock.json b/package-lock.json index 4cc797c..292ac06 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "happy-site-webpack-plugin", "version": "0.1.0", "dependencies": { + "crypto": "^1.0.1", "dayjs": "^1.11.6", "fast-xml-parser": "^4.0.11", "html-minifier": "^4.0.0", @@ -206,6 +207,12 @@ "node": ">= 6" } }, + "node_modules/crypto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", + "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==", + "deprecated": "This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in." + }, "node_modules/dayjs": { "version": "1.11.6", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.6.tgz", @@ -967,6 +974,11 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==" }, + "crypto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/crypto/-/crypto-1.0.1.tgz", + "integrity": "sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==" + }, "dayjs": { "version": "1.11.6", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.6.tgz", diff --git a/package.json b/package.json index a82e9f0..99cb31e 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "main": "index.js", "description": "Generating a Website from a Markdown Generator", "dependencies": { + "crypto": "^1.0.1", "dayjs": "^1.11.6", "fast-xml-parser": "^4.0.11", "html-minifier": "^4.0.0", diff --git a/resources/site/blog/cliche-vegan-messenger/index.md b/resources/site/blog/cliche-vegan-messenger/index.md deleted file mode 100644 index e30a302..0000000 --- a/resources/site/blog/cliche-vegan-messenger/index.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -title: "Cliche Vegan Messenger" -date_published: "2022-01-10 10:00" -view: "post.njk" -tags: - - tumeric - - heard - - bag -media: - teaser: 'jep.jpg' -meta: - description: "DSA yes plz hot chicken green juice" - robots: "index, follow" ---- -## TEST \ No newline at end of file diff --git a/resources/site/blog/index.md b/resources/site/blog/index.md deleted file mode 100644 index 988d654..0000000 --- a/resources/site/blog/index.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: "Blog" -view: "blog.njk" -meta: - description: "DSA yes plz hot chicken green juice" - robots: "index, follow" ---- \ No newline at end of file diff --git a/resources/views/home.njk b/resources/views/home.njk index 1f46c37..b209dda 100644 --- a/resources/views/home.njk +++ b/resources/views/home.njk @@ -2,7 +2,7 @@ {% block main %} - + {{ page.content | safe }} {% endblock %} \ No newline at end of file diff --git a/src/config.js b/src/config.js new file mode 100644 index 0000000..53a8f40 --- /dev/null +++ b/src/config.js @@ -0,0 +1,36 @@ +/** + * + * + */ + +class ConfigStore { + + constructor() { + if (!ConfigStore.instance) { + ConfigStore.instance = this; + this._data = {} + } + + return ConfigStore.instance; + } + + /** + * + */ + set(key, value) { + this._data[key] = value + } + + /** + * + * + */ + get(key) { + return this._data[key] + } +} + +// create instance +const instance = new ConfigStore(); + +export default instance \ No newline at end of file diff --git a/src/engine.js b/src/engine.js index 074e7b4..87c6125 100644 --- a/src/engine.js +++ b/src/engine.js @@ -1,7 +1,8 @@ import nunjucks from 'nunjucks' import { minify } from 'html-minifier' +import fs from 'fs' -import { asset, media } from './helpers/engine.js' +import { asset, resize } from './helpers/engine.js' /** * engine - handle eta.js @@ -18,13 +19,19 @@ class Engine { // merge options this._options = Object.assign({}, { autoescapes: true, - throwOnUndefined: true + throwOnUndefined: true, + web: { + async: true + } }, options) this.nunjucks = nunjucks.configure(views, this._options) - this.nunjucks.addFilter('media', function(options) { - return media(options) - }) + this.nunjucks.addFilter('resize', (...args) => { + const done = args.pop() + const options = args?.[2] ? {} : args[2] + + resize(args[0], args[1], options, done) + }, true) // adding defaults for view, function and data from config.yml this._defaults = { @@ -41,12 +48,11 @@ class Engine { * @return {string} * */ - render(view, data) { + render(view, data, done) { data = Object.assign({}, data, this._defaults) - return minify(this.nunjucks.render(view, data), { - removeComments: true, - collapseWhitespace: true + this.nunjucks.render(view, data, (error, response) => { + done(error, response) }) } diff --git a/src/happySite.js b/src/happySite.js index ae3e9f9..48a167a 100644 --- a/src/happySite.js +++ b/src/happySite.js @@ -8,12 +8,17 @@ import Sitemap from './sitemap.js' import PagesQuery from './queries/pages.js' import parseYamlFile from './parsers/yaml.js' +import configStore from './config.js' + /** + * Main + * + * * * @author Björn Hase * @license http://opensource.org/licenses/MIT The MIT License * @link https://gitea.node001.net/HerrHase/happy-site-webpack-plugin.git - * + * */ class HappySite { @@ -29,6 +34,10 @@ class HappySite { this._destination = destination this._views = views + configStore.set('source', source) + configStore.set('destination', destination) + configStore.set('views', views) + // get config for site if (fs.existsSync(this._source + '/site.yml')) { const file = fs.readFileSync(this._source + '/site.yml', 'utf8') @@ -37,6 +46,8 @@ class HappySite { throw new Error('site.yml not found in ' + this._source + '!') } + configStore.set('site', this._site) + this._engine = new Engine(views, this._site) } @@ -52,14 +63,26 @@ class HappySite { // run through pages and generate html files results.forEach((page) => { - const content = page.render(this._engine) + page.render(this._engine, (error, content) => { - // create directories and write file from page - mkdirp(this._destination + page.pathname).then(() => { - fs.writeFileSync(this._destination + page.pathname + '/' + page.filename, content) - }) + // show errors + if (error) { + console.error(error) + } + + // if no content show error message + if (!content) { + console.error('Error! Rendering Page ' + '"' + page.filename + '" is null') + return; + } - sitemap.addPage(page) + // create directories and write file from page + mkdirp(this._destination + page.pathname).then(() => { + fs.writeFileSync(this._destination + page.pathname + '/' + page.filename, content) + }) + + sitemap.addPage(page) + }) }) // write sitemap diff --git a/src/helpers/engine.js b/src/helpers/engine.js index e408906..b858996 100644 --- a/src/helpers/engine.js +++ b/src/helpers/engine.js @@ -1,8 +1,7 @@ import path from 'path' import * as fs from 'fs' -import sharp from 'sharp' -const basePath = path.join(path.resolve()) +import Media from './../media.js' /** * asset - checks manifest.json for given path and return @@ -19,7 +18,7 @@ function asset(staticPath) let result = staticPath // path to mix-manifest - const file = basePath + 'mix-manifest.json' + const file = path.join(path.resolve()) + 'mix-manifest.json' if (fs.existsSync(file)) { @@ -43,14 +42,12 @@ function asset(staticPath) * */ -function media(src, options) +async function resize(src, sizes, options, done) { - console.log(basePath) - console.log(path.resolve(src)) - sharp(src) - .toFile('output.png', (error, info) => { console.log(error) }) + const media = new Media() - return src + src = await media.resize(src, sizes, options) + done(null, src) } -export { asset, media } \ No newline at end of file +export { asset, resize } \ No newline at end of file diff --git a/src/media.js b/src/media.js new file mode 100644 index 0000000..2a140c2 --- /dev/null +++ b/src/media.js @@ -0,0 +1,101 @@ +import path from 'path' +import * as fs from 'fs' + +import sharp from 'sharp' +import mkdirp from 'mkdirp' +import crypto from 'crypto' +import slugify from 'slugify' + +import configStore from './config.js' + +/** + * + * + */ +class Media { + + constructor() { + this._DIR_ASSETS = '/assets/' + } + + /** + * + * @param {string} srcPath + * @param {object} sizes + * @param {Object} [options={}] + * @return {string} + * + */ + async resize(src, sizes, options = {}) { + + this._extension = path.extname(src) + this._filename = slugify(path.basename(src, this._extension)) + + this._process = await sharp(configStore.get('source') + '/' + src) + + // resize without options and with options + if (Object.getOwnPropertyNames(options).length === 0) { + await this._process + .resize(sizes) + } else { + this._process + .resize(sizes, options) + } + + // optimize + this._optimize() + + const fileBuffer = await this._process + .toBuffer() + + const relativeDestinationPath = this._DIR_ASSETS + this._resolveRelativeDestinationPath(fileBuffer) + + // create directories and write file + mkdirp.sync(configStore.get('destination') + relativeDestinationPath) + fs.writeFileSync(configStore.get('destination') + relativeDestinationPath + '/' + this._filename + this._extension, fileBuffer) + + return relativeDestinationPath + '/' + this._filename + this._extension + } + + /** + * @TODO much nicer to add a hook system so behavior can be change + * + * + * @param {string} extension + * + */ + _optimize() { + if (this._extension === '.gif') { + this._process + .gif({ + reoptimise: true + }) + } else { + + // change extension + this._extension = '.webp' + this._process + + .webp({ + lossless: true + }) + } + } + + /** + * resolve path to write file, hash will be get from fileBuffer and + * + * + * @param {object} fileBuffer + * @return {string} + * + */ + _resolveRelativeDestinationPath(fileBuffer) { + const hash = crypto.createHash('sha1') + hash.update(fileBuffer) + + return hash.digest('hex').match(new RegExp('.{1,8}', 'g')).join('/') + } +} + +export default Media \ No newline at end of file diff --git a/src/models/page.js b/src/models/page.js index 69b34f8..c253364 100644 --- a/src/models/page.js +++ b/src/models/page.js @@ -1,6 +1,7 @@ import path from 'path' import slugify from 'slugify' import merge from 'lodash.merge' +import nunjucks from 'nunjucks' import parseMarkdownFile from './../parsers/markdown.js' @@ -53,7 +54,7 @@ class Page { * @return {string} * */ - render(engine) { + render(engine, done) { const page = Object.assign({}, this._fields) @@ -61,11 +62,9 @@ class Page { page.blocks = this._blocks page.path = this.pathname + '/' + this.filename - const result = engine.render(this._fields.view, { + return engine.render(this._fields.view, { page: page - }) - - return result + }, done) } /** diff --git a/web.png b/web.png new file mode 100644 index 0000000..ba25a8b Binary files /dev/null and b/web.png differ