Bug if 2 (#8)

* Adjust variable marker to ignore {:else}

* Getting start branch right content and end branch left content when extracting block content

* remove open if block after {/if}

* look for left/right branch content only up to common ancestor

* fix test case to new reality

* fix minor bug
This commit is contained in:
David Bruant 2025-05-21 15:09:47 +02:00 committed by GitHub
parent 4e86fc1656
commit 29a2429c00
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 100 additions and 10 deletions

2
.gitignore vendored
View File

@ -3,5 +3,7 @@ node_modules/
build/* build/*
.~lock* .~lock*
**/*(Copie)*
**/*(Copy)*
stats.html stats.html

View File

@ -85,6 +85,7 @@ function findPlacesToFillInString(str, compartment) {
* @returns {{startChild: Node, endChild:Node, content: DocumentFragment}} * @returns {{startChild: Node, endChild:Node, content: DocumentFragment}}
*/ */
function extractBlockContent(blockStartNode, blockEndNode) { function extractBlockContent(blockStartNode, blockEndNode) {
//console.log('[extractBlockContent] blockStartNode', blockStartNode.textContent)
//console.log('[extractBlockContent] blockEndNode', blockEndNode.textContent) //console.log('[extractBlockContent] blockEndNode', blockEndNode.textContent)
// find common ancestor of blockStartNode and blockEndNode // find common ancestor of blockStartNode and blockEndNode
@ -93,6 +94,7 @@ function extractBlockContent(blockStartNode, blockEndNode) {
let startAncestor = blockStartNode let startAncestor = blockStartNode
let endAncestor = blockEndNode let endAncestor = blockEndNode
// ancestries in order of deepest first, closest to root last
const startAncestry = new Set([startAncestor]) const startAncestry = new Set([startAncestor])
const endAncestry = new Set([endAncestor]) const endAncestry = new Set([endAncestor])
@ -117,9 +119,11 @@ function extractBlockContent(blockStartNode, blockEndNode) {
const startAncestryToCommonAncestor = [...startAncestry].slice(0, [...startAncestry].indexOf(commonAncestor)) const startAncestryToCommonAncestor = [...startAncestry].slice(0, [...startAncestry].indexOf(commonAncestor))
const endAncestryToCommonAncestor = [...endAncestry].slice(0, [...endAncestry].indexOf(commonAncestor)) const endAncestryToCommonAncestor = [...endAncestry].slice(0, [...endAncestry].indexOf(commonAncestor))
// direct children of commonAncestor in the branch or blockStartNode and blockEndNode respectively
const startChild = startAncestryToCommonAncestor.at(-1) const startChild = startAncestryToCommonAncestor.at(-1)
const endChild = endAncestryToCommonAncestor.at(-1) const endChild = endAncestryToCommonAncestor.at(-1)
//console.log('[extractBlockContent] startChild', startChild.textContent)
//console.log('[extractBlockContent] endChild', endChild.textContent) //console.log('[extractBlockContent] endChild', endChild.textContent)
// Extract DOM content in a documentFragment // Extract DOM content in a documentFragment
@ -127,15 +131,57 @@ function extractBlockContent(blockStartNode, blockEndNode) {
const contentFragment = blockStartNode.ownerDocument.createDocumentFragment() const contentFragment = blockStartNode.ownerDocument.createDocumentFragment()
/** @type {Element[]} */ /** @type {Element[]} */
const repeatedPatternArray = [] const blockContent = []
// get start branch "right" content
for(const startAncestor of startAncestryToCommonAncestor){
if(startAncestor === startChild)
break;
let sibling = startAncestor.nextSibling
while(sibling) {
blockContent.push(sibling)
sibling = sibling.nextSibling;
}
}
let sibling = startChild.nextSibling let sibling = startChild.nextSibling
while(sibling !== endChild) { while(sibling !== endChild) {
repeatedPatternArray.push(sibling) blockContent.push(sibling)
sibling = sibling.nextSibling; sibling = sibling.nextSibling;
} }
for(const sibling of repeatedPatternArray) {
// get end branch "left" content
for(const endAncestor of [...endAncestryToCommonAncestor].reverse()){
if(endAncestor === endChild)
continue; // already taken care of
let sibling = endAncestor.previousSibling
const reversedBlockContentContribution = []
while(sibling) {
reversedBlockContentContribution.push(sibling)
sibling = sibling.previousSibling;
}
const blockContentContribution = reversedBlockContentContribution.reverse()
blockContent.push(...blockContentContribution)
if(endAncestor === blockEndNode)
break;
}
//console.log('blockContent', blockContent.map(n => n.textContent))
for(const sibling of blockContent) {
sibling.parentNode?.removeChild(sibling) sibling.parentNode?.removeChild(sibling)
contentFragment.appendChild(sibling) contentFragment.appendChild(sibling)
} }
@ -206,7 +252,6 @@ function fillIfBlock(ifOpeningMarkerNode, ifElseMarkerNode, ifClosingMarkerNode,
.add(startIfThenChild).add(endIfThenChild) .add(startIfThenChild).add(endIfThenChild)
} }
if(chosenFragment) { if(chosenFragment) {
fillOdtElementTemplate( fillOdtElementTemplate(
chosenFragment, chosenFragment,
@ -340,7 +385,8 @@ const EACH = eachStartMarkerRegex.source
* @returns {void} * @returns {void}
*/ */
export default function fillOdtElementTemplate(rootElement, compartment) { export default function fillOdtElementTemplate(rootElement, compartment) {
//console.log('fillTemplatedOdtElement', rootElement.nodeType, rootElement.nodeName) //console.log('fillTemplatedOdtElement', rootElement.nodeType, rootElement.nodeName, rootElement.textContent)
//console.log('fillTemplatedOdtElement', rootElement.childNodes[0].childNodes.length)
let currentlyOpenBlocks = [] let currentlyOpenBlocks = []
@ -362,9 +408,11 @@ export default function fillOdtElementTemplate(rootElement, compartment) {
let ifBlockConditionExpression let ifBlockConditionExpression
// Traverse "in document order" // Traverse "in document order"
// @ts-ignore // @ts-ignore
traverse(rootElement, currentNode => { traverse(rootElement, currentNode => {
//console.log('currentlyUnclosedBlocks', currentlyUnclosedBlocks) //console.log('currentlyOpenBlocks', currentlyOpenBlocks)
const insideAnOpenBlock = currentlyOpenBlocks.length >= 1 const insideAnOpenBlock = currentlyOpenBlocks.length >= 1
if(currentNode.nodeType === Node.TEXT_NODE) { if(currentNode.nodeType === Node.TEXT_NODE) {
@ -473,9 +521,9 @@ export default function fillOdtElementTemplate(rootElement, compartment) {
/** /**
* Looking for {/if} * Looking for {/if}
*/ */
const hasClosingMarker = text.includes(closingIfMarker); const ifClosingMarker = text.includes(closingIfMarker);
if(hasClosingMarker) { if(ifClosingMarker) {
if(!insideAnOpenBlock) if(!insideAnOpenBlock)
throw new Error('{/if} without a corresponding {#if}') throw new Error('{/if} without a corresponding {#if}')
@ -498,6 +546,8 @@ export default function fillOdtElementTemplate(rootElement, compartment) {
else { else {
// do nothing because the marker is too deep // do nothing because the marker is too deep
} }
currentlyOpenBlocks.pop()
} }

View File

@ -1,5 +1,5 @@
// the regexps below are shared, so they shoudn't have state (no 'g' flag) // the regexps below are shared, so they shoudn't have state (no 'g' flag)
export const variableRegex = /\{([^{#\/]+?)\}/ export const variableRegex = /\{([^{#\/:]+?)\}/
export const ifStartMarkerRegex = /{#if\s+([^}]+?)\s*}/; export const ifStartMarkerRegex = /{#if\s+([^}]+?)\s*}/;
export const elseMarker = '{:else}' export const elseMarker = '{:else}'

View File

@ -3,6 +3,7 @@
import {traverse, Node} from "../../DOMUtils.js"; import {traverse, Node} from "../../DOMUtils.js";
import {closingIfMarker, eachClosingMarker, eachStartMarkerRegex, elseMarker, ifStartMarkerRegex, variableRegex} from './markers.js' import {closingIfMarker, eachClosingMarker, eachStartMarkerRegex, elseMarker, ifStartMarkerRegex, variableRegex} from './markers.js'
/** /**
* *
* @param {string} text * @param {string} text
@ -166,6 +167,7 @@ function consolidateMarkers(document){
] ]
for(const potentialMarkersContainer of potentialMarkersContainers) { for(const potentialMarkersContainer of potentialMarkersContainers) {
/** @type {{marker: string, index: number}[]} */
const consolidatedMarkers = [] const consolidatedMarkers = []
/** @type {Text[]} */ /** @type {Text[]} */
@ -244,13 +246,16 @@ function consolidateMarkers(document){
// Check if marker spans multiple nodes // Check if marker spans multiple nodes
if(startNode !== endNode) { if(startNode !== endNode) {
//console.log('startNode !== endNode', startNode.textContent, endNode.textContent)
const commonAncestor = findCommonAncestor(startNode, endNode) const commonAncestor = findCommonAncestor(startNode, endNode)
/** @type {Node} */
let commonAncestorStartChild = startNode let commonAncestorStartChild = startNode
while(commonAncestorStartChild.parentNode !== commonAncestor){ while(commonAncestorStartChild.parentNode !== commonAncestor){
commonAncestorStartChild = commonAncestorStartChild.parentNode commonAncestorStartChild = commonAncestorStartChild.parentNode
} }
/** @type {Node} */
let commonAncestorEndChild = endNode let commonAncestorEndChild = endNode
while(commonAncestorEndChild.parentNode !== commonAncestor){ while(commonAncestorEndChild.parentNode !== commonAncestor){
commonAncestorEndChild = commonAncestorEndChild.parentNode commonAncestorEndChild = commonAncestorEndChild.parentNode
@ -322,6 +327,7 @@ function consolidateMarkers(document){
} }
} }
} }
} }
/** /**
@ -551,5 +557,4 @@ export default function prepareTemplateDOMTree(document){
isolateMarkerText(document) isolateMarkerText(document)
// after isolateMarkerText, each marker is in exactly one text node // after isolateMarkerText, each marker is in exactly one text node
// (markers are separated from text that was before or after in the same text node) // (markers are separated from text that was before or after in the same text node)
} }

View File

@ -327,16 +327,22 @@ Année
Année Année
Énergie par personne Énergie par personne
1970 1970
36252.637 36252.637
1980 1980
43328.78 43328.78
1990 1990
46971.94 46971.94
2000 2000
53147.277 53147.277
2010 2010
48062.32 48062.32
2020 2020
37859.246 37859.246
`.trim()) `.trim())

View File

@ -40,3 +40,30 @@ n est un grand nombre
}); });
test('weird bug', async t => {
const templatePath = join(import.meta.dirname, '../fixtures/left-branch-content-and-two-consecutive-ifs.odt')
const templateContent = `Utilisation de sources lumineuses : {#if scientifique.source_lumineuses}Oui{:else}Non{/if}
{#if scientifique.source_lumineuses && scientifique.modalités_source_lumineuses }
Modalités dutilisation de sources lumineuses : {scientifique.modalités_source_lumineuses}
{/if}
`
const data = {
scientifique: {
source_lumineuses: false,
//modalités_source_lumineuses: 'lampes torches'
}
}
const odtTemplate = await getOdtTemplate(templatePath)
const templateTextContent = await getOdtTextContent(odtTemplate)
t.deepEqual(templateTextContent.trim(), templateContent.trim(), 'reconnaissance du template')
const odtResult = await fillOdtTemplate(odtTemplate, data)
const odtResultTextContent = await getOdtTextContent(odtResult)
t.deepEqual(odtResultTextContent.trim(), `Utilisation de sources lumineuses : Non`)
});

Binary file not shown.