Skip to content

Support for importing js files and modules via require #5

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 5 additions & 33 deletions Electrino/Electrino.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@
5AA9DBB81EB9F58B00EF7CC9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5AA9DBB71EB9F58B00EF7CC9 /* Assets.xcassets */; };
5AA9DBBB1EB9F58B00EF7CC9 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5AA9DBB91EB9F58B00EF7CC9 /* MainMenu.xib */; };
5AA9DBC51EBA08BF00EF7CC9 /* ENOBrowserWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5AA9DBC31EBA08BF00EF7CC9 /* ENOBrowserWindowController.m */; };
5AA9DBCF1EBA0BFC00EF7CC9 /* index.html in CopyFiles */ = {isa = PBXBuildFile; fileRef = 5AA9DBC81EBA0BDD00EF7CC9 /* index.html */; };
5AA9DBD01EBA0BFC00EF7CC9 /* main.js in CopyFiles */ = {isa = PBXBuildFile; fileRef = 5AA9DBC91EBA0BDD00EF7CC9 /* main.js */; };
5AA9DBD11EBA0BFC00EF7CC9 /* package.json in CopyFiles */ = {isa = PBXBuildFile; fileRef = 5AA9DBCA1EBA0BDD00EF7CC9 /* package.json */; };
5AA9DBD41EBA0D1200EF7CC9 /* ENOJavaScriptApp.m in Sources */ = {isa = PBXBuildFile; fileRef = 5AA9DBD31EBA0D1200EF7CC9 /* ENOJavaScriptApp.m */; };
5AA9DBDE1EBA4F7B00EF7CC9 /* ENOJSPath.m in Sources */ = {isa = PBXBuildFile; fileRef = 5AA9DBDD1EBA4F7B00EF7CC9 /* ENOJSPath.m */; };
5AA9DBE41EBA4F9B00EF7CC9 /* ENOJSUrl.m in Sources */ = {isa = PBXBuildFile; fileRef = 5AA9DBE31EBA4F9B00EF7CC9 /* ENOJSUrl.m */; };
Expand All @@ -23,23 +20,9 @@
5AA9DBED1EBA55B700EF7CC9 /* ENOJSProcess.m in Sources */ = {isa = PBXBuildFile; fileRef = 5AA9DBEC1EBA55B700EF7CC9 /* ENOJSProcess.m */; };
5AA9DBF01EBA564600EF7CC9 /* ENOJSConsole.m in Sources */ = {isa = PBXBuildFile; fileRef = 5AA9DBEF1EBA564600EF7CC9 /* ENOJSConsole.m */; };
5AA9DBF21EBA7C0700EF7CC9 /* ENOBrowserWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5AA9DBF11EBA7C0700EF7CC9 /* ENOBrowserWindowController.xib */; };
BC44C7D01EBF2D0E008C569A /* app in Resources */ = {isa = PBXBuildFile; fileRef = BC44C7CF1EBF2D0E008C569A /* app */; };
/* End PBXBuildFile section */

/* Begin PBXCopyFilesBuildPhase section */
5AA9DBCE1EBA0BEA00EF7CC9 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = app;
dstSubfolderSpec = 7;
files = (
5AA9DBCF1EBA0BFC00EF7CC9 /* index.html in CopyFiles */,
5AA9DBD01EBA0BFC00EF7CC9 /* main.js in CopyFiles */,
5AA9DBD11EBA0BFC00EF7CC9 /* package.json in CopyFiles */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
5AA9DBAE1EB9F58A00EF7CC9 /* ElectrinoTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ElectrinoTest.app; sourceTree = BUILT_PRODUCTS_DIR; };
5AA9DBB11EB9F58B00EF7CC9 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
Expand All @@ -50,9 +33,6 @@
5AA9DBBC1EB9F58B00EF7CC9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
5AA9DBC21EBA08BE00EF7CC9 /* ENOBrowserWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ENOBrowserWindowController.h; sourceTree = "<group>"; };
5AA9DBC31EBA08BF00EF7CC9 /* ENOBrowserWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ENOBrowserWindowController.m; sourceTree = "<group>"; };
5AA9DBC81EBA0BDD00EF7CC9 /* index.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = index.html; sourceTree = "<group>"; };
5AA9DBC91EBA0BDD00EF7CC9 /* main.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = main.js; sourceTree = "<group>"; };
5AA9DBCA1EBA0BDD00EF7CC9 /* package.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = package.json; sourceTree = "<group>"; };
5AA9DBD21EBA0D1200EF7CC9 /* ENOJavaScriptApp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ENOJavaScriptApp.h; sourceTree = "<group>"; };
5AA9DBD31EBA0D1200EF7CC9 /* ENOJavaScriptApp.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ENOJavaScriptApp.m; sourceTree = "<group>"; };
5AA9DBDC1EBA4F7B00EF7CC9 /* ENOJSPath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ENOJSPath.h; sourceTree = "<group>"; };
Expand All @@ -68,6 +48,7 @@
5AA9DBEE1EBA564600EF7CC9 /* ENOJSConsole.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ENOJSConsole.h; sourceTree = "<group>"; };
5AA9DBEF1EBA564600EF7CC9 /* ENOJSConsole.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ENOJSConsole.m; sourceTree = "<group>"; };
5AA9DBF11EBA7C0700EF7CC9 /* ENOBrowserWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ENOBrowserWindowController.xib; sourceTree = "<group>"; };
BC44C7CF1EBF2D0E008C569A /* app */ = {isa = PBXFileReference; lastKnownFileType = folder; path = app; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand All @@ -84,7 +65,7 @@
5AA9DBA51EB9F58A00EF7CC9 = {
isa = PBXGroup;
children = (
5AA9DBC71EBA0BDD00EF7CC9 /* test-app */,
BC44C7CF1EBF2D0E008C569A /* app */,
5AA9DBB01EB9F58A00EF7CC9 /* Electrino */,
5AA9DBAF1EB9F58A00EF7CC9 /* Products */,
);
Expand Down Expand Up @@ -125,16 +106,6 @@
name = "Supporting Files";
sourceTree = "<group>";
};
5AA9DBC71EBA0BDD00EF7CC9 /* test-app */ = {
isa = PBXGroup;
children = (
5AA9DBC81EBA0BDD00EF7CC9 /* index.html */,
5AA9DBC91EBA0BDD00EF7CC9 /* main.js */,
5AA9DBCA1EBA0BDD00EF7CC9 /* package.json */,
);
path = "test-app";
sourceTree = "<group>";
};
5AA9DBD51EBA4D8500EF7CC9 /* JS API implementations */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -164,7 +135,6 @@
5AA9DBAA1EB9F58A00EF7CC9 /* Sources */,
5AA9DBAB1EB9F58A00EF7CC9 /* Frameworks */,
5AA9DBAC1EB9F58A00EF7CC9 /* Resources */,
5AA9DBCE1EBA0BEA00EF7CC9 /* CopyFiles */,
);
buildRules = (
);
Expand Down Expand Up @@ -213,6 +183,7 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
BC44C7D01EBF2D0E008C569A /* app in Resources */,
5AA9DBB81EB9F58B00EF7CC9 /* Assets.xcassets in Resources */,
5AA9DBF21EBA7C0700EF7CC9 /* ENOBrowserWindowController.xib in Resources */,
5AA9DBBB1EB9F58B00EF7CC9 /* MainMenu.xib in Resources */,
Expand Down Expand Up @@ -385,6 +356,7 @@
5AA9DBC11EB9F58B00EF7CC9 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
Expand Down
58 changes: 54 additions & 4 deletions Electrino/Electrino/ENOJavaScriptApp.m
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,47 @@ - (id)init
[weakSelf _jsException:exception];
};

self.jsContext[@"require"] = ^(NSString *arg) {
id module = weakSelf.jsModules[arg];
return module;
self.jsContext[@"require"] = ^(NSString *arg) {

NSString *appDir = [[[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"app"] stringByAppendingString:@"/"];

if ([arg hasSuffix:@".js"]) { // If a javascript file is being directly referenced
JSContext *tmpContext = [weakSelf newContextForEvaluation];

[tmpContext evaluateScript:[NSString stringWithContentsOfURL:[NSURL fileURLWithPath:[appDir stringByAppendingString:arg]] encoding:NSUTF8StringEncoding error:NULL]];
return (id)[tmpContext objectForKeyedSubscript:@"exports"]; // Casted to id as the compile doesn't like multiple types of return values when no return value is specified
} else if (weakSelf.jsModules[arg] != nil) {
id module = weakSelf.jsModules[arg];
return module;
}

BOOL isDirectory;
BOOL doesExist = [[NSFileManager defaultManager] fileExistsAtPath:[appDir stringByAppendingString:arg] isDirectory:&isDirectory];
if (doesExist && isDirectory) {
// Find where the starting point is within package.json
NSData *packageJSON = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:[[appDir stringByAppendingString:arg] stringByAppendingString:@"/package.json"]]];
if (packageJSON == nil) {
return (id)nil;
}
NSDictionary *packageDictionary = [NSJSONSerialization JSONObjectWithData:packageJSON options:0 error:NULL];
if (packageDictionary == nil || packageDictionary[@"main"] == nil) {
return (id)nil;
}
NSString *mainJSFile = packageDictionary[@"main"];
NSURL *fileURL = [NSURL fileURLWithPath:packageDictionary[@"main"] relativeToURL:[NSURL fileURLWithPath:[appDir stringByAppendingString:arg]]];
mainJSFile = [@"/" stringByAppendingString:mainJSFile];
NSString *jsFileContents = [NSString stringWithContentsOfURL:fileURL encoding:NSUTF8StringEncoding error:NULL];

JSContext *tmpContext = [weakSelf newContextForEvaluation];

[tmpContext evaluateScript:jsFileContents];
return (id)[tmpContext objectForKeyedSubscript:@"exports"]; // Casted to id as the compile doesn't like multiple types of return values when no return value is specified

} else {
// Module doesn't exist!
}
return (id)nil;

};

self.jsContext[@"process"] = [[ENOJSProcess alloc] init];
Expand All @@ -88,6 +126,18 @@ - (id)init
return self;
}

// Create a new context for just evaluating the file
// ISSUE: JSContext does not include -copyWithZone: method, so we have to manually copy the required methods.
-(JSContext*)newContextForEvaluation
{
JSContext* newContext = [[JSContext alloc] initWithVirtualMachine:self.jsVM];
newContext[@"require"] = self.jsContext[@"require"];
newContext[@"process"] = self.jsContext[@"process"];
newContext[@"console"] = self.jsContext[@"console"];
[newContext evaluateScript:@"var exports = {};"]; // Evaluated so the developer doesnt have to
Copy link

@gimenete gimenete May 8, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First, great work. This is a big step forwards.

To mimic Node.js behavior you should inject a module object that has an exports object initialized as an empty object. Pseudo-code:

module = @{ @"exports": @{} }
newContext[@"module"] = module;
newContext[@"exports"] = module.exports;
// 1.- evaluate
// 2.- pick the evaluated exports
return (id)[tmpContext objectForKeyedSubscript:@"module.exports"];

This way these two ways of populating exports will work:

exports.something = foo
module.exports = somethingCompletelyNew

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good! Just a node I originally used exports due to this article.

I've done a bit more research on this, and it does seem a bit confusing about the whole exports vs module.exports.

Regarding your suggested changes, how would setting exports also change module.exports? Unless I'm missing something, we are only exposing module, unless you propose that we leave both, and we check which variable is populated...

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, my mistake. It should be

newContext[@"exports"] = module.exports;

I've changed it in the example. I'll try to help more on this later.

return newContext;
}

- (void)dealloc
{
self.jsContext.exceptionHandler = NULL;
Expand Down Expand Up @@ -129,7 +179,7 @@ - (BOOL)loadMainJS:(NSString *)js error:(NSError **)outError
}
return NO; // --
}
NSLog(@"%s done", __func__);

return YES;
Expand Down
File renamed without changes.
9 changes: 8 additions & 1 deletion Electrino/test-app/main.js → Electrino/app/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ console.log("hello world starting, app is: ", app);
function createWindow () {
// Create the browser window.
win = new BrowserWindow({width: 800, height: 600})

console.log("createWindow", BrowserWindow, win);

// and load the index.html of the app.
Expand All @@ -23,6 +23,7 @@ function createWindow () {
slashes: true
}))


// Emitted when the window is closed.
win.on('closed', function(){
// Dereference the window object, usually you would store windows
Expand Down Expand Up @@ -53,3 +54,9 @@ app.on('activate', function(){
createWindow()
}
})

const module = require('module.js')
module.custom_function();

const module_folder = require('module_folder')
module_folder.custom_function();
5 changes: 5 additions & 0 deletions Electrino/app/module.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
function custom_function() {
console.log("Function from a module!");
}

exports.custom_function = custom_function;
5 changes: 5 additions & 0 deletions Electrino/app/module_folder/lib/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
function custom_function() {
console.log("Function from a module within a folder!");
}

exports.custom_function = custom_function;
6 changes: 6 additions & 0 deletions Electrino/app/module_folder/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "module_folder",
"version": "0.0.1",
"description": "Testing modules in folders!",
"main": "./lib/main.js"
}
File renamed without changes.