mirror of
https://github.com/project-gauntlet/gauntlet.git
synced 2025-12-23 10:35:53 +00:00
Beginnings of component model
This commit is contained in:
parent
45c005d6b9
commit
de8ee619a6
17 changed files with 662 additions and 26 deletions
9
Cargo.lock
generated
9
Cargo.lock
generated
|
|
@ -1021,6 +1021,15 @@ dependencies = [
|
|||
"zbus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "component_model"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "concurrent-queue"
|
||||
version = "2.3.0"
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ members = [
|
|||
"rust/common",
|
||||
"rust/utils",
|
||||
"rust/cli",
|
||||
"rust/component_model",
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
|
|
|
|||
5
js/component_model/.gitignore
vendored
Normal file
5
js/component_model/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
node_modules
|
||||
component_model.json
|
||||
gen
|
||||
gentypes
|
||||
gendist
|
||||
18
js/component_model/package.json
Normal file
18
js/component_model/package.json
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"name": "placeholdername-component-model",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"main": "./gendist/components.js",
|
||||
"types": "./gentypes/components.d.ts",
|
||||
"scripts": {
|
||||
"run-generator-json": "cd ../.. && cargo run --package component_model -- ./js/component_model/component_model.json",
|
||||
"run-generator-source": "ts-node --esm src/index.ts",
|
||||
"run-generator-declarations": "tsc --project tsconfig.gentypes.json",
|
||||
"run-generator": "npm run run-generator-json && npm run run-generator-source && npm run run-generator-declarations"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/node": "^18.17.1",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^5.2.2"
|
||||
}
|
||||
}
|
||||
277
js/component_model/src/index.ts
Normal file
277
js/component_model/src/index.ts
Normal file
|
|
@ -0,0 +1,277 @@
|
|||
import ts from "typescript";
|
||||
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs";
|
||||
|
||||
type Component = {
|
||||
internalName: string,
|
||||
name: string,
|
||||
props: Property[],
|
||||
members: Record<string, string>,
|
||||
}
|
||||
|
||||
type Property = {
|
||||
name: string
|
||||
optional: boolean
|
||||
type: Type
|
||||
}
|
||||
type Type = TypeString | TypeNumber | TypeBoolean | TypeReactNode | TypeArray | TypeOr | TypeFunction
|
||||
|
||||
type TypeString = {
|
||||
name: "string"
|
||||
}
|
||||
type TypeNumber = {
|
||||
name: "number"
|
||||
}
|
||||
type TypeBoolean = {
|
||||
name: "boolean"
|
||||
}
|
||||
type TypeReactNode = {
|
||||
name: "reactnode"
|
||||
}
|
||||
type TypeArray = {
|
||||
name: "array"
|
||||
nested: Type
|
||||
}
|
||||
type TypeOr = {
|
||||
name: "or"
|
||||
nested: Type[]
|
||||
}
|
||||
type TypeFunction = {
|
||||
name: "function"
|
||||
}
|
||||
|
||||
function generate(componentModelPath: string, outFile: string) {
|
||||
const content = readFileSync(componentModelPath).toString();
|
||||
const model = JSON.parse(content) as Component[]
|
||||
|
||||
const resultFile = ts.createSourceFile("unused", "", ts.ScriptTarget.Latest, false, ts.ScriptKind.TSX);
|
||||
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
|
||||
|
||||
const result = printer.printNode(ts.EmitHint.Unspecified, makeComponents(model), resultFile);
|
||||
|
||||
writeFileSync(outFile, result)
|
||||
}
|
||||
|
||||
function makeComponents(model: Component[]): ts.SourceFile {
|
||||
const imports = [
|
||||
ts.factory.createImportDeclaration(
|
||||
undefined,
|
||||
ts.factory.createImportClause(
|
||||
false,
|
||||
undefined,
|
||||
ts.factory.createNamedImports([
|
||||
ts.factory.createImportSpecifier(
|
||||
false,
|
||||
undefined,
|
||||
ts.factory.createIdentifier("FC")
|
||||
),
|
||||
ts.factory.createImportSpecifier(
|
||||
false,
|
||||
undefined,
|
||||
ts.factory.createIdentifier("ReactNode")
|
||||
)
|
||||
])
|
||||
),
|
||||
ts.factory.createStringLiteral("react"),
|
||||
undefined
|
||||
)
|
||||
];
|
||||
|
||||
// ts.factory.createJSDocComment("@internal"), // TODO
|
||||
|
||||
const declaration =
|
||||
ts.factory.createModuleDeclaration(
|
||||
[ts.factory.createToken(ts.SyntaxKind.DeclareKeyword)],
|
||||
ts.factory.createIdentifier("global"),
|
||||
ts.factory.createModuleBlock([ts.factory.createModuleDeclaration(
|
||||
undefined,
|
||||
ts.factory.createIdentifier("JSX"),
|
||||
ts.factory.createModuleBlock([ts.factory.createInterfaceDeclaration(
|
||||
undefined,
|
||||
ts.factory.createIdentifier("IntrinsicElements"),
|
||||
undefined,
|
||||
undefined,
|
||||
model.map(component => {
|
||||
return ts.factory.createPropertySignature(
|
||||
undefined,
|
||||
ts.factory.createIdentifier(`placeholdername__${component.internalName}`),
|
||||
undefined,
|
||||
ts.factory.createTypeLiteralNode(component.props.map(property => {
|
||||
return ts.factory.createPropertySignature(
|
||||
undefined,
|
||||
ts.factory.createIdentifier(property.name),
|
||||
!property.optional ? undefined : ts.factory.createToken(ts.SyntaxKind.QuestionToken),
|
||||
makeType(property.type)
|
||||
)
|
||||
}))
|
||||
)
|
||||
})
|
||||
)]),
|
||||
ts.NodeFlags.Namespace | ts.NodeFlags.ExportContext | ts.NodeFlags.ContextFlags
|
||||
)]),
|
||||
ts.NodeFlags.ExportContext | ts.NodeFlags.GlobalAugmentation | ts.NodeFlags.ContextFlags
|
||||
);
|
||||
|
||||
// abuse the fact that there is no space between multiline comment and content
|
||||
// is three a better way to add @internal tag to 'declare global'?
|
||||
ts.addSyntheticLeadingComment(declaration, ts.SyntaxKind.MultiLineCommentTrivia, "*@internal", true)
|
||||
|
||||
const components = model.flatMap(component => {
|
||||
|
||||
const componentFCType = ts.factory.createTypeReferenceNode(
|
||||
ts.factory.createIdentifier("FC"),
|
||||
[ts.factory.createTypeReferenceNode(
|
||||
ts.factory.createIdentifier(`${component.name}Props`),
|
||||
undefined
|
||||
)]
|
||||
)
|
||||
|
||||
const componentType = Object.entries(component.members).length == 0
|
||||
? componentFCType
|
||||
: ts.factory.createIntersectionTypeNode([
|
||||
componentFCType,
|
||||
ts.factory.createTypeLiteralNode(
|
||||
Object.entries(component.members).map(([key, value]) => {
|
||||
return ts.factory.createPropertySignature(
|
||||
undefined,
|
||||
ts.factory.createIdentifier(key),
|
||||
undefined,
|
||||
ts.factory.createTypeQueryNode(
|
||||
ts.factory.createIdentifier(value),
|
||||
undefined
|
||||
)
|
||||
);
|
||||
})
|
||||
)
|
||||
])
|
||||
|
||||
return [
|
||||
ts.factory.createInterfaceDeclaration(
|
||||
[ts.factory.createToken(ts.SyntaxKind.ExportKeyword)],
|
||||
ts.factory.createIdentifier(`${component.name}Props`),
|
||||
undefined,
|
||||
undefined,
|
||||
component.props.map(property => {
|
||||
return ts.factory.createPropertySignature(
|
||||
undefined,
|
||||
ts.factory.createIdentifier(property.name),
|
||||
!property.optional ? undefined : ts.factory.createToken(ts.SyntaxKind.QuestionToken),
|
||||
makeType(property.type)
|
||||
);
|
||||
})
|
||||
),
|
||||
ts.factory.createVariableStatement(
|
||||
[ts.factory.createToken(ts.SyntaxKind.ExportKeyword)],
|
||||
ts.factory.createVariableDeclarationList(
|
||||
[ts.factory.createVariableDeclaration(
|
||||
ts.factory.createIdentifier(component.name),
|
||||
undefined,
|
||||
componentType,
|
||||
ts.factory.createArrowFunction(
|
||||
undefined,
|
||||
undefined,
|
||||
[ts.factory.createParameterDeclaration(
|
||||
undefined,
|
||||
undefined,
|
||||
ts.factory.createIdentifier("props"),
|
||||
undefined,
|
||||
ts.factory.createTypeReferenceNode(
|
||||
ts.factory.createIdentifier(`${component.name}Props`),
|
||||
undefined
|
||||
),
|
||||
undefined
|
||||
)],
|
||||
ts.factory.createTypeReferenceNode(
|
||||
ts.factory.createIdentifier("ReactNode"),
|
||||
undefined
|
||||
),
|
||||
ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken),
|
||||
ts.factory.createBlock(
|
||||
[
|
||||
ts.factory.createReturnStatement(
|
||||
ts.factory.createJsxSelfClosingElement(
|
||||
ts.factory.createIdentifier(`placeholdername__${component.internalName}`),
|
||||
undefined,
|
||||
ts.factory.createJsxAttributes(component.props.map((prop) => (
|
||||
ts.factory.createJsxAttribute(
|
||||
ts.factory.createIdentifier(prop.name),
|
||||
ts.factory.createJsxExpression(
|
||||
undefined,
|
||||
ts.factory.createPropertyAccessExpression(
|
||||
ts.factory.createIdentifier("props"),
|
||||
ts.factory.createIdentifier(prop.name)
|
||||
)
|
||||
)
|
||||
)
|
||||
)))
|
||||
)
|
||||
)
|
||||
],
|
||||
true
|
||||
)
|
||||
)
|
||||
)],
|
||||
ts.NodeFlags.Const
|
||||
)
|
||||
),
|
||||
...Object.entries(component.members).map(([key, value]) => {
|
||||
return ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(
|
||||
ts.factory.createPropertyAccessExpression(
|
||||
ts.factory.createIdentifier(component.name),
|
||||
ts.factory.createIdentifier(key)
|
||||
),
|
||||
ts.factory.createToken(ts.SyntaxKind.EqualsToken),
|
||||
ts.factory.createIdentifier(value)
|
||||
))
|
||||
})
|
||||
]
|
||||
});
|
||||
|
||||
return ts.factory.createSourceFile(
|
||||
[...imports, declaration, ...components],
|
||||
ts.factory.createToken(ts.SyntaxKind.EndOfFileToken),
|
||||
ts.NodeFlags.None
|
||||
)
|
||||
}
|
||||
|
||||
function makeType(type: Type): ts.TypeNode {
|
||||
switch (type.name) {
|
||||
case "reactnode": {
|
||||
return ts.factory.createTypeReferenceNode("ReactNode")
|
||||
}
|
||||
case "array": {
|
||||
return ts.factory.createArrayTypeNode(makeType(type.nested))
|
||||
}
|
||||
case "or": {
|
||||
return ts.factory.createUnionTypeNode(
|
||||
type.nested.map(value => makeType(value))
|
||||
)
|
||||
}
|
||||
case "boolean": {
|
||||
return ts.factory.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword)
|
||||
}
|
||||
case "number": {
|
||||
return ts.factory.createKeywordTypeNode(ts.SyntaxKind.NumberKeyword)
|
||||
}
|
||||
case "string": {
|
||||
return ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword)
|
||||
}
|
||||
case "function": {
|
||||
return ts.factory.createFunctionTypeNode(
|
||||
undefined,
|
||||
[],
|
||||
ts.factory.createKeywordTypeNode(ts.SyntaxKind.VoidKeyword)
|
||||
)
|
||||
}
|
||||
default: {
|
||||
throw new Error(`unsupported type ${JSON.stringify(type)}`)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const genDir = "./gen";
|
||||
if (!existsSync(genDir)) {
|
||||
mkdirSync(genDir);
|
||||
}
|
||||
|
||||
generate("./component_model.json", `${genDir}/components.tsx`)
|
||||
16
js/component_model/tsconfig.gentypes.json
Normal file
16
js/component_model/tsconfig.gentypes.json
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"module": "ES2022",
|
||||
"esModuleInterop": true,
|
||||
"target": "ES2022",
|
||||
"moduleResolution": "bundler",
|
||||
"jsx": "react-jsx",
|
||||
"stripInternal": true,
|
||||
"declaration": true,
|
||||
"outDir": "./gendist",
|
||||
"declarationDir": "./gentypes"
|
||||
},
|
||||
"lib": ["ES2020"],
|
||||
"include": ["./gen"]
|
||||
}
|
||||
12
js/component_model/tsconfig.json
Normal file
12
js/component_model/tsconfig.json
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"module": "ES2022",
|
||||
"esModuleInterop": true,
|
||||
"target": "ES2022",
|
||||
"moduleResolution": "bundler",
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"lib": ["ES2020"],
|
||||
"include": ["./src"]
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@
|
|||
"placeholdername-binary": "*"
|
||||
},
|
||||
"dependencies": {
|
||||
"placeholdername-component-model": "*",
|
||||
"@types/lodash": "^4.14.196",
|
||||
"lodash": "^4.17.21"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
import lowerCase from "lodash/lowerCase";
|
||||
import { Box } from "placeholdername-component-model";
|
||||
import { ReactElement } from "react";
|
||||
|
||||
export default function View(): JSX.Element {
|
||||
export default function View(): ReactElement {
|
||||
return (
|
||||
<box>
|
||||
<Box test={1}>
|
||||
test {lowerCase("events")}
|
||||
</box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,34 +1,31 @@
|
|||
import {useState} from 'react';
|
||||
import { ReactElement, useState } from 'react';
|
||||
import upperCase from "lodash/upperCase";
|
||||
import { Box } from "placeholdername-component-model";
|
||||
|
||||
declare global {
|
||||
namespace JSX {
|
||||
interface IntrinsicElements {
|
||||
box: {}
|
||||
button1: { onClick?: () => void, children: string }
|
||||
// TODO remove default html IntrinsicElements
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default function View(): JSX.Element {
|
||||
export default function View(): ReactElement {
|
||||
|
||||
const [count, setCount] = useState(0);
|
||||
|
||||
return (
|
||||
<box>
|
||||
<Box test={1}>
|
||||
test
|
||||
<box>
|
||||
<Box.Text>
|
||||
{count}
|
||||
</box>
|
||||
<box>You clicked {count} times</box>
|
||||
<button1 onClick={() => {
|
||||
</Box.Text>
|
||||
<Box.Text>
|
||||
You clicked
|
||||
{count}
|
||||
times
|
||||
</Box.Text>
|
||||
<Box.Button onClick={() => {
|
||||
console.log("test " + upperCase("events") + count)
|
||||
setCount(count + 1);
|
||||
}}>
|
||||
Click me
|
||||
</button1>
|
||||
</box>
|
||||
</Box.Button>
|
||||
<Box.Text>Test</Box.Text>
|
||||
<Box.Text>Test</Box.Text>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
161
js/package-lock.json
generated
161
js/package-lock.json
generated
|
|
@ -11,7 +11,8 @@
|
|||
"dev_plugin",
|
||||
"react",
|
||||
"react_renderer",
|
||||
"typings"
|
||||
"typings",
|
||||
"component_model"
|
||||
]
|
||||
},
|
||||
"binary": {
|
||||
|
|
@ -31,6 +32,15 @@
|
|||
"placeholdername": "bin/main.js"
|
||||
}
|
||||
},
|
||||
"component_model": {
|
||||
"name": "placeholdername-component-model",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"@types/node": "^18.17.1",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^5.2.2"
|
||||
}
|
||||
},
|
||||
"core": {
|
||||
"name": "placeholdername-core",
|
||||
"version": "1.0.0",
|
||||
|
|
@ -57,10 +67,38 @@
|
|||
"placeholdername-binary": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@cspotcode/source-map-support": {
|
||||
"version": "0.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
|
||||
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
|
||||
"dependencies": {
|
||||
"@jridgewell/trace-mapping": "0.3.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/resolve-uri": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz",
|
||||
"integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/sourcemap-codec": {
|
||||
"version": "1.4.15",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@jridgewell/trace-mapping": {
|
||||
"version": "0.3.9",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
|
||||
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
|
||||
"dependencies": {
|
||||
"@jridgewell/resolve-uri": "^3.0.3",
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/plugin-commonjs": {
|
||||
"version": "25.0.7",
|
||||
"license": "MIT",
|
||||
|
|
@ -193,6 +231,26 @@
|
|||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@tsconfig/node10": {
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
|
||||
"integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA=="
|
||||
},
|
||||
"node_modules/@tsconfig/node12": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
|
||||
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag=="
|
||||
},
|
||||
"node_modules/@tsconfig/node14": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
|
||||
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow=="
|
||||
},
|
||||
"node_modules/@tsconfig/node16": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
|
||||
"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA=="
|
||||
},
|
||||
"node_modules/@types/estree": {
|
||||
"version": "1.0.5",
|
||||
"license": "MIT"
|
||||
|
|
@ -240,6 +298,30 @@
|
|||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.11.2",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz",
|
||||
"integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==",
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/acorn-walk": {
|
||||
"version": "8.3.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.0.tgz",
|
||||
"integrity": "sha512-FS7hV565M5l1R08MXqo8odwMTB02C2UqzB17RVgu9EyuYFBqJZ3/ZY97sQD5FewVu1UyDFc1yztUDrAwT0EypA==",
|
||||
"engines": {
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/arg": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
|
||||
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA=="
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"license": "MIT"
|
||||
|
|
@ -265,6 +347,11 @@
|
|||
"version": "1.0.1",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/create-require": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
|
||||
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ=="
|
||||
},
|
||||
"node_modules/csstype": {
|
||||
"version": "3.1.2",
|
||||
"dev": true,
|
||||
|
|
@ -277,6 +364,14 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/diff": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
|
||||
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
|
||||
"engines": {
|
||||
"node": ">=0.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/estree-walker": {
|
||||
"version": "2.0.2",
|
||||
"license": "MIT"
|
||||
|
|
@ -393,6 +488,11 @@
|
|||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/make-error": {
|
||||
"version": "1.3.6",
|
||||
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
|
||||
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw=="
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "5.1.6",
|
||||
"license": "ISC",
|
||||
|
|
@ -428,6 +528,10 @@
|
|||
"resolved": "binary",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/placeholdername-component-model": {
|
||||
"resolved": "component_model",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/placeholdername-core": {
|
||||
"resolved": "core",
|
||||
"link": true
|
||||
|
|
@ -510,6 +614,48 @@
|
|||
"version": "3.0.0",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ts-node": {
|
||||
"version": "10.9.1",
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
|
||||
"integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==",
|
||||
"dependencies": {
|
||||
"@cspotcode/source-map-support": "^0.8.0",
|
||||
"@tsconfig/node10": "^1.0.7",
|
||||
"@tsconfig/node12": "^1.0.7",
|
||||
"@tsconfig/node14": "^1.0.0",
|
||||
"@tsconfig/node16": "^1.0.2",
|
||||
"acorn": "^8.4.1",
|
||||
"acorn-walk": "^8.1.1",
|
||||
"arg": "^4.1.0",
|
||||
"create-require": "^1.1.0",
|
||||
"diff": "^4.0.1",
|
||||
"make-error": "^1.1.1",
|
||||
"v8-compile-cache-lib": "^3.0.1",
|
||||
"yn": "3.1.1"
|
||||
},
|
||||
"bin": {
|
||||
"ts-node": "dist/bin.js",
|
||||
"ts-node-cwd": "dist/bin-cwd.js",
|
||||
"ts-node-esm": "dist/bin-esm.js",
|
||||
"ts-node-script": "dist/bin-script.js",
|
||||
"ts-node-transpile-only": "dist/bin-transpile.js",
|
||||
"ts-script": "dist/bin-script-deprecated.js"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@swc/core": ">=1.2.50",
|
||||
"@swc/wasm": ">=1.2.50",
|
||||
"@types/node": "*",
|
||||
"typescript": ">=2.7"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@swc/core": {
|
||||
"optional": true
|
||||
},
|
||||
"@swc/wasm": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/tslib": {
|
||||
"version": "2.6.2",
|
||||
"license": "0BSD"
|
||||
|
|
@ -529,10 +675,23 @@
|
|||
"version": "5.26.5",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/v8-compile-cache-lib": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
|
||||
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg=="
|
||||
},
|
||||
"node_modules/wrappy": {
|
||||
"version": "1.0.2",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/yn": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"react": {
|
||||
"name": "placeholdername-react",
|
||||
"version": "1.0.0",
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
"dev_plugin",
|
||||
"react",
|
||||
"react_renderer",
|
||||
"typings"
|
||||
"typings",
|
||||
"component_model"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,11 +12,13 @@ pub struct BuiltInWidgetWrapper {
|
|||
inner: Arc<RwLock<BuiltInWidget>>,
|
||||
}
|
||||
|
||||
// TODO component model
|
||||
impl BuiltInWidgetWrapper {
|
||||
pub fn widget(id: NativeUiWidgetId, widget_type: &str, _properties: HashMap<String, NativeUiPropertyValue>) -> Self {
|
||||
let widget = match widget_type.as_ref() {
|
||||
"box" => BuiltInWidget::Container { children: vec![] },
|
||||
"button1" => BuiltInWidget::Button(widget_type.to_owned()),
|
||||
"placeholdername__box" => BuiltInWidget::Container { children: vec![] },
|
||||
"placeholdername__button" => BuiltInWidget::Button(widget_type.to_owned()),
|
||||
"placeholdername__text_inner" => BuiltInWidget::Button(widget_type.to_owned()),
|
||||
_ => panic!("widget_type {} not supported", widget_type)
|
||||
};
|
||||
|
||||
|
|
|
|||
9
rust/component_model/Cargo.toml
Normal file
9
rust/component_model/Cargo.toml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
[package]
|
||||
name = "component_model"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = { version = "1.0" }
|
||||
anyhow = "1.0.75"
|
||||
100
rust/component_model/src/lib.rs
Normal file
100
rust/component_model/src/lib.rs
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use serde::Serialize;
|
||||
|
||||
type ComponentName = String;
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct Component {
|
||||
#[serde(rename = "internalName")]
|
||||
internal_name: String,
|
||||
name: ComponentName,
|
||||
props: Vec<Property>,
|
||||
members: HashMap<String, ComponentName>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct Property {
|
||||
name: String,
|
||||
optional: bool,
|
||||
#[serde(rename = "type")]
|
||||
property_type: Type
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(tag = "name")]
|
||||
pub enum Type {
|
||||
#[serde(rename = "string")]
|
||||
String,
|
||||
#[serde(rename = "number")]
|
||||
Number,
|
||||
#[serde(rename = "boolean")]
|
||||
Boolean,
|
||||
#[serde(rename = "reactnode")]
|
||||
ReactNode,
|
||||
#[serde(rename = "array")]
|
||||
Array {
|
||||
nested: Box<Type>
|
||||
},
|
||||
#[serde(rename = "or")]
|
||||
Or {
|
||||
nested: Vec<Type>
|
||||
},
|
||||
#[serde(rename = "function")]
|
||||
Function,
|
||||
}
|
||||
|
||||
fn component(internal_name: impl Into<String>, name: impl Into<String>, properties: Vec<Property>, children: &[(&str, &str)]) -> Component {
|
||||
Component {
|
||||
internal_name: internal_name.into(),
|
||||
name: name.into(),
|
||||
props: properties,
|
||||
members: children.into_iter()
|
||||
.map(|&(key, value)| (key.to_owned(), value.to_owned()))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
fn property(name: impl Into<String>, optional: bool, property_type: Type) -> Property {
|
||||
Property {
|
||||
name: name.into(),
|
||||
optional,
|
||||
property_type,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_component_model() -> Vec<Component> {
|
||||
let text_inner_component = component(
|
||||
"text_inner",
|
||||
"TextInner",
|
||||
vec![
|
||||
property("children", false, Type::Or { nested: vec![Type::ReactNode]})
|
||||
],
|
||||
&[]
|
||||
);
|
||||
|
||||
let button_component = component(
|
||||
"button",
|
||||
"Button",
|
||||
vec![
|
||||
property("onClick", true, Type::Function),
|
||||
property("children", true, Type::String)
|
||||
],
|
||||
&[]
|
||||
);
|
||||
|
||||
let box_component = component(
|
||||
"box",
|
||||
"Box",
|
||||
vec![
|
||||
property("test", false, Type::Number),
|
||||
property("children", true, Type::ReactNode)
|
||||
],
|
||||
&[
|
||||
("Text", &text_inner_component.name),
|
||||
("Button", &button_component.name)
|
||||
]
|
||||
);
|
||||
|
||||
vec![text_inner_component, button_component, box_component]
|
||||
}
|
||||
24
rust/component_model/src/main.rs
Normal file
24
rust/component_model/src/main.rs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
use std::{env, fs};
|
||||
use anyhow::anyhow;
|
||||
use component_model::create_component_model;
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
|
||||
match args.len() {
|
||||
2 => {
|
||||
let path_to_save = &args[1];
|
||||
|
||||
let components = create_component_model();
|
||||
|
||||
let json = serde_json::to_string_pretty(&components)?;
|
||||
|
||||
fs::write(path_to_save, json)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
args @ _ => {
|
||||
Err(anyhow!("Unsupported args number: {args}"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -423,6 +423,7 @@ fn op_react_create_instance<'a>(
|
|||
widget_type: String,
|
||||
v8_properties: HashMap<String, serde_v8::Value<'a>>,
|
||||
) -> anyhow::Result<impl Future<Output=anyhow::Result<JsUiWidget>> + 'static> {
|
||||
// TODO component model
|
||||
println!("op_react_create_instance");
|
||||
|
||||
let properties = convert_properties(scope, v8_properties);
|
||||
|
|
@ -590,6 +591,8 @@ fn op_react_clone_instance<'a>(
|
|||
v8_properties: HashMap<String, serde_v8::Value<'a>>,
|
||||
) -> anyhow::Result<impl Future<Output=anyhow::Result<JsUiWidget>> + 'static> {
|
||||
|
||||
// TODO component model
|
||||
|
||||
let properties = convert_properties(scope, v8_properties);
|
||||
|
||||
let conversion_properties = properties.clone();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue