@makano/rew 1.1.2 → 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,11 +3,12 @@
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
- const { existsSync } = require('fs');
9
+ const { existsSync, readFileSync, writeFileSync, mkdirSync } = require('fs');
10
10
  const { log } = require('./log');
11
+ const { compileFile, compile } = require('../modules/compiler');
11
12
 
12
13
  yargs(hideBin(process.argv))
13
14
  .command(
@@ -97,6 +98,17 @@ yargs(hideBin(process.argv))
97
98
  utils.createProject(argv.path);
98
99
  }
99
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
+ )
100
112
  .command('run <path | package>', 'Run an app', (yargs) => {
101
113
  yargs
102
114
  .positional('path', {
@@ -125,9 +137,69 @@ yargs(hideBin(process.argv))
125
137
  .positional('file', {
126
138
  describe: 'File to build',
127
139
  type: 'string',
140
+ })
141
+ .option('output', {
142
+ alias: 'o',
143
+ describe: 'Output directory',
144
+ type: 'string',
128
145
  });
129
146
  }, (argv) => {
130
- console.log(`Building file: ${argv.file}`);
147
+
148
+ function readFile(filePath) {
149
+ return readFileSync(filePath, { encoding: 'utf-8' });
150
+ }
151
+
152
+ function extractImports(content) {
153
+ const importRegex = /(\w+)\s*=\s*imp\s*['"](.+?)['"]/g;
154
+ const imports = [];
155
+ let match;
156
+ while ((match = importRegex.exec(content)) !== null) {
157
+ imports.push({ variable: match[1], url: match[2] });
158
+ }
159
+ return imports;
160
+ }
161
+
162
+ function writeCompiledFile(filePath, compiledCode) {
163
+ const dirName = outputDir ? outputDir : path.dirname(filePath);
164
+ if(!existsSync(dirName)) mkdirSync(dirName, { recursive: true });
165
+ const baseName = path.basename(filePath, path.extname(filePath));
166
+ const newFilePath = path.join(dirName, `${baseName}.js`);
167
+ writeFileSync(newFilePath, compiledCode, { encoding: 'utf-8' });
168
+ log(`Compiled: ${newFilePath}`);
169
+ }
170
+
171
+ function processFile(filePath, importsArray) {
172
+ const content = readFile(filePath);
173
+ const imports = extractImports(content);
174
+
175
+ imports.forEach(importStatement => {
176
+ const importedFilePath = path.resolve(path.dirname(filePath), importStatement.url);
177
+ if (!importsArray.some(importObj => importObj.url === importStatement.url)) {
178
+
179
+ if(existsSync(importedFilePath)){
180
+ importsArray.push(importStatement);
181
+ processFile(importedFilePath, importsArray);
182
+ } else if(existsSync(importedFilePath+'.coffee')){
183
+ importsArray.push(importStatement);
184
+ processFile(importedFilePath+'.coffee', importsArray);
185
+ } else if(existsSync(importedFilePath+'.js')){
186
+ importsArray.push(importStatement);
187
+ processFile(importedFilePath+'.js', importsArray);
188
+ }
189
+
190
+ }
191
+ });
192
+
193
+ const compiled = compile({ content }, {});
194
+ writeCompiledFile(filePath, compiled);
195
+ }
196
+
197
+ const filePath = path.resolve(process.cwd(), argv.file);
198
+ const importsArray = [];
199
+ const outputDir = argv.output ? path.resolve(process.cwd(), argv.output) : null;
200
+ log('Start compile at', outputDir || 'default path');
201
+ processFile(filePath, importsArray);
202
+ log('Compiled', importsArray.length + 1, 'files.', ':end');
131
203
  })
132
204
  .help()
133
205
  .argv;
@@ -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;
@@ -0,0 +1,27 @@
1
+ const shell = require('child_process');
2
+
3
+
4
+ module.exports = (currentFile) => {
5
+
6
+ function exec(command, options){
7
+ return shell.execSync(command, { stdio: options?.output == false ? null : 'inherit' });
8
+ }
9
+
10
+ exec.background = function execAsync(command, options, callback){
11
+ if(typeof options == "function" && !callback){
12
+ callback = options;
13
+ options = {};
14
+ }
15
+ if(!options) options = {};
16
+ if(!callback) callback = () => {};
17
+ return shell.exec(command, {
18
+ ...options,
19
+ }, callback);
20
+ }
21
+
22
+ function spawn(command, options){
23
+ return shell.spawn(command, options);
24
+ }
25
+
26
+ return { exec, spawn };
27
+ }
@@ -1,9 +1,27 @@
1
+
2
+ function exportsThe(item, name, context){
3
+ if (name) {
4
+ if(!context.module.exports) context.module.exports = {};
5
+ context.module.exports[name] = item;
6
+ } else {
7
+ if(context.module.exports) context.module.exports.default = item;
8
+ else context.module.exports = item;
9
+ }
10
+ }
11
+
12
+ module.exports.pubFunction = function (context) {
13
+ return function (name, item) {
14
+ if(name && !item){
15
+ item = name;
16
+ name = null;
17
+ }
18
+ exportsThe(item, name, context);
19
+ };
20
+ };
21
+
1
22
  module.exports.exportsFunction = function (context) {
2
23
  return function (item, name) {
3
- if (name) {
4
- context.module.exports[name] = item;
5
- } else {
6
- context.module.exports = item;
7
- }
24
+ exportsThe(item, name, context);
8
25
  };
9
26
  };
27
+
@@ -13,12 +13,16 @@ 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
  }
19
23
 
20
24
  function exists(filepath, options){
21
- return fs.existsSync(filepath);
25
+ return fs.existsSync(gp(filepath));
22
26
  }
23
27
 
24
28
  function fstat(filepath, options){
@@ -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
  }