0x-lang 0.1.26 → 0.1.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ast.d.ts +5 -1
- package/dist/generators/react.d.ts +3 -0
- package/dist/generators/react.js +29 -4
- package/dist/generators/react.js.map +1 -1
- package/dist/generators/svelte.js +29 -7
- package/dist/generators/svelte.js.map +1 -1
- package/dist/generators/themes.d.ts +28 -0
- package/dist/generators/themes.js +1058 -0
- package/dist/generators/themes.js.map +1 -0
- package/dist/generators/vue.js +29 -7
- package/dist/generators/vue.js.map +1 -1
- package/dist/parser.js +14 -1
- package/dist/parser.js.map +1 -1
- package/dist/tokenizer.js +2 -0
- package/dist/tokenizer.js.map +1 -1
- package/package.json +1 -1
package/dist/ast.d.ts
CHANGED
|
@@ -200,6 +200,10 @@ export interface TopLevelVarDecl extends BaseNode {
|
|
|
200
200
|
name: string;
|
|
201
201
|
value: Expression;
|
|
202
202
|
}
|
|
203
|
+
export interface ThemeDecl extends BaseNode {
|
|
204
|
+
type: 'ThemeDecl';
|
|
205
|
+
theme: string;
|
|
206
|
+
}
|
|
203
207
|
export interface CommentNode extends BaseNode {
|
|
204
208
|
type: 'Comment';
|
|
205
209
|
text: string;
|
|
@@ -915,7 +919,7 @@ export interface RtlNode extends BaseNode {
|
|
|
915
919
|
props: Record<string, Expression>;
|
|
916
920
|
}
|
|
917
921
|
export type UINode = LayoutNode | TextNode | ButtonNode | InputNode | ImageNode | LinkNode | ToggleNode | SelectNode | IfBlock | ForBlock | ShowBlock | HideBlock | ComponentCall | CommentNode | TableNode | ChartNode | StatNode | NavNode | UploadNode | ModalNode | ToastNode | CrudNode | ListNode | LayoutShellNode | SlideOverNode | DrawerNode | CommandNode | ConfirmNode | PayNode | CartNode | MediaNode | NotificationNode | SearchNode | FilterNode | SocialNode | ProfileNode | HeroNode | FeaturesNode | PricingNode | FaqNode | TestimonialNode | FooterNode | AdminNode | SeoNode | A11yNode | AnimateNode | GestureNode | AiNode | AutomationNode | DevNode | EmitNode | ResponsiveNode | BreadcrumbNode | StatsGridNode | DividerNode | ProgressNode | ErrorNode | LoadingNode | OfflineNode | RetryNode | LogNode | RawBlock;
|
|
918
|
-
export type ASTNode = AppNode | PageNode | ComponentNode | StateDecl | DerivedDecl | PropDecl | TypeDecl | StoreDecl | ApiDecl | FnDecl | OnMount | OnDestroy | WatchBlock | CheckDecl | StyleDecl | JsImport | UseImport | JsBlock | RawBlock | TopLevelVarDecl | ModelNode | DataDecl | FormDecl | AuthDecl | RealtimeDecl | RouteDecl | RoleDecl | AutomationNode | DevNode | DeployNode | EnvNode | DockerNode | CiNode | DomainNode | CdnNode | MonitorNode | BackupNode | EndpointNode | MiddlewareNode | QueueNode | CronNode | CacheNode | MigrateNode | SeedNode | WebhookNode | StorageNode | TestNode | E2eNode | MockNode | FixtureNode | I18nNode | LocaleNode | RtlNode | UINode;
|
|
922
|
+
export type ASTNode = AppNode | PageNode | ComponentNode | StateDecl | DerivedDecl | PropDecl | TypeDecl | StoreDecl | ApiDecl | FnDecl | OnMount | OnDestroy | WatchBlock | CheckDecl | StyleDecl | JsImport | UseImport | JsBlock | RawBlock | TopLevelVarDecl | ModelNode | DataDecl | FormDecl | AuthDecl | RealtimeDecl | RouteDecl | RoleDecl | AutomationNode | DevNode | DeployNode | EnvNode | DockerNode | CiNode | DomainNode | CdnNode | MonitorNode | BackupNode | EndpointNode | MiddlewareNode | QueueNode | CronNode | CacheNode | MigrateNode | SeedNode | WebhookNode | StorageNode | TestNode | E2eNode | MockNode | FixtureNode | I18nNode | LocaleNode | RtlNode | ThemeDecl | UINode;
|
|
919
923
|
export interface CodeGenerator {
|
|
920
924
|
target: string;
|
|
921
925
|
generate(ast: PageNode | ComponentNode | AppNode): GeneratedCode;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ASTNode, StateDecl, StyleDecl, Expression, Statement, GeneratedCode } from '../ast.js';
|
|
2
2
|
import { SourceMapBuilder } from './source-map.js';
|
|
3
|
+
import { type ThemeName } from './themes.js';
|
|
3
4
|
export interface GenContext {
|
|
4
5
|
imports: Set<string>;
|
|
5
6
|
states: Map<string, StateDecl>;
|
|
@@ -9,6 +10,8 @@ export interface GenContext {
|
|
|
9
10
|
indent: number;
|
|
10
11
|
readOnly: boolean;
|
|
11
12
|
smb: SourceMapBuilder | null;
|
|
13
|
+
theme: ThemeName | null;
|
|
14
|
+
themeImports: Set<string>;
|
|
12
15
|
}
|
|
13
16
|
export declare function ctx(): GenContext;
|
|
14
17
|
export declare function generateReact(ast: ASTNode[]): GeneratedCode;
|
package/dist/generators/react.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// 0x → React Code Generator
|
|
2
2
|
import { SIZE_MAP, unquote, capitalize, parseGradient, addPx, getPassthroughProps, typeExprToJs, getFieldDefault, KNOWN_LAYOUT_PROPS, KNOWN_TEXT_PROPS, KNOWN_BUTTON_PROPS, KNOWN_INPUT_PROPS, KNOWN_IMAGE_PROPS, KNOWN_LINK_PROPS, KNOWN_TOGGLE_PROPS, KNOWN_SELECT_PROPS } from './shared.js';
|
|
3
3
|
import { SourceMapBuilder } from './source-map.js';
|
|
4
|
+
import { getReactThemeRenderer } from './themes.js';
|
|
4
5
|
export function ctx() {
|
|
5
6
|
return {
|
|
6
7
|
imports: new Set(),
|
|
@@ -11,6 +12,8 @@ export function ctx() {
|
|
|
11
12
|
indent: 1,
|
|
12
13
|
readOnly: false,
|
|
13
14
|
smb: null,
|
|
15
|
+
theme: null,
|
|
16
|
+
themeImports: new Set(),
|
|
14
17
|
};
|
|
15
18
|
}
|
|
16
19
|
function ind(c) {
|
|
@@ -18,8 +21,14 @@ function ind(c) {
|
|
|
18
21
|
}
|
|
19
22
|
export function generateReact(ast) {
|
|
20
23
|
const parts = [];
|
|
24
|
+
// Detect theme declaration
|
|
25
|
+
const themeNode = ast.find(n => n.type === 'ThemeDecl');
|
|
26
|
+
const theme = themeNode ? themeNode.theme : null;
|
|
21
27
|
for (const node of ast) {
|
|
22
|
-
if (node.type === '
|
|
28
|
+
if (node.type === 'ThemeDecl') {
|
|
29
|
+
// Handled above
|
|
30
|
+
}
|
|
31
|
+
else if (node.type === 'Model') {
|
|
23
32
|
parts.push(genModelCode(node));
|
|
24
33
|
}
|
|
25
34
|
else if (node.type === 'AuthDecl') {
|
|
@@ -114,7 +123,7 @@ export function generateReact(ast) {
|
|
|
114
123
|
parts.push(genRtlCode(node));
|
|
115
124
|
}
|
|
116
125
|
else if (node.type === 'Page' || node.type === 'Component' || node.type === 'App') {
|
|
117
|
-
parts.push(generateTopLevel(node));
|
|
126
|
+
parts.push(generateTopLevel(node, theme));
|
|
118
127
|
}
|
|
119
128
|
}
|
|
120
129
|
const code = parts.join('\n\n');
|
|
@@ -176,8 +185,9 @@ export function generateBackendCode(node) {
|
|
|
176
185
|
default: return null;
|
|
177
186
|
}
|
|
178
187
|
}
|
|
179
|
-
function generateTopLevel(node) {
|
|
188
|
+
function generateTopLevel(node, theme = null) {
|
|
180
189
|
const c = ctx();
|
|
190
|
+
c.theme = theme;
|
|
181
191
|
// First pass: collect states, derived, props, styles
|
|
182
192
|
for (const child of node.body) {
|
|
183
193
|
if (child.type === 'StateDecl')
|
|
@@ -265,6 +275,7 @@ function generateTopLevel(node) {
|
|
|
265
275
|
case 'I18n':
|
|
266
276
|
case 'Locale':
|
|
267
277
|
case 'Rtl':
|
|
278
|
+
case 'ThemeDecl':
|
|
268
279
|
// Handled at top level
|
|
269
280
|
break;
|
|
270
281
|
case 'JsImport':
|
|
@@ -333,14 +344,17 @@ function generateTopLevel(node) {
|
|
|
333
344
|
userImports.push(`import ${ui.name} from '${ui.source}';`);
|
|
334
345
|
}
|
|
335
346
|
}
|
|
347
|
+
// Collect theme imports (deduplicated)
|
|
348
|
+
const themeImportLines = Array.from(c.themeImports).sort();
|
|
336
349
|
const exportKw = isComponent ? '' : 'export default ';
|
|
337
350
|
// Add "use client" for Next.js SSR when client-side hooks are used
|
|
338
351
|
const needsClient = c.imports.has('useState') || c.imports.has('useEffect') || c.imports.has('useRef') || c.imports.has('useCallback');
|
|
339
352
|
const lines = [
|
|
340
353
|
...(needsClient ? [`'use client';`, ''] : []),
|
|
341
|
-
`// Generated by 0x`,
|
|
354
|
+
`// Generated by 0x${c.theme ? ` (theme: ${c.theme})` : ''}`,
|
|
342
355
|
importLine,
|
|
343
356
|
...userImports,
|
|
357
|
+
...themeImportLines,
|
|
344
358
|
'',
|
|
345
359
|
`${exportKw}function ${node.name}(${propsArg}) {`,
|
|
346
360
|
...hookLines.map(l => ` ${l}`),
|
|
@@ -451,6 +465,17 @@ function srcComment(node) {
|
|
|
451
465
|
return node.loc?.line ? `{/* 0x:L${node.loc.line} */}` : '';
|
|
452
466
|
}
|
|
453
467
|
function genUINode(node, c) {
|
|
468
|
+
// Check for themed renderer
|
|
469
|
+
if (c.theme) {
|
|
470
|
+
const renderer = getReactThemeRenderer(c.theme, node.type);
|
|
471
|
+
if (renderer) {
|
|
472
|
+
const helpers = { genExpr: (e, ctx) => genExpr(e, ctx || c), genTextContent: (e, ctx) => genTextContent(e, ctx || c), genActionExpr: (a, ctx) => genActionExpr(a, ctx || c), genUINode: (n, ctx) => genUINode(n, ctx || c), srcComment: (n) => srcComment(n) };
|
|
473
|
+
const result = renderer(node, c, helpers);
|
|
474
|
+
for (const imp of result.imports)
|
|
475
|
+
c.themeImports.add(imp);
|
|
476
|
+
return result.jsx;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
454
479
|
switch (node.type) {
|
|
455
480
|
case 'Layout': return genLayout(node, c);
|
|
456
481
|
case 'Text': return genText(node, c);
|