From f43fb98c0ac3c4310454cdbab50bfdeb027159dc Mon Sep 17 00:00:00 2001
From: Int <50065494+dhlolo@users.noreply.github.com>
Date: Thu, 24 Mar 2022 09:26:53 +0800
Subject: [PATCH] feat: support parse interpolation in `WXAttribute` (#25)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
* fix: #19
* fix: 修正測試用例
* fix: add new unit test
* fix: mv incoordination raw to rawValue
* fix: inline
Co-authored-by: iChenLei
---
.github/CODEOWNERS | 4 +-
.gitignore | 2 +-
.vscode/launch.json | 3 -
src/ast/build-ast.ts | 140 +++++++++++++++++++++++++++++++-----
src/ast/util.ts | 29 ++++++++
src/cst/lexer.ts | 124 ++++++++++++++++++++++++++++----
src/cst/parser.ts | 114 +++++++++++++++++++++++++----
tests/base-spec.js | 13 ++++
tests/error-spec.js | 8 +--
tests/interpolation-spec.js | 138 +++++++++++++++++++++++++++--------
tests/wxs-spec.js | 21 ++++++
11 files changed, 516 insertions(+), 80 deletions(-)
diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 6ee727b..12e25b5 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -1,3 +1,5 @@
# @wxml/parser is Lei Chen's personal project
# Owner
-* @iChenLei
\ No newline at end of file
+* @iChenLei
+# Collaborator
+src/ @dhlolo
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index 7f2f1d3..d8cfe41 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,4 +3,4 @@ lib
dist
.DS_store
.idea
-src/vscodedebug/test.ts
\ No newline at end of file
+src/vscodedebug/test.ts
diff --git a/.vscode/launch.json b/.vscode/launch.json
index d57a165..56d644d 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -1,7 +1,4 @@
{
- // Use IntelliSense to learn about possible attributes.
- // Hover to view descriptions of existing attributes.
- // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
diff --git a/src/ast/build-ast.ts b/src/ast/build-ast.ts
index f66dab8..7365da8 100644
--- a/src/ast/build-ast.ts
+++ b/src/ast/build-ast.ts
@@ -8,6 +8,7 @@ import {
convertLexerErrorToNode,
convertParseErrorToNode,
sortTokenChildren,
+ sortASTNode,
} from "./util";
type ICtx = Record;
@@ -87,15 +88,19 @@ class CstToAstVisitor extends BaseWxmlCstVisitor {
mergeLocation(astNode.startTag, startTagLocation);
}
- if (ctx.SLASH_OPEN?.[0] && ctx.END?.[0]) {
+ if (ctx.WXS_SLASH_CLOSE?.[0]) {
astNode.endTag = {
type: "WXEndTag",
- name: get(ctx, "END_NAME[0].image"),
- };
- const endTagLocation = {
- ...pick(ctx.SLASH_OPEN[0], ["startOffset", "startLine", "startColumn"]),
- ...pick(ctx.END[0], ["endOffset", "endLine", "endColumn"]),
+ name: "wxs",
};
+ const endTagLocation = pick(ctx.WXS_SLASH_CLOSE[0], [
+ "startOffset",
+ "startLine",
+ "startColumn",
+ "endOffset",
+ "endLine",
+ "endColumn",
+ ]);
mergeLocation(astNode.endTag, endTagLocation);
}
@@ -118,8 +123,8 @@ class CstToAstVisitor extends BaseWxmlCstVisitor {
if (ctx.SEA_WS !== undefined) {
allTokens = allTokens.concat(ctx.SEA_WS);
}
- if (ctx.WXS_TEXT !== undefined) {
- allTokens = allTokens.concat(ctx.WXS_TEXT);
+ if (ctx.INLINE_WXS_TEXT !== undefined) {
+ allTokens = allTokens.concat(ctx.INLINE_WXS_TEXT);
}
const sortedTokens = sortBy(allTokens, ["startOffset"]);
const fullText = map(sortedTokens, "image").join("");
@@ -130,19 +135,122 @@ class CstToAstVisitor extends BaseWxmlCstVisitor {
* AST - WXAttribute
*/
attribute(ctx, { location }) {
- const rawValue = get(ctx, "STRING[0].image") || null;
- const value = rawValue
- ? rawValue
- .split("")
- .slice(1, rawValue.length - 1)
- .join("")
+ const attributeValue = ctx.attributeValue
+ ? this.visit(ctx.attributeValue[0])
: null;
+
const astNode = {
type: "WXAttribute",
key: ctx.NAME[0].image,
+ quote: null,
+ value: null,
+ rawValue: null,
+ ...(attributeValue || {}),
+ };
+ mergeLocation(astNode, location);
+ return astNode;
+ }
+
+ /**
+ * AST - WXAttributeValue
+ */
+ attributeValue(ctx, { location }) {
+ if (ctx.PURE_STRING !== undefined) {
+ const raw = ctx.PURE_STRING[0].image;
+ const astNode = {
+ value: raw
+ .split("")
+ .slice(1, raw.length - 1)
+ .join(""),
+ rawValue: ctx.PURE_STRING[0].image,
+ children: [],
+ interpolations: [],
+ quote: raw?.length ? raw.slice(0, 1) : null,
+ };
+ mergeLocation(astNode, location);
+ return astNode;
+ } else if (ctx.doubleQuoteAttributeVal !== undefined) {
+ return this.visit(ctx.doubleQuoteAttributeVal[0]);
+ } else if (ctx.singleQuoteAttributeVal !== undefined) {
+ return this.visit(ctx.singleQuoteAttributeVal[0]);
+ }
+ }
+
+ doubleQuoteAttributeVal(ctx, { location }) {
+ const interpolationASTS = map(
+ ctx.attributeValInterpolation,
+ this.visit.bind(this)
+ );
+ const quote = '"';
+ let strASTs = map(ctx.PURE_STRING_IN_DOUBLE_QUOTE, (item) => {
+ const astNode = {
+ value: item.image,
+ type: "WXText",
+ };
+ mergeLocation(astNode, item);
+ return astNode;
+ });
+ const sortedValue = sortASTNode(interpolationASTS.concat(strASTs));
+ // @ts-expect-error
+ const value = sortedValue.map((v) => v.rawValue || v.value).join("");
+
+ const astNode = {
+ value,
+ rawValue: quote + value + quote,
+ children: sortedValue,
+ interpolations: interpolationASTS.map((intp) => {
+ return {
+ ...intp,
+ type: "WXInterpolation",
+ };
+ }),
+ quote: quote,
+ };
+ mergeLocation(astNode, location);
+ return astNode;
+ }
+
+ attributeValInterpolation(ctx, { location }) {
+ const child = sortTokenChildren(ctx);
+ // @ts-expect-error
+ const value = (child || []).map((token) => token.image).join("");
+ const astNode = {
+ type: "WXAttributeInterpolation",
+ rawValue: value,
+ value: value.replace(/^{{/, "").replace(/}}$/, ""),
+ };
+ mergeLocation(astNode, location);
+ return astNode;
+ }
+
+ singleQuoteAttributeVal(ctx, { location }) {
+ const interpolationASTS = map(
+ ctx.attributeValInterpolation,
+ this.visit.bind(this)
+ );
+ const quote = "'";
+ let strASTs = map(ctx.PURE_STRING_IN_SINGLE_QUOTE, (item) => {
+ const astNode = {
+ value: item.image,
+ type: "WXText",
+ };
+ mergeLocation(astNode, item);
+ return astNode;
+ });
+ const sortedValue = sortASTNode(interpolationASTS.concat(strASTs));
+ // @ts-expect-error
+ const value = sortedValue.map((v) => v.rawValue || v.value).join("");
+ const astNode = {
value,
- rawValue,
- quote: rawValue?.length ? rawValue.slice(0, 1) : null,
+ rawValue: quote + value + quote,
+ children: sortedValue,
+ interpolations: interpolationASTS.map((intp) => {
+ return {
+ ...intp,
+ type: "WXInterpolation",
+ };
+ }),
+ quote: quote,
};
mergeLocation(astNode, location);
return astNode;
diff --git a/src/ast/util.ts b/src/ast/util.ts
index 7ab59b5..205c83a 100644
--- a/src/ast/util.ts
+++ b/src/ast/util.ts
@@ -13,6 +13,35 @@ interface IEspreeError {
stack: string;
}
+interface ASTNode {
+ loc: {
+ start: {
+ line: number;
+ column: number;
+ };
+ };
+}
+
+export function sortASTNode(nodes: Array): Array {
+ let nodeASTs = [];
+ nodes.forEach((node) => {
+ nodeASTs.push(node);
+ });
+ let sortedNodes = [];
+ sortedNodes = nodes.sort((nodeA, nodeB) => {
+ if (
+ nodeA.loc.start.line > nodeB.loc.start.line ||
+ (nodeA.loc.start.line === nodeB.loc.start.line &&
+ nodeA.loc.start.column > nodeB.loc.start.column)
+ ) {
+ return 1;
+ } else {
+ return -1;
+ }
+ });
+ return sortedNodes;
+}
+
/**
* sort token children
*/
diff --git a/src/cst/lexer.ts b/src/cst/lexer.ts
index 3d72ef6..087f5a2 100644
--- a/src/cst/lexer.ts
+++ b/src/cst/lexer.ts
@@ -21,12 +21,10 @@ function makePattern(strings, ...args) {
return new RegExp(combined);
}
-const tokensArray = [];
export const tokensDictionary = {} as Record;
function createToken(options) {
const newTokenType = createTokenOrg(options);
- tokensArray.push(newTokenType);
tokensDictionary[options.name] = newTokenType;
return newTokenType;
}
@@ -44,7 +42,7 @@ FRAGMENT("Name", makePattern`${f.NameStartChar}(${f.NameChar})*`);
const WXS_START = createToken({
name: "WXS_START",
pattern: /)/;
-const INTPN = createToken({ name: "INTPN", pattern: /((?!('|"|}})).)+/ });
+const WXS_END = createToken({
+ name: "WXS_END",
+ pattern: /(?=<\/(( |\t|\n|\r\n)*)wxs(( |\t|\n|\r\n)*))>/,
+ pop_mode: true,
+});
+const WXS_CLOSE = createToken({
+ name: "WXS_CLOSE",
+ pattern: /(?!<\/(( |\t|\n|\r\n)*)wxs(( |\t|\n|\r\n)*))>/,
+ push_mode: "WXS_CONTENT",
+ longer_alt: WXS_END,
+});
+const WXS_SLASH_CLOSE = createToken({
+ name: "WXS_SLASH_CLOSE",
+ pattern: /<\/(( |\t|\n|\r\n)*)wxs(( |\t|\n|\r\n)*)>/,
+ pop_mode: true,
+});
const WXS_TEXT = createToken({
name: "WXS_TEXT",
- // ( |\t|\n|\r\n)*
- // allow these case or wxs />
- pattern: /[^]+?(?=<\/(( |\t|\n|\r\n)*)wxs(( |\t|\n|\r\n)*)>)/,
+ pattern: WXS_REG,
+ line_breaks: true,
+});
+
+const INLINE_WXS_TEXT = createToken({
+ name: "INLINE_WXS_TEXT",
+ pattern: WXS_REG,
line_breaks: true,
+ pop_mode: true,
});
const CLOSE = createToken({ name: "CLOSE", pattern: />/, pop_mode: true });
@@ -116,12 +138,24 @@ const MUSTACHE_LEFT = createToken({
push_mode: "INTPN_INSIDE",
});
+const MUSTACHE_LEFT_IN_QUOTE = createToken({
+ name: "MUSTACHE_LEFT_IN_QUOTE",
+ pattern: /\{\{/,
+ push_mode: "INTPN_IN_QUOTE",
+});
+
const MUSTACHE_RIGHT = createToken({
name: "MUSTACHE_RIGHT",
pattern: /\}\}/,
pop_mode: true,
});
+const MUSTACHE_RIGHT_IN_QUOTE = createToken({
+ name: "MUSTACHE_RIGHT_IN_QUOTE",
+ pattern: /\}\}/,
+ pop_mode: true,
+});
+
const EQUALS = createToken({ name: "EQUALS", pattern: /=/ });
const NAME = createToken({ name: "NAME", pattern: makePattern`${f.Name}` });
@@ -132,6 +166,45 @@ const SPACE = createToken({
group: Lexer.SKIPPED,
});
+const PURE_STRING = createToken({
+ name: "PURE_STRING",
+ pattern: /"[^"^\{\{]*"|'[^'^\{\{]*'/,
+});
+
+const PURE_STRING_IN_DOUBLE_QUOTE = createToken({
+ name: "PURE_STRING_IN_DOUBLE_QUOTE",
+ pattern: /[^"^\{\{^\}\}]+/,
+});
+
+const PURE_STRING_IN_SINGLE_QUOTE = createToken({
+ name: "PURE_STRING_IN_SINGLE_QUOTE",
+ pattern: /[^'^\{\{^\}\}]+/,
+});
+
+const DOUBLE_QUOTE_START = createToken({
+ name: "DOUBLE_QUOTE_START",
+ pattern: /"/,
+ push_mode: "DOUBLE_QUOTE_STR_INSIDE",
+});
+
+const DOUBLE_QUOTE_END = createToken({
+ name: "DOUBLE_QUOTE_END",
+ pattern: /"/,
+ pop_mode: true,
+});
+
+const SINGLE_QUOTE_START = createToken({
+ name: "SINGLE_QUOTE_START",
+ pattern: /'/,
+ push_mode: "SINGLE_QUOTE_STR_INSIDE",
+});
+
+const SINGLE_QUOTE_END = createToken({
+ name: "SINGLE_QUOTE_END",
+ pattern: /'/,
+ pop_mode: true,
+});
+
const wxmlLexerDefinition = {
defaultMode: "OUTSIDE",
@@ -142,25 +215,52 @@ const wxmlLexerDefinition = {
SEA_WS,
SLASH_OPEN,
OPEN,
- WXS_TEXT,
TEXT,
MUSTACHE_LEFT,
+ WXS_TEXT,
],
INTPN_INSIDE: [MUSTACHE_RIGHT, INTPN, SEA_WS, STRING],
+ DOUBLE_QUOTE_STR_INSIDE: [
+ DOUBLE_QUOTE_END,
+ MUSTACHE_LEFT_IN_QUOTE,
+ PURE_STRING_IN_DOUBLE_QUOTE,
+ SEA_WS,
+ ],
+ SINGLE_QUOTE_STR_INSIDE: [
+ SINGLE_QUOTE_END,
+ MUSTACHE_LEFT_IN_QUOTE,
+ PURE_STRING_IN_SINGLE_QUOTE,
+ SEA_WS,
+ ],
+ INTPN_IN_QUOTE: [MUSTACHE_RIGHT_IN_QUOTE, INTPN, STRING, SEA_WS],
INSIDE: [
- // Tokens from `OUTSIDE` to improve error recovery behavior
COMMENT,
INVALID_SLASH_OPEN,
INVALID_OPEN_INSIDE,
- // "Real" `INSIDE` tokens
CLOSE,
SLASH_CLOSE,
SLASH,
EQUALS,
- STRING,
+ PURE_STRING,
+ DOUBLE_QUOTE_START,
+ SINGLE_QUOTE_START,
+ NAME,
+ SPACE,
+ ],
+ WXS_INSIDE: [
+ WXS_SLASH_CLOSE,
+ WXS_CLOSE,
+ SLASH_CLOSE,
+ SLASH,
+ EQUALS,
+ PURE_STRING,
+ DOUBLE_QUOTE_START,
+ SINGLE_QUOTE_START,
NAME,
SPACE,
+ WXS_END,
],
+ WXS_CONTENT: [INLINE_WXS_TEXT],
},
};
diff --git a/src/cst/parser.ts b/src/cst/parser.ts
index bda1d06..e7aa03f 100644
--- a/src/cst/parser.ts
+++ b/src/cst/parser.ts
@@ -13,6 +13,10 @@ class Parser extends CstParser {
content: IRule;
wxscontent: IRule;
interpolation: IRule;
+ attributeValue: IRule;
+ doubleQuoteAttributeVal: IRule;
+ singleQuoteAttributeVal: IRule;
+ attributeValInterpolation: IRule;
constructor() {
super(t, {
@@ -59,23 +63,15 @@ class Parser extends CstParser {
$.OR([
{
ALT: () => {
- $.CONSUME(t.CLOSE, {
+ $.CONSUME(t.WXS_CLOSE, {
LABEL: "START_CLOSE",
ERR_MSG: "wxs element missing close '>'",
});
$.OPTION(() => {
$.SUBRULE($.wxscontent);
});
- $.CONSUME(t.SLASH_OPEN, {
- ERR_MSG: "wxs element missing slash open ''",
- });
- $.CONSUME2(t.NAME, {
- LABEL: "END_NAME",
- ERR_MSG: "wxs element missing end tag name",
- });
- $.CONSUME2(t.CLOSE, {
- LABEL: "END",
- ERR_MSG: "wxs element missing end close '>'",
+ $.CONSUME(t.WXS_SLASH_CLOSE, {
+ ERR_MSG: "wxs element missing slash open ''",
});
},
},
@@ -153,13 +149,105 @@ class Parser extends CstParser {
{
ALT: () => {
$.CONSUME(t.EQUALS);
- $.CONSUME(t.STRING, { ERR_MSG: "wx attributes missing value" });
+ $.SUBRULE($.attributeValue);
},
},
]);
});
});
+ $.RULE("attributeValue", () => {
+ $.OR([
+ {
+ ALT: () =>
+ $.CONSUME(t.PURE_STRING, {
+ ERR_MSG: "wx attributes missing value",
+ }),
+ },
+ {
+ ALT: () => $.SUBRULE($.doubleQuoteAttributeVal),
+ },
+ {
+ ALT: () => $.SUBRULE($.singleQuoteAttributeVal),
+ },
+ ]);
+ });
+
+ $.RULE("doubleQuoteAttributeVal", () => {
+ $.CONSUME(t.DOUBLE_QUOTE_START, {
+ ERR_MSG: "wx attributes unexpected start",
+ });
+ $.MANY(() => {
+ $.OR([
+ {
+ ALT: () =>
+ $.CONSUME(t.PURE_STRING_IN_DOUBLE_QUOTE, {
+ ERR_MSG: "wx attributes missing value",
+ }),
+ },
+ {
+ ALT: () => $.SUBRULE($.attributeValInterpolation),
+ },
+ ]);
+ });
+ $.CONSUME(t.DOUBLE_QUOTE_END, {
+ ERR_MSG: "wx attribute value unexpected end",
+ });
+ });
+
+ $.RULE("singleQuoteAttributeVal", () => {
+ $.CONSUME(t.SINGLE_QUOTE_START, {
+ ERR_MSG: "wx attributes unexpected start",
+ });
+ $.MANY(() => {
+ $.OR([
+ {
+ ALT: () =>
+ $.CONSUME(t.PURE_STRING_IN_SINGLE_QUOTE, {
+ ERR_MSG: "wx attributes missing value",
+ }),
+ },
+ {
+ ALT: () => $.SUBRULE($.attributeValInterpolation),
+ },
+ ]);
+ });
+ $.CONSUME(t.SINGLE_QUOTE_END, {
+ ERR_MSG: "wx attribute value unexpected end",
+ });
+ });
+
+ $.RULE("attributeValInterpolation", () => {
+ $.CONSUME(t.MUSTACHE_LEFT_IN_QUOTE, {
+ ERR_MSG: "wx interpolation in attributes value unexpected start",
+ });
+ $.MANY(() => {
+ $.OR([
+ {
+ ALT: () =>
+ $.CONSUME(t.INTPN, {
+ ERR_MSG: "wx interpolation in attributes unexpected intpn",
+ }),
+ },
+ {
+ ALT: () =>
+ $.CONSUME(t.STRING, {
+ ERR_MSG: "wx interpolation in attributes unexpected string",
+ }),
+ },
+ {
+ ALT: () =>
+ $.CONSUME(t.SEA_WS, {
+ ERR_MSG: "wx interpolation in attributes unexpected intpn",
+ }),
+ },
+ ]);
+ });
+ $.CONSUME(t.MUSTACHE_RIGHT_IN_QUOTE, {
+ ERR_MSG: "wx interpolation in attribute value unexpected end",
+ });
+ });
+
$.RULE("chardata", () => {
$.OR([
{ ALT: () => $.CONSUME(t.TEXT) },
@@ -172,7 +260,7 @@ class Parser extends CstParser {
$.CONSUME(t.SEA_WS);
});
$.OPTION(() => {
- $.CONSUME(t.WXS_TEXT);
+ $.CONSUME(t.INLINE_WXS_TEXT);
});
});
diff --git a/tests/base-spec.js b/tests/base-spec.js
index 92a36db..d2a2054 100644
--- a/tests/base-spec.js
+++ b/tests/base-spec.js
@@ -121,6 +121,19 @@ describe("Base Test Suite", () => {
expect(matches[2].quote).to.be.equals("'");
})
+ it("can parse WXAttribute #2", () => {
+ const ast = parse(`
+
+ `);
+ const matches = esquery(ast, "WXAttribute");
+ expect(matches).to.be.lengthOf(1);
+ expect(matches[0].key).to.be.equals("class");
+ expect(matches[0].value).to.be.equals(" {{isOdd ? 'odd' : 'even' }} num-{{idx}} show fixed 2");
+ expect(matches[0].rawValue).to.be.equals("\" {{isOdd ? 'odd' : 'even' }} num-{{idx}} show fixed 2\"");
+ const matches2 = esquery(ast, "WXAttributeInterpolation");
+ expect(matches2).to.be.lengthOf(2);
+ }),
+
it("can parse WXComment", () => {
const ast = parse(`
diff --git a/tests/error-spec.js b/tests/error-spec.js
index 88773fa..afea2ab 100644
--- a/tests/error-spec.js
+++ b/tests/error-spec.js
@@ -32,9 +32,9 @@ describe("Error Test Suite", () => {
// ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
const parseErrorMatchs = _.get(ast, 'errors') || [];
- expect(parseErrorMatchs).to.be.lengthOf(1);
+ expect(parseErrorMatchs).to.be.lengthOf(2);
- const parseError = parseErrorMatchs[0];
+ const parseError = parseErrorMatchs[1];
expect(parseError).to.have.property("rawType");
expect(parseError.rawType).to.be.equals("MismatchedTokenException");
});
@@ -53,9 +53,9 @@ describe("Error Test Suite", () => {
const parseError = parseErrorMatchs[0];
expect(parseError).to.have.property("rawType");
- expect(parseError.rawType).to.be.equals("MismatchedTokenException");
+ expect(parseError.rawType).to.be.equals("NoViableAltException");
expect(parseError).to.have.property("value");
- expect(parseError.value).to.be.equals("wx attributes missing value");
+ expect(parseError.value).to.be.equals('Expecting: one of these possible Token sequences:\n 1. [PURE_STRING]\n 2. [DOUBLE_QUOTE_START]\n 3. [SINGLE_QUOTE_START]\nbut found: \'>\'' );
// check WXAttribute
const attrsMatchs = esquery(ast, "WXAttribute")
diff --git a/tests/interpolation-spec.js b/tests/interpolation-spec.js
index 98b8ecb..84ec724 100644
--- a/tests/interpolation-spec.js
+++ b/tests/interpolation-spec.js
@@ -29,23 +29,61 @@ describe("Interpolation Test Suite", () => {
expect(wxInterpolation.value).to.be.equals(" message ");
})
- /**
- * @TODO not support parse WXInterpolation in "" string yet
- */
// #2 termplate attr
//
+ it("can parse WXInterpolation - literal template", () => {
+ const ast = parse(`
+
+ `);
+ expect(ast.errors).to.be.lengthOf(0);
+ const matches = esquery(ast, "WXInterpolation");
+ expect(matches).to.be.lengthOf(1);
+ const intp = _.get(matches, '[0]');
+ expect(intp).to.have.property("value");
+ expect(intp).to.have.property("rawValue");
+ expect(intp.rawValue).to.be.equals("{{id}}");
+ expect(intp.value).to.be.equals("id");
+ // raw WXAttribute is also exist
+ const matches2 = esquery(ast, "WXAttribute");
+ expect(matches2).to.be.lengthOf(1);
+ const wxAttr = matches2[0];
+ expect(wxAttr).to.have.property("value");
+ expect(wxAttr).to.have.property("rawValue");
+ expect(wxAttr.rawValue).to.be.equals("\"item-{{id}}\"");
+ expect(wxAttr.value).to.be.equals("item-{{id}}");
+ })
- /**
- * @TODO not support parse WXInterpolation in "" string yet
- */
// #3 condition
//
+ it("can parse WXInterpolation - condition", () => {
+ const ast = parse(`
+
+ `);
+ expect(ast.errors).to.be.lengthOf(0);
+ const matches = esquery(ast, "WXInterpolation");
+ expect(matches).to.be.lengthOf(1);
+ const intp = _.get(matches, '[0]');
+ expect(intp).to.have.property("value");
+ expect(intp).to.have.property("rawValue");
+ expect(intp.rawValue).to.be.equals("{{condition}}");
+ expect(intp.value).to.be.equals("condition");
+ })
- /**
- * @TODO not support parse WXInterpolation in "" string yet
- */
// #4 keyword
//
+ it("can parse WXInterpolation - keyword", () => {
+ const ast = parse(`
+
+ `);
+ expect(ast.errors).to.be.lengthOf(0);
+ const matches = esquery(ast, "WXInterpolation");
+ expect(matches).to.be.lengthOf(1);
+ const intp = _.get(matches, '[0]');
+ expect(intp).to.have.property("value");
+ expect(intp).to.have.property("rawValue");
+ expect(intp.rawValue).to.be.equals("{{false}}");
+ expect(intp.value).to.be.equals("false");
+ })
// #5 Ternary operation
// Hidden
@@ -75,23 +113,22 @@ describe("Interpolation Test Suite", () => {
expect(wxInterpolation.value).to.be.equals("a + b");
})
- /**
- * @TODO not support parse WXInterpolation in "" string yet
- */
// #7 logic check
//
//
- // it("can parse WXInterpolation - logic check", () => {
- // const ast = parse(`
- //
- // `);
- // expect(ast.errors).to.be.lengthOf(0);
- // const matches = esquery(ast, "WXInterpolation");
- // expect(matches).to.be.lengthOf(1);
- // const wxInterpolation = matches[0];
- // expect(wxInterpolation.rawValue).to.be.equals("{{length < 5}}");
- // expect(wxInterpolation.value).to.be.equals("length < 5");
- // })
+ it("can parse WXInterpolation - logic check", () => {
+ const ast = parse(`
+
+ `);
+ expect(ast.errors).to.be.lengthOf(0);
+ const matches = esquery(ast, "WXInterpolation");
+ expect(matches).to.be.lengthOf(1);
+ const intp = _.get(matches, '[0]');
+ expect(intp).to.have.property("value");
+ expect(intp).to.have.property("rawValue");
+ expect(intp.rawValue).to.be.equals("{{length < 5}}");
+ expect(intp.value).to.be.equals("length < 5");
+ })
// #8 string compute
// {{"hello<>>}}{{}}{{" + name}}
@@ -124,20 +161,47 @@ describe("Interpolation Test Suite", () => {
expect(wxInterpolation2.value).to.be.equals("array[0]");
})
- /**
- * @TODO not support parse WXInterpolation in "" string yet
- */
// #10 combine - array
// {{item}}
+ it("can parse WXInterpolation - combine array", () => {
+ const ast = parse(`
+ {{item}}
+ `);
+ expect(ast.errors).to.be.lengthOf(0);
+ const matches = esquery(ast, "WXInterpolation");
+ expect(matches).to.be.lengthOf(2);
+ const intp = _.get(matches, '[1]');
+ expect(intp).to.have.property("value");
+ expect(intp).to.have.property("rawValue");
+ expect(intp.rawValue).to.be.equals("{{[zero, 1, 2, 3, 4]}}");
+ expect(intp.value).to.be.equals("[zero, 1, 2, 3, 4]");
+ })
- /**
- * @TODO not support parse WXInterpolation in "" string yet
- */
// #11 combine - object
//
//
//
- //
+ it("can parse WXInterpolation - combine object", () => {
+ const ast = parse(`
+
+
+
+ `);
+ expect(ast.errors).to.be.lengthOf(0);
+ const matches = esquery(ast, "WXAttribute");
+ expect(matches[1].interpolations).to.be.lengthOf(1);
+ const intp = _.get(matches, '[1].interpolations[0]');
+ const intp2 = _.get(matches, '[3].interpolations[0]');
+ const intp3 = _.get(matches, '[5].interpolations[0]');
+ expect(intp).to.have.property("value");
+ expect(intp).to.have.property("rawValue");
+ expect(intp.rawValue).to.be.equals("{{for: a, bar: b}}");
+ expect(intp.value).to.be.equals("for: a, bar: b");
+ expect(intp2.rawValue).to.be.equals("{{foo, bar}}");
+ expect(intp2.value).to.be.equals("foo, bar");
+ expect(intp3.rawValue).to.be.equals("{{...obj1, ...obj2, e: 5}}");
+ expect(intp3.value).to.be.equals("...obj1, ...obj2, e: 5");
+ })
// #12 multi line
it("can parse WXInterpolation - multi line", () => {
@@ -153,4 +217,18 @@ describe("Interpolation Test Suite", () => {
expect(matches).to.be.lengthOf(1);
})
+ // #12 WXAttributeInterpolation count
+ it("can parse WXAttributeInterpolation - count correct", () => {
+ const ast = parse(`
+
+ {{i}}-{{j}}
+
+ `);
+ expect(ast.errors).to.be.lengthOf(0);
+ const matches = esquery(ast, "WXInterpolation");
+ expect(matches).to.be.lengthOf(4);
+ const matches2 = esquery(ast, "WXAttributeInterpolation");
+ expect(matches2).to.be.lengthOf(2);
+ })
+
});
diff --git a/tests/wxs-spec.js b/tests/wxs-spec.js
index 7556f6e..c4ce889 100644
--- a/tests/wxs-spec.js
+++ b/tests/wxs-spec.js
@@ -123,4 +123,25 @@ describe('WXS Test Suite', () => {
expect(matchs[0].name).to.be.equals("wxs");
});
+ it("WXSxript parse success when follow WXInterpolation", () => {
+ const ast = parse(`
+ {{100}}
+ var s = 22;
+ `)
+
+ expect(ast.errors.length).to.be.equals(0);
+ const matchs = esquery(ast, "WXScript");
+ expect(matchs[0].name).to.be.equals("wxs");
+ });
+
+ it("WXSxript parse success when follow WXText", () => {
+ const ast = parse(`
+ text
+ var s = 22;
+ `)
+
+ expect(ast.errors.length).to.be.equals(0);
+ const matchs = esquery(ast, "WXScript");
+ expect(matchs[0].name).to.be.equals("wxs");
+ });
})