Skip to content

Any way to get to module.exports? #56

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

Closed
dmitshur opened this issue Jun 22, 2014 · 14 comments
Closed

Any way to get to module.exports? #56

dmitshur opened this issue Jun 22, 2014 · 14 comments

Comments

@dmitshur
Copy link
Member

Hi,

I have very little knowledge about JavaScript/Node.js/Atom packages, so take this question with a grain of salt.

It seems one can currently "export" a Go func to be accessible to the external JavaScript environment, say, on a webpage, by doing this:

package main

import "github.com/gopherjs/gopherjs/js"

func Foo() string { return "hello from Go" }

func main() {
    js.Global.Set("Foo", Foo)
}

The js.Global.Set("Foo", Foo) line ends up producing $global.Foo = $externalize(Foo, ($funcType([], [$String], false)));, and $global is set to window for a web page.

However, I am looking to be able to have something equivalent to this in the generated javascript, in order to be able to require() it:

module.exports.Foo = $externalize(Foo, ($funcType([], [$String], false)));

Is there any way to achieve this with the current GopherJS?

If not, can a var Module Object be added to github.com/gopherjs/gopherjs/js so that it is possible?

@neelance
Copy link
Member

module is just a global variable. This means it is also accessible via $global.module, so you can do js.Global.Get("module").Get("exports").Set("Foo", Foo).

@dmitshur
Copy link
Member Author

Ok, I've tried that, which generates almost the code that I want:

$global.module.exports.Foo = $externalize(Foo, ($funcType([], [$String], false)));

But I get:

Uncaught TypeError: Object #<Object> has no method 'Foo'

I think this problem is specific to the JavaScript environment of Atom packages.

It seems to be similar to Node.js, but it has a few differences.

If I add some logging statements to the top section of the javascript:

if (typeof window !== "undefined") { /* web page */
    console.log("web page")
}
if (typeof self !== "undefined") { /* web worker */
    console.log("web worker")
}
if (typeof global !== "undefined") { /* Node.js */
    console.log("Node.js")
}

Then I see the following output with all 3 if statements being true:

image

So, despite me wanting to do module.exports (or global.module.exports), the Atom package environment is currently being detected as a web page and $global is set to window. But window.module.exports isn't the same as module.exports, hence it doesn't work.

What's strange is even if I modify the detection code manually to get it to use the Node.js path, i.e.:

var $global;
if (false && typeof window !== "undefined") { /* web page */
    console.log("web page")
    $global = window;
} else if (false && typeof self !== "undefined") { /* web worker */
    console.log("web worker")
    $global = self;
} else if (typeof global !== "undefined") { /* Node.js */
    console.log("Node.js")
    $global = global;
    $global.require = require;
} else {
    console.log("warning: no global object found")
}

To force $global to be global, it still doesn't work when I have:

$global.module.exports.Foo = $externalize(Foo, ($funcType([], [$String], false)));

But it does work if I manually change that to:

module.exports.Foo = $externalize(Foo, ($funcType([], [$String], false)));

So it seems for Atom packages, global variable does not contain module variable.

Another change I had to do to make it work was to replace all $global.eval(...); functions with a dummy function() {};. There were very few of them, like one for goexit and 2 more in my case. Otherwise, I was getting this error:

image

Any thoughts on whether you'd be willing to make some changes in order to support using gopherjs to convert Go functionality into exportable JavaScript functions that can be required and used within an Atom package?

Or, perhaps I should be making a request in Atom to have module variable be accessible from global (but then Node.js detection in topmost gopherjs generated javascript will still need to be modified).

So my current build process is:

  1. gopherjs build markdownfmt.go
  2. Replace all $global.module.exports. -> module.exports.
  3. Replace all $global.eval( -> function() {};//$global.eval(

That said, gopherjs worked amazingly well to turn my markdownfmt Go binary into a bunch of JavaScript, that I was then able to embed and use from within an Atom package in order to have zero external dependencies (and I can also use markdownfmt on web pages now). So that's an incredible real-world use for gopherjs, thank you!

@neelance
Copy link
Member

I really like the idea of using GopherJS for Atom packages. We can make that work! :-)

I am considering doing var $global = this; at the very top. This should work for web browsers and Node.js. Can you check if this solves the problem with Atom?

I'm taking care of the eval right now.

@dmitshur
Copy link
Member Author

Great! :D

I am considering doing var $global = this; at the very top. This should work for web browsers and Node.js. Can you check if this solves the problem with Atom?

I will try and let you know within 20 mins.

@dmitshur
Copy link
Member Author

I tried simply setting var $global = this; at the top, but that caused problems because of lines similar to math = $global.Math;, they stopped working.

So I tried keeping $global as is, and added var $global2 = this; instead.

Then I changed this:

module.exports.ProcessMarkdown = $externalize(ProcessMarkdown, ($funcType([$String], [$String], false)));

To:

$global2.module.exports.ProcessMarkdown = $externalize(ProcessMarkdown, ($funcType([$String], [$String], false)));

But it didn't work:

Failed to load package named 'markdown-format' TypeError: Cannot read property 'module' of undefined

@neelance
Copy link
Member

I have Atom on my machine. Can you tell me a bit about how I can test this?
I haven't looked into Atom's plugin system yet.

2014-06-22 22:23 GMT+02:00 Dmitri Shuralyov notifications@github.com:

I tried simply setting var $global = this; at the top, but that caused
problems like because of lines similar to math = $global.Math;.

Then I tried keeping $global as is, and added var $global2 = this;.

Then I changed my line:

module.exports.ProcessMarkdown = $externalize(ProcessMarkdown,
($funcType([$String], [$String], false)));

To:

$global2.module.exports.ProcessMarkdown = $externalize(ProcessMarkdown,
($funcType([$String], [$String], false)));

And it didn't work:

Failed to load package named 'markdown-format' TypeError: Cannot read property 'module' of undefined


Reply to this email directly or view it on GitHub
#56 (comment).

@dmitshur
Copy link
Member Author

Sure, let me make a very simple atom package that you can try this out with.

You will want to drop it in ~/.atom/packages/ folder.

@dmitshur
Copy link
Member Author

Okay, if you uncompress this zip into your ~/.atom/packages/ folder, I believe it should work. I just took my atom-markdown-format package and minified it to be a short test of whether GopherJS works.

https://dl.dropboxusercontent.com/u/8554242/temp/atom-gopherjs-test.zip

I've modified it so whenever you save any file type in Atom, it appends " GopherJS working" at the end.

@dmitshur
Copy link
Member Author

Also, before re-regenerating the .js file, take a look at the existing one and search for "// ATOM CHANGE". These are the manual changes I had to do after gopher build markdownfmt.go to make it work.

Note that you can use View -> Reload to reload the package after you make changes to it inside ~/.atom/packages/.

@dmitshur
Copy link
Member Author

Thank you @neelance!

That should completely remove the manual steps that I had to do after gopherjs build.

@neelance
Copy link
Member

Seems like Node's require is doing the following:

(function (exports, require, module, __filename, __dirname) {
     // your code is here
});

So module really is a local variable that you can not access via the global scope object. I now capture module at the beginning into the variable $module and that one is exposed to Go via js.Module.

@neelance
Copy link
Member

I'm glad I could help. :-)

@dmitshur
Copy link
Member Author

For reference, with the 4ececf2 commit of gopherjs, I changed my Go code to:

package main

import (
    "github.com/gopherjs/gopherjs/js"
)

func ProcessMarkdown(text string) string {
    output := ... // do stuff using Go code!
    return string(output)
}

func main() {
    js.Module.Get("exports").Set("ProcessMarkdown", ProcessMarkdown)
}

And running gopherjs build markdownfmt.go created markdownfmt.js which I was able to require and use from a .coffee file:

markdownfmt = require('./markdownfmt.js')

... = markdownfmt.ProcessMarkdown(...)

Confirmed everything working. Thanks again!

@yoo
Copy link

yoo commented Dec 30, 2014

I'm writing atom bindings for gopherjs. https://github.com/JohannWeging/atom is working with a minimal example. The API matches with the official Atom API.
It's only prof of concept with a hand full of functions.

@yoo yoo mentioned this issue Jan 3, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants