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") ){