@@ -13,8 +13,9 @@ const syntaxHighlight = require("@11ty/eleventy-plugin-syntaxhighlight");
1313
1414module . exports = function ( eleventyConfig ) {
1515 const quick = Boolean ( process . env . BUILD_QUICK ) ;
16+ const isDev = process . env . ELEVENTY_ENV === "development" || process . argv . includes ( "--serve" ) || process . argv . includes ( "--watch" ) ;
1617 const now = new Date ( ) ;
17-
18+ //
1819 /**
1920 * There is a quick build function (npm run start:quick) that only loads the
2021 * recent content (YTD and previous year), by excluding all year folders from
@@ -53,35 +54,92 @@ module.exports = function (eleventyConfig) {
5354 eleventyConfig . ignores . add ( "src/nl/vereniging/bestuur/notulen" ) ;
5455 }
5556
57+ /**
58+ * Read .eleventyignore file and apply ignores programmatically for faster builds.
59+ * This is more effective than relying on Eleventy's built-in ignore file reading
60+ * because it happens earlier in the process.
61+ */
62+ if ( isDev || quick ) {
63+ const ignoreFile = ".eleventyignore" ;
64+ if ( fs . existsSync ( ignoreFile ) ) {
65+ const ignoreContent = fs . readFileSync ( ignoreFile , "utf-8" ) ;
66+ const ignorePatterns = ignoreContent
67+ . split ( "\n" )
68+ . map ( ( line ) => line . trim ( ) )
69+ . filter ( ( line ) => line && ! line . startsWith ( "#" ) ) ;
70+
71+ ignorePatterns . forEach ( ( pattern ) => {
72+ // Convert glob pattern to actual file paths
73+ const files = glob . sync ( pattern , { cwd : process . cwd ( ) } ) ;
74+ files . forEach ( ( file ) => {
75+ eleventyConfig . ignores . add ( file ) ;
76+ } ) ;
77+ } ) ;
78+ console . debug ( `[ignore] Applied ${ ignorePatterns . length } ignore patterns from .eleventyignore` ) ;
79+ }
80+ }
81+
5682 // Custom date filter
5783 eleventyConfig . addFilter ( "localizedDate" , function ( dateObj , locale = "en" ) {
5884 return DateTime . fromJSDate ( dateObj )
5985 . setLocale ( locale )
6086 . toFormat ( "d LLLL yyyy" ) ;
6187 } ) ;
6288
89+ // Event date filter that handles tentative dates (hides day if tentative)
90+ // Usage: {{ activity.data.eventdate | eventDate: locale, activity.data.eventdateTentative }}
91+ eleventyConfig . addFilter ( "eventDate" , function ( dateObj , locale = "en" , isTentative = false ) {
92+ const dateTime = DateTime . fromJSDate ( dateObj ) . setLocale ( locale ) ;
93+
94+ if ( ! dateTime . isValid ) {
95+ return String ( dateObj ) ;
96+ }
97+
98+ // If tentative, show only month and year
99+ if ( isTentative ) {
100+ if ( locale === "nl" ) {
101+ return dateTime . toFormat ( "LLLL yyyy" ) ; // "april 2026"
102+ } else {
103+ return dateTime . toFormat ( "LLLL yyyy" ) ; // "April 2026"
104+ }
105+ }
106+
107+ // Otherwise show full date
108+ if ( locale === "nl" ) {
109+ return dateTime . toFormat ( "d LLLL yyyy" ) ; // "1 april 2026"
110+ } else {
111+ return dateTime . toFormat ( "LLLL d, yyyy" ) ; // "April 1, 2026"
112+ }
113+ } ) ;
114+
63115 /* Add id to heading elements */
64116 eleventyConfig . addPlugin ( pluginAddIdToHeadings ) ;
65117
66- eleventyConfig . addPlugin ( brokenLinksPlugin , {
67- redirect : "warn" ,
68- broken : "warn" ,
69- cacheDuration : "1d" ,
70- loggingLevel : 1 ,
71- excludeUrls : [
72- "https://www.openstreetmap.org*" ,
73- "https://www.youtube.com*" ,
74- "http://www.example.com*" ,
75- "https://codepen.io*" ,
76- "https://twitter.com*" ,
77- "http://api.dojotoolkit.org*" ,
78- "http://www.webdesignermagazine.nl/*" ,
79- "http://meetup.com*" ,
80- "https://github.com/fronteers/website*" ,
81- ] ,
82- excludeInputs : [ ] ,
83- callback : null ,
84- } ) ;
118+ // Only run broken links plugin in production builds (it's very slow)
119+ if ( ! isDev ) {
120+ eleventyConfig . addPlugin ( brokenLinksPlugin , {
121+ redirect : "warn" ,
122+ broken : "warn" ,
123+ cacheDuration : "1d" ,
124+ loggingLevel : 1 ,
125+ excludeUrls : [
126+ "https://www.openstreetmap.org*" ,
127+ "https://www.youtube.com*" ,
128+ "http://www.example.com*" ,
129+ "https://codepen.io*" ,
130+ "https://twitter.com*" ,
131+ "http://api.dojotoolkit.org*" ,
132+ "http://www.webdesignermagazine.nl/*" ,
133+ "http://meetup.com*" ,
134+ "https://github.com/fronteers/website*" ,
135+ "https://www.w3.org/*"
136+ ] ,
137+ excludeInputs : [ ] ,
138+ callback : null ,
139+ } ) ;
140+ } else {
141+ console . debug ( "[dev] Skipping broken links plugin for faster builds" ) ;
142+ }
85143
86144 eleventyConfig . addPlugin ( pluginRss ) ;
87145
@@ -220,63 +278,68 @@ module.exports = function (eleventyConfig) {
220278 return lines ;
221279 } ) ;
222280
223- eleventyConfig . on ( 'afterBuild' , async ( ) => {
224- async function convertSvgToJpeg ( inputDir , outputDir ) {
225- const browser = await puppeteer . launch ( ) ;
226- const page = await browser . newPage ( ) ;
227-
228- // Read all files in the input directory
229- const files = fs . readdirSync ( inputDir ) ;
230-
231- for ( const filename of files ) {
232- if ( filename . endsWith ( ".svg" ) ) {
233- const inputPath = path . join ( inputDir , filename ) ;
234- const outputPath = path . join ( outputDir , filename . replace ( '.svg' , '.jpg' ) ) ;
235-
236- // Read the SVG content
237- const svgContent = fs . readFileSync ( inputPath , 'utf8' ) ;
238-
239- // Extract width and height from SVG (Optional: If SVG has explicit size)
240- const matchWidth = svgContent . match ( / w i d t h = " ( [ 0 - 9 ] + ) " / ) ;
241- const matchHeight = svgContent . match ( / h e i g h t = " ( [ 0 - 9 ] + ) " / ) ;
242-
243- const width = matchWidth ? parseInt ( matchWidth [ 1 ] , 10 ) : 1200 ; // Default to 1200px
244- const height = matchHeight ? parseInt ( matchHeight [ 1 ] , 10 ) : 675 ; // Default to 630px
245-
246- // Set the viewport size to match SVG size
247- await page . setViewport ( { width, height } ) ;
248-
249- // Set SVG content inside an HTML wrapper
250- await page . setContent ( `
251- <html>
252- <body style="margin:0;padding:0;overflow:hidden;">
253- <div style="width:${ width } px; height:${ height } px;">
254- ${ svgContent }
255- </div>
256- </body>
257- </html>
258- ` ) ;
259-
260- // Take a screenshot and save as JPEG
261- await page . screenshot ( {
262- path : outputPath ,
263- type : 'jpeg' ,
264- quality : 100 ,
265- clip : { x : 0 , y : 0 , width, height } // Ensure clipping matches viewport
266- } ) ;
267-
268- console . log ( `Converted: ${ filename } -> ${ outputPath } ` ) ;
281+ // Only run SVG to JPEG conversion in production builds (puppeteer is slow)
282+ if ( ! isDev ) {
283+ eleventyConfig . on ( 'afterBuild' , async ( ) => {
284+ async function convertSvgToJpeg ( inputDir , outputDir ) {
285+ const browser = await puppeteer . launch ( ) ;
286+ const page = await browser . newPage ( ) ;
287+
288+ // Read all files in the input directory
289+ const files = fs . readdirSync ( inputDir ) ;
290+
291+ for ( const filename of files ) {
292+ if ( filename . endsWith ( ".svg" ) ) {
293+ const inputPath = path . join ( inputDir , filename ) ;
294+ const outputPath = path . join ( outputDir , filename . replace ( '.svg' , '.jpg' ) ) ;
295+
296+ // Read the SVG content
297+ const svgContent = fs . readFileSync ( inputPath , 'utf8' ) ;
298+
299+ // Extract width and height from SVG (Optional: If SVG has explicit size)
300+ const matchWidth = svgContent . match ( / w i d t h = " ( [ 0 - 9 ] + ) " / ) ;
301+ const matchHeight = svgContent . match ( / h e i g h t = " ( [ 0 - 9 ] + ) " / ) ;
302+
303+ const width = matchWidth ? parseInt ( matchWidth [ 1 ] , 10 ) : 1200 ; // Default to 1200px
304+ const height = matchHeight ? parseInt ( matchHeight [ 1 ] , 10 ) : 675 ; // Default to 630px
305+
306+ // Set the viewport size to match SVG size
307+ await page . setViewport ( { width, height } ) ;
308+
309+ // Set SVG content inside an HTML wrapper
310+ await page . setContent ( `
311+ <html>
312+ <body style="margin:0;padding:0;overflow:hidden;">
313+ <div style="width:${ width } px; height:${ height } px;">
314+ ${ svgContent }
315+ </div>
316+ </body>
317+ </html>
318+ ` ) ;
319+
320+ // Take a screenshot and save as JPEG
321+ await page . screenshot ( {
322+ path : outputPath ,
323+ type : 'jpeg' ,
324+ quality : 100 ,
325+ clip : { x : 0 , y : 0 , width, height } // Ensure clipping matches viewport
326+ } ) ;
327+
328+ console . log ( `Converted: ${ filename } -> ${ outputPath } ` ) ;
329+ }
269330 }
270- }
271331
272- await browser . close ( ) ;
273- }
332+ await browser . close ( ) ;
333+ }
274334
275- // Execute conversion
276- const inputDir = 'dist/assets/images/social-preview-images/' ;
277- const outputDir = 'dist/assets/images/social-preview-images/' ;
278- await convertSvgToJpeg ( inputDir , outputDir ) ;
279- } ) ;
335+ // Execute conversion
336+ const inputDir = 'dist/assets/images/social-preview-images/' ;
337+ const outputDir = 'dist/assets/images/social-preview-images/' ;
338+ await convertSvgToJpeg ( inputDir , outputDir ) ;
339+ } ) ;
340+ } else {
341+ console . debug ( "[dev] Skipping SVG to JPEG conversion for faster builds" ) ;
342+ }
280343
281344 // Allows you to debug a json object in eleventy templates data | stringify
282345 eleventyConfig . addFilter ( "stringify" , ( data ) => {
@@ -300,9 +363,15 @@ module.exports = function (eleventyConfig) {
300363 } ) ;
301364
302365 /* This log will appear before the first build. It is tied to the plugin that checks broken links. */
303- console . debug (
304- "Eleventy will now generate RSS feeds and then look for broken links. This may take a while. When its done, you should see logs appear."
305- ) ;
366+ if ( ! isDev ) {
367+ console . debug (
368+ "Eleventy will now generate RSS feeds and then look for broken links. This may take a while. When its done, you should see logs appear."
369+ ) ;
370+ } else {
371+ console . debug (
372+ "[dev] Running in development mode - broken links checking and SVG conversion disabled for faster builds"
373+ ) ;
374+ }
306375
307376 /* All templates in the content directory are parsed and copied to the dist directory */
308377 return {
0 commit comments