diff --git a/.gitignore b/.gitignore index d1aa60a..a89e41c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ .idea *iml _site/ +dist/ node_modules/ package-lock.json +src/compiled-assets diff --git a/README.md b/README.md index 6441ff0..e847be5 100644 --- a/README.md +++ b/README.md @@ -73,8 +73,8 @@ DEBUG=Eleventy* npx @11ty/eleventy - The `public` folder in your input directory will be copied to the output folder (via `addPassthroughCopy()` in the `.eleventy.js` file). This means `./public/css/*` will live at `./_site/css/*` after your build completes. [When using `--serve` this behavior is emulated](/docs/copy/#passthrough-during-serve) (the files will not show up in `_site`). - The blog post feed template is in `feed/feed.njk`. This is also a good example of using a global data files in that it uses `_data/metadata.json`. - This project uses three layouts: - - `_includes/layouts/base.njk`: the top level HTML structure - - `_includes/layouts/home.njk`: the home page template (wrapped into `base.njk`) - - `_includes/layouts/post.njk`: the blog post template (wrapped into `base.njk`) -- `_includes/postslist.njk` is a Nunjucks include and is a reusable component used to display a list of all the posts. `index.njk` has an example of how to use it. + - `_layouts/base.njk`: the top level HTML structure + - `_layouts/home.njk`: the home page template (wrapped into `base.njk`) + - `_layouts/post.njk`: the blog post template (wrapped into `base.njk`) +- `_components/postslist.njk` is a Nunjucks include and is a reusable component used to display a list of all the posts. `index.njk` has an example of how to use it. diff --git a/eleventy.config.js b/eleventy.config.js index fb15cf6..9a9e8e1 100644 --- a/eleventy.config.js +++ b/eleventy.config.js @@ -1,14 +1,12 @@ -const fs = require("fs"); -const path = require("path"); const { DateTime } = require("luxon"); -const rosetta = require("rosetta"); const markdownItAnchor = require("markdown-it-anchor"); const pluginRss = require("@11ty/eleventy-plugin-rss"); const pluginSyntaxHighlight = require("@11ty/eleventy-plugin-syntaxhighlight"); const pluginNavigation = require("@11ty/eleventy-navigation"); -const { EleventyI18nPlugin, EleventyHtmlBasePlugin } = require("@11ty/eleventy"); -const languageStrings = require("./i18n.js"); +const { EleventyHtmlBasePlugin } = require("@11ty/eleventy"); let markdownIt = require("markdown-it"); +const htmlmin = require('html-minifier'); + let options = { // whatever options you have set for the library here }; @@ -18,48 +16,28 @@ let figoptions = { }; const mdLib = markdownIt(options).use(mdfigcaption, figoptions); -const manifestPath = path.resolve(__dirname, "_site", "assets", "manifest.json"); -const manifest = JSON.parse( - fs.readFileSync(manifestPath, { encoding: "utf8" }) -); - module.exports = function(eleventyConfig) { - eleventyConfig.ignores.add("README.md"); + eleventyConfig.setUseGitIgnore(false); eleventyConfig.setLibrary("md", mdLib); - // Adds a universal shortcode to return the URL to a webpack asset. In Nunjack templates: - // {% webpackAsset 'main.js' %} or {% webpackAsset 'main.css' %} - eleventyConfig.addShortcode("webpackAsset", function(name) { - if (!manifest[name]) { - throw new Error(`The asset ${name} does not exist in ${manifestPath}`); - } - return manifest[name]; - }); + // Watch our compiled assets for changes + eleventyConfig.addWatchTarget('./src/compiled-assets/main.css'); + //eleventyConfig.addWatchTarget('./src/compiled-assets/main.js'); + //eleventyConfig.addWatchTarget('./src/compiled-assets/vendor.js'); - // Copy all images directly to dist. - eleventyConfig.addPassthroughCopy({ "public/img": "img" }); + // Copy src/compiled-assets to /assets + eleventyConfig.addPassthroughCopy({ 'src/compiled-assets': 'assets' }); + // Copy all images + eleventyConfig.addPassthroughCopy('src/images'); - // Copy the contents of the `public` folder to the output folder - // For example, `./public/css/` ends up in `_site/css/` - // eleventyConfig.addPassthroughCopy({ - // "./node_modules/prismjs/themes/prism-okaidia.css": "/public/css/prism-theme.css", - // }); - // // Watch our compiled assets for changes - // eleventyConfig.addWatchTarget('./src/compiled-assets/main.css'); - // // Copy src/compiled-assets to /assets - // eleventyConfig.addPassthroughCopy({ 'src/compiled-assets': 'assets' }); - - // Add plugins + // Add plugins eleventyConfig.addPlugin(pluginRss); eleventyConfig.addPlugin(pluginSyntaxHighlight); eleventyConfig.addPlugin(pluginNavigation); eleventyConfig.addPlugin(EleventyHtmlBasePlugin); - eleventyConfig.addPlugin(EleventyI18nPlugin, { - defaultLanguage: "fr", - errorMode: "allow-fallback", - }); + eleventyConfig.addFilter("env", (key, def="NOT DEFINED") => process.env[key] || def); eleventyConfig.addFilter("readableDate", (dateObj, format = "dd LLLL yyyy") => { return DateTime.fromJSDate(dateObj, {zone: 'utc', locale: 'fr'}).toFormat(format); @@ -115,23 +93,26 @@ module.exports = function(eleventyConfig) { }); }); - // Override @11ty/eleventy-dev-server defaults (used only with --serve) + // Override @11ty/eleventy-dev-server defaults (used only with --serve) eleventyConfig.setServerOptions({ showVersion: true, }); - // i18n filter using Rosetta - const rosettaLib = rosetta(languageStrings); + if (process.env.ELEVENTY_ENV === 'production') { + eleventyConfig.addTransform('htmlmin', (content, outputPath) => { + if (outputPath.endsWith('.html')) { + return htmlmin.minify(content, { + collapseInlineTagWhitespace: false, + collapseWhitespace: true, + removeComments: true, + sortClassName: true, + useShortDoctype: true, + }); + } - eleventyConfig.addFilter("i18n", function (key, lang) { - const I18N_PREFIX = "i18n."; - if(key.startsWith(I18N_PREFIX)) { - key = key.slice(I18N_PREFIX.length); - } - // depends on page.lang in 2.0.0-canary.14+ - let page = this.page || this.ctx?.page || this.context?.environments?.page || {}; - return rosettaLib.t(key, {}, lang || page.lang); - }); + return content; + }); + } return { // Control which files Eleventy will process @@ -165,10 +146,185 @@ module.exports = function(eleventyConfig) { // These are all optional (defaults are shown): dir: { - input: ".", - includes: "_includes", - data: "_data", - output: "_site" - } + input: 'src', + includes: '_components', + layouts: '_layouts', + output: 'dist', + }, }; }; + +// const fs = require("fs"); +// const path = require("path"); +// const { DateTime } = require("luxon"); +// const rosetta = require("rosetta"); +// const markdownItAnchor = require("markdown-it-anchor"); +// const pluginRss = require("@11ty/eleventy-plugin-rss"); +// const pluginSyntaxHighlight = require("@11ty/eleventy-plugin-syntaxhighlight"); +// const pluginNavigation = require("@11ty/eleventy-navigation"); +// const { EleventyI18nPlugin, EleventyHtmlBasePlugin } = require("@11ty/eleventy"); +// const languageStrings = require("./i18n.js"); +// let markdownIt = require("markdown-it"); +// let options = { +// // whatever options you have set for the library here +// }; +// let mdfigcaption = require('markdown-it-image-figures'); +// let figoptions = { +// figcaption: true +// }; +// const mdLib = markdownIt(options).use(mdfigcaption, figoptions); +// +// const manifestPath = path.resolve(__dirname, "_site", "assets", "manifest.json"); +// const manifest = JSON.parse( +// fs.readFileSync(manifestPath, { encoding: "utf8" }) +// ); +// +// module.exports = function(eleventyConfig) { +// eleventyConfig.ignores.add("README.md"); +// +// eleventyConfig.setLibrary("md", mdLib); +// +// // Adds a universal shortcode to return the URL to a webpack asset. In Nunjack templates: +// // {% webpackAsset 'main.js' %} or {% webpackAsset 'main.css' %} +// eleventyConfig.addShortcode("webpackAsset", function(name) { +// if (!manifest[name]) { +// throw new Error(`The asset ${name} does not exist in ${manifestPath}`); +// } +// return manifest[name]; +// }); +// +// // Copy all images directly to dist. +// eleventyConfig.addPassthroughCopy({ "public/img": "img" }); +// +// // Copy the contents of the `public` folder to the output folder +// // For example, `./public/css/` ends up in `_site/css/` +// // eleventyConfig.addPassthroughCopy({ +// // "./node_modules/prismjs/themes/prism-okaidia.css": "/public/css/prism-theme.css", +// // }); +// // // Watch our compiled assets for changes +// // eleventyConfig.addWatchTarget('./src/compiled-assets/main.css'); +// // // Copy src/compiled-assets to /assets +// // eleventyConfig.addPassthroughCopy({ 'src/compiled-assets': 'assets' }); +// +// // Add plugins +// eleventyConfig.addPlugin(pluginRss); +// eleventyConfig.addPlugin(pluginSyntaxHighlight); +// eleventyConfig.addPlugin(pluginNavigation); +// eleventyConfig.addPlugin(EleventyHtmlBasePlugin); +// +// eleventyConfig.addPlugin(EleventyI18nPlugin, { +// defaultLanguage: "fr", +// errorMode: "allow-fallback", +// }); +// +// eleventyConfig.addFilter("readableDate", (dateObj, format = "dd LLLL yyyy") => { +// return DateTime.fromJSDate(dateObj, {zone: 'utc', locale: 'fr'}).toFormat(format); +// }); +// +// // https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#valid-date-string +// eleventyConfig.addFilter('htmlDateString', (dateObj) => { +// return DateTime.fromJSDate(dateObj, {zone: 'utc', locale: 'fr'}).toFormat('yyyy-LL-dd'); +// }); +// +// // Get the first `n` elements of a collection. +// eleventyConfig.addFilter("head", (array, n) => { +// if(!Array.isArray(array) || array.length === 0) { +// return []; +// } +// if( n < 0 ) { +// return array.slice(n); +// } +// +// return array.slice(0, n); +// }); +// +// // Return the smallest number argument +// eleventyConfig.addFilter("min", (...numbers) => { +// return Math.min.apply(null, numbers); +// }); +// +// // Return all the tags used in a collection +// eleventyConfig.addFilter("getAllTags", collection => { +// let tagSet = new Set(); +// for(let item of collection) { +// (item.data.tags || []).forEach(tag => tagSet.add(tag)); +// } +// return Array.from(tagSet).sort((a, b) => { +// return a.localeCompare(b, undefined, {sensitivity: 'base'}); +// }); +// }); +// +// eleventyConfig.addFilter("filterTagList", function filterTagList(tags) { +// return (tags || []).filter(tag => ["all", "nav", "post", "posts"].indexOf(tag) === -1); +// }); +// +// // Customize Markdown library and settings: +// eleventyConfig.amendLibrary("md", mdLib => { +// mdLib.use(markdownItAnchor, { +// permalink: markdownItAnchor.permalink.ariaHidden({ +// placement: "after", +// class: "direct-link", +// symbol: "#", +// }), +// level: [1,2,3,4], +// slugify: eleventyConfig.getFilter("slug") +// }); +// }); +// +// // Override @11ty/eleventy-dev-server defaults (used only with --serve) +// eleventyConfig.setServerOptions({ +// showVersion: true, +// }); +// +// // i18n filter using Rosetta +// const rosettaLib = rosetta(languageStrings); +// +// eleventyConfig.addFilter("i18n", function (key, lang) { +// const I18N_PREFIX = "i18n."; +// if(key.startsWith(I18N_PREFIX)) { +// key = key.slice(I18N_PREFIX.length); +// } +// // depends on page.lang in 2.0.0-canary.14+ +// let page = this.page || this.ctx?.page || this.context?.environments?.page || {}; +// return rosettaLib.t(key, {}, lang || page.lang); +// }); +// +// return { +// // Control which files Eleventy will process +// // e.g.: *.md, *.njk, *.html, *.liquid +// templateFormats: [ +// "md", +// "njk", +// "html", +// "liquid" +// ], +// +// // Pre-process *.md files with: (default: `liquid`) +// markdownTemplateEngine: "njk", +// +// // Pre-process *.html files with: (default: `liquid`) +// htmlTemplateEngine: "njk", +// +// // ----------------------------------------------------------------- +// // If your site deploys to a subdirectory, change `pathPrefix`. +// // Don’t worry about leading and trailing slashes, we normalize these. +// +// // If you don’t have a subdirectory, use "" or "/" (they do the same thing) +// // This is only used for link URLs (it does not affect your file structure) +// // Best paired with the `url` filter: https://www.11ty.dev/docs/filters/url/ +// +// // You can also pass this in on the command line using `--pathprefix` +// +// // Optional (default is shown) +// pathPrefix: "/", +// // ----------------------------------------------------------------- +// +// // These are all optional (defaults are shown): +// dir: { +// input: ".", +// includes: "_includes", +// data: "_data", +// output: "_site" +// } +// }; +// }; diff --git a/fr/fr.11tydata.js b/fr/fr.11tydata.js deleted file mode 100644 index af14c94..0000000 --- a/fr/fr.11tydata.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - lang: "fr", - permalink: function(data) { - // Change (Francais) /fr/blog/my-post URLs to have an implied language code /blog/my-post URLs instead. - let [slashPrefixEmpty, langCode, ...stem] = data.page.filePathStem.split("/"); - let path = stem.join("/"); - - // Account for `permalink: 404.html` style - return stem[stem.length - 1] === "index" ? `${path}.html` : `${path}/index.html`; - } -} diff --git a/package.json b/package.json index c6b4a25..15f8291 100644 --- a/package.json +++ b/package.json @@ -3,38 +3,52 @@ "version": "1.0.0", "description": "Blog IT's on us", "scripts": { - "build": "npm-run-all clean build:assets build:site", - "build:assets": "NODE_ENV=production webpack --mode=production", - "build:site": "NODE_ENV=production eleventy", - "clean": "rm -rf ./_site", - "dev": "npm-run-all clean webpack:assets --parallel dev:*", - "dev:assets": "npm run webpack:assets --watch", - "dev:site": "NODE_ENV=development eleventy --serve", - "webpack:assets": "NODE_ENV=development webpack --mode=development" + "build:assets": "webpack --config webpack.config.prod.js", + "build:site": "ELEVENTY_ENV=production npx eleventy", + "del:assets": "rimraf ./src/compiled-assets", + "del:dist": "rimraf ./dist", + "dev": "npm run dev:assets & npm run dev:site", + "dev:assets": "webpack --config webpack.config.dev.js", + "dev:site": "ELEVENTY_ENV=development npx eleventy --serve", + "prod": "npm-run-all del:dist del:assets build:assets build:site", + "serve:prod": "serve ./dist/" + }, + "repository": { + "type": "git", + "url": "https://git.itsonus.fr/internal/itsonus-blog" }, "dependencies": { "@11ty/eleventy": "^2.0.0-canary.16", "@11ty/eleventy-navigation": "^0.3.5", "@11ty/eleventy-plugin-rss": "^1.2.0", "@11ty/eleventy-plugin-syntaxhighlight": "^4.1.0", - "@babel/core": "^7.10.2", - "@babel/preset-env": "^7.10.2", - "autoprefixer": "^9.8.0", - "babel-loader": "^8.1.0", - "css-loader": "^3.5.3", - "cssnano": "^4.1.10", - "luxon": "^3.0.1", "markdown-it-anchor": "^8.6.4", "markdown-it-image-figures": "^2.1.0", - "mini-css-extract-plugin": "^0.9.0", + "luxon": "^3.0.4", + + "@babel/core": "^7.18.2", + "@babel/preset-env": "^7.18.2", + "autoprefixer": "^10.4.7", + "babel-loader": "^8.2.5", + "core-js": "^3.22.8", + "css-loader": "^6.7.1", + "eslint": "^8.17.0", + "eslint-config-airbnb-base": "^15.0.0", + "eslint-plugin-import": "^2.26.0", + "html-minifier": "^4.0.0", + "md5-file": "^5.0.0", + "mini-css-extract-plugin": "^2.6.0", "npm-run-all": "^4.1.5", - "postcss-import": "^12.0.1", - "postcss-loader": "^3.0.0", - "postcss-preset-env": "^6.7.0", - "rosetta": "^1.1.0", - "webpack": "^4.43.0", - "webpack-cli": "^3.3.11", - "webpack-manifest-plugin": "^2.2.0" + "css-minimizer-webpack-plugin": "^4.0.0", + "postcss-loader": "^7.0.0", + "rimraf": "^3.0.2", + "sass": "^1.52.2", + "sass-loader": "^13.0.0", + "serve": "^13.0.2", + "terser-webpack-plugin": "^5.3.3", + "webpack": "^5.73.0", + "webpack-cli": "^4.9.2", + "webpack-merge": "^5.8.0" }, "devDependencies": { "prettier": "^2.0.5" diff --git a/postcss.config.js b/postcss.config.js index 988025d..3b16595 100644 --- a/postcss.config.js +++ b/postcss.config.js @@ -1,10 +1,6 @@ -const plugins = [ - require("postcss-import"), - require("postcss-preset-env"), -]; - -if (process.env.NODE_ENV === "production") { - plugins.push(require("cssnano")); -} - -module.exports = { plugins }; +module.exports = { + plugins: [ + // eslint-disable-next-line global-require + require('autoprefixer'), + ], +}; diff --git a/public/js/index.js b/public/js/index.js deleted file mode 100644 index d52773e..0000000 --- a/public/js/index.js +++ /dev/null @@ -1 +0,0 @@ -// console.log("Elevenpack javascript is loaded"); diff --git a/_includes/other_links.njk b/src/_components/other_links.njk similarity index 50% rename from _includes/other_links.njk rename to src/_components/other_links.njk index 2a426f4..a509ae1 100644 --- a/_includes/other_links.njk +++ b/src/_components/other_links.njk @@ -2,8 +2,6 @@ diff --git a/_includes/postslist.njk b/src/_components/postslist.njk similarity index 100% rename from _includes/postslist.njk rename to src/_components/postslist.njk diff --git a/_includes/tagslist.njk b/src/_components/tagslist.njk similarity index 100% rename from _includes/tagslist.njk rename to src/_components/tagslist.njk diff --git a/src/_data/cacheBust.js b/src/_data/cacheBust.js new file mode 100644 index 0000000..f59ab52 --- /dev/null +++ b/src/_data/cacheBust.js @@ -0,0 +1,21 @@ +const md5File = require('md5-file'); + +const cacheBust = () => { + // A "map" of files to cache bust + const files = { + mainCss: './src/compiled-assets/main.css', + // mainJs: './src/compiled-assets/main.js', + // vendorJs: './src/compiled-assets/vendor.js', + }; + + return Object.entries(files).reduce((acc, [key, path]) => { + const now = Date.now(); + const bust = process.env.ELEVENTY_ENV === 'production' ? md5File.sync(path, (_err, hash) => hash) : now; + + acc[key] = bust; + + return acc; + }, {}); +}; + +module.exports = cacheBust; diff --git a/_data/metadata.json b/src/_data/metadata.json similarity index 100% rename from _data/metadata.json rename to src/_data/metadata.json diff --git a/_includes/layouts/base.njk b/src/_layouts/base.njk similarity index 84% rename from _includes/layouts/base.njk rename to src/_layouts/base.njk index 1546485..42783f5 100644 --- a/_includes/layouts/base.njk +++ b/src/_layouts/base.njk @@ -1,29 +1,21 @@ - + {{ title or metadata.title }} - + - - - {%- set alternateUrls = page.url | locale_links %} - {% if alternateUrls.length %} - - {%- for link in alternateUrls %} - - {%- endfor %} - {%- endif %} +
@@ -53,7 +45,7 @@
  • - + Accéder au flux RSS du blog diff --git a/_includes/layouts/home.njk b/src/_layouts/home.njk similarity index 68% rename from _includes/layouts/home.njk rename to src/_layouts/home.njk index ebba6b3..e8f1f01 100644 --- a/_includes/layouts/home.njk +++ b/src/_layouts/home.njk @@ -1,5 +1,5 @@ --- -layout: layouts/base.njk +layout: base.njk templateClass: tmpl-home --- {{ content | safe }} diff --git a/_includes/layouts/post.njk b/src/_layouts/post.njk similarity index 53% rename from _includes/layouts/post.njk rename to src/_layouts/post.njk index bb17091..a8f7bce 100644 --- a/_includes/layouts/post.njk +++ b/src/_layouts/post.njk @@ -1,5 +1,5 @@ --- -layout: layouts/base.njk +layout: base.njk templateClass: tmpl-post ---

    {{ title }}

    @@ -16,19 +16,6 @@ templateClass: tmpl-post
    -{% set i18nLinks = page.url | locale_links %} -{% if i18nLinks.length %} - -{% endif %} - {%- if collections.posts %} {# these filters are locale-aware in 2.0.0-canary.14 #} {%- set previousPost = collections.posts | getPreviousCollectionItem %} @@ -36,8 +23,8 @@ templateClass: tmpl-post {%- if nextPost or previousPost %} {%- endif %} diff --git a/fr/404.md b/src/_pages/404.md similarity index 94% rename from fr/404.md rename to src/_pages/404.md index 9a37322..472a0f8 100644 --- a/fr/404.md +++ b/src/_pages/404.md @@ -1,5 +1,5 @@ --- -layout: layouts/home.njk +layout: home.njk permalink: 404.html eleventyExcludeFromCollections: true --- diff --git a/src/_pages/_pages.json b/src/_pages/_pages.json new file mode 100644 index 0000000..b60e378 --- /dev/null +++ b/src/_pages/_pages.json @@ -0,0 +1,3 @@ +{ + "permalink": "{{ page.filePathStem.replace('/_pages', '').replace('/index', '') }}/index.html" +} diff --git a/fr/all_articles.njk b/src/_pages/all_articles.njk similarity index 90% rename from fr/all_articles.njk rename to src/_pages/all_articles.njk index 46816a1..148444d 100644 --- a/fr/all_articles.njk +++ b/src/_pages/all_articles.njk @@ -1,7 +1,7 @@ --- -layout: layouts/home.njk +layout: home.njk eleventyNavigation: - key: nav.all + key: Tous les articles order: 3 ---