diff --git a/packages/moon/dist/moon.js b/packages/moon/dist/moon.js index 1c9d5e34..5a6182c7 100644 --- a/packages/moon/dist/moon.js +++ b/packages/moon/dist/moon.js @@ -651,7 +651,7 @@ * List of global variables to ignore in expression scoping */ - var globals = ["NaN", "false", "function", "in", "null", "this", "true", "typeof", "undefined", "window"]; + var globals = ["Infinity", "NaN", "break", "case", "catch", "class", "const", "continue", "default", "delete", "do", "else", "extends", "false", "finally", "for", "function", "if", "in", "instanceof", "let", "new", "null", "return", "super", "switch", "this", "throw", "true", "try", "typeof", "undefined", "var", "void", "while", "window"]; /** * Generates a static value or an expression using `md` or `mc`. * @@ -865,63 +865,70 @@ */ function generateNodeFor(element, variable, locals, staticParts, staticPartsMap) { - var variableFor = "m" + variable; + var variableForChildren = "m" + variable; + var variableForChild = "m" + (variable + 1); + var variableForKey = "m" + (variable + 2); var attributes = element.attributes; - var dataLocals = attributes[""].value.split(",").map(function (local) { - return local.trim(); - }); - locals = locals.concat(dataLocals); - var dataName = "name" in attributes ? attributes.name.value : "\"span\""; - var dataData = "data" in attributes ? generateValue("data", attributes.data, locals) : { + var parameters = attributes[""].value; + var name = "name" in attributes ? attributes.name.value : "\"span\""; + var data = "data" in attributes ? generateValue("data", attributes.data, locals) : { value: "{}", isStatic: true - }; - var prelude; - var generateChild = generateNode(element.children[0], element, 0, variable + 1, locals, staticParts, staticPartsMap); - var body; + }; // Extract locals from the child parameters. + + var local; + + while ((local = expressionRE.exec(parameters)) !== null) { + local = local[1]; + + if (name !== undefined && globals.indexOf(name) === -1 && locals.indexOf(name) === -1) { + locals = locals.concat([local]); + } + } // Generate the child and pass the parameters as locals. + + + var generateChild = generateNode(element.children[0], element, 0, variable + 3, locals, staticParts, staticPartsMap); // Generate the child function. + + var childFunction; variable = generateChild.variable; if (generateChild.isStatic) { - // If the body is static, then use a static node in place of it. - body = variableFor + ".push(" + generateStaticPart(generateChild.prelude, generateChild.node, staticParts, staticPartsMap) + ");"; + // If the child is static, then use a static node in place of it. + childFunction = "return " + generateStaticPart(generateChild.prelude, generateChild.node, staticParts, staticPartsMap) + ";"; } else { - // If the body is dynamic, then use the dynamic node in the loop body. - body = "" + generateChild.prelude + variableFor + ".push(" + generateChild.node + ");"; + // If the child is dynamic, then use the dynamic node in the loop body. + childFunction = generateChild.prelude + "return " + generateChild.node + ";"; } + childFunction = "var " + variableForChild + "=function(" + parameters + "){" + childFunction + "};"; // Generate the iterable, loop, and arguments for the child function. + + var iterable; + var loop; + var args; + if ("in" in attributes) { // Generate a `for` loop over an object. The first local is the key and // the second is the value. - var dataObject = generateValue("in", attributes["in"], locals).value; - var dataKey = dataLocals[0]; - var dataObjectValue; - - if (dataLocals.length === 2) { - dataObjectValue = "var " + dataLocals[1] + "=" + dataObject + "[" + dataKey + "];"; - } else { - dataObjectValue = ""; - } - - prelude = "for(var " + dataKey + " in " + dataObject + "){" + dataObjectValue + body + "}"; + iterable = generateValue("in", attributes["in"], locals).value; + loop = "for(var " + variableForKey + " in " + iterable + "){"; + args = variableForKey + "," + iterable + "[" + variableForKey + "]"; } else { // Generate a `for` loop over an array. The first local is the value and // the second is the key (index). - var dataArray = generateValue("of", attributes.of, locals).value; - - var _dataKey = dataLocals.length === 2 ? dataLocals[1] : "m" + variable++; - - prelude = "for(var " + _dataKey + "=0;" + _dataKey + "<" + dataArray + ".length;" + _dataKey + "++){var " + dataLocals[0] + "=" + dataArray + "[" + _dataKey + "];" + body + "}"; + iterable = generateValue("of", attributes.of, locals).value; + loop = "for(var " + variableForKey + "=0;" + variableForKey + "<" + iterable + ".length;" + variableForKey + "++){"; + args = iterable + "[" + variableForKey + "]," + variableForKey; } - if (dataData.isStatic) { - dataData = generateStaticPart("", dataData.value, staticParts, staticPartsMap); + if (data.isStatic) { + data = generateStaticPart("", data.value, staticParts, staticPartsMap); } else { - dataData = dataData.value; + data = data.value; } return { - prelude: "var " + variableFor + "=[];" + prelude, - node: "m(" + types.element + "," + dataName + "," + dataData + "," + variableFor + ")", + prelude: "var " + variableForChildren + "=[];" + childFunction + loop + variableForChildren + ".push(" + variableForChild + "(" + args + "));}", + node: "m(" + types.element + "," + name + "," + data + "," + variableForChildren + ")", isStatic: false, variable: variable }; diff --git a/packages/moon/dist/moon.min.js b/packages/moon/dist/moon.min.js index 1078dcd2..1a8c06a0 100644 --- a/packages/moon/dist/moon.min.js +++ b/packages/moon/dist/moon.min.js @@ -4,4 +4,4 @@ * Released under the MIT License * https://kbrsh.github.io/moon */ -!function(e,t){"undefined"==typeof module?e.Moon=t():module.exports=t()}(this,function(){"use strict";var F={element:0,text:1,component:2};function d(e,t,a){this.node=e,this.element=t,this.children=a}function s(e,t,a,n){this.type=e,this.name=t,this.data=a,this.children=n}function n(e,t,a,n){return new s(e,t,a,n)}var N=/^\s+$/,A=/&|>|<| |"|\\|"|\n|\r/g,M={class:"className",for:"htmlFor"},C={"&":"&",">":">","<":"<"," ":" ",""":'\\"',"\\":"\\\\",'"':'\\"',"\n":"\\n","\r":"\\r"};function t(e){e=e.trim();for(var t=[],a=0;a",a+2),l=e.slice(a+2,i);0,t.push({type:"tagClose",value:l}),a=i+1;continue}if("!"===r&&"-"===e[a+2]&&"-"===e[a+3]){var o=e.indexOf("--\x3e",a+4);0,a=o+3;continue}var u="",s="",v=!1,d={},c=!0,f=0;for(a++;a"===p||"/"===p&&">"===e[a+1]&&(v=!0)&&(a+=1))){a+=1;break}c?" "===p?c=!1:"="===p?(c=!1,s+=p):u+=p:s+=p}for(var m=0;m","<":"<"," ":" ",""":'\\"',"\\":"\\\\",'"':'\\"',"\n":"\\n","\r":"\\r"};function t(e){e=e.trim();for(var t=[],a=0;a",a+2),l=e.slice(a+2,i);0,t.push({type:"tagClose",value:l}),a=i+1;continue}if("!"===r&&"-"===e[a+2]&&"-"===e[a+3]){var o=e.indexOf("--\x3e",a+4);0,a=o+3;continue}var u="",s="",v=!1,d={},c=!0,f=0;for(a++;a"===p||"/"===p&&">"===e[a+1]&&(v=!0)&&(a+=1))){a+=1;break}c?" "===p?c=!1:"="===p?(c=!1,s+=p):u+=p:s+=p}for(var m=0;m local.trim()); + const parameters = attributes[""].value; + const name = "name" in attributes ? attributes.name.value : "\"span\""; + let data = "data" in attributes ? generateValue("data", attributes.data, locals) : { value: "{}", isStatic: true }; - locals = locals.concat(dataLocals); + // Extract locals from the child parameters. + let local; - const dataName = "name" in attributes ? attributes.name.value : "\"span\""; - let dataData = "data" in attributes ? generateValue("data", attributes.data, locals) : { value: "{}", isStatic: true }; - let prelude; + while ((local = expressionRE.exec(parameters)) !== null) { + local = local[1]; + if (name !== undefined && globals.indexOf(name) === -1 && locals.indexOf(name) === -1) { + locals = locals.concat([local]); + } + } + + // Generate the child and pass the parameters as locals. const generateChild = generateNode( element.children[0], element, 0, - variable + 1, + variable + 3, locals, staticParts, staticPartsMap ); - let body; + // Generate the child function. + let childFunction; variable = generateChild.variable; if (generateChild.isStatic) { - // If the body is static, then use a static node in place of it. - body = `${variableFor}.push(${generateStaticPart(generateChild.prelude, generateChild.node, staticParts, staticPartsMap)});`; + // If the child is static, then use a static node in place of it. + childFunction = `return ${generateStaticPart(generateChild.prelude, generateChild.node, staticParts, staticPartsMap)};`; } else { - // If the body is dynamic, then use the dynamic node in the loop body. - body = `${generateChild.prelude}${variableFor}.push(${generateChild.node});`; + // If the child is dynamic, then use the dynamic node in the loop body. + childFunction = `${generateChild.prelude}return ${generateChild.node};`; } + childFunction = `var ${variableForChild}=function(${parameters}){${childFunction}};`; + + // Generate the iterable, loop, and arguments for the child function. + let iterable; + let loop; + let args; + if ("in" in attributes) { // Generate a `for` loop over an object. The first local is the key and // the second is the value. - const dataObject = generateValue("in", attributes.in, locals).value; - const dataKey = dataLocals[0]; - let dataObjectValue; - - if (dataLocals.length === 2) { - dataObjectValue = `var ${dataLocals[1]}=${dataObject}[${dataKey}];`; - } else { - dataObjectValue = ""; - } - - prelude = `for(var ${dataKey} in ${dataObject}){${dataObjectValue}${body}}`; + iterable = generateValue("in", attributes.in, locals).value; + loop = `for(var ${variableForKey} in ${iterable}){`; + args = `${variableForKey},${iterable}[${variableForKey}]`; } else { // Generate a `for` loop over an array. The first local is the value and // the second is the key (index). - const dataArray = generateValue("of", attributes.of, locals).value; - const dataKey = dataLocals.length === 2 ? dataLocals[1] : ("m" + variable++); - prelude = `for(var ${dataKey}=0;${dataKey}<${dataArray}.length;${dataKey}++){var ${dataLocals[0]}=${dataArray}[${dataKey}];${body}}`; + iterable = generateValue("of", attributes.of, locals).value; + loop = `for(var ${variableForKey}=0;${variableForKey}<${iterable}.length;${variableForKey}++){`; + args = `${iterable}[${variableForKey}],${variableForKey}`; } - if (dataData.isStatic) { - dataData = generateStaticPart("", dataData.value, staticParts, staticPartsMap); + if (data.isStatic) { + data = generateStaticPart("", data.value, staticParts, staticPartsMap); } else { - dataData = dataData.value; + data = data.value; } return { - prelude: `var ${variableFor}=[];${prelude}`, - node: `m(${types.element},${dataName},${dataData},${variableFor})`, + prelude: `var ${variableForChildren}=[];${childFunction}${loop}${variableForChildren}.push(${variableForChild}(${args}));}`, + node: `m(${types.element},${name},${data},${variableForChildren})`, isStatic: false, variable }; diff --git a/packages/moon/src/compiler/generator/util/util.js b/packages/moon/src/compiler/generator/util/util.js index 2a9f0d1a..09fc5ca3 100644 --- a/packages/moon/src/compiler/generator/util/util.js +++ b/packages/moon/src/compiler/generator/util/util.js @@ -2,12 +2,12 @@ * Capture the variables in expressions to scope them within the data * parameter. This ignores property names and deep object accesses. */ -const expressionRE = /"[^"]*"|'[^']*'|\d+[a-zA-Z$_]\w*|\.[a-zA-Z$_]\w*|[a-zA-Z$_]\w*:|([a-zA-Z$_]\w*)/g; +export const expressionRE = /"[^"]*"|'[^']*'|\d+[a-zA-Z$_]\w*|\.[a-zA-Z$_]\w*|[a-zA-Z$_]\w*:|([a-zA-Z$_]\w*)/g; /** * List of global variables to ignore in expression scoping */ -const globals = ["NaN", "false", "function", "in", "null", "this", "true", "typeof", "undefined", "window"]; +export const globals = ["Infinity", "NaN", "break", "case", "catch", "class", "const", "continue", "default", "delete", "do", "else", "extends", "false", "finally", "for", "function", "if", "in", "instanceof", "let", "new", "null", "return", "super", "switch", "this", "throw", "true", "try", "typeof", "undefined", "var", "void", "while", "window"]; /** * Generates a static value or an expression using `md` or `mc`. diff --git a/packages/moon/test/compiler/generator.test.js b/packages/moon/test/compiler/generator.test.js index 16bc31c1..e6110679 100644 --- a/packages/moon/test/compiler/generator.test.js +++ b/packages/moon/test/compiler/generator.test.js @@ -177,62 +177,62 @@ test("generate nested if/else-if/else node", () => { test("generate static for-of node", () => { assertGenerate( "

test

", - "if(!(0 in ms)){ms[0]=m(0,\"p\",{},[m(1,\"text\",{\"\":\"test\"},[])]);ms[1]={};}var m0=[];for(var m1=0;m1 { assertGenerate( "

{item}

", - "if(!(0 in ms)){ms[0]=[];ms[1]={};}var m0=[];for(var m1=0;m1 { assertGenerate( "

{item} {index}

", - "if(!(0 in ms)){ms[0]=[];ms[1]={};}var m0=[];for(var index=0;index { assertGenerate( "

test

", - "if(!(0 in ms)){ms[0]=m(0,\"p\",{},[m(1,\"text\",{\"\":\"test\"},[])]);ms[1]={};}var m0=[];for(var key in md.obj){m0.push(ms[0]);}return m(0,\"span\",ms[1],m0);" + "if(!(0 in ms)){ms[0]=m(0,\"p\",{},[m(1,\"text\",{\"\":\"test\"},[])]);ms[1]={};}var m0=[];var m1=function(key){return ms[0];};for(var m2 in md.obj){m0.push(m1(m2,md.obj[m2]));}return m(0,\"span\",ms[1],m0);" ); }); test("generate for-in node", () => { assertGenerate( "

{key}

", - "if(!(0 in ms)){ms[0]=[];ms[1]={};}var m0=[];for(var key in md.obj){m0.push(m(0,\"p\",ms[1],[m(1,\"text\",{\"\":key},ms[0])]));}return m(0,\"span\",ms[1],m0);" + "if(!(0 in ms)){ms[0]=[];ms[1]={};}var m0=[];var m1=function(key){return m(0,\"p\",ms[1],[m(1,\"text\",{\"\":key},ms[0])]);};for(var m2 in md.obj){m0.push(m1(m2,md.obj[m2]));}return m(0,\"span\",ms[1],m0);" ); }); test("generate for-in node with value", () => { assertGenerate( "

{key} {value}

", - "if(!(0 in ms)){ms[0]=[];ms[1]={};}var m0=[];for(var key in md.obj){var value=md.obj[key];m0.push(m(0,\"p\",ms[1],[m(1,\"text\",{\"\":key},ms[0]),m(1,\"text\",{\"\":value},ms[0])]));}return m(0,\"span\",ms[1],m0);" + "if(!(0 in ms)){ms[0]=[];ms[1]={};}var m0=[];var m1=function(key,value){return m(0,\"p\",ms[1],[m(1,\"text\",{\"\":key},ms[0]),m(1,\"text\",{\"\":value},ms[0])]);};for(var m2 in md.obj){m0.push(m1(m2,md.obj[m2]));}return m(0,\"span\",ms[1],m0);" ); }); test("generate nested for nodes", () => { assertGenerate( "

{item} {index} {key} {value}

", - "if(!(0 in ms)){ms[0]=[];ms[1]={};}var m0=[];for(var index=0;index { assertGenerate( "

{item} {index}

", - "if(!(0 in ms)){ms[0]=[];ms[1]={};ms[2]={ custom: true };}var m0=[];for(var index=0;index { assertGenerate( "

{item} {index}

", - "if(!(0 in ms)){ms[0]=[];ms[1]={};}var m0=[];for(var index=0;index