|
@@ -1,19 +1,34 @@
|
|
-import { IPluginNode } from "../../Plugin";
|
|
|
|
|
|
+import { IPluginNode, PugToken } from "../../Plugin";
|
|
import { Hook } from "../Hook";
|
|
import { Hook } from "../Hook";
|
|
import { TagNode } from "../nodes/TagNode";
|
|
import { TagNode } from "../nodes/TagNode";
|
|
import { ScriptParser } from "../hooks/component/ScriptParser";
|
|
import { ScriptParser } from "../hooks/component/ScriptParser";
|
|
import { AstNode } from "../nodes/AstNode";
|
|
import { AstNode } from "../nodes/AstNode";
|
|
-import { Console } from "console";
|
|
|
|
import { PupperCompiler } from "../../Compiler";
|
|
import { PupperCompiler } from "../../Compiler";
|
|
import { CompilerNode } from "../../../model/core/nodes/CompilerNode";
|
|
import { CompilerNode } from "../../../model/core/nodes/CompilerNode";
|
|
|
|
|
|
const DefaultExportSymbol = Symbol("ExportedComponent");
|
|
const DefaultExportSymbol = Symbol("ExportedComponent");
|
|
|
|
|
|
|
|
+interface IImplementation {
|
|
|
|
+ name: string;
|
|
|
|
+ parameters: {
|
|
|
|
+ name: string;
|
|
|
|
+ initializer?: string;
|
|
|
|
+ }[]
|
|
|
|
+ body: string;
|
|
|
|
+}
|
|
|
|
+
|
|
export interface IComponent {
|
|
export interface IComponent {
|
|
name: string | symbol;
|
|
name: string | symbol;
|
|
|
|
|
|
|
|
+ implementation: {
|
|
|
|
+ methods?: IImplementation[];
|
|
|
|
+ when?: IImplementation[];
|
|
|
|
+ events?: (IImplementation & {
|
|
|
|
+ covers: string[]
|
|
|
|
+ })[];
|
|
|
|
+ },
|
|
|
|
+
|
|
template: string;
|
|
template: string;
|
|
- methods?: string;
|
|
|
|
script?: string;
|
|
script?: string;
|
|
style?: string;
|
|
style?: string;
|
|
data?: string;
|
|
data?: string;
|
|
@@ -31,14 +46,86 @@ export class PrepareComponents extends Hook {
|
|
protected exportedData: Record<string, string> = {};
|
|
protected exportedData: Record<string, string> = {};
|
|
|
|
|
|
public beforeStart(code: string) {
|
|
public beforeStart(code: string) {
|
|
- const matches = code.matchAll(/^\s*(methods|data).*[^.]$/gm);
|
|
|
|
|
|
+ const lines = code.replace(/\r\n/g, "\n").split(/\n/g);
|
|
|
|
+ const identation = this.plugin.detectIdentation();
|
|
|
|
+ const startWithRegExp = new RegExp("^" + identation + "(?!" + identation + ")");
|
|
|
|
+ const paramsRegExp = /^.+?\((?<params>.*?)\)$/gm;
|
|
|
|
+ const singleParamRegExp = /([^?=,]+)(=([^,]*))?/g;
|
|
|
|
+
|
|
|
|
+ for(let index = 0; index < lines.length; index++) {
|
|
|
|
+ let line = lines[index];
|
|
|
|
+
|
|
|
|
+ if (line.startsWith("data") && !line.trimEnd().endsWith(".")) {
|
|
|
|
+ lines[index] = line.trimEnd() + ".";
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!line.startsWith("implementation")) {
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
|
|
- // Add dots to ending "methods" and "data" tags
|
|
|
|
- for(let match of matches) {
|
|
|
|
- code = code.replace(match[0], match[0].trimEnd() + ".");
|
|
|
|
|
|
+ index++;
|
|
|
|
+
|
|
|
|
+ // Retrieve all lines until a non-identation was found
|
|
|
|
+ do {
|
|
|
|
+ line = lines[index];
|
|
|
|
+
|
|
|
|
+ if (line === undefined) {
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Ignore empty lines
|
|
|
|
+ if (line.length === 0) {
|
|
|
|
+ index++;
|
|
|
|
+ continue;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // If the line starts with one identation level
|
|
|
|
+ // but doesn't end with a dot and isn't a comment
|
|
|
|
+ if (line.match(startWithRegExp) && !line.trim().endsWith(".") && !line.trimStart().startsWith("//")) {
|
|
|
|
+ // Append a dot at the end of it
|
|
|
|
+ lines[index] = line.trimEnd() + ".";
|
|
|
|
+
|
|
|
|
+ let identifier = line.trimStart();
|
|
|
|
+
|
|
|
|
+ // If it's a "when"
|
|
|
|
+ if (identifier.startsWith("when")) {
|
|
|
|
+ // Replace it with the internal "p-when"
|
|
|
|
+ identifier = identifier.replace("when", "event-when");
|
|
|
|
+ } else
|
|
|
|
+ // If it's not an event
|
|
|
|
+ if (!identifier.startsWith("event")) {
|
|
|
|
+ // Assume it's a method then
|
|
|
|
+ identifier = identifier.replace(identifier, "method" + identifier);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Try matching params against the identifier
|
|
|
|
+ const matchedParams = paramsRegExp.exec(identifier);
|
|
|
|
+
|
|
|
|
+ // If matched
|
|
|
|
+ if (matchedParams) {
|
|
|
|
+ // Extract all single params
|
|
|
|
+ const singleParams = matchedParams.groups.params.matchAll(singleParamRegExp);
|
|
|
|
+
|
|
|
|
+ // Iterate over all params
|
|
|
|
+ for(let param of singleParams) {
|
|
|
|
+ // If it doesn't have a initializer
|
|
|
|
+ if (param[2] === undefined) {
|
|
|
|
+ // Strictly add an initializer to it
|
|
|
|
+ identifier = identifier.replace(param[0], param[0] + " = undefined");
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Replace the identifier with the new one
|
|
|
|
+ lines[index] = lines[index].replace(line.trimStart(), identifier);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ index++;
|
|
|
|
+ } while(line.length === 0 || line.startsWith(identation));
|
|
}
|
|
}
|
|
|
|
|
|
- return code;
|
|
|
|
|
|
+ return lines.join("\n");
|
|
}
|
|
}
|
|
|
|
|
|
public parse(nodes: IPluginNode[]) {
|
|
public parse(nodes: IPluginNode[]) {
|
|
@@ -88,8 +175,8 @@ export class PrepareComponents extends Hook {
|
|
const isRootComponent = node instanceof AstNode;
|
|
const isRootComponent = node instanceof AstNode;
|
|
const name = !isRootComponent ? node.getAttribute("name")?.replace(/"/g, "") : DefaultExportSymbol;
|
|
const name = !isRootComponent ? node.getAttribute("name")?.replace(/"/g, "") : DefaultExportSymbol;
|
|
|
|
|
|
|
|
+ const implementation = node.findFirstChildByTagName("implementation");
|
|
const template = node.findFirstChildByTagName("template");
|
|
const template = node.findFirstChildByTagName("template");
|
|
- const methods = node.findFirstChildByTagName("methods");
|
|
|
|
const script = node.findFirstChildByTagName("script");
|
|
const script = node.findFirstChildByTagName("script");
|
|
const style = node.findFirstChildByTagName("style");
|
|
const style = node.findFirstChildByTagName("style");
|
|
const data = node.findFirstChildByTagName("data");
|
|
const data = node.findFirstChildByTagName("data");
|
|
@@ -107,8 +194,12 @@ export class PrepareComponents extends Hook {
|
|
*/
|
|
*/
|
|
const component: IComponent = {
|
|
const component: IComponent = {
|
|
name,
|
|
name,
|
|
|
|
+ implementation: {
|
|
|
|
+ methods: [],
|
|
|
|
+ when: [],
|
|
|
|
+ events: []
|
|
|
|
+ },
|
|
template: null,
|
|
template: null,
|
|
- methods: null,
|
|
|
|
script: null,
|
|
script: null,
|
|
style: null,
|
|
style: null,
|
|
data: null,
|
|
data: null,
|
|
@@ -143,11 +234,11 @@ export class PrepareComponents extends Hook {
|
|
);
|
|
);
|
|
|
|
|
|
// Detect identation
|
|
// Detect identation
|
|
- const identation = /^([\t\n]*) */.exec(lines[0]);
|
|
|
|
|
|
+ const identation = this.plugin.detectIdentation();
|
|
|
|
|
|
const contents = lines
|
|
const contents = lines
|
|
// Replace the first identation
|
|
// Replace the first identation
|
|
- .map((line) => line.replace(identation[0], ""))
|
|
|
|
|
|
+ .map((line) => line.replace(identation, ""))
|
|
.join("\n");
|
|
.join("\n");
|
|
|
|
|
|
const templateAsString = new PupperCompiler(this.compiler.options).compileTemplate(contents);
|
|
const templateAsString = new PupperCompiler(this.compiler.options).compileTemplate(contents);
|
|
@@ -170,8 +261,73 @@ export class PrepareComponents extends Hook {
|
|
}
|
|
}
|
|
|
|
|
|
// If has methods
|
|
// If has methods
|
|
- if (methods) {
|
|
|
|
- component.methods = this.consumeChildrenAsString(methods);
|
|
|
|
|
|
+ if (implementation) {
|
|
|
|
+ // Iterate over all children
|
|
|
|
+ implementation.getChildren().forEach((child) => {
|
|
|
|
+ // Ignore comments
|
|
|
|
+ if (child.isType("Comment")) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // If it's not a tag
|
|
|
|
+ if (!(child instanceof TagNode)) {
|
|
|
|
+ throw this.plugin.compiler.makeParseError("The implementation tag should only contain methods and events, found a " + child.getType() + ".", {
|
|
|
|
+ line: child.getLine(),
|
|
|
|
+ column: child.getColumn()
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // If it isn't an event or method
|
|
|
|
+ if (!["method", "event", "event-when"].includes(child.getName())) {
|
|
|
|
+ throw this.plugin.compiler.makeParseError("The implementation tag should only contain methods, found an invalid tag " + child.getName() + ".", {
|
|
|
|
+ line: child.getLine(),
|
|
|
|
+ column: child.getColumn()
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ switch(child.getName()) {
|
|
|
|
+ // If it's a "method"
|
|
|
|
+ case "method":
|
|
|
|
+ // Add it to the methods
|
|
|
|
+ component.implementation.methods.push({
|
|
|
|
+ name: child.getId(),
|
|
|
|
+ parameters: child.getRawAttributes().filter((attr) => attr.name !== "class" && attr.name !== "id").map((attr) => ({
|
|
|
|
+ name: attr.name,
|
|
|
|
+ initializer: attr.val === "undefined" ? undefined : String(attr.val)
|
|
|
|
+ })),
|
|
|
|
+ body: this.consumeChildrenAsString(child)
|
|
|
|
+ });
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ // If it's a "when"
|
|
|
|
+ case "event-when":
|
|
|
|
+ // Add it to the when implementations
|
|
|
|
+ component.implementation.when.push({
|
|
|
|
+ name: child.getId(),
|
|
|
|
+ parameters: child.getAttributes().map((attr) => ({
|
|
|
|
+ name: attr.name,
|
|
|
|
+ initializer: attr.val
|
|
|
|
+ })),
|
|
|
|
+ body: this.consumeChildrenAsString(child)
|
|
|
|
+ });
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ // If it's an "event"
|
|
|
|
+ case "event":
|
|
|
|
+ // Add it to the events implementations
|
|
|
|
+ component.implementation.events.push({
|
|
|
|
+ // Events has the prefix "$$p_" to prevent conflicts.
|
|
|
|
+ name: "$$p_" + child.getId(),
|
|
|
|
+ parameters: child.getAttributes().filter((attr) => attr.name !== "class" && attr.name !== "id").map((attr) => ({
|
|
|
|
+ name: attr.name,
|
|
|
|
+ initializer: attr.val
|
|
|
|
+ })),
|
|
|
|
+ body: this.consumeChildrenAsString(child),
|
|
|
|
+ covers: child.getClasses()
|
|
|
|
+ });
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ });
|
|
}
|
|
}
|
|
|
|
|
|
return component;
|
|
return component;
|
|
@@ -179,6 +335,6 @@ export class PrepareComponents extends Hook {
|
|
|
|
|
|
protected consumeChildrenAsString(node: CompilerNode) {
|
|
protected consumeChildrenAsString(node: CompilerNode) {
|
|
this.plugin.parseChildren(node);
|
|
this.plugin.parseChildren(node);
|
|
- return node.getChildren().map((child) => child.getProp("val")).join("");
|
|
|
|
|
|
+ return node.getChildren().map((child) => child.getProp("val")).join("").trimEnd();
|
|
}
|
|
}
|
|
};
|
|
};
|