imxc 0.1.0 → 0.2.0

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/index.js CHANGED
@@ -2,16 +2,25 @@
2
2
  import { parseArgs } from 'node:util';
3
3
  import * as fs from 'node:fs';
4
4
  import * as path from 'node:path';
5
- import { parseIgxFile, extractImports } from './parser.js';
5
+ import { parseFile, extractImports } from './parser.js';
6
6
  import { validate } from './validator.js';
7
7
  import { lowerComponent } from './lowering.js';
8
8
  import { emitComponent, emitComponentHeader, emitRoot } from './emitter.js';
9
+ import { initProject } from './init.js';
10
+ // Handle `imxc init [dir]` subcommand
11
+ if (process.argv[2] === 'init') {
12
+ const dir = process.argv[3] ?? '.';
13
+ const absDir = path.resolve(dir);
14
+ initProject(absDir, path.basename(absDir));
15
+ process.exit(0);
16
+ }
9
17
  const { values, positionals } = parseArgs({
10
18
  allowPositionals: true,
11
19
  options: { output: { type: 'string', short: 'o' } },
12
20
  });
13
21
  if (positionals.length === 0) {
14
22
  console.error('Usage: imxc <input.tsx ...> -o <output-dir>');
23
+ console.error(' imxc init [project-dir]');
15
24
  process.exit(1);
16
25
  }
17
26
  const outputDir = values.output ?? '.';
@@ -26,7 +35,7 @@ for (const file of positionals) {
26
35
  continue;
27
36
  }
28
37
  const source = fs.readFileSync(file, 'utf-8');
29
- const parsed = parseIgxFile(file, source);
38
+ const parsed = parseFile(file, source);
30
39
  if (parsed.errors.length > 0) {
31
40
  parsed.errors.forEach(e => console.error(`${e.file}:${e.line}:${e.col} - error: ${e.message}`));
32
41
  hasErrors = true;
package/dist/init.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare function initProject(projectDir: string, projectName?: string): void;
package/dist/init.js ADDED
@@ -0,0 +1,290 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ const MAIN_CPP = `#include <reimgui/runtime.h>
4
+ #include <reimgui/renderer.h>
5
+
6
+ #include <imgui.h>
7
+ #include <imgui_impl_glfw.h>
8
+ #include <imgui_impl_opengl3.h>
9
+ #include <GLFW/glfw3.h>
10
+
11
+ struct App {
12
+ GLFWwindow* window = nullptr;
13
+ ImGuiIO* io = nullptr;
14
+ reimgui::Runtime runtime;
15
+ };
16
+
17
+ static void render_frame(App& app) {
18
+ glfwMakeContextCurrent(app.window);
19
+ if (glfwGetWindowAttrib(app.window, GLFW_ICONIFIED) != 0) return;
20
+
21
+ int fb_w = 0, fb_h = 0;
22
+ glfwGetFramebufferSize(app.window, &fb_w, &fb_h);
23
+ if (fb_w <= 0 || fb_h <= 0) return;
24
+
25
+ ImGui_ImplOpenGL3_NewFrame();
26
+ ImGui_ImplGlfw_NewFrame();
27
+ ImGui::NewFrame();
28
+
29
+ reimgui::render_root(app.runtime);
30
+
31
+ ImGui::Render();
32
+ glViewport(0, 0, fb_w, fb_h);
33
+ glClearColor(0.12F, 0.12F, 0.15F, 1.0F);
34
+ glClear(GL_COLOR_BUFFER_BIT);
35
+ ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
36
+
37
+ if ((app.io->ConfigFlags & ImGuiConfigFlags_ViewportsEnable) != 0) {
38
+ ImGui::UpdatePlatformWindows();
39
+ ImGui::RenderPlatformWindowsDefault();
40
+ }
41
+
42
+ glfwMakeContextCurrent(app.window);
43
+ glfwSwapBuffers(app.window);
44
+ }
45
+
46
+ static void window_size_callback(GLFWwindow* window, int, int) {
47
+ auto* app = static_cast<App*>(glfwGetWindowUserPointer(window));
48
+ if (app) render_frame(*app);
49
+ }
50
+
51
+ int main() {
52
+ if (glfwInit() == 0) return 1;
53
+
54
+ const char* glsl_version = "#version 150";
55
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
56
+ glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
57
+ glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
58
+ #ifdef __APPLE__
59
+ glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
60
+ #endif
61
+
62
+ GLFWwindow* window = glfwCreateWindow(800, 600, "APP_NAME", nullptr, nullptr);
63
+ if (!window) { glfwTerminate(); return 1; }
64
+ glfwMakeContextCurrent(window);
65
+ glfwSwapInterval(1);
66
+
67
+ IMGUI_CHECKVERSION();
68
+ ImGui::CreateContext();
69
+ ImGuiIO& io = ImGui::GetIO();
70
+ io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
71
+ io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;
72
+
73
+ ImGui::StyleColorsDark();
74
+ ImGuiStyle& style = ImGui::GetStyle();
75
+ if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {
76
+ style.WindowRounding = 0.0F;
77
+ style.Colors[ImGuiCol_WindowBg].w = 1.0F;
78
+ }
79
+
80
+ ImGui_ImplGlfw_InitForOpenGL(window, true);
81
+ ImGui_ImplOpenGL3_Init(glsl_version);
82
+
83
+ App app;
84
+ app.window = window;
85
+ app.io = &io;
86
+ glfwSetWindowUserPointer(window, &app);
87
+ glfwSetWindowSizeCallback(window, window_size_callback);
88
+
89
+ while (glfwWindowShouldClose(window) == 0) {
90
+ glfwPollEvents();
91
+ render_frame(app);
92
+ }
93
+
94
+ ImGui_ImplOpenGL3_Shutdown();
95
+ ImGui_ImplGlfw_Shutdown();
96
+ ImGui::DestroyContext();
97
+ glfwDestroyWindow(window);
98
+ glfwTerminate();
99
+ return 0;
100
+ }
101
+ `;
102
+ const APP_TSX = `export default function App() {
103
+ const [count, setCount] = useState(0);
104
+
105
+ return (
106
+ <DockSpace>
107
+ <MenuBar>
108
+ <Menu label="File">
109
+ <MenuItem label="Exit" />
110
+ </Menu>
111
+ </MenuBar>
112
+ <Window title="Hello">
113
+ <Column gap={8}>
114
+ <Text>Hello, World!</Text>
115
+ <Row gap={8}>
116
+ <Button title="Increment" onPress={() => setCount(count + 1)} />
117
+ <Text>Count: {count}</Text>
118
+ </Row>
119
+ </Column>
120
+ </Window>
121
+ </DockSpace>
122
+ );
123
+ }
124
+ `;
125
+ const REIMGUI_DTS = `// reimgui.d.ts — Type definitions for ReImGui components
126
+
127
+ interface Style {
128
+ padding?: number;
129
+ paddingHorizontal?: number;
130
+ paddingVertical?: number;
131
+ gap?: number;
132
+ width?: number;
133
+ height?: number;
134
+ minWidth?: number;
135
+ minHeight?: number;
136
+ backgroundColor?: [number, number, number, number];
137
+ textColor?: [number, number, number, number];
138
+ fontSize?: number;
139
+ }
140
+
141
+ declare function useState<T>(initial: T): [T, (value: T) => void];
142
+
143
+ interface WindowProps { title: string; style?: Style; children?: any; }
144
+ interface ViewProps { style?: Style; children?: any; }
145
+ interface RowProps { gap?: number; style?: Style; children?: any; }
146
+ interface ColumnProps { gap?: number; style?: Style; children?: any; }
147
+ interface TextProps { style?: Style; children?: any; }
148
+ interface ButtonProps { title: string; onPress: () => void; disabled?: boolean; style?: Style; }
149
+ interface TextInputProps { value: string; onChange: (v: string) => void; label?: string; placeholder?: string; style?: Style; }
150
+ interface CheckboxProps { value: boolean; onChange: (v: boolean) => void; label?: string; style?: Style; }
151
+ interface SeparatorProps {}
152
+ interface PopupProps { id: string; style?: Style; children?: any; }
153
+ interface DockSpaceProps { style?: Style; children?: any; }
154
+ interface MenuBarProps { children?: any; }
155
+ interface MenuProps { label: string; children?: any; }
156
+ interface MenuItemProps { label: string; onPress?: () => void; shortcut?: string; }
157
+ interface TableProps { columns: string[]; style?: Style; children?: any; }
158
+ interface TableRowProps { children?: any; }
159
+ interface TabBarProps { style?: Style; children?: any; }
160
+ interface TabItemProps { label: string; children?: any; }
161
+ interface TreeNodeProps { label: string; children?: any; }
162
+ interface CollapsingHeaderProps { label: string; children?: any; }
163
+ interface SliderFloatProps { label: string; value: number; onChange: (v: number) => void; min: number; max: number; style?: Style; }
164
+ interface SliderIntProps { label: string; value: number; onChange: (v: number) => void; min: number; max: number; style?: Style; }
165
+ interface DragFloatProps { label: string; value: number; onChange: (v: number) => void; speed?: number; style?: Style; }
166
+ interface DragIntProps { label: string; value: number; onChange: (v: number) => void; speed?: number; style?: Style; }
167
+ interface ComboProps { label: string; value: number; onChange: (v: number) => void; items: string[]; style?: Style; }
168
+ interface InputIntProps { label: string; value: number; onChange: (v: number) => void; style?: Style; }
169
+ interface InputFloatProps { label: string; value: number; onChange: (v: number) => void; style?: Style; }
170
+ interface ColorEditProps { label: string; value: number[]; onChange: (v: number[]) => void; style?: Style; }
171
+ interface ListBoxProps { label: string; value: number; onChange: (v: number) => void; items: string[]; style?: Style; }
172
+ interface ProgressBarProps { value: number; overlay?: string; style?: Style; }
173
+ interface TooltipProps { text: string; }
174
+
175
+ declare function Window(props: WindowProps): any;
176
+ declare function View(props: ViewProps): any;
177
+ declare function Row(props: RowProps): any;
178
+ declare function Column(props: ColumnProps): any;
179
+ declare function Text(props: TextProps): any;
180
+ declare function Button(props: ButtonProps): any;
181
+ declare function TextInput(props: TextInputProps): any;
182
+ declare function Checkbox(props: CheckboxProps): any;
183
+ declare function Separator(props: SeparatorProps): any;
184
+ declare function Popup(props: PopupProps): any;
185
+ declare function DockSpace(props: DockSpaceProps): any;
186
+ declare function MenuBar(props: MenuBarProps): any;
187
+ declare function Menu(props: MenuProps): any;
188
+ declare function MenuItem(props: MenuItemProps): any;
189
+ declare function Table(props: TableProps): any;
190
+ declare function TableRow(props: TableRowProps): any;
191
+ declare function TabBar(props: TabBarProps): any;
192
+ declare function TabItem(props: TabItemProps): any;
193
+ declare function TreeNode(props: TreeNodeProps): any;
194
+ declare function CollapsingHeader(props: CollapsingHeaderProps): any;
195
+ declare function SliderFloat(props: SliderFloatProps): any;
196
+ declare function SliderInt(props: SliderIntProps): any;
197
+ declare function DragFloat(props: DragFloatProps): any;
198
+ declare function DragInt(props: DragIntProps): any;
199
+ declare function Combo(props: ComboProps): any;
200
+ declare function InputInt(props: InputIntProps): any;
201
+ declare function InputFloat(props: InputFloatProps): any;
202
+ declare function ColorEdit(props: ColorEditProps): any;
203
+ declare function ListBox(props: ListBoxProps): any;
204
+ declare function ProgressBar(props: ProgressBarProps): any;
205
+ declare function Tooltip(props: TooltipProps): any;
206
+
207
+ declare module "reimgui/jsx-runtime" {
208
+ export namespace JSX {
209
+ type Element = any;
210
+ interface IntrinsicElements { [tag: string]: any; }
211
+ interface ElementChildrenAttribute { children: {}; }
212
+ }
213
+ export function jsx(type: any, props: any, key?: any): any;
214
+ export function jsxs(type: any, props: any, key?: any): any;
215
+ export function Fragment(props: any): any;
216
+ }
217
+ `;
218
+ const TSCONFIG = `{
219
+ "compilerOptions": {
220
+ "target": "ES2022",
221
+ "lib": ["ES2022"],
222
+ "module": "ES2022",
223
+ "moduleResolution": "bundler",
224
+ "jsx": "react-jsx",
225
+ "jsxImportSource": "reimgui",
226
+ "strict": false,
227
+ "noEmit": true,
228
+ "skipLibCheck": true
229
+ },
230
+ "include": ["src/**/*.tsx", "src/reimgui.d.ts"]
231
+ }
232
+ `;
233
+ function cmakeTemplate(projectName, repoUrl) {
234
+ return `cmake_minimum_required(VERSION 3.25)
235
+ project(${projectName} LANGUAGES CXX)
236
+
237
+ set(CMAKE_CXX_STANDARD 20)
238
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
239
+
240
+ include(FetchContent)
241
+
242
+ FetchContent_Declare(
243
+ reimgui
244
+ GIT_REPOSITORY ${repoUrl}
245
+ GIT_TAG main
246
+ )
247
+ FetchContent_MakeAvailable(reimgui)
248
+
249
+ include(ReImGuiCompile)
250
+
251
+ reimgui_compile_tsx(GENERATED
252
+ SOURCES src/App.tsx
253
+ OUTPUT_DIR \${CMAKE_BINARY_DIR}/generated
254
+ )
255
+
256
+ add_executable(${projectName}
257
+ src/main.cpp
258
+ \${GENERATED}
259
+ )
260
+ target_link_libraries(${projectName} PRIVATE reimgui::renderer)
261
+ target_include_directories(${projectName} PRIVATE \${CMAKE_BINARY_DIR}/generated)
262
+ `;
263
+ }
264
+ export function initProject(projectDir, projectName) {
265
+ const name = projectName ?? path.basename(projectDir);
266
+ const srcDir = path.join(projectDir, 'src');
267
+ if (fs.existsSync(path.join(srcDir, 'App.tsx'))) {
268
+ console.error(`Error: ${srcDir}/App.tsx already exists. Aborting.`);
269
+ process.exit(1);
270
+ }
271
+ fs.mkdirSync(srcDir, { recursive: true });
272
+ // Write files
273
+ fs.writeFileSync(path.join(srcDir, 'main.cpp'), MAIN_CPP.replace('APP_NAME', name));
274
+ fs.writeFileSync(path.join(srcDir, 'App.tsx'), APP_TSX);
275
+ fs.writeFileSync(path.join(srcDir, 'reimgui.d.ts'), REIMGUI_DTS);
276
+ fs.writeFileSync(path.join(projectDir, 'tsconfig.json'), TSCONFIG);
277
+ fs.writeFileSync(path.join(projectDir, 'CMakeLists.txt'), cmakeTemplate(name, 'https://github.com/bgocumlu/reimgui.git'));
278
+ console.log(`imxc: initialized project "${name}"`);
279
+ console.log('');
280
+ console.log(' Created:');
281
+ console.log(` src/main.cpp — app shell (GLFW + OpenGL + ImGui)`);
282
+ console.log(` src/App.tsx — your root component`);
283
+ console.log(` src/reimgui.d.ts — type definitions for IDE support`);
284
+ console.log(` tsconfig.json — TypeScript config`);
285
+ console.log(` CMakeLists.txt — build config with FetchContent`);
286
+ console.log('');
287
+ console.log(' Next steps:');
288
+ console.log(` cmake -B build`);
289
+ console.log(` cmake --build build`);
290
+ }
package/dist/parser.d.ts CHANGED
@@ -11,5 +11,5 @@ export interface ParseError {
11
11
  col: number;
12
12
  message: string;
13
13
  }
14
- export declare function parseIgxFile(filePath: string, source: string): ParsedFile;
14
+ export declare function parseFile(filePath: string, source: string): ParsedFile;
15
15
  export declare function extractImports(sourceFile: ts.SourceFile): Map<string, string>;
package/dist/parser.js CHANGED
@@ -4,7 +4,7 @@ function formatError(sourceFile, node, message) {
4
4
  const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
5
5
  return { file: sourceFile.fileName, line: line + 1, col: character + 1, message };
6
6
  }
7
- export function parseIgxFile(filePath, source) {
7
+ export function parseFile(filePath, source) {
8
8
  const fileName = path.basename(filePath);
9
9
  const sourceFile = ts.createSourceFile(fileName, source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);
10
10
  const errors = [];
@@ -12,7 +12,7 @@ export function parseIgxFile(filePath, source) {
12
12
  for (const stmt of sourceFile.statements) {
13
13
  if (ts.isFunctionDeclaration(stmt) && stmt.name) {
14
14
  if (component !== null) {
15
- errors.push(formatError(sourceFile, stmt, 'Only one component function per .igx file is supported'));
15
+ errors.push(formatError(sourceFile, stmt, 'Only one component function per file is supported'));
16
16
  }
17
17
  else {
18
18
  component = stmt;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "imxc",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Compiler for ReImGui — compiles React-like .tsx to native Dear ImGui C++",
5
5
  "type": "module",
6
6
  "bin": {