0.1.33 • Published 8 years ago

aq-web-front v0.1.33

Weekly downloads
19
License
MIT
Repository
github
Last release
8 years ago

AQ Web Front

AWF is a compilation framework to allow coding web frontends in a modern style.

Integration of some of the currently best frontend frameworks: Babel, React and Stylus.

Features

  • ES2015 provided by Babel (not including features requiring pollyfill)
  • CSS preprocessing provided by Stylus (nib import allowed)
  • Merge source files according to imports
  • Listen for data changes by using BoundObject
  • Best effort to generate legacy IE compatible JS code (will automatically load ES5-Shim, ES5-Sham, and JSON3)
  • for...in on arrays will only traverse array members
  • Easy import for popular JS libraries: lodash, jQuery and more (React and ReactDOM are automatically imported if when use of JSX exists)
  • Source map gereration for both js and css artifacts
  • Code will be called after the DOM is ready

Installation

npm install -g aq-web-front

Usage

Initialize project directory, generate a configuration file:

$> cd 'Project Root/'
$> mkdir app
$> cd app
$> awf init

Begin auto progressive build:

$> cd 'Project Root/app'
$> awf

Running awf will start watching changes in the app directory, and generate the following files in Project Root/: app.js, app.css.

TIP:

  • The name of the generated files will be the same as the watched directory. i.e. watching in home directory will generate home.js and home.css.
  • Although you have to run awf init right at the directory where source files are watched, running awf is not required to be so. You may run it anywhere, and the framework will scan child directories in search of configuration files, or traverse up the directory tree if none were found. Watching will start at where existing configuration files were found.

Directory Structure

This is just an example of how things might work, actual usage may vary.

Project Root/ --+-- app/ -----------+-- awf.conf
                |                   |
                +-- @ app.js        +-- ClassA/ ---------+-- index.js
                |                   |                    |
                +-- @ app.css       +-- ClassB.js        +-- View.jsx
                |                   |                    |
                |                   +-- other sources    +-- Style.styl
                |                   |
                |                   +-- @ _build/
                |
                +-- index.html (or other AWF unrelated files)

TIP:

  • Items marked @ are generated by this framework
  • As described in the usage description, the names of app.js and app.css will follow the name of app directory
  • _build directory is used for storing progressive compile cache and source maps. You may change this to another name by editing the configuration file.
  • Delete _build directory if you want a full and clean recompile.
  • You may want to add _build/ into your .gitignore or other version control ignore files to keep repo clean.

Configuration

Configuration is required before building. By calling awf init, a configuration file named awf.conf is generated in the current directory. By default, awf init will take you through some steps to setup the initial preferences. You may call awf init --default or awf init -d to generate a configuration file with all default values.

A configuration file is basically a YAML file contain these setups:

  • Debug mode true: when off, source maps will not be gernerated and all non-error console outputs will be hidden. When on, debug versions of external libraries will be loaded if available.
  • Build Directory _build: where compile caches and source maps are stored.
  • External libraries non-inline cdnjs: libraries that you may import, providing it's url and whether it should be inlined. Inlining a library means that the content of the script will be written directly into the generated artifact rather than loading it during runtime.

Examples

Basic usage

index.html

<!DOCTYPE html>
<html>
  <head>
    <title>AWF Basic Usage</title>
    <link href="app.css" rel="stylesheet" type="text/css"/>
    <script src="app.js" type="text/javascript"></script>
  </head>
  <body>
  </body>
</html>

app/BasicUsage.js

console.log('It works');                                // output to console
ReactDOM.render(<div>It works</div>, document.body);    // output to DOM

Note:

  • document.body will be available in all popular browsers including IE6
  • React deprecates rendering to document.body, but if you didn't receive a warning message, you're probably not in debug mode. Debug mode can be toggled by editing configuration file.
  • When you debug mode, source maps will allow you to be able to trace where the console outputs are located in the source files. Some browsers (such as safari) will cache source map files, and may require manual clean up.

ES2015

app/ES2015.js

// class definition and inheritance
class A {
    whoAmI() {
        return 'A';
    }
}
class B extends A {
    whoAmI() {
        return 'B : ' + super.whoAmI();
    }
}
var b = new B();
console.log(b.whoAmI()); //$> B : A

// for of iteration (AWF fixed this feature to make it compatible with IE6)
var str = '';
for (var num of [5, 4, 3, 2, 1]) {
    str += num + ' - ';
}
console.log(str); //$> 5 - 4 - 3 - 2 - 1 -

// function argument default values
function add(a, b=1) {
    return a + b;
}
console.log(add(2, add(1))); //$> 4

This is an example of how you can use some of the language features specified in EcmaScript 6. For more information checkout the learn ES2015 section of the Babel project.

Please beware that features requiring pollyfill will not be resolved automatically. To use them, you must manually load the scripts required, but in this case AWF will not be responsible for compatibility issues on legacy browsers.

Module import and export

app/A.js

class A { // symbol with the same name of the source file is exported by default
    whoAmI() {
        return 'A';
    }
}

app/B/index.js (when named as index, this module is imported as directory path, i.e. 'B')

import '../A'; // will be parsed as `import A from '../A';`

export default ClassB extends A {
    whoAmI() {
        return 'B : ' + super.whoAmI();
    }
}

app/subdir/C.js

import Class from 'B'; // when not starting with './' or '../', the path will be 
                       // resolved relative to app diretory, so this is the same 
                       // as writing `import '../B'`

var b = new Class();
console.log(b.whoAmI()); //$> B : A

JSX classes

app/Template.jsx

<div>
    <button if={this.state.showBtn} onClick={this.onBtnClick}>click me</button>
</div>

app/Control.js

import './Template';

class Control extends Template {
    componentWillMount() {
        this.setState({showBtn:true});
    }
    onBtnClick() {
        alert('Button clicked!');
        this.setState({showBtn:false});
    }
}

app/Body.jsx

import 'Control'; // You may import in a jsx file just as how you would do in a js file

<div>
    <Control/>
</div>

app/Main.js

import 'Body';

class Main extends React.Component { // an example of how to define a React component 
                                     // directly in js code
    render() {
        return <Body/>;
    }
}

ReactDOM.render(<Main/>, document.body);

A jsx file generates a module exporting a React class rendering the contents of of the jsx file.

TIP: React and ReactDOM are auto imported when any jsx syntax exists in the source file

Stylus integration

app/Style.styl

@import 'nib'; // nib is available to import

color: #00F
.message
    background: #F00
    

app/Body.jsx

import 'Style';

<div className={Style}>
    <div className="message">
        Hello World
    </div>
</div>

Stylus files will construct a module which can be imported and used as a class name. Stylus style sheets will never be applied globally, it will only affect elements with corresponding class name.

BoundObject

var obj = BoundObject.create();
BoundObject(obj).listen((e) => {console.log(e);})
e.x = 1;
//$> {obj:obj, key:"x", newValue:1, oldValue:undefined}

var arr = [];
BoundObject.create(arr);
BoundObject(arr).listen((e) => {console.log(e);})
arr.push(1);
//$> {obj:arr, key:"length", newValue:1, oldValue:0}

With BoundObject, you'll be able to watch for changes on the object. When a BoundObject is assigned as a property of another BoundObject, change events will propagate to the parent.

class ExampleView extends React.Component {
    
    componentWillMount() {
        this.obj = BoundObject.create({
            counter: 0
        });
        setInterval(() => this.obj.counter++, 1000);
    }
    
    render() {
        return <div>{this.obj.counter}s has elapsed</div>
    }
}

React.Component has been modified to issue forceUpdate whenever a member BoundObject has changed.

lodash, jQuery, and more

import $ from 'jQuery';
import _ from 'lodash';
import _ from 'underscore';
import 'chart';
import Promise from 'bluebird';

You'll be able to use these popular libraries in your project just by a line of import. Which and how libraries are loaded depends on configuration. Other external libraries can made available be by editing the library section of the configuration file.

A fully working example: Grocery list

Code along with compiled result are located in the example directory of the repo.

Caveat

  • Do not name variable or object member starting with $$$AWF$$$. Doing so might break the framework.
  • Do not define function or variable named require, it will break the import feature.
  • Do not put anything yourself in the _build directory if you don't want to mess up the auto-build.
  • Module names do not include anything after ., app/Mod.A.js should be imported as Mod. So app/Mod.A.js will conflict with app/Mod.B.jsx, and will cause an link error.
  • Circular import is not allowed. A link error will occur if done so.
  • When using stylus, the image and other urls should be relative to the source file.
  • Although the generated artifacts are compact, they are not minimized. You may save some extra KB by using uglify.
  • This framework is still under development, don't use it for anything serious yet
0.1.33

8 years ago

0.1.32

8 years ago

0.1.31

8 years ago

0.1.30

8 years ago

0.1.29

8 years ago

0.1.27

8 years ago

0.1.26

8 years ago

0.1.25

8 years ago

0.1.24

8 years ago

0.1.23

8 years ago

0.1.22

8 years ago

0.1.21

8 years ago

0.1.20

8 years ago

0.1.19

8 years ago

0.1.18

8 years ago

0.1.17

8 years ago

0.1.16

8 years ago

0.1.15

8 years ago

0.1.14

8 years ago

0.1.13

8 years ago

0.1.12

8 years ago

0.1.11

8 years ago

0.1.10

8 years ago

0.1.9

8 years ago

0.1.8

8 years ago

0.1.7

8 years ago

0.1.6

8 years ago

0.1.5

8 years ago

0.1.3

8 years ago

0.1.2

8 years ago

0.1.1

8 years ago

0.1.0

8 years ago

0.0.9

8 years ago

0.0.8

8 years ago

0.0.7

8 years ago

0.0.6

8 years ago

0.0.5

8 years ago

0.0.4

8 years ago

0.0.3

8 years ago

0.0.2

8 years ago

0.0.1

8 years ago