-
Notifications
You must be signed in to change notification settings - Fork 5
Tutorial
Let's create the mother of all tutorial-applications: a simple "Hello, World!"
Install the command-line tool via Homebrew so that you can easily run spaghetti from the command-line:
$ brew tap prezi/oss
$ brew install spaghetti
$ spaghetti version
Spaghetti version 2.0
Then create a new directory somewhere (we will call our created directory "spaghetti"), and start cracking.
We'll need a directory for each module, so let's create one for the first one:
spaghetti$ mkdir greeter-module
spaghetti$ cd greeter-module
Create a Greeter.module
file in greeter-module
:
module com.example.greeter
/**
* A simple greeter.
*/
interface Greeter {
string sayHello(string user)
}
Greeter createGreeter()
Now let's generate some TypeScript headers:
greeter-module$ spaghetti generate headers --definition Greeter.module \
--language typescript --output headers
You should see the following files in the headers
directory created:
headers
|-- GreeterModule.ts
`-- Spaghetti.ts
GreeterModule.ts
is the TypeScript manifestation of the Spaghetti API we just defined:
/*
* Generated by Spaghetti 2.0
*/
module com.example.greeter {
/**
* A simple greeter.
*/
export interface Greeter {
sayHello(user:string):string;
}
export class __GreeterModuleProxy {
createGreeter():com.example.greeter.Greeter {
return com.example.greeter.GreeterModule.createGreeter();
}
}
export function __createSpaghettiModule():any {
return new com.example.greeter.__GreeterModuleProxy();
}
}
Pro tip: You can add Javadoc-style comments to methods, interfaces, or even whole modules that are preserved in the generated code.
The role of Spaghetti.ts
is explained on the wrapping page.
Add a GreeterModuleImpl.ts
:
module com.example.greeter {
export class GreeterImpl {
sayHello(user:string):string {
return "Hello " + user + "!";
}
}
export class GreeterModule {
static createGreeter():com.example.greeter.Greeter {
return new GreeterImpl();
}
}
}
We will need the TypeScript compiler for the next step, so install it with npm install -g typescript
if you don't already have it.
Then, compile GreeterModuleImpl.ts
:
greeter-module$ tsc GreeterModuleImpl.ts headers/GreeterModule.ts \
headers/Spaghetti.ts --out Greeter.js
This creates a Greeter.js
file that should look something like this:
var com;
(function (com) {
(function (example) {
(function (greeter) {
var GreeterImpl = (function () {
function GreeterImpl() {
}
GreeterImpl.prototype.sayHello = function (user) {
return "Hello " + user + "!";
};
return GreeterImpl;
})();
greeter.GreeterImpl = GreeterImpl;
var GreeterModule = (function () {
function GreeterModule() {
}
GreeterModule.createGreeter = function () {
return new GreeterImpl();
};
return GreeterModule;
})();
greeter.GreeterModule = GreeterModule;
})(example.greeter || (example.greeter = {}));
var greeter = example.greeter;
})(com.example || (com.example = {}));
var example = com.example;
})(com || (com = {}));
var com;
(function (com) {
(function (example) {
/*
* Generated by Spaghetti 2.0
*/
(function (greeter) {
var __GreeterModuleProxy = (function () {
function __GreeterModuleProxy() {
}
__GreeterModuleProxy.prototype.createGreeter = function () {
return com.example.greeter.GreeterModule.createGreeter();
};
return __GreeterModuleProxy;
})();
greeter.__GreeterModuleProxy = __GreeterModuleProxy;
function __createSpaghettiModule() {
return new com.example.greeter.__GreeterModuleProxy();
}
greeter.__createSpaghettiModule = __createSpaghettiModule;
})(example.greeter || (example.greeter = {}));
var greeter = example.greeter;
})(com.example || (com.example = {}));
var example = com.example;
})(com || (com = {}));
Now we need to make our module into a reusable module bundle:
greeter-module$ spaghetti bundle --definition Greeter.module --language typescript \
--source Greeter.js --output bundle
This will create a directory called bundle
:
bundle
|-- META-INF
| `-- MANIFEST.MF
|-- module.def
`-- module.js
1 directory, 3 files
The module.def
file contains the module's definition. The module.js
file is almost exactly the same as Greeter.js
, except for the bundle's wrapping function:
module(function(Spaghetti) {
// ...
})
Now we have bundled our module, so we can create another one:
greeter-module$ cd ..
spaghetti$ mkdir runner-module
spaghetti$ cd runner-module
Let's create an executable module in Runner.module
. It's as simple as:
module com.example.runner
void main()
From this module we want to use greeter-module
, so we need to supply its bundle on the command-line:
runner-module$ spaghetti generate headers --definition Runner.module \
--language haxe --dependency-path ../greeter-module/bundle --output headers
Again, we get some sources in the headers
directory:
headers
|-- Spaghetti.hx
`-- com
`-- example
|-- greeter
| |-- Greeter.hx
| `-- GreeterModule.hx
`-- runner
|-- __RunnerModuleInit.hx
`-- __RunnerModuleProxy.hx
This looks a little more complicated, because the Haxe generator uses a directory structure that matches the package structure of the source files. Haxe itself also needs a little bit more convincing to compile into Spaghetti-compatible JavaScript code, hence the __RunnerModuleInit.hx
. But as you will see, this will compile to small enough JavaScript in the end.
But first, we need to write our second module! You need to create the directories src/com/example/runner
and create RunnerModule.hx
in this directory:
package com.example.runner;
import com.example.greeter.GreeterModule;
class RunnerModule {
public static function main():Void {
var greeter = GreeterModule.createGreeter();
trace(greeter.sayHello("Spaghetti"));
}
}
Now we need Haxe. You can install Haxe with brew install haxe
(it may ask you install XQuartz as well -- please do so).
Compile it with:
runner-module$ haxe -cp headers -cp src -js Runner.js \
--macro "include('com.example.runner')"
The resulting Runner.js
file should look like:
(function () { "use strict";
var com = {};
com.example = {};
com.example.greeter = {};
com.example.greeter.Greeter = function() { };
com.example.greeter.GreeterModule = function() { };
com.example.runner = {};
com.example.runner.RunnerModule = function() { };
com.example.runner.RunnerModule.main = function() {
var greeter = com.example.greeter.GreeterModule.module.createGreeter();
greeter.sayHello("Spaghetti");
};
com.example.runner.__RunnerModuleProxy = function() {
};
com.example.runner.__RunnerModuleProxy.prototype = {
main: function() {
com.example.runner.RunnerModule.main();
}
};
com.example.runner.__RunnerModuleInit = function() { };
com.example.runner.__RunnerModuleInit.delayedInit = function() {
__haxeModule = new com.example.runner.__RunnerModuleProxy();
return true;
};
com.example.greeter.GreeterModule.module = Spaghetti["dependencies"]["com.example.greeter"]["module"];
com.example.runner.__RunnerModuleInit.delayedInitFinished = com.example.runner.__RunnerModuleInit.delayedInit();
})();
As usual, we bundle this module, too:
runner-module$ spaghetti bundle --definition Runner.module --language haxe \
--source Runner.js --output bundle -d ../greeter-module/bundle/
Now, let's create a runnable application:
runner-module$ cd ..
spaghetti$ spaghetti package --wrapper node \
--dependency-path greeter-module/bundle:runner-module/bundle \
--execute --main com.example.runner --output app
This will create an application in app
:
app
|-- application.js
`-- node_modules
|-- com.example.greeter
| `-- index.js
`-- com.example.runner
`-- index.js
To run the application, simply execute:
spaghetti$ node app/application.js
Hello Spaghetti!
Read the Spaghetti language reference to add further features to your application. Or head over to the Spaghetti Gradle example containing examples for many features.