Documentation

Quick Start

Incoder is a platform to generate code from domain driven templates. A template can be used to generate a simple inline method or entire projects - frontend, APIs, auth, deployment pipelines & even tests.

Templates are written in Javascript. They typically manipulate source code and then write to the file system. Incoder provides a set of predefined functions to make templating simple. To see the full list of functions view the documentation.

To see code generation in action follow the quick start tutorial.

Set up incoder

  1. Download, install and sign in to an incoder IDE plugin
  2. Create a new project so you can generate code into it
  3. Open the incoder panel
  4. Create a new template by clicking the + icon
  5. Within the dialog select ‘Basic example’ and click ‘Create’
  6. Your new template is now available within the incoder panel
  7. Open your project explorer and right click on a directory
  8. Select the ‘Use template’
  9. Select your template, fill in a value and click Run
  10. View the generated file under the selected directory

    Now to edit the code that is generated

  11. Navigate to incoder plugin
  12. Find and open your template by clicking on its name in the template explorer
  13. You should see the following files
    
        inputs/ --template input file(s)
    input.json
    src/ --source for code generation
    __starthere
    README.md --your templates instructions
  14. The input.json is the domain that is injected into your template each time it is run. Changing the values here is what makes your template dynamic.
  15. Click on __template. It’s a simple Javascript file that uses the following incoder functions.

    $source - is used to build a string of the source code and substitute a variables

    $io - is used to write the HelloWorld.js file

  16. On line XXX you can see that we’ve called another template. This template is imported like any Javascript function, we simply add it to our list of template dependencies. It demonstrates how templates can be hierarchical and can be used to build solutions with composition.

Congratulations on creating your first incoder template and using it to generate some code albeit noddy. See the docs for details on all the functions available and for more advanced examples. Alternatively get started by downloading one of the community templates.

IDE plugin overview

Using the plugin

If you haven’t done so already download and install an IDE plugin. Once installed follow the Quick Start guide. These are the features of the plugin.

Search and install templates

You can search for templates using by going to incoder.io/templates. Simply click the install button. This will sync with the IDE and within a few seconds you should see you new template is available to use.

Run code generation within existing source code

You can run a template to generate code in one of two ways. Either;

  • Right click to get the context menu, selecting ‘Generate code’. Recently used templates will appear in a sub-menu.
  • Use the CRTL + SHIFT + g as a shortcut

If not selected you’ll then be prompted to choose a template you’d like to run.

A template can either be applied to directories or files. The plugin will pass the currently selected location as the output, this is one of the following

  • The location of the cursor in the file editor
  • The file or directory selected in the project explorer

When you’re happy with the generation location, update the input.json if different and hit run.

Create a template from an existing solution

Within your IDE select some code from the editor or a file/directory in the project explorer. Right click and select Template from…

incoder will prompt you if you want to automatically detect variables. This extracts common names that only differ by naming conventions across the code base.

You’ll then be shown each file, which is your chance to add any further variables or convert to a language specific source builder. This can all be skipped and done once your template is created.

Once happy hit ‘Create’ and the code you’ve selected will be transformed into a template.

Publishing your template

You can share your template with others by publishing it to the incoder platform. Your solution will then appear when others search at incoder.io/templates.

To publish a template simply right click on it in within the incoder panel and select publish.

Each time you publish we’ll ask what type of change you’ve made and automatically update the version for you. Please follow the convention, as it helps to improve compatibility of templates.

We follow the major.minor.patch version convention

  • major - breaking changes, i.e. you’ve renamed or removed a method
  • minor - added a feature or changed the way a feature works
  • patch - bug fixes or small incremental changes

Pull requests

If you’d like to request a change to a community template then you can make edits and when you’re ready right click the template and select Publish at which point we’ll prompt you to create a pull request. Others may ask the same of you, when they’d like to extend to the functionality you’ve provided. This is a great way for open source templates to evolve.

If you receive a pull request you’ll be emailed and you’ll see a notification on your template. You can choose to accept or dismiss the request. Once accepted we’ll auto merge, unless there are conflicts. In this scenario the merge tool will automatically be triggered.

The team licence supports multiple people with rights to accept pull request and merge code.

Manipulate existing source code

Quite often we need our template to edit existing source code. This is easy to achieve with the $io and $source functions.

  1. Create a file called example.js in your project with the following code.

    console.log('Hello Mars');

  2. In your template add the code below to read the ‘example.js’ file into a source builder.

    When you use ‘right click > template’, incoder uses the location you've selected as the working directory, so you we’re assuming a file called example.js will be present.

    let source = $source.fromResource('example.js');

  3. To place some text before the word ‘Mars’, move the cursor into position and then insert.

    source.placeCursorBefore('Mars').insert('Hello');

  4. Overwrite the contents of the original file.

    source.toFile('example.js');

  5. The contents of the file should now be.

    console.log('Hello Mars');

This simple example shows how to place a cursor within existing code, insert new content and then replace the original file. See the $source function documentation for more ways to place the cursor and manipulate source code.

Template from resource files

One of the quickest ways to template a solution is to copy existing code as a resource and replace variables. The resources directory keeps reference files separate from template source code.

  1. Create resource called example.js to your template and load it into a source builder. In the IDE incoder plugin click the arrow next to the + button to add a resource.
  2. let source = $source.fromResource('example.js');

  3. Add the following to example.js
  4. console.log(`My favourite planet is ${planet}`);

  5. Within your template open the provided input.json file under the inputs directory. Add the following property.
  6. { "planet" : "Mars" }

  7. In your template use the provided input variable to substitute values within example.js and then write it to your project.
  8. let source = $source.fromResource('example.js')
    .substitute(input)
    .toFile('other.js');

This simple example shows how to replace variables by using the substitute function. See the $source function documentation for more ways to manipulate code.

String manipulation

The same name may appear across a code base with variations in naming convention or we may need to indent the code we’ve generated. The $string functions has many utility methods for transforming text. For example;

  1. Change the naming convention from whitespace to camel
  2. $string.whitespaceTo(str, CONVENTION.CAMEL)

  3. Indent a string with four spaces. Any line breaks will be prefixed.
  4. $string.indentFourSpaces(str)

  5. Handy when you’re composing a comma separated list of variables.
  6. $string.removeLastComma(str)

See the $string documentation for many more functions.

Using Lexers to generate code

The language lexers are the most flexible way to represent classes and interfaces as a templates. The following is a hypothetical example, to view the language specific builders check the Template Syntax section.

let classBuilder = $LANGUAGE.classBuilder("ClassName").withPackage("com.example")

Once you have a builder you can dynamically add imports, inheritance, annotations

.addImport("com.example.domain.SomeObject")
.addInterface("ParentClass")
.addParentClass("ParentClass")
.addAnnotation("Controller")

Instance variables as getter & setters or constants

.addInstanceVariable($LANGUAGE.instanceVariableBuilder("name")
    .type("int")
    .addAnnotation("Three")
    .addAnnotations(["One", "Two"])
    .asGetSet()
    .asGet()
    .asSet()
    .asPublic().asPrivate().asProtected()
    .asStatic()
    .asConstant()
    .asReadOnly()
    .asNonSerialized()
    .asVolatile()
    .value("Example")
    .build())

Methods

.addMethod($LANGUAGE.methodBuilder("valid")
    .returnType("boolean")
    .source("return true;")
    .variableFrom("request", "RequestType", "@Annotation")
    .variable($LANGUAGE.variableBuilder("request")
        .type("RequestType")
        .addAnnotation("@Annotation")
        .build()));
 

Constructors

.addConstructor($LANGUAGE.constructorBuilder("ClassName")
    .constructorParameter("name", "type")
    .assignedConstructorParameter("instanceVariableName", "type")
    .initializedVariableWithSource("this.name", "name")
    .initializedVariableWithArgs("name", "TypeToInitialise", ["Arg1", "Arg2"])
    .build())

When you’re ready, you can write it to a file.

classBuilder.toFile("path/ClassName.ext")

Best practice

Template in a way that one template can be applied on top of another. By doing this each template becomes easier to maintain and gives full flexibility when deciding which features to include on each generation.

For example, we could create a base application template that includes the core framework and then using several others we could inject CRUD APIs or even the frontend and backend of a feature, e.g. manage account, make a reservation, view your invoice, send an email, etc.

By making templates composite they are easier to maintain and can be used as building blocks.

On the contrary, if additions are relatively simple then you could provide toggles within your input and include them within the same template.

Template syntax

These are the functions supported by incoder.

  • File
  • Source
  • String
  • HTTP
  • XML
  • Google
  • C#
  • Java
  • React

File functions

$io.exists('/path/file.txt')
$io.newFile('/path/file.txt')
$io.newFile('/path/file.txt', "content")
$io.newDirectory('/path/file.txt')
$io.readFile('/path/file.txt')
$io.deleteFile('/path/file.txt')
$io.execute('run command')
$io.append('/path/file.txt', 'appended content')

Source functions

The source builder is helper for manipulating text.

let source = $source.builder("Some Text");

Load a template resource file into a source builder

let source = $source.fromResource("example.txt");

Load a template resource file into a source builder

let source = $source.fromFile('/path/example.txt');

Check if text contains value

let hasText = source.contains("Do I Exist?");

Replace all matching values

source.replace("match", "All");

Prepend or append to text

source.prepend("Some Text");
source.append("Some Text");

Move the cursor within your source

source.adjustCursorBy(5);
source.placeCursorAfter("Some Text");
source.placeCursorBefore("Some Text");
source.placeCursorAfterLast("Some Text");
source.placeCursorBeforeLast("Some Text");
source.placeCursorAfterMatch("Some", 't');
source.placeCursorAfterLastMatch("Some", 't');

Insert at the cursor position

source.insert("Some Text");
source.insert("Numbers %s and %s", "one", "two");

If your text contains substitution variables like ${substituteMe}

source.substitute({ "substituteMe" : "Hello World"} );

Recurse folder containing resources and substitute

let model = { "name" : "value" };
$source.resourceSubstitution('folder', model, true);

Output the source code as a string

let sourceCode = source.toString();
let sourceCode = source.build();

Save the source to file

source.toFile('directory/file.ext');

String functions

It’s often necessary to transform text to a particular code syntax, e.g. camel case. These string utilities make it easy to manipulate your domain into the various forms required for source code.

Naming convention

CONVENTION.HYPHEN // One-Two
CONVENTION.PERIOD // One.Two
CONVENTION.SNAKE // one_two
CONVENTION.UNDERSCORE // one_two
CONVENTION.WHITESPACE // One Two
$string.camelTo("OneTwo", CONVENTION.*)
$string.hyphenTo("one-two", CONVENTION.*)
$string.periodTo("one.two", CONVENTION.*)
$string.underscoresTo("one_two", CONVENTION.*)
$string.whitespaceTo("one two", CONVENTION.*)

Manipulate strings

$string.isNotBlank("string") // true
$string.isBlank("string") // false
$string.capitalize("string") // string -> String
$string.decapitalize("String") // String -> string
$string.removeDuplicateSpaces("one  two") // one  two -> one two
$string.removeWhitespaces("on e") // on e -> one
$string.replaceLast("one, two,", ";", ";") // one, two, -> one, two;
$string.removeLastComma("one, two, three,") // one, two, three, -> one, two, three
$string.insertTextAt("one three", " two", 3) // one two three
$string.substitute("{{replace}}", { "replace" : "Hello World"}) // Hello World
$string.removeNonCharacters("one! *&two^$%£") // one two
$string.justCapitals("One Two") // OT
$string.replaceTabsWithFourSpaces("\tstring") // string prefixed with four spaces
$string.replaceTabsWithSpaces("\tstring", 2) // string prefixed with two spaces
$string.replaceTabs("\tstring", ' ', 4) // string prefixed with four spaces
$string.abbreviate("StringExample")

Indents

$string.removeIndent("\ttext")
$string.countIndent("\t\ttext")
$string.indentFourSpaces("text")
$string.indentLines("text", 2, '\t')
$string.addIndent("text", 2, '\t')

Handling empty values

$string.defaultValue("testValue", "defaultValue")
$string.emptyIfBlank("testValue")
$string.whenNotBlank("testValue", "value")
$string.elseBlank(statement, "value")

Misc

$string.uuid(length)
$string.cursorAtEnd("example text ();", "text", ";")
$string.cursorAtLastOccurrenceOf("example text ();", "te", "t")
$string.findCharBetweenCursorAndEnd("skip end", "n", 4)

Http functions

Make HTTP GET requests. Useful if you’re generating code based on a public API.

$http.get(url, contentType)
$http.getXML(url)
$http.getJSON(url)

XML Functions

Convert an object to XML

$xml.objectToXml({"one": "1", "two": {"a": ""}});

Parse and manipulate XML.

let xml = $xml.parse('<one><two /></one>');
let xml = $xml.fromResource("example.xml");
let tagName = xml.name();
let elements = xml.elementsAt('xpath');
for (var element in elements) {
   var tagName = xml.name();
}
let element = xml.elementAt('xpath');
let attributes = xml.attributes();
xml.textAt('xpath');
xml.extract('xpath');
let hasValue = xml.hasAttr('xpath');
let attrValue = xml.attr('xpath');

Java functions

$java.pojo({
   "package" : "com.example",
   "name" : "HelloWorld",
   "json" : JSON.stringify({
       "name" : "",
       "address" : {
           "number" : 3,
           "street" : ""
       }
   })
});

Building a Java class

let classBuilder = $java.classBuilder(className)
   .addImport("com.example.domain.SomeObject")
   .addStaticImport("com.example.domain.SomeOtherObject.staticMethod")
   .addAnnotation("Controller")
   .withPackage("com.example")
   .addInterface("ParentClass")
   .addParentClass("ParentClass")
   .addConstructor($java.constructorBuilder().build())
   .addConstructor($java.constructorBuilder("ClassName")
       .constructorParameter("name", "type")
       .assignedConstructorParameter("instanceVariableName", "type")
       .initializedVariableWithSource("this.name", "name")
       .initializedVariableWithArgs("name", "TypeToInitialise", ["Arg1", "Arg2"])
       .build())
   .addInstanceVariable($java.instanceVariableBuilder("name")
       .type("int")
       .addAnnotation("Three")
       .addAnnotations(["One", "Two"])
       .asGetSet()
       .asGet()
       .asSet()
       .asPublic().asPrivate().asProtected()
       .asStatic()
       .asConstant()
       .asReadOnly()
       .asNonSerialized()
       .asVolatile()
       .value("Example")
       .build())
   .instanceVariableFrom("aTypeVariable", "type")
   .instanceVariableFrom("aTypeVariableWithScope", "type", "PUBLIC") // or PROTECTED PACKAGE_PRIVATE, PRIVATE
   .instanceVariableFrom("aTypeVariableWithScopeAndAssignment", "type", "PUBLIC", "assignedValue")
   .addMethod("methodName", "ResponseType")
   .addMethod("methodName", "ResponseType", "PRIVATE")
   .addMethod("methodName", "ResponseType", "PRIVATE", 'String message = "Hello World"')
   .addMethodAsSource("private void someMethod() {}")
   .addMethod($java.methodBuilder("valid")
       .returnType("boolean")
       .source("return true;")
       .variableFrom("request", "RequestType", "@Annotation")
       .variable($java.variableBuilder("request")
           .type("RequestType")
           .addAnnotation("@Annotation")
           .build()));
 
let classAsString = classBuilder.toString();

Specify the directory to write the file to. Note that the file name is implied.

classBuilder.toFile('/path');
let classFileName = classBuilder.fileName(); // ClassName.java

Building a Java interface

  1. Use the interface builder
let interfaceBuilder = $java.interfaceBuilder("Name")
   .withPackage("Namespace")
   .addImport("com.Example.Domain.SomeObject")
   .addMethod($java.methodBuilder("Name")
       .returnType("type")
       .variableFrom("name", "type")
       .build());
     
  1. Build from an existing class
$java.interfaceBuilderFromClass(classBuilder.build()).build();

Once built you can do one of the following

  1. Write to a string
interfaceBuilder.toString();
  1. Write to a file
interfaceBuilder.toFile('/path/Name.java');
  1. Add to a class builder
classBuilder.addInnerInterface(interfaceBuilder.build());

C# functions

$csharp.poco({
   "namespace" : "com.example",
   "name" : "HelloWorld",
   "json" : JSON.stringify({
       "name" : "",
       "address" : {
           "number" : 3,
           "street" : ""
       }
   })
});

 

Building a C# class

let classBuilder = $csharp.classBuilder("ClassName")
    .classScope("PUBLIC")
    .addImport("Com.Example.Domain.SomeObject")
    .addStaticImport("Com.Example.Domain.SomeOtherObject.staticMethod")
    .addAttribute("[Annotation]")
    .withNamespace("Com.Example")
    .addConstructor($csharp.constructorBuilder().build())
    .addConstructor($csharp.constructorBuilder("ClassName")
        .constructorParameter("name", "type")
        .assignedConstructorParameter("instanceVariableName", "type")
        .initializedVariableWithSource("_name", "name")
        .initializedVariableWithArgs("name", "TypeToInitialise", ["Arg1", "Arg2"])
        .build())
    .addGetSetProperty("name", "type")
    .addGetProperty("name", "type")
    .addSetProperty("name", "type")
    .addInterface("ParentClass") // OR .addParentClass("ParentClass")
    .instanceVariableFrom("aStringVariable")
    .instanceVariableFrom("aTypeVariable", "type")
    // add scope PROTECTED, PACKAGE_PRIVATE, PRIVATE
    .instanceVariableFrom("aTypeVariableWithScope", "type", "PUBLIC")
    .instanceVariableFrom("aTypeVariableWithScopeAndAssignment", "type", "PUBLIC", "assignedValue")
    .addInstanceVariable($csharp.instanceVariableBuilder("name", 'string')
        .type("int")
        .addAnnotation("Three")
        .addAnnotations(["One", "Two"])
        .asGetSet()
        .asGet()
        .asSet()
        .asPublic().asPrivate().asProtected()
        .asStatic()
        .asConstant()
        .asReadOnly()
        .asNonSerialized()
        .asVolatile()
        .value("Example")
        .build())
    .addInstanceVariable($csharp.instanceVariableBuilder("name", "Type", ["Annotation1", "Annotation2"]).build())
    .addMethodFrom("MethodName", "type")
    .addMethodFrom("MethodName", "type", "PRIVATE")
    .addMethodFrom("MethodName", "type", "PRIVATE", 'String message = "Hello"')
    .addMethodAsSource("private void SomeMethod() {}")
    .addMethod($csharp.methodBuilder("Handle")
        .addAnnotations(["One", "Two"])
        .addAnnotation("Three")
        .withReturnType("Model")
        .asPublic().asPrivate().asProtected().asPackagePrivate()
        .asAsync()
        .asAbstract()
        .asStatic()
        .asFinal()
        .asNative()
        .asPartial()
        .asVirtual()
        .source("")
        .withIndent(4)
        .variable($csharp.variableBuilder("name").build())
        .variable($csharp.variableBuilder("name", "type").build())
        .variable($csharp.variableBuilder("name", "type", ["Annotation1", "Annotation2"]).build())
        .variableFrom("name", "Type")
        .variableFrom("name", "Type", "Annotation")
        .build())
    .addInnerClass($csharp.classBuilder("NestedClass").build());

let classAsAString = classBuilder.toString();

Specify the directory to write the file to. Note that the file name is implied.

classBuilder.toFile('/path');

let classFileName = classBuilder.fileName();

Building interfaces

There are two ways to create an interface builder

  1. Use the interface builder
let interfaceBuilder = $csharp.interfaceBuilder("IClassName")
     .withNamespace("Namespace")
     .addImport("Com.Example.Domain.SomeObject")
     .addMethod($csharp.methodBuilder("Name")
         .returnType("type")
         .variableFrom("name", "type")
         .build());
  1. Build from an existing class
$csharp.interfaceBuilderFromClass(classBuilder.build()).build();

Once built you can do one of the following:

  1. Write to a string
interfaceBuilder.toString();
  1. Write to a file
interfaceBuilder.toFile('/path/IClassName.cs');
  1. Add to a class builder
classBuilder.addInnerInterface(interfaceBuilder.build());

React functions

Create a React app

$react.createReactApp("my-project");

Build React components and write to file

  1. Build a React component specifying the directory under the project src
let component = $react.componentBuilder(componentName, directory)
  1. Add a JSX element
let element = $react.elementBuilder('div').withTag('div')
   .withJsxElement($react.elementBuilder('p').withText("")
       .withAttribute("name", "value")
       .build())
   .build();
component.withJsxElement(element);
  1. A CSS module is included with each component
component.cssModule.addCssProperties("className", [{
   property: "border", value: `${colour} solid ${thickness}px`
}])
  1. Add the class to the component or element
component.addClass('${styles.className}');
element.addClass('${styles.className}');
  1. Add routing information
component.routeBuilder.withRoute("ComponentName", [{
   property: "border", value: `${colour} solid ${thickness}px`
}])
  1. Output the React and modular CSS file
component.toFile();
component.cssModule.toFile();

 

 

Design to code

The ‘Design to code’ incoder plugins generate code from a design. In order to do this we have to let incoder know what certain things are. For example if we are generating HTML then we would add meta information about which design elements are inputs, tables, radio buttons, forms, etc. We do this using the incoder UI.

When we run code generation the plugin extracts the meta information, applies layout transforms and then produces a JSON model that is sent to incoder. The JSON is used as the input to any template you select from within the incoder UI. The template implementor will use this meta information to decide how code is generated.

 

  1. To get started install a design plugin
  2. Open the incoder UI
  1. Shortcut CTRL + SHIFT + i
  2. Via the menu Plugins > incoder
  1. As you select each layer you’ll be provided the following dropdowns within the incoder UI. Use these to provide meta information for that layer. The UI will prompt you if additional information is required.
  1. UI element - paragraph, link, active link, form, button, text input, textarea, dropdown, checkbox, table, mandatory, readonly
  2. Responsive grid - row or column, offset and size
  3. SVG - will transform a layer to SVG
  4. API:CRUD - post or get data for a table, form or select. Each letter in CRUD is optional, you may just want a form to support create and update (CU) or a dropdown would only read (R)
  5. ORM - used in conjunction with API:CRUD to build an entity model that can then be used to generate APIs, ORM and databases
  1. Once complete specify where you’d like to generate the code to.
  2. Optionally choose the output format, e.g. HTML. If not selected incoder won’t attempt to apply any layout transforms and the raw JSON will be provided.
  3. Finally select a template you’d like to run and hit ‘Generate’. Only installed templates will be available. As an example try installing ‘Sketch to React’

 

 

You can speed up the workflow by creating or downloading a component library.

Creating your own design components

You can pass through any design components to incoder, however we have predefined some layouts for well known UI elements. Either use a component library for reference or follow the below conventions.

 

We’ve used a # to indicate where you’ll be prompted by the UI to provide additional information.

 

Button

        Value (Text)

        BG (Rectangle)

 

Input

        Label (Text)

        Layer group

            Value (Text)

            BG (Rectangle)

 

Text Area

        Label (Text)

        Layer group

            Value (Text)

            BG (Rectangle)

 

Select (dropdown)

        Label (Text)

        Icon (image)

        Layer group

            Value (Text)

            BG (Rectangle)

 

Custom Checkbox

        Label (Text)

        Icon (image) #selected

        Icon (image) #unselected

 

Table

        Layer group #html:th

            Header 1

            Header 2

        Layer group #html:tr

            Cell example 1

            Cell example 2

 

API & ORM

Layer Group #api:crud #html:form

    Address #orm:Client.Profile.AddressLine1

    Address line 2 #orm:Client.Profile.AddressLine2

    City #orm:Client.Profile.City

    Postcode #orm:Client.Profile.Postcode

 

#orm:(Parent *optional).Entity.Field