@makano/rew 1.1.5 → 1.1.6

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/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",