Skip to content
This repository has been archived by the owner on Aug 2, 2022. It is now read-only.

Integration with UI frameworks: packaging and classes #16

Closed
pioneer opened this issue Oct 23, 2014 · 41 comments
Closed

Integration with UI frameworks: packaging and classes #16

pioneer opened this issue Oct 23, 2014 · 41 comments

Comments

@pioneer
Copy link
Collaborator

pioneer commented Oct 23, 2014

Note: This issue was posted first here - chiquitinxx/grails-grooscript#1. This is just by occasion, so it was closed there and reopened here.

Introduction.

There are several UI frameworks on JavaScript market, such as Dojo Toolkit, Ext JS, Qooxdoo, Google Closure Library. All of these listed products provide two important means, which are absent or have no estabilished standard in pure JavaScript: packages and classes. The purpose of this feature request is to add as seamless as possible configurable and pluggable integration of Grooscript with specific packaging and classes mechanisms present in a framework which is chosen for a project.

This will significantly help in writing large client applications using such a pleasant language as Groovy is, by reusing a lot of well-proven JavaScript code with huge amount of already implemented components and tooling.

Please note: I'm still a beginner in Groovy (mostly with 6 years of Python/JavaScript experience), so some of my suggestions can be naive.

Packages.

Problem: there are many packaging systems in JS world: AMD, NodeJS-style require, Ext.require, goog.require etc. Some of the languages which compile to JavaScript introduce their own packaging (GWT, Scala-JS, Dart) or force to use an existing one (CoffeeScript, ClojureScript). When using a compiler and UI framework which have different packaging systems, it's always tricky to do integration, combine both paradigms etc.

Suggested solution: configurable and pluggable packaging system for a compiler, so JS code can be easily imported and used within Groovy, as well as Groovy code within JS, using single selected packaging system, which is the most comfortable to use in given project.

Ideal implementation: import some.package.name.Class in Groovy code translates to require('...'), or Ext.require('...'), or AMD construct; package some.package.name translates to goog.provide("...") or whatever.

Possible implementation: maybe annotations, like @Grab.

Potential tricky things: AMD allows to "import" not only JS code, but also modules which should just be loaded (i.e. ensuring DOM is loaded), or even requiring an HTML template as if it's a module (AFAIK Groovy doesn't allow to do something like import dojo.text("./templates/SomeWidget.html") as template).

Classes.

Problem: in JS world, there are no classes, but OOP concept is still very usable thing. That's why many JS frameworks introduce their own OOP implementations, as well as some languages which compile to JavaScript, including Groovy. When using multiple implementations of objects and classes in one project, it may become very clumsy to deal with both styles (consider an example of creating a Dojo Toolkit/Ext JS/Qooxdoo widget completely in Groovy).

Solution: I don't really know what'll be the best way. Maybe a set of intermediate integration libraries, each for specific framework (i.e. Ext JS), which contain special Grooscript class (maybe classes?) with the following characteristics:

  • should be the wrapper for any existing JS class from the framework, allowing to reuse all the code from given class and all its parents, Groovy way
  • should be the base for any Groovy class intended to be used along with given JS framework
  • if used as a base for a component, written in Groovy, should be able to export itself to JS in given OOP style

Note: due to very different OOP implememtations, I doubt about an idea to replace Grooscript classes implementation with one from a JS framework, even in pluggable way, because of different logic behind Groovy's OOP and other OOPs. However, in ideal world it would be really beautiful to just import a JS class in Groovy or import some Groovy class in JS.

@pioneer
Copy link
Collaborator Author

pioneer commented Oct 23, 2014

The main idea is to have JS classes and packages to be first-class citizens in Grooscript code. I'll try to post some code examples.

Example 1 -- Create a widget in its own package, using Dojo Toolkit

build.gradle:

plugins {
  id "org.grooscript.conversion" version "0.6"
}

grooscript {
    source = ['src/main/groovy',
    ...
    ]
    sourceNative = ['src/lib/dojotoolkit', // Dojo Toolkit's JavaScript code goes here'
    ]
    packageSystem = 'AMD'
}

src/main/groovy/com/mycompany/myproject/SomeButton.groovy:

import org.grooscript.dojotoolkit.annotations

@AMDPackage
import dijit.form.Button


@DojoToolkitClass
class SomeButton extends Button{ 
    def label = "Some Button"

    @GsNative
    def logMessage = {/*
        console.log("I was clicked!");
    */}

    def onClick = {
        logMessage()
        super.onClick()  // @DojoToolkitClass converts superclass calls to this.inherited(arguments)
    }
}

Generated JavaScript:

define([
    "dojo/_base/declare",
    "dijit/form/Button"
], function(declare, Button){
    return declare("com.mycompany.myproject.SomeButton", Button, {
        label: "Some Button",
        logMessage: function() {
            console.log("I was clicked!");
        },
        onClick: function(evt){
            this.logMessage("I was clicked!");
            this.inherited(arguments);
        }
    });
});

Note: I haven't got clear vision on how exactly the transformation should work in this case, mostly due to DojoToolkit's classes are quite different from Groovy ones. Any ideas are welcome. Will add more examples soon.

@chiquitinxx
Copy link
Owner

Nice example. I think use js UI frameworks can be awesome addiction. But I don't know the future of that frameworks, javascript is progressing very fast and I don't know if people prefer use this one's or others. So I think the problem is javascript itself, lot of implementations, no standards. So for this enhancement the first thing to do is choose one of them. What is the more used UI framework this days?

In the Dojo example, I think the conversion is the easy step. The @DojoToolkitClass can be used to apply a 'special' conversion using some kinda conversion hooks and templates. But, in the Groovy class, where is Button? are you thinking in create base or abstract classes? Create that java code can be boring.

I think can be a good idea explore the GWT, and try use it. We have the java code, mature project and good java - js interaction. What do you think?

@pioneer
Copy link
Collaborator Author

pioneer commented Oct 29, 2014

But I don't know the future of that frameworks, javascript is progressing very fast and I don't know if people prefer use this one's or others.

So for this enhancement the first thing to do is choose one of them. What is the more used UI framework this days?

No estabilished preferences, AFAIK. Some old parties use large libraries, such as ExtJS, some modern-style smart guys use React. My idea is mostly to have tools to integrate all this stuff seamlessly, rather to focus on specific library.

But, in the Groovy class, where is Button?

Button is a piece of classy JavaScript code from Dojo Toolkit, see here https://github.com/dojo/dijit/blob/master/form/Button.js. It's not a Groovy class, but my idea is to have tools which make these Dojo classes (as well as Ext, Qooxdoo etc.) look and feel like Groovy ones.

I think can be a good idea explore the GWT, and try use it.

Hey, you said here - chiquitinxx/grails-grooscript#1 - that

I think convert from groovy to java, and then to js, not a good idea.

I have no estabilished opinion on that, but moving to GWT will definitely require a significant change of the Grooscript compiler, to be able to convert Groovy to Java source instead of JavaScript one. I believe it is a decision which changes the way of Grooscript project, so it should be done with care.

@pioneer
Copy link
Collaborator Author

pioneer commented Oct 29, 2014

For GWT, I think it may be really not a bad idea, even though there will be two compilation steps - from Groovy to Java source, and then from Java source via GWT compiler to JS. I think so because of Java -> JS phase is highly optimized thing, after years of GWT evolution.

Talking about JS frameworks, I see two general approaches, and we should choose one of them.

  1. Seamless integration, as I told before. Requires a bit of magic, a bit of as much universal as possible approach, a lot of thinking and prototyping.
  2. Separation of JS and Groovy worlds, keeping stuff like Dojo classes as alien objects. Some import and export tooling are required in this case.

@pioneer
Copy link
Collaborator Author

pioneer commented Oct 29, 2014

Can you please answer on two questions below, in order for me to understand possible technological impediments?

  1. (in case of choosing compile-to-Java way) How hard it'll be to reimplement Grooscript compiler to produce Java source instead of JavaScript?
  2. How hard it'll be to implement pluggable packaging support?

@glaforge
Copy link

I wouldn't go to/through Java, and even less so through GWT, an almost dead technology!
It would make the code base more complex for not much good benefit, more time-consuming for transpiling throughout two languages.
Avoid at all cost :-)

@chiquitinxx
Copy link
Owner

Agree with Guillaume, not the best idea use a dead tech. Anyway:

  1. Grooscript can't generate Java code, and forget about it -:) There are some tools that convert Groovy code to Java, for example IntelliJ Idea can do. But GWT is java, and Groovy is a jar that you can use in your Java project. Dunno GWT restrictions generating js code to use any external Jar's.
  2. For the pluggable packaging support, what do you think about:
import org.grooscript.dojotoolkit.annotations

@DojoToolkitClass(extends='Button', package='dijit.form')
class SomeButton { 
    def label = "Some Button"

    @GsNative
    def logMessage = {/*
        console.log("I was clicked!");
    */}

    def onClick = {
        logMessage()
        super.onClick()  // @DojoToolkitClass converts superclass calls to this.inherited(arguments)
    }
}

Using the @DojoToolkitClass to 'special' conversions and generate the result js file. With this approach you don't have to worry about the js code, just worry about the right generation of js code. That is easy.

I think can be more interesting give support to create custom components using react, polymer or similar new tools. See this example view code

@pioneer
Copy link
Collaborator Author

pioneer commented Oct 30, 2014

Guys, would you like to create a Google Group for such kind of discussions? It seems our talk extends usual ticket-related conversation.

@pioneer
Copy link
Collaborator Author

pioneer commented Oct 30, 2014

Packaging support can be split into two pieces.

Note 1: I consider it's too hard to mix different packagind systems in a single project, so one of them (i.e. AMD, Google Closure) should be chosen at project's start and then used across all the code, including both Groovy and JavaScript.

Note 2: I may be wrong, and it may be even better to split Groovy and JavaScript worlds completely, using some adapter code.

Import a piece of code from JS library and use it within Grooscript.

Consider the following JavaScript code:

require(["dojo/cldr/monetary"], function(cldrMonetary){
    var isoCodes = ["EUR", "USD", "GBP", "JPY"];
    for(var i=0; i<isoCodes.length; i++){
        var isoCode = isoCodes[i];
        var cldrMonetaryData = cldrMonetary.getData(isoCode);
        console.log("Places: " + cldrMonetaryData.places);
        console.log("Round: " + cldrMonetaryData.round);
    };
});

It would be good to be able to do the equivalent job in Groovy like this:

import dojo.cldr.monetary


["EUR", "USD", "GBP", "JPY"].each {
    def cldrMonetaryData = dojo.cldr.monetary.getData(it)
    println "Places: $cldrMonetaryData.places"
    println "Round: $cldrMonetaryData.round"
}

Another example:

goog.require('goog.crypt.base64');


var input = "Lorem ipsum dolor sit amet";
var output = goog.crypt.base64.encodeString(input);
console.log("Original string: " + input);
console.log("Encoded base64 string: " + output);

It would be good to use the same things in Groovy like below:

import goog.crypt.base64.encodeString


def input = "Lorem ipsum dolor sit amet"
def output = goog.crypt.base64.encodeString(input)
println "Original string: $input"
println "Encoded base64 string: $output"

Export a piece of Groovy code to use in JavaScript directly, or just provide Groovy code to JS library in a way which this library requires.

An example with @DojoToolkitClass annotation could serve for this purpose. I'll think for more examples, just wanted to deliver these thoughts faster.

Any thoughts are welcome!

@pioneer
Copy link
Collaborator Author

pioneer commented Oct 30, 2014

React and Polymer are really amazing projects. Though, on my opinion, they don't provide full-stack frontend solutions. Anyway, I highly discourage to consider Grooscript as a thing which is tightly bound to some framework. Instead, it would be great to have a Swiss Army knife tool.

@chiquitinxx
Copy link
Owner

Just one conversation, I prefer we continue in Github. I like grooscript as a Swiss Army knife to work with js :)

I think last examples can be a good start, use js function in your Groovy code.

a. Groovy code has to compile and run, the js code runs nothing in groovy environment.
b. In js, converted code will run groovy and js code.

Some solutions:

First. Maybe we can run this code in a baseScript environment. Offering functions to work with this ui frameworks.

@groovy.transform.BaseScript DojoScript dojoScript

use ('dojo.cldr.monetary')

["EUR", "USD", "GBP", "JPY"].each {
    def cldrMonetaryData = dojo.cldr.monetary.getData(it)
    println "Places: $cldrMonetaryData.places"
    println "Round: $cldrMonetaryData.round"
}

That DojoScript will offer a dojo.*, with expandos or maps or classes with property missing and method missings. Also 'use' function to correct generate js file, we can add more interesting functions. Then, we don't have to create any groovy code, just nothing will run and nothing will return. We can improve DojoScript in the future adding better groovy behavior like return something on functions or create interfaces.

If I have time this weekend will try create this dojo example.

Second. Maybe a mix approach.

def monetary = org.grooscript.dojo.use('dojo.cldr.monetary')

["EUR", "USD", "GBP", "JPY"].each {
    def cldrMonetaryData = monetary.getData(it)
    println "Places: $cldrMonetaryData.places"
    println "Round: $cldrMonetaryData.round"
}

That use static function can return an class with the correct interface or just a expando / map.

Third. Create all the interfaces / classes to import and use in groovy. Boring and too much work.

Thoughts?

I think we can begin with one of this approach and see hows going. Later we can go on use UI objects.

@pioneer
Copy link
Collaborator Author

pioneer commented Oct 31, 2014

For third approach,

Third. Create all the interfaces / classes to import and use in groovy. Boring and too much work.

I believe we can even go through Dojo source and autogenerate some of the wrappers. Can you give me an example of a wrapper class / interface, in order for me to estimate such kind of work?

@pioneer
Copy link
Collaborator Author

pioneer commented Oct 31, 2014

For first and second approaches, it seems you suggest mostly separating JS and Groovy worlds in these cases. Thus, here we have Grooscript packaging support unsolved. In another words, just leaving it in its present state, with manual adding of each js/app/Foo.js, js/app/Bar.js to HTML headers in correct order which should respect dependencies, and this order currently is just controlled manually by eyes and hands.

I believe we will need to solve that problem earlier or later. Dependency management is sometimes a complex thing. Willing to have a Swiss Army knife, I don't see any better approaches rather than to have a flexible packaging support, i.e. through some options in build.gradle. So that it would be possible to use, for specific project, whether goog.require or require(["package/name"], function(packageName){...}) or Node.js-style require or whatever. Or even leave current manual approach as one of the options.

Besides of resolving dependencies, packaging subsystem should also be able to gather all JS into a single file and minify it for deployment. AFAIK at least Google Closure and Dojo can do such kind of things, and they definitely can be automated via Gradle. I'll provide you with quick overview of capabilities of these packaging systems soon.

Anyway, I personally think all of this stuff should be flexible, allowing to configure whatever we need to configure, with reasonable defaults.

@chiquitinxx
Copy link
Owner

I have created branch 'packages', you can see the 1c45e65 Still have to add some hooks in conversion process to handle that packages.

I like the 'use' function to add external packages, can be used with any framework, in 'google'. Also take a look at 'google2' where I created the 'base64' that is the kinda classes to be generated to use js functions.

Agree to make this as many flexible as possible. I prefer separating JS and groovy aye. Use js libraries in easy way in groovy, then the user by hand or with gradle add js libraries for execute the code in js. Waiting for your suggestion :)

@pioneer
Copy link
Collaborator Author

pioneer commented Nov 3, 2014

So then it looks like you're suggesting to use use() for getting an object or function from JS package. This is a total separation of concerns approach, and I like more seamless way. But, it's a serious thing, and I believe it should be proven by practice, and I don't know the right answer (even though I have personal preferences of course). So, for now, I think it's a good start, it can serve as a base for more advanced solutions.

My suggestions: implement two functions, use() and export() (names are discussable), which will be able to obtain JS objects and provide Grooscript objects from and to Javascript respectively.

Talking about use() - it should support multiple packaging systems. Need your suggestions where it the best place to put the configuration of which packaging system to use - function parameter, Gradle configuration, something else? As a starter project, being Groovy newbie, I can implement it, with architecturing suggestions about where to put its config.

Function export() is a bit more tricky thing (see example with @DojoToolkitClass above), so I will not touch it before I've got enough experience.

@pioneer
Copy link
Collaborator Author

pioneer commented Nov 3, 2014

Anyway, thus we're solving only a problem of how to deal with packages from JS world. What are your thoughts about how to manage Grooscript packages themselves? I don't think it's a good idea to always put all js/app/Foo.js, js/app/Bar.js etc. to HTML header manually, and keep in mind their correct order of inclusion.

@chiquitinxx
Copy link
Owner

Please if you have something in mind, share it :) I just think this 'use' approach can be a good start. But as you say, we have to think in the future. Be flexible, and how to export packages, can be better with other approach. I'm just thinking in small changes and see them.

So, first, we are going to use functions from external js libraries. I'm thinking in google because is a very easy conversion. So, when we have:

def goog = use('goog.crypt.base64')

That give us two things. No errors compiling code because goog.* is fine. And information for the conversion: the 'use' have to be changed to 'goog.require('goog.crypt.base64'):' and where 'goog.crypt.base64' is used we have to do any 'special' conversion.

Agree that 'use' can be used by other libraries, for example 'def dojo = use("dojo.cldr.monetary")'.

Grooscript itself have some conversion options, to configure conversion. That conversion options can be configured in the gradle plugin also. We can add a new conversion option 'useJsLib' where you can put 'dojo' or 'google',.. We can start with 'google' for example, but I thought was a big js, and it's a lot of js's.

I think this can be a good start, and see how's going. This week I'm a bit busy, but I'm happy to start something next week.

I can give you permissions if you want to commit something in the branch. Also you can email me if you want to make a 'virtual meeting' or something.

@chiquitinxx
Copy link
Owner

I did this commit

Was easy to make conversions. The code of the 'specific package' conversion isn't in the right place, I think we can create an interface or something, and during conversion handle that conversion events properly.

I hope you get the idea and tell me what you think. At least is easy for google, can be more complex for other packages.

@pioneer
Copy link
Collaborator Author

pioneer commented Nov 12, 2014

I've prepared a sample for requiring a Google Closure library's function and Dojo's one, will commit it soon when I'll get it stable.

@pioneer
Copy link
Collaborator Author

pioneer commented Nov 13, 2014

Please take a look here - https://github.com/pioneer/grooscript-packaging. Try to open index-goog.html and index-dojo.html files in your browser. Seems that both Google Closure and Dojo implementations work correctly there. It's in a separate repo, due to I still not so familiar with Grooscript code and I'm not sure how it'll be better to integrate my code to it.

Looked to your commit, you used different approach with AllAllowedClass - nice! Though I think it's a bit hard-coded to use goog.require. Have you tested it? In both Goog and Dojo packaging, modules are loaded asynchronously, so usually it's not possible to require() a library and use it immediately. My approach is, for now, to require libraries at the beginning of the file, and use them within a method, such as onclick. When you try to require and use at the same time, there's a high chance that, at the time of use is called, module is still loading and thus not available.

Asynchronous load of modules, from my side, looks not in the Groovy style, so I consider this as an issue - what do you think about it? How it can be overcome?

Also, any feedback regarding to my code is welcome.

By the way, it was sometimes annoying to maintain correct <script src=""> tags in HTML files, I needed to always keep in mind an order of these scripts, and not to forget if I added a new file. So, I believe, eventually we should utilize some good packaging in Grooscript.

@pioneer
Copy link
Collaborator Author

pioneer commented Nov 13, 2014

Things I especially want to get a feedback, as a Groovy newbie:

  • naming things - classes, methods etc. - do they fit into Groovy traditions?
  • there's a trick within DojoPackageManager - I use gs.mc method call inside Javascript code -- can it be done in more nice way?
  • I'm not sure that PackageManagerFactory is a good thing - too much code, maybe it's better to use static properties and methods, or traits? -- anyway, it seems there's minimal support for static classes with inheritance

@pioneer
Copy link
Collaborator Author

pioneer commented Nov 13, 2014

Looked at your commit again, I'll try to test the Javascript it generates. The main problem which is needed to solve is that some packaging systems (including Google Closure and Dojo) are asynchronous, so that the library which was just required using useJsLib or packageManager.require() might be not available immediately.

@chiquitinxx
Copy link
Owner

Hello! Thank you for your time, you are helping to improve grooscript. Keep it up :)

First, about your code. Your class and method naming is ok, groovy hasn't special naming for that, use the same as for example Java. If you have a variable registry, you don't have to create getRegistry() { registry }, in groovy you don't have to create getter and setters if you don't want it; groovy offer a free getRegistry() that do the same. You don't have to use def always, def is the same that Object. I usually prefer put the type, or put nothing if possible like static getPackageManager(). Also, I don't see much abstract classes, like groovy is dynamic, you don't have to create interfaces or bases classes; no bad if you use them. You use good naming, and groovy, without all that ceremony, reading code is very easy and clear.

use is a function in groovy, I think we better name require or something similar.

About gs.mc, you don't have to use it, you can use gSobject.storeModule(packageSpec, module) or even this.storeModule(packageSpec, module) can work. gs.mc is used in conversion for method calls, like groovy is dynamic, that method can be in 50 places :) but where you use it, you know the method is in the class.

I have more ideas, but, as you say, we have to solve that asynchronous functions. That load code in the fly isn't ideal, I prefer libraries with all the code in the js, all this 'js packages' use this approach?. We can use something like this:

static require(String package, Closure closure) {
  waitPackageLoaded() //this can be just send the closure as callback in dojo
  closure()
}

require('goog.crypt.base64') {
  def input = "Lorem ipsum dolor sit amet"
  def output = goog.crypt.base64.encodeString(input)
  println "Original string: $input"
  println "Encoded base64 string: $output"
}

I see that Dojo is doing the same, so conversion will fit perfectly. I don't know in goog, do you know it?

About <script src="">, I agree is boring. I still have no idea how to manage with our packages :/ From grooscript 0.6.1 you can define a file as destination of your conversions. In gradle plugin 0.7, but beware Binder has been removed. You just have to add aFile.js in html.

grooscript {
  source = ['src/main/groovy', 'src/main/groovy/packaging']
  destination = 'aFile.js'
}

I think your code is ok, but I prefer my code to start. Mine, has less 'package code' and has more complex in conversions, but generated code not need extra js libs to run. What do you think?

@pioneer
Copy link
Collaborator Author

pioneer commented Nov 16, 2014

Thanks for your feedback! Your require function looks like the same idea of requiring Javascript libraries as alien objects, but with more concise syntax. It does not matter, in my opinion, which approach to use, it's more important to decide whether we stay on a way where JS libraries are foreign stuff, or they are first-class Grooscript citizens and can be imported through the same mechanism as other Grooscript files. I've thought about it and I personally think that import jquery is much more better than def jquery = useJsLib('jquery').

Thus we solve two problems at once - 1) Grooscript dependency management 2) importing external Javascript libraries.

Underlying mechanism, in my opinion, of course should be configurable.

What do you think?

@chiquitinxx
Copy link
Owner

I prefer use js libraries as foreign stuff. You can use dynamic groovy capabilities :)

Three reasons to not use that 'imports': a lot of work, who will maintain that libraries and introducing a lot code to be mocked / stubbed for tests.

My last chance to convince you, with this code:

require('goog.crypt.base64') {
  def input = "Lorem ipsum dolor sit amet"
  def output = goog.crypt.base64.encodeString(input)
  println "Original string: $input"
  println "Encoded base64 string: $output"
}

You don't have to define goog, can be injected in require closure and it doesn't fail compiling.

If you still prefer 'import' approach, is ok, I'll try create a demo to compare with yours.

@pioneer
Copy link
Collaborator Author

pioneer commented Nov 17, 2014

I still prefer 'import' approach, due to on a backend we're able to import any Java class. Why not be able to do the same stuff on Javascript platform?

Talking about your require() { ... closure .. } approach - I think it's one of the best syntaxes if considering Javascript libraries as foreign things. It's clear, concise, and does not provide too much entities that regular developer should keep in mind (compare with mine approach, where a developer was obliged to think about a package manager factory, when his goal was only to require something). However, my idea was to have such kind of syntax on top of classes I wrote.

Which technical impediments I met while developing my piece of code? First, as I've said, asynchronous module loading. Google Closure Library loads a module by adding <src> tags, so, for now, I'm not sure how to catch the event when a module was actually loaded. Dojo uses require(..., function(...) { ... }) approach -- exactly as in your Groovy example above, so, in Dojo we can be sure that the code which requires some library, will be executed really after module loading. And it seems that this approach needs to be somewhat generalized, as there's no sense to support only Closure and Dojo.

Second, the place where loaded modules are put. Google Closure puts them into global namespace, whereas Dojo keep them isolated. That's why I introduced registry, as the place to store loaded modules in packagesystem-independent way. Please take this into account.

@chiquitinxx
Copy link
Owner

Hello!

I have commited the dojo demo. Commits 1 2

I have tried in a browser the conversion and works fine :)

I have used distinct approach to connect both worlds. I don't use import static, I use a variable 'dojo' using classes, works pretty well with @DelegatesTo if you use an IDE. In intelliJ IDEA autocompletes.

You can run the groovy script and works in groovy environment, good for testing :)

I think to solve the async module loading, we have to use this closure approach. Between use static functions or this variable approach, you decide, I don't like them, I prefer foreign worlds :) As you have seen, I don't convert any classes to js, in the conversion process I generate js code that only need grooscript.js to run. I don't use any registry or something, javascript code has to manage it.

So, what do you think? can we start with dojo functions? do we missing something?

@pioneer
Copy link
Collaborator Author

pioneer commented Nov 29, 2014

Sorry for delay. I need some testing against that, but, from the first impression, this solution can work. The only thing I have no clear idea is why there are some very specific things like monetary data or base64 encoding stuff? I believe they are very specific to its libraries, and should be imported from respective libraries (i.e. from Dojo or Google Closure), not hardcoded in Grooscript. Why they are here?

@pioneer
Copy link
Collaborator Author

pioneer commented Nov 29, 2014

Can you also please tell - what's your vision on how to organize compiled Javascript files into packages? I strongly believe that existing manual including js files is a temporary solution, isn't it?

@chiquitinxx
Copy link
Owner

I don't know what you mean about base64 or monetary. The groovy implementations that I coded aren't important, because only used in groovy. In javascript own library code is used.

Aye, no idea how to package compiled files. Have no standards in javascript doesn't help. Do you have a suggestion?

@pioneer
Copy link
Collaborator Author

pioneer commented Dec 4, 2014

Short answer: RequireJS.

Long answer: I still think we should not rely closely on specific implementations like RequireJS or CommonJS or whatever. But, not inventing the wheel, from my point of view, is much more important. So, why I think RequireJS is a good thing for our purposes:

  • It's a library which is ready to use
  • It's well-known and tested
  • It has clear syntax and clear internals
  • It looks like one of community-accepted de-facto standards
  • My frontend dev colleagues say that they were really happy using RequireJS
  • It's on Javascript side, not Grooscript, so, it's even possible to import jquery using it
  • Ceylon language, which compiles both to JVM and Javascript, decided to use RequireJS as a base for their Javascript backend
  • It has adapter for Node.js/CommonJS, so it's possible to import Node modules seamlesslly in server-side Grooscript code
  • RequireJS is actually an implementation of AMD, we can easily switch to another AMD backend, such as from Dojo Toolkit or curljs, if needed

@chiquitinxx
Copy link
Owner

I like requireJs, I have used it to include js files generated with grooscript.

@pioneer
Copy link
Collaborator Author

pioneer commented Dec 9, 2014

I'm still not so proficient in Groovy to implement this RequireJS (or whatever) support. So, let it will serve as an item from my wishlist. If you know which dirty work can I do to help you, please let me know.

@chiquitinxx
Copy link
Owner

Can you show me an example in javascript side? I mean, some groovy classes, using a javascript package like dojo, and how to join all with requireJs. I have used requireJs to include files, not for create modules for it, I don't know how to use classes with it

@pioneer
Copy link
Collaborator Author

pioneer commented Dec 9, 2014

I'll create a repo soon and give you a link.

@pioneer
Copy link
Collaborator Author

pioneer commented Dec 13, 2014

Please take a look here - https://github.com/pioneer/grooscript-require. I added a piece of code along with explanations of how Grooscript compiler can be improved with RequireJS. What do you think?

@chiquitinxx
Copy link
Owner

I like it a lot :) I think we can configure things in the build.gradle

grooscript {
  //I think is not needed, is the default option
  classPath = ['src/main/groovy']
  //So we can configure require to find js files here
  destination = 'src/main/webapp/js/app'
  require {
    //With this, we don't have to define source
    //conversion of main.groovy have to be clever and convert dependencies
    entryPoint = 'main'
    //Where entry point must be saved, to be used in the html
    destination = 'src/main/webapp/js/main.js'
  }
}

I can start a first demo of this feature. The hardest thing is to solve dependencies, first with groovy dependencies, later we will see external / js dependencies.

Anyway I'm busy with documentation and prepare 1.0 release. All this stuff will be delayed to next versions, but I think is very interesting. Do you prefer start with this require improvement or use external libraries as Dojo / Google?

@pioneer
Copy link
Collaborator Author

pioneer commented Dec 14, 2014

I think this require improvement is much more important. I believe first we should be able to deal better with Groovy code, and only after that it's makes sense to pay attention to outside JS world. Can I help you with docs?

@chiquitinxx
Copy link
Owner

Yeah sure :) I have added info in #21

@chiquitinxx
Copy link
Owner

Added an Ast to include other require.js modules. In version 1.1.1, released today. More info in http://grooscript.org/doc.html#_add_javascript_dependencies

@chiquitinxx
Copy link
Owner

I'm going to close this issue. It seems with this require.js improvement is enought. With the number of packaging solutions out each day in javascript, I think we lost time supporting a specific package system. Feel free to reopen or create a new issue if you miss something or need an improvement in require.js support.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants