├── .eslintrc ├── .gitignore ├── README.md ├── app ├── index.css ├── index.js ├── index.sass ├── index.styl ├── index.ts └── vendor │ └── index.js ├── package.json ├── public └── index.html └── workflow ├── bin └── rails_start ├── config ├── parameters.js ├── paths.js └── tasks.js ├── index.js ├── lib ├── Argv.js ├── Config.js ├── Help.js ├── Print.js ├── ProcessManager.js ├── Task.js ├── TaskManager.js ├── TaskOptions.js ├── TaskProcess.js ├── functions │ ├── guid.js │ ├── object.js │ └── require_dir.js └── mixins │ └── Bind.js ├── tasks ├── browserify.js ├── clean.js ├── index.js ├── postcss.js ├── sass.js ├── server.js ├── stylus.js ├── template.js ├── typescript.js ├── uglify.js └── watcher.js ├── templates ├── html │ └── section.html ├── js │ └── section.js └── stylus │ └── section.styl └── test ├── app ├── index.css ├── index.js ├── index.sass ├── index.styl └── index.ts ├── config.js ├── index.js └── public └── main.css /.eslintrc: -------------------------------------------------------------------------------- 1 | rules: 2 | indent: 0 3 | quotes: 0 4 | linebreak-style: [2, "unix"] 5 | semi: 0 6 | no-console: 0 7 | prefer-const: 2 8 | 9 | globals: 10 | $: true 11 | jQuery: true 12 | Vue: true 13 | NotificationCenter: true 14 | TimelineLite: true 15 | TweenLite: true 16 | Ease: true 17 | UserData: true 18 | ResultManager: true 19 | FB: true 20 | 21 | ecmaFeatures: 22 | arrowFunctions: true 23 | binaryLiterals: true 24 | blockBindings: true 25 | classes: true 26 | defaultParams: true 27 | destructuring: true 28 | forOf: false 29 | generators: false 30 | modules: true 31 | objectLiteralComputedProperties: false 32 | objectLiteralDuplicateProperties: false 33 | objectLiteralShorthandMethods: true 34 | objectLiteralShorthandProperties: true 35 | octalLiterals: false 36 | regexUFlag: false 37 | regexYFlag: false 38 | restParams: true 39 | spread: true 40 | superInFunctions: true 41 | templateStrings: true 42 | unicodeCodePointEscapes: false 43 | 44 | env: 45 | es6: true 46 | browser: true 47 | node: true 48 | 49 | extends: 'eslint:recommended' 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .sass-cache 3 | .tmp 4 | *.mp4 5 | node_modules 6 | npm-debug.* 7 | public/main.css 8 | public/main.js 9 | public/vendor.js 10 | workflow/tmp 11 | .idea -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Frontend workflow 2 | 3 | Set of javascript tasks for front development 4 | 5 | ## Summary 6 | 7 | * [Available tasks](#available-tasks) 8 | * [Task execution](#task-execution) 9 | * [Parameters](#parameters) 10 | * [Configuration](#configuration) 11 | * [Task](#task) 12 | * [Task class](#task-class) 13 | * [TaskProcess class](#taskprocess-class) 14 | * [Template](#template) 15 | 16 | ## Available tasks 17 | 18 | Available tasks : 19 | 20 | * [`browserify`](https://github.com/substack/node-browserify) 21 | * [`server`](https://github.com/BrowserSync/browser-sync) 22 | * [`watch`](https://github.com/paulmillr/chokidar) 23 | * [`uglify`](https://github.com/mishoo/UglifyJS2) 24 | * [`postcss`](https://github.com/postcss/postcss) 25 | * [`sass`](https://github.com/sass/sass) 26 | * [`stylus`](https://github.com/stylus/stylus) 27 | * [`typescript`](https://github.com/Microsoft/TypeScript) 28 | * [`watcher`](https://github.com/paulmillr/chokidar) 29 | * [`template`](#template) 30 | 31 | ## Tasks execution 32 | 33 | ``` 34 | node workflow stylus -w -m -s 35 | ``` 36 | 37 | Via `npm-scripts` 38 | 39 | ``` 40 | npm run stylus:compile 41 | ``` 42 | 43 | You can call scripts declared inside `package.json`, if the task does not exist inside the workflow. 44 | 45 | ``` 46 | node workflow stylus:compile browserify:compile 47 | ``` 48 | 49 | 50 | ## Parameters 51 | 52 | `subarg` is used to fetch your command parameters. `subarg` options are located at `config/parameters.js`. 53 | 54 | Available parameters : 55 | 56 | * `input` 57 | * `output` 58 | * `watch` - `false` by default 59 | * `compress` - `false` by default 60 | * `sourcemap` - `false` by default 61 | * `verbose` - `true` by default. Display verbose log. 62 | * `kill_pids` - `true` by default. Kill pids inside `tmp/pids` 63 | 64 | You can ovveride these parameters by task inside your configuration file. 65 | 66 | 67 | ## Configuration 68 | 69 | ### Set a configuration file 70 | 71 | By default, the workflow take the file at `config/tasks.js`. You can set a new path inside your `package.json` 72 | 73 | ```json 74 | "workflow": { 75 | "config": "./workflow/config/tasks.js" 76 | } 77 | ``` 78 | 79 | You can set a config file by environment. By default, `development` config file is used. 80 | 81 | ```json 82 | "workflow": { 83 | "development": "./workflow/config/development.js", 84 | "production": "./workflow/config/production.js" 85 | } 86 | ``` 87 | 88 | ### Create a configuration file 89 | 90 | The configuration file exports an `Object`. Each key of the object is the name of a task and the value is an array of object. 91 | 92 | 93 | ```js 94 | const _tasks = {} 95 | 96 | _tasks['browserify'] = [ 97 | { 98 | /** 99 | * Precise a file name 100 | * By default, this property is splitted to `input` and `output` parameters 101 | * 102 | * For example : 103 | * file: "./app/index.js ./public/main.js" 104 | * 105 | * equivalent to : 106 | * 107 | * override_parameters: { 108 | * input: "./app/index.js", 109 | * output: "./public/main.js" 110 | * } 111 | * 112 | */ 113 | file: "./app/index.js ./public/main.js", 114 | 115 | /** 116 | * You can override process parameters 117 | */ 118 | override_parameters: { 119 | input: './app/index.js', 120 | output: './public/main.js', 121 | watch: false, 122 | sourcemaps: false, 123 | compress: false 124 | }, 125 | options: {} 126 | } 127 | ] 128 | 129 | module.exports = _tasks 130 | 131 | ``` 132 | 133 | ## Task 134 | 135 | ### Create a new task 136 | 137 | To create a new task, you have to choose between a `Task` object and a `TaskProcess` process. A `TaskProcess` extends `Task`. It is dedicated to child process execution. 138 | 139 | #### Task class 140 | 141 | 142 | ```js 143 | const Task = require('./../lib/Task') 144 | 145 | class MyTask extend Task { 146 | 147 | /** 148 | * Override execute method 149 | * Take care to call `super.execute()` 150 | */ 151 | execute() { 152 | /** 153 | * EXECUTE SOMETHING 154 | */ 155 | super.execute() 156 | } 157 | 158 | /** 159 | * Override kill method 160 | * Take care to call `super.kill()` 161 | */ 162 | kill() { 163 | // Kill or close something 164 | super.kill() 165 | } 166 | 167 | } 168 | 169 | module.exports = MyTask 170 | ``` 171 | 172 | **WARNING** Take care to call `super.execute()` at the end otherwise your task will be not registered inside the `TaskManager` 173 | 174 | 175 | **Exemple :** 176 | 177 | ```js 178 | const Task = require('./../lib/Task') 179 | const Chokidar = require('chokidar') 180 | 181 | class Watcher extend Task { 182 | 183 | constructor() { 184 | super(...arguments) 185 | this.watcher = null 186 | } 187 | 188 | /** 189 | * Override execute method 190 | * Take care to call `super.execute()` 191 | */ 192 | execute() { 193 | const config = this.getConfig() 194 | const watcher = Chokidar.watch(config.file, config.options) 195 | 196 | watcher.on('ready', function(file) { 197 | console.log('Ready to watch') 198 | this.on('add', function(file) { 199 | console.log('Add', file) 200 | }) 201 | }) 202 | 203 | watcher.on('change', function(file) { 204 | console.log('Change', file) 205 | }) 206 | 207 | watcher.on('unlink', function(file) { 208 | console.log('Remove', file) 209 | }) 210 | 211 | this.watcher = watcher 212 | 213 | super.execute() 214 | } 215 | 216 | /** 217 | * Override kill method 218 | * Take care to call `super.kill()` 219 | */ 220 | kill() { 221 | this.watcher.close() 222 | super.kill() 223 | } 224 | 225 | } 226 | 227 | module.exports = Watcher 228 | ``` 229 | 230 | #### TaskProcess class 231 | 232 | 233 | ```js 234 | const TaskProcess = require('./../lib/TaskProcess') 235 | 236 | class MyTaskProcess extend TaskProcess { 237 | /** 238 | * Override execute method 239 | * You must pass your command inside `super.execute` 240 | * `super.execute` returns a `ChildProcess` 241 | */ 242 | execute() { 243 | const command = "pwd" 244 | super.execute(command) 245 | } 246 | } 247 | 248 | module.exports = MyTaskProcess 249 | ``` 250 | 251 | **Exemple :** 252 | 253 | ```js 254 | const TaskProcess = require('./../lib/TaskProcess') 255 | const STYLUS_CLI = path.join(path.dirname(require.resolve('stylus')), 'bin', 'stylus') 256 | const AUTOPREFIXER_PATH = path.dirname(require.resolve('autoprefixer-stylus')) 257 | 258 | 259 | class Stylus extends TaskProcess { 260 | /** 261 | * Override execute method 262 | * You must pass your command inside `super.execute` 263 | */ 264 | execute() { 265 | const config = this.getConfig() 266 | const params = this.getParameters() 267 | 268 | const input = params.input 269 | const output = params.output 270 | 271 | const command = [STYLUS_CLI] 272 | if (params.sourcemaps) command.push("--sourcemap-inline") 273 | if (params.watch) command.push("--watch") 274 | if (params.compress) command.push("--compress") 275 | 276 | if (config.autoprefixer) { 277 | command.push('--use '+AUTOPREFIXER_PATH) 278 | command.push('--with'+JSON.stringify(config.autoprefixer)) 279 | } 280 | 281 | command.push(input) 282 | command.push('--out') 283 | command.push(output) 284 | 285 | const ps = super.execute(command.join(' ')) 286 | ps.on('close', this._onClose) 287 | } 288 | 289 | _onClose() { 290 | console.log('Stylus task is closed') 291 | } 292 | } 293 | 294 | module.exports = Stylus 295 | ``` 296 | 297 | ## Template 298 | 299 | You can create template file directly inside `workflow/templates/{{ extension_name }}`. 300 | 301 | ### Template configuration 302 | 303 | Inside `config.yml`, you configure your template generation. 304 | 305 | **Exemple :** 306 | 307 | ```js 308 | _tasks['template'] = [{ 309 | 310 | // Section template 311 | section: { 312 | output: "index", 313 | destination_path: "./app/sections", 314 | files: [ 315 | "section.html", 316 | "stylus/section.styl", 317 | "js" 318 | ] 319 | }, 320 | 321 | // Component template 322 | component: { 323 | destination_path: "./app/components", 324 | files: [ 325 | "stylus/section.styl", 326 | "js/component.js" 327 | ] 328 | } 329 | 330 | }] 331 | ``` 332 | 333 | `ouput` (optional) is the name of the file generated. By default, the name of the template is taken 334 | 335 | `destination_path` is the directory where files will be generated 336 | 337 | `files` are templates to use to generate files. You can indicate an extension, a filename or a path relative to the template directory 338 | 339 | 340 | ### Template generation 341 | 342 | To generate a template : 343 | 344 | ``` 345 | node workflow {{ template }} {{ name }} 346 | ``` 347 | 348 | **Exemple :** 349 | 350 | ``` 351 | node workflow section MySectionName 352 | ``` 353 | 354 | You can add more parameters 355 | 356 | ``` 357 | npm run template section -- [ -name="MySectionName" --prefix="app" --test="HelloWorld" ] 358 | ``` -------------------------------------------------------------------------------- /app/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: red; 3 | transition: all 500ms; 4 | transform: matrix(1, 0, 0, 1, 0, 0); 5 | border-radius: 20px; 6 | box-sizing: border-box; 7 | } 8 | -------------------------------------------------------------------------------- /app/index.js: -------------------------------------------------------------------------------- 1 | console.log('HELLO') 2 | -------------------------------------------------------------------------------- /app/index.sass: -------------------------------------------------------------------------------- 1 | /* I am SASS */ 2 | body 3 | background-color: red -------------------------------------------------------------------------------- /app/index.styl: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: red 3 | transition: all 500ms 4 | transform: matrix(1, 0, 0, 1, 0, 0) 5 | border-radius: 20px 6 | box-sizing: border-box 7 | } 8 | -------------------------------------------------------------------------------- /app/index.ts: -------------------------------------------------------------------------------- 1 | console.log('from typescript') -------------------------------------------------------------------------------- /app/vendor/index.js: -------------------------------------------------------------------------------- 1 | console.log('VENDOR') 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "workflow", 3 | "version": "1.3.9", 4 | "description": "Set of javascript tasks for front development", 5 | "main": "workflow/index.js", 6 | "scripts": { 7 | "test": "mocha workflow/test", 8 | "stylus:compile": "node workflow stylus -s", 9 | "stylus:watch": "node workflow stylus -s -w", 10 | "stylus:build": "node workflow stylus -m", 11 | "browserify:compile": "node workflow browserify -s", 12 | "browserify:watch": "node workflow browserify -s -w", 13 | "browserify:build": "node workflow browserify -m", 14 | "watcher": "node workflow watcher", 15 | "server": "node workflow server", 16 | "uglify": "node workflow uglify", 17 | "compile": "node workflow stylus:compile browserify:compile", 18 | "watch": "node workflow stylus:watch browserify:watch watcher", 19 | "build": "node workflow stylus:build browserify:build uglify", 20 | "start": "node workflow stylus:watch browserify:watch server" 21 | }, 22 | "author": "", 23 | "license": "ISC", 24 | "dependencies": { 25 | "autoprefixer": "^6.3.3", 26 | "autoprefixer-stylus": "^0.8.1", 27 | "babel-polyfill": "^6.3.14", 28 | "babelify": "^6.1.3", 29 | "browser-sync": "^2.10.0", 30 | "browserify": "^12.0.1", 31 | "colors": "^1.1.2", 32 | "eslint": "^2.2.0", 33 | "fs-extra": "^0.26.2", 34 | "mocha": "^2.4.5", 35 | "mustache": "^2.2.0", 36 | "partialify": "^3.1.5", 37 | "postcss-cli": "^2.5.1", 38 | "rupture": "^0.6.1", 39 | "stylus": "^0.52.4", 40 | "subarg": "^1.0.0", 41 | "typescript": "^1.8.9", 42 | "uglifyjs": "^2.4.10", 43 | "watchify": "^3.6.1" 44 | }, 45 | "workflow": { 46 | "config": "./workflow/config/tasks.js" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Hello World 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /workflow/bin/rails_start: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | SPID=./tmp/pids/server.pid 4 | if [ -f "$SPID" ] 5 | then 6 | PID=$( cat ./tmp/pids/server.pid ) 7 | kill $PID 8 | fi 9 | 10 | for param in "$@" 11 | do 12 | 13 | ### Compile assets 14 | if [ "$param" = "--compile" ] 15 | then 16 | cd frontend && npm run compile 17 | exit 18 | fi 19 | 20 | ### Compile assets and minify them 21 | if [ "$param" = "--build" ] 22 | then 23 | cd frontend && npm run build 24 | exit 25 | fi 26 | 27 | done 28 | 29 | ## Start rails server and frontend watchers 30 | rails s -b 0.0.0.0 & ( sleep 5 && cd frontend/ && npm start ); 31 | -------------------------------------------------------------------------------- /workflow/config/parameters.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * Parameters 5 | * Documentation (https://github.com/substack/subarg) 6 | */ 7 | 8 | module.exports = { 9 | boolean: ['watch', 'compress', 'sourcemap', 'verbose', 'kill_pids', 'help'], 10 | alias: { 11 | input: ['i'], 12 | watch: ['w'], 13 | output: ['o', 'out'], 14 | compress: ['m', 'minify', 'min'], 15 | sourcemap: ['s', 'sourcemaps'], 16 | verbose: ['v', 'debug'], 17 | kill_pids: [], 18 | help: ['h'] 19 | }, 20 | 'default': { 21 | sourcemap: false, 22 | compress: false, 23 | watch: false, 24 | verbose: false, 25 | kill_pids: false, 26 | help: false 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /workflow/config/paths.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | /** 4 | * List of paths 5 | */ 6 | 7 | module.exports = (function() { 8 | const path = require('path') 9 | const fs = require('fs-extra') 10 | 11 | const paths = { 12 | workflow_path: path.join(__dirname, '..'), 13 | bin_path: path.join(__dirname, '..', 'bin'), 14 | tasks_path: path.join(__dirname, '..', 'tasks'), 15 | templates_path: path.join(__dirname, '..', 'templates'), 16 | tmp_path: path.join(__dirname, '..', 'tmp'), 17 | pids_path: path.join(__dirname, '..', 'tmp', 'pids'), 18 | lib_path: path.join(__dirname), 19 | } 20 | 21 | // Create directories from paths 22 | for (let k in paths) { 23 | fs.ensureDirSync(paths[k]) 24 | } 25 | 26 | return paths 27 | 28 | })() 29 | -------------------------------------------------------------------------------- /workflow/config/tasks.js: -------------------------------------------------------------------------------- 1 | const paths = require('./paths') 2 | const _ = require('../lib/functions/object') 3 | 4 | const _tasks = {} 5 | 6 | //_tasks['typescript'] = [ 7 | // { 8 | // file: `./app/index.ts ./public/main.js`, 9 | // options: {}, 10 | // override_parameters: {} 11 | // } 12 | //] 13 | 14 | //_tasks['uglify'] = [ 15 | // { 16 | // file: `./public/main.js ./public/main.js`, 17 | // options: {}, 18 | // override_parameters: {} 19 | // } 20 | //] 21 | 22 | //_tasks['stylus'] = [ 23 | // { 24 | // file: `./app/index.styl ./public/main.css`, 25 | // options: {}, 26 | // override_parameters: {} 27 | // autoprefixer: {remove: false, browsers: ["ie 9", "last 2 versions", "safari 7"]} 28 | // } 29 | //] 30 | 31 | //_tasks['postcss'] = [ 32 | // { 33 | // file: `./app/index.css ./public/main.css`, 34 | // options: {}, 35 | // override_parameters: {} 36 | // } 37 | //] 38 | 39 | //_tasks['sass'] = [ 40 | // { 41 | // file: `./app/index.sass ./public/main.css`, 42 | // options: {}, 43 | // override_parameters: {} 44 | // } 45 | //] 46 | 47 | //_tasks['watcher'] = [ 48 | // { 49 | // file: './public/**/*', 50 | // options: {}, 51 | // override_parameters: { 52 | // watch: true 53 | // } 54 | // } 55 | //] 56 | 57 | //_tasks['server'] = [ 58 | // { 59 | // options: { 60 | // files: ['./public/**/*'], 61 | // 'no-open': true, 62 | // server: './public' 63 | // }, 64 | // override_parameters: { 65 | // watch: true 66 | // } 67 | // } 68 | //] 69 | 70 | 71 | // const browserify_options = { 72 | // options: { 73 | // cache: {}, 74 | // packageCache: {}, 75 | // extensions: [ '.js', '.es' ], 76 | // paths: [ 77 | // './app' 78 | // ] 79 | // }, 80 | // transforms: { 81 | // babelify: { 82 | // modules: "common", 83 | // compact: false, 84 | // comments: true 85 | // }, 86 | // partialify: [ 'svg', 'txt' ], 87 | // watchify: { 88 | // delay: 600 89 | // } 90 | // } 91 | // } 92 | 93 | // _tasks['browserify'] = [ 94 | // _.extend({ 95 | // file: `./app/index.js ./public/main.js`, 96 | // override_parameters: {} 97 | // }, browserify_options), 98 | 99 | // _.extend({ 100 | // file: `./app/vendor/index.js ./public/vendor.js`, 101 | // override_parameters: { 102 | // watch: false 103 | // } 104 | // }, browserify_options) 105 | // ] 106 | 107 | //_tasks['template'] = [{ 108 | // section: { 109 | // output: 'index', 110 | // destination_path: './app/sections', 111 | // files: [ 112 | // 'section.html', 113 | // 'stylus/section.styl', 114 | // 'js' 115 | // ] 116 | // }, 117 | // components: { 118 | // destination_path: './app/components', 119 | // files: [ 120 | // 'stylus/section.styl', 121 | // 'js/section.js' 122 | // ] 123 | // } 124 | //}] 125 | 126 | //_tasks['clean'] = [{}] 127 | 128 | module.exports = _tasks 129 | -------------------------------------------------------------------------------- /workflow/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const path = require('path') 3 | const Help = require('./lib/Help') 4 | const Argv = require('./lib/Argv') 5 | const Print = require('./lib/Print') 6 | const TaskManager = require('./lib/TaskManager') 7 | let config = require('./lib/Config') 8 | const Tasks = require('./tasks') 9 | const packageJSON = require('../package.json') 10 | 11 | /** 12 | * Create and execute a task from parameters 13 | * 14 | * @param args {array} - List of parameters 15 | * @param ignoreExecution - To ignore the task execution 16 | * @returns {*} 17 | */ 18 | const execute = function(args, ignoreExecution) { 19 | ignoreExecution = ignoreExecution || false 20 | 21 | const argv = new Argv(args) 22 | const task_name = argv.fetch()._[0] 23 | const cfg = config[task_name] 24 | 25 | if (!Tasks.isTask(task_name)) { 26 | if (packageJSON.scripts[task_name]) { 27 | Print.log(`"${task_name}" is not a task, but a task from package.json.`) 28 | } else { 29 | Print.log(`"${task_name}" is not a task.`, 'yellow') 30 | } 31 | return null 32 | } 33 | 34 | if (!cfg) { 35 | Print.log(`Task "${task_name}" is not configured.`, 'yellow') 36 | Print.log(`Please see workflow/config/tasks.js or your file configuration written in your package.json`, 'yellow') 37 | return null 38 | } 39 | 40 | const tasks = [] 41 | for (let t, i = 0, len = cfg.length; i < len ; i++) { 42 | t = Tasks.createTask(task_name, cfg[i]) 43 | t.options.argv.replace(args) 44 | tasks.push(t) 45 | } 46 | 47 | if (!ignoreExecution) { 48 | TaskManager.execute(tasks) 49 | } 50 | 51 | return tasks 52 | } 53 | 54 | if (Argv.main.fetch().help) { 55 | return Help() 56 | } 57 | 58 | /** 59 | * By default, execute a task from parameters 60 | */ 61 | if (execute(process.argv.slice(2))) { 62 | return 63 | } 64 | 65 | 66 | /** 67 | * Detect npm context 68 | * If multiple tasks are detected, execute each task 69 | * 70 | * Inside package.json, you can replace the task name by a npm task name 71 | * eg.: 72 | * 73 | * "scripts": { 74 | * "browserify:compile": "node workflow browserify", 75 | * "stylus:compile": "node workflow stylus", 76 | * "compile": "node workflow stylus:compile browserify:compile" 77 | * } 78 | * 79 | * By default, each task is executed one by one. If you want to execute your task 80 | * before the end of the previous, override the watcher parameter to `true` inside `tasks.js`. 81 | * eg.: 82 | * 83 | * _tasks['watcher'] = [ 84 | * { 85 | * file: './public/**', 86 | * override_parameters: { 87 | * watch: true 88 | * } 89 | * } 90 | * ] 91 | * 92 | * Or add the watch option 93 | * 94 | * node workflow watcher -w 95 | * 96 | */ 97 | const scripts = packageJSON.scripts 98 | const commands = Argv.main.fetch()._ 99 | let tasks = [] 100 | 101 | if (commands.length > 0) { 102 | 103 | for (let len = commands.length, i = 0; i < len; i++) { 104 | if (scripts.hasOwnProperty(commands[i])) { 105 | tasks = Array.prototype.concat(tasks, execute(scripts[commands[i]].split(' ').slice(2), true)) 106 | } 107 | } 108 | 109 | // Clean array 110 | let tmp = [] 111 | for (let ln = tasks.length, j = 0; j < ln; j++) { 112 | if (tasks[j]) tmp.push(tasks[j]) 113 | } 114 | 115 | // Execute the array of tasks 116 | TaskManager.execute(tmp) 117 | 118 | return 119 | } 120 | -------------------------------------------------------------------------------- /workflow/lib/Argv.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const subargOptions = require('./../config/parameters') 4 | const subarg = require('subarg') 5 | 6 | /** 7 | * Create an Argv object. 8 | * It is an object that keep your list or arguments 9 | * and parse them with subarg and subargOptions defined in config/parameters.js 10 | * @class Argv 11 | */ 12 | class Argv { 13 | 14 | /** 15 | * @param {Array} [argv] 16 | */ 17 | constructor(argv) { 18 | this._argv = argv || process.argv.slice(2) 19 | } 20 | 21 | /** 22 | * Replace the list of arguments 23 | * @param {Array} argv 24 | */ 25 | replace(argv) { 26 | this._argv = argv 27 | } 28 | 29 | /** 30 | * Reset the list of arguments by the list from the process 31 | */ 32 | reset() { 33 | this._argv = process.argv.slice(2) 34 | } 35 | 36 | /** 37 | * Parse arguments with subargOptions defined in config/parameters.js 38 | * It will return an parsed object 39 | * @returns {Object} 40 | */ 41 | fetch() { 42 | return subarg(this._argv, subargOptions) 43 | } 44 | 45 | } 46 | 47 | /** 48 | * The main Argv object with the process arguments 49 | * @type {Argv} 50 | */ 51 | Argv.main = new Argv 52 | 53 | module.exports = Argv 54 | -------------------------------------------------------------------------------- /workflow/lib/Config.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const path = require('path') 4 | const packageJSON = require('../../package.json') 5 | let config = require('../config/tasks') 6 | 7 | 8 | /** 9 | * Use config file written inside package.json 10 | * 11 | * Inside package.json, you can precise a different config file per environment 12 | * eg.: 13 | * 14 | * "workflow": { 15 | * "production": "./workflow/production.js" 16 | * "development": "./workflow/development.js" 17 | * } 18 | * 19 | * "workflow": { 20 | * "config": "./workflow/config/tasks.js" 21 | * } 22 | * 23 | * To precise an environment : 24 | * 25 | * "workflow": { 26 | * "env": "production" 27 | * } 28 | * 29 | * OR 30 | * 31 | * NODE_ENV=production node workflow browserify 32 | * 33 | * The environment by default is `development` 34 | * 35 | */ 36 | if (packageJSON.workflow) { 37 | const w = packageJSON.workflow 38 | const ENV = w['env'] || process.env.NODE_ENV || 'development' 39 | 40 | if (w[ENV]) config = require(path.resolve(w[ENV])) 41 | else if (w['config']) config = require(path.resolve(w['config'])) 42 | } 43 | 44 | module.exports = config 45 | -------------------------------------------------------------------------------- /workflow/lib/Help.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Print = require('./Print') 4 | const Argv = require('./Argv') 5 | const Tasks = require('../tasks') 6 | 7 | const TaskDescription = function(task_name, task) { 8 | let header = `\n== ${task_name.toUpperCase()} ==\n` 9 | if (task.description) { 10 | Print.log(`${header}${task.description}`, 'yellow') 11 | return 12 | } 13 | Print.log(`${header}No description found`, 'yellow') 14 | return 15 | } 16 | 17 | const Documentation = function() { 18 | 19 | console.log(`== Frontend Workflow == 20 | 21 | Available tasks : \n - ${Tasks.tasks.join('\n - ')} 22 | 23 | Can be executed with: 24 | node workflow stylus 25 | 26 | Display task description: 27 | node workflow stylus --help 28 | 29 | Options: 30 | -i, --input Input files 31 | -o, --output Output files 32 | 33 | -w, --watch Add watch files 34 | -s, --sourcemaps Add sourcemaps 35 | -m, --min, --compress Minify file 36 | 37 | -v, --debug, --verbose Display extra informations 38 | --kill_pids Destroy PIDS inside tmp/pids 39 | -h, --help Display help 40 | `) 41 | } 42 | 43 | module.exports = function() { 44 | const task_name = Argv.main.fetch()._[0] 45 | const task = Tasks.getTask(task_name) 46 | 47 | if (task) { return TaskDescription(task_name, task) } 48 | Documentation() 49 | } 50 | -------------------------------------------------------------------------------- /workflow/lib/Print.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict' 3 | 4 | const _colors = require('colors/safe') 5 | 6 | class Print { 7 | 8 | constructor() { 9 | this.argv = require('./Argv').main 10 | this.colors = _colors 11 | this._setup() 12 | } 13 | 14 | _setup() { 15 | // const verbose = process.argv.indexOf('-v') !== -1 || process.argv.indexOf('--verbose') !== -1 16 | this.verboseMode = this.argv.fetch().verbose 17 | } 18 | 19 | /** 20 | * console.log with colors 21 | * @param {*} value 22 | * @param {string|Object} [color_or_options={}] 23 | * @param {boolean} [color_or_options.no_color=false] color_or_options.no_color 24 | * @param {boolean} [color_or_options.use_date=true] color_or_options.use_date 25 | * @param {boolean} [color_or_options.is_array=true] color_or_options.is_array 26 | * @param {string} [color_or_options.color] 27 | * 28 | */ 29 | log(value, color_or_options) { 30 | if (value === undefined) return 31 | 32 | const opts = typeof color_or_options === 'object' ? color_or_options : {} 33 | let color = typeof color_or_options === 'string' ? color_or_options : null 34 | color = opts.color || color 35 | const use_date = typeof opts.use_date === 'boolean' ? opts.use_date : true 36 | const no_color = typeof opts.no_color === 'boolean' ? opts.no_color : false 37 | const is_array = typeof opts.is_array === 'boolean' ? opts.is_array : false 38 | 39 | // Transform value to string 40 | let string = typeof value === 'string' ? [value] : [value.toString()] 41 | string = is_array ? value : string 42 | string = string.join(' ') 43 | 44 | if (no_color) { 45 | string = _colors['strip'](string) 46 | } else if (color) { 47 | string = _colors[color](_colors['strip'](string)) 48 | } 49 | 50 | if (use_date) { 51 | let time = this.getTime() 52 | let date = no_color ? time : _colors['cyan'](time) 53 | console.log('%s %s', date, string) 54 | return 55 | } 56 | 57 | console.log('%s', string) 58 | } 59 | 60 | /** 61 | * 62 | * @returns {string} 63 | */ 64 | getTime() { 65 | const d = new Date(); 66 | const h = this.pad(d.getHours(), 2) 67 | const m = this.pad(d.getMinutes(), 2) 68 | const s = this.pad(d.getSeconds(), 2) 69 | return `[${h}:${m}:${s}]` 70 | } 71 | 72 | /** 73 | * 74 | * @param {string} value 75 | * @param {number} max 76 | * @returns {string} 77 | */ 78 | pad(value, max) { 79 | if (value === undefined) return 80 | let s = value.toString(); 81 | return s.length < max ? this.pad("0"+value, max) : s 82 | } 83 | 84 | /** 85 | * 86 | * @param string 87 | * @returns {string} 88 | */ 89 | clean(value) { 90 | if (value === undefined) return 91 | return value.toString().replace(/^(\s|\n)+|(\s|\n)+$/g, '') 92 | } 93 | 94 | /** 95 | * console.log with colors only verbose mode activated 96 | * @param {*} value 97 | * @param {string|Object} [color_or_options={}] 98 | * @param {boolean} [color_or_options.no_color=false] color_or_options.no_color 99 | * @param {boolean} [color_or_options.use_date=true] color_or_options.use_date 100 | * @param {boolean} [color_or_options.is_array=true] color_or_options.is_array 101 | * @param {string} [color_or_options.color] 102 | */ 103 | verbose(value, color_or_options) { 104 | if (this.verboseMode) this.log(value, color_or_options) 105 | } 106 | 107 | } 108 | 109 | const _print = new Print 110 | module.exports = _print 111 | -------------------------------------------------------------------------------- /workflow/lib/ProcessManager.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fs = require('fs') 4 | const exec = require('child_process').exec 5 | const Print = require('./Print') 6 | const Argv = require('./Argv') 7 | const Bind = require('./mixins/Bind') 8 | const paths = require('./../config/paths') 9 | 10 | class ProcessManager { 11 | 12 | constructor() { 13 | Bind.assign(this, [ '_onBeforeExit' ]) 14 | 15 | this.processes = {} 16 | 17 | this.activate() 18 | if (Argv.main.fetch().kill_pids) this.clean() 19 | } 20 | 21 | /** 22 | * Activate listeners 23 | */ 24 | activate() { 25 | process.on('beforeExit', this._onBeforeExit) 26 | } 27 | 28 | /** 29 | * Desactivate listeners 30 | */ 31 | desactivate() { 32 | process.removeListener('beforeExit', this._onBeforeExit) 33 | } 34 | 35 | /** 36 | * Execute a child process 37 | * @param {string} psName - Name of the process 38 | * @param {string} command - Command to execute 39 | * @returns {ChildProcess} 40 | */ 41 | executeProcess(psName, command) { 42 | 43 | const ps = exec(command) 44 | ps.GUID = psName 45 | this._createTemporaryFile(ps) 46 | 47 | ps.stdout.setEncoding('utf-8') 48 | ps.stdout.on('data', function(data) { 49 | data = Print.clean(data.toString('utf-8')) 50 | Print.log([ 51 | Print.colors['magenta'](`[${psName}]`), 52 | data 53 | ], { is_array: true }) 54 | }) 55 | 56 | ps.stderr.on('data', function(data) { 57 | data = Print.clean(data.toString('utf-8')) 58 | Print.log([ 59 | Print.colors['red'](`[${psName}]`), 60 | data 61 | ], { is_array: true }) 62 | }) 63 | 64 | ps.on('close', (function(code){ 65 | Print.log(`[${psName}] child process exited with code ${code}`, code === 0 || code === null ? 'magenta' : 'red') 66 | this._deleteTemporaryFile(this.processes[psName]) 67 | }).bind(this)) 68 | 69 | return ps 70 | 71 | } 72 | 73 | /** 74 | * Kill the child process 75 | * @param {ChildProcess|string} psOrGUID 76 | */ 77 | killProcess(psOrGUID) { 78 | const child_ps = typeof psOrGUID === 'string' ? this.processes[psOrGUID] : psOrGUID 79 | child_ps.kill() 80 | this._deleteTemporaryFile(child_ps.GUID) 81 | } 82 | 83 | /** 84 | * Kill all pids inside tmp/pids directory 85 | */ 86 | clean() { 87 | const files = fs.readdirSync( `${paths.pids_path}` ) 88 | for (let filename, i = 0, len = files.length; i < len; i++) { 89 | filename = files[i] 90 | if (filename.match(/\.pid$/)) { 91 | const PID = fs.readFileSync(`${paths.pids_path}/${filename}`, 'utf8') 92 | try { 93 | process.kill(PID, 'SIGINT') 94 | Print.log(`Process ${PID} is killed (${action}.pid)`, 'yellow') 95 | } catch(e) { 96 | Print.log(`No process '${PID}' founded`, 'grey') 97 | } 98 | fs.unlinkSync(`${paths.pids_path}/${filename}`) 99 | } 100 | } 101 | } 102 | 103 | /** 104 | * Before exiting kill all child process 105 | * @private 106 | */ 107 | _onBeforeExit() { 108 | for (var k in this.processes) { 109 | this.killProcess(k) 110 | } 111 | } 112 | 113 | /** 114 | * Create a temporary file with the pid number 115 | * @param {ChildProcess} ps 116 | * @private 117 | */ 118 | _createTemporaryFile(ps) { 119 | const psName = ps.GUID 120 | const stream = fs.createWriteStream(`${paths.pids_path}/${psName}.pid`) 121 | stream.write(ps.pid.toString()) 122 | this.processes[psName] = ps 123 | } 124 | 125 | /** 126 | * Delete the temporary file of the process 127 | * @param {ChildProcess} ps 128 | * @private 129 | */ 130 | _deleteTemporaryFile(ps) { 131 | const psName = ps.GUID 132 | const file_path = `${paths.pids_path}/${psName}.pid` 133 | if (fs.existsSync(file_path)) fs.unlinkSync(file_path) 134 | delete this.processes[psName] 135 | } 136 | 137 | } 138 | 139 | module.exports = new ProcessManager 140 | 141 | -------------------------------------------------------------------------------- /workflow/lib/Task.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const EventEmitter = require('events').EventEmitter 4 | const guid = require('./functions/guid').guid 5 | const Bind = require('./mixins/Bind') 6 | const TaskManager = require('./TaskManager') 7 | const TaskOptions = require('./TaskOptions') 8 | 9 | /** 10 | * Create a task object 11 | * @extend EventEmitter 12 | * @class Task 13 | */ 14 | class Task extends EventEmitter { 15 | 16 | /** 17 | * 18 | * @param {string} name 19 | * @param {Object} config 20 | */ 21 | constructor(name, config) { 22 | super() 23 | Bind.assign(this, [ '_onExit' ]) 24 | 25 | this.options = new TaskOptions(config) 26 | this._name = name || 'noname' 27 | this.guid = guid() 28 | this.running = false 29 | } 30 | 31 | /** 32 | * Activate listeners 33 | */ 34 | activate() { 35 | process.on('beforeExit', this._onExit) 36 | } 37 | 38 | /** 39 | * Desactivate listeners 40 | */ 41 | desactivate() { 42 | process.on('beforeExit', this._onExit) 43 | } 44 | 45 | /** 46 | * Get the full name of task 47 | * @returns {string} 48 | */ 49 | get fullname() { 50 | return this._name + '_' + this.guid 51 | } 52 | 53 | /** 54 | * Set the name of task 55 | * @param {string} value 56 | */ 57 | set name(value) { 58 | this._name = value 59 | } 60 | 61 | /** 62 | * Get the shorten name of task 63 | * @returns {string} 64 | */ 65 | get name() { 66 | return this._name + '_' + this.guid.slice(0, 4) 67 | } 68 | 69 | /** 70 | * Get the task configuration 71 | * @returns {Object} 72 | */ 73 | getConfig() { 74 | return this.options.getConfig() 75 | } 76 | 77 | /** 78 | * Get the task parameters 79 | * @returns {Object} 80 | */ 81 | getParameters() { 82 | return this.options.getParameters() 83 | } 84 | 85 | /** 86 | * Execute the task 87 | */ 88 | execute() { 89 | if (this.running) return 90 | this.running = true 91 | if (typeof this.activate === 'function') this.activate() 92 | TaskManager.emit('task:execute', this) 93 | } 94 | 95 | /** 96 | * Kill the task 97 | */ 98 | kill() { 99 | if (!this.running) return 100 | this.running = false 101 | if (typeof this.desactivate === 'function') this.desactivate() 102 | TaskManager.emit('task:kill', this) 103 | } 104 | 105 | /** 106 | * It is difficult to detect when a task is finished or not. 107 | * So I listen the main process event 'beforeExit', to detect that. 108 | * Call the kill method to dispatch 'kill' event and desactivate listeners 109 | * @private 110 | */ 111 | _onExit() { 112 | this.kill() 113 | } 114 | } 115 | 116 | /** 117 | * Create a task 118 | * @returns {Task} 119 | */ 120 | Task.create = function() { 121 | return new (this)(...arguments) 122 | } 123 | 124 | module.exports = Task 125 | -------------------------------------------------------------------------------- /workflow/lib/TaskManager.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const EventEmitter = require('events').EventEmitter 4 | const Print = require('./Print') 5 | const Bind = require('./mixins/Bind') 6 | 7 | class TaskManager extends EventEmitter { 8 | constructor() { 9 | super() 10 | Bind.assign(this, [ '_onTaskExecute', '_onTaskKill', '_onBeforeExit' ]) 11 | 12 | this.tasks = {} 13 | 14 | this.activate() 15 | } 16 | 17 | activate() { 18 | // process.on('beforeExit', this._onBeforeExit) 19 | process.on('SIGINT', this._onBeforeExit) 20 | this.on('task:execute', this._onTaskExecute) 21 | this.on('task:kill', this._onTaskKill) 22 | } 23 | 24 | desactivate() { 25 | // process.off('beforeExit', this._onBeforeExit) 26 | process.off('SIGINT', this._onBeforeExit) 27 | this.off('task:execute', this._onTaskExecute) 28 | this.off('task:kill', this._onTaskKill) 29 | } 30 | 31 | register(task) { 32 | if (this.tasks[task.name]) { 33 | Print.verbose(`[${task.name}] already registered`, 'yellow') 34 | return 35 | } 36 | Print.verbose(`[${task.name}] is registered with success`, 'green') 37 | this.tasks[task.name] = task 38 | } 39 | 40 | unregister(taskOrName) { 41 | let name 42 | if (typeof taskOrName === "string") name = taskOrName 43 | if (typeof taskOrName.name === "string") name = taskOrName.name 44 | 45 | if (!this.tasks[name]) { 46 | Print.verbose(`'${name}' is not registered`, 'yellow') 47 | return 48 | } 49 | 50 | Print.verbose(`'${name}' is unregistered with success`, 'green') 51 | delete this.tasks[name] 52 | } 53 | 54 | _onTaskExecute(task) { 55 | this.register(task) 56 | } 57 | 58 | _onTaskKill(task) { 59 | this.unregister(task) 60 | } 61 | 62 | _onBeforeExit() { 63 | if (this._hasTasks()) { 64 | this._killAll() 65 | setTimeout(this._onBeforeExit, 500) 66 | return 67 | } 68 | process.exit() 69 | } 70 | 71 | _hasTasks() { 72 | let i = 0 73 | for (let k in this.tasks) { 74 | i++ 75 | } 76 | return i !== 0 77 | } 78 | 79 | _killAll() { 80 | for (let k in this.tasks) { 81 | this.tasks[k].kill() 82 | } 83 | } 84 | 85 | execute(taskOrTasks) { 86 | // Detect if it's an array 87 | if (typeof taskOrTasks.length === 'undefined') { 88 | taskOrTasks.execute() 89 | return 90 | } 91 | 92 | // Execute the array of tasks, one by one 93 | const tasks = taskOrTasks 94 | let current_task = null 95 | let len = tasks.length 96 | let index = 0 97 | 98 | // Sort tasks by no-watchers and watchers 99 | tasks.sort(function(task) { return task.getParameters().watch }) 100 | 101 | const _onNext = (function() { 102 | if (current_task) { 103 | this.removeListener('task:kill', _onNext) 104 | } 105 | 106 | if (tasks.length > 0) { 107 | index++ 108 | current_task = tasks.shift() 109 | if (current_task) { 110 | this.on('task:kill', _onNext) 111 | 112 | const isWatching = current_task.getParameters().watch 113 | let msg = `Execute task [${current_task.name}] (${index}/${len})` 114 | 115 | if (isWatching) { 116 | msg += '. Watching...' 117 | } 118 | 119 | Print.log(msg, 'white') 120 | 121 | try { 122 | current_task.execute() 123 | if (isWatching) { 124 | setTimeout(_onNext, 1000) // Execute the next task if the current is a watcher 125 | } 126 | } catch(e) { 127 | Print.log(e, 'red') 128 | } 129 | } else { 130 | _onNext() 131 | } 132 | } 133 | }).bind(this) 134 | 135 | _onNext() 136 | } 137 | 138 | } 139 | 140 | module.exports = new TaskManager 141 | -------------------------------------------------------------------------------- /workflow/lib/TaskOptions.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict' 3 | 4 | const Argv = require('./Argv') 5 | const _exports = [ 6 | 'input', 7 | 'output', 8 | 'watch', 9 | 'sourcemaps', 10 | 'compress' 11 | ] 12 | 13 | /** 14 | * Create TaskOptions object. 15 | * This object has the config task and parameters (Argv object) 16 | * @class 17 | */ 18 | class TaskOptions { 19 | 20 | /** 21 | * 22 | * @param {Object} config 23 | */ 24 | constructor(config) { 25 | this._config = config 26 | this.argv = new Argv 27 | } 28 | 29 | /** 30 | * Get parameters 31 | * @returns {{argv: Object}} 32 | */ 33 | getParameters() { 34 | const argv = this.argv.fetch() 35 | const result = {argv:argv} 36 | 37 | for (let len = _exports.length, i = 0; i < len; i++) { 38 | result[_exports[i]] = argv[_exports[i]] 39 | } 40 | 41 | const override = this._config.override_parameters || {} 42 | 43 | if (this._config.file) { 44 | const input_output = this._config.file.split(' ') 45 | override.input = input_output[0] 46 | override.output = input_output[1] 47 | } 48 | 49 | for (let len = _exports.length, i = 0; i < len; i++) { 50 | if (result.hasOwnProperty(_exports[i]) && override.hasOwnProperty(_exports[i])) { 51 | result[_exports[i]] = override[_exports[i]] 52 | } 53 | } 54 | 55 | return result 56 | } 57 | 58 | /** 59 | * Get config 60 | * @returns {Object} 61 | */ 62 | getConfig() { 63 | return this._config 64 | } 65 | 66 | } 67 | 68 | module.exports = TaskOptions 69 | -------------------------------------------------------------------------------- /workflow/lib/TaskProcess.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const ProcessManager = require('../lib/ProcessManager') 4 | const Task = require('./Task') 5 | 6 | class TaskProcess extends Task { 7 | constructor() { 8 | super(...arguments) 9 | this.ps = null 10 | } 11 | 12 | /** 13 | * Activate listeners 14 | */ 15 | activate() { 16 | this.ps.on('close', this._onExit) 17 | } 18 | 19 | /** 20 | * Desactivate listeners 21 | */ 22 | desactivate() { 23 | this.ps.removeListener('close', this._onExit) 24 | } 25 | 26 | /** 27 | * Execute command to a child process 28 | * @param {string} command 29 | * @returns {null|ChildProcess} 30 | */ 31 | execute(command) { 32 | if (this.running) return null 33 | this.ps = ProcessManager.executeProcess(this.name, command) 34 | super.execute() 35 | return this.ps 36 | } 37 | 38 | /** 39 | * Kill the child process 40 | */ 41 | kill() { 42 | if (!this.running) return null 43 | if (this.ps) ProcessManager.killProcess(this.ps) 44 | } 45 | 46 | /** 47 | * When the process is killed 48 | * Call the super.kill method to dispatch 'kill' event and desactivate listeners 49 | * @private 50 | */ 51 | _onExit() { 52 | super._onExit() 53 | super.kill() 54 | } 55 | } 56 | 57 | module.exports = TaskProcess 58 | -------------------------------------------------------------------------------- /workflow/lib/functions/guid.js: -------------------------------------------------------------------------------- 1 | const s4 = function () { 2 | return Math.floor((1 + Math.random()) * 0x10000) 3 | .toString(16) 4 | .substring(1); 5 | } 6 | 7 | const guid = function () { 8 | return s4() + s4() + '-' + s4() + '-' + s4() + '-' + 9 | s4() + '-' + s4() + s4() + s4(); 10 | } 11 | 12 | 13 | module.exports = { 14 | guid: guid, 15 | s4: s4 16 | } 17 | -------------------------------------------------------------------------------- /workflow/lib/functions/object.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const merge = function() { 4 | return Object.assign( ...arguments ) 5 | } 6 | 7 | const clone = function( a ) { 8 | return JSON.parse(JSON.stringify(a)) 9 | } 10 | 11 | const extend = function() { 12 | const args = [...arguments] 13 | const a = args[0] 14 | 15 | const items = args.map(function(arg) { 16 | if (arg === a) return a 17 | return clone(arg) 18 | }) 19 | 20 | return merge(...items) 21 | } 22 | 23 | module.exports = { 24 | merge: merge, 25 | clone: clone, 26 | extend: extend 27 | } 28 | -------------------------------------------------------------------------------- /workflow/lib/functions/require_dir.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const requireDir = function(dir) { 4 | 5 | const fs = require('fs') 6 | const path = require('path') 7 | const paths = require('paths') 8 | 9 | // if (typeof absolute === 'undefined') absolute = false 10 | // if (!absolute) { dir = `${paths.workflow_path}/${dir}` }//dir = `${__dirname}/../${dir}` } 11 | 12 | if (!path.isAbsolute(dir)) { dir = `${paths.workflow_path}/${dir}` } 13 | 14 | var files = fs.readdirSync( `${dir}` ) 15 | var result = {} 16 | for (var pth, i = 0; i < files.length; i++) { 17 | pth = `${dir}/${files[i]}` 18 | if (fs.statSync(pth).isFile() && path.extname(pth).match(/\.js$/i)) { 19 | result[files[i].split('.')[0]] = require(pth) 20 | } 21 | } 22 | 23 | return result 24 | 25 | } 26 | 27 | module.exports = requireDir 28 | -------------------------------------------------------------------------------- /workflow/lib/mixins/Bind.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const _getBinder = function(fnName, ctx) { 4 | if (!ctx) { ctx = this; } 5 | if (!this._binders) this._binders = [] 6 | return this._binders.find((binder) => { 7 | return binder.fnName === fnName && binder.ctx === ctx 8 | }) 9 | } 10 | 11 | const _bind = function(fnNameOrArray, ctx, override){ 12 | if (!ctx) { ctx = this; } 13 | if (typeof override !== 'boolean') override = true 14 | 15 | const fnName = fnNameOrArray; 16 | if (typeof fnNameOrArray !== 'string') { 17 | for (var i = 0; i < fnNameOrArray.length; i++) { 18 | this.bind(fnNameOrArray[i]) 19 | } 20 | return; 21 | } 22 | 23 | let binder = this.getBinder(fnName, ctx) 24 | if(!binder) { 25 | binder = { fnName: fnName, ctx: ctx, original: ctx[fnName] } 26 | this._binders.push(binder) 27 | } 28 | 29 | if (override) { 30 | if (!ctx[fnName]) { 31 | console.warn('The method '+fnName+' does not exist') 32 | return; 33 | } 34 | ctx[fnName] = binder.ctx[binder.fnName].bind(binder.ctx) 35 | } 36 | 37 | } 38 | 39 | const _unbind = function(fnNameOrArray, ctx) { 40 | if (!ctx) { ctx = this; } 41 | 42 | const fnName = fnNameOrArray; 43 | if (typeof fnNameOrArray !== 'string') { 44 | for (var i = 0; i < fnNameOrArray.length; i++) { 45 | this.bind(fnNameOrArray[i]) 46 | } 47 | return; 48 | } 49 | 50 | const binder = this.getBinder(fnName, ctx) 51 | if (!binder) { 52 | console.warn(`Cannot unbind "${fnName}", because this method was never binded before`) 53 | return 54 | } 55 | 56 | ctx[fnName] = binder.original 57 | } 58 | 59 | const _exports = { 60 | bind: _bind, 61 | unbind: _unbind, 62 | getBinder: _getBinder 63 | } 64 | 65 | const _assign = function(ctx, fnNameOrArray) { 66 | var _instance = Object.assign(ctx, _exports) 67 | _instance.bind(fnNameOrArray) 68 | return _instance 69 | } 70 | 71 | _exports.assign = _assign 72 | 73 | module.exports = _exports 74 | -------------------------------------------------------------------------------- /workflow/tasks/browserify.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fs = require('fs-extra') 4 | const path = require('path') 5 | const browserify = require('browserify') 6 | const watchify = require('watchify') 7 | const partialify = require('partialify/custom') 8 | const babelify = require('babelify') 9 | 10 | const Print = require('./../lib/Print') 11 | const Task = require('./../lib/Task') 12 | 13 | const _ = require('../lib/functions/object') 14 | 15 | 16 | class Browserify extends Task { 17 | 18 | constructor() { 19 | super(...arguments) 20 | this.b = null 21 | } 22 | 23 | execute() { 24 | super.execute() 25 | 26 | // Configure 27 | const params = this.getParameters() 28 | const config = this.getConfig() 29 | const browserify_options = config.options 30 | const input = params.input 31 | const output = params.output 32 | const tmp_output = `${__dirname}/../tmp/${path.basename(params.output)}` 33 | const name = this.name 34 | 35 | browserify_options.debug = params.sourcemaps 36 | config.transforms.babelify.sourceMaps = params.sourcemaps ? 'inline' : false 37 | 38 | // Check tmp_output directory exists 39 | fs.ensureDirSync(path.dirname(tmp_output)) 40 | 41 | // Functions 42 | const bundle = function() { 43 | var bndle = b.bundle(); 44 | bndle = bndle.on('error', onError); 45 | 46 | if (tmp_output) { 47 | bndle.pipe(fs.createWriteStream(tmp_output)); 48 | } else { 49 | bndle.pipe(process.stdout); 50 | } 51 | } 52 | 53 | const onLabel = function(e) { 54 | const i = input.replace('./', ''); 55 | if (e.match(i)) { 56 | fs.move(tmp_output, output, { clobber: true }, function() { 57 | Print.log([ 58 | Print.colors['magenta'](`[${name}]`), 59 | Print.colors['gray']('compiled'), 60 | output 61 | ], { is_array: true }) 62 | }) 63 | } 64 | } 65 | 66 | const onError = function(err) { 67 | Print.log(`[${name}] Error`, 'red') 68 | Print.log(err.message, true, 'red') 69 | } 70 | 71 | const onLog = function(msg) { 72 | Print.verbose(`[${name}] [Wachify] ${msg}`, 'white') 73 | } 74 | 75 | const onUpdate = bundle 76 | 77 | // Force create a new cache 78 | if (browserify_options.cache) { 79 | browserify_options.cache = {} 80 | browserify_options.packageCache = {} 81 | } 82 | 83 | // Configure Browserify 84 | var b = browserify(input, browserify_options) 85 | b.on('label', onLabel) 86 | 87 | // Configure Watchify 88 | if (params.watch) { 89 | b = watchify(b, config.transforms.watchify) 90 | b.on('update', onUpdate) 91 | b.on('log', onLog) 92 | } 93 | 94 | // Configure transforms 95 | if (config.transforms.babelify) b.transform(babelify, config.transforms.babelify); 96 | if (config.transforms.partialify) b.transform(partialify.alsoAllow(config.transforms.partialify)); 97 | 98 | // Start bundle 99 | this.b = b 100 | bundle() 101 | } 102 | 103 | kill() { 104 | if (typeof this.b.close === 'function') this.b.close() 105 | super.kill() 106 | } 107 | 108 | } 109 | 110 | Browserify.description = `Browserify lets you require('modules') in the browser by bundling up all of your dependencies.` 111 | 112 | module.exports = Browserify 113 | -------------------------------------------------------------------------------- /workflow/tasks/clean.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict'; 3 | 4 | const Task = require('./../lib/Task') 5 | const ProcessManager = require('./../lib/ProcessManager') 6 | 7 | class Clean extends Task { 8 | 9 | execute() { 10 | super.execute() 11 | ProcessManager.clean() 12 | } 13 | 14 | } 15 | 16 | Clean.description = `Kill all process available inside workflow/tmp/` 17 | 18 | module.exports = Clean 19 | -------------------------------------------------------------------------------- /workflow/tasks/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const path = require('path') 4 | const paths = require('../config/paths') 5 | const fs = require('fs') 6 | 7 | const files = fs.readdirSync(paths.tasks_path) 8 | const _tasks = [] 9 | for (let i = 0, len = files.length; i < len; i++) { 10 | if (path.extname(files[i]) === '.js' && files[i] !== 'index.js') { 11 | _tasks.push(files[i].replace('.js', '')) 12 | } 13 | } 14 | 15 | const _isTask = function(task_name) { 16 | return _tasks.indexOf(task_name) !== -1 17 | } 18 | 19 | const _getTask = function(task_name) { 20 | if (!_isTask(task_name)) return null 21 | return require(path.join(paths.tasks_path, task_name+'.js')) 22 | } 23 | 24 | const _createTask = function(task_name, config) { 25 | const Task = _getTask(task_name) 26 | if (!Task) return null 27 | return Task.create(task_name, config) 28 | } 29 | 30 | module.exports = { 31 | tasks: _tasks, 32 | isTask: _isTask, 33 | getTask: _getTask, 34 | createTask: _createTask 35 | } -------------------------------------------------------------------------------- /workflow/tasks/postcss.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict'; 3 | 4 | const path = require('path') 5 | const TaskProcess = require('../lib/TaskProcess') 6 | 7 | const POSTCSS_CLI = path.join(path.dirname(require.resolve('postcss-cli')), 'bin', 'postcss') 8 | const AUTOPREFIXER_PATH = path.join(path.dirname(require.resolve('autoprefixer')), '..') 9 | 10 | class PostCSS extends TaskProcess { 11 | 12 | execute() { 13 | const params = this.getParameters() 14 | 15 | const input = params.input 16 | const output = params.output 17 | 18 | var command = [POSTCSS_CLI] 19 | if (params.sourcemaps) command.push("--map") 20 | if (params.watch) command.push("--watch") 21 | 22 | command.push(`--use ${AUTOPREFIXER_PATH}`) 23 | command.push(`--output ${output}`) 24 | command.push(input) 25 | 26 | super.execute(command.join(' ')) 27 | } 28 | 29 | } 30 | 31 | module.exports = PostCSS 32 | -------------------------------------------------------------------------------- /workflow/tasks/sass.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict'; 3 | 4 | const TaskProcess = require('../lib/TaskProcess') 5 | 6 | class SASS extends TaskProcess { 7 | 8 | execute() { 9 | const params = this.getParameters() 10 | 11 | const input = params.input 12 | const output = params.output 13 | 14 | var command = ['sass'] 15 | if (params.bourbon) command.push('-r bourbon') 16 | 17 | if (params.sourcemaps) command.push("--sourcemap=inline") 18 | if (!params.sourcemaps) command.push("--sourcemap=none") 19 | if (params.watch) command.push("--watch") 20 | if (params.compress) { 21 | command.push("--style") 22 | command.push("compressed") 23 | } 24 | if (params.scss) command.push("--scss") 25 | command.push(input + ':' + output) 26 | 27 | super.execute(command.join(' ')) 28 | } 29 | 30 | } 31 | 32 | SASS.description = `An extension of CSS that adds power and elegance to the basic language. It allows to use variables, nested rules, mixins, inline imports, and more. 33 | Compile '.sass' and '.scss' file to '.css'` 34 | 35 | module.exports = SASS 36 | -------------------------------------------------------------------------------- /workflow/tasks/server.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict'; 3 | 4 | const path = require('path') 5 | const TaskProcess = require('./../lib/TaskProcess') 6 | const BROWSER_SYNC_CLI = path.join(path.dirname(require.resolve('browser-sync')), 'bin', 'browser-sync.js') 7 | 8 | class Server extends TaskProcess { 9 | 10 | execute() { 11 | const config = this.getConfig() 12 | const options = config.options || {} 13 | 14 | const command = [BROWSER_SYNC_CLI, 'start'] 15 | let value = null 16 | 17 | for (let key in options) { 18 | value = options[key] 19 | if (typeof value === 'boolean') { 20 | if (value) command.push('--'+key) 21 | } else { 22 | if (typeof value !== 'string') value = value.toString() 23 | command.push(`--${key}='${value}'`) 24 | } 25 | } 26 | 27 | super.execute(command.join(' ')) 28 | } 29 | 30 | } 31 | 32 | Server.description = `Start a HTTP server with BrowserSync` 33 | 34 | module.exports = Server 35 | -------------------------------------------------------------------------------- /workflow/tasks/stylus.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict'; 3 | 4 | const path = require('path') 5 | const fs = require('fs') 6 | const TaskProcess = require('../lib/TaskProcess') 7 | const paths = require('../config/paths') 8 | 9 | const STYLUS_CLI = path.join(path.dirname(require.resolve('stylus')), 'bin', 'stylus') 10 | const RUPTURE_PATH = path.dirname(require.resolve('rupture')) 11 | const AUTOPREFIXER_PATH = path.dirname(require.resolve('autoprefixer-stylus')) 12 | 13 | class Stylus extends TaskProcess { 14 | 15 | execute() { 16 | const params = this.getParameters() 17 | const config = this.getConfig() 18 | 19 | const input = params.input 20 | let output = params.output 21 | 22 | /** 23 | * Handle output with an extension different of ".css" 24 | */ 25 | let outputExtname = path.extname(output) 26 | if (outputExtname !== '.css') { 27 | output = output.replace(outputExtname, '') 28 | } else { 29 | outputExtname = null 30 | } 31 | 32 | const applyExtname = function() { 33 | if (outputExtname) { 34 | try { 35 | fs.renameSync(output, output+outputExtname) 36 | } catch(e) {} 37 | } 38 | } 39 | 40 | /** 41 | * Configuration 42 | */ 43 | const command = [STYLUS_CLI]; 44 | if (params.sourcemaps) { command.push("--sourcemap-inline"); } 45 | if (params.watch) { command.push("--watch"); } 46 | if (params.compress) { command.push("--compress"); } 47 | 48 | // Allow css file import 49 | command.push("--include-css"); 50 | 51 | // import autoprefixer 52 | if (config.autoprefixer) { 53 | command.push("--use"); 54 | command.push(AUTOPREFIXER_PATH); 55 | command.push(`--with`); 56 | var stringified = JSON.stringify(config.autoprefixer); 57 | stringified = stringified.replace(/\"/g, "\\\""); 58 | command.push(`"${stringified}"`); 59 | } 60 | 61 | // import rupture 62 | command.push("--use"); 63 | command.push(RUPTURE_PATH); 64 | 65 | // Set input/output 66 | command.push(input); 67 | command.push("--out"); 68 | command.push(output); 69 | 70 | const ps = super.execute(command.join(' ')) 71 | ps.stdout.on('data', applyExtname) 72 | ps.on('close', applyExtname) 73 | } 74 | 75 | } 76 | 77 | Stylus.description = `Stylus is a revolutionary new language, providing an efficient, dynamic, and expressive way to generate CSS. Supporting both an indented syntax and regular CSS style. 78 | Compile '.styl' to '.css'` 79 | 80 | module.exports = Stylus 81 | -------------------------------------------------------------------------------- /workflow/tasks/template.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fs = require('fs-extra') 4 | const m = require('mustache') 5 | const path = require('path') 6 | const Print = require('../lib/Print') 7 | const Task = require('../lib/Task') 8 | const paths = require('../config/paths') 9 | 10 | /** 11 | * 12 | * @type Object 13 | * @private 14 | * 15 | * Regex used to detect the file type 16 | */ 17 | const _regex = { 18 | html: /html/i, 19 | js: /(js|es)/i, 20 | stylus: /(styl|stylus)/i, 21 | sass: /(sass)/i 22 | } 23 | 24 | class Template extends Task { 25 | 26 | execute() { 27 | super.execute() 28 | 29 | const params = this.getParameters() 30 | const argv = params.argv 31 | const templateName = argv._[1] 32 | const dstName = argv._[2] 33 | const config = this.getConfig(templateName) 34 | 35 | if (!config) { 36 | Print.log(`The "${templateName}" template do not have any configuration`, 'yellow') 37 | } 38 | 39 | const manifest = this._buildManifest(config, templateName, dstName) 40 | 41 | const p = argv._[3] || {} 42 | p.name = p.name || dstName 43 | 44 | // Generate files from templates 45 | for (let j = 0; j < manifest.length; j++) { 46 | const template = fs.readFileSync(manifest[j].inputFile).toString('utf8') 47 | const output = m.render(template, p) 48 | fs.ensureDirSync(path.dirname(manifest[j].outputFile)) 49 | fs.writeFile(manifest[j].outputFile, output) 50 | Print.log(`${manifest[j].outputFile} generated`, 'green') 51 | } 52 | } 53 | 54 | getConfig(templateName) { 55 | const config = super.getConfig() 56 | return config[templateName] 57 | } 58 | 59 | _buildManifest(config, templateName, dstName) { 60 | const templatePaths = [] 61 | 62 | // Build a manifest from template files 63 | for (let file, output, pth, i = 0; i < config.files.length; i++) { 64 | file = config.files[i] 65 | 66 | if (path.dirname(file) === '.') { 67 | for (let key in _regex) { 68 | if (file.match(_regex[key])) { 69 | 70 | // Detect a file 71 | if (path.extname(file).length > 0) { 72 | pth = [paths.templates_path, key, file] 73 | } 74 | 75 | // Detect an extension 76 | else { 77 | pth = [paths.templates_path, key, templateName + '.' + file] 78 | } 79 | 80 | break 81 | } 82 | } 83 | } 84 | 85 | // Detect a path 86 | else { 87 | pth = [paths.templates_path, file] 88 | } 89 | 90 | pth = pth.join('/') 91 | 92 | // Override the output name if the config.ouput exists 93 | output = config.output ? config.output + path.extname(pth) : dstName + path.extname(pth) 94 | 95 | templatePaths.push({ 96 | inputFile: pth, 97 | outputFile: path.join(config.destination_path, dstName, output) 98 | }) 99 | } 100 | 101 | return templatePaths 102 | } 103 | 104 | } 105 | 106 | Template.description = 'Generate files' 107 | 108 | module.exports = Template 109 | -------------------------------------------------------------------------------- /workflow/tasks/typescript.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict'; 3 | 4 | const path = require('path') 5 | const TaskProcess = require('../lib/TaskProcess') 6 | 7 | const TSC_CLI = path.join(path.dirname(require.resolve('typescript')), '..', 'bin', 'tsc') 8 | 9 | class Typescript extends TaskProcess { 10 | 11 | execute() { 12 | const params = this.getParameters() 13 | 14 | const input = params.input 15 | const output = params.output 16 | 17 | var command = [TSC_CLI, input, '--allowJs'] 18 | 19 | if (params.sourcemaps) command.push("--inlineSourceMap") 20 | if (params.watch) command.push("--watch") 21 | if (path.extname(output).length === 0) command.push("--outDir "+output) 22 | else command.push("--out "+output) 23 | 24 | super.execute(command.join(' ')) 25 | } 26 | 27 | } 28 | 29 | Typescript.description = `TypeScript is a typed superset of JavaScript that compiles to plain JavaScript. 30 | Compile '.ts' file to '.js'` 31 | 32 | module.exports = Typescript 33 | -------------------------------------------------------------------------------- /workflow/tasks/uglify.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict'; 3 | 4 | const path = require('path') 5 | const fs = require('fs-extra') 6 | const paths = require('../config/paths') 7 | const TaskProcess = require('../lib/TaskProcess') 8 | 9 | const UGLIFY_CLI = path.join(path.dirname(require.resolve('uglifyjs')), '..', 'bin', 'uglifyjs') 10 | 11 | class Uglify extends TaskProcess { 12 | 13 | execute() { 14 | const params = this.getParameters() 15 | 16 | let input = params.input 17 | const output = params.output 18 | let tmp_input = null 19 | 20 | if (input === output) { 21 | const extname = path.extname(input) 22 | tmp_input = `${paths.tmp_path}/${path.basename(input).replace(extname, '')}_uglify${extname}` 23 | fs.copySync(input, tmp_input) 24 | input = tmp_input 25 | } 26 | 27 | const command = [UGLIFY_CLI] 28 | command.push(input) 29 | command.push('-c') 30 | command.push('--mangle') 31 | command.push("--output") 32 | command.push(output) 33 | 34 | const ps = super.execute(command.join(' ')) 35 | ps.on('close', function() { 36 | if (tmp_input) fs.unlinkSync(tmp_input) 37 | }) 38 | } 39 | 40 | } 41 | 42 | Uglify.description = `Minify JS file` 43 | 44 | module.exports = Uglify 45 | -------------------------------------------------------------------------------- /workflow/tasks/watcher.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 'use strict'; 3 | 4 | const bs = require('browser-sync') 5 | const Print = require('./../lib/Print') 6 | const Task = require('./../lib/Task') 7 | 8 | class Watcher extends Task { 9 | 10 | constructor() { 11 | super(...arguments) 12 | this.bs = null 13 | } 14 | 15 | execute() { 16 | super.execute() 17 | 18 | const config = this.getConfig() 19 | const pattern = config.file 20 | const options = config.options || {} 21 | 22 | this.bs = bs.create() 23 | 24 | const watcher = this.bs.watch(pattern, options) 25 | const name = this.name 26 | 27 | watcher.on('ready', function(file) { 28 | Print.log(`[${name}] Ready to watch`, 'magenta') 29 | this.on('add', function(file){ 30 | Print.log(`[${name}] Add ${file}`, 'magenta') 31 | }) 32 | }) 33 | 34 | watcher.on('change', function(file) { 35 | Print.log(`[${name}] Change ${file}`, 'magenta') 36 | }) 37 | 38 | watcher.on('unlink', function(file) { 39 | Print.log(`[${name}] Remove ${file}`, 'magenta') 40 | }) 41 | } 42 | 43 | kill() { 44 | this.bs.exit() 45 | super.kill() 46 | } 47 | 48 | } 49 | 50 | Watcher.description = 'Watch files' 51 | 52 | module.exports = Watcher 53 | -------------------------------------------------------------------------------- /workflow/templates/html/section.html: -------------------------------------------------------------------------------- 1 |
2 | -------------------------------------------------------------------------------- /workflow/templates/js/section.js: -------------------------------------------------------------------------------- 1 | module.exports = class {{ name }} { 2 | constructor() { 3 | this.$el = document.querySelector('.{{ name }}') 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /workflow/templates/stylus/section.styl: -------------------------------------------------------------------------------- 1 | section#{{ name }} { 2 | &.{{ name }}--hidden { display: none; } 3 | } 4 | -------------------------------------------------------------------------------- /workflow/test/app/index.css: -------------------------------------------------------------------------------- 1 | /* Generated from sass */ 2 | -------------------------------------------------------------------------------- /workflow/test/app/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/makemepulse/frontend_workflow/e401d2efa2567e47e79486d32c284527ea772974/workflow/test/app/index.js -------------------------------------------------------------------------------- /workflow/test/app/index.sass: -------------------------------------------------------------------------------- 1 | /* Generated from sass */ 2 | -------------------------------------------------------------------------------- /workflow/test/app/index.styl: -------------------------------------------------------------------------------- 1 | /* Generated from stylus */ 2 | -------------------------------------------------------------------------------- /workflow/test/app/index.ts: -------------------------------------------------------------------------------- 1 | /* Generated from Typescript */ 2 | -------------------------------------------------------------------------------- /workflow/test/config.js: -------------------------------------------------------------------------------- 1 | const paths = { 2 | app: './workflow/test/app', 3 | public: './workflow/test/public' 4 | } 5 | 6 | const _tasks = {} 7 | 8 | _tasks['typescript'] = [ 9 | { 10 | file: `${paths.app}/index.ts ${paths.public}/main.js`, 11 | options: {}, 12 | override_parameters: {} 13 | } 14 | ] 15 | 16 | _tasks['uglify'] = [ 17 | { 18 | file: `${paths.public}/main.js ${paths.public}/main.js`, 19 | options: {}, 20 | override_parameters: {} 21 | } 22 | ] 23 | 24 | _tasks['stylus'] = [ 25 | { 26 | file: `${paths.app}/index.styl ${paths.public}/main.css`, 27 | options: {}, 28 | override_parameters: {} 29 | } 30 | ] 31 | 32 | _tasks['postcss'] = [ 33 | { 34 | file: `${paths.app}/index.css ${paths.public}/main.css`, 35 | options: {}, 36 | override_parameters: {} 37 | } 38 | ] 39 | 40 | _tasks['sass'] = [ 41 | { 42 | file: `${paths.app}/index.sass ${paths.public}/main.css`, 43 | options: {}, 44 | override_parameters: {} 45 | } 46 | ] 47 | 48 | _tasks['watcher'] = [ 49 | { 50 | file: `${paths.public}/**/*`, 51 | options: {}, 52 | override_parameters: { 53 | watch: true 54 | } 55 | } 56 | ] 57 | 58 | _tasks['server'] = [ 59 | { 60 | options: { 61 | files: `${paths.public}/**/*`, 62 | open: false, 63 | server: `${paths.public}` 64 | }, 65 | override_parameters: { 66 | watch: true 67 | } 68 | } 69 | ] 70 | 71 | 72 | const browserify_options = { 73 | options: { 74 | cache: {}, 75 | packageCache: {}, 76 | extensions: [ '.js', '.es' ], 77 | paths: [ 78 | `${paths.app}` 79 | ] 80 | }, 81 | transforms: { 82 | babelify: { 83 | modules: "common", 84 | compact: false, 85 | comments: true 86 | }, 87 | partialify: [ 'svg', 'txt' ], 88 | watchify: { 89 | delay: 600 90 | } 91 | } 92 | } 93 | 94 | _tasks['browserify'] = [ 95 | Object.assign({ 96 | file: `${paths.app}/index.js ${paths.public}/main.js`, 97 | options: {}, 98 | override_parameters: {} 99 | }, browserify_options), 100 | 101 | Object.assign({ 102 | file: `${paths.app}/vendor/index.js ${paths.public}/vendor.js`, 103 | options: {}, 104 | override_parameters: { 105 | watch: false 106 | } 107 | }, browserify_options) 108 | ] 109 | 110 | _tasks['template'] = [{ 111 | section: { 112 | output: 'index', 113 | destination_path: `${paths.app}/sections`, 114 | files: [ 115 | 'section.html', 116 | 'stylus/section.styl', 117 | 'js' 118 | ] 119 | }, 120 | components: { 121 | destination_path: `${paths.public}/components`, 122 | files: [ 123 | 'stylus/section.styl', 124 | 'js/section.js' 125 | ] 126 | } 127 | }] 128 | 129 | module.exports = _tasks 130 | -------------------------------------------------------------------------------- /workflow/test/index.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert') 2 | 3 | const Task = require('./../lib/Task') 4 | const TaskManager = require('./../lib/TaskManager') 5 | 6 | const Stylus = require('./../tasks/stylus') 7 | 8 | describe('TaskProcess', function() { 9 | 10 | it('Execute a child process', function(done) { 11 | assert.doesNotThrow(function(){ 12 | const stylusConfig = { 13 | override_parameters: { 14 | input: './workflow/test/app/index.styl', 15 | output: './workflow/test/public/main.css' 16 | } 17 | } 18 | const task = Stylus.create('stylus', stylusConfig) 19 | task.options.argv.replace('node workflow stylus -s'.split(' ')) 20 | TaskManager.on('task:kill', function() { 21 | done() 22 | }) 23 | task.execute() 24 | }) 25 | }) 26 | 27 | }) 28 | -------------------------------------------------------------------------------- /workflow/test/public/main.css: -------------------------------------------------------------------------------- 1 | /* Generated from stylus */ 2 | /*# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL2FwcC9pbmRleC5zdHlsIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBIiwiZmlsZSI6Im1haW4uY3NzIiwic291cmNlc0NvbnRlbnQiOlsiLyogR2VuZXJhdGVkIGZyb20gc3R5bHVzICovXG4iXX0= */ --------------------------------------------------------------------------------