From bf236504d51119a955aacda5390c232510b3c320 Mon Sep 17 00:00:00 2001 From: flik1337 <664604982@qq.com> Date: Sat, 3 Aug 2024 18:51:15 +0800 Subject: [PATCH 1/3] Add 'orderSort' attribute to specify array sorting direction --- src/parser.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/parser.js b/src/parser.js index 0f5283c..59ff32b 100644 --- a/src/parser.js +++ b/src/parser.js @@ -555,7 +555,16 @@ module.exports = { const orderBy = attributes.orderBy.split(',').map(item => item.trim()); const valueA = a[orderBy[0]]; const valueB = b[orderBy[0]]; - let direction = 'asc'; + //let direction = 'asc'; + // Check if the orderBy array has more than one element + if (orderBy.length > 1) { + // If there is more than one element, assume the second element specifies the sort direction + // Convert the direction to lower case and trim any whitespace + direction = orderBy[1].toLowerCase().trim(); + }else{ + // Check if orderSort is 'desc' and set direction to 'desc' if true, otherwise set to 'asc' as default + direction = attributes.orderSort == 'desc' ? 'desc' : 'asc' + } if (orderBy.length > 1) { direction = orderBy[1].toLowerCase().trim(); From f199e498b3870b81ac0a9ea6fc5fd713257f7f99 Mon Sep 17 00:00:00 2001 From: flik1337 <664604982@qq.com> Date: Sat, 3 Aug 2024 18:54:14 +0800 Subject: [PATCH 2/3] Added pagination handling method to support array pagination. --- src/build.js | 55 +++++++++++++++++++++- src/dev.js | 47 +++++++++++++++++-- src/parser.js | 125 ++++++++++++++++++++++++++++++++++---------------- 3 files changed, 181 insertions(+), 46 deletions(-) diff --git a/src/build.js b/src/build.js index 349c2ff..234d876 100644 --- a/src/build.js +++ b/src/build.js @@ -47,7 +47,18 @@ function removeDirectory(dirPath) { } function buildPages(pagesDir, buildDir, url) { + paginationList = []; + // check if it's pagination page + const staticJsonPath = path.join(currentDirectory, 'static.json'); + if (fs.existsSync(staticJsonPath)) { + const staticJsonContent = fs.readFileSync(staticJsonPath, 'utf8'); + staticJSON = JSON.parse(staticJsonContent); + if (staticJSON.hasOwnProperty('paginationList')) { + paginationList = staticJSON.paginationList + } + } const entries = fs.readdirSync(pagesDir, { withFileTypes: true }); + for (const entry of entries) { const entryPath = path.join(pagesDir, entry.name); if (entry.isDirectory()) { @@ -55,7 +66,15 @@ function buildPages(pagesDir, buildDir, url) { fs.mkdirSync(newBuildDir, { recursive: true }); buildPages(entryPath, newBuildDir, url); } else if (entry.isFile() && entry.name.endsWith('.html')) { - buildFile(entryPath, buildDir, url); + routeBaseName = path.basename(entry.name,'.html') + // check this route in static.json + let pagination = getPageSizeForRoute("/" + routeBaseName, paginationList) + + if ( paginationList == [] || !pagination ){ + buildFile(entryPath, buildDir, url); + }else{ + buildPaginationFile(entryPath, buildDir, url, routeBaseName, pagination); + } } } } @@ -122,4 +141,38 @@ function buildFile(filePath, buildDir, url){ if(content != null){ fs.writeFileSync(filePath, content); } +} +function buildPaginationFile(filePath, buildDir, url, keyName, pagination){ + containsMagicLast = false + pageNo = 0; + while (!containsMagicLast){ + pageContent = parser.processFile(filePath, true, url, pageNo, pagination) + pageContent = parser.parseURLs(pageContent, url); + if ( pageContent.includes("
")){ + containsMagicLast = true + } + if (pageNo == 5){ + containsMagicLast = true + } + // Generate the file path for the current page + let pageFileDir = path.join(buildDir, keyName, 'pgn', `${pageNo}`); + fs.mkdirSync(pageFileDir, { recursive: true }); + fs.writeFileSync(path.join(pageFileDir, 'index.html'), pageContent); + // 额外为page0设置非/pgn的访问 + if (pageNo == 0){ + const newBuildDir = path.join(buildDir, keyName); + fs.writeFileSync(path.join(newBuildDir, 'index.html'), pageContent); + } + pageNo ++ + + } + +} +function getPageSizeForRoute(routeToCheck, paginationList) { + // Use the Array.prototype.find() method to find the first object that matches the given route. + const item = paginationList.find(item => item.route === routeToCheck); + + // Check if an item was found with the specified route; if so, return its pageSize. + // If no item is found (item is undefined), return null. + return item ? item : null; } \ No newline at end of file diff --git a/src/dev.js b/src/dev.js index 89c4281..25916de 100644 --- a/src/dev.js +++ b/src/dev.js @@ -96,7 +96,7 @@ module.exports = { }, handleRequest(req, res, url){ - const route = req.path === '/' ? '/index' : req.path; + let route = req.path === '/' ? '/index' : req.path; // First we are going to check if we have a content file in this location let contentPath = path.join(currentDirectory, './content', route + '.md'); @@ -114,21 +114,50 @@ module.exports = { return res.send(contentFile); } - // If we made it this far we want to now check if the static html file exists + // Handle pagination if specified in 'static.json'. + paginationList = []; + const staticJsonPath = path.join(currentDirectory, 'static.json'); + if (fs.existsSync(staticJsonPath)) { + const staticJsonContent = fs.readFileSync(staticJsonPath, 'utf8'); + staticJSON = JSON.parse(staticJsonContent); + if (staticJSON.hasOwnProperty('paginationList')) { + paginationList = staticJSON.paginationList + } + } + + // Regex to extract page number from the route. + let pageNo = null; + const pageRegex = /\/pgn\/(\d+)/; + isPaginationRoute = false; + const containsPageNo = route.match(pageRegex); // route = /posts/page/0 + if ( containsPageNo ){ + pageNo = parseInt(containsPageNo[1], 10); + isPaginationRoute = true + route = route.replace(pageRegex, '') + } + + // Check for pagination details in the static JSON configuration. + let pagination = this.getPageSizeForRoute(route, paginationList) + + if ( isPaginationRoute || pagination ){ + pageNo = isPaginationRoute ? pageNo : 0 + } + // Define paths to page files based on the current directory and the route. let pagePath = path.join(currentDirectory, './pages', route + '.html'); let pagePathIndex = path.join(currentDirectory, './pages', route, '/index.html'); let pageContent = null; + // Check if the specified HTML file or its index exists and process it if found. if (fs.existsSync(pagePath)) { - pageContent = parser.processFile(pagePath); + pageContent = parser.processFile(pagePath, false, 'relative', pageNo, pagination); + } else if(fs.existsSync(pagePathIndex)) { - pageContent = parser.processFile(pagePathIndex); + pageContent = parser.processFile(pagePathIndex, false, 'relative', pageNo, pagination); } if (pageContent != null) { pageContent = parser.parseURLs(pageContent, url); - return res.send(pageContent); } @@ -165,5 +194,13 @@ module.exports = { server.listen(port); }); + }, + getPageSizeForRoute(routeToCheck, paginationList) { + // Use the Array.prototype.find() method to find the first object that matches the given route. + const item = paginationList.find(item => item.route === routeToCheck); + + // Check if an item was found with the specified route; if so, return its pageSize. + // If no item is found (item is undefined), return null. + return item ? item : null; } } diff --git a/src/parser.js b/src/parser.js index 59ff32b..40d3d4c 100644 --- a/src/parser.js +++ b/src/parser.js @@ -8,9 +8,8 @@ let isContentFile = false; let env = require('./env.js'); module.exports = { - processFile(filePath, build=false, url='relative') { - - + processFile(filePath, build=false, url='relative', pageNo=null, pagination=null) { + let page = this.getPage(filePath); const layoutTagExists = /]*>[\s\S]*?<\/layout>/.test(page); @@ -30,8 +29,10 @@ module.exports = { // replace {slot} with content inside of Layout layout = layout.replace('{slot}', this.parseIncludeContent(this.getPageContent(page))); + + processedContentLoops = this.processContentLoops(this.parseShortCodes(this.replaceAttributesInLayout(layout, layoutAttributes), url, build), filePath); - page = this.processCollectionLoops(this.processContentLoops(this.parseShortCodes(this.replaceAttributesInLayout(layout, layoutAttributes), url, build), filePath), filePath); + page = this.processCollectionLoops(processedContentLoops, filePath, pageNo, pagination); page = this.processCollectionJSON(page); } @@ -450,71 +451,118 @@ module.exports = { fs.writeFileSync(filePath, jsonData); }, - processCollectionLoops(template, filePath) { + processCollectionLoops(template, filePath, pageNo, pagination) { + const { route, pageSize, iteratorKey } = pagination || {}; // Destructure pagination details + // Regular expression to capture the ForEach sections const loopRegex = /]+)>([\s\S]*?)<\/ForEach>/g; - let match; + // Define regex to match tag and its content + // const paginatorRegex = /([\s\S]*?)<\/paginator>/; + const paginatorRegex = /(?([\s\S]*?)<\/paginator>(?!\s*-->)/; + + // Extract paginator inner content + const extractedContentMatch = template.match(paginatorRegex); + let extractedPaginator = extractedContentMatch ? extractedContentMatch[1].trim() : null; + + // Remove tag and its content from the template + template = template.replace(paginatorRegex, '').trim(); + while ((match = loopRegex.exec(template)) !== null) { - const attributeString = match[1]; - const loopBody = match[2]; - - const attributes = this.forEachAttributesAndValues(''); + const attributeString = match[1]; // Extract attributes string + const loopBody = match[2]; // Extract loop body content - // Extract the collection name from the attributes - //const collectionNameMatch = /collection="([^"]+)"/.exec(attributeString); + const attributes = this.forEachAttributesAndValues(''); + + // Continue if the collection name is not found in attributes if (!attributes.collection) { - continue; // Skip if collection name is not found + continue; } - - // Load the corresponding JSON file + // Load JSON data from specified file let jsonData = JSON.parse(fs.readFileSync(path.join(currentDirectory, '/collections/', `${attributes.collection}.json`), 'utf8')); + + // Determine the loop keyword, default to collection name + let loopKeyword = attributes.as || attributes.collection.replace(/\//g, '.'); - let loopKeyword = attributes.collection.replace(/\//g, '.'); - if (attributes.as) { - loopKeyword = attributes.as; - } + // Target the specific ForEach loop based on iteratorKey + const targetForEach = attributes.iteratorKey && attributes.iteratorKey === iteratorKey; + + let count = attributes.count || null; // Maximum items to process, if specified - let count = null; - if(attributes.count){ - count = attributes.count; - } + jsonData = this.handleOrderBy(jsonData, attributes); // Apply sorting to data + + let paginationHtml = "" + const generatePgn = targetForEach && pageSize != null && pageNo != null - jsonData = this.handleOrderBy(jsonData, attributes); + // slice the jsonData and generate paginator content + if (generatePgn) { + const { pageData, isFirstPage, isLastPage } = this.paginateData(jsonData, pageSize, pageNo); //slice + jsonData = pageData; + // Generate pagination links + const { prevLink, nextLink } = this.generatePaginationLinks(isFirstPage, isLastPage, pageNo, '/posts/pgn/'); + if (extractedPaginator != null + && extractedPaginator.includes("prev") + && extractedPaginator.includes("next") ){ + + paginationHtml = extractedPaginator.replace("prev", prevLink).replace("next", nextLink); + } + + if (isLastPage) { + paginationHtml += "
"; // Add a marker for the last page + } + + } let loopResult = ''; let loop = 1; for (const item of jsonData) { let processedBody = loopBody; - const data = { ...item, loop }; + const data = { ...item, loop }; // Merge item data with loop index - // Process conditions + // Process conditions and placeholders processedBody = this.processConditions(processedBody, data, loopKeyword, loop); - + for (const key in item) { - // Regular expression to replace the placeholders const placeholderRegex = new RegExp(`{${loopKeyword}.${key}}`, 'g'); - let itemValue = item[key]; - if (Array.isArray(item[key])) { - itemValue = item[key].join("|"); - } + let itemValue = Array.isArray(item[key]) ? item[key].join("|") : item[key]; processedBody = processedBody.replace(placeholderRegex, itemValue); } - + loopResult += processedBody; loop++; - if((loop-1) == count){ - break; + if ((loop - 1) == count) { + break; // Stop processing if count limit is reached } } - + + // add the paginationHtml + if ( generatePgn ){ + loopResult += paginationHtml + } template = template.replace(match[0], loopResult); + + } return template; }, + paginateData(jsonData, pageSize, pageNo) { + const totalPages = Math.ceil(jsonData.length / pageSize); // Calculate total pages + const startIndex = pageNo * pageSize; // Compute start index for slicing + const endIndex = startIndex + pageSize; // Compute end index for slicing + const pageData = jsonData.slice(startIndex, endIndex); // Extract data for the current page + const isLastPage = pageNo === totalPages - 1; // Check if it is the last page + const isFirstPage = pageNo === 0; // Check if it is the first page + + return { pageData, isFirstPage, isLastPage, totalPages }; + }, + generatePaginationLinks(isFirstPage, isLastPage, pageNo, baseUrl) { + let prevLink = isFirstPage ? `style='visibility: hidden;'` : `href='${baseUrl}${pageNo - 1}'`; // Generate previous page link + let nextLink = isLastPage ? `style='visibility: hidden;'` : `href='${baseUrl}${pageNo + 1}'`; // Generate next page link + return { prevLink, nextLink }; + }, processConditions(content, data, parentCollection) { // Regular expression to capture the If sections @@ -555,6 +603,7 @@ module.exports = { const orderBy = attributes.orderBy.split(',').map(item => item.trim()); const valueA = a[orderBy[0]]; const valueB = b[orderBy[0]]; + //let direction = 'asc'; // Check if the orderBy array has more than one element if (orderBy.length > 1) { @@ -566,10 +615,6 @@ module.exports = { direction = attributes.orderSort == 'desc' ? 'desc' : 'asc' } - if (orderBy.length > 1) { - direction = orderBy[1].toLowerCase().trim(); - } - if (typeof valueA === 'string' && typeof valueB === 'string') { if (direction === 'desc') { return valueB.localeCompare(valueA); From 2a6702cd7b5fa50ab0d74fb5b50c9914532b7e76 Mon Sep 17 00:00:00 2001 From: flik1337 <664604982@qq.com> Date: Sat, 14 Sep 2024 01:24:07 +0800 Subject: [PATCH 3/3] Removed the pagination identifier prefix. --- src/build.js | 6 ++++-- src/dev.js | 4 +++- src/parser.js | 8 ++++++-- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/build.js b/src/build.js index 234d876..60ee718 100644 --- a/src/build.js +++ b/src/build.js @@ -155,10 +155,12 @@ function buildPaginationFile(filePath, buildDir, url, keyName, pagination){ containsMagicLast = true } // Generate the file path for the current page - let pageFileDir = path.join(buildDir, keyName, 'pgn', `${pageNo}`); + // let pageFileDir = path.join(buildDir, keyName, 'pgn', `${pageNo}`); + let pageFileDir = path.join(buildDir, keyName, `${pageNo}`); + fs.mkdirSync(pageFileDir, { recursive: true }); fs.writeFileSync(path.join(pageFileDir, 'index.html'), pageContent); - // 额外为page0设置非/pgn的访问 + // default : no /pageNo if (pageNo == 0){ const newBuildDir = path.join(buildDir, keyName); fs.writeFileSync(path.join(newBuildDir, 'index.html'), pageContent); diff --git a/src/dev.js b/src/dev.js index 25916de..65e122a 100644 --- a/src/dev.js +++ b/src/dev.js @@ -127,7 +127,9 @@ module.exports = { // Regex to extract page number from the route. let pageNo = null; - const pageRegex = /\/pgn\/(\d+)/; + // const pageRegex = /\/pgn\/(\d+)/; + const pageRegex = /\/(\d+)/; + isPaginationRoute = false; const containsPageNo = route.match(pageRegex); // route = /posts/page/0 if ( containsPageNo ){ diff --git a/src/parser.js b/src/parser.js index 40d3d4c..1d8672c 100644 --- a/src/parser.js +++ b/src/parser.js @@ -453,7 +453,7 @@ module.exports = { processCollectionLoops(template, filePath, pageNo, pagination) { const { route, pageSize, iteratorKey } = pagination || {}; // Destructure pagination details - + // Regular expression to capture the ForEach sections const loopRegex = /]+)>([\s\S]*?)<\/ForEach>/g; @@ -501,7 +501,11 @@ module.exports = { jsonData = pageData; // Generate pagination links - const { prevLink, nextLink } = this.generatePaginationLinks(isFirstPage, isLastPage, pageNo, '/posts/pgn/'); + // const { prevLink, nextLink } = this.generatePaginationLinks(isFirstPage, isLastPage, pageNo, '/posts/'); + routeMatch = route.endsWith('/') ? route : route + '/' + + const { prevLink, nextLink } = this.generatePaginationLinks(isFirstPage, isLastPage, pageNo, routeMatch); + if (extractedPaginator != null && extractedPaginator.includes("prev") && extractedPaginator.includes("next") ){