slush-markoa

Markoa app generator

Usage no npm install needed!

<script type="module">
  import slushMarkoa from 'https://cdn.skypack.dev/slush-markoa';
</script>

README

Slush Markoa Build Status NPM version

Markoa app generator

Generators included

  • slush markoa (basic project with markoa apps)
  • slush markoa:app (add app to apps)
  • slush markoa:data (add app to apps)
  • slush markoa:layout (Stylus and Jade->Marko configuration)
  • slush markoa:roots (roots.cx configuration)
  • slush markoa:tag (add one marko tag)
  • slush markoa:taglibs (add one or more marko taglibs)
  • slush markoa:tags (add multiple marko tags)
  • slush markoa:widget (add one or more marko widgets)

TODO: Improve widget generator similar to make more similar to tag generator ;)

Getting Started

Install slush-markoa globally:

$ npm install -g slush-markoa

Usage

Create a new folder for your project:

$ mkdir my-slush-markoa

Run the generator from within the new folder:

$ cd my-slush-markoa && slush markoa

Layout and Styling

We recommend running the layout generator included to get a nice project setup for using Stylus with various cool plugins to make layout and styling of your app a much more fun experience!!!

Note: Be sure to read the Layout.md file generated for tips, info etc. ;)

App container generated

App file structure

/apps
  /_global
    /components
    /data
      index.js
    /layouts
      layout.jade
      mobile.jade
  /index
    /components
      /tags
        /project-feed
          marko-tag.json
          app.jade
      marko-taglib.json
    /layouts
      base.jade
      mobile.jade
    /page
      /dependencies
        browser.json
        app.browser.json
      app.jade
    /data
      index.js
    marko-taglib.json
    meta.js
  /repositories
  /teams
  ...
  marko-taglib.json  

  // Gulp build tasks
  /build
    /tasks
      jade.js
      stylus.js
      watch.js
    paths.js
    ...

Development

Watching files:

gulp watch - watches .jade files in /apps and .styl files in /styles

The Markoa server is by default configured to watch .marko files in the /apps folders registered and will reload in order to recompile to .js template files.

You can enable browser refresh

npm install browser-refresh --global
browser-refresh server.js

Which should auto reload the browser whenever a .marko page is changes, for instant feedback!

There is also a browser-refresh.json file for configuring this in more details, including for SSL support.

Static asset dependencies

Static assets in public are compiled to /dist and can be referenced from marko directly.

<link rel="stylesheet" type="text/css" href="app.css">
<link rel="stylesheet" type="text/css" href="semantic.min.css">
<script src="semantic.min.js"></script>

Or the same in a .jade file

link(rel="stylesheet" type="text/css" href="app.css")
link(rel="stylesheet" type="text/css" href="semantic.min.css")
script(src="semantic.min.js")

However we recommend using proper page dependencies via Lasso and browser.json files (see below)

Layout

First run slush markoa:layout to generate setup for Jade, Stylus and Semantic UI - to power the layout of your app.

Install Semantic UI as instructed: npm install semantic-ui --save

Generating apps

slush markoa:app

This geneator will create an app under apps/[app-name] similar to the default index app generated by the default marko generator. Use this generator each time you want to add an app!

/[app]
  /components
    /tags
      /project-feed
        marko-tag.json
        template.jade
    marko-taglib.json

  /layouts
    base.jade

  /data
    index.js

  /page
    app.jade
    /dependencies
      app.browser.json

  marko-taglib.json

Generating tags

slush markoa:tag

Use this generator each time you want to add a tag! If no app name is given, the tag becomes global:

apps/_global/components

/apps
  /_global
    /components
      /tags
        /[tag]
          marko-tag.json
          renderer.js
          template.jade

    marko-taglib.json

If an app name is given: apps/[app-name]/components

/apps
  /[app]
    /components
      /tags
        /[tag]
          marko-tag.json
          renderer.js
          template.jade

Tags and Taglibs

As you get more fine grained tags, it starts to make sense to group them into categories such as:

  • button tags
  • list tags
  • list item tags
  • ...

In order to support this, you can use a special namespace feature xxx:aaa or xxx:aaa-bb

This will create a subfolder xxx for the tag. F.ex for the tag menu:top

/apps
  /_global
    /components
      /tags
        /menu
          /menu-top
            marko-tag.json
            renderer.js
            template.marko
            template.jade

    marko-taglib.json
  marko-taglib.json

The new folder menu is a new sub-tag lib. For this new menu taglib to be reachable, it must be imported in a parent taglib (see section below on Taglibs).

Single tag example

  • What is the name of your tag or tags (, separated) ? top-menu
  • For which app (empty: global) ?

Creates the global tag top-menu under apps/_global

- template.jade
- template.marko
- renderer.js
- marko-tag.json

Example ui-tabs

Example: ui-tabs/marko-tag.json

{
    "@orientation": "string",
    "@tabs <tab>[]": {
        "@title": "string"
    }
}

renderer.js references template.marko (see below)

var template = require('./template.marko');
exports.renderer = function(input, out) {
  template.render(input, out);
};

ui-tabs/template.marko

<div class="tabs">
    <ul class="nav nav-tabs">
        <li class="tab" for="tab in data.tabs">
            <a href="#">
                ...
            </a>
        </li>
    </ul>
    <div class="tab-content">
        <div class="tab-pane" for="tab in data.tabs">
            <invoke function="tab.renderBody(out)"/>
        </div>
    </div>
</div>

Marko taglibs

Each app has its own taglib file. This file can reference multiple folders if needed.

marko-taglib.json example:

{
    "tags-dir": ["./components", "./modules"]
}

You can also import other taglibs into a taglib. This feature is used to include the /menu taglib for the global taglib:

{
    "tags-dir": "./_global/components",
    "taglib-imports": [
      "./_global/components/menu/marko-taglib.json"
    ]
}

Multiple tags

  • What is the name of your tag or tags (, separated) ? top-menu, side-bar, session-bar
  • For which app (empty: global) ? index

Creates the tags: top-menu, side-bar and session-bar for the app apps/index

Marko Widgets

Marko Widgets are special kinds of tags that support dynamic rendering and data binding etc. much like Reactive widgets/components from other frameworks such as React components. However Marko Widgets support both client and server side rendering amongst many other benefits, such as much lower footprint (kb) and higher server rendering performance (x10) than typical React components.

To create one or more widgets:

slush markoa:widget

Name the widget or list widgets to be created, just like for a tag. The simplest Widget template looks something like this:

<div w-bind>
    Hello $data.name
</div>

With a corresponding "ViewModel" or Widget controller:

// widget.js
module.exports = require('marko-widgets').defineComponent({
    template: require('./template.marko'),

    getTemplateData: function(state, input) {
        return {
            name: input.name || 'unknown name'
        };
    },

    init: function() {
        var el = this.el; // The root DOM element that the widget is bound to
        console.log('Initializing widget: ' + el.id);
    }
});

Read more about how to get the full benefits of reactive Widgets for client and server on the Marko Widgets page. Enjoy :)

Full page setup

<lasso-page name="index" package-path="./dependencies/app-browser.json" />

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Marko Widgets: Bind</title>
    <lasso-head/>
</head>
<body>
    <h1>Marko Widgets: Bind</h1>

    <div class="my-component" w-bind="./widget">
        <h2>Click Me</h2>
    </div>

    <lasso-body/>
    <init-widgets/>
</body>
</html>

The browser.json that includes the required client-side code is shown below:

src/pages/index/browser.json

{
    "dependencies": [
        "require: marko-widgets",
        "require: ./widget"
    ]
}

Browser Widget dependencies

This browser.json setup for client side widget dependencies doesn't work as is. Our setup is more advanced. We need the /widget folder to expose methods to give us all global widgets and all widgets for each app. Then determine or find the widgets being used on the page, by either: - use a list such as widgets.json in the /page folder - if no widgets.json, parse through the marko.html for non-html tags

look up widgets in widget registry as part of yet another compile phase (on gulp watch) and dynamically generate the browser.json file (perhaps by having a browser.json (no marko widgets) and an index.browser.json generated and referenced).

Registering the apps

Follow a recipe like the following...

let markoa = require('markoa');
// see markoa docs for how to create appContainer
let myAppContainer = ...;

let myApp = require('./my-app');
let apps = ['index', 'projects'];

// mount apps on your appContainer using conventions!
myApp.mountIn(myAppContainer, apps);

Getting To Know Slush

Slush is a tool that uses Gulp for project scaffolding.

Slush does not contain anything "out of the box", except the ability to locate installed slush generators and to run them with liftoff.

To find out more about Slush, check out the documentation.

Contributing

See the CONTRIBUTING Guidelines

Support

If you have any problem or suggestion please open an issue here.