Skip to content

Commit

Permalink
feat(compile): auto create widget prototype
Browse files Browse the repository at this point in the history
  • Loading branch information
lc-soft committed Aug 26, 2023
1 parent f71cba9 commit 423891a
Show file tree
Hide file tree
Showing 9 changed files with 279 additions and 108 deletions.
128 changes: 91 additions & 37 deletions lib/compiler/json.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,77 @@ 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 = [];
const globalLines = [];
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;
Expand All @@ -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":
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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});`);
}
});
Expand All @@ -118,34 +191,15 @@ function compileData(rootNode, { filePath, indent = 8 }) {
"",
"#include <ui.h>",
"",
...(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");
}

Expand Down
26 changes: 18 additions & 8 deletions lib/compiler/react-tsx.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -116,6 +120,7 @@ function transformReactElement(el) {
node.name = "widget";
break;
default:
node.name = el.type;
break;
}
} else if (typeof el.type === "function") {
Expand Down Expand Up @@ -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],
},
],
};
}
Expand All @@ -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());
Expand Down
12 changes: 12 additions & 0 deletions test/fixtures/resource/src/ui/home.css.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
static const char *css_str_0 = "\
root {\
background-color: #f6f8fa;\
}\
\
.feedback {\
color: #28a745;\
font-size: 12px;\
margin-top: 5px;\
}\
\
";
56 changes: 34 additions & 22 deletions test/fixtures/resource/src/ui/home.js
Original file line number Diff line number Diff line change
@@ -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!" }],
},
],
},
},
],
};
Loading

0 comments on commit 423891a

Please sign in to comment.