Compare commits
9 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
29140453e8 | ||
|
|
d786f02c9a | ||
|
|
6f508915f3 | ||
|
|
8ffa99f8ac | ||
|
|
ca111b50ae | ||
|
|
b41d0124b7 | ||
|
|
d64ffb77a1 | ||
|
|
adeb5731c1 | ||
|
|
f38a464c94 |
80
_config/eleventy-plugin-cooklang.js
Normal file
80
_config/eleventy-plugin-cooklang.js
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
import { Parser } from '@cooklang/cooklang';
|
||||
import fs from 'fs';
|
||||
|
||||
let config = {}
|
||||
export default function (eleventyConfig, pluginOptions) {
|
||||
config = pluginOptions;
|
||||
eleventyConfig.addTemplateFormats('cook');
|
||||
eleventyConfig.addExtension("cook", cookExtension);
|
||||
};
|
||||
|
||||
const cookExtension = {
|
||||
getData: async function (inputPath) {
|
||||
const content = fs.readFileSync(inputPath, "utf-8");
|
||||
|
||||
// Parse recipe using cooklang
|
||||
const parser = new Parser();
|
||||
const recipe = parser.parse(content);
|
||||
|
||||
let sections = [];
|
||||
let ingredients = recipe.recipe.ingredients || [];
|
||||
let cookware = recipe.recipe.cookware || [];
|
||||
let timers = recipe.recipe.timers || [];
|
||||
|
||||
const metadata = recipe?.metadata || [];
|
||||
|
||||
recipe.recipe.sections.forEach((section, i) => {
|
||||
if (!sections[i]) sections[i] = {};
|
||||
if (section.name != null) {
|
||||
sections[i].title = { type: "title", content: section.name };
|
||||
}
|
||||
section.content.forEach((step, stepI) => {
|
||||
if (!sections[i].content) sections[i].content = [];
|
||||
let steps = [];
|
||||
|
||||
step.value.items.forEach(item => {
|
||||
if (item.type === 'text') {
|
||||
steps.push({ type: "text", content: item.value });
|
||||
} else if (item.type === 'ingredient') {
|
||||
let ingredient = recipe.recipe.ingredients[item.index];
|
||||
steps.push({ type: "ingredient", content: ingredient.name, ...ingredient });
|
||||
} else if (item.type === 'cookware') {
|
||||
let cookware = recipe.recipe.cookware[item.index];
|
||||
steps.push({ type: "cookware", content: cookware.name, ...cookware});
|
||||
} else if (item.type === 'timer') {
|
||||
let timer = recipe.recipe.timers[item.index];
|
||||
steps.push({ type: "timer", content: timer.quantity.value.value.value + " " + timer.quantity.unit, ...timer });
|
||||
} else if (item.type === 'inlineQuantity') {
|
||||
let inlineQuantity = recipe.recipe.inline_quantities[item.index];
|
||||
steps.push({ type: "inlineQuantity", content: inlineQuantity.value.value.value + " " + inlineQuantity.unit, ...inlineQuantity });
|
||||
} else {
|
||||
console.log("Unknown type: ", item.type)
|
||||
}
|
||||
});
|
||||
sections[i].content.push(steps);
|
||||
});
|
||||
});
|
||||
|
||||
// const {value, error} = parser.parse_render(
|
||||
// content
|
||||
// );
|
||||
// const html = value;
|
||||
|
||||
// TODO Sections name in ingredients list
|
||||
|
||||
return {
|
||||
recipe,
|
||||
sections,
|
||||
ingredients,
|
||||
cookware,
|
||||
timers,
|
||||
metadata
|
||||
};
|
||||
},
|
||||
compile: async (inputContent) => {
|
||||
// We probably don't need the raw content but it's here if we want
|
||||
return async () => {
|
||||
return inputContent;
|
||||
};
|
||||
},
|
||||
};
|
||||
|
|
@ -8,6 +8,7 @@
|
|||
import Image from '@11ty/eleventy-img';
|
||||
import util from 'util';
|
||||
import eleventyNavigationPlugin from "@11ty/eleventy-navigation";
|
||||
import eleventyCooklang from "./_config/eleventy-plugin-cooklang.js";
|
||||
|
||||
const emojiRegex = /[\u{1f300}-\u{1f5ff}\u{1f900}-\u{1f9ff}\u{1f600}-\u{1f64f}\u{1f680}-\u{1f6ff}\u{2600}-\u{26ff}\u{2700}-\u{27bf}\u{1f1e6}-\u{1f1ff}\u{1f191}-\u{1f251}\u{1f004}\u{1f0cf}\u{1f170}-\u{1f171}\u{1f17e}-\u{1f17f}\u{1f18e}\u{3030}\u{2b50}\u{2b55}\u{2934}-\u{2935}\u{2b05}-\u{2b07}\u{2b1b}-\u{2b1c}\u{3297}\u{3299}\u{303d}\u{00a9}\u{00ae}\u{2122}\u{23f3}\u{24c2}\u{23e9}-\u{23ef}\u{25b6}\u{23f8}-\u{23fa}]/ug
|
||||
|
||||
|
|
@ -21,6 +22,7 @@ export default async function (eleventyConfig) {
|
|||
eleventyConfig.addPassthroughCopy('src/admin');
|
||||
|
||||
eleventyConfig.addPlugin(eleventyNavigationPlugin);
|
||||
eleventyConfig.addPlugin(eleventyCooklang);
|
||||
|
||||
/* Collections */
|
||||
eleventyConfig.addCollection('recipes', collection => {
|
||||
|
|
|
|||
12770
package-lock.json
generated
12770
package-lock.json
generated
File diff suppressed because it is too large
Load diff
12
package.json
12
package.json
|
|
@ -7,6 +7,7 @@
|
|||
"scripts": {
|
||||
"dev:css": "sass src/scss/main.scss:src/_includes/css/main.css --watch --style=compressed",
|
||||
"dev:11ty": "eleventy --serve",
|
||||
"dev:decap": "decap-server",
|
||||
"dev": "npm-run-all --parallel dev:*",
|
||||
"prod:css": "sass src/scss/main.scss:src/_includes/css/main.css --style=compressed",
|
||||
"prod:11ty": "eleventy",
|
||||
|
|
@ -15,14 +16,17 @@
|
|||
"author": "Maël Brunet",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@11ty/eleventy": "^3.0.0",
|
||||
"@11ty/eleventy-img": "^6.0.1",
|
||||
"@11ty/eleventy-navigation": "^1.0.4",
|
||||
"@11ty/eleventy": "^3.1.2",
|
||||
"@11ty/eleventy-img": "^6.0.4",
|
||||
"@11ty/eleventy-navigation": "^1.0.5",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"sass": "^1.86.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"alpinejs": "^2.8.2"
|
||||
"@cooklang/cooklang": "^0.17.2",
|
||||
"alpinejs": "^2.8.2",
|
||||
"decap-cms-app": "^3.6.2",
|
||||
"decap-server": "^3.2.0"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
|
|||
126
src/_includes/layouts/recipe-cook.njk
Normal file
126
src/_includes/layouts/recipe-cook.njk
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
{% extends 'layouts/base.njk' %}
|
||||
|
||||
{% block content %}
|
||||
{% recipeimage image, "c-recipe__header-image", title, "100vw" %}
|
||||
<h1 class="c-recipe__title">{{ title }}</h1>
|
||||
<section>
|
||||
<div class="l-container">
|
||||
<div class="c-recipe__recipe-content-wrapper">
|
||||
<div class="c-recipe__tag-list">
|
||||
{% for tag in tags %}
|
||||
<a class="c-tags__tag" href="/tags/{{ tag | noEmoji | slug }}">{{ tag }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% set ingredientsArray = [] %}
|
||||
{% for ingredient in ingredients %}
|
||||
{% set notes = "" %}
|
||||
{% if ingredient.note %}
|
||||
{% set notes = [" (", ingredient.note, ")"] | join %}
|
||||
{% endif %}
|
||||
{% set value = [ingredient.quantity.value.value.value, ingredient.quantity.unit, " ", ingredient.name, notes] | join %}
|
||||
{% set ingredientsArray = (ingredientsArray.push(value), ingredientsArray) %}
|
||||
{% endfor %}
|
||||
<div class="c-recipe__ingredients-wrapper" x-data='{currentServings: {{ servings if not servings == "" else false }}, ingredients: decodeURI("{{ ingredientsArray | arrayToString }}").split("£") }'>
|
||||
{% if time or not servings == "" %}
|
||||
<div class="c-recipe__additional-info">
|
||||
{% if time %}
|
||||
{% if time.prep %}
|
||||
<p>{% include "icons/time.svg" %}Prep: {{ time.prep }}</p>
|
||||
{% endif %}
|
||||
{% if time.cook %}
|
||||
<p>{% include "icons/time.svg" %}Cook: {{ time.cook }}</p>
|
||||
{% endif %}
|
||||
{% if not time.cook and not time.prep %}
|
||||
<p>{% include "icons/time.svg" %}Total: {{ time }}</p>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if duration %}
|
||||
<p>{% include "icons/time.svg" %}Total: {{ duration }}</p>
|
||||
{% endif %}
|
||||
{% if not servings == "" %}
|
||||
<p>
|
||||
<template x-if="currentServings > 1">
|
||||
<button @click="currentServings -= 1" class="c-recipe__serving-button">
|
||||
-
|
||||
<span class="u-sr-only">Remove 1 serving</span>
|
||||
</button>
|
||||
</template>
|
||||
<span x-text="currentServings"></span>
|
||||
<button @click="currentServings += 1" class="c-recipe__serving-button">
|
||||
+
|
||||
<span class="u-sr-only">Add 1 serving</span>
|
||||
</button>
|
||||
<span>{{ site.servingsLabel }}</span>
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<h3>{{ site.ingredientsLabel }}</h3>
|
||||
<template x-if="currentServings">
|
||||
<ul class="c-recipe__ingredients-list">
|
||||
<template x-for="(ingredient, index) in ingredients" :key="index">
|
||||
<li><label><input type="checkbox"><span x-text="adaptQuantity(ingredient, {{servings}}, currentServings)"></span></label></li>
|
||||
</template>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<template x-if="!currentServings">
|
||||
<ul class="c-recipe__ingredients-list">
|
||||
{% for ingredient in ingredients %}
|
||||
<li><label><input type="checkbox"><span>{{ingredient.quantity}}{{ ingredient.units}} {{ ingredient.name }}</span></label></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<h3>Cookware</h3>
|
||||
<ul class="c-recipe__ingredients-list">
|
||||
{% for ingredient in cookware %}
|
||||
{% set notes = "" %}
|
||||
{% if ingredient.note %}
|
||||
{% set notes = [" (", ingredient.note, ")"] | join %}
|
||||
{% endif %}
|
||||
<li><label><input type="checkbox"><span>{{ingredient.quantity}}{{ ingredient.units}} {{ ingredient.name }} {{notes}}</span></label></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="c-recipe__instructions-wrapper u-free-text">
|
||||
{% for section in sections %}
|
||||
{% if section.title %}
|
||||
<h3>{{ section.title.content }}</h3>
|
||||
{% endif %}
|
||||
<ol>
|
||||
{% for steps in section.content %}
|
||||
<li>
|
||||
{% for items in steps %}{% if items.type == "ingredient" %}{% if items.reference %}<a href="../{{ items.reference.components | join("/") }}/{{ items.reference.name }}" target="_blank">{{items.content}}</a>{% else %}{{items.content}}{% endif %} ({{items.quantity.value.value.value}}{% if items.quantity.unit %} {{items.quantity.unit}}{% endif %}){% else %}{{items.content}}{% endif %}{% endfor %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ol>
|
||||
{% endfor %}
|
||||
|
||||
{% set notesArr = notes.split('|') %}
|
||||
{% if notesArr.length > 1 %}
|
||||
<h3>Notes</h3>
|
||||
<aside class="alert">
|
||||
{%- for note in notesArr -%}
|
||||
{{ note | safe }}<br>
|
||||
{%- endfor -%}
|
||||
</aside>
|
||||
{% endif %}
|
||||
|
||||
{% if source %}
|
||||
<p><a href="{{ source }}" target="_blank" rel="noopener">Source</a></p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
function adaptQuantity (ingredient, originalServings, currentServings) {
|
||||
return ingredient.replace(/(\d|\.|,)+/, match => Number(Math.round(parseFloat(match.replace(',', '.')) * currentServings / originalServings + 'e' + 2) + 'e-' + 2));
|
||||
}
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
backend:
|
||||
name: git-gateway
|
||||
branch: main
|
||||
local_backend: true
|
||||
|
||||
media_folder: "src/img/recipes"
|
||||
public_folder: "/img/recipes"
|
||||
|
||||
|
|
|
|||
3
src/recipes-cook/recipes-cook.json
Normal file
3
src/recipes-cook/recipes-cook.json
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"layout": "layouts/recipe-cook"
|
||||
}
|
||||
20
src/recipes-cook/sauces/Hollandaise.cook
Normal file
20
src/recipes-cook/sauces/Hollandaise.cook
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
---
|
||||
title: Hollandaise
|
||||
image: https://ichef.bbci.co.uk/food/ic/food_16x9_1600/recipes/richchocolatebrownie_1933_16x9.jpg
|
||||
tags:
|
||||
- Sauce
|
||||
time: 5 minutes
|
||||
servings: 4
|
||||
source: https://www.bbc.co.uk/food/recipes/richchocolatebrownie_1933/
|
||||
notes: I much prefer a flatbread to a crunchy tostada or taco so that's what I have this with. | I like [these Deli Kitchen ones](https://mydelikitchen.co.uk/products/4-greek-style-flatbreads/).
|
||||
---
|
||||
|
||||
= Dough
|
||||
|
||||
Heat the oven to 190C/170C Fan/Gas 5. Line a #20x30cm baking tin{} with baking paper.
|
||||
|
||||
Gently melt the @butter{225%g}(preferably unsalted) and the @caster sugar{450%g} together in a #large pan{}. Once melted, take off the heat and add the @chocolate{140%g}(dark, broken into pieces). Stir until melted.
|
||||
|
||||
Beat in the @eggs{5}(free-range medium eggs), then stir in the @flour{110%g}(plain) and the @cocoa powder{55%g}.
|
||||
|
||||
Mix @onion{1}(peeled and finely chopped) and @garlic{2%cloves}(peeled and minced) into paste.
|
||||
35
src/recipes-cook/simple-brownies.cook
Normal file
35
src/recipes-cook/simple-brownies.cook
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
---
|
||||
title: Simple brownies
|
||||
image: https://ichef.bbci.co.uk/food/ic/food_16x9_1600/recipes/richchocolatebrownie_1933_16x9.jpg
|
||||
tags:
|
||||
- Sweet 🍬
|
||||
- Cake 🍰
|
||||
- Vegetarian 🌿
|
||||
- Sharable
|
||||
- Favourite ⭐
|
||||
duration: 45 minutes
|
||||
time:
|
||||
prep: 10 minutes
|
||||
cook: 35 minutes
|
||||
servings: 4
|
||||
source: https://www.bbc.co.uk/food/recipes/richchocolatebrownie_1933/
|
||||
notes: I much prefer a flatbread to a crunchy tostada or taco so that's what I have this with. | I like [these Deli Kitchen ones](https://mydelikitchen.co.uk/products/4-greek-style-flatbreads/).
|
||||
---
|
||||
|
||||
= Dough
|
||||
|
||||
Heat the oven to 190C/170C Fan/Gas 5. Line a #20x30cm baking tin{} with baking paper.
|
||||
|
||||
Gently melt the @butter{225%g}(preferably unsalted) and the @caster sugar{450%g} together in a #large pan{}. Once melted, take off the heat and add the @chocolate{140%g}(dark, broken into pieces). Stir until melted.
|
||||
|
||||
Beat in the @eggs{5}(free-range medium eggs), then stir in the @flour{110%g}(plain) and the @cocoa powder{55%g}.
|
||||
|
||||
Mix @onion{1}(peeled and finely chopped) and @garlic{2%cloves}(peeled and minced) into paste.
|
||||
|
||||
|
||||
= Baking
|
||||
Pour the brownie batter into the prepared tin and bake for ~{30%minutes}, ~{35%minutes}, or until the top of the brownie is just firm but there is still a gentle wobble in the middle.
|
||||
|
||||
Take out of the oven and leave to cool in the tin. Cut the brownies into 5cm squares when only just warm, or completely cool. -- Test
|
||||
|
||||
Pour over with @./sauces/Hollandaise{150%g}
|
||||
Loading…
Reference in a new issue