Skip to content

Commit

Permalink
Merge pull request #608 from shawnthompson/HTMLminify
Browse files Browse the repository at this point in the history
TOC front matter option
  • Loading branch information
shawnthompson authored Jul 17, 2024
2 parents 9e51c59 + 258454a commit 90d4c32
Show file tree
Hide file tree
Showing 21 changed files with 284 additions and 243 deletions.
116 changes: 89 additions & 27 deletions .eleventy.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,42 +4,83 @@ const markdownItAttrs = require('markdown-it-attrs');
const { EleventyHtmlBasePlugin } = require('@11ty/eleventy');
const { stripHtml } = require('string-strip-html');
const htmlmin = require("html-minifier");
const { dynamicImport } = require('tsimportlib');
const slugify = require('@sindresorhus/slugify');

module.exports = function (eleventyConfig) {

let markdownItOptions = {
html: true, // you can include HTML tags
};

const md = markdownIt(markdownItOptions).use(markdownItAttrs);
const md = markdownIt(markdownItOptions)
.use(markdownItAttrs)
.use(markdownItAnchor, {
slugify: s => slugify(s, { lower: true, strict: true, locale: 'fr' }),
permalink: false, // Disable permalinks
});

eleventyConfig.setLibrary("md", md);

dynamicImport('@sindresorhus/slugify', module)
.then((module) => {
const slugify = module.default;
md.use(markdownItAnchor, { slugify });
})
.catch((err) => {
console.error('Error importing slugify:', err);
eleventyConfig.addShortcode('extractHeadings', function (content, tocType) {
const tokens = md.parse(content, {});
const levels = tocType === 'tocSimple' ? 1 : 2; // tocSimple only includes level 2 headings
const validTags = Array.from({ length: levels + 1 }, (_, i) => `h${i + 2}`);
const headings = tokens.filter(token =>
validTags.includes(token.tag) && token.type === 'heading_open'
).map(token => {
const level = token.tag;
const rawText = tokens[tokens.indexOf(token) + 1].content;
const text = stripHtml(rawText).result; // Strip HTML tags from the heading text
const id = slugify(text, { lower: true, strict: true, locale: 'fr' });
return { level, text, id };
});

const slugifyFilter = eleventyConfig.javascriptFunctions.slugify;
// Create TOC HTML
let tocHTML = '<aside><h2>{{ onThisPage[locale].heading }}</h2><ul>';
const levelsStack = [];

eleventyConfig.addFilter("stripTagsSlugify", (str) => {
headings.forEach(heading => {
const levelIndex = parseInt(heading.level.substring(1)) - 1;

if (!str) return;
while (levelsStack.length && levelsStack[levelsStack.length - 1] > levelIndex) {
tocHTML += '</ul></li>';
levelsStack.pop();
}

return slugifyFilter(stripHtml(str).result, {
if (levelsStack.length && levelsStack[levelsStack.length - 1] === levelIndex) {
tocHTML += '</li>';
}

if (!levelsStack.length || levelsStack[levelsStack.length - 1] < levelIndex) {
tocHTML += '<ul>';
levelsStack.push(levelIndex);
}

tocHTML += `<li><a href="#${heading.id}">${heading.text}</a>`;
});

while (levelsStack.length) {
tocHTML += '</ul></li>';
levelsStack.pop();
}

tocHTML += '</ul></aside>';

return tocHTML;
});

const slugifyFilter = eleventyConfig.javascriptFunctions.slugify;

eleventyConfig.addFilter("stripTagsSlugify", (str) => {
if (!str) return;
return slugify(stripHtml(str).result, { lower: true, strict: true, locale: 'fr' });
});

const { DateTime } = require("luxon");

eleventyConfig.addPlugin(EleventyHtmlBasePlugin);

// Minify HTML output
eleventyConfig.addTransform("htmlmin", function(content, outputPath) {
// Minify HTML output
eleventyConfig.addTransform("htmlmin", function (content, outputPath) {
if (outputPath && outputPath.endsWith(".html")) {
return htmlmin.minify(content, {
useShortDoctype: true,
Expand All @@ -61,20 +102,41 @@ module.exports = function (eleventyConfig) {
.toFormat("yyyy'-'MM'-'dd");
});

eleventyConfig.addPassthroughCopy({ "./src/_docs" : "docs" });
eleventyConfig.addPassthroughCopy({ "./src/_images" : "img" });
eleventyConfig.addPassthroughCopy({ "./src/CNAME" : "CNAME" });
eleventyConfig.addPassthroughCopy({ "./src/_docs": "docs" });
eleventyConfig.addPassthroughCopy({ "./src/_images": "img" });
eleventyConfig.addPassthroughCopy({ "./src/CNAME": "CNAME" });

eleventyConfig.addCollection("allHeadings", function (collectionApi) {
return collectionApi.getAll().map(item => {
if (item.data.toc || item.data.tocSimple) {
const tokens = md.parse(item.template.frontMatter.content, {});
const levels = item.data.tocSimple ? 1 : 2; // tocSimple only includes level 2 headings
const validTags = Array.from({ length: levels + 1 }, (_, i) => `h${i + 2}`);
const headings = tokens.filter(token =>
validTags.includes(token.tag) && token.type === 'heading_open'
).map(token => {
const level = token.tag;
const rawText = tokens[tokens.indexOf(token) + 1].content;
const text = stripHtml(rawText).result; // Strip HTML tags from the heading text
const id = slugify(text, { lower: true, strict: true, locale: 'fr' });
return { level, text, id };
});
item.data.headings = headings;
}
return item;
});
});

return {
dir: {
input : "src",
output : "_site",
includes : "_includes",
data : "_data"
input: "src",
output: "_site",
includes: "_includes",
data: "_data"
},
templateFormats : ["html", "md", "njk", "css"],
htmlTemplateEngine : "njk",
markdownTemplate : "njk",
setUseGitIgnore : false
templateFormats: ["html", "md", "njk", "css"],
htmlTemplateEngine: "njk",
markdownTemplateEngine: "njk",
setUseGitIgnore: false
};
};
84 changes: 75 additions & 9 deletions .github/DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ You can tell Node.js to stop running by pressing the <kbd>Control</kbd> and <kbd
We have three types of alerts that can be used either individually or together in your webpage, as per your needs and their logic can be found in [alert include](https://github.com/gc-da11yn/gc-da11yn.github.io/blob/main/src/_includes/partials/alerts.njk)

### isDraft

#### What is the function of isDraft

The `isDraft` variable is used to control whether or not an alert message includes a `isDraft` message. If the `isDraft` variable is set to `true`, the alert message will include the message that the content is still in draft and is not final. If it is included not at all, the "Draft" message will be excluded
Expand Down Expand Up @@ -183,18 +184,50 @@ To trigger the `internalLinks` variable in the front matter, you have to set its
---
```

#### How to run markdown-link-checker to check for broken or dead links on the console
#### What happens when internalLinks is not included

If you don't include the `internalLinks` variable in the front matter of your Markdown file, then the "internalLinks" message will not be included in the alert message by default. This is because the `internalLinks` variable is used to control whether or not the "internalLinks" message is included, and if it is not specified in the front matter, it will default to `false`.

## On this page / Table of Contents (TOC)

We have implemented an automatic generation of a "On this page" section for pages that have `toc: true` or `tocSimple: true` set in their front matter.

### Full TOC (`toc: true`)

If `toc: true` is set in the front matter, the table of contents will include headings at levels 2 and 3. This is useful for providing a detailed overview of the page content.

To enable the full TOC on a specific page, add the following to the front matter of the Markdown file:

```yaml
---
toc: true
---
```

#### Simple TOC (tocSimple: true)

If `tocSimple: true` is set in the front matter, the table of contents will only include headings at level 2. This is useful for a more concise overview without listing subheadings.

To enable the simple TOC on a specific page, add the following to the front matter of the Markdown file:

```yaml
---
tocSimple: true
---
```

This configuration will automatically generate a "On this page" section at the beginning of the content, listing the specified headings based on the toc or tocSimple setting.

## markdown-link-checker

### How to run markdown-link-checker to check for broken or dead links on the console

The markdown-link-checker is an implemented plugin from [markdown-link-checker](https://www.npmjs.com/package/markdown-link-check) that automatically scans and checks for working and dead links. The code used in this project is a modified version from [canada.ca link checker](https://github.com/canada-ca/Open_First_Whitepaper/blob/master/link-check.js). We are currently using version 3.0.

To run the plugin simply type 'npm run link-check' in your terminal. The links will load in the terminal but they will also be generated inside a 'broken-links.json' in the root directory.

Tip: Keep in mind it might lag, but just give it a few seconds to finish

#### What happens when internalLinks is not included

If you don't include the `internalLinks` variable in the front matter of your Markdown file, then the "internalLinks" message will not be included in the alert message by default. This is because the `internalLinks` variable is used to control whether or not the "internalLinks" message is included, and if it is not specified in the front matter, it will default to `false`.

______________________

<div lang="fr">
Expand Down Expand Up @@ -379,16 +412,49 @@ Pour déclencher la variable `internalLinks` dans le front matter, vous devez d
---
```

#### Comment exécuter markdown-link-checker pour vérifier les liens brisés ou morts sur la console ?
#### Que se passe-t-il lorsque internalLinks n'est pas inclus

Si vous n'incluez pas la variable `internalLinks` dans l'avant-propos de votre fichier Markdown, le message "internalLinks" ne sera pas inclus dans le message d'alerte par défaut. C'est parce que la variable `internalLinks` est utilisée pour contrôler si le message "internalLinks" est inclus ou non, et s'il n'est pas spécifié dans l'avant-propos, il sera par défaut à `false`.

## Sur cette page / Table des matières (TOC)

Nous avons mis en place une génération automatique d'une section "Sur cette page" pour les pages qui ont `toc: true` ou `tocSimple: true` définies dans leur front matter.

### TOC complet (`toc: true`)

Si `toc: true` est défini dans le front matter, la table des matières inclura les titres aux niveaux 2 et 3. Cela est utile pour fournir un aperçu détaillé du contenu de la page.

Pour activer le TOC complet sur une page spécifique, ajoutez ce qui suit dans le front matter du fichier Markdown :

```yaml
---
toc: true
---
```

### TOC simple (`tocSimple: true`)

Si `tocSimple: true` est défini dans le front matter, la table des matières n'inclura que les titres de niveau 2. Cela est utile pour un aperçu plus concis sans lister les sous-titres.

Pour activer le TOC simple sur une page spécifique, ajoutez ce qui suit dans le front matter du fichier Markdown :

```yaml
---
tocSimple: true
---
```

Cette configuration générera automatiquement une section "Sur cette page" au début du contenu, listant les titres spécifiés en fonction du paramètre `toc` ou `tocSimple`.

## markdown-link-checker

### Comment exécuter markdown-link-checker pour vérifier les liens brisés ou morts sur la console ?

Le markdown-link-checker est un plugin implémenté à partir de [markdown-link-checker] (https://www.npmjs.com/package/markdown-link-check) qui analyse et vérifie automatiquement les liens actifs et morts. Le code utilisé dans ce projet est une version modifiée de [canada.ca link checker](https://github.com/canada-ca/Open_First_Whitepaper/blob/master/link-check.js). Nous utilisons actuellement la version 3.0.

Pour lancer le plugin, tapez simplement 'npm run link-check' dans votre terminal. Les liens se chargeront dans le terminal mais ils seront également générés dans un fichier 'broken-links.json' dans le répertoire racine.

Astuce : Gardez à l'esprit qu'il peut y avoir un décalage, mais donnez-lui quelques secondes pour terminer.

#### Que se passe-t-il lorsque internalLinks n'est pas inclus

Si vous n'incluez pas la variable `internalLinks` dans l'avant-propos de votre fichier Markdown, le message "internalLinks" ne sera pas inclus dans le message d'alerte par défaut. C'est parce que la variable `internalLinks` est utilisée pour contrôler si le message "internalLinks" est inclus ou non, et s'il n'est pas spécifié dans l'avant-propos, il sera par défaut à `false`.

</div>
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"start-dev": "cross-env ELEVENTY_ENV=dev npm-run-all sass-start --parallel watch:*",
"start-prod": "cross-env ELEVENTY_ENV=prod npm-run-all sass-start --parallel watch:*",
"dev": "cross-env ELEVENTY_ENV=dev npm-run-all sass-build --parallel eleventy",
"debug": "DEBUG=Eleventy* npx @11ty/eleventy",
"build": "npm-run-all sass-build eleventy"
},
"repository": {
Expand Down
9 changes: 9 additions & 0 deletions src/_data/onThisPage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module.exports = {
fr: {
heading: "Sur cette page",
},

en: {
heading: "On this page",
}
};
58 changes: 32 additions & 26 deletions src/_includes/layouts/base.njk
Original file line number Diff line number Diff line change
Expand Up @@ -5,54 +5,60 @@
<!DOCTYPE html>
<html class="no-js" lang="{{ locale }}" dir="ltr">

{% include "partials/head.njk" %}
{% include "partials/head.njk" %}

<body vocab="http://schema.org/" typeof="WebPage">
<body vocab="http://schema.org/" typeof="WebPage">

{% include "partials/skipnav.njk" %}
{% include "partials/skipnav.njk" %}

{% include "partials/header.njk" %}
{% include "partials/header.njk" %}

<main property="mainContentOfPage" resource="#wb-main" typeof="WebPageElement">
<main property="mainContentOfPage" resource="#wb-main" typeof="WebPageElement">

<div class="home">
<div class="container p-0 p-sm-3">
<div class="well header-rwd brdr-0 brdr-rds-0 text-white bg-gctheme">
<h1 property="name" id="wb-cont">
{{ title | safe }}
</h1>
{% if description %}
<div class="home">
<div class="container p-0 p-sm-3">
<div class="well header-rwd brdr-0 brdr-rds-0 text-white bg-gctheme">
<h1 property="name" id="wb-cont">
{{ title | safe }}
</h1>
{% if description %}
<p>{{ description | safe }}</p>
{% else %}
{% else %}
<p><strong>{{ landingPage[locale].descriptionNoneText }}</strong></p>
{% endif %}
{% endif %}
</div>
</div>
</div>
</div>

{% include "partials/alerts.njk" %}
<div class="container">
{% if officeTOC === true %}
{% include "partials/office-toc.njk" %}
{% include "partials/office365Notice.njk" %}
{% endif %}
{% if officeTOC === true %}
{% include "partials/office-toc.njk" %}
{% include "partials/office365Notice.njk" %}
{% endif %}

{% if toc or tocSimple %}
{% include "partials/onThisPage.njk" %}
{% endif %}

{{ content | safe }}
{% if officeTOC === true %}
{% include "partials/office-toc.njk" %}
</div>
{% endif %}
{% include "partials/pagedetails.njk" %}

</main>
</main>

{% include "partials/contribute.njk" %}

{% include "partials/contribute.njk" %}
{% include "partials/footer.njk" %}

{% include "partials/footer.njk" %}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.js"></script>
<script src="https://www.canada.ca/etc/designs/canada/wet-boew/js/wet-boew.min.js"></script>
<script src="https://www.canada.ca/etc/designs/canada/wet-boew/js/theme.min.js"></script>
</body>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.js"></script>
<script src="https://www.canada.ca/etc/designs/canada/wet-boew/js/wet-boew.min.js"></script>
<script src="https://www.canada.ca/etc/designs/canada/wet-boew/js/theme.min.js"></script>
</body>
</html>

{% endif %}
Loading

0 comments on commit 90d4c32

Please sign in to comment.