@makano/rew 1.1.5 → 1.1.6

Sign up to get free protection for your applications and to get access to all the features.
package/bin/ui CHANGED
Binary file
package/build.sh ADDED
@@ -0,0 +1,6 @@
1
+ #!/bin/sh
2
+ opath=./bin/ui
3
+ if [ $1 ]; then
4
+ opath=$1
5
+ fi
6
+ g++ ./cpp/ui.cpp -o $opath `pkg-config --cflags --libs gtk+-3.0 webkit2gtk-4.0 libwebsockets jsoncpp`
package/cpp/ui.cpp ADDED
@@ -0,0 +1,217 @@
1
+ #include <gtk/gtk.h>
2
+ #include <webkit2/webkit2.h>
3
+ #include <json/json.h>
4
+ #include <iostream>
5
+ #include <sstream>
6
+ #include <string>
7
+ #include <fstream>
8
+ #include <thread>
9
+ #include <sys/inotify.h>
10
+ #include <unistd.h>
11
+ #include <vector>
12
+
13
+ struct AppData
14
+ {
15
+ GtkWidget *window;
16
+ WebKitWebView *web_view;
17
+ WebKitUserContentManager *content_manager;
18
+ };
19
+
20
+ static void handle_json_message(const Json::Value &json, AppData *user_data)
21
+ {
22
+ if (json.isMember("action") && json.isMember("data"))
23
+ {
24
+ std::string action = json["action"].asString();
25
+ std::string data = json["data"].asString();
26
+
27
+ // std::cout << action << std::endl;
28
+
29
+ if (action == "setTitle")
30
+ {
31
+ gtk_window_set_title(GTK_WINDOW(user_data->window), data.c_str());
32
+ }
33
+ else if (action == "log")
34
+ {
35
+ g_print("%s\n", data.c_str());
36
+ }
37
+ else if (action == "HTML")
38
+ {
39
+ webkit_web_view_load_html(user_data->web_view, data.c_str(), NULL);
40
+ g_print("SETUP::HTML");
41
+ }
42
+ else if (action == "JS")
43
+ {
44
+ webkit_web_view_run_javascript(user_data->web_view, data.c_str(), NULL, NULL, NULL);
45
+ }
46
+ else if (action == "JS2")
47
+ {
48
+ const char *js_code = g_strdup_printf("%s;", data.c_str());
49
+ WebKitUserScript *user_script = webkit_user_script_new(js_code,
50
+ WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES,
51
+ WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_START,
52
+ NULL, NULL);
53
+ webkit_user_content_manager_add_script(user_data->content_manager, user_script);
54
+ }
55
+ }
56
+ else
57
+ {
58
+ g_print("Invalid JSON format: 'action' and 'data' fields are required\n");
59
+ }
60
+ }
61
+
62
+ static void js_callback(WebKitUserContentManager *manager,
63
+ WebKitJavascriptResult *js_result,
64
+ gpointer user_data)
65
+ {
66
+ JSCValue *value = webkit_javascript_result_get_js_value(js_result);
67
+ if (jsc_value_is_string(value))
68
+ {
69
+ char *str_value = jsc_value_to_string(value);
70
+
71
+ Json::Value root;
72
+ Json::CharReaderBuilder builder;
73
+ std::string errors;
74
+ std::istringstream json_stream(str_value);
75
+ bool success = Json::parseFromStream(builder, json_stream, &root, &errors);
76
+
77
+ if (success)
78
+ {
79
+ handle_json_message(root, static_cast<AppData *>(user_data));
80
+ }
81
+ else
82
+ {
83
+ g_print("%s", str_value);
84
+ }
85
+
86
+ g_free(str_value);
87
+ }
88
+ }
89
+
90
+ void watch_file(const std::string &file_path, AppData *app_data)
91
+ {
92
+ int inotify_fd = inotify_init();
93
+ if (inotify_fd < 0)
94
+ {
95
+ perror("inotify_init");
96
+ return;
97
+ }
98
+
99
+ int watch_fd = inotify_add_watch(inotify_fd, file_path.c_str(), IN_MODIFY);
100
+ if (watch_fd < 0)
101
+ {
102
+ perror("inotify_add_watch");
103
+ close(inotify_fd);
104
+ return;
105
+ }
106
+
107
+ const size_t event_size = sizeof(struct inotify_event);
108
+ const size_t buf_len = 1024 * (event_size + 16);
109
+ std::vector<char> buffer(buf_len);
110
+
111
+ while (true)
112
+ {
113
+ int length = read(inotify_fd, buffer.data(), buf_len);
114
+ if (length < 0)
115
+ {
116
+ perror("read");
117
+ break;
118
+ }
119
+
120
+ for (int i = 0; i < length;)
121
+ {
122
+ struct inotify_event *event = reinterpret_cast<struct inotify_event *>(&buffer[i]);
123
+ if (event->mask & IN_MODIFY)
124
+ {
125
+ std::ifstream file(file_path);
126
+ std::stringstream buffer;
127
+ buffer << file.rdbuf();
128
+ std::string content = buffer.str();
129
+ file.close();
130
+
131
+ if (!content.empty())
132
+ {
133
+ Json::Value root;
134
+ Json::CharReaderBuilder builder;
135
+ std::string errors;
136
+ std::istringstream json_stream(content);
137
+ bool success = Json::parseFromStream(builder, json_stream, &root, &errors);
138
+
139
+ // std::cout << "CONTENT DETECTED" << std::endl;
140
+
141
+ if (success)
142
+ {
143
+ handle_json_message(root, app_data);
144
+ }
145
+ else
146
+ {
147
+ g_print("Failed to parse JSON string: %s\n", errors.c_str());
148
+ }
149
+
150
+ std::ofstream clear_file(file_path, std::ofstream::out | std::ofstream::trunc);
151
+ clear_file.close();
152
+ }
153
+ }
154
+ i += event_size + event->len;
155
+ }
156
+ }
157
+
158
+ close(watch_fd);
159
+ close(inotify_fd);
160
+ }
161
+
162
+ int main(int argc, char **argv)
163
+ {
164
+ if (argc != 2)
165
+ {
166
+ g_print("Usage: %s <RUNID>\n", argv[0]);
167
+ return 1;
168
+ }
169
+
170
+ const char *rid = argv[1];
171
+ std::string file_path = "/tmp/" + std::string(rid) + ".ruw.ui.socket";
172
+
173
+ AppData app_data;
174
+
175
+ gtk_init(&argc, &argv);
176
+
177
+ GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
178
+ gtk_window_set_title(GTK_WINDOW(window), "WebKit Example");
179
+ gtk_window_set_default_size(GTK_WINDOW(window), 800, 600);
180
+
181
+ app_data.window = window;
182
+
183
+ WebKitUserContentManager *content_manager = webkit_user_content_manager_new();
184
+ webkit_user_content_manager_register_script_message_handler(content_manager, "external");
185
+ g_signal_connect(content_manager, "script-message-received::external", G_CALLBACK(js_callback), &app_data);
186
+ app_data.content_manager = content_manager;
187
+
188
+ const char *js_code = g_strdup_printf("window.RUNID = \"%s\";", rid);
189
+ WebKitUserScript *user_script = webkit_user_script_new(js_code,
190
+ WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES,
191
+ WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_START,
192
+ NULL, NULL);
193
+ webkit_user_content_manager_add_script(content_manager, user_script);
194
+
195
+ WebKitWebView *web_view = WEBKIT_WEB_VIEW(webkit_web_view_new_with_user_content_manager(content_manager));
196
+ gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(web_view));
197
+ app_data.web_view = web_view;
198
+
199
+ webkit_web_view_load_html(web_view, "Loading...", NULL);
200
+
201
+ GIOChannel *channel = g_io_channel_unix_new(fileno(stdin));
202
+ GIOFlags flags = static_cast<GIOFlags>(g_io_channel_get_flags(channel) | G_IO_FLAG_NONBLOCK);
203
+ g_io_channel_set_flags(channel, flags, NULL);
204
+
205
+ g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
206
+
207
+ std::thread watch_thread(watch_file, file_path, &app_data);
208
+
209
+ g_print("INIT::READY");
210
+
211
+ gtk_widget_show_all(window);
212
+ gtk_main();
213
+
214
+ watch_thread.detach();
215
+
216
+ return 0;
217
+ }
package/cpp/ui1.cpp ADDED
@@ -0,0 +1,101 @@
1
+ #include <gtk/gtk.h>
2
+ #include <webkit2/webkit2.h>
3
+ #include <json/json.h>
4
+
5
+ struct AppData {
6
+ const char* url;
7
+ GtkWidget* window;
8
+ };
9
+
10
+ static void handle_json_message(const Json::Value& json, AppData* user_data) {
11
+ // Check if the JSON object has the "action" and "data" fields
12
+ if (json.isMember("action") && json.isMember("data")) {
13
+ std::string action = json["action"].asString();
14
+ std::string data = json["data"].asString();
15
+
16
+ // Perform actions based on the received data
17
+ if (action == "setTitle") {
18
+ // Set the GTK window's title
19
+ gtk_window_set_title(GTK_WINDOW(user_data->window), data.c_str());
20
+ } else if (action == "log") {
21
+ // Set the GTK window's title
22
+ g_print("%s\n", data.c_str());
23
+ } else {
24
+ // Handle other actions as needed
25
+ }
26
+ } else {
27
+ // Invalid JSON format
28
+ g_print("Invalid JSON format: 'action' and 'data' fields are required\n");
29
+ }
30
+ }
31
+
32
+ static void js_callback(WebKitUserContentManager* manager,
33
+ WebKitJavascriptResult* js_result,
34
+ gpointer user_data) {
35
+ JSCValue* value = webkit_javascript_result_get_js_value(js_result);
36
+ if (jsc_value_is_string(value)) {
37
+ char* str_value = jsc_value_to_string(value);
38
+
39
+ Json::Value root;
40
+ Json::CharReaderBuilder builder;
41
+ std::string errors;
42
+ std::istringstream json_stream(str_value);
43
+ bool success = Json::parseFromStream(builder, json_stream, &root, &errors);
44
+
45
+ if (success) {
46
+ // Handle the incoming JSON message
47
+ handle_json_message(root, static_cast<AppData*>(user_data));
48
+ } else {
49
+ g_print("%s", str_value);
50
+ // g_print("Failed to parse JSON string: %s\n", errors.c_str());
51
+ }
52
+
53
+ g_free(str_value);
54
+ }
55
+ }
56
+
57
+
58
+ int main(int argc, char** argv) {
59
+ if (argc != 3) {
60
+ g_print("Usage: %s <URL> <RUNID>\n", argv[0]);
61
+ return 1;
62
+ }
63
+
64
+ const char* url = argv[1];
65
+ const char* rid = argv[2];
66
+
67
+ AppData app_data;
68
+ app_data.url = url;
69
+
70
+ gtk_init(&argc, &argv);
71
+
72
+ GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
73
+ gtk_window_set_title(GTK_WINDOW(window), "WebKit Example");
74
+ gtk_window_set_default_size(GTK_WINDOW(window), 800, 600);
75
+
76
+ app_data.window = window;
77
+
78
+ WebKitUserContentManager* content_manager = webkit_user_content_manager_new();
79
+ webkit_user_content_manager_register_script_message_handler(content_manager, "external");
80
+ g_signal_connect(content_manager, "script-message-received::external", G_CALLBACK(js_callback), &app_data);
81
+
82
+ const char* js_code = g_strdup_printf("window.RUNID = \"%s\";", rid);
83
+ WebKitUserScript* user_script = webkit_user_script_new(js_code,
84
+ WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES,
85
+ WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_START,
86
+ NULL, NULL);
87
+ webkit_user_content_manager_add_script(content_manager, user_script);
88
+
89
+ WebKitWebView* web_view = WEBKIT_WEB_VIEW(webkit_web_view_new_with_user_content_manager(content_manager));
90
+ gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(web_view));
91
+
92
+ webkit_web_view_load_uri(web_view, url);
93
+
94
+ g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
95
+
96
+ gtk_widget_show_all(window);
97
+
98
+ gtk_main();
99
+
100
+ return 0;
101
+ }
package/cpp/ui2.cpp ADDED
@@ -0,0 +1,105 @@
1
+ #include <gtk/gtk.h>
2
+ #include <libwebsockets.h>
3
+ #include <json/json.h>
4
+ #include <iostream>
5
+ #include <string>
6
+ #include <map>
7
+
8
+ std::map<std::string, GtkWidget *> widgets;
9
+
10
+ static void on_button_clicked(GtkWidget *widget, gpointer data)
11
+ {
12
+ // Send event back to Node.js server
13
+ std::string id = static_cast<char *>(data);
14
+ std::string message = "{\"event\": \"buttonClicked\", \"id\": \"" + id + "\"}";
15
+ lws_write(static_cast<lws *>(data), (unsigned char *)message.c_str(), message.length(), LWS_WRITE_TEXT);
16
+ }
17
+
18
+ static int callback_websocket(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len)
19
+ {
20
+ switch (reason)
21
+ {
22
+ case LWS_CALLBACK_CLIENT_ESTABLISHED:
23
+ std::cout << "Connected to server" << std::endl;
24
+ break;
25
+ case LWS_CALLBACK_CLIENT_RECEIVE:
26
+ {
27
+ std::string message(static_cast<char *>(in), len);
28
+ Json::CharReaderBuilder rbuilder;
29
+ Json::Value root;
30
+ std::string errors;
31
+
32
+ std::istringstream s(message);
33
+ std::string doc;
34
+ std::getline(s, doc);
35
+ std::istringstream ss(doc);
36
+ if (Json::parseFromStream(rbuilder, ss, &root, &errors))
37
+ {
38
+ std::string action = root["action"].asString();
39
+
40
+ if (action == "createButton")
41
+ {
42
+ GtkWidget *button = gtk_button_new_with_label(root["label"].asString().c_str());
43
+ g_signal_connect(button, "clicked", G_CALLBACK(on_button_clicked), strdup(root["id"].asString().c_str()));
44
+ widgets[root["id"].asString()] = button;
45
+ gtk_container_add(GTK_CONTAINER(gtk_window_new(GTK_WINDOW_TOPLEVEL)), button);
46
+ gtk_widget_show_all(button);
47
+ }
48
+ }
49
+ break;
50
+ }
51
+ case LWS_CALLBACK_CLIENT_CLOSED:
52
+ std::cout << "Disconnected from server" << std::endl;
53
+ break;
54
+ default:
55
+ break;
56
+ }
57
+ return 0;
58
+ }
59
+
60
+ static const struct lws_protocols protocols[] = {
61
+ {
62
+ "example-protocol",
63
+ callback_websocket,
64
+ 0,
65
+ 1024,
66
+ },
67
+ {NULL, NULL, 0, 0}};
68
+
69
+ int main(int argc, char **argv)
70
+ {
71
+ std::cout << "Starting" << std::endl;
72
+ if (argc != 2)
73
+ {
74
+ std::cerr << "Usage: " << argv[0] << " <WebSocket server URI>" << std::endl;
75
+ return 1;
76
+ }
77
+
78
+ gtk_init(&argc, &argv);
79
+
80
+ struct lws_context_creation_info info;
81
+ memset(&info, 0, sizeof info);
82
+ info.port = CONTEXT_PORT_NO_LISTEN;
83
+ info.protocols = protocols;
84
+
85
+ struct lws_context *context = lws_create_context(&info);
86
+
87
+ struct lws_client_connect_info ccinfo = {0};
88
+ ccinfo.context = context;
89
+ ccinfo.address = argv[1];
90
+ ccinfo.port = 8080;
91
+ ccinfo.path = "/";
92
+ ccinfo.host = lws_canonical_hostname(context);
93
+ ccinfo.origin = "origin";
94
+ ccinfo.protocol = protocols[0].name;
95
+
96
+ struct lws *wsi = lws_client_connect_via_info(&ccinfo);
97
+
98
+ while (lws_service(context, 1000) >= 0)
99
+ {
100
+ gtk_main_iteration_do(false);
101
+ }
102
+
103
+ lws_context_destroy(context);
104
+ return 0;
105
+ }
@@ -3,7 +3,7 @@
3
3
  const yargs = require('yargs/yargs');
4
4
  const path = require('path');
5
5
  const { hideBin } = require('yargs/helpers');
6
- const { fork, exec } = require('child_process');
6
+ const { fork, exec, execSync } = require('child_process');
7
7
  const { watch } = require('chokidar');
8
8
  const utils = require('./utils');
9
9
  const { existsSync, readFileSync, writeFileSync, mkdirSync } = require('fs');
@@ -98,6 +98,17 @@ yargs(hideBin(process.argv))
98
98
  utils.createProject(argv.path);
99
99
  }
100
100
  )
101
+ .command('ui-bin <path>', 'Build the UI bin for your own app', (yargs) => {
102
+ yargs
103
+ .positional('path', {
104
+ describe: 'Path of the output bin',
105
+ type: 'string',
106
+ });
107
+ },
108
+ (argv) => {
109
+ execSync('sh '+path.resolve(__dirname, '../../../build.sh')+' '+argv.path);
110
+ }
111
+ )
101
112
  .command('run <path | package>', 'Run an app', (yargs) => {
102
113
  yargs
103
114
  .positional('path', {
@@ -72,15 +72,16 @@ module.exports = {
72
72
  fs.writeFileSync(path.join(projectPath, '.gitignore'), `node_modules/\npackage-lock.json`);
73
73
  execSync('cd '+projectPath+' && git init .');
74
74
  }
75
- log('Installing '+npm_package_name);
76
- exec('cd '+projectPath+' && npm i '+npm_package_name, (err) => {
77
- if(err){
78
- console.error(err);
79
- process.exit(0);
80
- } else {
81
- rl.close();
82
- }
83
- });
75
+ // log('Installing '+npm_package_name);
76
+ // exec('cd '+projectPath+' && npm i '+npm_package_name, (err) => {
77
+ // if(err){
78
+ // console.error(err);
79
+ // process.exit(0);
80
+ // } else {
81
+ // rl.close();
82
+ // }
83
+ // });
84
+ log('Done.', ':end');
84
85
  }
85
86
  if (!fs.existsSync(projectPath)) {
86
87
  rl.question(logget('Package Name: '), (pkg) => {
@@ -2,7 +2,8 @@ const execOptions = {
2
2
  sharedContext: true,
3
3
  resolveExtensions: [{ext: '.js', options: { type: 'js' }}, '.coffee'],
4
4
  nativeRequire: false,
5
- cwdAlias: '$'
5
+ cwdAlias: '$',
6
+ jsxPragma: 'createElement'
6
7
  }
7
8
 
8
9
  module.exports.execOptions = execOptions;
@@ -13,6 +13,10 @@ module.exports = (currentFile) => {
13
13
  return fs.readFileSync(gp(filepath), options);
14
14
  }
15
15
 
16
+ function realpath(filepath, options = { encoding: 'utf-8' }){
17
+ return gp(filepath);
18
+ }
19
+
16
20
  function write(filepath, content, options){
17
21
  return fs.writeFileSync(gp(filepath), content, options);
18
22
  }
@@ -49,6 +53,7 @@ module.exports = (currentFile) => {
49
53
  fstat,
50
54
  exists,
51
55
  write,
52
- read
56
+ read,
57
+ realpath
53
58
  };
54
59
  }
@@ -14,17 +14,18 @@ const lookUpInOtherApps = (fullPath) => {
14
14
  const name = fullPath.indexOf('/') ? fullPath.split('/')[0] : fullPath;
15
15
  let dpath = fullPath.indexOf('/') ? fullPath.split('/')[1] : '';
16
16
  let ppath = path.join(con.CONFIG_PATH, name, 'app');
17
- if(!existsSync(ppath)) return null;
18
- if(!dpath){
17
+ if (!existsSync(ppath)) return null;
18
+ if (!dpath) {
19
19
  dpath = jsYaml.load(readFileSync(path.join(ppath, 'app.yaml'), 'utf-8')).entry;
20
20
  }
21
21
  ppath = path.join(ppath, dpath);
22
- if(existsSync(ppath)) return ppath;
22
+ if (existsSync(ppath)) return ppath;
23
23
  else return null;
24
24
  }
25
25
 
26
26
  module.exports.imp = function (runPath, context) {
27
27
  return function (filename, options = {}) {
28
+ if (!options) options = {};
28
29
  let type = options.type || "coffee";
29
30
  let exports,
30
31
  ispkg = findPackage(filename);
@@ -35,24 +36,24 @@ module.exports.imp = function (runPath, context) {
35
36
 
36
37
  const lookUp = () => {
37
38
  const otherPath = lookUpInOtherApps(filename);
38
- if(!otherPath) throw new Error('Module "'+filename+'" not found');
39
+ if (!otherPath) throw new Error('Module "' + filename + '" not found');
39
40
  else filepath = otherPath;
40
41
  }
41
42
 
42
43
  const foundCache = cachedFiles.find(f => f.filepath == filepath);
43
44
 
44
- if(!ispkg && foundCache){
45
+ if (!ispkg && foundCache) {
45
46
  exports = foundCache.exports;
46
47
  }
47
48
 
48
- if(!ispkg && !existsSync(filepath)){
49
- if(Array.isArray(execOptions.resolveExtensions) && execOptions.resolveExtensions.length){
50
- const resolve = execOptions.resolveExtensions.find(ext => typeof ext == "string" ? existsSync(filepath+ext) : existsSync(filepath+(ext.ext || '')));
51
- if(resolve) {
49
+ if (!ispkg && !existsSync(filepath)) {
50
+ if (Array.isArray(execOptions.resolveExtensions) && execOptions.resolveExtensions.length) {
51
+ const resolve = execOptions.resolveExtensions.find(ext => typeof ext == "string" ? existsSync(filepath + ext) : existsSync(filepath + (ext.ext || '')));
52
+ if (resolve) {
52
53
  filepath += typeof resolve == "string" ? resolve : resolve.ext;
53
- if(typeof resolve == "object" && resolve.options){
54
- if(resolve.options.type) type = resolve.options.type;
55
- for(let i in resolve.options) options[i] = resolve.options[i];
54
+ if (typeof resolve == "object" && resolve.options) {
55
+ if (resolve.options.type) type = resolve.options.type;
56
+ for (let i in resolve.options) options[i] = resolve.options[i];
56
57
  }
57
58
  } else lookUp();
58
59
  } else lookUp();
@@ -60,16 +61,24 @@ module.exports.imp = function (runPath, context) {
60
61
 
61
62
  const exec = (coptions = {}) => runPath(
62
63
  filepath,
63
- { import: options, main: false, useContext: execOptions.sharedContext == false ? false : true, ...coptions },
64
- execOptions.sharedContext == false ? {} : context,
64
+ {
65
+ import: options,
66
+ main: false,
67
+ useContext: execOptions.sharedContext == false ? false : !(options.context && options.context == 'new'),
68
+ ...coptions,
69
+ as: options.as == 'main' ? context.module.main ? 'main' : 'parent' : options.as == 'parent' ? 'parent' : 'child',
70
+ fromMain: context.module.main
71
+ },
72
+ execOptions.sharedContext == false ? {} :
73
+ options.context && options.context == 'new' ? {} : context,
65
74
  ).context.module.exports;
66
75
 
67
76
  if (ispkg) {
68
77
  exports = getPackage(filename)(context);
69
- } else if(foundCache) {
70
-
78
+ } else if (foundCache) {
79
+
71
80
  } else if (type == "coffee") {
72
- exports = exec({ });
81
+ exports = exec({});
73
82
  } else if (type == "js") {
74
83
  exports = exec({ compile: false });
75
84
  } else if (type == "yaml" || type == "json" || type == "text") {
@@ -95,8 +104,8 @@ module.exports.imp = function (runPath, context) {
95
104
  }
96
105
  }
97
106
 
98
- if(!ispkg) context.module.imports.push(filepath);
99
- if(!ispkg) cachedFiles.push({ filepath, exports });
107
+ if (!ispkg) context.module.imports.push(filepath);
108
+ if (!ispkg) cachedFiles.push({ filepath, exports });
100
109
 
101
110
  return exports;
102
111
  };
@@ -15,17 +15,21 @@ function getType(value){
15
15
  return typeof value === 'object' ? Array.isArray(value) ? 'array' : typeof value : typeof value;
16
16
  }
17
17
 
18
- function typedef(value) {
18
+ function typedef(value, strict = false) {
19
19
  return {
20
+ strict,
20
21
  defaultValue: value,
21
22
  class: typeof value == 'function' ? value : typeof value === 'object' && value !== null && !Array.isArray(value) ? value.constructor : _defaultConstructors[getType(value)],
22
23
  type: getType(value),
23
24
  isConstucted: typeof value === 'object' && value !== null && !Array.isArray(value),
24
- isEmpty: typeof value == "object" ? !Object.keys(value).length : typeof value == "string" ? value == "" : true
25
+ isEmpty: typeof value == "object" ? !Object.keys(value).length : typeof value == "string" ? value == "" : typeof value !== "function"
25
26
  };
26
27
  }
27
28
 
28
29
  function typeis(obj, typeDef) {
30
+ // Resolve Type
31
+ if(typeof typeDef == "function" && typeDef.type) typeDef = typeDef.type;
32
+
29
33
  if (typeDef.isConstucted && typeDef.class && !(obj instanceof typeDef.class)) {
30
34
  return false;
31
35
  }
@@ -41,7 +45,9 @@ function typeis(obj, typeDef) {
41
45
  if(!typeDef.isEmpty) {
42
46
  if(typeDef.type == 'object'){
43
47
  for (const key in typeDef.defaultValue) {
44
- const propTypeDef = typeDef.defaultValue[key];
48
+ let propTypeDef = typeDef.defaultValue[key];
49
+ // Resolve type
50
+ if(typeof propTypeDef == "function" && propTypeDef.type) propTypeDef = propTypeDef.type;
45
51
 
46
52
  if (typeof propTypeDef === 'object') {
47
53
  if (!typeis(obj[key], propTypeDef)) {
@@ -51,8 +57,13 @@ function typeis(obj, typeDef) {
51
57
  return false;
52
58
  }
53
59
  }
60
+ if(typeDef.strict) {
61
+ if(Object.keys(obj).some(key => !Object.keys(typeDef.defaultValue).includes(key))) return false;
62
+ }
54
63
  } else if(typeDef.type == 'string'){
55
64
  return typeDef.defaultValue == obj;
65
+ } else if(typeDef.type == 'function'){
66
+ return typeDef.defaultValue == obj;
56
67
  }
57
68
  }
58
69
 
@@ -70,24 +81,29 @@ function typei(child, parent) {
70
81
  function int(str){
71
82
  return parseInt(str);
72
83
  }
84
+ int.type = typedef(1)
73
85
 
74
86
  function float(str){
75
87
  return parseFloat(str);
76
88
  }
89
+ float.type = typedef(1.0)
77
90
 
78
91
  function num(str){
79
92
  return Number(str);
80
93
  }
94
+ num.type = typedef(1)
81
95
 
82
96
  function str(str){
83
97
  return str ? str.toString() : "";
84
98
  }
99
+ str.type = typedef('')
85
100
 
86
101
  function bool(value){
87
102
  return typeof value == 'string' ? (
88
103
  value == 'true' ? true : false
89
104
  ) : value !== null && value !== undefined;
90
105
  }
106
+ bool.type = typedef(true)
91
107
 
92
108
  module.exports = {
93
109
  typex,
@@ -189,10 +189,20 @@ window.recieveMessage = (data) => {
189
189
  } catch (e) {
190
190
  log(e.toString());
191
191
  }
192
- }
192
+ } else if(edata.action == 'message') {
193
+ window.dispatchEvent(new CustomEvent('message', {
194
+ detail: edata.data
195
+ }));
196
+ }
193
197
  };
194
198
 
195
199
  window.addEventListener('load', () => {
196
- window.exec(window.execContext);
200
+ window.exec({
201
+ ...window.execContext,
202
+ window,
203
+ log,
204
+ send: (data) => sendData({ action: 'message', data }),
205
+ onRecieve: (cb) => window.addEventListener('message', (e) => cb(e.detail || {}))
206
+ });
197
207
  log('SETUP::READY');
198
208
  });
@@ -1,5 +1,8 @@
1
1
  const { compile } = require("../../coffeescript/coffeescript");
2
+ const { execOptions } = require("../const/opt");
2
3
  const { getFile } = require("./fs");
4
+ const babel = require('@babel/core');
5
+ const babelReact = require('@babel/preset-react');
3
6
 
4
7
  function tokenizeCoffeeScript(code) {
5
8
  const tokens = [];
@@ -11,7 +14,7 @@ function tokenizeCoffeeScript(code) {
11
14
 
12
15
  if (char === '#') {
13
16
  // Comment
14
- tokens.push({ type: 'COMMENT', value: char + code.substring(i + 1).split('\n')[0] });
17
+ tokens.push({ type: 'COMMENT', value: char + code.substring(i + 1).split('\n')[0]+'\n' });
15
18
  i = code.indexOf('\n', i);
16
19
  } else if (char === '"' || char === "'") {
17
20
  // String
@@ -75,7 +78,7 @@ const fnextToken = (i, tokens, type, value) => {
75
78
  return tokens.map((t, ind) => { t.ti = ind; return t }).slice(i, tokens.length - 1).map((t, ind) => { t.ri = ind; t.index = ind - i; return t }).find(t => t.type == type && (value ? t.value == value : true));
76
79
  }
77
80
 
78
- function compileRewStuff(content) {
81
+ function compileRewStuff(content, options) {
79
82
  const tokens = tokenizeCoffeeScript(content);
80
83
  let result = '';
81
84
 
@@ -85,12 +88,27 @@ function compileRewStuff(content) {
85
88
  const token = tokens[i];
86
89
  let { nextToken, n } = gnextToken(i, 1, tokens) || {};
87
90
 
91
+ if (token.type === 'IDENTIFIER' && token.value === 'opt') {
92
+ const { nextToken: nextNextToken } = gnextToken(i, 2, tokens) || {};
93
+ if(nextNextToken && nextNextToken.value == 'jsxPragma'){
94
+ const { nextToken: nextLastToken } = gnextToken(i, 5, tokens) || {};
95
+ execOptions.jsxPragma = nextLastToken.value.slice(1).slice(0, -1);
96
+ }
97
+ }
98
+
99
+ if (token.type === 'COMMENT' && token.value.slice(1).trim() === '@jsx') {
100
+ options.jsx = true;
101
+ }
102
+
88
103
  if (token.type === 'IDENTIFIER' && token.value === 'import') {
89
104
  // console.log(nextToken.type);
90
105
  let ind = i + n + 2;
91
106
 
92
107
  let defaultName;
93
- if (nextToken.value === '{') {
108
+ if (nextToken.type === 'STRING') {
109
+ result += `inc ${nextToken.value}`;
110
+ i += n;
111
+ } else if (nextToken.value === '{') {
94
112
  const closingBraceToken = fnextToken(ind, tokens, 'OTHER', '}');
95
113
  const nameToken = fnextToken(ind, tokens, 'STRING');
96
114
  if (closingBraceToken) {
@@ -108,11 +126,23 @@ function compileRewStuff(content) {
108
126
  result += `${defaultName} = inc ${nameToken.value}`;
109
127
  i = ind + 6;
110
128
  }
111
- } else {
129
+ } else if(nextToken) {
112
130
  const nameToken = fnextToken(ind, tokens, 'STRING');
113
131
  defaultName = nextToken.value;
114
- result += `{ default: ${defaultName} } = inc ${nameToken.value}`;
115
- i = ind + 2;
132
+ let { nextToken: nextNextToken, n: n2 } = gnextToken(i + 2, 1, tokens) || {};
133
+ if(nextNextToken?.type == 'OTHER' && nextNextToken?.value == ','){
134
+ const closingBraceToken = fnextToken(ind, tokens, 'OTHER', '}');
135
+ if(closingBraceToken){
136
+ const exportsTokens = tokens.slice(ind, closingBraceToken.ti);
137
+ const exports = exportsTokens.filter(t => t.type === 'IDENTIFIER').map(t => t.value).join(', ');
138
+ result += `{ default: ${defaultName}, ${exports} } = inc ${nameToken?.value || ""}`;
139
+ i = closingBraceToken.ti + 4;
140
+ }
141
+ } else {
142
+ result += `{ default: ${defaultName} } = inc ${nameToken?.value || ""}`;
143
+ i = ind + 2;
144
+ }
145
+
116
146
  }
117
147
 
118
148
  const nextLastToken = fnextToken(i, tokens, 'IDENTIFIER');
@@ -154,7 +184,14 @@ function compileRewStuff(content) {
154
184
 
155
185
 
156
186
  const cpl = (module.exports.compile = function (file, options = {}) {
157
- return compile(compileRewStuff(file.content), { ...options, filename: file.path, bare: true, inlineMap: true });
187
+ let c = compile(compileRewStuff(file.content, options), { ...options, filename: file.path, bare: false, inlineMap: false });
188
+ if(options.jsx) {
189
+ c = babel.transformSync(c, {
190
+ presets: [[babelReact, { pragma: execOptions.jsxPragma }]],
191
+ plugins: []
192
+ }).code;
193
+ }
194
+ return c;
158
195
  });
159
196
 
160
197
  module.exports.compileFile = function (filepath, options = {}) {
@@ -8,17 +8,20 @@ const fsLib = require('../functions/fs');
8
8
  const pathLib = require('../functions/path');
9
9
  const execLib = require('../functions/exec');
10
10
 
11
+ let mainFile = "";
12
+ const isMainFile = filepath => filepath == mainFile;
11
13
  module.exports.prepareContext = function (
12
14
  custom_context,
13
15
  options,
14
16
  filepath = "",
15
17
  runPath = () => {},
16
18
  ) {
19
+ if(mainFile == "") mainFile = filepath;
17
20
  let context = {
18
21
  module: {
19
22
  exports: null,
20
23
  filepath,
21
- main: options.main ?? true,
24
+ main: isMainFile(filepath),
22
25
  imports: []
23
26
  },
24
27
  imports: {
@@ -29,8 +32,8 @@ module.exports.prepareContext = function (
29
32
  };
30
33
  if (options.useContext) {
31
34
  context = {
32
- ...context,
33
35
  ...custom_context,
36
+ ...context
34
37
  };
35
38
  } else {
36
39
  context = {
@@ -45,34 +48,43 @@ module.exports.prepareContext = function (
45
48
  throw new Error("Module "+package+" not found");
46
49
  }
47
50
  },
48
- opt: {
49
- set: (key, value) => execOptions[key] = value,
50
- get: (key) => execOptions[key],
51
- push: (key, value) => execOptions[key]?.push(value),
52
- pop: (key) => execOptions[key]?.pop()
53
- },
54
51
  ...custom_context,
55
52
  };
56
- context.imp = imp(runPath, context);
57
- context.inc = (package, asserts) => {
58
- try{
59
- return context.imp(package, asserts);
60
- } catch(e) {
61
- return context.require(package);
62
- }
63
- };
64
- context.pub = pubFunction(context);
65
- context.exports = exportsFunction(context);
66
53
  }
67
- if (!context.global) context.global = context;
68
54
  if (!context.process)
69
55
  context.process = {
70
56
  argv: process.argv,
71
- target: emitter(),
57
+ target: {
58
+ on: (event, listener) => process.on(event, listener),
59
+ off: (event, listener) => process.off(event, listener),
60
+ emit: (event, code) => process.emit(event, code)
61
+ },
72
62
  env: process.env,
73
63
  cwd: () => process.cwd(),
74
64
  arch: process.arch
75
65
  };
66
+
67
+ context.global = context;
76
68
  context.imports.assert = options.import ?? {};
69
+ context.imp = imp(runPath, context);
70
+ context.inc = (package, asserts) => {
71
+ try{
72
+ if(package.startsWith('node:') || package.startsWith('pkg:')) throw new Error('');
73
+ return context.imp(package, asserts);
74
+ } catch(e) {
75
+ return context.require(package.startsWith('pkg:') ? package.split('pkg:')[1] : package);
76
+ }
77
+ };
78
+ context.pub = pubFunction(context);
79
+ context.exports = exportsFunction(context);
80
+
81
+ if(context.module.main || (options.fromMain == true && options.as == 'main')){
82
+ context.opt = {
83
+ set: (key, value) => execOptions[key] = value,
84
+ get: (key) => execOptions[key],
85
+ push: (key, value) => execOptions[key]?.push(value),
86
+ pop: (key) => execOptions[key]?.pop()
87
+ };
88
+ } else delete context.opt
77
89
  return context;
78
90
  };
@@ -3,7 +3,11 @@ const { compileFile } = require("./compiler");
3
3
  const { prepareContext } = require("./context");
4
4
 
5
5
  const exec = (module.exports.exec = function (code, context) {
6
- return vm.runInNewContext(code, vm.createContext(context));
6
+ return vm.runInNewContext(code, vm.createContext(context), {
7
+ filename: context.module.filepath,
8
+ lineOffset: 0,
9
+ displayErrors: true
10
+ });
7
11
  });
8
12
 
9
13
  module.exports.runPath = function runPath(
@@ -161,11 +161,17 @@ module.exports.uiClasses = (context, options, send, recieve, hook, rmHook) => {
161
161
  // }
162
162
  // }, false);
163
163
 
164
+ const Transmitter = {
165
+ send: (data) => send({ action: 'message', data }),
166
+ recieve: (cb) => recieve((data) => cb(data.data))
167
+ }
168
+
164
169
  return {
165
170
  Widget: RewWidget,
166
171
  Text: RewTextWidget,
167
172
  WidgetOptions: RemWidgetOptions,
168
173
  findElement,
169
- StyleSheet: StyleSheet
174
+ StyleSheet: StyleSheet,
175
+ Transmitter
170
176
  }
171
177
  }
@@ -1,7 +1,5 @@
1
1
  const path = require("path");
2
2
  const { spawn, exec } = require("child_process");
3
- const WebSocket = require("ws");
4
- const http = require("http");
5
3
  const fs = require("fs");
6
4
  const { uiClasses } = require("./modules/ui/classes");
7
5
  const { generateRandomID } = require("../functions/id");
@@ -92,9 +90,7 @@ module.exports = (context) => ({
92
90
  }
93
91
  });
94
92
 
95
- const p = spawn(BIN_PATH, [runId]);
96
-
97
-
93
+ const p = spawn(options.bin || BIN_PATH, [runId]);
98
94
 
99
95
  p.on("close", (code) => {
100
96
  rl.close();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@makano/rew",
3
- "version": "1.1.5",
3
+ "version": "1.1.6",
4
4
  "description": "A simple coffescript runtime",
5
5
  "main": "lib/rew/main.js",
6
6
  "directories": {
@@ -11,7 +11,9 @@
11
11
  },
12
12
  "files": [
13
13
  "lib/",
14
+ "cpp/",
14
15
  "bin/",
16
+ "build.sh",
15
17
  "README.md"
16
18
  ],
17
19
  "bin": {
@@ -25,6 +27,9 @@
25
27
  "author": "makano",
26
28
  "license": "ISC",
27
29
  "dependencies": {
30
+ "@babel/core": "^7.24.6",
31
+ "@babel/preset-react": "^7.24.6",
32
+ "babel-plugin-jsx": "^1.2.0",
28
33
  "chalk": "^5.3.0",
29
34
  "chokidar": "^3.6.0",
30
35
  "js-yaml": "^4.1.0",