mirror of
https://github.com/naturalcrit/homebrewery.git
synced 2026-01-05 12:22:44 +00:00
Merge branch 'master' into imageWrappin
This commit is contained in:
@@ -67,6 +67,9 @@ jobs:
|
|||||||
- run:
|
- run:
|
||||||
name: Test - Definition Lists
|
name: Test - Definition Lists
|
||||||
command: npm run test:definition-lists
|
command: npm run test:definition-lists
|
||||||
|
- run:
|
||||||
|
name: Test - Hard Breaks
|
||||||
|
command: npm run test:hard-breaks
|
||||||
- run:
|
- run:
|
||||||
name: Test - Variables
|
name: Test - Variables
|
||||||
command: npm run test:variables
|
command: npm run test:variables
|
||||||
|
|||||||
9
package-lock.json
generated
9
package-lock.json
generated
@@ -58,7 +58,7 @@
|
|||||||
"jest-expect-message": "^1.1.3",
|
"jest-expect-message": "^1.1.3",
|
||||||
"postcss-less": "^6.0.0",
|
"postcss-less": "^6.0.0",
|
||||||
"stylelint": "^16.8.2",
|
"stylelint": "^16.8.2",
|
||||||
"stylelint-config-recess-order": "^5.0.1",
|
"stylelint-config-recess-order": "^5.1.0",
|
||||||
"stylelint-config-recommended": "^14.0.1",
|
"stylelint-config-recommended": "^14.0.1",
|
||||||
"supertest": "^7.0.0"
|
"supertest": "^7.0.0"
|
||||||
},
|
},
|
||||||
@@ -13678,11 +13678,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/stylelint-config-recess-order": {
|
"node_modules/stylelint-config-recess-order": {
|
||||||
"version": "5.0.1",
|
"version": "5.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/stylelint-config-recess-order/-/stylelint-config-recess-order-5.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/stylelint-config-recess-order/-/stylelint-config-recess-order-5.1.0.tgz",
|
||||||
"integrity": "sha512-rKbGkoa3h0rINrGln9TFVowvSCLgPJC5O0EuPiqlqWcJMb1lImEtXktcjFCVz+hwtSUiHD3ijJc3vP9muFOgJg==",
|
"integrity": "sha512-ddapCF6B/kEtQYIFhQFReQ0dvK1ZdgJDM/SGFtIyeooYDbqaJqcOlGkRRGaVErCQYJY/bPSPsLRS2LdQtLJUVQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"stylelint-order": "^6.0.4"
|
"stylelint-order": "^6.0.4"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
"test:mustache-syntax:block": "jest \".*(mustache-syntax).*\" -t '^Block:.*' --verbose --noStackTrace",
|
"test:mustache-syntax:block": "jest \".*(mustache-syntax).*\" -t '^Block:.*' --verbose --noStackTrace",
|
||||||
"test:mustache-syntax:injection": "jest \".*(mustache-syntax).*\" -t '^Injection:.*' --verbose --noStackTrace",
|
"test:mustache-syntax:injection": "jest \".*(mustache-syntax).*\" -t '^Injection:.*' --verbose --noStackTrace",
|
||||||
"test:definition-lists": "jest tests/markdown/definition-lists.test.js --verbose --noStackTrace",
|
"test:definition-lists": "jest tests/markdown/definition-lists.test.js --verbose --noStackTrace",
|
||||||
|
"test:hard-breaks": "jest tests/markdown/hard-breaks.test.js --verbose --noStackTrace",
|
||||||
"test:emojis": "jest tests/markdown/emojis.test.js --verbose --noStackTrace",
|
"test:emojis": "jest tests/markdown/emojis.test.js --verbose --noStackTrace",
|
||||||
"test:route": "jest tests/routes/static-pages.test.js --verbose",
|
"test:route": "jest tests/routes/static-pages.test.js --verbose",
|
||||||
"phb": "node scripts/phb.js",
|
"phb": "node scripts/phb.js",
|
||||||
@@ -131,7 +132,7 @@
|
|||||||
"jest-expect-message": "^1.1.3",
|
"jest-expect-message": "^1.1.3",
|
||||||
"postcss-less": "^6.0.0",
|
"postcss-less": "^6.0.0",
|
||||||
"stylelint": "^16.8.2",
|
"stylelint": "^16.8.2",
|
||||||
"stylelint-config-recess-order": "^5.0.1",
|
"stylelint-config-recess-order": "^5.1.0",
|
||||||
"stylelint-config-recommended": "^14.0.1",
|
"stylelint-config-recommended": "^14.0.1",
|
||||||
"supertest": "^7.0.0"
|
"supertest": "^7.0.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ const GoogleActions = require('./googleActions.js');
|
|||||||
const serveCompressedStaticAssets = require('./static-assets.mv.js');
|
const serveCompressedStaticAssets = require('./static-assets.mv.js');
|
||||||
const sanitizeFilename = require('sanitize-filename');
|
const sanitizeFilename = require('sanitize-filename');
|
||||||
const asyncHandler = require('express-async-handler');
|
const asyncHandler = require('express-async-handler');
|
||||||
|
const templateFn = require('./../client/template.js');
|
||||||
|
|
||||||
const { DEFAULT_BREW } = require('./brewDefaults.js');
|
const { DEFAULT_BREW } = require('./brewDefaults.js');
|
||||||
|
|
||||||
@@ -420,8 +421,16 @@ if(isLocalEnvironment){
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Send rendered page
|
||||||
|
app.use(asyncHandler(async (req, res, next)=>{
|
||||||
|
if (!req.route) return res.redirect('/'); // Catch-all for invalid routes
|
||||||
|
|
||||||
|
const page = await renderPage(req, res);
|
||||||
|
if(!page) return;
|
||||||
|
res.send(page);
|
||||||
|
}));
|
||||||
|
|
||||||
//Render the page
|
//Render the page
|
||||||
const templateFn = require('./../client/template.js');
|
|
||||||
const renderPage = async (req, res)=>{
|
const renderPage = async (req, res)=>{
|
||||||
// Create configuration object
|
// Create configuration object
|
||||||
const configuration = {
|
const configuration = {
|
||||||
@@ -450,13 +459,6 @@ const renderPage = async (req, res)=>{
|
|||||||
return page;
|
return page;
|
||||||
};
|
};
|
||||||
|
|
||||||
//Send rendered page
|
|
||||||
app.use(asyncHandler(async (req, res, next)=>{
|
|
||||||
const page = await renderPage(req, res);
|
|
||||||
if(!page) return;
|
|
||||||
res.send(page);
|
|
||||||
}));
|
|
||||||
|
|
||||||
//v=====----- Error-Handling Middleware -----=====v//
|
//v=====----- Error-Handling Middleware -----=====v//
|
||||||
//Format Errors as plain objects so all fields will appear in the string sent
|
//Format Errors as plain objects so all fields will appear in the string sent
|
||||||
const formatErrors = (key, value)=>{
|
const formatErrors = (key, value)=>{
|
||||||
|
|||||||
@@ -370,6 +370,27 @@ const superSubScripts = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const forcedParagraphBreaks = {
|
||||||
|
name : 'hardBreaks',
|
||||||
|
level : 'block',
|
||||||
|
start(src) { return src.match(/\n:+$/m)?.index; }, // Hint to Marked.js to stop and check for a match
|
||||||
|
tokenizer(src, tokens) {
|
||||||
|
const regex = /^(:+)(?:\n|$)/ym;
|
||||||
|
const match = regex.exec(src);
|
||||||
|
if(match?.length) {
|
||||||
|
return {
|
||||||
|
type : 'hardBreaks', // Should match "name" above
|
||||||
|
raw : match[0], // Text to consume from the source
|
||||||
|
length : match[1].length,
|
||||||
|
text : ''
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
renderer(token) {
|
||||||
|
return `<div class='blank'></div>`.repeat(token.length).concat('\n');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const definitionListsSingleLine = {
|
const definitionListsSingleLine = {
|
||||||
name : 'definitionListsSingleLine',
|
name : 'definitionListsSingleLine',
|
||||||
level : 'block',
|
level : 'block',
|
||||||
@@ -414,9 +435,9 @@ const definitionListsSingleLine = {
|
|||||||
const definitionListsMultiLine = {
|
const definitionListsMultiLine = {
|
||||||
name : 'definitionListsMultiLine',
|
name : 'definitionListsMultiLine',
|
||||||
level : 'block',
|
level : 'block',
|
||||||
start(src) { return src.match(/\n[^\n]*\n::/m)?.index; }, // Hint to Marked.js to stop and check for a match
|
start(src) { return src.match(/\n[^\n]*\n::[^:\n]/m)?.index; }, // Hint to Marked.js to stop and check for a match
|
||||||
tokenizer(src, tokens) {
|
tokenizer(src, tokens) {
|
||||||
const regex = /(\n?\n?(?!::)[^\n]+?(?=\n::))|\n::(.(?:.|\n)*?(?=(?:\n::)|(?:\n\n)|$))/y;
|
const regex = /(\n?\n?(?!::)[^\n]+?(?=\n::[^:\n]))|\n::([^:\n](?:.|\n)*?(?=(?:\n::)|(?:\n\n)|$))/y;
|
||||||
let match;
|
let match;
|
||||||
let endIndex = 0;
|
let endIndex = 0;
|
||||||
const definitions = [];
|
const definitions = [];
|
||||||
@@ -721,7 +742,8 @@ const MarkedEmojiOptions = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Marked.use(MarkedVariables());
|
Marked.use(MarkedVariables());
|
||||||
Marked.use({ extensions: [definitionListsMultiLine, definitionListsSingleLine, superSubScripts, mustacheSpans, mustacheDivs, mustacheInjectInline] });
|
Marked.use({ extensions : [definitionListsMultiLine, definitionListsSingleLine, forcedParagraphBreaks, superSubScripts,
|
||||||
|
mustacheSpans, mustacheDivs, mustacheInjectInline] });
|
||||||
Marked.use(mustacheInjectBlock);
|
Marked.use(mustacheInjectBlock);
|
||||||
Marked.use({ renderer: renderer, tokenizer: tokenizer, mangle: false });
|
Marked.use({ renderer: renderer, tokenizer: tokenizer, mangle: false });
|
||||||
Marked.use(MarkedExtendedTables(), MarkedGFMHeadingId(), MarkedSmartypantsLite(), MarkedEmojis(MarkedEmojiOptions));
|
Marked.use(MarkedExtendedTables(), MarkedGFMHeadingId(), MarkedSmartypantsLite(), MarkedEmojis(MarkedEmojiOptions));
|
||||||
@@ -833,8 +855,7 @@ module.exports = {
|
|||||||
varsQueue = []; //Could move into MarkedVariables()
|
varsQueue = []; //Could move into MarkedVariables()
|
||||||
globalPageNumber = pageNumber;
|
globalPageNumber = pageNumber;
|
||||||
|
|
||||||
rawBrewText = rawBrewText.replace(/^\\column$/gm, `\n<div class='columnSplit'></div>\n`)
|
rawBrewText = rawBrewText.replace(/^\\column$/gm, `\n<div class='columnSplit'></div>\n`);
|
||||||
.replace(/^(:+)$/gm, (match)=>`${`<div class='blank'></div>`.repeat(match.length)}\n`);
|
|
||||||
const opts = Marked.defaults;
|
const opts = Marked.defaults;
|
||||||
|
|
||||||
rawBrewText = opts.hooks.preprocess(rawBrewText);
|
rawBrewText = opts.hooks.preprocess(rawBrewText);
|
||||||
|
|||||||
@@ -88,4 +88,16 @@ describe('Multiline Definition Lists', ()=>{
|
|||||||
const rendered = Markdown.render(source).trim();
|
const rendered = Markdown.render(source).trim();
|
||||||
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<dl><dt>Term 1</dt><dd>Inline definition 1</dd>\n<dt></dt><dd>Inline definition 2 (no DT)</dd>\n</dl>');
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('<dl><dt>Term 1</dt><dd>Inline definition 1</dd>\n<dt></dt><dd>Inline definition 2 (no DT)</dd>\n</dl>');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Multiline Definition Term must have at least one non-empty Definition', function() {
|
||||||
|
const source = 'Term 1\n::';
|
||||||
|
const rendered = Markdown.render(source).trim();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<p>Term 1</p>\n<div class='blank'></div><div class='blank'></div>`);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Multiline Definition List must have at least one non-newline character after ::', function() {
|
||||||
|
const source = 'Term 1\n::\nDefinition 1\n\n';
|
||||||
|
const rendered = Markdown.render(source).trim();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<p>Term 1</p>\n<div class='blank'></div><div class='blank'></div>\n<p>Definition 1</p>`);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
47
tests/markdown/hard-breaks.test.js
Normal file
47
tests/markdown/hard-breaks.test.js
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
/* eslint-disable max-lines */
|
||||||
|
|
||||||
|
const Markdown = require('naturalcrit/markdown.js');
|
||||||
|
|
||||||
|
describe('Hard Breaks', ()=>{
|
||||||
|
test('Single Break', function() {
|
||||||
|
const source = ':\n\n';
|
||||||
|
const rendered = Markdown.render(source).trim();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class='blank'></div>`);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Double Break', function() {
|
||||||
|
const source = '::\n\n';
|
||||||
|
const rendered = Markdown.render(source).trim();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class='blank'></div><div class='blank'></div>`);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Triple Break', function() {
|
||||||
|
const source = ':::\n\n';
|
||||||
|
const rendered = Markdown.render(source).trim();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class='blank'></div><div class='blank'></div><div class='blank'></div>`);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Many Break', function() {
|
||||||
|
const source = '::::::::::\n\n';
|
||||||
|
const rendered = Markdown.render(source).trim();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class='blank'></div><div class='blank'></div><div class='blank'></div><div class='blank'></div><div class='blank'></div><div class='blank'></div><div class='blank'></div><div class='blank'></div><div class='blank'></div><div class='blank'></div>`);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Multiple sets of Breaks', function() {
|
||||||
|
const source = ':::\n:::\n:::';
|
||||||
|
const rendered = Markdown.render(source).trim();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<div class='blank'></div><div class='blank'></div><div class='blank'></div>\n<div class='blank'></div><div class='blank'></div><div class='blank'></div>\n<div class='blank'></div><div class='blank'></div><div class='blank'></div>`);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Break directly between two paragraphs', function() {
|
||||||
|
const source = 'Line 1\n::\nLine 2';
|
||||||
|
const rendered = Markdown.render(source).trim();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<p>Line 1</p>\n<div class='blank'></div><div class='blank'></div>\n<p>Line 2</p>`);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Ignored inside a code block', function() {
|
||||||
|
const source = '```\n\n:\n\n```\n';
|
||||||
|
const rendered = Markdown.render(source).trim();
|
||||||
|
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<pre><code>\n:\n</code></pre>`);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user