Compare commits

...

9 Commits
v0.3.1 ... main

@ -13,7 +13,6 @@ finished, it was only a proof of concept. But now it works for created a entire
Next will be,
* Some more Tests
* Image Handling in Markdown
Maybe later,
@ -200,6 +199,30 @@ media:
---
```
Options from Sharp can be add on two different ways. As "options" for all Sizes or
you can simply add options to a sizes, that means the main options will be ignored.
```
---
title: "health goth DIY tattooed"
view: "home.njk"
meta:
description: "La"
media:
teaser:
src:
src: '_images/dog.jpg'
sizes:
- width: 300
- width: 500
height: 100
position: 'left'
alt: "cold-pressed"
options:
position: 'right'
---
```
## Queries
Queries can be used in Templates to get Pages.

@ -1,6 +1,6 @@
{
"name": "@site-o-mat/core",
"version": "0.3.1",
"version": "0.5.0",
"build": "webpack",
"author": "Björn Hase <me@herr-hase.wtf>",
"main": "index.js",

@ -3,6 +3,7 @@ title: "health goth DIY tattooed"
view: "page.njk"
meta:
description: "DSA yes plz hot chicken green juice"
robots: "noindex"
media:
src:
src: '_images/dog.jpg'
@ -21,4 +22,6 @@ bicycle rights sartorial godard slow-carb thundercats art party cray JOMO. Truff
## Bitters kale chips chambray activated charcoal
wolf keffiyeh hell of selfies. Wolf readymade shoreditch flexitarian venmo single-origin coffee, knausgaard fit actually street art cold-pressed iPhone gatekeep. Migas bruh adaptogen semiotics marfa pickled yuccie. Locavore normcore lomo, shoreditch fashion axe actually glossier iPhone photo booth blue bottle DIY XOXO williamsburg. Pinterest whatever taxidermy, kale chips prism XOXO schlitz twee tote bag woke swag. Wayfarers fashion axe heirloom humblebrag synth. Whatever succulents PBR&B, pop-up enamel pin echo park tonx stumptown taiyaki.
wolf keffiyeh hell of selfies. Wolf readymade shoreditch flexitarian venmo single-origin coffee, knausgaard fit actually street art cold-pressed iPhone gatekeep. Migas bruh adaptogen semiotics marfa pickled yuccie. Locavore normcore lomo, shoreditch fashion axe actually glossier iPhone photo booth blue bottle DIY XOXO williamsburg. Pinterest whatever taxidermy, kale chips prism XOXO schlitz twee tote bag woke swag. Wayfarers fashion axe heirloom humblebrag synth. Whatever succulents PBR&B, pop-up enamel pin echo park tonx stumptown taiyaki.
![_images/dog.jpg](_images/dog.jpg)

@ -37,11 +37,6 @@ class ConfigStore {
* @return {String|Object}
*/
get(key) {
if (!this._data?.[key]) {
throw new Error(key + ' not found in ConfigStore!')
}
return this._data[key]
}
}
@ -49,4 +44,4 @@ class ConfigStore {
// create instance
const instance = new ConfigStore();
module.exports = instance
module.exports = instance

@ -1,5 +1,6 @@
const path = require('path')
const fs = require('fs')
const configStore = require('./../config.js')
/**
* asset - checks manifest.json for given path and return
@ -16,7 +17,7 @@ function asset(staticPath) {
let result = staticPath
// path to mix-manifest
const file = path.join(path.resolve()) + 'mix-manifest.json'
const file = path.join(configStore.get('destination')) + '/mix-manifest.json'
if (fs.existsSync(file)) {

@ -25,7 +25,7 @@ class Block {
constructor(fileString, dirPath) {
// parse string of file
const parsedFile = parseMarkdownFile(fileString)
const parsedFile = parseMarkdownFile(fileString, dirPath)
this._dirPath = dirPath

@ -4,6 +4,7 @@ const sharp = require('sharp')
const mkdirp = require('mkdirp')
const crypto = require('crypto')
const slugify = require('slugify')
const assign = require('assign-deep')
const configStore = require('./../config.js')
@ -20,8 +21,8 @@ const configStore = require('./../config.js')
class Media {
constructor(path = NULL) {
this._path = path
constructor(dirPath = NULL) {
this._path = dirPath
this._DIR_ASSETS = '/assets/'
}
@ -42,11 +43,7 @@ class Media {
const filename = slugify(path.basename(src, extension))
// check for images in path
if (this._path && fs.existsSync(configStore.get('source') + this._path + '/' + src)) {
sourcePath = configStore.get('source') + this._path + '/' + src
} else {
sourcePath = configStore.get('source') + '/' + src
}
sourcePath = this._getSourcePath(src)
// getting sharp
const process = sharp(sourcePath)
@ -91,6 +88,30 @@ class Media {
return this._reduce(results)
}
/**
* get source path
*
* @param {String} src
* @return {String}
*
*/
_getSourcePath(src) {
let sourcePath = configStore.get('source') + '/' + src
if (Array.isArray(this._path)) {
if (fs.existsSync(configStore.get('source') + this._path[0] + '/' + src)) {
sourcePath = configStore.get('source') + this._path[0] + '/' + src
} else if (fs.existsSync(configStore.get('source') + this._path[1] + '/' + src)) {
sourcePath = configStore.get('source') + this._path[1] + '/' + src
}
} else if (this._path && fs.existsSync(configStore.get('source') + this._path + '/' + src)) {
sourcePath = configStore.get('source') + this._path + '/' + src
}
return sourcePath
}
/**
* if only full is in results, reduce object to string
*
@ -142,16 +163,34 @@ class Media {
*/
_writeFile(file, process, options) {
// resize without options and with options
if (file.sizes && Object.getOwnPropertyNames(options).length === 0 && Object.getOwnPropertyNames(file.sizes).length > 0) {
// if sizes have height or width with no optional parameters then check for merge of options
if (file.sizes &&
(((!file.sizes.height || !file.sizes.width) && Object.getOwnPropertyNames(file.sizes).length === 1) ||
(file.sizes.height && file.sizes.width && Object.getOwnPropertyNames(file.sizes).length === 2))) {
process.resize(this._mergeOptions(file.sizes, options))
// if already options in sizes ignore options
} else if (file.sizes) {
process.resize(file.sizes)
} else if (file.sizes && Object.getOwnPropertyNames(options).length > 0 && Object.getOwnPropertyNames(file.sizes).length > 0) {
process.resize(file.sizes, options)
}
process.toFile(configStore.get('destination') + file.path)
}
/**
* if options are exists merge them with sizes
*
* @param {object} sizes
* @param {Object} options
*/
_mergeOptions(sizes, options) {
if (Object.getOwnPropertyNames(options).length > 0) {
sizes = assign(options, sizes)
}
return sizes
}
/**
* generate destination path from hash of file
*

@ -3,9 +3,11 @@ const slugify = require('slugify')
const merge = require('lodash.merge')
const nunjucks = require('nunjucks')
const assign = require('assign-deep')
const fs = require('fs')
const Media = require('./../factories/media.js')
const parseMarkdownFile = require('./../parsers/markdown.js')
const configStore = require('./../config.js')
/**
* Page - building from markdown file
@ -30,8 +32,11 @@ class Page {
*/
constructor(file, parent, fileString, blocks = {}) {
// getting dirPath for files for page
this._dirPath = this._resolvePath(parent)
// parse file
const result = parseMarkdownFile(fileString)
const result = parseMarkdownFile(fileString, this._dirPath)
// fields merge by default values
this._fields = merge({
@ -46,13 +51,18 @@ class Page {
// adding filename for html as pathname and relative path in structure
this._filename = this._resolveFilename(file)
this._slug = this._resolveSlug(this._filename)
this._dirPath = this._resolvePath(parent)
this._permalink = this._dirPath
if (this._slug) {
this._permalink = this._dirPath + '/' + this._slug
}
// check if page is in subdirectory
if (fs.existsSync(configStore.get('source') + this._permalink) && this._slug) {
this._dirPath += '/' + this._slug
this._filename = 'index'
}
this._filename += '.' + this._fields.extensions
this._content = result.content
@ -113,7 +123,12 @@ class Page {
return fields
}
/**
*
*
*/
_resolveMediaSrc(field, dirPath) {
const media = new Media(dirPath)
if (typeof field === 'string' || field instanceof String) {
@ -121,7 +136,11 @@ class Page {
}
if (typeof field === 'object' || field instanceof Object) {
field = media.resolve(field.src, field.sizes)
if (field.options) {
field = media.resolve(field.src, field.sizes, field.options)
} else {
field = media.resolve(field.src, field.sizes)
}
}
return field

@ -1,5 +1,6 @@
const { XMLParser, XMLBuilder, XMLValidator} = require('fast-xml-parser')
const dayjs = require('dayjs')
const assign = require('assign-deep')
/**
*
@ -19,7 +20,11 @@ class Sitemap {
*
*/
constructor(site) {
this._site = site
this._site = assign({
'sitemap': {
'use_permalinks': true
}
}, site)
this._urls = []
}
@ -31,8 +36,15 @@ class Sitemap {
*/
addPage(page) {
if (this._isValid(page)) {
let path = page.permalink
if (this._site.sitemap.use_permalinks === false) {
path = page.path
}
this._urls.push({
loc: 'https://' + this._site.domain + page.path,
loc: 'https://' + this._site.domain + path,
lastmod: dayjs().format()
})
}
@ -59,14 +71,8 @@ class Sitemap {
let result = true
if (page.meta) {
page.meta = Object.entries(page.meta)
page.meta.forEach((meta) => {
if (meta['name'] === 'robots' && meta['content'].includes('noindex')) {
result = false
return;
}
})
if (page.meta && page.meta.robots && page.meta.robots.includes('noindex')) {
result = false
}
if (page.extensions !== 'html') {

@ -0,0 +1,112 @@
const { marked } = require('marked')
const configStore = require('./../config.js')
const Media = require('./../factories/media.js')
/**
*
*
*/
// copy from @marked/src/helpers.js, no export possible
function cleanUrl(sanitize, base, href) {
if (sanitize) {
let prot
try {
prot = decodeURIComponent(unescape(href))
.replace(nonWordAndColonTest, '')
.toLowerCase()
} catch (e) {
return null
}
if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) {
return null
}
}
if (base && !originIndependentUrl.test(href)) {
href = resolveUrl(base, href)
}
try {
href = encodeURI(href).replace(/%25/g, '%')
} catch (e) {
return null
}
return href
}
const renderer = {
/**
*
* @param {string} href
* @param {string} title
* @param {string} text
*/
link(href, title, text) {
href = cleanUrl(this.options.sanitize, this.options.baseUrl, href)
if (href === null) {
return text
}
let out = '<a href="' + href + '"'
if (title) {
out += ' title="' + title + '"'
}
// check if url is external and add target
if (href.match(/^(http|https):\/\//)) {
out += ' target="_blank" rel="noopener" '
}
out += '>' + text + '</a>'
return out
},
/**
*
* @param {string} href
* @param {string} title
* @param {string} text
*/
image(href, title, text) {
href = cleanUrl(this.options.sanitize, this.options.baseUrl, href)
if (href === null) {
return text
}
// check if href for image is relative
if (!href.match(/^(http|https):\/\//)) {
const markedDirPath = configStore.get('markedDirPath')
// check if dirPath are exists from options
if (markedDirPath || markedDirPath === '') {
const media = new Media(markedDirPath)
href = media.resolve(href)
}
}
let out = `<img src="${href}" alt="${text}"`
if (title) {
out += ` title="${title}"`
}
out += this.options.xhtml ? '/>' : '>'
return out
}
}
module.exports = renderer

@ -1,6 +1,9 @@
const yaml = require('js-yaml')
const { marked } = require('marked')
const configStore = require('./../config.js')
const renderer = require('./../marked/renderer.js')
/**
* parse string of file, parse yaml and parse markdown
*
@ -10,7 +13,7 @@ const { marked } = require('marked')
*
*/
function parseMarkdownFile(fileString) {
function parseMarkdownFile(fileString, dirPath = '') {
// regex get yaml section and markdown
// thanks to, https://github.com/getgrav/grav
@ -33,10 +36,20 @@ function parseMarkdownFile(fileString) {
// if markdown section exits parse it to html 6565
if (matches?.[3]) {
// reset configStore
configStore.set('markedDirPath', false)
// check for dirPath and set it to configStore for marked/renderer.js
if (dirPath || dirPath === '') {
configStore.set('markedDirPath', dirPath)
}
marked.use({ renderer })
result.content = marked.parse(matches[3])
}
return result
}
module.exports = parseMarkdownFile
module.exports = parseMarkdownFile

@ -29,6 +29,10 @@ describe('Page /index.md', function () {
it('permalink', function() {
assert.equal(page.permalink, '')
})
it('parsed content contains image', () => {
assert.match(page.content, /<img src="\/assets\/88c010ea\/4ca9b5f5\/6024c57d\/05899fae\/a33d9a45\/dog.webp" alt="_images\/dog.jpg">/)
})
})
describe('Page /blog/index.md', function () {
@ -50,7 +54,7 @@ describe('Page /blog/index.md', function () {
})
it('path', function() {
assert.equal(page.path, '/blog.html')
assert.equal(page.path, '/blog/index.html')
})
it('permalink', function() {
@ -59,9 +63,9 @@ describe('Page /blog/index.md', function () {
it('fields has media src', function() {
assert.deepEqual(page.media.src, {
"300": "/assets/88c010ea/4ca9b5f5/6024c57d/05899fae/a33d9a45/dog300.webp",
"500x100": "/assets/88c010ea/4ca9b5f5/6024c57d/05899fae/a33d9a45/dog500x100.webp",
"full": "/assets/88c010ea/4ca9b5f5/6024c57d/05899fae/a33d9a45/dog.webp"
"300": "/assets/a6c45d17/11bf0a4e/a2b1d75d/dc85ca56/71c63294/dog300.webp",
"500x100": "/assets/a6c45d17/11bf0a4e/a2b1d75d/dc85ca56/71c63294/dog500x100.webp",
"full": "/assets/a6c45d17/11bf0a4e/a2b1d75d/dc85ca56/71c63294/dog.webp"
})
})
})

@ -39,7 +39,8 @@ describe('Parser Markdown', function () {
title: 'health goth DIY tattooed',
view: 'page.njk',
meta: {
description: 'DSA yes plz hot chicken green juice'
description: 'DSA yes plz hot chicken green juice',
robots: 'noindex'
}
})
})

@ -25,6 +25,10 @@ describe('Sitemap', function () {
// check results
it('loc-tag with url', function() {
assert.match(sitemap.getXmlAsString(), /<loc>https:\/\/test.lan\/blog\/article.html<\/loc>/)
assert.match(sitemap.getXmlAsString(), /<loc>https:\/\/test.lan\/blog\/article<\/loc>/)
})
it('loc-tag has robotos:noindex and has missing', function() {
assert.notMatch(sitemap.getXmlAsString(), /<loc>https:\/\/test.lan\/<\/loc>/)
})
})

Loading…
Cancel
Save