diff --git a/src/lib/codegenerator/generator.js b/src/lib/codegenerator/generator.js index ab97fea3..3fb55b64 100644 --- a/src/lib/codegenerator/generator.js +++ b/src/lib/codegenerator/generator.js @@ -16,6 +16,9 @@ */ let counter +let forRelativeDepth +let forCounter +let forMetadataMap export default function (templateObject = { children: [] }) { const ctx = { @@ -25,6 +28,9 @@ export default function (templateObject = { children: [] }) { } counter = -1 + forCounter = -1 + forRelativeDepth = -1 + forMetadataMap = new Map() generateCode.call(ctx, templateObject) ctx.renderCode.push('return elms') @@ -264,11 +270,22 @@ const generateComponentCode = function ( } const generateForLoopCode = function (templateObject, parent) { + // This is to handle number of elements created by previous forloop in nested case + if (forMetadataMap.has(forRelativeDepth) === true) { + forMetadataMap.get(forRelativeDepth).set('endCounter', counter - 1) + } + + // Increment forloop relative depth by 1 + forRelativeDepth++ + const forLoop = templateObject[':for'] delete templateObject[':for'] const key = templateObject['key'] - const forKey = interpolate(key, 'scope.') + + // Increment forCounter to make sure scope, __index variables always get unique names + forCounter++ + const forKey = interpolate(key, `scope${forCounter}.`) const shallow = !!!( templateObject['$shallow'] && templateObject['$shallow'].toLowerCase() === 'false' @@ -281,13 +298,27 @@ const generateForLoopCode = function (templateObject, parent) { const result = regex.exec(forLoop) + // Get previous for-loop data member path, like component.data + const pathPrefix = forMetadataMap.has(forRelativeDepth - 1) + ? forMetadataMap.get(forRelativeDepth - 1).get('path') + : 'component.' + + const loopingOverProp = result[2].replace('$', '').split('.').pop() + const path = `${pathPrefix}${loopingOverProp}[__index${forCounter}].` + + const currentForInfo = new Map() + currentForInfo.set('path', path) + currentForInfo.set('endCounter', -1) + + forMetadataMap.set(forRelativeDepth, currentForInfo) + // can be improved with a smarter regex const [item, index = 'index'] = result[1] .replace('(', '') .replace(')', '') .split(/\s*,\s*/) - const scopeRegex = new RegExp(`(scope\\.(?!${item}\\.|${index}|key)(\\w+))`, 'gi') + const scopeRegex = new RegExp(`(scope${forCounter}\\.(?!${item}\\.|${index}|key)(\\w+))`, 'gi') // local context const ctx = { @@ -322,23 +353,24 @@ const generateForLoopCode = function (templateObject, parent) { ctx.renderCode.push(` created.length = 0 const length = rawCollection.length - for(let __index = 0; __index < length; __index++) { - const scope = Object.create(component) + for(let __index${forCounter} = 0; __index${forCounter} < length; __index${forCounter}++) { + const scope${forCounter} = Object.create(component) parent = ${parent} - scope['${index}'] = __index - scope['${item}'] = rawCollection[__index] - scope['key'] = '' + ${forKey || '__index'} + scope${forCounter}['${index}'] = __index${forCounter} + scope${forCounter}['${item}'] = rawCollection[__index${forCounter}] + scope${forCounter}['key'] = '' + ${forKey || '__index' + forCounter} `) + if ('ref' in templateObject && templateObject.ref.indexOf('$') === -1) { // automatically map the ref for each item in the loop based on the given ref key ctx.renderCode.push(` - scope['__ref'] = '${templateObject.ref}' + __index + scope${forCounter}['__ref'] = '${templateObject.ref}' + __index${forCounter} `) templateObject.ref = '$__ref' } ctx.renderCode.push(` - created.push(scope.key) + created.push(scope${forCounter}.key) `) if ( @@ -347,15 +379,15 @@ const generateForLoopCode = function (templateObject, parent) { templateObject[Symbol.for('componentType')] === 'Text' ) { generateElementCode.call(ctx, templateObject, parent, { - key: 'scope.key', - component: 'scope.', + key: `scope${forCounter}.key`, + component: `scope${forCounter}.`, forceEffect: false, forloop: true, }) } else { generateComponentCode.call(ctx, templateObject, false, { - key: 'scope.key', - component: 'scope.', + key: `scope${forCounter}.key`, + component: `scope${forCounter}.`, forceEffect: false, forloop: true, }) @@ -373,8 +405,8 @@ const generateForLoopCode = function (templateObject, parent) { if (shallow === false) { ctx.renderCode.push(` - scope['${item}'] = null - scope['${item}'] = collection[__index] + scope${forCounter}['${item}'] = null + scope${forCounter}['${item}'] = collection[__index${forCounter}] `) } @@ -410,7 +442,13 @@ const generateForLoopCode = function (templateObject, parent) { if (keys.has(created[i]) === false) { const key = created[i] `) - const forEndCounter = counter + // const forEndCounter = counter + + const currentForStoredInfo = forMetadataMap.get(forRelativeDepth) + const forEndCounter = + currentForStoredInfo && currentForStoredInfo.get('endCounter') === -1 + ? counter + : currentForStoredInfo.get('endCounter') for (let i = forStartCounter; i <= forEndCounter; i++) { destroyCode.push(` @@ -425,10 +463,16 @@ const generateForLoopCode = function (templateObject, parent) { // inject the destroy code in the correct spot ctx.renderCode.splice(indexToInjectDestroyCode, 0, ...destroyCode) + + const dataArr = + forRelativeDepth == 0 + ? cast(result[2], ':for') + : cast(`$${loopingOverProp}`, ':for', forMetadataMap.get(forRelativeDepth - 1).get('path')) + ctx.renderCode.push(` effect(() => { - forloop${forStartCounter}(${cast(result[2], ':for')}, elms, created${forStartCounter}) - }, '${interpolate(result[2], '')}') + forloop${forStartCounter}(${dataArr}, elms, created${forStartCounter}) + }, '${forRelativeDepth == 0 ? interpolate(result[2], '') : loopingOverProp}') `) outerScopeEffects.forEach((effect) => { @@ -447,11 +491,11 @@ const generateForLoopCode = function (templateObject, parent) { ctx.renderCode.push(` effect(() => { void ${refs.join(', ')} - for(let __index = 0; __index < ${interpolate(result[2])}.length; __index++) { - const scope = {} - scope['${index}'] = __index - scope['${item}'] = ${interpolate(result[2])}[__index] - scope['key'] = ${forKey || '__index'} + for(let __index${forCounter} = 0; __index${forCounter} < ${dataArr}.length; __index${forCounter}++) { + const scope${forCounter} = {} + scope${forCounter}['${index}'] = __index${forCounter} + scope${forCounter}['${item}'] = ${dataArr}[__index${forCounter}] + scope${forCounter}['key'] = ${forKey || '__index' + forCounter} `) ctx.renderCode.push(` @@ -460,8 +504,9 @@ const generateForLoopCode = function (templateObject, parent) { }) `) }) - this.renderCode.push(ctx.renderCode.join('\n')) + + forRelativeDepth-- } const generateCode = function (templateObject, parent = false, options = {}) {