From 423891ac30cbffbdf6a0bb86e75fde4d0f62b279 Mon Sep 17 00:00:00 2001 From: Liu Date: Mon, 21 Aug 2023 22:10:11 +0800 Subject: [PATCH] feat(compile): auto create widget prototype --- lib/compiler/json.js | 128 +++++++++++++++------- lib/compiler/react-tsx.js | 26 +++-- test/fixtures/resource/src/ui/home.css.h | 12 ++ test/fixtures/resource/src/ui/home.js | 56 ++++++---- test/fixtures/resource/src/ui/home.js.h | 61 +++++++++++ test/fixtures/resource/src/ui/home.json.h | 27 +++-- test/fixtures/resource/src/ui/home.tsx.h | 38 ++++--- test/fixtures/resource/src/ui/home.xml.h | 12 +- test/fixtures/resource/src/ui/home.yml.h | 27 +++-- 9 files changed, 279 insertions(+), 108 deletions(-) create mode 100644 test/fixtures/resource/src/ui/home.css.h create mode 100644 test/fixtures/resource/src/ui/home.js.h diff --git a/lib/compiler/json.js b/lib/compiler/json.js index 99dc0dc..7054c9a 100644 --- a/lib/compiler/json.js +++ b/lib/compiler/json.js @@ -9,6 +9,7 @@ function toIdent(str) { function compileData(rootNode, { filePath, indent = 8 }) { let count = 0; + let templateNode = null; const { name: fileName, base: fileBase } = path.parse(filePath); const identPrefix = toIdent(fileName); const refs = []; @@ -16,6 +17,69 @@ function compileData(rootNode, { filePath, indent = 8 }) { const resourceLines = []; const templateLines = []; const indentStr = " ".repeat(indent); + const protoIdent = `${identPrefix}_proto`; + const parentIdent = "parent"; + + function generateRefsType() { + return refs.length > 0 + ? [ + `typedef struct {`, + ...refs.map((ref) => `${indentStr}ui_widget_t *${ref};`), + `} ${identPrefix}_refs_t;`, + "", + ] + : []; + } + + function generateResourceFunc() { + return resourceLines.length > 0 + ? [ + `static void ${identPrefix}_load_resources(void)`, + "{", + ...resourceLines.map((line) => (line ? `${indentStr}${line}` : line)), + "}", + "", + ] + : []; + } + + function generateTemplateFunc() { + return [ + `static void ${identPrefix}_load_template(ui_widget_t *parent${ + refs.length > 0 ? `, ${identPrefix}_refs_t *refs` : "" + })`, + "{", + ...(count > 0 ? [`${indentStr}ui_widget_t *w[${count}];\n`] : []), + ...templateLines.map((line) => (line ? `${indentStr}${line}` : line)), + "}", + ]; + } + + function generateProtoFunc() { + let typeName = "NULL"; + + if (!templateNode) { + return []; + } + if ( + templateNode && + Array.isArray(templateNode.children) && + templateNode.children.length === 1 + ) { + typeName = templateNode.children[0].name; + if (typeName === "w" || typeName === "widget") { + typeName = "NULL"; + } else { + typeName = `"${typeName}"`; + } + } + return [ + `static void ${identPrefix}_init_prototype(void)`, + "{", + `${indentStr}${protoIdent} = ui_create_widget_prototype("${fileName}", ${typeName});`, + "}", + ]; + } function translateNode(node) { let ident; @@ -24,7 +88,8 @@ function compileData(rootNode, { filePath, indent = 8 }) { switch (node.name) { case "ui": - ident = `${identPrefix}_parent`; + ident = parentIdent; + templateNode = node; case "lcui-app": break; case "resource": @@ -61,23 +126,31 @@ function compileData(rootNode, { filePath, indent = 8 }) { } return; default: + ident = null; if (["w", "widget"].includes(node.name)) { widgetType = attrs.type; } else { widgetType = node.name; } - if (attrs.ref && typeof attrs.ref === "string") { - ident = toIdent(attrs.ref); - refs.push(ident); - ident = `refs->${ident}`; + if ( + templateNode.children.length == 1 && + templateNode.children[0] === node + ) { + ident = parentIdent; } else { - ident = `w[${count++}]`; + if (attrs.ref && typeof attrs.ref === "string") { + ident = toIdent(attrs.ref); + refs.push(ident); + ident = `refs->${ident}`; + } else { + ident = `w[${count++}]`; + } + templateLines.push( + `${ident} = ui_create_widget(${ + widgetType ? `"${widgetType}"` : "NULL" + });` + ); } - templateLines.push( - `${ident} = ui_create_widget(${ - widgetType ? `"${widgetType}"` : "NULL" - });` - ); Object.keys(attrs).forEach((attrName) => { if (attrName === "class") { templateLines.push( @@ -105,7 +178,7 @@ function compileData(rootNode, { filePath, indent = 8 }) { } if (Array.isArray(node.children)) { node.children.map(translateNode).forEach((childIdent) => { - if (ident && childIdent) { + if (ident && childIdent && childIdent !== parentIdent) { templateLines.push(`ui_widget_append(${ident}, ${childIdent});`); } }); @@ -118,34 +191,15 @@ function compileData(rootNode, { filePath, indent = 8 }) { "", "#include ", "", - ...(refs.length > 0 - ? [ - `typedef struct {`, - ...refs.map((ref) => `${indentStr}ui_widget_t *${ref};`), - `} ${identPrefix}_refs_t;`, - "", - ] - : []), + ...generateRefsType(), + templateNode ? `static ui_widget_prototype_t *${protoIdent};\n` : "", ...globalLines, "", - `static void ${identPrefix}_load_template(ui_widget_t *${identPrefix}_parent${ - refs.length > 0 ? `, ${identPrefix}_refs_t *refs` : "" - })`, - "{", - ...[`ui_widget_t *w[${count}];`, "", ...templateLines].map((line) => - line ? `${indentStr}${line}` : line - ), - "}", + ...generateProtoFunc(), "", - ...(resourceLines.length > 0 - ? [ - `static void ${identPrefix}_load_resources(void)`, - "{", - ...resourceLines.map((line) => (line ? `${indentStr}${line}` : line)), - "}", - "", - ] - : []), + ...generateTemplateFunc(), + "", + ...generateResourceFunc(), ].join("\n"); } diff --git a/lib/compiler/react-tsx.js b/lib/compiler/react-tsx.js index 335e053..8236fd4 100644 --- a/lib/compiler/react-tsx.js +++ b/lib/compiler/react-tsx.js @@ -2,26 +2,30 @@ import fs from "fs"; import path from "path"; import postcss from "postcss"; import postcssModules from "postcss-modules"; -import postcssSass from "postcss-sass"; import React from "react"; import ts from "typescript"; import json from "./json.js"; +import { compileSass } from "./sass.js"; async function loadCSSFiles(input, options) { const files = []; async function loadCSSFile({ ident, filename, start, end }) { - const cssText = fs.readFileSync(filename, { encoding: "utf-8" }); + let cssText = fs.readFileSync(filename, { encoding: "utf-8" }); const mainExt = path.parse(filename).ext.toLowerCase(); const secondExt = path.parse(path.parse(filename).name).ext.toLowerCase(); const plugins = [ - [".sass", ".scss"].includes(mainExt) && postcssSass(), secondExt === ".module" && postcssModules({ exportGlobals: true, + getJSON() {}, }), ].filter(Boolean); + if ([".sass", ".scss"].includes(mainExt)) { + cssText = compileSass(cssText, filename).css; + } + const result = await postcss(plugins) .process(cssText, { from: filename }) .async(); @@ -116,6 +120,7 @@ function transformReactElement(el) { node.name = "widget"; break; default: + node.name = el.type; break; } } else if (typeof el.type === "function") { @@ -151,21 +156,23 @@ function transformReactElement(el) { function mergeResourceElements(tree, files, sourceFileName) { return { - ...tree, + name: "lcui-app", children: [ ...files.map((file) => ({ - type: "element", name: "resource", comment: `This css code string is compiled from file ${path.relative( path.dirname(sourceFileName), - file.filename, + file.filename )}`, attributes: { type: "text/css", }, text: file.css, })), - ...tree.children, + { + name: "ui", + children: [tree], + }, ], }; } @@ -181,7 +188,10 @@ async function compile(input, options) { }, }); const output = path.parse(path.join(options.buildDir, options.filePath)); - const outputPath = path.join(output.dir, `${output.name}.js`); + const outputPath = path.join(output.dir, `${output.name}.mjs`); + if (!fs.existsSync(output.dir)) { + fs.mkdirSync(output.dir, { recursive: true }); + } fs.writeFileSync(outputPath, result.outputText, { encoding: "utf-8" }); const component = (await import(`file://${outputPath}`)).default; const reactTree = transformReactElement(component()); diff --git a/test/fixtures/resource/src/ui/home.css.h b/test/fixtures/resource/src/ui/home.css.h new file mode 100644 index 0000000..38f74a6 --- /dev/null +++ b/test/fixtures/resource/src/ui/home.css.h @@ -0,0 +1,12 @@ +static const char *css_str_0 = "\ +root {\ + background-color: #f6f8fa;\ +}\ +\ +.feedback {\ + color: #28a745;\ + font-size: 12px;\ + margin-top: 5px;\ +}\ +\ +"; diff --git a/test/fixtures/resource/src/ui/home.js b/test/fixtures/resource/src/ui/home.js index bc1cc59..7661dc8 100644 --- a/test/fixtures/resource/src/ui/home.js +++ b/test/fixtures/resource/src/ui/home.js @@ -1,30 +1,42 @@ module.exports = { - resource: [ + name: "lcui-app", + children: [ { - "@type": "text/css", - "@src": "home.css", + name: "resource", + attributes: { type: "text/css", src: "home.css" }, }, { - "@type": "application/font-ttf", - "@src": "iconfont.ttf", - }, - ], - ui: { - text: [ - "Enter a message and save it.", - { - "#text": "Message has been saved!", - "@ref": "feedback", - "@class": "feedback", + name: "resource", + attributes: { + type: "application/font-ttf", + src: "iconfont.ttf", }, - ], - textedit: { - "@ref": "input-message", - "@placeholder": "eg: hello, world!", }, - button: { - "#text": "Save", - "@ref": "btn-save-message", + { + name: "ui", + children: [ + { + name: "text", + children: [{ type: "text", text: "Enter a message and save it." }], + }, + { + name: "textedit", + attributes: { + ref: "input-message", + placeholder: "eg: hello, world!", + }, + }, + { + name: "button", + attributes: { ref: "btn-save-message" }, + children: [{ type: "text", text: "Save" }], + }, + { + name: "text", + attributes: { ref: "feedback", class: "feedback" }, + children: [{ type: "text", text: "Message has been saved!" }], + }, + ], }, - }, + ], }; diff --git a/test/fixtures/resource/src/ui/home.js.h b/test/fixtures/resource/src/ui/home.js.h new file mode 100644 index 0000000..7ddeea1 --- /dev/null +++ b/test/fixtures/resource/src/ui/home.js.h @@ -0,0 +1,61 @@ +/** This file is generated from home.js */ + +#include + +typedef struct { + ui_widget_t *input_message; + ui_widget_t *btn_save_message; + ui_widget_t *feedback; +} home_refs_t; + +static ui_widget_prototype_t *home_proto; + +static const char *css_str_0 = "\ +root {\ + background-color: #f6f8fa;\ +}\ +\ +.feedback {\ + color: #28a745;\ + font-size: 12px;\ + margin-top: 5px;\ +}\ +\ +"; + + +static void home_init_prototype(void) +{ + home_proto = ui_create_widget_prototype("home", NULL); +} + +static void home_load_template(ui_widget_t *parent, home_refs_t *refs) +{ + ui_widget_t *w[4]; + + w[0] = ui_create_widget("text"); + w[1] = ui_create_widget(NULL); + ui_widget_set_text(w[1], "Enter a message and save it."); + ui_widget_append(w[0], w[1]); + refs->input_message = ui_create_widget("textedit"); + ui_widget_set_attr(refs->input_message, "placeholder", "eg: hello, world!"); + refs->btn_save_message = ui_create_widget("button"); + w[2] = ui_create_widget(NULL); + ui_widget_set_text(w[2], "Save"); + ui_widget_append(refs->btn_save_message, w[2]); + refs->feedback = ui_create_widget("text"); + ui_widget_add_class(refs->feedback, "feedback"); + w[3] = ui_create_widget(NULL); + ui_widget_set_text(w[3], "Message has been saved!"); + ui_widget_append(refs->feedback, w[3]); + ui_widget_append(parent, w[0]); + ui_widget_append(parent, refs->input_message); + ui_widget_append(parent, refs->btn_save_message); + ui_widget_append(parent, refs->feedback); +} + +static void home_load_resources(void) +{ + ui_load_css_string(css_str_0, "home.css"); + pd_font_library_load_file("iconfont.ttf"); +} diff --git a/test/fixtures/resource/src/ui/home.json.h b/test/fixtures/resource/src/ui/home.json.h index 9270b86..0c6e191 100644 --- a/test/fixtures/resource/src/ui/home.json.h +++ b/test/fixtures/resource/src/ui/home.json.h @@ -1,4 +1,4 @@ -/** This file is generated from Home.json */ +/** This file is generated from home.json */ #include @@ -6,9 +6,11 @@ typedef struct { ui_widget_t *input_message; ui_widget_t *btn_save_message; ui_widget_t *feedback; -} Home_refs_t; +} home_refs_t; -static const char *css_str_0 = "\ +static ui_widget_prototype_t *home_proto; + +static const char *css_str_1 = "\ root {\ background-color: #f6f8fa;\ }\ @@ -22,7 +24,12 @@ root {\ "; -static void Home_load_template(ui_widget_t *Home_parent, Home_refs_t *refs) +static void home_init_prototype(void) +{ + home_proto = ui_create_widget_prototype("home", NULL); +} + +static void home_load_template(ui_widget_t *parent, home_refs_t *refs) { ui_widget_t *w[4]; @@ -41,14 +48,14 @@ static void Home_load_template(ui_widget_t *Home_parent, Home_refs_t *refs) w[3] = ui_create_widget(NULL); ui_widget_set_text(w[3], "Message has been saved!"); ui_widget_append(refs->feedback, w[3]); - ui_widget_append(Home_parent, w[0]); - ui_widget_append(Home_parent, refs->input_message); - ui_widget_append(Home_parent, refs->btn_save_message); - ui_widget_append(Home_parent, refs->feedback); + ui_widget_append(parent, w[0]); + ui_widget_append(parent, refs->input_message); + ui_widget_append(parent, refs->btn_save_message); + ui_widget_append(parent, refs->feedback); } -static void Home_load_resources(void) +static void home_load_resources(void) { - ui_load_css_string(css_str_0, "home.css"); + ui_load_css_string(css_str_1, "home.css"); pd_font_library_load_file("iconfont.ttf"); } diff --git a/test/fixtures/resource/src/ui/home.tsx.h b/test/fixtures/resource/src/ui/home.tsx.h index 28d9632..d442500 100644 --- a/test/fixtures/resource/src/ui/home.tsx.h +++ b/test/fixtures/resource/src/ui/home.tsx.h @@ -2,6 +2,8 @@ #include +static ui_widget_prototype_t *home_proto; + /** This css code string is compiled from file home.module.css */ static const char *css_str_0 = "\ .home {\ @@ -23,24 +25,28 @@ static const char *css_str_0 = "\ "; -static void home_load_template(ui_widget_t *home_parent) +static void home_init_prototype(void) { - ui_widget_t *w[5]; - - w[0] = ui_create_widget(NULL); - ui_widget_add_class(w[0], "home"); - w[1] = ui_create_widget(NULL); - ui_widget_add_class(w[1], "_text_1ayu2_23"); - w[2] = ui_create_widget("text"); - ui_widget_set_text(w[2], "Hello, World!"); - ui_widget_append(w[1], w[2]); - w[3] = ui_create_widget(NULL); - ui_widget_add_class(w[3], "_button_1ayu2_9"); - w[4] = ui_create_widget("text"); - ui_widget_set_text(w[4], "Ok"); - ui_widget_append(w[3], w[4]); + home_proto = ui_create_widget_prototype("home", NULL); +} + +static void home_load_template(ui_widget_t *parent) +{ + ui_widget_t *w[4]; + + ui_widget_add_class(parent, "home"); + w[0] = ui_create_widget("text"); + ui_widget_add_class(w[0], "_text_1ayu2_23"); + w[1] = ui_create_widget("text"); + ui_widget_set_text(w[1], "Hello, World!"); ui_widget_append(w[0], w[1]); - ui_widget_append(w[0], w[3]); + w[2] = ui_create_widget("button"); + ui_widget_add_class(w[2], "_button_1ayu2_9"); + w[3] = ui_create_widget("text"); + ui_widget_set_text(w[3], "Ok"); + ui_widget_append(w[2], w[3]); + ui_widget_append(parent, w[0]); + ui_widget_append(parent, w[2]); } static void home_load_resources(void) diff --git a/test/fixtures/resource/src/ui/home.xml.h b/test/fixtures/resource/src/ui/home.xml.h index abb851a..804948a 100644 --- a/test/fixtures/resource/src/ui/home.xml.h +++ b/test/fixtures/resource/src/ui/home.xml.h @@ -22,7 +22,7 @@ root {\ "; -static void home_load_template(ui_widget_t *home_parent, home_refs_t *refs) +static void home_load_template(ui_widget_t *parent, home_refs_t *refs) { ui_widget_t *w[5]; @@ -42,10 +42,12 @@ static void home_load_template(ui_widget_t *home_parent, home_refs_t *refs) w[4] = ui_create_widget(NULL); ui_widget_set_text(w[4], "Message has been saved!"); ui_widget_append(refs->feedback, w[4]); - ui_widget_append(home_parent, w[1]); - ui_widget_append(home_parent, refs->input_message); - ui_widget_append(home_parent, refs->btn_save_message); - ui_widget_append(home_parent, refs->feedback); + ui_widget_append(parent, w[1]); + ui_widget_append(parent, refs->input_message); + ui_widget_append(parent, refs->btn_save_message); + ui_widget_append(parent, refs->feedback); + ui_widget_append(parent, parent); + ui_widget_append(w[0], parent); } static void home_load_resources(void) diff --git a/test/fixtures/resource/src/ui/home.yml.h b/test/fixtures/resource/src/ui/home.yml.h index 58b2de0..ddd2a14 100644 --- a/test/fixtures/resource/src/ui/home.yml.h +++ b/test/fixtures/resource/src/ui/home.yml.h @@ -1,4 +1,4 @@ -/** This file is generated from Home.yml */ +/** This file is generated from home.yml */ #include @@ -6,9 +6,11 @@ typedef struct { ui_widget_t *input_message; ui_widget_t *btn_save_message; ui_widget_t *feedback; -} Home_refs_t; +} home_refs_t; -static const char *css_str_0 = "\ +static ui_widget_prototype_t *home_proto; + +static const char *css_str_3 = "\ root {\ background-color: #f6f8fa;\ }\ @@ -22,7 +24,12 @@ root {\ "; -static void Home_load_template(ui_widget_t *Home_parent, Home_refs_t *refs) +static void home_init_prototype(void) +{ + home_proto = ui_create_widget_prototype("home", NULL); +} + +static void home_load_template(ui_widget_t *parent, home_refs_t *refs) { ui_widget_t *w[4]; @@ -41,14 +48,14 @@ static void Home_load_template(ui_widget_t *Home_parent, Home_refs_t *refs) w[3] = ui_create_widget(NULL); ui_widget_set_text(w[3], "Message has been saved!"); ui_widget_append(refs->feedback, w[3]); - ui_widget_append(Home_parent, w[0]); - ui_widget_append(Home_parent, refs->input_message); - ui_widget_append(Home_parent, refs->btn_save_message); - ui_widget_append(Home_parent, refs->feedback); + ui_widget_append(parent, w[0]); + ui_widget_append(parent, refs->input_message); + ui_widget_append(parent, refs->btn_save_message); + ui_widget_append(parent, refs->feedback); } -static void Home_load_resources(void) +static void home_load_resources(void) { - ui_load_css_string(css_str_0, "home.css"); + ui_load_css_string(css_str_3, "home.css"); pd_font_library_load_file("iconfont.ttf"); }