All files / src/compiler/lexer lexer.js

100% Statements 52/52
100% Branches 19/19
100% Functions 1/1
100% Lines 51/51

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 1212x 2x     13x   13x   13x 32x   32x 20x   20x 9x 9x   9x         9x 9x 11x         1x 1x     10x   10x 10x 10x 10x 10x 10x     10x       15x 15x 15x 15x   15x 10x   5x             10x             10x 12x 1x   1x 9x   9x 1x   8x       1x                 1x   11x   11x 67x   67x 9x   58x       11x                     13x    
const typeRE = /<([\w\d-_]+)([^>]*?)(\/?)>/g;
const attributeRE = /\s*([\w\d-_]*)(?:=(?:("[\w\d-_]*"|'[\w\d-_]*')|{([\w\d-_]*)}))?/g;
 
export function lex(input) {
	input = input.trim();
 
	let tokens = [];
 
	for (let i = 0; i < input.length;) {
		const char = input[i];
 
		if (char === "<") {
			const nextChar = input[i + 1];
 
			if (nextChar === "/") {
				const closeIndex = input.indexOf(">", i + 2);
				const type = input.slice(i + 2, closeIndex);
 
				tokens.push({
					type: "tagClose",
					value: type
				});
 
				i = closeIndex + 1;
				continue;
			} else if (
				nextChar === "!" &&
				input[i + 2] === "-" &&
				input[i + 3] === "-"
			) {
				i = input.indexOf("-->", i + 4) + 3;
				continue;
			}
 
			typeRE.lastIndex = i;
 
			const typeExec = typeRE.exec(input);
			const typeMatch = typeExec[0];
			const type = typeExec[1];
			const attributesText = typeExec[2];
			const closingSlash = typeExec[3];
			const attributes = {};
			let attributeExec;
 
			while (
				(attributeExec = attributeRE.exec(attributesText)) !==
				null
			) {
				const attributeMatch = attributeExec[0];
				const attributeKey = attributeExec[1];
				const attributeValue = attributeExec[2];
				const attributeExpression = attributeExec[3];
 
				if (attributeMatch.length === 0) {
					attributeRE.lastIndex += 1;
				} else {
					attributes[attributeKey] =
						attributeExpression === undefined ?
						attributeValue :
						attributeExpression;
				}
			}
 
			tokens.push({
				type: "tagOpen",
				value: type,
				attributes,
				closed: closingSlash === "/"
			});
 
			i += typeMatch.length;
		} else if (char === "{") {
			let expression = "";
 
			for (i += 1; i < input.length; i++) {
				const char = input[i];
 
				if (char === "}") {
					break;
				} else {
					expression += char;
				}
			}
 
			tokens.push({
				type: "tagOpen",
				value: "Text",
				attributes: {
					"": expression
				},
				closed: true
			});
 
			i += 1;
		} else {
			let text = "";
 
			for (; i < input.length; i++) {
				const char = input[i];
 
				if (char === "<") {
					break;
				} else {
					text += char;
				}
			}
 
			tokens.push({
				type: "tagOpen",
				value: "Text",
				attributes: {
					"": `"${text}"`
				},
				closed: true
			});
		}
	}
 
	return tokens;
}